/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file newgrf.cpp Base of all NewGRF support. */ #include "stdafx.h" #include "core/backup_type.hpp" #include "core/container_func.hpp" #include "debug.h" #include "fileio_func.h" #include "engine_func.h" #include "engine_base.h" #include "bridge.h" #include "town.h" #include "newgrf_engine.h" #include "newgrf_text.h" #include "spritecache.h" #include "currency.h" #include "landscape.h" #include "newgrf_badge.h" #include "newgrf_cargo.h" #include "newgrf_sound.h" #include "newgrf_station.h" #include "industrytype.h" #include "newgrf_canal.h" #include "newgrf_townname.h" #include "newgrf_industries.h" #include "newgrf_airporttiles.h" #include "newgrf_airport.h" #include "newgrf_object.h" #include "network/core/config.h" #include "smallmap_gui.h" #include "genworld.h" #include "error_func.h" #include "vehicle_base.h" #include "road.h" #include "newgrf_roadstop.h" #include "newgrf/newgrf_bytereader.h" #include "newgrf/newgrf_internal_vehicle.h" #include "newgrf/newgrf_internal.h" #include "newgrf/newgrf_stringmapping.h" #include "table/strings.h" #include "safeguards.h" /* TTDPatch extended GRF format codec * (c) Petr Baudis 2004 (GPL'd) * Changes by Florian octo Forster are (c) by the OpenTTD development team. * * Contains portions of documentation by TTDPatch team. * Thanks especially to Josef Drexler for the documentation as well as a lot * of help at #tycoon. Also thanks to Michael Blunck for his GRF files which * served as subject to the initial testing of this codec. */ /** List of all loaded GRF files */ static std::vector _grf_files; std::span GetAllGRFFiles() { return _grf_files; } /** Miscellaneous GRF features, set by Action 0x0D, parameter 0x9E */ GrfMiscBits _misc_grf_features{}; /** Indicates which are the newgrf features currently loaded ingame */ GRFLoadedFeatures _loaded_newgrf_features; GrfProcessingState _cur; ReferenceThroughBaseContainer> _gted; ///< Temporary engine data used during NewGRF loading /** * Debug() function dedicated to newGRF debugging messages * Function is essentially the same as Debug(grf, severity, ...) with the * addition of file:line information when parsing grf files. * NOTE: for the above reason(s) GrfMsg() should ONLY be used for * loading/parsing grf files, not for runtime debug messages as there * is no file information available during that time. * @param severity debugging severity level, see debug.h * @param msg the message */ void GrfMsgI(int severity, const std::string &msg) { Debug(grf, severity, "[{}:{}] {}", _cur.grfconfig->filename, _cur.nfo_line, msg); } /** * Obtain a NewGRF file by its grfID * @param grfid The grfID to obtain the file for * @return The file. */ GRFFile *GetFileByGRFID(uint32_t grfid) { auto it = std::ranges::find(_grf_files, grfid, &GRFFile::grfid); if (it != std::end(_grf_files)) return &*it; return nullptr; } /** * Obtain a NewGRF file by its filename * @param filename The filename to obtain the file for. * @return The file. */ static GRFFile *GetFileByFilename(const std::string &filename) { auto it = std::ranges::find(_grf_files, filename, &GRFFile::filename); if (it != std::end(_grf_files)) return &*it; return nullptr; } /** Reset all NewGRFData that was used only while processing data */ static void ClearTemporaryNewGRFData(GRFFile *gf) { gf->labels.clear(); } /** * Disable a GRF * @param message Error message or STR_NULL. * @param config GRFConfig to disable, nullptr for current. * @return Error message of the GRF for further customisation. */ GRFError *DisableGrf(StringID message, GRFConfig *config) { GRFFile *file; if (config != nullptr) { file = GetFileByGRFID(config->ident.grfid); } else { config = _cur.grfconfig; file = _cur.grffile; } config->status = GCS_DISABLED; if (file != nullptr) ClearTemporaryNewGRFData(file); if (config == _cur.grfconfig) _cur.skip_sprites = -1; if (message == STR_NULL) return nullptr; config->error = {STR_NEWGRF_ERROR_MSG_FATAL, message}; if (config == _cur.grfconfig) config->error->param_value[0] = _cur.nfo_line; return &config->error.value(); } /** * Disable a static NewGRF when it is influencing another (non-static) * NewGRF as this could cause desyncs. * * We could just tell the NewGRF querying that the file doesn't exist, * but that might give unwanted results. Disabling the NewGRF gives the * best result as no NewGRF author can complain about that. * @param c The NewGRF to disable. */ void DisableStaticNewGRFInfluencingNonStaticNewGRFs(GRFConfig &c) { GRFError *error = DisableGrf(STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC, &c); error->data = _cur.grfconfig->GetName(); } static std::map _grf_id_overrides; /** * Set the override for a NewGRF * @param source_grfid The grfID which wants to override another NewGRF. * @param target_grfid The grfID which is being overridden. */ void SetNewGRFOverride(uint32_t source_grfid, uint32_t target_grfid) { if (target_grfid == 0) { _grf_id_overrides.erase(source_grfid); GrfMsg(5, "SetNewGRFOverride: Removed override of 0x{:X}", std::byteswap(source_grfid)); } else { _grf_id_overrides[source_grfid] = target_grfid; GrfMsg(5, "SetNewGRFOverride: Added override of 0x{:X} to 0x{:X}", std::byteswap(source_grfid), std::byteswap(target_grfid)); } } /** * Get overridden GRF for current GRF if present. * @return Overridden GRFFile if present, or nullptr. */ GRFFile *GetCurrentGRFOverride() { auto found = _grf_id_overrides.find(_cur.grffile->grfid); if (found != std::end(_grf_id_overrides)) { GRFFile *grffile = GetFileByGRFID(found->second); if (grffile != nullptr) return grffile; } return nullptr; } /** * Returns the engine associated to a certain internal_id, resp. allocates it. * @param file NewGRF that wants to change the engine. * @param type Vehicle type. * @param internal_id Engine ID inside the NewGRF. * @param static_access If the engine is not present, return nullptr instead of allocating a new engine. (Used for static Action 0x04). * @return The requested engine. */ Engine *GetNewEngine(const GRFFile *file, VehicleType type, uint16_t internal_id, bool static_access) { /* Hack for add-on GRFs that need to modify another GRF's engines. This lets * them use the same engine slots. */ uint32_t scope_grfid = INVALID_GRFID; // If not using dynamic_engines, all newgrfs share their ID range if (_settings_game.vehicle.dynamic_engines) { /* If dynamic_engies is enabled, there can be multiple independent ID ranges. */ scope_grfid = file->grfid; if (auto it = _grf_id_overrides.find(file->grfid); it != std::end(_grf_id_overrides)) { scope_grfid = it->second; const GRFFile *grf_match = GetFileByGRFID(scope_grfid); if (grf_match == nullptr) { GrfMsg(5, "Tried mapping from GRFID {:x} to {:x} but target is not loaded", std::byteswap(file->grfid), std::byteswap(scope_grfid)); } else { GrfMsg(5, "Mapping from GRFID {:x} to {:x}", std::byteswap(file->grfid), std::byteswap(scope_grfid)); } } /* Check if the engine is registered in the override manager */ EngineID engine = _engine_mngr.GetID(type, internal_id, scope_grfid); if (engine != EngineID::Invalid()) { Engine *e = Engine::Get(engine); if (!e->grf_prop.HasGrfFile()) { e->grf_prop.SetGRFFile(file); } return e; } } /* Check if there is an unreserved slot */ EngineID engine = _engine_mngr.UseUnreservedID(type, internal_id, scope_grfid, static_access); if (engine != EngineID::Invalid()) { Engine *e = Engine::Get(engine); if (!e->grf_prop.HasGrfFile()) { e->grf_prop.SetGRFFile(file); GrfMsg(5, "Replaced engine at index {} for GRFID {:x}, type {}, index {}", e->index, std::byteswap(file->grfid), type, internal_id); } return e; } if (static_access) return nullptr; if (!Engine::CanAllocateItem()) { GrfMsg(0, "Can't allocate any more engines"); return nullptr; } size_t engine_pool_size = Engine::GetPoolSize(); /* ... it's not, so create a new one based off an existing engine */ Engine *e = new Engine(type, internal_id); e->grf_prop.SetGRFFile(file); /* Reserve the engine slot */ _engine_mngr.SetID(type, internal_id, scope_grfid, std::min(internal_id, _engine_counts[type]), e->index); if (engine_pool_size != Engine::GetPoolSize()) { /* Resize temporary engine data ... */ _gted.resize(Engine::GetPoolSize()); } if (type == VEH_TRAIN) { _gted[e->index].railtypelabel = GetRailTypeInfo(e->u.rail.railtype)->label; } GrfMsg(5, "Created new engine at index {} for GRFID {:x}, type {}, index {}", e->index, std::byteswap(file->grfid), type, internal_id); return e; } /** * Return the ID of a new engine * @param file The NewGRF file providing the engine. * @param type The Vehicle type. * @param internal_id NewGRF-internal ID of the engine. * @return The new EngineID. * @note depending on the dynamic_engine setting and a possible override * property the grfID may be unique or overwriting or partially re-defining * properties of an existing engine. */ EngineID GetNewEngineID(const GRFFile *file, VehicleType type, uint16_t internal_id) { uint32_t scope_grfid = INVALID_GRFID; // If not using dynamic_engines, all newgrfs share their ID range if (_settings_game.vehicle.dynamic_engines) { scope_grfid = file->grfid; if (auto it = _grf_id_overrides.find(file->grfid); it != std::end(_grf_id_overrides)) { scope_grfid = it->second; } } return _engine_mngr.GetID(type, internal_id, scope_grfid); } /** * Translate the refit mask. refit_mask is uint32_t as it has not been mapped to CargoTypes. */ CargoTypes TranslateRefitMask(uint32_t refit_mask) { CargoTypes result = 0; for (uint8_t bit : SetBitIterator(refit_mask)) { CargoType cargo = GetCargoTranslation(bit, _cur.grffile, true); if (IsValidCargoType(cargo)) SetBit(result, cargo); } return result; } /** * Converts TTD(P) Base Price pointers into the enum used by OTTD * See http://wiki.ttdpatch.net/tiki-index.php?page=BaseCosts * @param base_pointer TTD(P) Base Price Pointer * @param error_location Function name for grf error messages * @param[out] index If \a base_pointer is valid, \a index is assigned to the matching price; else it is left unchanged */ void ConvertTTDBasePrice(uint32_t base_pointer, const char *error_location, Price *index) { /* Special value for 'none' */ if (base_pointer == 0) { *index = INVALID_PRICE; return; } static const uint32_t start = 0x4B34; ///< Position of first base price static const uint32_t size = 6; ///< Size of each base price record if (base_pointer < start || (base_pointer - start) % size != 0 || (base_pointer - start) / size >= PR_END) { GrfMsg(1, "{}: Unsupported running cost base 0x{:04X}, ignoring", error_location, base_pointer); return; } *index = (Price)((base_pointer - start) / size); } /** * Get the language map associated with a given NewGRF and language. * @param grfid The NewGRF to get the map for. * @param language_id The (NewGRF) language ID to get the map for. * @return The LanguageMap, or nullptr if it couldn't be found. */ /* static */ const LanguageMap *LanguageMap::GetLanguageMap(uint32_t grfid, uint8_t language_id) { /* LanguageID "MAX_LANG", i.e. 7F is any. This language can't have a gender/case mapping, but has to be handled gracefully. */ const GRFFile *grffile = GetFileByGRFID(grfid); if (grffile == nullptr) return nullptr; auto it = grffile->language_map.find(language_id); if (it == std::end(grffile->language_map)) return nullptr; return &it->second; } /** * Set the current NewGRF as unsafe for static use * @note Used during safety scan on unsafe actions. */ void GRFUnsafe(ByteReader &) { _cur.grfconfig->flags.Set(GRFConfigFlag::Unsafe); /* Skip remainder of GRF */ _cur.skip_sprites = -1; } /** Reset and clear all NewGRFs */ static void ResetNewGRF() { _cur.grffile = nullptr; _grf_files.clear(); /* We store pointers to GRFFiles in many places, so need to ensure that the pointers do not become invalid * due to vector reallocation. Should not happen due to loading taking place in multiple stages, but * reserving when the size is known is good practice anyway. */ _grf_files.reserve(_grfconfig.size()); } /** Clear all NewGRF errors */ static void ResetNewGRFErrors() { for (const auto &c : _grfconfig) { c->error.reset(); } } extern void ResetCallbacks(bool final); extern void ResetGRM(); /** * Reset all NewGRF loaded data */ void ResetNewGRFData() { CleanUpStrings(); CleanUpGRFTownNames(); ResetBadges(); /* Copy/reset original engine info data */ SetupEngines(); /* Copy/reset original bridge info data */ ResetBridges(); /* Reset rail type information */ ResetRailTypes(); /* Copy/reset original road type info data */ ResetRoadTypes(); /* Allocate temporary refit/cargo class data */ _gted.resize(Engine::GetPoolSize()); /* Fill rail type label temporary data for default trains */ for (const Engine *e : Engine::IterateType(VEH_TRAIN)) { _gted[e->index].railtypelabel = GetRailTypeInfo(e->u.rail.railtype)->label; } /* Reset GRM reservations */ ResetGRM(); /* Reset generic feature callback lists */ ResetGenericCallbacks(); /* Reset price base data */ ResetPriceBaseMultipliers(); /* Reset the curencies array */ ResetCurrencies(); /* Reset the house array */ ResetHouses(); /* Reset the industries structures*/ ResetIndustries(); /* Reset the objects. */ ObjectClass::Reset(); ResetObjects(); /* Reset station classes */ StationClass::Reset(); /* Reset airport-related structures */ AirportClass::Reset(); AirportSpec::ResetAirports(); AirportTileSpec::ResetAirportTiles(); /* Reset road stop classes */ RoadStopClass::Reset(); /* Reset canal sprite groups and flags */ _water_feature.fill({}); /* Reset the snowline table. */ ClearSnowLine(); /* Reset NewGRF files */ ResetNewGRF(); /* Reset NewGRF errors. */ ResetNewGRFErrors(); /* Set up the default cargo types */ SetupCargoForClimate(_settings_game.game_creation.landscape); /* Reset misc GRF features and train list display variables */ _misc_grf_features = {}; _loaded_newgrf_features.has_2CC = false; _loaded_newgrf_features.used_liveries = 1 << LS_DEFAULT; _loaded_newgrf_features.shore = SHORE_REPLACE_NONE; _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NONE; /* Clear all GRF overrides */ _grf_id_overrides.clear(); InitializeSoundPool(); _spritegroup_pool.CleanPool(); ResetCallbacks(false); } /** * Reset NewGRF data which is stored persistently in savegames. */ void ResetPersistentNewGRFData() { /* Reset override managers */ _engine_mngr.ResetToDefaultMapping(); _house_mngr.ResetMapping(); _industry_mngr.ResetMapping(); _industile_mngr.ResetMapping(); _airport_mngr.ResetMapping(); _airporttile_mngr.ResetMapping(); } /** * Get the cargo translation table to use for the given GRF file. * @param grffile GRF file. * @returns Readonly cargo translation table to use. */ std::span GetCargoTranslationTable(const GRFFile &grffile) { /* Always use the translation table if it's installed. */ if (!grffile.cargo_list.empty()) return grffile.cargo_list; /* Pre-v7 use climate-dependent "slot" table. */ if (grffile.grf_version < 7) return GetClimateDependentCargoTranslationTable(); /* Otherwise use climate-independent "bitnum" table. */ return GetClimateIndependentCargoTranslationTable(); } /** * Construct the Cargo Mapping * @note This is the reverse of a cargo translation table */ static void BuildCargoTranslationMap() { _cur.grffile->cargo_map.fill(UINT8_MAX); auto cargo_list = GetCargoTranslationTable(*_cur.grffile); for (const CargoSpec *cs : CargoSpec::Iterate()) { /* Check the translation table for this cargo's label */ int idx = find_index(cargo_list, cs->label); if (idx >= 0) _cur.grffile->cargo_map[cs->Index()] = idx; } } /** * Prepare loading a NewGRF file with its config * @param config The NewGRF configuration struct with name, id, parameters and alike. */ static void InitNewGRFFile(const GRFConfig &config) { GRFFile *newfile = GetFileByFilename(config.filename); if (newfile != nullptr) { /* We already loaded it once. */ _cur.grffile = newfile; return; } assert(_grf_files.size() < _grf_files.capacity()); // We must not invalidate pointers. _cur.grffile = &_grf_files.emplace_back(config); } /** * Constructor for GRFFile * @param config GRFConfig to copy name, grfid and parameters from. */ GRFFile::GRFFile(const GRFConfig &config) { this->filename = config.filename; this->grfid = config.ident.grfid; /* Initialise local settings to defaults */ this->traininfo_vehicle_pitch = 0; this->traininfo_vehicle_width = TRAININFO_DEFAULT_VEHICLE_WIDTH; /* Mark price_base_multipliers as 'not set' */ this->price_base_multipliers.fill(INVALID_PRICE_MODIFIER); /* Initialise rail type map with default rail types */ this->railtype_map.fill(INVALID_RAILTYPE); this->railtype_map[0] = RAILTYPE_RAIL; this->railtype_map[1] = RAILTYPE_ELECTRIC; this->railtype_map[2] = RAILTYPE_MONO; this->railtype_map[3] = RAILTYPE_MAGLEV; /* Initialise road type map with default road types */ this->roadtype_map.fill(INVALID_ROADTYPE); this->roadtype_map[0] = ROADTYPE_ROAD; /* Initialise tram type map with default tram types */ this->tramtype_map.fill(INVALID_ROADTYPE); this->tramtype_map[0] = ROADTYPE_TRAM; /* Copy the initial parameter list */ this->param = config.param; } /* Some compilers get confused about vectors of unique_ptrs. */ GRFFile::GRFFile() = default; GRFFile::GRFFile(GRFFile &&other) = default; GRFFile::~GRFFile() = default; /** * Find first cargo label that exists and is active from a list of cargo labels. * @param labels List of cargo labels. * @returns First cargo label in list that exists, or CT_INVALID if none exist. */ static CargoLabel GetActiveCargoLabel(const std::initializer_list &labels) { for (const CargoLabel &label : labels) { CargoType cargo_type = GetCargoTypeByLabel(label); if (cargo_type != INVALID_CARGO) return label; } return CT_INVALID; } /** * Get active cargo label from either a cargo label or climate-dependent mixed cargo type. * @param label Cargo label or climate-dependent mixed cargo type. * @returns Active cargo label, or CT_INVALID if cargo label is not active. */ static CargoLabel GetActiveCargoLabel(const std::variant &label) { struct visitor { CargoLabel operator()(const CargoLabel &label) { return label; } CargoLabel operator()(const MixedCargoType &mixed) { switch (mixed) { case MCT_LIVESTOCK_FRUIT: return GetActiveCargoLabel({CT_LIVESTOCK, CT_FRUIT}); case MCT_GRAIN_WHEAT_MAIZE: return GetActiveCargoLabel({CT_GRAIN, CT_WHEAT, CT_MAIZE}); case MCT_VALUABLES_GOLD_DIAMONDS: return GetActiveCargoLabel({CT_VALUABLES, CT_GOLD, CT_DIAMONDS}); default: NOT_REACHED(); } } }; return std::visit(visitor{}, label); } /** * Precalculate refit masks from cargo classes for all vehicles. */ static void CalculateRefitMasks() { CargoTypes original_known_cargoes = 0; for (CargoType cargo_type = 0; cargo_type != NUM_CARGO; ++cargo_type) { if (IsDefaultCargo(cargo_type)) SetBit(original_known_cargoes, cargo_type); } for (Engine *e : Engine::Iterate()) { EngineID engine = e->index; EngineInfo *ei = &e->info; bool only_defaultcargo; ///< Set if the vehicle shall carry only the default cargo /* Apply default cargo translation map if cargo type hasn't been set, either explicitly or by aircraft cargo handling. */ if (!IsValidCargoType(e->info.cargo_type)) { e->info.cargo_type = GetCargoTypeByLabel(GetActiveCargoLabel(e->info.cargo_label)); } /* If the NewGRF did not set any cargo properties, we apply default values. */ if (_gted[engine].defaultcargo_grf == nullptr) { /* If the vehicle has any capacity, apply the default refit masks */ if (e->type != VEH_TRAIN || e->u.rail.capacity != 0) { static constexpr LandscapeType T = LandscapeType::Temperate; static constexpr LandscapeType A = LandscapeType::Arctic; static constexpr LandscapeType S = LandscapeType::Tropic; static constexpr LandscapeType Y = LandscapeType::Toyland; static const struct DefaultRefitMasks { LandscapeTypes climate; CargoLabel cargo_label; CargoClasses cargo_allowed; CargoClasses cargo_disallowed; } _default_refit_masks[] = { {{T, A, S, Y}, CT_PASSENGERS, {CargoClass::Passengers}, {}}, {{T, A, S }, CT_MAIL, {CargoClass::Mail}, {}}, {{T, A, S }, CT_VALUABLES, {CargoClass::Armoured}, {CargoClass::Liquid}}, {{ Y}, CT_MAIL, {CargoClass::Mail, CargoClass::Armoured}, {CargoClass::Liquid}}, {{T, A }, CT_COAL, {CargoClass::Bulk}, {}}, {{ S }, CT_COPPER_ORE, {CargoClass::Bulk}, {}}, {{ Y}, CT_SUGAR, {CargoClass::Bulk}, {}}, {{T, A, S }, CT_OIL, {CargoClass::Liquid}, {}}, {{ Y}, CT_COLA, {CargoClass::Liquid}, {}}, {{T }, CT_GOODS, {CargoClass::PieceGoods, CargoClass::Express}, {CargoClass::Liquid, CargoClass::Passengers}}, {{ A, S }, CT_GOODS, {CargoClass::PieceGoods, CargoClass::Express}, {CargoClass::Liquid, CargoClass::Passengers, CargoClass::Refrigerated}}, {{ A, S }, CT_FOOD, {CargoClass::Refrigerated}, {}}, {{ Y}, CT_CANDY, {CargoClass::PieceGoods, CargoClass::Express}, {CargoClass::Liquid, CargoClass::Passengers}}, }; if (e->type == VEH_AIRCRAFT) { /* Aircraft default to "light" cargoes */ _gted[engine].cargo_allowed = {CargoClass::Passengers, CargoClass::Mail, CargoClass::Armoured, CargoClass::Express}; _gted[engine].cargo_disallowed = {CargoClass::Liquid}; } else if (e->type == VEH_SHIP) { CargoLabel label = GetActiveCargoLabel(ei->cargo_label); switch (label.base()) { case CT_PASSENGERS.base(): /* Ferries */ _gted[engine].cargo_allowed = {CargoClass::Passengers}; _gted[engine].cargo_disallowed = {}; break; case CT_OIL.base(): /* Tankers */ _gted[engine].cargo_allowed = {CargoClass::Liquid}; _gted[engine].cargo_disallowed = {}; break; default: /* Cargo ships */ if (_settings_game.game_creation.landscape == LandscapeType::Toyland) { /* No tanker in toyland :( */ _gted[engine].cargo_allowed = {CargoClass::Mail, CargoClass::Armoured, CargoClass::Express, CargoClass::Bulk, CargoClass::PieceGoods, CargoClass::Liquid}; _gted[engine].cargo_disallowed = {CargoClass::Passengers}; } else { _gted[engine].cargo_allowed = {CargoClass::Mail, CargoClass::Armoured, CargoClass::Express, CargoClass::Bulk, CargoClass::PieceGoods}; _gted[engine].cargo_disallowed = {CargoClass::Liquid, CargoClass::Passengers}; } break; } e->u.ship.old_refittable = true; } else if (e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON) { /* Train engines default to all cargoes, so you can build single-cargo consists with fast engines. * Trains loading multiple cargoes may start stations accepting unwanted cargoes. */ _gted[engine].cargo_allowed = {CargoClass::Passengers, CargoClass::Mail, CargoClass::Armoured, CargoClass::Express, CargoClass::Bulk, CargoClass::PieceGoods, CargoClass::Liquid}; _gted[engine].cargo_disallowed = {}; } else { /* Train wagons and road vehicles are classified by their default cargo type */ CargoLabel label = GetActiveCargoLabel(ei->cargo_label); for (const auto &drm : _default_refit_masks) { if (!drm.climate.Test(_settings_game.game_creation.landscape)) continue; if (drm.cargo_label != label) continue; _gted[engine].cargo_allowed = drm.cargo_allowed; _gted[engine].cargo_disallowed = drm.cargo_disallowed; break; } /* All original cargoes have specialised vehicles, so exclude them */ _gted[engine].ctt_exclude_mask = original_known_cargoes; } } _gted[engine].UpdateRefittability(_gted[engine].cargo_allowed.Any()); if (IsValidCargoType(ei->cargo_type)) ClrBit(_gted[engine].ctt_exclude_mask, ei->cargo_type); } /* Compute refittability */ { CargoTypes mask = 0; CargoTypes not_mask = 0; CargoTypes xor_mask = ei->refit_mask; /* If the original masks set by the grf are zero, the vehicle shall only carry the default cargo. * Note: After applying the translations, the vehicle may end up carrying no defined cargo. It becomes unavailable in that case. */ only_defaultcargo = _gted[engine].refittability != GRFTempEngineData::NONEMPTY; if (_gted[engine].cargo_allowed.Any()) { /* Build up the list of cargo types from the set cargo classes. */ for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->classes.Any(_gted[engine].cargo_allowed) && cs->classes.All(_gted[engine].cargo_allowed_required)) SetBit(mask, cs->Index()); if (cs->classes.Any(_gted[engine].cargo_disallowed)) SetBit(not_mask, cs->Index()); } } ei->refit_mask = ((mask & ~not_mask) ^ xor_mask) & _cargo_mask; /* Apply explicit refit includes/excludes. */ ei->refit_mask |= _gted[engine].ctt_include_mask; ei->refit_mask &= ~_gted[engine].ctt_exclude_mask; /* Custom refit mask callback. */ const GRFFile *file = _gted[e->index].defaultcargo_grf; if (file == nullptr) file = e->GetGRF(); if (file != nullptr && e->info.callback_mask.Test(VehicleCallbackMask::CustomRefit)) { for (const CargoSpec *cs : CargoSpec::Iterate()) { uint8_t local_slot = file->cargo_map[cs->Index()]; uint16_t callback = GetVehicleCallback(CBID_VEHICLE_CUSTOM_REFIT, cs->classes.base(), local_slot, engine, nullptr); switch (callback) { case CALLBACK_FAILED: case 0: break; // Do nothing. case 1: SetBit(ei->refit_mask, cs->Index()); break; case 2: ClrBit(ei->refit_mask, cs->Index()); break; default: ErrorUnknownCallbackResult(file->grfid, CBID_VEHICLE_CUSTOM_REFIT, callback); } } } } /* Clear invalid cargoslots (from default vehicles or pre-NewCargo GRFs) */ if (IsValidCargoType(ei->cargo_type) && !HasBit(_cargo_mask, ei->cargo_type)) ei->cargo_type = INVALID_CARGO; /* Ensure that the vehicle is either not refittable, or that the default cargo is one of the refittable cargoes. * Note: Vehicles refittable to no cargo are handle differently to vehicle refittable to a single cargo. The latter might have subtypes. */ if (!only_defaultcargo && (e->type != VEH_SHIP || e->u.ship.old_refittable) && IsValidCargoType(ei->cargo_type) && !HasBit(ei->refit_mask, ei->cargo_type)) { ei->cargo_type = INVALID_CARGO; } /* Check if this engine's cargo type is valid. If not, set to the first refittable * cargo type. Finally disable the vehicle, if there is still no cargo. */ if (!IsValidCargoType(ei->cargo_type) && ei->refit_mask != 0) { /* Figure out which CTT to use for the default cargo, if it is 'first refittable'. */ const GRFFile *file = _gted[engine].defaultcargo_grf; if (file == nullptr) file = e->GetGRF(); if (file != nullptr && file->grf_version >= 8 && !file->cargo_list.empty()) { /* Use first refittable cargo from cargo translation table */ uint8_t best_local_slot = UINT8_MAX; for (CargoType cargo_type : SetCargoBitIterator(ei->refit_mask)) { uint8_t local_slot = file->cargo_map[cargo_type]; if (local_slot < best_local_slot) { best_local_slot = local_slot; ei->cargo_type = cargo_type; } } } if (!IsValidCargoType(ei->cargo_type)) { /* Use first refittable cargo slot */ ei->cargo_type = (CargoType)FindFirstBit(ei->refit_mask); } } if (!IsValidCargoType(ei->cargo_type) && e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON && e->u.rail.capacity == 0) { /* For train engines which do not carry cargo it does not matter if their cargo type is invalid. * Fallback to the first available instead, if the cargo type has not been changed (as indicated by * cargo_label not being CT_INVALID). */ if (GetActiveCargoLabel(ei->cargo_label) != CT_INVALID) { ei->cargo_type = static_cast(FindFirstBit(_standard_cargo_mask)); } } if (!IsValidCargoType(ei->cargo_type)) ei->climates = {}; /* Clear refit_mask for not refittable ships */ if (e->type == VEH_SHIP && !e->u.ship.old_refittable) { ei->refit_mask = 0; } } } /** Set to use the correct action0 properties for each canal feature */ static void FinaliseCanals() { for (uint i = 0; i < CF_END; i++) { if (_water_feature[i].grffile != nullptr) { _water_feature[i].callback_mask = _water_feature[i].grffile->canal_local_properties[i].callback_mask; _water_feature[i].flags = _water_feature[i].grffile->canal_local_properties[i].flags; } } } /** Check for invalid engines */ static void FinaliseEngineArray() { for (Engine *e : Engine::Iterate()) { if (e->GetGRF() == nullptr) { auto found = std::ranges::find(_engine_mngr.mappings[e->type], e->index, &EngineIDMapping::engine); if (found == std::end(_engine_mngr.mappings[e->type]) || found->grfid != INVALID_GRFID || found->internal_id != found->substitute_id) { e->info.string_id = STR_NEWGRF_INVALID_ENGINE; } } /* Do final mapping on variant engine ID. */ if (e->info.variant_id != EngineID::Invalid()) { e->info.variant_id = GetNewEngineID(e->grf_prop.grffile, e->type, e->info.variant_id.base()); } if (!e->info.climates.Test(_settings_game.game_creation.landscape)) continue; switch (e->type) { case VEH_TRAIN: AppendCopyableBadgeList(e->badges, GetRailTypeInfo(e->u.rail.railtype)->badges, GSF_TRAINS); break; case VEH_ROAD: AppendCopyableBadgeList(e->badges, GetRoadTypeInfo(e->u.road.roadtype)->badges, GSF_ROADVEHICLES); break; default: break; } /* Skip wagons, there livery is defined via the engine */ if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) { LiveryScheme ls = GetEngineLiveryScheme(e->index, EngineID::Invalid(), nullptr); SetBit(_loaded_newgrf_features.used_liveries, ls); /* Note: For ships and roadvehicles we assume that they cannot be refitted between passenger and freight */ if (e->type == VEH_TRAIN) { SetBit(_loaded_newgrf_features.used_liveries, LS_FREIGHT_WAGON); switch (ls) { case LS_STEAM: case LS_DIESEL: case LS_ELECTRIC: case LS_MONORAIL: case LS_MAGLEV: SetBit(_loaded_newgrf_features.used_liveries, LS_PASSENGER_WAGON_STEAM + ls - LS_STEAM); break; case LS_DMU: case LS_EMU: SetBit(_loaded_newgrf_features.used_liveries, LS_PASSENGER_WAGON_DIESEL + ls - LS_DMU); break; default: NOT_REACHED(); } } } } /* Check engine variants don't point back on themselves (either directly or via a loop) then set appropriate flags * on variant engine. This is performed separately as all variant engines need to have been resolved. */ for (Engine *e : Engine::Iterate()) { EngineID parent = e->info.variant_id; while (parent != EngineID::Invalid()) { parent = Engine::Get(parent)->info.variant_id; if (parent != e->index) continue; /* Engine looped back on itself, so clear the variant. */ e->info.variant_id = EngineID::Invalid(); GrfMsg(1, "FinaliseEngineArray: Variant of engine {:x} in '{}' loops back on itself", e->grf_prop.local_id, e->GetGRF()->filename); break; } if (e->info.variant_id != EngineID::Invalid()) { Engine::Get(e->info.variant_id)->display_flags.Set(EngineDisplayFlag::HasVariants).Set(EngineDisplayFlag::IsFolded); } } } /** Check for invalid cargoes */ void FinaliseCargoArray() { for (CargoSpec &cs : CargoSpec::array) { if (cs.town_production_effect == INVALID_TPE) { /* Set default town production effect by cargo label. */ switch (cs.label.base()) { case CT_PASSENGERS.base(): cs.town_production_effect = TPE_PASSENGERS; break; case CT_MAIL.base(): cs.town_production_effect = TPE_MAIL; break; default: cs.town_production_effect = TPE_NONE; break; } } if (!cs.IsValid()) { cs.name = cs.name_single = cs.units_volume = STR_NEWGRF_INVALID_CARGO; cs.quantifier = STR_NEWGRF_INVALID_CARGO_QUANTITY; cs.abbrev = STR_NEWGRF_INVALID_CARGO_ABBREV; } } } /** * Check if a given housespec is valid and disable it if it's not. * The housespecs that follow it are used to check the validity of * multitile houses. * @param hs The housespec to check. * @param next1 The housespec that follows \c hs. * @param next2 The housespec that follows \c next1. * @param next3 The housespec that follows \c next2. * @param filename The filename of the newgrf this house was defined in. * @return Whether the given housespec is valid. */ static bool IsHouseSpecValid(HouseSpec *hs, const HouseSpec *next1, const HouseSpec *next2, const HouseSpec *next3, const std::string &filename) { if ((hs->building_flags.Any(BUILDING_HAS_2_TILES) && (next1 == nullptr || !next1->enabled || next1->building_flags.Any(BUILDING_HAS_1_TILE))) || (hs->building_flags.Any(BUILDING_HAS_4_TILES) && (next2 == nullptr || !next2->enabled || next2->building_flags.Any(BUILDING_HAS_1_TILE) || next3 == nullptr || !next3->enabled || next3->building_flags.Any(BUILDING_HAS_1_TILE)))) { hs->enabled = false; if (!filename.empty()) Debug(grf, 1, "FinaliseHouseArray: {} defines house {} as multitile, but no suitable tiles follow. Disabling house.", filename, hs->grf_prop.local_id); return false; } /* Some places sum population by only counting north tiles. Other places use all tiles causing desyncs. * As the newgrf specs define population to be zero for non-north tiles, we just disable the offending house. * If you want to allow non-zero populations somewhen, make sure to sum the population of all tiles in all places. */ if ((hs->building_flags.Any(BUILDING_HAS_2_TILES) && next1->population != 0) || (hs->building_flags.Any(BUILDING_HAS_4_TILES) && (next2->population != 0 || next3->population != 0))) { hs->enabled = false; if (!filename.empty()) Debug(grf, 1, "FinaliseHouseArray: {} defines multitile house {} with non-zero population on additional tiles. Disabling house.", filename, hs->grf_prop.local_id); return false; } /* Substitute type is also used for override, and having an override with a different size causes crashes. * This check should only be done for NewGRF houses because grf_prop.subst_id is not set for original houses.*/ if (!filename.empty() && (hs->building_flags & BUILDING_HAS_1_TILE) != (HouseSpec::Get(hs->grf_prop.subst_id)->building_flags & BUILDING_HAS_1_TILE)) { hs->enabled = false; Debug(grf, 1, "FinaliseHouseArray: {} defines house {} with different house size then it's substitute type. Disabling house.", filename, hs->grf_prop.local_id); return false; } /* Make sure that additional parts of multitile houses are not available. */ if (!hs->building_flags.Any(BUILDING_HAS_1_TILE) && (hs->building_availability & HZ_ZONALL) != 0 && (hs->building_availability & HZ_CLIMALL) != 0) { hs->enabled = false; if (!filename.empty()) Debug(grf, 1, "FinaliseHouseArray: {} defines house {} without a size but marked it as available. Disabling house.", filename, hs->grf_prop.local_id); return false; } return true; } /** * Make sure there is at least one house available in the year 0 for the given * climate / housezone combination. * @param bitmask The climate and housezone to check for. Exactly one climate * bit and one housezone bit should be set. */ static void EnsureEarlyHouse(HouseZones bitmask) { TimerGameCalendar::Year min_year = CalendarTime::MAX_YEAR; for (const auto &hs : HouseSpec::Specs()) { if (!hs.enabled) continue; if ((hs.building_availability & bitmask) != bitmask) continue; if (hs.min_year < min_year) min_year = hs.min_year; } if (min_year == 0) return; for (auto &hs : HouseSpec::Specs()) { if (!hs.enabled) continue; if ((hs.building_availability & bitmask) != bitmask) continue; if (hs.min_year == min_year) hs.min_year = CalendarTime::MIN_YEAR; } } /** * Add all new houses to the house array. House properties can be set at any * time in the GRF file, so we can only add a house spec to the house array * after the file has finished loading. We also need to check the dates, due to * the TTDPatch behaviour described below that we need to emulate. */ static void FinaliseHouseArray() { /* If there are no houses with start dates before 1930, then all houses * with start dates of 1930 have them reset to 0. This is in order to be * compatible with TTDPatch, where if no houses have start dates before * 1930 and the date is before 1930, the game pretends that this is 1930. * If there have been any houses defined with start dates before 1930 then * the dates are left alone. * On the other hand, why 1930? Just 'fix' the houses with the lowest * minimum introduction date to 0. */ for (const auto &file : _grf_files) { if (file.housespec.empty()) continue; size_t num_houses = file.housespec.size(); for (size_t i = 0; i < num_houses; i++) { HouseSpec *hs = file.housespec[i].get(); if (hs == nullptr) continue; const HouseSpec *next1 = (i + 1 < num_houses ? file.housespec[i + 1].get() : nullptr); const HouseSpec *next2 = (i + 2 < num_houses ? file.housespec[i + 2].get() : nullptr); const HouseSpec *next3 = (i + 3 < num_houses ? file.housespec[i + 3].get() : nullptr); if (!IsHouseSpecValid(hs, next1, next2, next3, file.filename)) continue; _house_mngr.SetEntitySpec(hs); } } for (size_t i = 0; i < HouseSpec::Specs().size(); i++) { HouseSpec *hs = HouseSpec::Get(i); const HouseSpec *next1 = (i + 1 < NUM_HOUSES ? HouseSpec::Get(i + 1) : nullptr); const HouseSpec *next2 = (i + 2 < NUM_HOUSES ? HouseSpec::Get(i + 2) : nullptr); const HouseSpec *next3 = (i + 3 < NUM_HOUSES ? HouseSpec::Get(i + 3) : nullptr); /* We need to check all houses again to we are sure that multitile houses * did get consecutive IDs and none of the parts are missing. */ if (!IsHouseSpecValid(hs, next1, next2, next3, std::string{})) { /* GetHouseNorthPart checks 3 houses that are directly before * it in the house pool. If any of those houses have multi-tile * flags set it assumes it's part of a multitile house. Since * we can have invalid houses in the pool marked as disabled, we * don't want to have them influencing valid tiles. As such set * building_flags to zero here to make sure any house following * this one in the pool is properly handled as 1x1 house. */ hs->building_flags = {}; } /* Apply default cargo translation map for unset cargo slots */ for (uint i = 0; i < lengthof(hs->accepts_cargo_label); ++i) { if (!IsValidCargoType(hs->accepts_cargo[i])) hs->accepts_cargo[i] = GetCargoTypeByLabel(hs->accepts_cargo_label[i]); /* Disable acceptance if cargo type is invalid. */ if (!IsValidCargoType(hs->accepts_cargo[i])) hs->cargo_acceptance[i] = 0; } } HouseZones climate_mask = (HouseZones)(1 << (to_underlying(_settings_game.game_creation.landscape) + 12)); EnsureEarlyHouse(HZ_ZON1 | climate_mask); EnsureEarlyHouse(HZ_ZON2 | climate_mask); EnsureEarlyHouse(HZ_ZON3 | climate_mask); EnsureEarlyHouse(HZ_ZON4 | climate_mask); EnsureEarlyHouse(HZ_ZON5 | climate_mask); if (_settings_game.game_creation.landscape == LandscapeType::Arctic) { EnsureEarlyHouse(HZ_ZON1 | HZ_SUBARTC_ABOVE); EnsureEarlyHouse(HZ_ZON2 | HZ_SUBARTC_ABOVE); EnsureEarlyHouse(HZ_ZON3 | HZ_SUBARTC_ABOVE); EnsureEarlyHouse(HZ_ZON4 | HZ_SUBARTC_ABOVE); EnsureEarlyHouse(HZ_ZON5 | HZ_SUBARTC_ABOVE); } } /** * Add all new industries to the industry array. Industry properties can be set at any * time in the GRF file, so we can only add a industry spec to the industry array * after the file has finished loading. */ static void FinaliseIndustriesArray() { for (const auto &file : _grf_files) { for (const auto &indsp : file.industryspec) { if (indsp == nullptr || !indsp->enabled) continue; _industry_mngr.SetEntitySpec(indsp.get()); } for (const auto &indtsp : file.indtspec) { if (indtsp != nullptr) { _industile_mngr.SetEntitySpec(indtsp.get()); } } } for (auto &indsp : _industry_specs) { if (indsp.enabled && indsp.grf_prop.HasGrfFile()) { for (auto &conflicting : indsp.conflicting) { conflicting = MapNewGRFIndustryType(conflicting, indsp.grf_prop.grfid); } } if (!indsp.enabled) { indsp.name = STR_NEWGRF_INVALID_INDUSTRYTYPE; } /* Apply default cargo translation map for unset cargo slots */ for (size_t i = 0; i < std::size(indsp.produced_cargo_label); ++i) { if (!IsValidCargoType(indsp.produced_cargo[i])) indsp.produced_cargo[i] = GetCargoTypeByLabel(GetActiveCargoLabel(indsp.produced_cargo_label[i])); } for (size_t i = 0; i < std::size(indsp.accepts_cargo_label); ++i) { if (!IsValidCargoType(indsp.accepts_cargo[i])) indsp.accepts_cargo[i] = GetCargoTypeByLabel(GetActiveCargoLabel(indsp.accepts_cargo_label[i])); } } for (auto &indtsp : _industry_tile_specs) { /* Apply default cargo translation map for unset cargo slots */ for (size_t i = 0; i < std::size(indtsp.accepts_cargo_label); ++i) { if (!IsValidCargoType(indtsp.accepts_cargo[i])) indtsp.accepts_cargo[i] = GetCargoTypeByLabel(GetActiveCargoLabel(indtsp.accepts_cargo_label[i])); } } } /** * Add all new objects to the object array. Object properties can be set at any * time in the GRF file, so we can only add an object spec to the object array * after the file has finished loading. */ static void FinaliseObjectsArray() { for (const auto &file : _grf_files) { for (auto &objectspec : file.objectspec) { if (objectspec != nullptr && objectspec->grf_prop.HasGrfFile() && objectspec->IsEnabled()) { _object_mngr.SetEntitySpec(objectspec.get()); } } } ObjectSpec::BindToClasses(); } /** * Add all new airports to the airport array. Airport properties can be set at any * time in the GRF file, so we can only add a airport spec to the airport array * after the file has finished loading. */ static void FinaliseAirportsArray() { for (const auto &file : _grf_files) { for (auto &as : file.airportspec) { if (as != nullptr && as->enabled) { _airport_mngr.SetEntitySpec(as.get()); } } for (auto &ats : file.airtspec) { if (ats != nullptr && ats->enabled) { _airporttile_mngr.SetEntitySpec(ats.get()); } } } } /** Helper class to invoke a GrfActionHandler. */ struct InvokeGrfActionHandler { template static void Invoke(ByteReader &buf, GrfLoadingStage stage) { switch (stage) { case GLS_FILESCAN: GrfActionHandler::FileScan(buf); break; case GLS_SAFETYSCAN: GrfActionHandler::SafetyScan(buf); break; case GLS_LABELSCAN: GrfActionHandler::LabelScan(buf); break; case GLS_INIT: GrfActionHandler::Init(buf); break; case GLS_RESERVE: GrfActionHandler::Reserve(buf); break; case GLS_ACTIVATION: GrfActionHandler::Activation(buf); break; default: NOT_REACHED(); } } using Invoker = void(*)(ByteReader &buf, GrfLoadingStage stage); static constexpr Invoker funcs[] = { // Must be listed in action order. Invoke<0x00>, Invoke<0x01>, Invoke<0x02>, Invoke<0x03>, Invoke<0x04>, Invoke<0x05>, Invoke<0x06>, Invoke<0x07>, Invoke<0x08>, Invoke<0x09>, Invoke<0x0A>, Invoke<0x0B>, Invoke<0x0C>, Invoke<0x0D>, Invoke<0x0E>, Invoke<0x0F>, Invoke<0x10>, Invoke<0x11>, Invoke<0x12>, Invoke<0x13>, Invoke<0x14>, }; static void Invoke(uint8_t action, GrfLoadingStage stage, ByteReader &buf) { Invoker func = action < std::size(funcs) ? funcs[action] : nullptr; if (func == nullptr) { GrfMsg(7, "DecodeSpecialSprite: Skipping unknown action 0x{:02X}", action); } else { GrfMsg(7, "DecodeSpecialSprite: Handling action 0x{:02X} in stage {}", action, stage); func(buf, stage); } } }; /* Here we perform initial decoding of some special sprites (as are they * described at http://www.ttdpatch.net/src/newgrf.txt, but this is only a very * partial implementation yet). * XXX: We consider GRF files trusted. It would be trivial to exploit OTTD by * a crafted invalid GRF file. We should tell that to the user somehow, or * better make this more robust in the future. */ static void DecodeSpecialSprite(uint8_t *buf, uint num, GrfLoadingStage stage) { auto it = _grf_line_to_action6_sprite_override.find({_cur.grfconfig->ident.grfid, _cur.nfo_line}); if (it == _grf_line_to_action6_sprite_override.end()) { /* No preloaded sprite to work with; read the * pseudo sprite content. */ _cur.file->ReadBlock(buf, num); } else { /* Use the preloaded sprite data. */ buf = it->second.data(); GrfMsg(7, "DecodeSpecialSprite: Using preloaded pseudo sprite data"); /* Skip the real (original) content of this action. */ _cur.file->SeekTo(num, SEEK_CUR); } ByteReader br(buf, buf + num); try { uint8_t action = br.ReadByte(); if (action == 0xFF) { GrfMsg(2, "DecodeSpecialSprite: Unexpected data block, skipping"); } else if (action == 0xFE) { GrfMsg(2, "DecodeSpecialSprite: Unexpected import block, skipping"); } else { InvokeGrfActionHandler::Invoke(action, stage, br); } } catch (...) { GrfMsg(1, "DecodeSpecialSprite: Tried to read past end of pseudo-sprite data"); DisableGrf(STR_NEWGRF_ERROR_READ_BOUNDS); } } /** * Load a particular NewGRF from a SpriteFile. * @param config The configuration of the to be loaded NewGRF. * @param stage The loading stage of the NewGRF. * @param file The file to load the GRF data from. */ static void LoadNewGRFFileFromFile(GRFConfig &config, GrfLoadingStage stage, SpriteFile &file) { AutoRestoreBackup cur_file(_cur.file, &file); AutoRestoreBackup cur_config(_cur.grfconfig, &config); Debug(grf, 2, "LoadNewGRFFile: Reading NewGRF-file '{}'", config.filename); uint8_t grf_container_version = file.GetContainerVersion(); if (grf_container_version == 0) { Debug(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format"); return; } if (stage == GLS_INIT || stage == GLS_ACTIVATION) { /* We need the sprite offsets in the init stage for NewGRF sounds * and in the activation stage for real sprites. */ ReadGRFSpriteOffsets(file); } else { /* Skip sprite section offset if present. */ if (grf_container_version >= 2) file.ReadDword(); } if (grf_container_version >= 2) { /* Read compression value. */ uint8_t compression = file.ReadByte(); if (compression != 0) { Debug(grf, 7, "LoadNewGRFFile: Unsupported compression format"); return; } } /* Skip the first sprite; we don't care about how many sprites this * does contain; newest TTDPatches and George's longvehicles don't * neither, apparently. */ uint32_t num = grf_container_version >= 2 ? file.ReadDword() : file.ReadWord(); if (num == 4 && file.ReadByte() == 0xFF) { file.ReadDword(); } else { Debug(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format"); return; } _cur.ClearDataForNextFile(); ReusableBuffer buf; while ((num = (grf_container_version >= 2 ? file.ReadDword() : file.ReadWord())) != 0) { uint8_t type = file.ReadByte(); _cur.nfo_line++; if (type == 0xFF) { if (_cur.skip_sprites == 0) { /* Limit the special sprites to 1 MiB. */ if (num > 1024 * 1024) { GrfMsg(0, "LoadNewGRFFile: Unexpectedly large sprite, disabling"); DisableGrf(STR_NEWGRF_ERROR_UNEXPECTED_SPRITE); break; } DecodeSpecialSprite(buf.Allocate(num), num, stage); /* Stop all processing if we are to skip the remaining sprites */ if (_cur.skip_sprites == -1) break; continue; } else { file.SkipBytes(num); } } else { if (_cur.skip_sprites == 0) { GrfMsg(0, "LoadNewGRFFile: Unexpected sprite, disabling"); DisableGrf(STR_NEWGRF_ERROR_UNEXPECTED_SPRITE); break; } if (grf_container_version >= 2 && type == 0xFD) { /* Reference to data section. Container version >= 2 only. */ file.SkipBytes(num); } else { file.SkipBytes(7); SkipSpriteData(file, type, num - 8); } } if (_cur.skip_sprites > 0) _cur.skip_sprites--; } } /** * Load a particular NewGRF. * @param config The configuration of the to be loaded NewGRF. * @param stage The loading stage of the NewGRF. * @param subdir The sub directory to find the NewGRF in. * @param temporary The NewGRF/sprite file is to be loaded temporarily and should be closed immediately, * contrary to loading the SpriteFile and having it cached by the SpriteCache. */ void LoadNewGRFFile(GRFConfig &config, GrfLoadingStage stage, Subdirectory subdir, bool temporary) { const std::string &filename = config.filename; /* A .grf file is activated only if it was active when the game was * started. If a game is loaded, only its active .grfs will be * reactivated, unless "loadallgraphics on" is used. A .grf file is * considered active if its action 8 has been processed, i.e. its * action 8 hasn't been skipped using an action 7. * * During activation, only actions 0, 1, 2, 3, 4, 5, 7, 8, 9, 0A and 0B are * carried out. All others are ignored, because they only need to be * processed once at initialization. */ if (stage != GLS_FILESCAN && stage != GLS_SAFETYSCAN && stage != GLS_LABELSCAN) { _cur.grffile = GetFileByFilename(filename); if (_cur.grffile == nullptr) UserError("File '{}' lost in cache.\n", filename); if (stage == GLS_RESERVE && config.status != GCS_INITIALISED) return; if (stage == GLS_ACTIVATION && !config.flags.Test(GRFConfigFlag::Reserved)) return; } bool needs_palette_remap = config.palette & GRFP_USE_MASK; if (temporary) { SpriteFile temporarySpriteFile(filename, subdir, needs_palette_remap); LoadNewGRFFileFromFile(config, stage, temporarySpriteFile); } else { LoadNewGRFFileFromFile(config, stage, OpenCachedSpriteFile(filename, subdir, needs_palette_remap)); } } /** * Relocates the old shore sprites at new positions. * * 1. If shore sprites are neither loaded by Action5 nor ActionA, the extra sprites from openttd(w/d).grf are used. (SHORE_REPLACE_ONLY_NEW) * 2. If a newgrf replaces some shore sprites by ActionA. The (maybe also replaced) grass tiles are used for corner shores. (SHORE_REPLACE_ACTION_A) * 3. If a newgrf replaces shore sprites by Action5 any shore replacement by ActionA has no effect. (SHORE_REPLACE_ACTION_5) */ static void ActivateOldShore() { /* Use default graphics, if no shore sprites were loaded. * Should not happen, as the base set's extra grf should include some. */ if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_A; if (_loaded_newgrf_features.shore != SHORE_REPLACE_ACTION_5) { DupSprite(SPR_ORIGINALSHORE_START + 1, SPR_SHORE_BASE + 1); // SLOPE_W DupSprite(SPR_ORIGINALSHORE_START + 2, SPR_SHORE_BASE + 2); // SLOPE_S DupSprite(SPR_ORIGINALSHORE_START + 6, SPR_SHORE_BASE + 3); // SLOPE_SW DupSprite(SPR_ORIGINALSHORE_START + 0, SPR_SHORE_BASE + 4); // SLOPE_E DupSprite(SPR_ORIGINALSHORE_START + 4, SPR_SHORE_BASE + 6); // SLOPE_SE DupSprite(SPR_ORIGINALSHORE_START + 3, SPR_SHORE_BASE + 8); // SLOPE_N DupSprite(SPR_ORIGINALSHORE_START + 7, SPR_SHORE_BASE + 9); // SLOPE_NW DupSprite(SPR_ORIGINALSHORE_START + 5, SPR_SHORE_BASE + 12); // SLOPE_NE } if (_loaded_newgrf_features.shore == SHORE_REPLACE_ACTION_A) { DupSprite(SPR_FLAT_GRASS_TILE + 16, SPR_SHORE_BASE + 0); // SLOPE_STEEP_S DupSprite(SPR_FLAT_GRASS_TILE + 17, SPR_SHORE_BASE + 5); // SLOPE_STEEP_W DupSprite(SPR_FLAT_GRASS_TILE + 7, SPR_SHORE_BASE + 7); // SLOPE_WSE DupSprite(SPR_FLAT_GRASS_TILE + 15, SPR_SHORE_BASE + 10); // SLOPE_STEEP_N DupSprite(SPR_FLAT_GRASS_TILE + 11, SPR_SHORE_BASE + 11); // SLOPE_NWS DupSprite(SPR_FLAT_GRASS_TILE + 13, SPR_SHORE_BASE + 13); // SLOPE_ENW DupSprite(SPR_FLAT_GRASS_TILE + 14, SPR_SHORE_BASE + 14); // SLOPE_SEN DupSprite(SPR_FLAT_GRASS_TILE + 18, SPR_SHORE_BASE + 15); // SLOPE_STEEP_E /* XXX - SLOPE_EW, SLOPE_NS are currently not used. * If they would be used somewhen, then these grass tiles will most like not look as needed */ DupSprite(SPR_FLAT_GRASS_TILE + 5, SPR_SHORE_BASE + 16); // SLOPE_EW DupSprite(SPR_FLAT_GRASS_TILE + 10, SPR_SHORE_BASE + 17); // SLOPE_NS } } /** * Replocate the old tram depot sprites to the new position, if no new ones were loaded. */ static void ActivateOldTramDepot() { if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK) { DupSprite(SPR_ROAD_DEPOT + 0, SPR_TRAMWAY_DEPOT_NO_TRACK + 0); // use road depot graphics for "no tracks" DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 1, SPR_TRAMWAY_DEPOT_NO_TRACK + 1); DupSprite(SPR_ROAD_DEPOT + 2, SPR_TRAMWAY_DEPOT_NO_TRACK + 2); // use road depot graphics for "no tracks" DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 3, SPR_TRAMWAY_DEPOT_NO_TRACK + 3); DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 4, SPR_TRAMWAY_DEPOT_NO_TRACK + 4); DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 5, SPR_TRAMWAY_DEPOT_NO_TRACK + 5); } } /** * Decide whether price base multipliers of grfs shall apply globally or only to the grf specifying them */ static void FinalisePriceBaseMultipliers() { extern const PriceBaseSpec _price_base_specs[]; /** Features, to which '_grf_id_overrides' applies. Currently vehicle features only. */ static const uint32_t override_features = (1 << GSF_TRAINS) | (1 << GSF_ROADVEHICLES) | (1 << GSF_SHIPS) | (1 << GSF_AIRCRAFT); /* Evaluate grf overrides */ int num_grfs = (uint)_grf_files.size(); std::vector grf_overrides(num_grfs, -1); for (int i = 0; i < num_grfs; i++) { GRFFile &source = _grf_files[i]; auto it = _grf_id_overrides.find(source.grfid); if (it == std::end(_grf_id_overrides)) continue; uint32_t override_grfid = it->second; auto dest = std::ranges::find(_grf_files, override_grfid, &GRFFile::grfid); if (dest == std::end(_grf_files)) continue; grf_overrides[i] = static_cast(std::ranges::distance(std::begin(_grf_files), dest)); assert(grf_overrides[i] >= 0); } /* Override features and price base multipliers of earlier loaded grfs */ for (int i = 0; i < num_grfs; i++) { if (grf_overrides[i] < 0 || grf_overrides[i] >= i) continue; GRFFile &source = _grf_files[i]; GRFFile &dest = _grf_files[grf_overrides[i]]; uint32_t features = (source.grf_features | dest.grf_features) & override_features; source.grf_features |= features; dest.grf_features |= features; for (Price p = PR_BEGIN; p < PR_END; p++) { /* No price defined -> nothing to do */ if (!HasBit(features, _price_base_specs[p].grf_feature) || source.price_base_multipliers[p] == INVALID_PRICE_MODIFIER) continue; Debug(grf, 3, "'{}' overrides price base multiplier {} of '{}'", source.filename, p, dest.filename); dest.price_base_multipliers[p] = source.price_base_multipliers[p]; } } /* Propagate features and price base multipliers of afterwards loaded grfs, if none is present yet */ for (int i = num_grfs - 1; i >= 0; i--) { if (grf_overrides[i] < 0 || grf_overrides[i] <= i) continue; GRFFile &source = _grf_files[i]; GRFFile &dest = _grf_files[grf_overrides[i]]; uint32_t features = (source.grf_features | dest.grf_features) & override_features; source.grf_features |= features; dest.grf_features |= features; for (Price p = PR_BEGIN; p < PR_END; p++) { /* Already a price defined -> nothing to do */ if (!HasBit(features, _price_base_specs[p].grf_feature) || dest.price_base_multipliers[p] != INVALID_PRICE_MODIFIER) continue; Debug(grf, 3, "Price base multiplier {} from '{}' propagated to '{}'", p, source.filename, dest.filename); dest.price_base_multipliers[p] = source.price_base_multipliers[p]; } } /* The 'master grf' now have the correct multipliers. Assign them to the 'addon grfs' to make everything consistent. */ for (int i = 0; i < num_grfs; i++) { if (grf_overrides[i] < 0) continue; GRFFile &source = _grf_files[i]; GRFFile &dest = _grf_files[grf_overrides[i]]; uint32_t features = (source.grf_features | dest.grf_features) & override_features; source.grf_features |= features; dest.grf_features |= features; for (Price p = PR_BEGIN; p < PR_END; p++) { if (!HasBit(features, _price_base_specs[p].grf_feature)) continue; if (source.price_base_multipliers[p] != dest.price_base_multipliers[p]) { Debug(grf, 3, "Price base multiplier {} from '{}' propagated to '{}'", p, dest.filename, source.filename); } source.price_base_multipliers[p] = dest.price_base_multipliers[p]; } } /* Apply fallback prices for grf version < 8 */ for (auto &file : _grf_files) { if (file.grf_version >= 8) continue; PriceMultipliers &price_base_multipliers = file.price_base_multipliers; for (Price p = PR_BEGIN; p < PR_END; p++) { Price fallback_price = _price_base_specs[p].fallback_price; if (fallback_price != INVALID_PRICE && price_base_multipliers[p] == INVALID_PRICE_MODIFIER) { /* No price multiplier has been set. * So copy the multiplier from the fallback price, maybe a multiplier was set there. */ price_base_multipliers[p] = price_base_multipliers[fallback_price]; } } } /* Decide local/global scope of price base multipliers */ for (auto &file : _grf_files) { PriceMultipliers &price_base_multipliers = file.price_base_multipliers; for (Price p = PR_BEGIN; p < PR_END; p++) { if (price_base_multipliers[p] == INVALID_PRICE_MODIFIER) { /* No multiplier was set; set it to a neutral value */ price_base_multipliers[p] = 0; } else { if (!HasBit(file.grf_features, _price_base_specs[p].grf_feature)) { /* The grf does not define any objects of the feature, * so it must be a difficulty setting. Apply it globally */ Debug(grf, 3, "'{}' sets global price base multiplier {}", file.filename, p); SetPriceBaseMultiplier(p, price_base_multipliers[p]); price_base_multipliers[p] = 0; } else { Debug(grf, 3, "'{}' sets local price base multiplier {}", file.filename, p); } } } } } template void AddBadgeToSpecs(T &specs, GrfSpecFeature feature, Badge &badge) { for (auto &spec : specs) { if (spec == nullptr) continue; spec->badges.push_back(badge.index); badge.features.Set(feature); } } /** Finish up applying badges to things */ static void FinaliseBadges() { for (const auto &file : _grf_files) { Badge *badge = GetBadgeByLabel(fmt::format("newgrf/{:08x}", std::byteswap(file.grfid))); if (badge == nullptr) continue; for (Engine *e : Engine::Iterate()) { if (e->grf_prop.grffile != &file) continue; e->badges.push_back(badge->index); badge->features.Set(static_cast(GSF_TRAINS + e->type)); } AddBadgeToSpecs(file.stations, GSF_STATIONS, *badge); AddBadgeToSpecs(file.housespec, GSF_HOUSES, *badge); AddBadgeToSpecs(file.industryspec, GSF_INDUSTRIES, *badge); AddBadgeToSpecs(file.indtspec, GSF_INDUSTRYTILES, *badge); AddBadgeToSpecs(file.objectspec, GSF_OBJECTS, *badge); AddBadgeToSpecs(file.airportspec, GSF_AIRPORTS, *badge); AddBadgeToSpecs(file.airtspec, GSF_AIRPORTTILES, *badge); AddBadgeToSpecs(file.roadstops, GSF_ROADSTOPS, *badge); } ApplyBadgeFeaturesToClassBadges(); } extern void InitGRFTownGeneratorNames(); /** Finish loading NewGRFs and execute needed post-processing */ static void AfterLoadGRFs() { /* Cached callback groups are no longer needed. */ ResetCallbacks(true); FinaliseStringMapping(); /* Clear the action 6 override sprites. */ _grf_line_to_action6_sprite_override.clear(); FinaliseBadges(); /* Polish cargoes */ FinaliseCargoArray(); /* Pre-calculate all refit masks after loading GRF files. */ CalculateRefitMasks(); /* Polish engines */ FinaliseEngineArray(); /* Set the actually used Canal properties */ FinaliseCanals(); /* Add all new houses to the house array. */ FinaliseHouseArray(); /* Add all new industries to the industry array. */ FinaliseIndustriesArray(); /* Add all new objects to the object array. */ FinaliseObjectsArray(); InitializeSortedCargoSpecs(); /* Sort the list of industry types. */ SortIndustryTypes(); /* Create dynamic list of industry legends for smallmap_gui.cpp */ BuildIndustriesLegend(); /* Build the routemap legend, based on the available cargos */ BuildLinkStatsLegend(); /* Add all new airports to the airports array. */ FinaliseAirportsArray(); BindAirportSpecs(); /* Update the townname generators list */ InitGRFTownGeneratorNames(); /* Run all queued vehicle list order changes */ CommitVehicleListOrderChanges(); /* Load old shore sprites in new position, if they were replaced by ActionA */ ActivateOldShore(); /* Load old tram depot sprites in new position, if no new ones are present */ ActivateOldTramDepot(); /* Set up custom rail types */ InitRailTypes(); InitRoadTypes(); for (Engine *e : Engine::IterateType(VEH_ROAD)) { if (_gted[e->index].rv_max_speed != 0) { /* Set RV maximum speed from the mph/0.8 unit value */ e->u.road.max_speed = _gted[e->index].rv_max_speed * 4; } RoadTramType rtt = e->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) ? RTT_TRAM : RTT_ROAD; const GRFFile *file = e->GetGRF(); if (file == nullptr || _gted[e->index].roadtramtype == 0) { e->u.road.roadtype = (rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; continue; } /* Remove +1 offset. */ _gted[e->index].roadtramtype--; const std::vector *list = (rtt == RTT_TRAM) ? &file->tramtype_list : &file->roadtype_list; if (_gted[e->index].roadtramtype < list->size()) { RoadTypeLabel rtl = (*list)[_gted[e->index].roadtramtype]; RoadType rt = GetRoadTypeByLabel(rtl); if (rt != INVALID_ROADTYPE && GetRoadTramType(rt) == rtt) { e->u.road.roadtype = rt; continue; } } /* Road type is not available, so disable this engine */ e->info.climates = {}; } for (Engine *e : Engine::IterateType(VEH_TRAIN)) { RailType railtype = GetRailTypeByLabel(_gted[e->index].railtypelabel); if (railtype == INVALID_RAILTYPE) { /* Rail type is not available, so disable this engine */ e->info.climates = {}; } else { e->u.rail.railtype = railtype; e->u.rail.intended_railtype = railtype; } } SetYearEngineAgingStops(); FinalisePriceBaseMultipliers(); /* Deallocate temporary loading data */ _gted.clear(); _grm_sprites.clear(); } /** * Load all the NewGRFs. * @param load_index The offset for the first sprite to add. * @param num_baseset Number of NewGRFs at the front of the list to look up in the baseset dir instead of the newgrf dir. */ void LoadNewGRF(SpriteID load_index, uint num_baseset) { /* In case of networking we need to "sync" the start values * so all NewGRFs are loaded equally. For this we use the * start date of the game and we set the counters, etc. to * 0 so they're the same too. */ TimerGameCalendar::Date date = TimerGameCalendar::date; TimerGameCalendar::Year year = TimerGameCalendar::year; TimerGameCalendar::DateFract date_fract = TimerGameCalendar::date_fract; TimerGameEconomy::Date economy_date = TimerGameEconomy::date; TimerGameEconomy::Year economy_year = TimerGameEconomy::year; TimerGameEconomy::DateFract economy_date_fract = TimerGameEconomy::date_fract; uint64_t tick_counter = TimerGameTick::counter; uint8_t display_opt = _display_opt; if (_networking) { TimerGameCalendar::year = _settings_game.game_creation.starting_year; TimerGameCalendar::date = TimerGameCalendar::ConvertYMDToDate(TimerGameCalendar::year, 0, 1); TimerGameCalendar::date_fract = 0; TimerGameEconomy::year = TimerGameEconomy::Year{_settings_game.game_creation.starting_year.base()}; TimerGameEconomy::date = TimerGameEconomy::ConvertYMDToDate(TimerGameEconomy::year, 0, 1); TimerGameEconomy::date_fract = 0; TimerGameTick::counter = 0; _display_opt = 0; } InitializePatchFlags(); ResetNewGRFData(); /* * Reset the status of all files, so we can 'retry' to load them. * This is needed when one for example rearranges the NewGRFs in-game * and a previously disabled NewGRF becomes usable. If it would not * be reset, the NewGRF would remain disabled even though it should * have been enabled. */ for (const auto &c : _grfconfig) { if (c->status != GCS_NOT_FOUND) c->status = GCS_UNKNOWN; } _cur.spriteid = load_index; /* Load newgrf sprites * in each loading stage, (try to) open each file specified in the config * and load information from it. */ for (GrfLoadingStage stage = GLS_LABELSCAN; stage <= GLS_ACTIVATION; stage++) { /* Set activated grfs back to will-be-activated between reservation- and activation-stage. * This ensures that action7/9 conditions 0x06 - 0x0A work correctly. */ for (const auto &c : _grfconfig) { if (c->status == GCS_ACTIVATED) c->status = GCS_INITIALISED; } if (stage == GLS_RESERVE) { static const std::pair default_grf_overrides[] = { { std::byteswap(0x44442202), std::byteswap(0x44440111) }, // UKRS addons modifies UKRS { std::byteswap(0x6D620402), std::byteswap(0x6D620401) }, // DBSetXL ECS extension modifies DBSetXL { std::byteswap(0x4D656f20), std::byteswap(0x4D656F17) }, // LV4cut modifies LV4 }; for (const auto &grf_override : default_grf_overrides) { SetNewGRFOverride(grf_override.first, grf_override.second); } } uint num_grfs = 0; uint num_non_static = 0; _cur.stage = stage; for (const auto &c : _grfconfig) { if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND) continue; if (stage > GLS_INIT && c->flags.Test(GRFConfigFlag::InitOnly)) continue; Subdirectory subdir = num_grfs < num_baseset ? BASESET_DIR : NEWGRF_DIR; if (!FioCheckFileExists(c->filename, subdir)) { Debug(grf, 0, "NewGRF file is missing '{}'; disabling", c->filename); c->status = GCS_NOT_FOUND; continue; } if (stage == GLS_LABELSCAN) InitNewGRFFile(*c); if (!c->flags.Test(GRFConfigFlag::Static) && !c->flags.Test(GRFConfigFlag::System)) { if (num_non_static == NETWORK_MAX_GRF_COUNT) { Debug(grf, 0, "'{}' is not loaded as the maximum number of non-static GRFs has been reached", c->filename); c->status = GCS_DISABLED; c->error = {STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED}; continue; } num_non_static++; } num_grfs++; LoadNewGRFFile(*c, stage, subdir, false); if (stage == GLS_RESERVE) { c->flags.Set(GRFConfigFlag::Reserved); } else if (stage == GLS_ACTIVATION) { c->flags.Reset(GRFConfigFlag::Reserved); assert(GetFileByGRFID(c->ident.grfid) == _cur.grffile); ClearTemporaryNewGRFData(_cur.grffile); BuildCargoTranslationMap(); Debug(sprite, 2, "LoadNewGRF: Currently {} sprites are loaded", _cur.spriteid); } else if (stage == GLS_INIT && c->flags.Test(GRFConfigFlag::InitOnly)) { /* We're not going to activate this, so free whatever data we allocated */ ClearTemporaryNewGRFData(_cur.grffile); } } } /* Pseudo sprite processing is finished; free temporary stuff */ _cur.ClearDataForNextFile(); /* Call any functions that should be run after GRFs have been loaded. */ AfterLoadGRFs(); /* Now revert back to the original situation */ TimerGameCalendar::year = year; TimerGameCalendar::date = date; TimerGameCalendar::date_fract = date_fract; TimerGameEconomy::year = economy_year; TimerGameEconomy::date = economy_date; TimerGameEconomy::date_fract = economy_date_fract; TimerGameTick::counter = tick_counter; _display_opt = display_opt; }