Version: Committing version data for tag: jgrpp-0.64.1
[openttd-jgr.git] / src / newgrf.cpp
blob40b34904528f68194e9189b49083e14cb66b2a1a
1 /*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <http://www.gnu.org/licenses/>.
6 */
8 /** @file newgrf.cpp Base of all NewGRF support. */
10 #include "stdafx.h"
12 #include "newgrf_internal.h"
13 #include "core/backup_type.hpp"
14 #include "core/container_func.hpp"
15 #include "core/bit_cast.hpp"
16 #include "debug.h"
17 #include "fileio_func.h"
18 #include "engine_func.h"
19 #include "engine_base.h"
20 #include "engine_override.h"
21 #include "bridge.h"
22 #include "town.h"
23 #include "newgrf_engine.h"
24 #include "newgrf_text.h"
25 #include "fontcache.h"
26 #include "currency.h"
27 #include "landscape.h"
28 #include "newgrf_cargo.h"
29 #include "newgrf_house.h"
30 #include "newgrf_sound.h"
31 #include "newgrf_station.h"
32 #include "industrytype.h"
33 #include "industry_map.h"
34 #include "newgrf_act5.h"
35 #include "newgrf_canal.h"
36 #include "newgrf_townname.h"
37 #include "newgrf_industries.h"
38 #include "newgrf_airporttiles.h"
39 #include "newgrf_airport.h"
40 #include "newgrf_object.h"
41 #include "newgrf_newsignals.h"
42 #include "newgrf_newlandscape.h"
43 #include "newgrf_extension.h"
44 #include "rev.h"
45 #include "fios.h"
46 #include "strings_func.h"
47 #include "date_func.h"
48 #include "string_func.h"
49 #include "network/core/config.h"
50 #include "smallmap_gui.h"
51 #include "genworld.h"
52 #include "error.h"
53 #include "error_func.h"
54 #include "vehicle_func.h"
55 #include "language.h"
56 #include "vehicle_base.h"
57 #include "road.h"
58 #include "newgrf_roadstop.h"
59 #include "debug_settings.h"
61 #include "table/strings.h"
62 #include "table/build_industry.h"
64 #include "3rdparty/cpp-btree/btree_map.h"
65 #include "3rdparty/robin_hood/robin_hood.h"
67 #include "safeguards.h"
69 /* TTDPatch extended GRF format codec
70 * (c) Petr Baudis 2004 (GPL'd)
71 * Changes by Florian octo Forster are (c) by the OpenTTD development team.
73 * Contains portions of documentation by TTDPatch team.
74 * Thanks especially to Josef Drexler for the documentation as well as a lot
75 * of help at #tycoon. Also thanks to Michael Blunck for his GRF files which
76 * served as subject to the initial testing of this codec. */
78 /** List of all loaded GRF files */
79 static std::vector<GRFFile *> _grf_files;
80 static robin_hood::unordered_map<uint32_t, GRFFile *> _grf_file_map;
82 const std::vector<GRFFile *> &GetAllGRFFiles()
84 return _grf_files;
87 static robin_hood::unordered_map<uint16_t, const CallbackResultSpriteGroup *> _callback_result_cache;
89 /** Miscellaneous GRF features, set by Action 0x0D, parameter 0x9E */
90 uint8_t _misc_grf_features = 0;
92 /** 32 * 8 = 256 flags. Apparently TTDPatch uses this many.. */
93 static uint32_t _ttdpatch_flags[8];
94 static uint32_t _observed_ttdpatch_flags[8];
96 /** Indicates which are the newgrf features currently loaded ingame */
97 GRFLoadedFeatures _loaded_newgrf_features;
99 GrfProcessingState _cur;
103 * Helper to check whether an image index is valid for a particular NewGRF vehicle.
104 * @tparam T The type of vehicle.
105 * @param image_index The image index to check.
106 * @return True iff the image index is valid, or 0xFD (use new graphics).
108 template <VehicleType T>
109 static inline bool IsValidNewGRFImageIndex(uint8_t image_index)
111 return image_index == 0xFD || IsValidImageIndex<T>(image_index);
114 class OTTDByteReaderSignal { };
116 /** Class to read from a NewGRF file */
117 class ByteReader {
118 protected:
119 uint8_t *data;
120 uint8_t *end;
122 public:
123 ByteReader(uint8_t *data, uint8_t *end) : data(data), end(end) { }
125 inline uint8_t *ReadBytes(size_t size)
127 if (data + size >= end) {
128 /* Put data at the end, as would happen if every byte had been individually read. */
129 data = end;
130 throw OTTDByteReaderSignal();
133 uint8_t *ret = data;
134 data += size;
135 return ret;
138 inline uint8_t ReadByte()
140 if (data < end) return *(data)++;
141 throw OTTDByteReaderSignal();
144 uint16_t ReadWord()
146 uint16_t val = ReadByte();
147 return val | (ReadByte() << 8);
150 uint16_t ReadExtendedByte()
152 uint16_t val = ReadByte();
153 return val == 0xFF ? ReadWord() : val;
156 uint32_t ReadDWord()
158 uint32_t val = ReadWord();
159 return val | (ReadWord() << 16);
162 uint32_t PeekDWord()
164 AutoRestoreBackup backup(this->data, this->data);
165 return this->ReadDWord();
168 uint32_t ReadVarSize(uint8_t size)
170 switch (size) {
171 case 1: return ReadByte();
172 case 2: return ReadWord();
173 case 4: return ReadDWord();
174 default:
175 NOT_REACHED();
176 return 0;
180 std::string_view ReadString()
182 char *string = reinterpret_cast<char *>(data);
183 size_t string_length = ttd_strnlen(string, Remaining());
185 /* Skip past the terminating NUL byte if it is present, but not more than remaining. */
186 Skip(std::min(string_length + 1, Remaining()));
188 return std::string_view(string, string_length);
191 inline size_t Remaining() const
193 return end - data;
196 inline bool HasData(size_t count = 1) const
198 return data + count <= end;
201 inline uint8_t *Data()
203 return data;
206 inline void Skip(size_t len)
208 data += len;
209 /* It is valid to move the buffer to exactly the end of the data,
210 * as there may not be any more data read. */
211 if (data > end) throw OTTDByteReaderSignal();
214 inline void ResetReadPosition(uint8_t *pos)
216 data = pos;
220 typedef void (*SpecialSpriteHandler)(ByteReader &buf);
222 /** The maximum amount of stations a single GRF is allowed to add */
223 static const uint NUM_STATIONS_PER_GRF = UINT16_MAX - 1;
225 /** Temporary engine data used when loading only */
226 struct GRFTempEngineData {
227 /** Summary state of refittability properties */
228 enum Refittability {
229 UNSET = 0, ///< No properties assigned. Default refit masks shall be activated.
230 EMPTY, ///< GRF defined vehicle as not-refittable. The vehicle shall only carry the default cargo.
231 NONEMPTY, ///< GRF defined the vehicle as refittable. If the refitmask is empty after translation (cargotypes not available), disable the vehicle.
234 CargoClasses cargo_allowed; ///< Bitmask of cargo classes that are allowed as a refit.
235 CargoClasses cargo_allowed_required; ///< Bitmask of cargo classes that are required to be all present to allow a cargo as a refit.
236 CargoClasses cargo_disallowed; ///< Bitmask of cargo classes that are disallowed as a refit.
237 RailTypeLabel railtypelabel;
238 uint8_t roadtramtype;
239 const GRFFile *defaultcargo_grf; ///< GRF defining the cargo translation table to use if the default cargo is the 'first refittable'.
240 Refittability refittability; ///< Did the newgrf set any refittability property? If not, default refittability will be applied.
241 uint8_t rv_max_speed; ///< Temporary storage of RV prop 15, maximum speed in mph/0.8
242 CargoTypes ctt_include_mask; ///< Cargo types always included in the refit mask.
243 CargoTypes ctt_exclude_mask; ///< Cargo types always excluded from the refit mask.
246 * Update the summary refittability on setting a refittability property.
247 * @param non_empty true if the GRF sets the vehicle to be refittable.
249 void UpdateRefittability(bool non_empty)
251 if (non_empty) {
252 this->refittability = NONEMPTY;
253 } else if (this->refittability == UNSET) {
254 this->refittability = EMPTY;
259 static std::vector<GRFTempEngineData> _gted; ///< Temporary engine data used during NewGRF loading
262 * Contains the GRF ID of the owner of a vehicle if it has been reserved.
263 * GRM for vehicles is only used if dynamic engine allocation is disabled,
264 * so 256 is the number of original engines. */
265 static uint32_t _grm_engines[256];
267 /** Contains the GRF ID of the owner of a cargo if it has been reserved */
268 static uint32_t _grm_cargoes[NUM_CARGO * 2];
270 struct GRFLocation {
271 uint32_t grfid;
272 uint32_t nfoline;
274 GRFLocation() { }
275 GRFLocation(uint32_t grfid, uint32_t nfoline) : grfid(grfid), nfoline(nfoline) { }
277 bool operator<(const GRFLocation &other) const
279 return this->grfid < other.grfid || (this->grfid == other.grfid && this->nfoline < other.nfoline);
282 bool operator == (const GRFLocation &other) const
284 return this->grfid == other.grfid && this->nfoline == other.nfoline;
288 static btree::btree_map<GRFLocation, std::pair<SpriteID, uint16_t>> _grm_sprites;
289 typedef btree::btree_map<GRFLocation, std::unique_ptr<uint8_t[]>> GRFLineToSpriteOverride;
290 static GRFLineToSpriteOverride _grf_line_to_action6_sprite_override;
291 static bool _action6_override_active = false;
294 * Debug() function dedicated to newGRF debugging messages
295 * Function is essentially the same as Debug(grf, severity, ...) with the
296 * addition of file:line information when parsing grf files.
297 * NOTE: for the above reason(s) GrfMsg() should ONLY be used for
298 * loading/parsing grf files, not for runtime debug messages as there
299 * is no file information available during that time.
300 * @param severity debugging severity level, see debug.h
301 * @param msg the message
302 * @param msg format arguments
304 void GrfInfoVFmt(int severity, fmt::string_view msg, fmt::format_args args)
306 format_buffer buf;
307 buf.format("[{}:{}] ", _cur.grfconfig->filename, _cur.nfo_line);
308 buf.vformat(msg, args);
309 debug_print(DebugLevelID::grf, severity, buf);
313 * Obtain a NewGRF file by its grfID
314 * @param grfid The grfID to obtain the file for
315 * @return The file.
317 GRFFile *GetFileByGRFID(uint32_t grfid)
319 auto iter = _grf_file_map.find(grfid);
320 if (iter != _grf_file_map.end()) return iter->second;
321 return nullptr;
325 * Obtain a NewGRF file by its grfID, expect it to usually be the current GRF's grfID
326 * @param grfid The grfID to obtain the file for
327 * @return The file.
329 GRFFile *GetFileByGRFIDExpectCurrent(uint32_t grfid)
331 if (_cur.grffile->grfid == grfid) return _cur.grffile;
332 return GetFileByGRFID(grfid);
336 * Obtain a NewGRF file by its filename
337 * @param filename The filename to obtain the file for.
338 * @return The file.
340 static GRFFile *GetFileByFilename(const std::string &filename)
342 for (GRFFile * const file : _grf_files) {
343 if (file->filename == filename) return file;
345 return nullptr;
348 /** Reset all NewGRFData that was used only while processing data */
349 static void ClearTemporaryNewGRFData(GRFFile *gf)
351 gf->labels.clear();
355 * Disable a GRF
356 * @param message Error message or STR_NULL.
357 * @param config GRFConfig to disable, nullptr for current.
358 * @return Error message of the GRF for further customisation.
360 static GRFError *DisableGrf(StringID message = STR_NULL, GRFConfig *config = nullptr)
362 GRFFile *file;
363 if (config != nullptr) {
364 file = GetFileByGRFID(config->ident.grfid);
365 } else {
366 config = _cur.grfconfig;
367 file = _cur.grffile;
370 config->status = GCS_DISABLED;
371 if (file != nullptr) ClearTemporaryNewGRFData(file);
372 if (config == _cur.grfconfig) _cur.skip_sprites = -1;
374 if (message == STR_NULL) return nullptr;
376 config->error = {STR_NEWGRF_ERROR_MSG_FATAL, message};
377 if (config == _cur.grfconfig) config->error->param_value[0] = _cur.nfo_line;
378 return &config->error.value();
381 using StringIDMappingHandler = void(*)(StringID, uintptr_t);
384 * Information for mapping static StringIDs.
386 struct StringIDMapping {
387 const GRFFile *grf; ///< Source NewGRF.
388 StringID source; ///< Source StringID (GRF local).
389 StringIDMappingHandler func; ///< Function for mapping result.
390 uintptr_t func_data; ///< Data for func.
392 StringIDMapping(const GRFFile *grf, StringID source, uintptr_t func_data, StringIDMappingHandler func) : grf(grf), source(source), func(func), func_data(func_data) { }
395 /** Strings to be mapped during load. */
396 static std::vector<StringIDMapping> _string_to_grf_mapping;
399 * Record a static StringID for getting translated later.
400 * @param source Source StringID (GRF local).
401 * @param target Destination for the mapping result.
403 static void AddStringForMapping(StringID source, StringID *target)
405 *target = STR_UNDEFINED;
406 _string_to_grf_mapping.emplace_back(_cur.grffile, source, reinterpret_cast<uintptr_t>(target), nullptr);
410 * Record a static StringID for getting translated later.
411 * @param source Source StringID (GRF local).
412 * @param data Arbitrary data (e.g pointer), must fit into a uintptr_t.
413 * @param func Function to call to set the mapping result.
415 template <typename T, typename F>
416 static void AddStringForMapping(StringID source, T data, F func)
418 static_assert(sizeof(T) <= sizeof(uintptr_t));
420 func(STR_UNDEFINED, data);
422 _string_to_grf_mapping.emplace_back(_cur.grffile, source, bit_cast_to_storage<uintptr_t>(data), [](StringID str, uintptr_t func_data) {
423 F handler;
424 handler(str, bit_cast_from_storage<T>(func_data));
429 * Perform a mapping from TTDPatch's string IDs to OpenTTD's
430 * string IDs, but only for the ones we are aware off; the rest
431 * like likely unused and will show a warning.
432 * @param str the string ID to convert
433 * @return the converted string ID
435 static StringID TTDPStringIDToOTTDStringIDMapping(StringID str)
437 /* StringID table for TextIDs 0x4E->0x6D */
438 static const StringID units_volume[] = {
439 STR_ITEMS, STR_PASSENGERS, STR_TONS, STR_BAGS,
440 STR_LITERS, STR_ITEMS, STR_CRATES, STR_TONS,
441 STR_TONS, STR_TONS, STR_TONS, STR_BAGS,
442 STR_TONS, STR_TONS, STR_TONS, STR_BAGS,
443 STR_TONS, STR_TONS, STR_BAGS, STR_LITERS,
444 STR_TONS, STR_LITERS, STR_TONS, STR_ITEMS,
445 STR_BAGS, STR_LITERS, STR_TONS, STR_ITEMS,
446 STR_TONS, STR_ITEMS, STR_LITERS, STR_ITEMS
449 /* A string straight from a NewGRF; this was already translated by MapGRFStringID(). */
450 assert(!IsInsideMM(str, 0xD000, 0xD7FF));
452 #define TEXTID_TO_STRINGID(begin, end, stringid, stringend) \
453 static_assert(stringend - stringid == end - begin); \
454 if (str >= begin && str <= end) return str + (stringid - begin)
456 /* We have some changes in our cargo strings, resulting in some missing. */
457 TEXTID_TO_STRINGID(0x000E, 0x002D, STR_CARGO_PLURAL_NOTHING, STR_CARGO_PLURAL_FIZZY_DRINKS);
458 TEXTID_TO_STRINGID(0x002E, 0x004D, STR_CARGO_SINGULAR_NOTHING, STR_CARGO_SINGULAR_FIZZY_DRINK);
459 if (str >= 0x004E && str <= 0x006D) return units_volume[str - 0x004E];
460 TEXTID_TO_STRINGID(0x006E, 0x008D, STR_QUANTITY_NOTHING, STR_QUANTITY_FIZZY_DRINKS);
461 TEXTID_TO_STRINGID(0x008E, 0x00AD, STR_ABBREV_NOTHING, STR_ABBREV_FIZZY_DRINKS);
462 TEXTID_TO_STRINGID(0x00D1, 0x00E0, STR_COLOUR_DARK_BLUE, STR_COLOUR_WHITE);
464 /* Map building names according to our lang file changes. There are several
465 * ranges of house ids, all of which need to be remapped to allow newgrfs
466 * to use original house names. */
467 TEXTID_TO_STRINGID(0x200F, 0x201F, STR_TOWN_BUILDING_NAME_TALL_OFFICE_BLOCK_1, STR_TOWN_BUILDING_NAME_OLD_HOUSES_1);
468 TEXTID_TO_STRINGID(0x2036, 0x2041, STR_TOWN_BUILDING_NAME_COTTAGES_1, STR_TOWN_BUILDING_NAME_SHOPPING_MALL_1);
469 TEXTID_TO_STRINGID(0x2059, 0x205C, STR_TOWN_BUILDING_NAME_IGLOO_1, STR_TOWN_BUILDING_NAME_PIGGY_BANK_1);
471 /* Same thing for industries */
472 TEXTID_TO_STRINGID(0x4802, 0x4826, STR_INDUSTRY_NAME_COAL_MINE, STR_INDUSTRY_NAME_SUGAR_MINE);
473 TEXTID_TO_STRINGID(0x482D, 0x482E, STR_NEWS_INDUSTRY_CONSTRUCTION, STR_NEWS_INDUSTRY_PLANTED);
474 TEXTID_TO_STRINGID(0x4832, 0x4834, STR_NEWS_INDUSTRY_CLOSURE_GENERAL, STR_NEWS_INDUSTRY_CLOSURE_LACK_OF_TREES);
475 TEXTID_TO_STRINGID(0x4835, 0x4838, STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_GENERAL, STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_FARM);
476 TEXTID_TO_STRINGID(0x4839, 0x483A, STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_GENERAL, STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_FARM);
478 switch (str) {
479 case 0x4830: return STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY;
480 case 0x4831: return STR_ERROR_FOREST_CAN_ONLY_BE_PLANTED;
481 case 0x483B: return STR_ERROR_CAN_ONLY_BE_POSITIONED;
483 #undef TEXTID_TO_STRINGID
485 if (str == STR_NULL) return STR_EMPTY;
487 Debug(grf, 0, "Unknown StringID 0x{:04X} remapped to STR_EMPTY. Please open a Feature Request if you need it", str);
489 return STR_EMPTY;
493 * Used when setting an object's property to map to the GRF's strings
494 * while taking in consideration the "drift" between TTDPatch string system and OpenTTD's one
495 * @param grfid Id of the grf file.
496 * @param str StringID that we want to have the equivalent in OoenTTD.
497 * @return The properly adjusted StringID.
499 template <typename T>
500 StringID MapGRFStringIDCommon(T grfid, StringID str)
502 if (IsInsideMM(str, 0xD800, 0x10000)) {
503 /* General text provided by NewGRF.
504 * In the specs this is called the 0xDCxx range (misc persistent texts),
505 * but we meanwhile extended the range to 0xD800-0xFFFF.
506 * Note: We are not involved in the "persistent" business, since we do not store
507 * any NewGRF strings in savegames. */
508 return GetGRFStringID(grfid, str);
509 } else if (IsInsideMM(str, 0xD000, 0xD800)) {
510 /* Callback text provided by NewGRF.
511 * In the specs this is called the 0xD0xx range (misc graphics texts).
512 * These texts can be returned by various callbacks.
514 * Due to how TTDP implements the GRF-local- to global-textid translation
515 * texts included via 0x80 or 0x81 control codes have to add 0x400 to the textid.
516 * We do not care about that difference and just mask out the 0x400 bit.
518 str &= ~0x400;
519 return GetGRFStringID(grfid, str);
520 } else {
521 /* The NewGRF wants to include/reference an original TTD string.
522 * Try our best to find an equivalent one. */
523 return TTDPStringIDToOTTDStringIDMapping(str);
527 StringID MapGRFStringID(uint32_t grfid, StringID str)
529 return MapGRFStringIDCommon(grfid, str);
532 /* This form should be preferred over the uint32_t grfid form, to avoid redundant GRFID to GRF lookups */
533 StringID MapGRFStringID(const GRFFile *grf, StringID str)
535 return MapGRFStringIDCommon(grf, str);
538 static robin_hood::unordered_flat_map<uint32_t, uint32_t> _grf_id_overrides;
541 * Set the override for a NewGRF
542 * @param source_grfid The grfID which wants to override another NewGRF.
543 * @param target_grfid The grfID which is being overridden.
545 static void SetNewGRFOverride(uint32_t source_grfid, uint32_t target_grfid)
547 if (target_grfid == 0) {
548 _grf_id_overrides.erase(source_grfid);
549 GrfMsg(5, "SetNewGRFOverride: Removed override of 0x{:X}", BSWAP32(source_grfid));
550 } else {
551 _grf_id_overrides[source_grfid] = target_grfid;
552 GrfMsg(5, "SetNewGRFOverride: Added override of 0x{:X} to 0x{:X}", BSWAP32(source_grfid), BSWAP32(target_grfid));
557 * Get overridden GRF for current GRF if present.
558 * @return Overridden GRFFile if present, or nullptr.
560 static GRFFile *GetCurrentGRFOverride()
562 auto found = _grf_id_overrides.find(_cur.grffile->grfid);
563 if (found != std::end(_grf_id_overrides)) {
564 GRFFile *grffile = GetFileByGRFID(found->second);
565 if (grffile != nullptr) return grffile;
567 return nullptr;
571 * Returns the engine associated to a certain internal_id, resp. allocates it.
572 * @param file NewGRF that wants to change the engine.
573 * @param type Vehicle type.
574 * @param internal_id Engine ID inside the NewGRF.
575 * @param static_access If the engine is not present, return nullptr instead of allocating a new engine. (Used for static Action 0x04).
576 * @return The requested engine.
578 static Engine *GetNewEngine(const GRFFile *file, VehicleType type, uint16_t internal_id, bool static_access = false)
580 /* Hack for add-on GRFs that need to modify another GRF's engines. This lets
581 * them use the same engine slots. */
582 uint32_t scope_grfid = INVALID_GRFID; // If not using dynamic_engines, all newgrfs share their ID range
583 if (_settings_game.vehicle.dynamic_engines) {
584 /* If dynamic_engies is enabled, there can be multiple independent ID ranges. */
585 scope_grfid = file->grfid;
586 if (auto it = _grf_id_overrides.find(file->grfid); it != std::end(_grf_id_overrides)) {
587 scope_grfid = it->second;
588 const GRFFile *grf_match = GetFileByGRFID(scope_grfid);
589 if (grf_match == nullptr) {
590 GrfMsg(5, "Tried mapping from GRFID {:x} to {:x} but target is not loaded", BSWAP32(file->grfid), BSWAP32(scope_grfid));
591 } else {
592 GrfMsg(5, "Mapping from GRFID {:x} to {:x}", BSWAP32(file->grfid), BSWAP32(scope_grfid));
596 /* Check if the engine is registered in the override manager */
597 EngineID engine = _engine_mngr.GetID(type, internal_id, scope_grfid);
598 if (engine != INVALID_ENGINE) {
599 Engine *e = Engine::Get(engine);
600 if (!e->grf_prop.HasGrfFile()) {
601 e->grf_prop.grfid = file->grfid;
602 e->grf_prop.grffile = file;
604 return e;
608 /* Check if there is an unreserved slot */
609 EngineID engine = _engine_mngr.GetID(type, internal_id, INVALID_GRFID);
610 if (engine != INVALID_ENGINE) {
611 Engine *e = Engine::Get(engine);
613 if (!e->grf_prop.HasGrfFile()) {
614 e->grf_prop.grfid = file->grfid;
615 e->grf_prop.grffile = file;
616 GrfMsg(5, "Replaced engine at index {} for GRFID {:x}, type {}, index {}", e->index, BSWAP32(file->grfid), type, internal_id);
619 /* Reserve the engine slot */
620 if (!static_access) {
621 _engine_mngr.RemoveFromIndex(engine);
622 EngineIDMapping &eid = _engine_mngr.mappings[engine];
623 eid.grfid = scope_grfid; // Note: this is INVALID_GRFID if dynamic_engines is disabled, so no reservation
624 _engine_mngr.AddToIndex(engine);
627 return e;
630 if (static_access) return nullptr;
632 if (!Engine::CanAllocateItem()) {
633 GrfMsg(0, "Can't allocate any more engines");
634 return nullptr;
637 size_t engine_pool_size = Engine::GetPoolSize();
639 /* ... it's not, so create a new one based off an existing engine */
640 Engine *e = new Engine(type, internal_id);
641 e->grf_prop.grfid = file->grfid;
642 e->grf_prop.grffile = file;
644 /* Reserve the engine slot */
645 assert(_engine_mngr.mappings.size() == e->index);
646 _engine_mngr.mappings.push_back({
647 scope_grfid, // Note: this is INVALID_GRFID if dynamic_engines is disabled, so no reservation
648 internal_id,
649 type,
650 std::min<uint8_t>(internal_id, _engine_counts[type]) // substitute_id == _engine_counts[subtype] means "no substitute"
652 _engine_mngr.AddToIndex(e->index);
654 if (engine_pool_size != Engine::GetPoolSize()) {
655 /* Resize temporary engine data ... */
656 _gted.resize(Engine::GetPoolSize());
658 if (type == VEH_TRAIN) {
659 _gted[e->index].railtypelabel = GetRailTypeInfo(e->u.rail.railtype)->label;
662 GrfMsg(5, "Created new engine at index {} for GRFID {:x}, type {}, index {}", e->index, BSWAP32(file->grfid), type, internal_id);
664 return e;
668 * Return the ID of a new engine
669 * @param file The NewGRF file providing the engine.
670 * @param type The Vehicle type.
671 * @param internal_id NewGRF-internal ID of the engine.
672 * @return The new EngineID.
673 * @note depending on the dynamic_engine setting and a possible override
674 * property the grfID may be unique or overwriting or partially re-defining
675 * properties of an existing engine.
677 EngineID GetNewEngineID(const GRFFile *file, VehicleType type, uint16_t internal_id)
679 uint32_t scope_grfid = INVALID_GRFID; // If not using dynamic_engines, all newgrfs share their ID range
680 if (_settings_game.vehicle.dynamic_engines) {
681 scope_grfid = file->grfid;
682 if (auto it = _grf_id_overrides.find(file->grfid); it != std::end(_grf_id_overrides)) {
683 scope_grfid = it->second;
687 return _engine_mngr.GetID(type, internal_id, scope_grfid);
691 * Map the colour modifiers of TTDPatch to those that Open is using.
692 * @param grf_sprite Pointer to the structure been modified.
694 static void MapSpriteMappingRecolour(PalSpriteID *grf_sprite)
696 if (HasBit(grf_sprite->pal, 14)) {
697 ClrBit(grf_sprite->pal, 14);
698 SetBit(grf_sprite->sprite, SPRITE_MODIFIER_OPAQUE);
701 if (HasBit(grf_sprite->sprite, 14)) {
702 ClrBit(grf_sprite->sprite, 14);
703 SetBit(grf_sprite->sprite, PALETTE_MODIFIER_TRANSPARENT);
706 if (HasBit(grf_sprite->sprite, 15)) {
707 ClrBit(grf_sprite->sprite, 15);
708 SetBit(grf_sprite->sprite, PALETTE_MODIFIER_COLOUR);
713 * Read a sprite and a palette from the GRF and convert them into a format
714 * suitable to OpenTTD.
715 * @param buf Input stream.
716 * @param read_flags Whether to read TileLayoutFlags.
717 * @param invert_action1_flag Set to true, if palette bit 15 means 'not from action 1'.
718 * @param use_cur_spritesets Whether to use currently referenceable action 1 sets.
719 * @param feature GrfSpecFeature to use spritesets from.
720 * @param[out] grf_sprite Read sprite and palette.
721 * @param[out] max_sprite_offset Optionally returns the number of sprites in the spriteset of the sprite. (0 if no spritset)
722 * @param[out] max_palette_offset Optionally returns the number of sprites in the spriteset of the palette. (0 if no spritset)
723 * @return Read TileLayoutFlags.
725 static TileLayoutFlags ReadSpriteLayoutSprite(ByteReader &buf, bool read_flags, bool invert_action1_flag, bool use_cur_spritesets, int feature, PalSpriteID *grf_sprite, uint16_t *max_sprite_offset = nullptr, uint16_t *max_palette_offset = nullptr)
727 grf_sprite->sprite = buf.ReadWord();
728 grf_sprite->pal = buf.ReadWord();
729 TileLayoutFlags flags = read_flags ? (TileLayoutFlags)buf.ReadWord() : TLF_NOTHING;
731 MapSpriteMappingRecolour(grf_sprite);
733 bool custom_sprite = HasBit(grf_sprite->pal, 15) != invert_action1_flag;
734 ClrBit(grf_sprite->pal, 15);
736 if (custom_sprite) {
737 /* Use sprite from Action 1 */
738 uint index = GB(grf_sprite->sprite, 0, 14);
739 SpriteSetInfo sprite_set_info;
740 if (use_cur_spritesets) sprite_set_info = _cur.GetSpriteSetInfo(feature, index);
741 if (use_cur_spritesets && (!sprite_set_info.IsValid() || sprite_set_info.GetNumEnts() == 0)) {
742 GrfMsg(1, "ReadSpriteLayoutSprite: Spritelayout uses undefined custom spriteset {}", index);
743 grf_sprite->sprite = SPR_IMG_QUERY;
744 grf_sprite->pal = PAL_NONE;
745 } else {
746 SpriteID sprite = use_cur_spritesets ? sprite_set_info.GetSprite() : index;
747 if (max_sprite_offset != nullptr) *max_sprite_offset = use_cur_spritesets ? sprite_set_info.GetNumEnts() : UINT16_MAX;
748 SB(grf_sprite->sprite, 0, SPRITE_WIDTH, sprite);
749 SetBit(grf_sprite->sprite, SPRITE_MODIFIER_CUSTOM_SPRITE);
751 } else if ((flags & TLF_SPRITE_VAR10) && !(flags & TLF_SPRITE_REG_FLAGS)) {
752 GrfMsg(1, "ReadSpriteLayoutSprite: Spritelayout specifies var10 value for non-action-1 sprite");
753 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
754 return flags;
757 if (flags & TLF_CUSTOM_PALETTE) {
758 /* Use palette from Action 1 */
759 uint index = GB(grf_sprite->pal, 0, 14);
760 SpriteSetInfo sprite_set_info;
761 if (use_cur_spritesets) sprite_set_info = _cur.GetSpriteSetInfo(feature, index);
762 if (use_cur_spritesets && (!sprite_set_info.IsValid() || sprite_set_info.GetNumEnts() == 0)) {
763 GrfMsg(1, "ReadSpriteLayoutSprite: Spritelayout uses undefined custom spriteset {} for 'palette'", index);
764 grf_sprite->pal = PAL_NONE;
765 } else {
766 SpriteID sprite = use_cur_spritesets ? sprite_set_info.GetSprite() : index;
767 if (max_palette_offset != nullptr) *max_palette_offset = use_cur_spritesets ? sprite_set_info.GetNumEnts() : UINT16_MAX;
768 SB(grf_sprite->pal, 0, SPRITE_WIDTH, sprite);
769 SetBit(grf_sprite->pal, SPRITE_MODIFIER_CUSTOM_SPRITE);
771 } else if ((flags & TLF_PALETTE_VAR10) && !(flags & TLF_PALETTE_REG_FLAGS)) {
772 GrfMsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 value for non-action-1 palette");
773 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
774 return flags;
777 return flags;
781 * Preprocess the TileLayoutFlags and read register modifiers from the GRF.
782 * @param buf Input stream.
783 * @param flags TileLayoutFlags to process.
784 * @param is_parent Whether the sprite is a parentsprite with a bounding box.
785 * @param dts Sprite layout to insert data into.
786 * @param index Sprite index to process; 0 for ground sprite.
788 static void ReadSpriteLayoutRegisters(ByteReader &buf, TileLayoutFlags flags, bool is_parent, NewGRFSpriteLayout *dts, uint index)
790 if (!(flags & TLF_DRAWING_FLAGS)) return;
792 if (dts->registers == nullptr) dts->AllocateRegisters();
793 TileLayoutRegisters &regs = const_cast<TileLayoutRegisters&>(dts->registers[index]);
794 regs.flags = flags & TLF_DRAWING_FLAGS;
796 if (flags & TLF_DODRAW) regs.dodraw = buf.ReadByte();
797 if (flags & TLF_SPRITE) regs.sprite = buf.ReadByte();
798 if (flags & TLF_PALETTE) regs.palette = buf.ReadByte();
800 if (is_parent) {
801 if (flags & TLF_BB_XY_OFFSET) {
802 regs.delta.parent[0] = buf.ReadByte();
803 regs.delta.parent[1] = buf.ReadByte();
805 if (flags & TLF_BB_Z_OFFSET) regs.delta.parent[2] = buf.ReadByte();
806 } else {
807 if (flags & TLF_CHILD_X_OFFSET) regs.delta.child[0] = buf.ReadByte();
808 if (flags & TLF_CHILD_Y_OFFSET) regs.delta.child[1] = buf.ReadByte();
811 if (flags & TLF_SPRITE_VAR10) {
812 regs.sprite_var10 = buf.ReadByte();
813 if (regs.sprite_var10 > TLR_MAX_VAR10) {
814 GrfMsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 ({}) exceeding the maximal allowed value {}", regs.sprite_var10, TLR_MAX_VAR10);
815 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
816 return;
820 if (flags & TLF_PALETTE_VAR10) {
821 regs.palette_var10 = buf.ReadByte();
822 if (regs.palette_var10 > TLR_MAX_VAR10) {
823 GrfMsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 ({}) exceeding the maximal allowed value {}", regs.palette_var10, TLR_MAX_VAR10);
824 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
825 return;
831 * Read a spritelayout from the GRF.
832 * @param buf Input
833 * @param num_building_sprites Number of building sprites to read
834 * @param use_cur_spritesets Whether to use currently referenceable action 1 sets.
835 * @param feature GrfSpecFeature to use spritesets from.
836 * @param allow_var10 Whether the spritelayout may specify var10 values for resolving multiple action-1-2-3 chains
837 * @param no_z_position Whether bounding boxes have no Z offset
838 * @param dts Layout container to output into
839 * @return True on error (GRF was disabled).
841 static bool ReadSpriteLayout(ByteReader &buf, uint num_building_sprites, bool use_cur_spritesets, uint8_t feature, bool allow_var10, bool no_z_position, NewGRFSpriteLayout *dts)
843 bool has_flags = HasBit(num_building_sprites, 6);
844 ClrBit(num_building_sprites, 6);
845 TileLayoutFlags valid_flags = TLF_KNOWN_FLAGS;
846 if (!allow_var10) valid_flags &= ~TLF_VAR10_FLAGS;
847 dts->Allocate(num_building_sprites); // allocate before reading groundsprite flags
849 TempBufferT<uint16_t, 16> max_sprite_offset(num_building_sprites + 1, 0);
850 TempBufferT<uint16_t, 16> max_palette_offset(num_building_sprites + 1, 0);
852 /* Groundsprite */
853 TileLayoutFlags flags = ReadSpriteLayoutSprite(buf, has_flags, false, use_cur_spritesets, feature, &dts->ground, max_sprite_offset, max_palette_offset);
854 if (_cur.skip_sprites < 0) return true;
856 if (flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS)) {
857 GrfMsg(1, "ReadSpriteLayout: Spritelayout uses invalid flag 0x{:X} for ground sprite", flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS));
858 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
859 return true;
862 ReadSpriteLayoutRegisters(buf, flags, false, dts, 0);
863 if (_cur.skip_sprites < 0) return true;
865 for (uint i = 0; i < num_building_sprites; i++) {
866 DrawTileSeqStruct *seq = const_cast<DrawTileSeqStruct*>(&dts->seq[i]);
868 flags = ReadSpriteLayoutSprite(buf, has_flags, false, use_cur_spritesets, feature, &seq->image, max_sprite_offset + i + 1, max_palette_offset + i + 1);
869 if (_cur.skip_sprites < 0) return true;
871 if (flags & ~valid_flags) {
872 GrfMsg(1, "ReadSpriteLayout: Spritelayout uses unknown flag 0x{:X}", flags & ~valid_flags);
873 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
874 return true;
877 seq->delta_x = buf.ReadByte();
878 seq->delta_y = buf.ReadByte();
880 if (!no_z_position) seq->delta_z = buf.ReadByte();
882 if (seq->IsParentSprite()) {
883 seq->size_x = buf.ReadByte();
884 seq->size_y = buf.ReadByte();
885 seq->size_z = buf.ReadByte();
888 ReadSpriteLayoutRegisters(buf, flags, seq->IsParentSprite(), dts, i + 1);
889 if (_cur.skip_sprites < 0) return true;
892 /* Check if the number of sprites per spriteset is consistent */
893 bool is_consistent = true;
894 dts->consistent_max_offset = 0;
895 for (uint i = 0; i < num_building_sprites + 1; i++) {
896 if (max_sprite_offset[i] > 0) {
897 if (dts->consistent_max_offset == 0) {
898 dts->consistent_max_offset = max_sprite_offset[i];
899 } else if (dts->consistent_max_offset != max_sprite_offset[i]) {
900 is_consistent = false;
901 break;
904 if (max_palette_offset[i] > 0) {
905 if (dts->consistent_max_offset == 0) {
906 dts->consistent_max_offset = max_palette_offset[i];
907 } else if (dts->consistent_max_offset != max_palette_offset[i]) {
908 is_consistent = false;
909 break;
914 /* When the Action1 sets are unknown, everything should be 0 (no spriteset usage) or UINT16_MAX (some spriteset usage) */
915 assert(use_cur_spritesets || (is_consistent && (dts->consistent_max_offset == 0 || dts->consistent_max_offset == UINT16_MAX)));
917 if (!is_consistent || dts->registers != nullptr) {
918 dts->consistent_max_offset = 0;
919 if (dts->registers == nullptr) dts->AllocateRegisters();
921 for (uint i = 0; i < num_building_sprites + 1; i++) {
922 TileLayoutRegisters &regs = const_cast<TileLayoutRegisters&>(dts->registers[i]);
923 regs.max_sprite_offset = max_sprite_offset[i];
924 regs.max_palette_offset = max_palette_offset[i];
928 return false;
932 * Translate the refit mask. refit_mask is uint32_t as it has not been mapped to CargoTypes.
934 static CargoTypes TranslateRefitMask(uint32_t refit_mask)
936 CargoTypes result = 0;
937 for (uint8_t bit : SetBitIterator(refit_mask)) {
938 CargoID cargo = GetCargoTranslation(bit, _cur.grffile, true);
939 if (IsValidCargoID(cargo)) SetBit(result, cargo);
941 return result;
945 * Converts TTD(P) Base Price pointers into the enum used by OTTD
946 * See http://wiki.ttdpatch.net/tiki-index.php?page=BaseCosts
947 * @param base_pointer TTD(P) Base Price Pointer
948 * @param error_location Function name for grf error messages
949 * @param[out] index If \a base_pointer is valid, \a index is assigned to the matching price; else it is left unchanged
951 static void ConvertTTDBasePrice(uint32_t base_pointer, const char *error_location, Price *index)
953 /* Special value for 'none' */
954 if (base_pointer == 0) {
955 *index = INVALID_PRICE;
956 return;
959 static const uint32_t start = 0x4B34; ///< Position of first base price
960 static const uint32_t size = 6; ///< Size of each base price record
962 if (base_pointer < start || (base_pointer - start) % size != 0 || (base_pointer - start) / size >= PR_END) {
963 GrfMsg(1, "{}: Unsupported running cost base 0x{:04X}, ignoring", error_location, base_pointer);
964 return;
967 *index = (Price)((base_pointer - start) / size);
970 /** Possible return values for the FeatureChangeInfo functions */
971 enum ChangeInfoResult {
972 CIR_SUCCESS, ///< Variable was parsed and read
973 CIR_DISABLED, ///< GRF was disabled due to error
974 CIR_UNHANDLED, ///< Variable was parsed but unread
975 CIR_UNKNOWN, ///< Variable is unknown
976 CIR_INVALID_ID, ///< Attempt to modify an invalid ID
979 typedef ChangeInfoResult (*VCI_Handler)(uint engine, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf);
981 static ChangeInfoResult HandleAction0PropertyDefault(ByteReader &buf, int prop)
983 if (prop == A0RPI_UNKNOWN_ERROR) {
984 return CIR_DISABLED;
985 } else if (prop < A0RPI_UNKNOWN_IGNORE) {
986 return CIR_UNKNOWN;
987 } else {
988 buf.Skip(buf.ReadExtendedByte());
989 return CIR_SUCCESS;
993 static bool MappedPropertyLengthMismatch(ByteReader &buf, uint expected_size, const GRFFilePropertyRemapEntry *mapping_entry)
995 uint length = buf.ReadExtendedByte();
996 if (length != expected_size) {
997 if (mapping_entry != nullptr) {
998 GrfMsg(2, "Ignoring use of mapped property: {}, feature: {}, mapped to: {:X}{}, with incorrect data size: {} instead of {}",
999 mapping_entry->name, GetFeatureString(mapping_entry->feature),
1000 mapping_entry->property_id, mapping_entry->extended ? " (extended)" : "",
1001 length, expected_size);
1003 buf.Skip(length);
1004 return true;
1005 } else {
1006 return false;
1011 * Define properties common to all vehicles
1012 * @param ei Engine info.
1013 * @param prop The property to change.
1014 * @param buf The property value.
1015 * @return ChangeInfoResult.
1017 static ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
1019 switch (prop) {
1020 case 0x00: // Introduction date
1021 ei->base_intro = CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + buf.ReadWord();
1022 break;
1024 case 0x02: // Decay speed
1025 ei->decay_speed = buf.ReadByte();
1026 break;
1028 case 0x03: // Vehicle life
1029 ei->lifelength = YearDelta{buf.ReadByte()};
1030 break;
1032 case 0x04: // Model life
1033 ei->base_life = YearDelta{buf.ReadByte()};
1034 break;
1036 case 0x06: // Climates available
1037 ei->climates = buf.ReadByte();
1038 break;
1040 case PROP_VEHICLE_LOAD_AMOUNT: // 0x07 Loading speed
1041 /* Amount of cargo loaded during a vehicle's "loading tick" */
1042 ei->load_amount = buf.ReadByte();
1043 break;
1045 default:
1046 return HandleAction0PropertyDefault(buf, prop);
1049 return CIR_SUCCESS;
1053 * Define properties for rail vehicles
1054 * @param engine :ocal ID of the first vehicle.
1055 * @param numinfo Number of subsequent IDs to change the property for.
1056 * @param prop The property to change.
1057 * @param buf The property value.
1058 * @return ChangeInfoResult.
1060 static ChangeInfoResult RailVehicleChangeInfo(uint engine, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
1062 ChangeInfoResult ret = CIR_SUCCESS;
1064 for (int i = 0; i < numinfo; i++) {
1065 Engine *e = GetNewEngine(_cur.grffile, VEH_TRAIN, engine + i);
1066 if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
1068 EngineInfo *ei = &e->info;
1069 RailVehicleInfo *rvi = &e->u.rail;
1071 switch (prop) {
1072 case 0x05: { // Track type
1073 uint8_t tracktype = buf.ReadByte();
1075 if (tracktype < _cur.grffile->railtype_list.size()) {
1076 _gted[e->index].railtypelabel = _cur.grffile->railtype_list[tracktype];
1077 break;
1080 switch (tracktype) {
1081 case 0: _gted[e->index].railtypelabel = rvi->engclass >= 2 ? RAILTYPE_LABEL_ELECTRIC : RAILTYPE_LABEL_RAIL; break;
1082 case 1: _gted[e->index].railtypelabel = RAILTYPE_LABEL_MONO; break;
1083 case 2: _gted[e->index].railtypelabel = RAILTYPE_LABEL_MAGLEV; break;
1084 default:
1085 GrfMsg(1, "RailVehicleChangeInfo: Invalid track type {} specified, ignoring", tracktype);
1086 break;
1088 break;
1091 case 0x08: // AI passenger service
1092 /* Tells the AI that this engine is designed for
1093 * passenger services and shouldn't be used for freight. */
1094 rvi->ai_passenger_only = buf.ReadByte();
1095 break;
1097 case PROP_TRAIN_SPEED: { // 0x09 Speed (1 unit is 1 km-ish/h)
1098 uint16_t speed = buf.ReadWord();
1099 if (speed == 0xFFFF) speed = 0;
1101 rvi->max_speed = speed;
1102 break;
1105 case PROP_TRAIN_POWER: // 0x0B Power
1106 rvi->power = buf.ReadWord();
1108 /* Set engine / wagon state based on power */
1109 if (rvi->power != 0) {
1110 if (rvi->railveh_type == RAILVEH_WAGON) {
1111 rvi->railveh_type = RAILVEH_SINGLEHEAD;
1113 } else {
1114 rvi->railveh_type = RAILVEH_WAGON;
1116 break;
1118 case PROP_TRAIN_RUNNING_COST_FACTOR: // 0x0D Running cost factor
1119 rvi->running_cost = buf.ReadByte();
1120 break;
1122 case 0x0E: // Running cost base
1123 ConvertTTDBasePrice(buf.ReadDWord(), "RailVehicleChangeInfo", &rvi->running_cost_class);
1124 break;
1126 case 0x12: { // Sprite ID
1127 uint8_t spriteid = buf.ReadByte();
1128 uint8_t orig_spriteid = spriteid;
1130 /* TTD sprite IDs point to a location in a 16bit array, but we use it
1131 * as an array index, so we need it to be half the original value. */
1132 if (spriteid < 0xFD) spriteid >>= 1;
1134 if (IsValidNewGRFImageIndex<VEH_TRAIN>(spriteid)) {
1135 rvi->image_index = spriteid;
1136 } else {
1137 GrfMsg(1, "RailVehicleChangeInfo: Invalid Sprite {} specified, ignoring", orig_spriteid);
1138 rvi->image_index = 0;
1140 break;
1143 case 0x13: { // Dual-headed
1144 uint8_t dual = buf.ReadByte();
1146 if (dual != 0) {
1147 rvi->railveh_type = RAILVEH_MULTIHEAD;
1148 } else {
1149 rvi->railveh_type = rvi->power == 0 ?
1150 RAILVEH_WAGON : RAILVEH_SINGLEHEAD;
1152 break;
1155 case PROP_TRAIN_CARGO_CAPACITY: // 0x14 Cargo capacity
1156 rvi->capacity = buf.ReadByte();
1157 break;
1159 case 0x15: { // Cargo type
1160 _gted[e->index].defaultcargo_grf = _cur.grffile;
1161 uint8_t ctype = buf.ReadByte();
1163 if (ctype == 0xFF) {
1164 /* 0xFF is specified as 'use first refittable' */
1165 ei->cargo_type = INVALID_CARGO;
1166 } else {
1167 /* Use translated cargo. Might result in INVALID_CARGO (first refittable), if cargo is not defined. */
1168 ei->cargo_type = GetCargoTranslation(ctype, _cur.grffile);
1169 if (ei->cargo_type == INVALID_CARGO) GrfMsg(2, "RailVehicleChangeInfo: Invalid cargo type {}, using first refittable", ctype);
1171 ei->cargo_label = CT_INVALID;
1172 break;
1175 case PROP_TRAIN_WEIGHT: // 0x16 Weight
1176 SB(rvi->weight, 0, 8, buf.ReadByte());
1177 break;
1179 case PROP_TRAIN_COST_FACTOR: // 0x17 Cost factor
1180 rvi->cost_factor = buf.ReadByte();
1181 break;
1183 case 0x18: // AI rank
1184 GrfMsg(2, "RailVehicleChangeInfo: Property 0x18 'AI rank' not used by NoAI, ignored.");
1185 buf.ReadByte();
1186 break;
1188 case 0x19: { // Engine traction type
1189 /* What do the individual numbers mean?
1190 * 0x00 .. 0x07: Steam
1191 * 0x08 .. 0x27: Diesel
1192 * 0x28 .. 0x31: Electric
1193 * 0x32 .. 0x37: Monorail
1194 * 0x38 .. 0x41: Maglev
1196 uint8_t traction = buf.ReadByte();
1197 EngineClass engclass;
1199 if (traction <= 0x07) {
1200 engclass = EC_STEAM;
1201 } else if (traction <= 0x27) {
1202 engclass = EC_DIESEL;
1203 } else if (traction <= 0x31) {
1204 engclass = EC_ELECTRIC;
1205 } else if (traction <= 0x37) {
1206 engclass = EC_MONORAIL;
1207 } else if (traction <= 0x41) {
1208 engclass = EC_MAGLEV;
1209 } else {
1210 break;
1213 if (_cur.grffile->railtype_list.empty()) {
1214 /* Use traction type to select between normal and electrified
1215 * rail only when no translation list is in place. */
1216 if (_gted[e->index].railtypelabel == RAILTYPE_LABEL_RAIL && engclass >= EC_ELECTRIC) _gted[e->index].railtypelabel = RAILTYPE_LABEL_ELECTRIC;
1217 if (_gted[e->index].railtypelabel == RAILTYPE_LABEL_ELECTRIC && engclass < EC_ELECTRIC) _gted[e->index].railtypelabel = RAILTYPE_LABEL_RAIL;
1220 rvi->engclass = engclass;
1221 break;
1224 case 0x1A: // Alter purchase list sort order
1225 AlterVehicleListOrder(e->index, buf.ReadExtendedByte());
1226 break;
1228 case 0x1B: // Powered wagons power bonus
1229 rvi->pow_wag_power = buf.ReadWord();
1230 break;
1232 case 0x1C: // Refit cost
1233 ei->refit_cost = buf.ReadByte();
1234 break;
1236 case 0x1D: { // Refit cargo
1237 uint32_t mask = buf.ReadDWord();
1238 _gted[e->index].UpdateRefittability(mask != 0);
1239 ei->refit_mask = TranslateRefitMask(mask);
1240 _gted[e->index].defaultcargo_grf = _cur.grffile;
1241 break;
1244 case 0x1E: // Callback
1245 SB(ei->callback_mask, 0, 8, buf.ReadByte());
1246 break;
1248 case PROP_TRAIN_TRACTIVE_EFFORT: // 0x1F Tractive effort coefficient
1249 rvi->tractive_effort = buf.ReadByte();
1250 break;
1252 case 0x20: // Air drag
1253 rvi->air_drag = buf.ReadByte();
1254 break;
1256 case PROP_TRAIN_SHORTEN_FACTOR: // 0x21 Shorter vehicle
1257 rvi->shorten_factor = buf.ReadByte();
1258 break;
1260 case 0x22: // Visual effect
1261 rvi->visual_effect = buf.ReadByte();
1262 /* Avoid accidentally setting visual_effect to the default value
1263 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
1264 if (rvi->visual_effect == VE_DEFAULT) {
1265 assert(HasBit(rvi->visual_effect, VE_DISABLE_EFFECT));
1266 SB(rvi->visual_effect, VE_TYPE_START, VE_TYPE_COUNT, 0);
1268 break;
1270 case 0x23: // Powered wagons weight bonus
1271 rvi->pow_wag_weight = buf.ReadByte();
1272 break;
1274 case 0x24: { // High byte of vehicle weight
1275 uint8_t weight = buf.ReadByte();
1277 if (weight > 4) {
1278 GrfMsg(2, "RailVehicleChangeInfo: Nonsensical weight of {} tons, ignoring", weight << 8);
1279 } else {
1280 SB(rvi->weight, 8, 8, weight);
1282 break;
1285 case PROP_TRAIN_USER_DATA: // 0x25 User-defined bit mask to set when checking veh. var. 42
1286 rvi->user_def_data = buf.ReadByte();
1287 break;
1289 case 0x26: // Retire vehicle early
1290 ei->retire_early = buf.ReadByte();
1291 break;
1293 case 0x27: // Miscellaneous flags
1294 ei->misc_flags = buf.ReadByte();
1295 _loaded_newgrf_features.has_2CC |= HasBit(ei->misc_flags, EF_USES_2CC);
1296 break;
1298 case 0x28: // Cargo classes allowed
1299 _gted[e->index].cargo_allowed = buf.ReadWord();
1300 _gted[e->index].UpdateRefittability(_gted[e->index].cargo_allowed != 0);
1301 _gted[e->index].defaultcargo_grf = _cur.grffile;
1302 break;
1304 case 0x29: // Cargo classes disallowed
1305 _gted[e->index].cargo_disallowed = buf.ReadWord();
1306 _gted[e->index].UpdateRefittability(false);
1307 break;
1309 case 0x2A: // Long format introduction date (days since year 0)
1310 ei->base_intro = CalTime::Date(static_cast<int32_t>(buf.ReadDWord()));
1311 break;
1313 case PROP_TRAIN_CARGO_AGE_PERIOD: // 0x2B Cargo aging period
1314 ei->cargo_age_period = buf.ReadWord();
1315 break;
1317 case 0x2C: // CTT refit include list
1318 case 0x2D: { // CTT refit exclude list
1319 uint8_t count = buf.ReadByte();
1320 _gted[e->index].UpdateRefittability(prop == 0x2C && count != 0);
1321 if (prop == 0x2C) _gted[e->index].defaultcargo_grf = _cur.grffile;
1322 CargoTypes &ctt = prop == 0x2C ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
1323 ctt = 0;
1324 while (count--) {
1325 CargoID ctype = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
1326 if (IsValidCargoID(ctype)) SetBit(ctt, ctype);
1328 break;
1331 case PROP_TRAIN_CURVE_SPEED_MOD: // 0x2E Curve speed modifier
1332 rvi->curve_speed_mod = buf.ReadWord();
1333 break;
1335 case 0x2F: // Engine variant
1336 ei->variant_id = buf.ReadWord();
1337 break;
1339 case 0x30: // Extra miscellaneous flags
1340 ei->extra_flags = static_cast<ExtraEngineFlags>(buf.ReadDWord());
1341 break;
1343 case 0x31: // Callback additional mask
1344 SB(ei->callback_mask, 8, 8, buf.ReadByte());
1345 break;
1347 case 0x32: // Cargo classes required for a refit.
1348 _gted[e->index].cargo_allowed_required = buf.ReadWord();
1349 break;
1351 default:
1352 ret = CommonVehicleChangeInfo(ei, prop, mapping_entry, buf);
1353 break;
1357 return ret;
1361 * Define properties for road vehicles
1362 * @param engine Local ID of the first vehicle.
1363 * @param numinfo Number of subsequent IDs to change the property for.
1364 * @param prop The property to change.
1365 * @param buf The property value.
1366 * @return ChangeInfoResult.
1368 static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
1370 ChangeInfoResult ret = CIR_SUCCESS;
1372 for (int i = 0; i < numinfo; i++) {
1373 Engine *e = GetNewEngine(_cur.grffile, VEH_ROAD, engine + i);
1374 if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
1376 EngineInfo *ei = &e->info;
1377 RoadVehicleInfo *rvi = &e->u.road;
1379 switch (prop) {
1380 case 0x05: // Road/tram type
1381 /* RoadTypeLabel is looked up later after the engine's road/tram
1382 * flag is set, however 0 means the value has not been set. */
1383 _gted[e->index].roadtramtype = buf.ReadByte() + 1;
1384 break;
1386 case 0x08: // Speed (1 unit is 0.5 kmh)
1387 rvi->max_speed = buf.ReadByte();
1388 break;
1390 case PROP_ROADVEH_RUNNING_COST_FACTOR: // 0x09 Running cost factor
1391 rvi->running_cost = buf.ReadByte();
1392 break;
1394 case 0x0A: // Running cost base
1395 ConvertTTDBasePrice(buf.ReadDWord(), "RoadVehicleChangeInfo", &rvi->running_cost_class);
1396 break;
1398 case 0x0E: { // Sprite ID
1399 uint8_t spriteid = buf.ReadByte();
1400 uint8_t orig_spriteid = spriteid;
1402 /* cars have different custom id in the GRF file */
1403 if (spriteid == 0xFF) spriteid = 0xFD;
1405 if (spriteid < 0xFD) spriteid >>= 1;
1407 if (IsValidNewGRFImageIndex<VEH_ROAD>(spriteid)) {
1408 rvi->image_index = spriteid;
1409 } else {
1410 GrfMsg(1, "RoadVehicleChangeInfo: Invalid Sprite {} specified, ignoring", orig_spriteid);
1411 rvi->image_index = 0;
1413 break;
1416 case PROP_ROADVEH_CARGO_CAPACITY: // 0x0F Cargo capacity
1417 rvi->capacity = buf.ReadByte();
1418 break;
1420 case 0x10: { // Cargo type
1421 _gted[e->index].defaultcargo_grf = _cur.grffile;
1422 uint8_t ctype = buf.ReadByte();
1424 if (ctype == 0xFF) {
1425 /* 0xFF is specified as 'use first refittable' */
1426 ei->cargo_type = INVALID_CARGO;
1427 } else {
1428 /* Use translated cargo. Might result in INVALID_CARGO (first refittable), if cargo is not defined. */
1429 ei->cargo_type = GetCargoTranslation(ctype, _cur.grffile);
1430 if (ei->cargo_type == INVALID_CARGO) GrfMsg(2, "RoadVehicleChangeInfo: Invalid cargo type {}, using first refittable", ctype);
1432 ei->cargo_label = CT_INVALID;
1433 break;
1436 case PROP_ROADVEH_COST_FACTOR: // 0x11 Cost factor
1437 rvi->cost_factor = buf.ReadByte();
1438 break;
1440 case 0x12: // SFX
1441 rvi->sfx = GetNewGRFSoundID(_cur.grffile, buf.ReadByte());
1442 break;
1444 case PROP_ROADVEH_POWER: // Power in units of 10 HP.
1445 rvi->power = buf.ReadByte();
1446 break;
1448 case PROP_ROADVEH_WEIGHT: // Weight in units of 1/4 tons.
1449 rvi->weight = buf.ReadByte();
1450 break;
1452 case PROP_ROADVEH_SPEED: // Speed in mph/0.8
1453 _gted[e->index].rv_max_speed = buf.ReadByte();
1454 break;
1456 case 0x16: { // Cargoes available for refitting
1457 uint32_t mask = buf.ReadDWord();
1458 _gted[e->index].UpdateRefittability(mask != 0);
1459 ei->refit_mask = TranslateRefitMask(mask);
1460 _gted[e->index].defaultcargo_grf = _cur.grffile;
1461 break;
1464 case 0x17: // Callback mask
1465 SB(ei->callback_mask, 0, 8, buf.ReadByte());
1466 break;
1468 case PROP_ROADVEH_TRACTIVE_EFFORT: // Tractive effort coefficient in 1/256.
1469 rvi->tractive_effort = buf.ReadByte();
1470 break;
1472 case 0x19: // Air drag
1473 rvi->air_drag = buf.ReadByte();
1474 break;
1476 case 0x1A: // Refit cost
1477 ei->refit_cost = buf.ReadByte();
1478 break;
1480 case 0x1B: // Retire vehicle early
1481 ei->retire_early = buf.ReadByte();
1482 break;
1484 case 0x1C: // Miscellaneous flags
1485 ei->misc_flags = buf.ReadByte();
1486 _loaded_newgrf_features.has_2CC |= HasBit(ei->misc_flags, EF_USES_2CC);
1487 break;
1489 case 0x1D: // Cargo classes allowed
1490 _gted[e->index].cargo_allowed = buf.ReadWord();
1491 _gted[e->index].UpdateRefittability(_gted[e->index].cargo_allowed != 0);
1492 _gted[e->index].defaultcargo_grf = _cur.grffile;
1493 break;
1495 case 0x1E: // Cargo classes disallowed
1496 _gted[e->index].cargo_disallowed = buf.ReadWord();
1497 _gted[e->index].UpdateRefittability(false);
1498 break;
1500 case 0x1F: // Long format introduction date (days since year 0)
1501 ei->base_intro = CalTime::Date(static_cast<int32_t>(buf.ReadDWord()));
1502 break;
1504 case 0x20: // Alter purchase list sort order
1505 AlterVehicleListOrder(e->index, buf.ReadExtendedByte());
1506 break;
1508 case 0x21: // Visual effect
1509 rvi->visual_effect = buf.ReadByte();
1510 /* Avoid accidentally setting visual_effect to the default value
1511 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
1512 if (rvi->visual_effect == VE_DEFAULT) {
1513 assert(HasBit(rvi->visual_effect, VE_DISABLE_EFFECT));
1514 SB(rvi->visual_effect, VE_TYPE_START, VE_TYPE_COUNT, 0);
1516 break;
1518 case PROP_ROADVEH_CARGO_AGE_PERIOD: // 0x22 Cargo aging period
1519 ei->cargo_age_period = buf.ReadWord();
1520 break;
1522 case PROP_ROADVEH_SHORTEN_FACTOR: // 0x23 Shorter vehicle
1523 rvi->shorten_factor = buf.ReadByte();
1524 break;
1526 case 0x24: // CTT refit include list
1527 case 0x25: { // CTT refit exclude list
1528 uint8_t count = buf.ReadByte();
1529 _gted[e->index].UpdateRefittability(prop == 0x24 && count != 0);
1530 if (prop == 0x24) _gted[e->index].defaultcargo_grf = _cur.grffile;
1531 CargoTypes &ctt = prop == 0x24 ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
1532 ctt = 0;
1533 while (count--) {
1534 CargoID ctype = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
1535 if (IsValidCargoID(ctype)) SetBit(ctt, ctype);
1537 break;
1540 case 0x26: // Engine variant
1541 ei->variant_id = buf.ReadWord();
1542 break;
1544 case 0x27: // Extra miscellaneous flags
1545 ei->extra_flags = static_cast<ExtraEngineFlags>(buf.ReadDWord());
1546 break;
1548 case 0x28: // Callback additional mask
1549 SB(ei->callback_mask, 8, 8, buf.ReadByte());
1550 break;
1552 case 0x29: // Cargo classes required for a refit.
1553 _gted[e->index].cargo_allowed_required = buf.ReadWord();
1554 break;
1556 default:
1557 ret = CommonVehicleChangeInfo(ei, prop, mapping_entry, buf);
1558 break;
1562 return ret;
1566 * Define properties for ships
1567 * @param engine Local ID of the first vehicle.
1568 * @param numinfo Number of subsequent IDs to change the property for.
1569 * @param prop The property to change.
1570 * @param buf The property value.
1571 * @return ChangeInfoResult.
1573 static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
1575 ChangeInfoResult ret = CIR_SUCCESS;
1577 for (int i = 0; i < numinfo; i++) {
1578 Engine *e = GetNewEngine(_cur.grffile, VEH_SHIP, engine + i);
1579 if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
1581 EngineInfo *ei = &e->info;
1582 ShipVehicleInfo *svi = &e->u.ship;
1584 switch (prop) {
1585 case 0x08: { // Sprite ID
1586 uint8_t spriteid = buf.ReadByte();
1587 uint8_t orig_spriteid = spriteid;
1589 /* ships have different custom id in the GRF file */
1590 if (spriteid == 0xFF) spriteid = 0xFD;
1592 if (spriteid < 0xFD) spriteid >>= 1;
1594 if (IsValidNewGRFImageIndex<VEH_SHIP>(spriteid)) {
1595 svi->image_index = spriteid;
1596 } else {
1597 GrfMsg(1, "ShipVehicleChangeInfo: Invalid Sprite {} specified, ignoring", orig_spriteid);
1598 svi->image_index = 0;
1600 break;
1603 case 0x09: // Refittable
1604 svi->old_refittable = (buf.ReadByte() != 0);
1605 break;
1607 case PROP_SHIP_COST_FACTOR: // 0x0A Cost factor
1608 svi->cost_factor = buf.ReadByte();
1609 break;
1611 case PROP_SHIP_SPEED: // 0x0B Speed (1 unit is 0.5 km-ish/h). Use 0x23 to achieve higher speeds.
1612 svi->max_speed = buf.ReadByte();
1613 break;
1615 case 0x0C: { // Cargo type
1616 _gted[e->index].defaultcargo_grf = _cur.grffile;
1617 uint8_t ctype = buf.ReadByte();
1619 if (ctype == 0xFF) {
1620 /* 0xFF is specified as 'use first refittable' */
1621 ei->cargo_type = INVALID_CARGO;
1622 } else {
1623 /* Use translated cargo. Might result in INVALID_CARGO (first refittable), if cargo is not defined. */
1624 ei->cargo_type = GetCargoTranslation(ctype, _cur.grffile);
1625 if (ei->cargo_type == INVALID_CARGO) GrfMsg(2, "ShipVehicleChangeInfo: Invalid cargo type {}, using first refittable", ctype);
1627 ei->cargo_label = CT_INVALID;
1628 break;
1631 case PROP_SHIP_CARGO_CAPACITY: // 0x0D Cargo capacity
1632 svi->capacity = buf.ReadWord();
1633 break;
1635 case PROP_SHIP_RUNNING_COST_FACTOR: // 0x0F Running cost factor
1636 svi->running_cost = buf.ReadByte();
1637 break;
1639 case 0x10: // SFX
1640 svi->sfx = GetNewGRFSoundID(_cur.grffile, buf.ReadByte());
1641 break;
1643 case 0x11: { // Cargoes available for refitting
1644 uint32_t mask = buf.ReadDWord();
1645 _gted[e->index].UpdateRefittability(mask != 0);
1646 ei->refit_mask = TranslateRefitMask(mask);
1647 _gted[e->index].defaultcargo_grf = _cur.grffile;
1648 break;
1651 case 0x12: // Callback mask
1652 SB(ei->callback_mask, 0, 8, buf.ReadByte());
1653 break;
1655 case 0x13: // Refit cost
1656 ei->refit_cost = buf.ReadByte();
1657 break;
1659 case 0x14: // Ocean speed fraction
1660 svi->ocean_speed_frac = buf.ReadByte();
1661 break;
1663 case 0x15: // Canal speed fraction
1664 svi->canal_speed_frac = buf.ReadByte();
1665 break;
1667 case 0x16: // Retire vehicle early
1668 ei->retire_early = buf.ReadByte();
1669 break;
1671 case 0x17: // Miscellaneous flags
1672 ei->misc_flags = buf.ReadByte();
1673 _loaded_newgrf_features.has_2CC |= HasBit(ei->misc_flags, EF_USES_2CC);
1674 break;
1676 case 0x18: // Cargo classes allowed
1677 _gted[e->index].cargo_allowed = buf.ReadWord();
1678 _gted[e->index].UpdateRefittability(_gted[e->index].cargo_allowed != 0);
1679 _gted[e->index].defaultcargo_grf = _cur.grffile;
1680 break;
1682 case 0x19: // Cargo classes disallowed
1683 _gted[e->index].cargo_disallowed = buf.ReadWord();
1684 _gted[e->index].UpdateRefittability(false);
1685 break;
1687 case 0x1A: // Long format introduction date (days since year 0)
1688 ei->base_intro = CalTime::Date(static_cast<int32_t>(buf.ReadDWord()));
1689 break;
1691 case 0x1B: // Alter purchase list sort order
1692 AlterVehicleListOrder(e->index, buf.ReadExtendedByte());
1693 break;
1695 case 0x1C: // Visual effect
1696 svi->visual_effect = buf.ReadByte();
1697 /* Avoid accidentally setting visual_effect to the default value
1698 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
1699 if (svi->visual_effect == VE_DEFAULT) {
1700 assert(HasBit(svi->visual_effect, VE_DISABLE_EFFECT));
1701 SB(svi->visual_effect, VE_TYPE_START, VE_TYPE_COUNT, 0);
1703 break;
1705 case PROP_SHIP_CARGO_AGE_PERIOD: // 0x1D Cargo aging period
1706 ei->cargo_age_period = buf.ReadWord();
1707 break;
1709 case 0x1E: // CTT refit include list
1710 case 0x1F: { // CTT refit exclude list
1711 uint8_t count = buf.ReadByte();
1712 _gted[e->index].UpdateRefittability(prop == 0x1E && count != 0);
1713 if (prop == 0x1E) _gted[e->index].defaultcargo_grf = _cur.grffile;
1714 CargoTypes &ctt = prop == 0x1E ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
1715 ctt = 0;
1716 while (count--) {
1717 CargoID ctype = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
1718 if (IsValidCargoID(ctype)) SetBit(ctt, ctype);
1720 break;
1723 case 0x20: // Engine variant
1724 ei->variant_id = buf.ReadWord();
1725 break;
1727 case 0x21: // Extra miscellaneous flags
1728 ei->extra_flags = static_cast<ExtraEngineFlags>(buf.ReadDWord());
1729 break;
1731 case 0x22: // Callback additional mask
1732 SB(ei->callback_mask, 8, 8, buf.ReadByte());
1733 break;
1735 case 0x23: // Speed (1 unit is 0.5 km-ish/h)
1736 svi->max_speed = buf.ReadWord();
1737 break;
1739 case 0x24: // Acceleration (1 unit is 0.5 km-ish/h per tick)
1740 svi->acceleration = std::max<uint8_t>(1, buf.ReadByte());
1741 break;
1743 case 0x25: // Cargo classes required for a refit.
1744 _gted[e->index].cargo_allowed_required = buf.ReadWord();
1745 break;
1747 default:
1748 ret = CommonVehicleChangeInfo(ei, prop, mapping_entry, buf);
1749 break;
1753 return ret;
1757 * Define properties for aircraft
1758 * @param engine Local ID of the aircraft.
1759 * @param numinfo Number of subsequent IDs to change the property for.
1760 * @param prop The property to change.
1761 * @param buf The property value.
1762 * @return ChangeInfoResult.
1764 static ChangeInfoResult AircraftVehicleChangeInfo(uint engine, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
1766 ChangeInfoResult ret = CIR_SUCCESS;
1768 for (int i = 0; i < numinfo; i++) {
1769 Engine *e = GetNewEngine(_cur.grffile, VEH_AIRCRAFT, engine + i);
1770 if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
1772 EngineInfo *ei = &e->info;
1773 AircraftVehicleInfo *avi = &e->u.air;
1775 switch (prop) {
1776 case 0x08: { // Sprite ID
1777 uint8_t spriteid = buf.ReadByte();
1778 uint8_t orig_spriteid = spriteid;
1780 /* aircraft have different custom id in the GRF file */
1781 if (spriteid == 0xFF) spriteid = 0xFD;
1783 if (spriteid < 0xFD) spriteid >>= 1;
1785 if (IsValidNewGRFImageIndex<VEH_AIRCRAFT>(spriteid)) {
1786 avi->image_index = spriteid;
1787 } else {
1788 GrfMsg(1, "AircraftVehicleChangeInfo: Invalid Sprite {} specified, ignoring", orig_spriteid);
1789 avi->image_index = 0;
1791 break;
1794 case 0x09: // Helicopter
1795 if (buf.ReadByte() == 0) {
1796 avi->subtype = AIR_HELI;
1797 } else {
1798 SB(avi->subtype, 0, 1, 1); // AIR_CTOL
1800 break;
1802 case 0x0A: // Large
1803 AssignBit(avi->subtype, 1, buf.ReadByte() != 0); // AIR_FAST
1804 break;
1806 case PROP_AIRCRAFT_COST_FACTOR: // 0x0B Cost factor
1807 avi->cost_factor = buf.ReadByte();
1808 break;
1810 case PROP_AIRCRAFT_SPEED: // 0x0C Speed (1 unit is 8 mph, we translate to 1 unit is 1 km-ish/h)
1811 avi->max_speed = (buf.ReadByte() * 128) / 10;
1812 break;
1814 case 0x0D: // Acceleration
1815 avi->acceleration = buf.ReadByte();
1816 break;
1818 case PROP_AIRCRAFT_RUNNING_COST_FACTOR: // 0x0E Running cost factor
1819 avi->running_cost = buf.ReadByte();
1820 break;
1822 case PROP_AIRCRAFT_PASSENGER_CAPACITY: // 0x0F Passenger capacity
1823 avi->passenger_capacity = buf.ReadWord();
1824 break;
1826 case PROP_AIRCRAFT_MAIL_CAPACITY: // 0x11 Mail capacity
1827 avi->mail_capacity = buf.ReadByte();
1828 break;
1830 case 0x12: // SFX
1831 avi->sfx = GetNewGRFSoundID(_cur.grffile, buf.ReadByte());
1832 break;
1834 case 0x13: { // Cargoes available for refitting
1835 uint32_t mask = buf.ReadDWord();
1836 _gted[e->index].UpdateRefittability(mask != 0);
1837 ei->refit_mask = TranslateRefitMask(mask);
1838 _gted[e->index].defaultcargo_grf = _cur.grffile;
1839 break;
1842 case 0x14: // Callback mask
1843 SB(ei->callback_mask, 0, 8, buf.ReadByte());
1844 break;
1846 case 0x15: // Refit cost
1847 ei->refit_cost = buf.ReadByte();
1848 break;
1850 case 0x16: // Retire vehicle early
1851 ei->retire_early = buf.ReadByte();
1852 break;
1854 case 0x17: // Miscellaneous flags
1855 ei->misc_flags = buf.ReadByte();
1856 _loaded_newgrf_features.has_2CC |= HasBit(ei->misc_flags, EF_USES_2CC);
1857 break;
1859 case 0x18: // Cargo classes allowed
1860 _gted[e->index].cargo_allowed = buf.ReadWord();
1861 _gted[e->index].UpdateRefittability(_gted[e->index].cargo_allowed != 0);
1862 _gted[e->index].defaultcargo_grf = _cur.grffile;
1863 break;
1865 case 0x19: // Cargo classes disallowed
1866 _gted[e->index].cargo_disallowed = buf.ReadWord();
1867 _gted[e->index].UpdateRefittability(false);
1868 break;
1870 case 0x1A: // Long format introduction date (days since year 0)
1871 ei->base_intro = CalTime::Date(static_cast<int32_t>(buf.ReadDWord()));
1872 break;
1874 case 0x1B: // Alter purchase list sort order
1875 AlterVehicleListOrder(e->index, buf.ReadExtendedByte());
1876 break;
1878 case PROP_AIRCRAFT_CARGO_AGE_PERIOD: // 0x1C Cargo aging period
1879 ei->cargo_age_period = buf.ReadWord();
1880 break;
1882 case 0x1D: // CTT refit include list
1883 case 0x1E: { // CTT refit exclude list
1884 uint8_t count = buf.ReadByte();
1885 _gted[e->index].UpdateRefittability(prop == 0x1D && count != 0);
1886 if (prop == 0x1D) _gted[e->index].defaultcargo_grf = _cur.grffile;
1887 CargoTypes &ctt = prop == 0x1D ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
1888 ctt = 0;
1889 while (count--) {
1890 CargoID ctype = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
1891 if (IsValidCargoID(ctype)) SetBit(ctt, ctype);
1893 break;
1896 case PROP_AIRCRAFT_RANGE: // 0x1F Max aircraft range
1897 avi->max_range = buf.ReadWord();
1898 break;
1900 case 0x20: // Engine variant
1901 ei->variant_id = buf.ReadWord();
1902 break;
1904 case 0x21: // Extra miscellaneous flags
1905 ei->extra_flags = static_cast<ExtraEngineFlags>(buf.ReadDWord());
1906 break;
1908 case 0x22: // Callback additional mask
1909 SB(ei->callback_mask, 8, 8, buf.ReadByte());
1910 break;
1912 case 0x23: // Cargo classes required for a refit.
1913 _gted[e->index].cargo_allowed_required = buf.ReadWord();
1914 break;
1916 default:
1917 ret = CommonVehicleChangeInfo(ei, prop, mapping_entry, buf);
1918 break;
1922 return ret;
1926 * Define properties for stations
1927 * @param stid StationID of the first station tile.
1928 * @param numinfo Number of subsequent station tiles to change the property for.
1929 * @param prop The property to change.
1930 * @param buf The property value.
1931 * @return ChangeInfoResult.
1933 static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
1935 ChangeInfoResult ret = CIR_SUCCESS;
1937 if (stid + numinfo > NUM_STATIONS_PER_GRF) {
1938 GrfMsg(1, "StationChangeInfo: Station {} is invalid, max {}, ignoring", stid + numinfo, NUM_STATIONS_PER_GRF);
1939 return CIR_INVALID_ID;
1942 /* Allocate station specs if necessary */
1943 if (_cur.grffile->stations.size() < stid + numinfo) _cur.grffile->stations.resize(stid + numinfo);
1945 for (int i = 0; i < numinfo; i++) {
1946 StationSpec *statspec = _cur.grffile->stations[stid + i].get();
1948 /* Check that the station we are modifying is defined. */
1949 if (statspec == nullptr && prop != 0x08) {
1950 GrfMsg(2, "StationChangeInfo: Attempt to modify undefined station {}, ignoring", stid + i);
1951 return CIR_INVALID_ID;
1954 switch (prop) {
1955 case 0x08: { // Class ID
1956 /* Property 0x08 is special; it is where the station is allocated */
1957 if (statspec == nullptr) {
1958 _cur.grffile->stations[stid + i] = std::make_unique<StationSpec>();
1959 statspec = _cur.grffile->stations[stid + i].get();
1962 /* Swap classid because we read it in BE meaning WAYP or DFLT */
1963 uint32_t classid = buf.ReadDWord();
1964 statspec->class_index = StationClass::Allocate(BSWAP32(classid));
1965 break;
1968 case 0x09: { // Define sprite layout
1969 uint16_t tiles = buf.ReadExtendedByte();
1970 statspec->renderdata.clear(); // delete earlier loaded stuff
1971 statspec->renderdata.reserve(tiles);
1973 for (uint t = 0; t < tiles; t++) {
1974 NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back();
1975 dts->consistent_max_offset = UINT16_MAX; // Spritesets are unknown, so no limit.
1977 if (buf.HasData(4) && buf.PeekDWord() == 0) {
1978 buf.Skip(4);
1979 extern const DrawTileSprites _station_display_datas_rail[8];
1980 dts->Clone(&_station_display_datas_rail[t % 8]);
1981 continue;
1984 ReadSpriteLayoutSprite(buf, false, false, false, GSF_STATIONS, &dts->ground);
1985 /* On error, bail out immediately. Temporary GRF data was already freed */
1986 if (_cur.skip_sprites < 0) return CIR_DISABLED;
1988 static std::vector<DrawTileSeqStruct> tmp_layout;
1989 tmp_layout.clear();
1990 for (;;) {
1991 /* no relative bounding box support */
1992 DrawTileSeqStruct &dtss = tmp_layout.emplace_back();
1993 MemSetT(&dtss, 0);
1995 dtss.delta_x = buf.ReadByte();
1996 if (dtss.IsTerminator()) break;
1997 dtss.delta_y = buf.ReadByte();
1998 dtss.delta_z = buf.ReadByte();
1999 dtss.size_x = buf.ReadByte();
2000 dtss.size_y = buf.ReadByte();
2001 dtss.size_z = buf.ReadByte();
2003 ReadSpriteLayoutSprite(buf, false, true, false, GSF_STATIONS, &dtss.image);
2004 /* On error, bail out immediately. Temporary GRF data was already freed */
2005 if (_cur.skip_sprites < 0) return CIR_DISABLED;
2007 dts->Clone(tmp_layout.data());
2010 /* Number of layouts must be even, alternating X and Y */
2011 if (statspec->renderdata.size() & 1) {
2012 GrfMsg(1, "StationChangeInfo: Station {} defines an odd number of sprite layouts, dropping the last item", stid + i);
2013 statspec->renderdata.pop_back();
2015 break;
2018 case 0x0A: { // Copy sprite layout
2019 uint16_t srcid = buf.ReadExtendedByte();
2020 const StationSpec *srcstatspec = srcid >= _cur.grffile->stations.size() ? nullptr : _cur.grffile->stations[srcid].get();
2022 if (srcstatspec == nullptr) {
2023 GrfMsg(1, "StationChangeInfo: Station {} is not defined, cannot copy sprite layout to {}.", srcid, stid + i);
2024 continue;
2027 statspec->renderdata.clear(); // delete earlier loaded stuff
2028 statspec->renderdata.reserve(srcstatspec->renderdata.size());
2030 for (const auto &it : srcstatspec->renderdata) {
2031 NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back();
2032 dts->Clone(&it);
2034 break;
2037 case 0x0B: // Callback mask
2038 statspec->callback_mask = buf.ReadByte();
2039 break;
2041 case 0x0C: // Disallowed number of platforms
2042 statspec->disallowed_platforms = buf.ReadByte();
2043 break;
2045 case 0x0D: // Disallowed platform lengths
2046 statspec->disallowed_lengths = buf.ReadByte();
2047 break;
2049 case 0x0E: // Define custom layout
2050 while (buf.HasData()) {
2051 uint8_t length = buf.ReadByte();
2052 uint8_t number = buf.ReadByte();
2054 if (length == 0 || number == 0) break;
2056 const uint8_t *buf_layout = buf.ReadBytes(length * number);
2058 /* Create entry in layouts and assign the layout to it. */
2059 auto &layout = statspec->layouts[GetStationLayoutKey(number, length)];
2060 layout.assign(buf_layout, buf_layout + length * number);
2062 /* Ensure the first bit, axis, is zero. The rest of the value is validated during rendering, as we don't know the range yet. */
2063 for (auto &tile : layout) {
2064 if ((tile & ~1U) != tile) {
2065 GrfMsg(1, "StationChangeInfo: Invalid tile {} in layout {}x{}", tile, length, number);
2066 tile &= ~1U;
2070 break;
2072 case 0x0F: { // Copy custom layout
2073 uint16_t srcid = buf.ReadExtendedByte();
2074 const StationSpec *srcstatspec = srcid >= _cur.grffile->stations.size() ? nullptr : _cur.grffile->stations[srcid].get();
2076 if (srcstatspec == nullptr) {
2077 GrfMsg(1, "StationChangeInfo: Station {} is not defined, cannot copy tile layout to {}.", srcid, stid + i);
2078 continue;
2081 statspec->layouts = srcstatspec->layouts;
2082 break;
2085 case 0x10: // Little/lots cargo threshold
2086 statspec->cargo_threshold = buf.ReadWord();
2087 break;
2089 case 0x11: { // Pylon placement
2090 uint8_t pylons = buf.ReadByte();
2091 if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8);
2092 for (int j = 0; j < 8; ++j) {
2093 if (HasBit(pylons, j)) {
2094 statspec->tileflags[j] |= StationSpec::TileFlags::Pylons;
2095 } else {
2096 statspec->tileflags[j] &= ~StationSpec::TileFlags::Pylons;
2099 break;
2102 case 0x12: // Cargo types for random triggers
2103 if (_cur.grffile->grf_version >= 7) {
2104 statspec->cargo_triggers = TranslateRefitMask(buf.ReadDWord());
2105 } else {
2106 statspec->cargo_triggers = (CargoTypes)buf.ReadDWord();
2108 break;
2110 case 0x13: // General flags
2111 statspec->flags = buf.ReadByte();
2112 break;
2114 case 0x14: { // Overhead wire placement
2115 uint8_t wires = buf.ReadByte();
2116 if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8);
2117 for (int j = 0; j < 8; ++j) {
2118 if (HasBit(wires, j)) {
2119 statspec->tileflags[j] |= StationSpec::TileFlags::NoWires;
2120 } else {
2121 statspec->tileflags[j] &= ~StationSpec::TileFlags::NoWires;
2124 break;
2127 case 0x15: { // Blocked tiles
2128 uint8_t blocked = buf.ReadByte();
2129 if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8);
2130 for (int j = 0; j < 8; ++j) {
2131 if (HasBit(blocked, j)) {
2132 statspec->tileflags[j] |= StationSpec::TileFlags::Blocked;
2133 } else {
2134 statspec->tileflags[j] &= ~StationSpec::TileFlags::Blocked;
2137 break;
2140 case 0x16: // Animation info
2141 statspec->animation.frames = buf.ReadByte();
2142 statspec->animation.status = buf.ReadByte();
2143 break;
2145 case 0x17: // Animation speed
2146 statspec->animation.speed = buf.ReadByte();
2147 break;
2149 case 0x18: // Animation triggers
2150 statspec->animation.triggers = buf.ReadWord();
2151 break;
2153 /* 0x19 road routing (not implemented) */
2155 case 0x1A: { // Advanced sprite layout
2156 uint16_t tiles = buf.ReadExtendedByte();
2157 statspec->renderdata.clear(); // delete earlier loaded stuff
2158 statspec->renderdata.reserve(tiles);
2160 for (uint t = 0; t < tiles; t++) {
2161 NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back();
2162 uint num_building_sprites = buf.ReadByte();
2163 /* On error, bail out immediately. Temporary GRF data was already freed */
2164 if (ReadSpriteLayout(buf, num_building_sprites, false, GSF_STATIONS, true, false, dts)) return CIR_DISABLED;
2167 /* Number of layouts must be even, alternating X and Y */
2168 if (statspec->renderdata.size() & 1) {
2169 GrfMsg(1, "StationChangeInfo: Station {} defines an odd number of sprite layouts, dropping the last item", stid + i);
2170 statspec->renderdata.pop_back();
2172 break;
2175 case A0RPI_STATION_MIN_BRIDGE_HEIGHT: {
2176 SetBit(statspec->internal_flags, SSIF_BRIDGE_HEIGHTS_SET);
2177 size_t length = buf.ReadExtendedByte();
2178 if (statspec->bridge_above_flags.size() < length) statspec->bridge_above_flags.resize(length);
2179 for (size_t i = 0; i < length; i++) {
2180 statspec->bridge_above_flags[i].height = buf.ReadByte();
2182 break;
2185 case 0x1B: // Minimum height for a bridge above
2186 SetBit(statspec->internal_flags, SSIF_BRIDGE_HEIGHTS_SET);
2187 if (statspec->bridge_above_flags.size() < 8) statspec->bridge_above_flags.resize(8);
2188 for (uint i = 0; i < 8; i++) {
2189 statspec->bridge_above_flags[i].height = buf.ReadByte();
2191 break;
2193 case A0RPI_STATION_DISALLOWED_BRIDGE_PILLARS: {
2194 SetBit(statspec->internal_flags, SSIF_BRIDGE_DISALLOWED_PILLARS_SET);
2195 size_t length = buf.ReadExtendedByte();
2196 if (statspec->bridge_above_flags.size() < length) statspec->bridge_above_flags.resize(length);
2197 for (size_t i = 0; i < length; i++) {
2198 statspec->bridge_above_flags[i].disallowed_pillars = buf.ReadByte();
2200 break;
2203 case 0x1C: // Station Name
2204 AddStringForMapping(buf.ReadWord(), &statspec->name);
2205 break;
2207 case 0x1D: // Station Class name
2208 AddStringForMapping(buf.ReadWord(), statspec, [](StringID str, StationSpec *statspec) { StationClass::Get(statspec->class_index)->name = str; });
2209 break;
2211 case 0x1E: { // Extended tile flags (replaces prop 11, 14 and 15)
2212 uint16_t tiles = buf.ReadExtendedByte();
2213 auto flags = reinterpret_cast<const StationSpec::TileFlags *>(buf.ReadBytes(tiles));
2214 statspec->tileflags.assign(flags, flags + tiles);
2215 break;
2218 default:
2219 ret = HandleAction0PropertyDefault(buf, prop);
2220 break;
2224 return ret;
2228 * Define properties for water features
2229 * @param id Type of the first water feature.
2230 * @param numinfo Number of subsequent water feature ids to change the property for.
2231 * @param prop The property to change.
2232 * @param buf The property value.
2233 * @return ChangeInfoResult.
2235 static ChangeInfoResult CanalChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
2237 ChangeInfoResult ret = CIR_SUCCESS;
2239 if (id + numinfo > CF_END) {
2240 GrfMsg(1, "CanalChangeInfo: Canal feature 0x{:02X} is invalid, max {}, ignoring", id + numinfo, CF_END);
2241 return CIR_INVALID_ID;
2244 for (int i = 0; i < numinfo; i++) {
2245 CanalProperties *cp = &_cur.grffile->canal_local_properties[id + i];
2247 switch (prop) {
2248 case 0x08:
2249 cp->callback_mask = buf.ReadByte();
2250 break;
2252 case 0x09:
2253 cp->flags = buf.ReadByte();
2254 break;
2256 default:
2257 ret = HandleAction0PropertyDefault(buf, prop);
2258 break;
2262 return ret;
2266 * Define properties for bridges
2267 * @param brid BridgeID of the bridge.
2268 * @param numinfo Number of subsequent bridgeIDs to change the property for.
2269 * @param prop The property to change.
2270 * @param buf The property value.
2271 * @return ChangeInfoResult.
2273 static ChangeInfoResult BridgeChangeInfo(uint brid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
2275 ChangeInfoResult ret = CIR_SUCCESS;
2277 if (brid + numinfo > MAX_BRIDGES) {
2278 GrfMsg(1, "BridgeChangeInfo: Bridge {} is invalid, max {}, ignoring", brid + numinfo, MAX_BRIDGES);
2279 return CIR_INVALID_ID;
2282 for (int i = 0; i < numinfo; i++) {
2283 BridgeSpec *bridge = &_bridge[brid + i];
2285 switch (prop) {
2286 case 0x08: { // Year of availability
2287 /* We treat '0' as always available */
2288 uint8_t year = buf.ReadByte();
2289 bridge->avail_year = (year > 0 ? CalTime::ORIGINAL_BASE_YEAR + year : CalTime::Year{0});
2290 break;
2293 case 0x09: // Minimum length
2294 bridge->min_length = buf.ReadByte();
2295 break;
2297 case 0x0A: // Maximum length
2298 bridge->max_length = buf.ReadByte();
2299 if (bridge->max_length > 16) bridge->max_length = UINT16_MAX;
2300 break;
2302 case 0x0B: // Cost factor
2303 bridge->price = buf.ReadByte();
2304 break;
2306 case 0x0C: // Maximum speed
2307 bridge->speed = buf.ReadWord();
2308 if (bridge->speed == 0) bridge->speed = UINT16_MAX;
2309 break;
2311 case 0x0D: { // Bridge sprite tables
2312 uint8_t tableid = buf.ReadByte();
2313 uint8_t numtables = buf.ReadByte();
2315 if (bridge->sprite_table == nullptr) {
2316 /* Allocate memory for sprite table pointers and zero out */
2317 bridge->sprite_table = CallocT<PalSpriteID*>(NUM_BRIDGE_PIECES);
2320 for (; numtables-- != 0; tableid++) {
2321 if (tableid >= NUM_BRIDGE_PIECES) { // skip invalid data
2322 GrfMsg(1, "BridgeChangeInfo: Table {} >= {}, skipping", tableid, NUM_BRIDGE_PIECES);
2323 for (uint8_t sprite = 0; sprite < SPRITES_PER_BRIDGE_PIECE; sprite++) buf.ReadDWord();
2324 continue;
2327 if (bridge->sprite_table[tableid] == nullptr) {
2328 bridge->sprite_table[tableid] = MallocT<PalSpriteID>(SPRITES_PER_BRIDGE_PIECE);
2331 for (uint8_t sprite = 0; sprite < SPRITES_PER_BRIDGE_PIECE; sprite++) {
2332 SpriteID image = buf.ReadWord();
2333 PaletteID pal = buf.ReadWord();
2335 bridge->sprite_table[tableid][sprite].sprite = image;
2336 bridge->sprite_table[tableid][sprite].pal = pal;
2338 MapSpriteMappingRecolour(&bridge->sprite_table[tableid][sprite]);
2341 if (!HasBit(bridge->ctrl_flags, BSCF_CUSTOM_PILLAR_FLAGS)) SetBit(bridge->ctrl_flags, BSCF_INVALID_PILLAR_FLAGS);
2342 break;
2345 case 0x0E: // Flags; bit 0 - disable far pillars
2346 bridge->flags = buf.ReadByte();
2347 break;
2349 case 0x0F: // Long format year of availability (year since year 0)
2350 bridge->avail_year = CalTime::DeserialiseYearClamped(static_cast<int32_t>(buf.ReadDWord()));
2351 break;
2353 case 0x10: { // purchase string
2354 StringID newone = GetGRFStringID(_cur.grffile, buf.ReadWord());
2355 if (newone != STR_UNDEFINED) bridge->material = newone;
2356 break;
2359 case 0x11: // description of bridge with rails or roads
2360 case 0x12: {
2361 StringID newone = GetGRFStringID(_cur.grffile, buf.ReadWord());
2362 if (newone != STR_UNDEFINED) bridge->transport_name[prop - 0x11] = newone;
2363 break;
2366 case 0x13: // 16 bits cost multiplier
2367 bridge->price = buf.ReadWord();
2368 break;
2370 case A0RPI_BRIDGE_MENU_ICON:
2371 if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
2372 [[fallthrough]];
2373 case 0x14: // purchase sprite
2374 bridge->sprite = buf.ReadWord();
2375 bridge->pal = buf.ReadWord();
2376 break;
2378 case A0RPI_BRIDGE_PILLAR_FLAGS:
2379 if (MappedPropertyLengthMismatch(buf, 12, mapping_entry)) break;
2380 for (uint i = 0; i < 12; i++) {
2381 bridge->pillar_flags[i] = buf.ReadByte();
2383 ClrBit(bridge->ctrl_flags, BSCF_INVALID_PILLAR_FLAGS);
2384 SetBit(bridge->ctrl_flags, BSCF_CUSTOM_PILLAR_FLAGS);
2385 break;
2387 case A0RPI_BRIDGE_AVAILABILITY_FLAGS: {
2388 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
2389 uint8_t flags = buf.ReadByte();
2390 AssignBit(bridge->ctrl_flags, BSCF_NOT_AVAILABLE_TOWN, HasBit(flags, 0));
2391 AssignBit(bridge->ctrl_flags, BSCF_NOT_AVAILABLE_AI_GS, HasBit(flags, 1));
2392 break;
2395 default:
2396 ret = HandleAction0PropertyDefault(buf, prop);
2397 break;
2401 return ret;
2405 * Ignore a house property
2406 * @param prop Property to read.
2407 * @param buf Property value.
2408 * @return ChangeInfoResult.
2410 static ChangeInfoResult IgnoreTownHouseProperty(int prop, ByteReader &buf)
2412 ChangeInfoResult ret = CIR_SUCCESS;
2414 switch (prop) {
2415 case 0x09:
2416 case 0x0B:
2417 case 0x0C:
2418 case 0x0D:
2419 case 0x0E:
2420 case 0x0F:
2421 case 0x11:
2422 case 0x14:
2423 case 0x15:
2424 case 0x16:
2425 case 0x18:
2426 case 0x19:
2427 case 0x1A:
2428 case 0x1B:
2429 case 0x1C:
2430 case 0x1D:
2431 case 0x1F:
2432 buf.ReadByte();
2433 break;
2435 case 0x0A:
2436 case 0x10:
2437 case 0x12:
2438 case 0x13:
2439 case 0x21:
2440 case 0x22:
2441 buf.ReadWord();
2442 break;
2444 case 0x1E:
2445 buf.ReadDWord();
2446 break;
2448 case 0x17:
2449 for (uint j = 0; j < 4; j++) buf.ReadByte();
2450 break;
2452 case 0x20: {
2453 uint8_t count = buf.ReadByte();
2454 for (uint8_t j = 0; j < count; j++) buf.ReadByte();
2455 break;
2458 case 0x23:
2459 buf.Skip(buf.ReadByte() * 2);
2460 break;
2462 default:
2463 ret = HandleAction0PropertyDefault(buf, prop);
2464 break;
2466 return ret;
2470 * Define properties for houses
2471 * @param hid HouseID of the house.
2472 * @param numinfo Number of subsequent houseIDs to change the property for.
2473 * @param prop The property to change.
2474 * @param buf The property value.
2475 * @return ChangeInfoResult.
2477 static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
2479 ChangeInfoResult ret = CIR_SUCCESS;
2481 if (hid + numinfo > NUM_HOUSES_PER_GRF) {
2482 GrfMsg(1, "TownHouseChangeInfo: Too many houses loaded ({}), max ({}). Ignoring.", hid + numinfo, NUM_HOUSES_PER_GRF);
2483 return CIR_INVALID_ID;
2486 /* Allocate house specs if they haven't been allocated already. */
2487 if (_cur.grffile->housespec.size() < hid + numinfo) _cur.grffile->housespec.resize(hid + numinfo);
2489 for (int i = 0; i < numinfo; i++) {
2490 HouseSpec *housespec = _cur.grffile->housespec[hid + i].get();
2492 if (prop != 0x08 && housespec == nullptr) {
2493 /* If the house property 08 is not yet set, ignore this property */
2494 ChangeInfoResult cir = IgnoreTownHouseProperty(prop, buf);
2495 if (cir > ret) ret = cir;
2496 continue;
2499 switch (prop) {
2500 case 0x08: { // Substitute building type, and definition of a new house
2501 uint8_t subs_id = buf.ReadByte();
2502 if (subs_id == 0xFF) {
2503 /* Instead of defining a new house, a substitute house id
2504 * of 0xFF disables the old house with the current id. */
2505 if (hid + i < NEW_HOUSE_OFFSET) HouseSpec::Get(hid + i)->enabled = false;
2506 continue;
2507 } else if (subs_id >= NEW_HOUSE_OFFSET) {
2508 /* The substitute id must be one of the original houses. */
2509 GrfMsg(2, "TownHouseChangeInfo: Attempt to use new house {} as substitute house for {}. Ignoring.", subs_id, hid + i);
2510 continue;
2513 /* Allocate space for this house. */
2514 if (housespec == nullptr) {
2515 /* Only the first property 08 setting copies properties; if you later change it, properties will stay. */
2516 _cur.grffile->housespec[hid + i] = std::make_unique<HouseSpec>(*HouseSpec::Get(subs_id));
2517 housespec = _cur.grffile->housespec[hid + i].get();
2519 housespec->enabled = true;
2520 housespec->grf_prop.local_id = hid + i;
2521 housespec->grf_prop.subst_id = subs_id;
2522 housespec->grf_prop.grfid = _cur.grffile->grfid;
2523 housespec->grf_prop.grffile = _cur.grffile;
2524 /* Set default colours for randomization, used if not overridden. */
2525 housespec->random_colour[0] = COLOUR_RED;
2526 housespec->random_colour[1] = COLOUR_BLUE;
2527 housespec->random_colour[2] = COLOUR_ORANGE;
2528 housespec->random_colour[3] = COLOUR_GREEN;
2530 /* House flags 40 and 80 are exceptions; these flags are never set automatically. */
2531 housespec->building_flags &= ~(BUILDING_IS_CHURCH | BUILDING_IS_STADIUM);
2533 /* Make sure that the third cargo type is valid in this
2534 * climate. This can cause problems when copying the properties
2535 * of a house that accepts food, where the new house is valid
2536 * in the temperate climate. */
2537 CargoID cid = housespec->accepts_cargo[2];
2538 if (!IsValidCargoID(cid)) cid = GetCargoIDByLabel(housespec->accepts_cargo_label[2]);
2539 if (!IsValidCargoID(cid)) {
2540 housespec->cargo_acceptance[2] = 0;
2543 break;
2546 case 0x09: // Building flags
2547 housespec->building_flags = (BuildingFlags)buf.ReadByte();
2548 break;
2550 case 0x0A: { // Availability years
2551 uint16_t years = buf.ReadWord();
2552 housespec->min_year = GB(years, 0, 8) > 150 ? CalTime::MAX_YEAR : CalTime::ORIGINAL_BASE_YEAR + GB(years, 0, 8);
2553 housespec->max_year = GB(years, 8, 8) > 150 ? CalTime::MAX_YEAR : CalTime::ORIGINAL_BASE_YEAR + GB(years, 8, 8);
2554 break;
2557 case 0x0B: // Population
2558 housespec->population = buf.ReadByte();
2559 break;
2561 case 0x0C: // Mail generation multiplier
2562 housespec->mail_generation = buf.ReadByte();
2563 break;
2565 case 0x0D: // Passenger acceptance
2566 case 0x0E: // Mail acceptance
2567 housespec->cargo_acceptance[prop - 0x0D] = buf.ReadByte();
2568 break;
2570 case 0x0F: { // Goods/candy, food/fizzy drinks acceptance
2571 int8_t goods = buf.ReadByte();
2573 /* If value of goods is negative, it means in fact food or, if in toyland, fizzy_drink acceptance.
2574 * Else, we have "standard" 3rd cargo type, goods or candy, for toyland once more */
2575 CargoID cid = (goods >= 0) ? ((_settings_game.game_creation.landscape == LT_TOYLAND) ? GetCargoIDByLabel(CT_CANDY) : GetCargoIDByLabel(CT_GOODS)) :
2576 ((_settings_game.game_creation.landscape == LT_TOYLAND) ? GetCargoIDByLabel(CT_FIZZY_DRINKS) : GetCargoIDByLabel(CT_FOOD));
2578 /* Make sure the cargo type is valid in this climate. */
2579 if (!IsValidCargoID(cid)) goods = 0;
2581 housespec->accepts_cargo[2] = cid;
2582 housespec->accepts_cargo_label[2] = CT_INVALID;
2583 housespec->cargo_acceptance[2] = abs(goods); // but we do need positive value here
2584 break;
2587 case 0x10: // Local authority rating decrease on removal
2588 housespec->remove_rating_decrease = buf.ReadWord();
2589 break;
2591 case 0x11: // Removal cost multiplier
2592 housespec->removal_cost = buf.ReadByte();
2593 break;
2595 case 0x12: // Building name ID
2596 AddStringForMapping(buf.ReadWord(), &housespec->building_name);
2597 break;
2599 case 0x13: // Building availability mask
2600 housespec->building_availability = (HouseZones)buf.ReadWord();
2601 break;
2603 case 0x14: // House callback mask
2604 housespec->callback_mask |= buf.ReadByte();
2605 break;
2607 case 0x15: { // House override byte
2608 uint8_t override = buf.ReadByte();
2610 /* The house being overridden must be an original house. */
2611 if (override >= NEW_HOUSE_OFFSET) {
2612 GrfMsg(2, "TownHouseChangeInfo: Attempt to override new house {} with house id {}. Ignoring.", override, hid + i);
2613 continue;
2616 _house_mngr.Add(hid + i, _cur.grffile->grfid, override);
2617 break;
2620 case 0x16: // Periodic refresh multiplier
2621 housespec->processing_time = std::min<uint8_t>(buf.ReadByte(), 63u);
2622 break;
2624 case 0x17: // Four random colours to use
2625 for (uint j = 0; j < 4; j++) housespec->random_colour[j] = static_cast<Colours>(GB(buf.ReadByte(), 0, 4));
2626 break;
2628 case 0x18: // Relative probability of appearing
2629 housespec->probability = buf.ReadByte();
2630 break;
2632 case 0x19: // Extra flags
2633 housespec->extra_flags = (HouseExtraFlags)buf.ReadByte();
2634 break;
2636 case 0x1A: // Animation frames
2637 housespec->animation.frames = buf.ReadByte();
2638 housespec->animation.status = GB(housespec->animation.frames, 7, 1);
2639 SB(housespec->animation.frames, 7, 1, 0);
2640 break;
2642 case 0x1B: // Animation speed
2643 housespec->animation.speed = Clamp(buf.ReadByte(), 2, 16);
2644 break;
2646 case 0x1C: // Class of the building type
2647 housespec->class_id = AllocateHouseClassID(buf.ReadByte(), _cur.grffile->grfid);
2648 break;
2650 case 0x1D: // Callback mask part 2
2651 housespec->callback_mask |= (buf.ReadByte() << 8);
2652 break;
2654 case 0x1E: { // Accepted cargo types
2655 uint32_t cargotypes = buf.ReadDWord();
2657 /* Check if the cargo types should not be changed */
2658 if (cargotypes == 0xFFFFFFFF) break;
2660 for (uint j = 0; j < HOUSE_ORIGINAL_NUM_ACCEPTS; j++) {
2661 /* Get the cargo number from the 'list' */
2662 uint8_t cargo_part = GB(cargotypes, 8 * j, 8);
2663 CargoID cargo = GetCargoTranslation(cargo_part, _cur.grffile);
2665 if (!IsValidCargoID(cargo)) {
2666 /* Disable acceptance of invalid cargo type */
2667 housespec->cargo_acceptance[j] = 0;
2668 } else {
2669 housespec->accepts_cargo[j] = cargo;
2671 housespec->accepts_cargo_label[j] = CT_INVALID;
2673 break;
2676 case 0x1F: // Minimum life span
2677 housespec->minimum_life = buf.ReadByte();
2678 break;
2680 case 0x20: { // Cargo acceptance watch list
2681 uint8_t count = buf.ReadByte();
2682 for (uint8_t j = 0; j < count; j++) {
2683 CargoID cargo = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
2684 if (IsValidCargoID(cargo)) SetBit(housespec->watched_cargoes, cargo);
2686 break;
2689 case 0x21: // long introduction year
2690 housespec->min_year = CalTime::Year{buf.ReadWord()};
2691 break;
2693 case 0x22: // long maximum year
2694 housespec->max_year = housespec->max_year = CalTime::Year{buf.ReadWord()};
2695 if (housespec->max_year == UINT16_MAX) housespec->max_year = CalTime::MAX_YEAR;
2696 break;
2698 case 0x23: { // variable length cargo types accepted
2699 uint count = buf.ReadByte();
2700 if (count > lengthof(housespec->accepts_cargo)) {
2701 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
2702 error->param_value[1] = prop;
2703 return CIR_DISABLED;
2705 /* Always write the full accepts_cargo array, and check each index for being inside the
2706 * provided data. This ensures all values are properly initialized, and also avoids
2707 * any risks of array overrun. */
2708 for (uint i = 0; i < lengthof(housespec->accepts_cargo); i++) {
2709 if (i < count) {
2710 housespec->accepts_cargo[i] = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
2711 housespec->cargo_acceptance[i] = buf.ReadByte();
2712 } else {
2713 housespec->accepts_cargo[i] = INVALID_CARGO;
2714 housespec->cargo_acceptance[i] = 0;
2716 if (i < std::size(housespec->accepts_cargo_label)) housespec->accepts_cargo_label[i] = CT_INVALID;
2718 break;
2721 default:
2722 ret = HandleAction0PropertyDefault(buf, prop);
2723 break;
2727 return ret;
2731 * Get the language map associated with a given NewGRF and language.
2732 * @param grfid The NewGRF to get the map for.
2733 * @param language_id The (NewGRF) language ID to get the map for.
2734 * @return The LanguageMap, or nullptr if it couldn't be found.
2736 /* static */ const LanguageMap *LanguageMap::GetLanguageMap(uint32_t grfid, uint8_t language_id)
2738 /* LanguageID "MAX_LANG", i.e. 7F is any. This language can't have a gender/case mapping, but has to be handled gracefully. */
2739 const GRFFile *grffile = GetFileByGRFID(grfid);
2740 if (grffile == nullptr) return nullptr;
2742 auto it = grffile->language_map.find(language_id);
2743 if (it == std::end(grffile->language_map)) return nullptr;
2745 return &it->second;
2749 * Load a cargo- or railtype-translation table.
2750 * @param gvid ID of the global variable. This is basically only checked for zerones.
2751 * @param numinfo Number of subsequent IDs to change the property for.
2752 * @param buf The property value.
2753 * @param gettable Function to get storage for the translation table.
2754 * @param name Name of the table for debug output.
2755 * @return ChangeInfoResult.
2757 template <typename T, typename TGetTableFunc>
2758 static ChangeInfoResult LoadTranslationTable(uint gvid, int numinfo, ByteReader &buf, TGetTableFunc gettable, std::string_view name)
2760 if (gvid != 0) {
2761 GrfMsg(1, "LoadTranslationTable: {} translation table must start at zero", name);
2762 return CIR_INVALID_ID;
2765 std::vector<T> &translation_table = gettable(*_cur.grffile);
2766 translation_table.clear();
2767 translation_table.reserve(numinfo);
2768 for (int i = 0; i < numinfo; i++) {
2769 translation_table.push_back(T(BSWAP32(buf.ReadDWord())));
2772 GRFFile *grf_override = GetCurrentGRFOverride();
2773 if (grf_override != nullptr) {
2774 /* GRF override is present, copy the translation table to the overridden GRF as well. */
2775 GrfMsg(1, "LoadTranslationTable: Copying {} translation table to override GRFID '{}'", name, BSWAP32(grf_override->grfid));
2776 std::vector<T> &override_table = gettable(*grf_override);
2777 override_table = translation_table;
2780 return CIR_SUCCESS;
2784 * Helper to read a DWord worth of bytes from the reader
2785 * and to return it as a valid string.
2786 * @param reader The source of the DWord.
2787 * @return The read DWord as string.
2789 static std::string ReadDWordAsString(ByteReader &reader)
2791 std::string output;
2792 for (int i = 0; i < 4; i++) output.push_back(reader.ReadByte());
2793 return StrMakeValid(output);
2797 * Define properties for global variables
2798 * @param gvid ID of the global variable.
2799 * @param numinfo Number of subsequent IDs to change the property for.
2800 * @param prop The property to change.
2801 * @param buf The property value.
2802 * @return ChangeInfoResult.
2804 static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
2806 /* Properties which are handled as a whole */
2807 switch (prop) {
2808 case 0x09: // Cargo Translation Table; loading during both reservation and activation stage (in case it is selected depending on defined cargos)
2809 return LoadTranslationTable<CargoLabel>(gvid, numinfo, buf, [](GRFFile &grf) -> std::vector<CargoLabel> & { return grf.cargo_list; }, "Cargo");
2811 case 0x12: // Rail type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes)
2812 return LoadTranslationTable<RailTypeLabel>(gvid, numinfo, buf, [](GRFFile &grf) -> std::vector<RailTypeLabel> & { return grf.railtype_list; }, "Rail type");
2814 case 0x16: // Road type translation table; loading during both reservation and activation stage (in case it is selected depending on defined roadtypes)
2815 return LoadTranslationTable<RoadTypeLabel>(gvid, numinfo, buf, [](GRFFile &grf) -> std::vector<RoadTypeLabel> & { return grf.roadtype_list; }, "Road type");
2817 case 0x17: // Tram type translation table; loading during both reservation and activation stage (in case it is selected depending on defined tramtypes)
2818 return LoadTranslationTable<RoadTypeLabel>(gvid, numinfo, buf, [](GRFFile &grf) -> std::vector<RoadTypeLabel> & { return grf.tramtype_list; }, "Tram type");
2820 default:
2821 break;
2824 /* Properties which are handled per item */
2825 ChangeInfoResult ret = CIR_SUCCESS;
2826 for (int i = 0; i < numinfo; i++) {
2827 switch (prop) {
2828 case 0x08: { // Cost base factor
2829 int factor = buf.ReadByte();
2830 uint price = gvid + i;
2832 if (price < PR_END) {
2833 _cur.grffile->price_base_multipliers[price] = std::min<int>(factor - 8, MAX_PRICE_MODIFIER);
2834 } else {
2835 GrfMsg(1, "GlobalVarChangeInfo: Price {} out of range, ignoring", price);
2837 break;
2840 case 0x0A: { // Currency display names
2841 uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
2842 StringID newone = GetGRFStringID(_cur.grffile, buf.ReadWord());
2844 if ((newone != STR_UNDEFINED) && (curidx < CURRENCY_END)) {
2845 _currency_specs[curidx].name = newone;
2846 _currency_specs[curidx].code.clear();
2848 break;
2851 case 0x0B: { // Currency multipliers
2852 uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
2853 uint32_t rate = buf.ReadDWord();
2855 if (curidx < CURRENCY_END) {
2856 /* TTDPatch uses a multiple of 1000 for its conversion calculations,
2857 * which OTTD does not. For this reason, divide grf value by 1000,
2858 * to be compatible */
2859 _currency_specs[curidx].rate = rate / 1000;
2860 } else {
2861 GrfMsg(1, "GlobalVarChangeInfo: Currency multipliers {} out of range, ignoring", curidx);
2863 break;
2866 case 0x0C: { // Currency options
2867 uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
2868 uint16_t options = buf.ReadWord();
2870 if (curidx < CURRENCY_END) {
2871 _currency_specs[curidx].separator.clear();
2872 _currency_specs[curidx].separator.push_back(GB(options, 0, 8));
2873 /* By specifying only one bit, we prevent errors,
2874 * since newgrf specs said that only 0 and 1 can be set for symbol_pos */
2875 _currency_specs[curidx].symbol_pos = GB(options, 8, 1);
2876 } else {
2877 GrfMsg(1, "GlobalVarChangeInfo: Currency option {} out of range, ignoring", curidx);
2879 break;
2882 case 0x0D: { // Currency prefix symbol
2883 uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
2884 std::string prefix = ReadDWordAsString(buf);
2886 if (curidx < CURRENCY_END) {
2887 _currency_specs[curidx].prefix = prefix;
2888 } else {
2889 GrfMsg(1, "GlobalVarChangeInfo: Currency symbol {} out of range, ignoring", curidx);
2891 break;
2894 case 0x0E: { // Currency suffix symbol
2895 uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
2896 std::string suffix = ReadDWordAsString(buf);
2898 if (curidx < CURRENCY_END) {
2899 _currency_specs[curidx].suffix = suffix;
2900 } else {
2901 GrfMsg(1, "GlobalVarChangeInfo: Currency symbol {} out of range, ignoring", curidx);
2903 break;
2906 case 0x0F: { // Euro introduction dates
2907 uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
2908 CalTime::Year year_euro{buf.ReadWord()};
2910 if (curidx < CURRENCY_END) {
2911 _currency_specs[curidx].to_euro = year_euro;
2912 } else {
2913 GrfMsg(1, "GlobalVarChangeInfo: Euro intro date {} out of range, ignoring", curidx);
2915 break;
2918 case 0x10: // Snow line height table
2919 if (numinfo > 1 || IsSnowLineSet()) {
2920 GrfMsg(1, "GlobalVarChangeInfo: The snowline can only be set once ({})", numinfo);
2921 } else if (buf.Remaining() < SNOW_LINE_MONTHS * SNOW_LINE_DAYS) {
2922 GrfMsg(1, "GlobalVarChangeInfo: Not enough entries set in the snowline table ({})", buf.Remaining());
2923 } else {
2924 uint8_t table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS];
2926 for (uint i = 0; i < SNOW_LINE_MONTHS; i++) {
2927 for (uint j = 0; j < SNOW_LINE_DAYS; j++) {
2928 table[i][j] = buf.ReadByte();
2929 if (_cur.grffile->grf_version >= 8) {
2930 if (table[i][j] != 0xFF) table[i][j] = table[i][j] * (1 + _settings_game.construction.map_height_limit) / 256;
2931 } else {
2932 if (table[i][j] >= 128) {
2933 /* no snow */
2934 table[i][j] = 0xFF;
2935 } else {
2936 table[i][j] = table[i][j] * (1 + _settings_game.construction.map_height_limit) / 128;
2941 SetSnowLine(table);
2943 break;
2945 case 0x11: // GRF match for engine allocation
2946 /* This is loaded during the reservation stage, so just skip it here. */
2947 /* Each entry is 8 bytes. */
2948 buf.Skip(8);
2949 break;
2951 case 0x13: // Gender translation table
2952 case 0x14: // Case translation table
2953 case 0x15: { // Plural form translation
2954 uint curidx = gvid + i; // The current index, i.e. language.
2955 const LanguageMetadata *lang = curidx < MAX_LANG ? GetLanguage(curidx) : nullptr;
2956 if (lang == nullptr) {
2957 GrfMsg(1, "GlobalVarChangeInfo: Language {} is not known, ignoring", curidx);
2958 /* Skip over the data. */
2959 if (prop == 0x15) {
2960 buf.ReadByte();
2961 } else {
2962 while (buf.ReadByte() != 0) {
2963 buf.ReadString();
2966 break;
2969 if (prop == 0x15) {
2970 uint plural_form = buf.ReadByte();
2971 if (plural_form >= LANGUAGE_MAX_PLURAL) {
2972 GrfMsg(1, "GlobalVarChanceInfo: Plural form {} is out of range, ignoring", plural_form);
2973 } else {
2974 _cur.grffile->language_map[curidx].plural_form = plural_form;
2976 break;
2979 uint8_t newgrf_id = buf.ReadByte(); // The NewGRF (custom) identifier.
2980 while (newgrf_id != 0) {
2981 std::string_view name = buf.ReadString(); // The name for the OpenTTD identifier.
2983 /* We'll just ignore the UTF8 identifier character. This is (fairly)
2984 * safe as OpenTTD's strings gender/cases are usually in ASCII which
2985 * is just a subset of UTF8, or they need the bigger UTF8 characters
2986 * such as Cyrillic. Thus we will simply assume they're all UTF8. */
2987 char32_t c;
2988 size_t len = Utf8Decode(&c, name.data());
2989 if (c == NFO_UTF8_IDENTIFIER) name = name.substr(len);
2991 LanguageMap::Mapping map;
2992 map.newgrf_id = newgrf_id;
2993 if (prop == 0x13) {
2994 map.openttd_id = lang->GetGenderIndex(name.data());
2995 if (map.openttd_id >= MAX_NUM_GENDERS) {
2996 GrfMsg(1, "GlobalVarChangeInfo: Gender name {} is not known, ignoring", StrMakeValid(name));
2997 } else {
2998 _cur.grffile->language_map[curidx].gender_map.push_back(map);
3000 } else {
3001 map.openttd_id = lang->GetCaseIndex(name.data());
3002 if (map.openttd_id >= MAX_NUM_CASES) {
3003 GrfMsg(1, "GlobalVarChangeInfo: Case name {} is not known, ignoring", StrMakeValid(name));
3004 } else {
3005 _cur.grffile->language_map[curidx].case_map.push_back(map);
3008 newgrf_id = buf.ReadByte();
3010 break;
3013 case A0RPI_GLOBALVAR_EXTRA_STATION_NAMES: {
3014 if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
3015 uint16_t str = buf.ReadWord();
3016 uint16_t flags = buf.ReadWord();
3017 if (_extra_station_names.size() < MAX_EXTRA_STATION_NAMES) {
3018 size_t idx = _extra_station_names.size();
3019 ExtraStationNameInfo &info = _extra_station_names.emplace_back();
3020 AddStringForMapping(str, idx, [](StringID str, size_t idx) { _extra_station_names[idx].str = str; });
3021 info.flags = flags;
3023 break;
3026 case A0RPI_GLOBALVAR_EXTRA_STATION_NAMES_PROBABILITY: {
3027 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
3028 _extra_station_names_probability = buf.ReadByte();
3029 break;
3032 case A0RPI_GLOBALVAR_LIGHTHOUSE_GENERATE_AMOUNT:
3033 case A0RPI_GLOBALVAR_TRANSMITTER_GENERATE_AMOUNT: {
3034 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
3035 extern std::vector<ObjectSpec> _object_specs;
3036 ObjectType type = (prop == A0RPI_GLOBALVAR_LIGHTHOUSE_GENERATE_AMOUNT) ? OBJECT_LIGHTHOUSE : OBJECT_TRANSMITTER;
3037 _object_specs[type].generate_amount = buf.ReadByte();
3038 break;
3041 case A0RPI_GLOBALVAR_ALLOW_ROCKS_DESERT: {
3042 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
3043 extern bool _allow_rocks_desert;
3044 _allow_rocks_desert = (buf.ReadByte() != 0);
3045 break;
3048 default:
3049 ret = HandleAction0PropertyDefault(buf, prop);
3050 break;
3054 return ret;
3057 static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
3059 /* Properties which are handled as a whole */
3060 switch (prop) {
3061 case 0x09: // Cargo Translation Table; loading during both reservation and activation stage (in case it is selected depending on defined cargos)
3062 return LoadTranslationTable<CargoLabel>(gvid, numinfo, buf, [](GRFFile &grf) -> std::vector<CargoLabel> & { return grf.cargo_list; }, "Cargo");
3064 case 0x12: // Rail type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes)
3065 return LoadTranslationTable<RailTypeLabel>(gvid, numinfo, buf, [](GRFFile &grf) -> std::vector<RailTypeLabel> & { return grf.railtype_list; }, "Rail type");
3067 case 0x16: // Road type translation table; loading during both reservation and activation stage (in case it is selected depending on defined roadtypes)
3068 return LoadTranslationTable<RoadTypeLabel>(gvid, numinfo, buf, [](GRFFile &grf) -> std::vector<RoadTypeLabel> & { return grf.roadtype_list; }, "Road type");
3070 case 0x17: // Tram type translation table; loading during both reservation and activation stage (in case it is selected depending on defined tramtypes)
3071 return LoadTranslationTable<RoadTypeLabel>(gvid, numinfo, buf, [](GRFFile &grf) -> std::vector<RoadTypeLabel> & { return grf.tramtype_list; }, "Tram type");
3073 default:
3074 break;
3077 /* Properties which are handled per item */
3078 ChangeInfoResult ret = CIR_SUCCESS;
3079 for (int i = 0; i < numinfo; i++) {
3080 switch (prop) {
3081 case 0x08: // Cost base factor
3082 case 0x15: // Plural form translation
3083 buf.ReadByte();
3084 break;
3086 case 0x0A: // Currency display names
3087 case 0x0C: // Currency options
3088 case 0x0F: // Euro introduction dates
3089 buf.ReadWord();
3090 break;
3092 case 0x0B: // Currency multipliers
3093 case 0x0D: // Currency prefix symbol
3094 case 0x0E: // Currency suffix symbol
3095 buf.ReadDWord();
3096 break;
3098 case 0x10: // Snow line height table
3099 buf.Skip(SNOW_LINE_MONTHS * SNOW_LINE_DAYS);
3100 break;
3102 case 0x11: { // GRF match for engine allocation
3103 uint32_t s = buf.ReadDWord();
3104 uint32_t t = buf.ReadDWord();
3105 SetNewGRFOverride(s, t);
3106 break;
3109 case 0x13: // Gender translation table
3110 case 0x14: // Case translation table
3111 while (buf.ReadByte() != 0) {
3112 buf.ReadString();
3114 break;
3116 case A0RPI_GLOBALVAR_EXTRA_STATION_NAMES:
3117 case A0RPI_GLOBALVAR_EXTRA_STATION_NAMES_PROBABILITY:
3118 case A0RPI_GLOBALVAR_LIGHTHOUSE_GENERATE_AMOUNT:
3119 case A0RPI_GLOBALVAR_TRANSMITTER_GENERATE_AMOUNT:
3120 case A0RPI_GLOBALVAR_ALLOW_ROCKS_DESERT:
3121 buf.Skip(buf.ReadExtendedByte());
3122 break;
3124 default:
3125 ret = HandleAction0PropertyDefault(buf, prop);
3126 break;
3130 return ret;
3135 * Define properties for cargoes
3136 * @param cid Local ID of the cargo.
3137 * @param numinfo Number of subsequent IDs to change the property for.
3138 * @param prop The property to change.
3139 * @param buf The property value.
3140 * @return ChangeInfoResult.
3142 static ChangeInfoResult CargoChangeInfo(uint cid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
3144 ChangeInfoResult ret = CIR_SUCCESS;
3146 if (cid + numinfo > NUM_CARGO) {
3147 GrfMsg(2, "CargoChangeInfo: Cargo type {} out of range (max {})", cid + numinfo, NUM_CARGO - 1);
3148 return CIR_INVALID_ID;
3151 for (int i = 0; i < numinfo; i++) {
3152 CargoSpec *cs = CargoSpec::Get(cid + i);
3154 switch (prop) {
3155 case 0x08: // Bit number of cargo
3156 cs->bitnum = buf.ReadByte();
3157 if (cs->IsValid()) {
3158 cs->grffile = _cur.grffile;
3159 SetBit(_cargo_mask, cid + i);
3160 } else {
3161 ClrBit(_cargo_mask, cid + i);
3163 BuildCargoLabelMap();
3164 break;
3166 case 0x09: // String ID for cargo type name
3167 AddStringForMapping(buf.ReadWord(), &cs->name);
3168 break;
3170 case 0x0A: // String for 1 unit of cargo
3171 AddStringForMapping(buf.ReadWord(), &cs->name_single);
3172 break;
3174 case 0x0B: // String for singular quantity of cargo (e.g. 1 tonne of coal)
3175 case 0x1B: // String for cargo units
3176 /* String for units of cargo. This is different in OpenTTD
3177 * (e.g. tonnes) to TTDPatch (e.g. {COMMA} tonne of coal).
3178 * Property 1B is used to set OpenTTD's behaviour. */
3179 AddStringForMapping(buf.ReadWord(), &cs->units_volume);
3180 break;
3182 case 0x0C: // String for plural quantity of cargo (e.g. 10 tonnes of coal)
3183 case 0x1C: // String for any amount of cargo
3184 /* Strings for an amount of cargo. This is different in OpenTTD
3185 * (e.g. {WEIGHT} of coal) to TTDPatch (e.g. {COMMA} tonnes of coal).
3186 * Property 1C is used to set OpenTTD's behaviour. */
3187 AddStringForMapping(buf.ReadWord(), &cs->quantifier);
3188 break;
3190 case 0x0D: // String for two letter cargo abbreviation
3191 AddStringForMapping(buf.ReadWord(), &cs->abbrev);
3192 break;
3194 case 0x0E: // Sprite ID for cargo icon
3195 cs->sprite = buf.ReadWord();
3196 break;
3198 case 0x0F: // Weight of one unit of cargo
3199 cs->weight = buf.ReadByte();
3200 break;
3202 case 0x10: // Used for payment calculation
3203 cs->transit_periods[0] = buf.ReadByte();
3204 break;
3206 case 0x11: // Used for payment calculation
3207 cs->transit_periods[1] = buf.ReadByte();
3208 break;
3210 case 0x12: // Base cargo price
3211 cs->initial_payment = buf.ReadDWord();
3212 break;
3214 case 0x13: // Colour for station rating bars
3215 cs->rating_colour = buf.ReadByte();
3216 break;
3218 case 0x14: // Colour for cargo graph
3219 cs->legend_colour = buf.ReadByte();
3220 break;
3222 case 0x15: // Freight status
3223 cs->is_freight = (buf.ReadByte() != 0);
3224 break;
3226 case 0x16: // Cargo classes
3227 cs->classes = buf.ReadWord();
3228 break;
3230 case 0x17: // Cargo label
3231 cs->label = CargoLabel{BSWAP32(buf.ReadDWord())};
3232 BuildCargoLabelMap();
3233 break;
3235 case 0x18: { // Town growth substitute type
3236 uint8_t substitute_type = buf.ReadByte();
3238 switch (substitute_type) {
3239 case 0x00: cs->town_acceptance_effect = TAE_PASSENGERS; break;
3240 case 0x02: cs->town_acceptance_effect = TAE_MAIL; break;
3241 case 0x05: cs->town_acceptance_effect = TAE_GOODS; break;
3242 case 0x09: cs->town_acceptance_effect = TAE_WATER; break;
3243 case 0x0B: cs->town_acceptance_effect = TAE_FOOD; break;
3244 default:
3245 GrfMsg(1, "CargoChangeInfo: Unknown town growth substitute value {}, setting to none.", substitute_type);
3246 [[fallthrough]];
3247 case 0xFF: cs->town_acceptance_effect = TAE_NONE; break;
3249 break;
3252 case 0x19: // Town growth coefficient
3253 buf.ReadWord();
3254 break;
3256 case 0x1A: // Bitmask of callbacks to use
3257 cs->callback_mask = buf.ReadByte();
3258 break;
3260 case 0x1D: // Vehicle capacity muliplier
3261 cs->multiplier = std::max<uint16_t>(1u, buf.ReadWord());
3262 break;
3264 case 0x1E: { // Town production substitute type
3265 uint8_t substitute_type = buf.ReadByte();
3267 switch (substitute_type) {
3268 case 0x00: cs->town_production_effect = TPE_PASSENGERS; break;
3269 case 0x02: cs->town_production_effect = TPE_MAIL; break;
3270 default:
3271 GrfMsg(1, "CargoChangeInfo: Unknown town production substitute value {}, setting to none.", substitute_type);
3272 [[fallthrough]];
3273 case 0xFF: cs->town_production_effect = TPE_NONE; break;
3275 break;
3278 case 0x1F: // Town production multiplier
3279 cs->town_production_multiplier = std::max<uint16_t>(1U, buf.ReadWord());
3280 break;
3282 default:
3283 ret = HandleAction0PropertyDefault(buf, prop);
3284 break;
3288 return ret;
3293 * Define properties for sound effects
3294 * @param sid Local ID of the sound.
3295 * @param numinfo Number of subsequent IDs to change the property for.
3296 * @param prop The property to change.
3297 * @param buf The property value.
3298 * @return ChangeInfoResult.
3300 static ChangeInfoResult SoundEffectChangeInfo(uint sid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
3302 ChangeInfoResult ret = CIR_SUCCESS;
3304 if (_cur.grffile->sound_offset == 0) {
3305 GrfMsg(1, "SoundEffectChangeInfo: No effects defined, skipping");
3306 return CIR_INVALID_ID;
3309 if (sid + numinfo - ORIGINAL_SAMPLE_COUNT > _cur.grffile->num_sounds) {
3310 GrfMsg(1, "SoundEffectChangeInfo: Attempting to change undefined sound effect ({}), max ({}). Ignoring.", sid + numinfo, ORIGINAL_SAMPLE_COUNT + _cur.grffile->num_sounds);
3311 return CIR_INVALID_ID;
3314 for (int i = 0; i < numinfo; i++) {
3315 SoundEntry *sound = GetSound(sid + i + _cur.grffile->sound_offset - ORIGINAL_SAMPLE_COUNT);
3317 switch (prop) {
3318 case 0x08: // Relative volume
3319 sound->volume = Clamp(buf.ReadByte(), 0, SOUND_EFFECT_MAX_VOLUME);
3320 break;
3322 case 0x09: // Priority
3323 sound->priority = buf.ReadByte();
3324 break;
3326 case 0x0A: { // Override old sound
3327 SoundID orig_sound = buf.ReadByte();
3329 if (orig_sound >= ORIGINAL_SAMPLE_COUNT) {
3330 GrfMsg(1, "SoundEffectChangeInfo: Original sound {} not defined (max {})", orig_sound, ORIGINAL_SAMPLE_COUNT);
3331 } else {
3332 SoundEntry *old_sound = GetSound(orig_sound);
3334 /* Literally copy the data of the new sound over the original */
3335 *old_sound = *sound;
3337 break;
3340 default:
3341 ret = HandleAction0PropertyDefault(buf, prop);
3342 break;
3346 return ret;
3350 * Ignore an industry tile property
3351 * @param prop The property to ignore.
3352 * @param buf The property value.
3353 * @return ChangeInfoResult.
3355 static ChangeInfoResult IgnoreIndustryTileProperty(int prop, ByteReader &buf)
3357 ChangeInfoResult ret = CIR_SUCCESS;
3359 switch (prop) {
3360 case 0x09:
3361 case 0x0D:
3362 case 0x0E:
3363 case 0x10:
3364 case 0x11:
3365 case 0x12:
3366 buf.ReadByte();
3367 break;
3369 case 0x0A:
3370 case 0x0B:
3371 case 0x0C:
3372 case 0x0F:
3373 buf.ReadWord();
3374 break;
3376 case 0x13:
3377 buf.Skip(buf.ReadByte() * 2);
3378 break;
3380 default:
3381 ret = HandleAction0PropertyDefault(buf, prop);
3382 break;
3384 return ret;
3388 * Define properties for industry tiles
3389 * @param indtid Local ID of the industry tile.
3390 * @param numinfo Number of subsequent industry tile IDs to change the property for.
3391 * @param prop The property to change.
3392 * @param buf The property value.
3393 * @return ChangeInfoResult.
3395 static ChangeInfoResult IndustrytilesChangeInfo(uint indtid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
3397 ChangeInfoResult ret = CIR_SUCCESS;
3399 if (indtid + numinfo > NUM_INDUSTRYTILES_PER_GRF) {
3400 GrfMsg(1, "IndustryTilesChangeInfo: Too many industry tiles loaded ({}), max ({}). Ignoring.", indtid + numinfo, NUM_INDUSTRYTILES_PER_GRF);
3401 return CIR_INVALID_ID;
3404 /* Allocate industry tile specs if they haven't been allocated already. */
3405 if (_cur.grffile->indtspec.size() < indtid + numinfo) _cur.grffile->indtspec.resize(indtid + numinfo);
3407 for (int i = 0; i < numinfo; i++) {
3408 IndustryTileSpec *tsp = _cur.grffile->indtspec[indtid + i].get();
3410 if (prop != 0x08 && tsp == nullptr) {
3411 ChangeInfoResult cir = IgnoreIndustryTileProperty(prop, buf);
3412 if (cir > ret) ret = cir;
3413 continue;
3416 switch (prop) {
3417 case 0x08: { // Substitute industry tile type
3418 uint8_t subs_id = buf.ReadByte();
3419 if (subs_id >= NEW_INDUSTRYTILEOFFSET) {
3420 /* The substitute id must be one of the original industry tile. */
3421 GrfMsg(2, "IndustryTilesChangeInfo: Attempt to use new industry tile {} as substitute industry tile for {}. Ignoring.", subs_id, indtid + i);
3422 continue;
3425 /* Allocate space for this industry. */
3426 if (tsp == nullptr) {
3427 _cur.grffile->indtspec[indtid + i] = std::make_unique<IndustryTileSpec>(_industry_tile_specs[subs_id]);
3428 tsp = _cur.grffile->indtspec[indtid + i].get();
3430 tsp->enabled = true;
3432 /* A copied tile should not have the animation infos copied too.
3433 * The anim_state should be left untouched, though
3434 * It is up to the author to animate them */
3435 tsp->anim_production = INDUSTRYTILE_NOANIM;
3436 tsp->anim_next = INDUSTRYTILE_NOANIM;
3438 tsp->grf_prop.local_id = indtid + i;
3439 tsp->grf_prop.subst_id = subs_id;
3440 tsp->grf_prop.grfid = _cur.grffile->grfid;
3441 tsp->grf_prop.grffile = _cur.grffile;
3442 _industile_mngr.AddEntityID(indtid + i, _cur.grffile->grfid, subs_id); // pre-reserve the tile slot
3444 break;
3447 case 0x09: { // Industry tile override
3448 uint8_t ovrid = buf.ReadByte();
3450 /* The industry being overridden must be an original industry. */
3451 if (ovrid >= NEW_INDUSTRYTILEOFFSET) {
3452 GrfMsg(2, "IndustryTilesChangeInfo: Attempt to override new industry tile {} with industry tile id {}. Ignoring.", ovrid, indtid + i);
3453 continue;
3456 _industile_mngr.Add(indtid + i, _cur.grffile->grfid, ovrid);
3457 break;
3460 case 0x0A: // Tile acceptance
3461 case 0x0B:
3462 case 0x0C: {
3463 uint16_t acctp = buf.ReadWord();
3464 tsp->accepts_cargo[prop - 0x0A] = GetCargoTranslation(GB(acctp, 0, 8), _cur.grffile);
3465 tsp->acceptance[prop - 0x0A] = Clamp(GB(acctp, 8, 8), 0, 16);
3466 tsp->accepts_cargo_label[prop - 0x0A] = CT_INVALID;
3467 break;
3470 case 0x0D: // Land shape flags
3471 tsp->slopes_refused = (Slope)buf.ReadByte();
3472 break;
3474 case 0x0E: // Callback mask
3475 tsp->callback_mask = buf.ReadByte();
3476 break;
3478 case 0x0F: // Animation information
3479 tsp->animation.frames = buf.ReadByte();
3480 tsp->animation.status = buf.ReadByte();
3481 break;
3483 case 0x10: // Animation speed
3484 tsp->animation.speed = buf.ReadByte();
3485 break;
3487 case 0x11: // Triggers for callback 25
3488 tsp->animation.triggers = buf.ReadByte();
3489 break;
3491 case 0x12: // Special flags
3492 tsp->special_flags = (IndustryTileSpecialFlags)buf.ReadByte();
3493 break;
3495 case 0x13: { // variable length cargo acceptance
3496 uint8_t num_cargoes = buf.ReadByte();
3497 if (num_cargoes > std::size(tsp->acceptance)) {
3498 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
3499 error->param_value[1] = prop;
3500 return CIR_DISABLED;
3502 for (uint i = 0; i < std::size(tsp->acceptance); i++) {
3503 if (i < num_cargoes) {
3504 tsp->accepts_cargo[i] = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
3505 /* Tile acceptance can be negative to counteract the INDTILE_SPECIAL_ACCEPTS_ALL_CARGO flag */
3506 tsp->acceptance[i] = (int8_t)buf.ReadByte();
3507 } else {
3508 tsp->accepts_cargo[i] = INVALID_CARGO;
3509 tsp->acceptance[i] = 0;
3511 if (i < std::size(tsp->accepts_cargo_label)) tsp->accepts_cargo_label[i] = CT_INVALID;
3513 break;
3516 default:
3517 ret = HandleAction0PropertyDefault(buf, prop);
3518 break;
3522 return ret;
3526 * Ignore an industry property
3527 * @param prop The property to ignore.
3528 * @param buf The property value.
3529 * @return ChangeInfoResult.
3531 static ChangeInfoResult IgnoreIndustryProperty(int prop, ByteReader &buf)
3533 ChangeInfoResult ret = CIR_SUCCESS;
3535 switch (prop) {
3536 case 0x09:
3537 case 0x0B:
3538 case 0x0F:
3539 case 0x12:
3540 case 0x13:
3541 case 0x14:
3542 case 0x17:
3543 case 0x18:
3544 case 0x19:
3545 case 0x21:
3546 case 0x22:
3547 buf.ReadByte();
3548 break;
3550 case 0x0C:
3551 case 0x0D:
3552 case 0x0E:
3553 case 0x10: // INDUSTRY_ORIGINAL_NUM_OUTPUTS bytes
3554 case 0x1B:
3555 case 0x1F:
3556 case 0x24:
3557 buf.ReadWord();
3558 break;
3560 case 0x11: // INDUSTRY_ORIGINAL_NUM_INPUTS bytes + 1
3561 case 0x1A:
3562 case 0x1C:
3563 case 0x1D:
3564 case 0x1E:
3565 case 0x20:
3566 case 0x23:
3567 buf.ReadDWord();
3568 break;
3570 case 0x0A: {
3571 uint8_t num_table = buf.ReadByte();
3572 for (uint8_t j = 0; j < num_table; j++) {
3573 for (uint k = 0;; k++) {
3574 uint8_t x = buf.ReadByte();
3575 if (x == 0xFE && k == 0) {
3576 buf.ReadByte();
3577 buf.ReadByte();
3578 break;
3581 uint8_t y = buf.ReadByte();
3582 if (x == 0 && y == 0x80) break;
3584 uint8_t gfx = buf.ReadByte();
3585 if (gfx == 0xFE) buf.ReadWord();
3588 break;
3591 case 0x16:
3592 for (uint8_t j = 0; j < INDUSTRY_ORIGINAL_NUM_INPUTS; j++) buf.ReadByte();
3593 break;
3595 case 0x15:
3596 case 0x25:
3597 case 0x26:
3598 case 0x27:
3599 buf.Skip(buf.ReadByte());
3600 break;
3602 case 0x28: {
3603 int num_inputs = buf.ReadByte();
3604 int num_outputs = buf.ReadByte();
3605 buf.Skip(num_inputs * num_outputs * 2);
3606 break;
3609 default:
3610 ret = HandleAction0PropertyDefault(buf, prop);
3611 break;
3613 return ret;
3617 * Validate the industry layout; e.g. to prevent duplicate tiles.
3618 * @param layout The layout to check.
3619 * @return True if the layout is deemed valid.
3621 static bool ValidateIndustryLayout(const IndustryTileLayout &layout)
3623 const size_t size = layout.size();
3624 if (size == 0) return false;
3626 for (size_t i = 0; i < size - 1; i++) {
3627 for (size_t j = i + 1; j < size; j++) {
3628 if (layout[i].ti.x == layout[j].ti.x &&
3629 layout[i].ti.y == layout[j].ti.y) {
3630 return false;
3635 bool have_regular_tile = false;
3636 for (const auto &tilelayout : layout) {
3637 if (tilelayout.gfx != GFX_WATERTILE_SPECIALCHECK) {
3638 have_regular_tile = true;
3639 break;
3643 return have_regular_tile;
3647 * Define properties for industries
3648 * @param indid Local ID of the industry.
3649 * @param numinfo Number of subsequent industry IDs to change the property for.
3650 * @param prop The property to change.
3651 * @param buf The property value.
3652 * @return ChangeInfoResult.
3654 static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
3656 ChangeInfoResult ret = CIR_SUCCESS;
3658 if (indid + numinfo > NUM_INDUSTRYTYPES_PER_GRF) {
3659 GrfMsg(1, "IndustriesChangeInfo: Too many industries loaded ({}), max ({}). Ignoring.", indid + numinfo, NUM_INDUSTRYTYPES_PER_GRF);
3660 return CIR_INVALID_ID;
3663 /* Allocate industry specs if they haven't been allocated already. */
3664 if (_cur.grffile->industryspec.size() < indid + numinfo) _cur.grffile->industryspec.resize(indid + numinfo);
3666 for (int i = 0; i < numinfo; i++) {
3667 IndustrySpec *indsp = _cur.grffile->industryspec[indid + i].get();
3669 if (prop != 0x08 && indsp == nullptr) {
3670 ChangeInfoResult cir = IgnoreIndustryProperty(prop, buf);
3671 if (cir > ret) ret = cir;
3672 continue;
3675 switch (prop) {
3676 case 0x08: { // Substitute industry type
3677 uint8_t subs_id = buf.ReadByte();
3678 if (subs_id == 0xFF) {
3679 /* Instead of defining a new industry, a substitute industry id
3680 * of 0xFF disables the old industry with the current id. */
3681 _industry_specs[indid + i].enabled = false;
3682 continue;
3683 } else if (subs_id >= NEW_INDUSTRYOFFSET) {
3684 /* The substitute id must be one of the original industry. */
3685 GrfMsg(2, "_industry_specs: Attempt to use new industry {} as substitute industry for {}. Ignoring.", subs_id, indid + i);
3686 continue;
3689 /* Allocate space for this industry.
3690 * Only need to do it once. If ever it is called again, it should not
3691 * do anything */
3692 if (indsp == nullptr) {
3693 _cur.grffile->industryspec[indid + i] = std::make_unique<IndustrySpec>(_origin_industry_specs[subs_id]);
3694 indsp = _cur.grffile->industryspec[indid + i].get();
3696 indsp->enabled = true;
3697 indsp->grf_prop.local_id = indid + i;
3698 indsp->grf_prop.subst_id = subs_id;
3699 indsp->grf_prop.grfid = _cur.grffile->grfid;
3700 indsp->grf_prop.grffile = _cur.grffile;
3701 /* If the grf industry needs to check its surrounding upon creation, it should
3702 * rely on callbacks, not on the original placement functions */
3703 indsp->check_proc = CHECK_NOTHING;
3705 break;
3708 case 0x09: { // Industry type override
3709 uint8_t ovrid = buf.ReadByte();
3711 /* The industry being overridden must be an original industry. */
3712 if (ovrid >= NEW_INDUSTRYOFFSET) {
3713 GrfMsg(2, "IndustriesChangeInfo: Attempt to override new industry {} with industry id {}. Ignoring.", ovrid, indid + i);
3714 continue;
3716 indsp->grf_prop.override = ovrid;
3717 _industry_mngr.Add(indid + i, _cur.grffile->grfid, ovrid);
3718 break;
3721 case 0x0A: { // Set industry layout(s)
3722 uint8_t new_num_layouts = buf.ReadByte();
3723 uint32_t definition_size = buf.ReadDWord();
3724 uint32_t bytes_read = 0;
3725 std::vector<IndustryTileLayout> new_layouts;
3726 IndustryTileLayout layout;
3728 for (uint8_t j = 0; j < new_num_layouts; j++) {
3729 layout.clear();
3730 layout.reserve(new_num_layouts);
3732 for (uint k = 0;; k++) {
3733 if (bytes_read >= definition_size) {
3734 GrfMsg(3, "IndustriesChangeInfo: Incorrect size for industry tile layout definition for industry {}.", indid);
3735 /* Avoid warning twice */
3736 definition_size = UINT32_MAX;
3739 IndustryTileLayoutTile &it = layout.emplace_back();
3741 it.ti.x = buf.ReadByte(); // Offsets from northermost tile
3742 ++bytes_read;
3744 if (it.ti.x == 0xFE && k == 0) {
3745 /* This means we have to borrow the layout from an old industry */
3746 IndustryType type = buf.ReadByte();
3747 uint8_t laynbr = buf.ReadByte();
3748 bytes_read += 2;
3750 if (type >= lengthof(_origin_industry_specs)) {
3751 GrfMsg(1, "IndustriesChangeInfo: Invalid original industry number for layout import, industry {}", indid);
3752 DisableGrf(STR_NEWGRF_ERROR_INVALID_ID);
3753 return CIR_DISABLED;
3755 if (laynbr >= _origin_industry_specs[type].layouts.size()) {
3756 GrfMsg(1, "IndustriesChangeInfo: Invalid original industry layout index for layout import, industry {}", indid);
3757 DisableGrf(STR_NEWGRF_ERROR_INVALID_ID);
3758 return CIR_DISABLED;
3760 layout = _origin_industry_specs[type].layouts[laynbr];
3761 break;
3764 it.ti.y = buf.ReadByte(); // Or table definition finalisation
3765 ++bytes_read;
3767 if (it.ti.x == 0 && it.ti.y == 0x80) {
3768 /* Terminator, remove and finish up */
3769 layout.pop_back();
3770 break;
3773 it.gfx = buf.ReadByte();
3774 ++bytes_read;
3776 if (it.gfx == 0xFE) {
3777 /* Use a new tile from this GRF */
3778 int local_tile_id = buf.ReadWord();
3779 bytes_read += 2;
3781 /* Read the ID from the _industile_mngr. */
3782 int tempid = _industile_mngr.GetID(local_tile_id, _cur.grffile->grfid);
3784 if (tempid == INVALID_INDUSTRYTILE) {
3785 GrfMsg(2, "IndustriesChangeInfo: Attempt to use industry tile {} with industry id {}, not yet defined. Ignoring.", local_tile_id, indid);
3786 } else {
3787 /* Declared as been valid, can be used */
3788 it.gfx = tempid;
3790 } else if (it.gfx == GFX_WATERTILE_SPECIALCHECK) {
3791 it.ti.x = (int8_t)GB(it.ti.x, 0, 8);
3792 it.ti.y = (int8_t)GB(it.ti.y, 0, 8);
3794 /* When there were only 256x256 maps, TileIndex was a uint16_t and
3795 * it.ti was just a TileIndexDiff that was added to it.
3796 * As such negative "x" values were shifted into the "y" position.
3797 * x = -1, y = 1 -> x = 255, y = 0
3798 * Since GRF version 8 the position is interpreted as pair of independent int8.
3799 * For GRF version < 8 we need to emulate the old shifting behaviour.
3801 if (_cur.grffile->grf_version < 8 && it.ti.x < 0) it.ti.y += 1;
3805 if (!ValidateIndustryLayout(layout)) {
3806 /* The industry layout was not valid, so skip this one. */
3807 GrfMsg(1, "IndustriesChangeInfo: Invalid industry layout for industry id {}. Ignoring", indid);
3808 new_num_layouts--;
3809 j--;
3810 } else {
3811 new_layouts.push_back(layout);
3815 /* Install final layout construction in the industry spec */
3816 indsp->layouts = new_layouts;
3817 break;
3820 case 0x0B: // Industry production flags
3821 indsp->life_type = (IndustryLifeType)buf.ReadByte();
3822 break;
3824 case 0x0C: // Industry closure message
3825 AddStringForMapping(buf.ReadWord(), &indsp->closure_text);
3826 break;
3828 case 0x0D: // Production increase message
3829 AddStringForMapping(buf.ReadWord(), &indsp->production_up_text);
3830 break;
3832 case 0x0E: // Production decrease message
3833 AddStringForMapping(buf.ReadWord(), &indsp->production_down_text);
3834 break;
3836 case 0x0F: // Fund cost multiplier
3837 indsp->cost_multiplier = buf.ReadByte();
3838 break;
3840 case 0x10: // Production cargo types
3841 for (uint8_t j = 0; j < INDUSTRY_ORIGINAL_NUM_OUTPUTS; j++) {
3842 indsp->produced_cargo[j] = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
3843 indsp->produced_cargo_label[j] = CT_INVALID;
3845 break;
3847 case 0x11: // Acceptance cargo types
3848 for (uint8_t j = 0; j < INDUSTRY_ORIGINAL_NUM_INPUTS; j++) {
3849 indsp->accepts_cargo[j] = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
3850 indsp->accepts_cargo_label[j] = CT_INVALID;
3852 buf.ReadByte(); // Unnused, eat it up
3853 break;
3855 case 0x12: // Production multipliers
3856 case 0x13:
3857 indsp->production_rate[prop - 0x12] = buf.ReadByte();
3858 break;
3860 case 0x14: // Minimal amount of cargo distributed
3861 indsp->minimal_cargo = buf.ReadByte();
3862 break;
3864 case 0x15: { // Random sound effects
3865 uint8_t num_sounds = buf.ReadByte();
3867 std::vector<uint8_t> sounds;
3868 sounds.reserve(num_sounds);
3869 for (uint8_t j = 0; j < num_sounds; ++j) {
3870 sounds.push_back(buf.ReadByte());
3873 indsp->random_sounds = std::move(sounds);
3874 break;
3877 case 0x16: // Conflicting industry types
3878 for (uint8_t j = 0; j < 3; j++) indsp->conflicting[j] = buf.ReadByte();
3879 break;
3881 case 0x17: // Probability in random game
3882 indsp->appear_creation[_settings_game.game_creation.landscape] = buf.ReadByte();
3883 break;
3885 case 0x18: // Probability during gameplay
3886 indsp->appear_ingame[_settings_game.game_creation.landscape] = buf.ReadByte();
3887 break;
3889 case 0x19: // Map colour
3890 indsp->map_colour = buf.ReadByte();
3891 break;
3893 case 0x1A: // Special industry flags to define special behavior
3894 indsp->behaviour = (IndustryBehaviour)buf.ReadDWord();
3895 break;
3897 case 0x1B: // New industry text ID
3898 AddStringForMapping(buf.ReadWord(), &indsp->new_industry_text);
3899 break;
3901 case 0x1C: // Input cargo multipliers for the three input cargo types
3902 case 0x1D:
3903 case 0x1E: {
3904 uint32_t multiples = buf.ReadDWord();
3905 indsp->input_cargo_multiplier[prop - 0x1C][0] = GB(multiples, 0, 16);
3906 indsp->input_cargo_multiplier[prop - 0x1C][1] = GB(multiples, 16, 16);
3907 break;
3910 case 0x1F: // Industry name
3911 AddStringForMapping(buf.ReadWord(), &indsp->name);
3912 break;
3914 case 0x20: // Prospecting success chance
3915 indsp->prospecting_chance = buf.ReadDWord();
3916 break;
3918 case 0x21: // Callback mask
3919 case 0x22: { // Callback additional mask
3920 uint8_t aflag = buf.ReadByte();
3921 SB(indsp->callback_mask, (prop - 0x21) * 8, 8, aflag);
3922 break;
3925 case 0x23: // removal cost multiplier
3926 indsp->removal_cost_multiplier = buf.ReadDWord();
3927 break;
3929 case 0x24: { // name for nearby station
3930 uint16_t str = buf.ReadWord();
3931 if (str == 0) {
3932 indsp->station_name = STR_NULL;
3933 } else {
3934 AddStringForMapping(str, &indsp->station_name);
3936 break;
3939 case 0x25: { // variable length produced cargoes
3940 uint8_t num_cargoes = buf.ReadByte();
3941 if (num_cargoes > std::size(indsp->produced_cargo)) {
3942 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
3943 error->param_value[1] = prop;
3944 return CIR_DISABLED;
3946 for (uint i = 0; i < std::size(indsp->produced_cargo); i++) {
3947 if (i < num_cargoes) {
3948 CargoID cargo = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
3949 indsp->produced_cargo[i] = cargo;
3950 } else {
3951 indsp->produced_cargo[i] = INVALID_CARGO;
3953 if (i < std::size(indsp->produced_cargo_label)) indsp->produced_cargo_label[i] = CT_INVALID;
3955 break;
3958 case 0x26: { // variable length accepted cargoes
3959 uint8_t num_cargoes = buf.ReadByte();
3960 if (num_cargoes > std::size(indsp->accepts_cargo)) {
3961 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
3962 error->param_value[1] = prop;
3963 return CIR_DISABLED;
3965 for (uint i = 0; i < std::size(indsp->accepts_cargo); i++) {
3966 if (i < num_cargoes) {
3967 CargoID cargo = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
3968 indsp->accepts_cargo[i] = cargo;
3969 } else {
3970 indsp->accepts_cargo[i] = INVALID_CARGO;
3972 if (i < std::size(indsp->accepts_cargo_label)) indsp->accepts_cargo_label[i] = CT_INVALID;
3974 break;
3977 case 0x27: { // variable length production rates
3978 uint8_t num_cargoes = buf.ReadByte();
3979 if (num_cargoes > std::size(indsp->production_rate)) {
3980 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
3981 error->param_value[1] = prop;
3982 return CIR_DISABLED;
3984 for (uint i = 0; i < std::size(indsp->production_rate); i++) {
3985 if (i < num_cargoes) {
3986 indsp->production_rate[i] = buf.ReadByte();
3987 } else {
3988 indsp->production_rate[i] = 0;
3991 break;
3994 case 0x28: { // variable size input/output production multiplier table
3995 uint8_t num_inputs = buf.ReadByte();
3996 uint8_t num_outputs = buf.ReadByte();
3997 if (num_inputs > std::size(indsp->accepts_cargo) || num_outputs > std::size(indsp->produced_cargo)) {
3998 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
3999 error->param_value[1] = prop;
4000 return CIR_DISABLED;
4002 for (uint i = 0; i < std::size(indsp->accepts_cargo); i++) {
4003 for (uint j = 0; j < std::size(indsp->produced_cargo); j++) {
4004 uint16_t mult = 0;
4005 if (i < num_inputs && j < num_outputs) mult = buf.ReadWord();
4006 indsp->input_cargo_multiplier[i][j] = mult;
4009 break;
4012 default:
4013 ret = HandleAction0PropertyDefault(buf, prop);
4014 break;
4018 return ret;
4022 * Define properties for airports
4023 * @param airport Local ID of the airport.
4024 * @param numinfo Number of subsequent airport IDs to change the property for.
4025 * @param prop The property to change.
4026 * @param buf The property value.
4027 * @return ChangeInfoResult.
4029 static ChangeInfoResult AirportChangeInfo(uint airport, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
4031 ChangeInfoResult ret = CIR_SUCCESS;
4033 if (airport + numinfo > NUM_AIRPORTS_PER_GRF) {
4034 GrfMsg(1, "AirportChangeInfo: Too many airports, trying id ({}), max ({}). Ignoring.", airport + numinfo, NUM_AIRPORTS_PER_GRF);
4035 return CIR_INVALID_ID;
4038 /* Allocate industry specs if they haven't been allocated already. */
4039 if (_cur.grffile->airportspec.size() < airport + numinfo) _cur.grffile->airportspec.resize(airport + numinfo);
4041 for (int i = 0; i < numinfo; i++) {
4042 AirportSpec *as = _cur.grffile->airportspec[airport + i].get();
4044 if (as == nullptr && prop != 0x08 && prop != 0x09) {
4045 GrfMsg(2, "AirportChangeInfo: Attempt to modify undefined airport {}, ignoring", airport + i);
4046 return CIR_INVALID_ID;
4049 switch (prop) {
4050 case 0x08: { // Modify original airport
4051 uint8_t subs_id = buf.ReadByte();
4052 if (subs_id == 0xFF) {
4053 /* Instead of defining a new airport, an airport id
4054 * of 0xFF disables the old airport with the current id. */
4055 AirportSpec::GetWithoutOverride(airport + i)->enabled = false;
4056 continue;
4057 } else if (subs_id >= NEW_AIRPORT_OFFSET) {
4058 /* The substitute id must be one of the original airports. */
4059 GrfMsg(2, "AirportChangeInfo: Attempt to use new airport {} as substitute airport for {}. Ignoring.", subs_id, airport + i);
4060 continue;
4063 /* Allocate space for this airport.
4064 * Only need to do it once. If ever it is called again, it should not
4065 * do anything */
4066 if (as == nullptr) {
4067 _cur.grffile->airportspec[airport + i] = std::make_unique<AirportSpec>(*AirportSpec::GetWithoutOverride(subs_id));
4068 as = _cur.grffile->airportspec[airport + i].get();
4070 as->enabled = true;
4071 as->grf_prop.local_id = airport + i;
4072 as->grf_prop.subst_id = subs_id;
4073 as->grf_prop.grfid = _cur.grffile->grfid;
4074 as->grf_prop.grffile = _cur.grffile;
4075 /* override the default airport */
4076 _airport_mngr.Add(airport + i, _cur.grffile->grfid, subs_id);
4078 break;
4081 case 0x0A: { // Set airport layout
4082 uint8_t num_layouts = buf.ReadByte();
4083 buf.ReadDWord(); // Total size of definition, unneeded.
4084 uint8_t size_x = 0;
4085 uint8_t size_y = 0;
4087 std::vector<AirportTileLayout> layouts;
4088 layouts.reserve(num_layouts);
4090 for (uint8_t j = 0; j != num_layouts; ++j) {
4091 auto &layout = layouts.emplace_back();
4092 layout.rotation = static_cast<Direction>(buf.ReadByte() & 6); // Rotation can only be DIR_NORTH, DIR_EAST, DIR_SOUTH or DIR_WEST.
4094 for (;;) {
4095 auto &tile = layout.tiles.emplace_back();
4096 tile.ti.x = buf.ReadByte();
4097 tile.ti.y = buf.ReadByte();
4098 if (tile.ti.x == 0 && tile.ti.y == 0x80) {
4099 /* Convert terminator to our own. */
4100 tile.ti.x = -0x80;
4101 tile.ti.y = 0;
4102 tile.gfx = 0;
4103 break;
4106 tile.gfx = buf.ReadByte();
4108 if (tile.gfx == 0xFE) {
4109 /* Use a new tile from this GRF */
4110 int local_tile_id = buf.ReadWord();
4112 /* Read the ID from the _airporttile_mngr. */
4113 uint16_t tempid = _airporttile_mngr.GetID(local_tile_id, _cur.grffile->grfid);
4115 if (tempid == INVALID_AIRPORTTILE) {
4116 GrfMsg(2, "AirportChangeInfo: Attempt to use airport tile {} with airport id {}, not yet defined. Ignoring.", local_tile_id, airport + i);
4117 } else {
4118 /* Declared as been valid, can be used */
4119 tile.gfx = tempid;
4121 } else if (tile.gfx == 0xFF) {
4122 tile.ti.x = static_cast<int8_t>(GB(tile.ti.x, 0, 8));
4123 tile.ti.y = static_cast<int8_t>(GB(tile.ti.y, 0, 8));
4126 /* Determine largest size. */
4127 if (layout.rotation == DIR_E || layout.rotation == DIR_W) {
4128 size_x = std::max<uint8_t>(size_x, tile.ti.y + 1);
4129 size_y = std::max<uint8_t>(size_y, tile.ti.x + 1);
4130 } else {
4131 size_x = std::max<uint8_t>(size_x, tile.ti.x + 1);
4132 size_y = std::max<uint8_t>(size_y, tile.ti.y + 1);
4136 as->layouts = std::move(layouts);
4137 as->size_x = size_x;
4138 as->size_y = size_y;
4139 break;
4142 case 0x0C:
4143 as->min_year = CalTime::Year{buf.ReadWord()};
4144 as->max_year = CalTime::Year{buf.ReadWord()};
4145 if (as->max_year == 0xFFFF) as->max_year = CalTime::MAX_YEAR;
4146 break;
4148 case 0x0D:
4149 as->ttd_airport_type = (TTDPAirportType)buf.ReadByte();
4150 break;
4152 case 0x0E:
4153 as->catchment = Clamp(buf.ReadByte(), 1, MAX_CATCHMENT);
4154 break;
4156 case 0x0F:
4157 as->noise_level = buf.ReadByte();
4158 break;
4160 case 0x10:
4161 AddStringForMapping(buf.ReadWord(), &as->name);
4162 break;
4164 case 0x11: // Maintenance cost factor
4165 as->maintenance_cost = buf.ReadWord();
4166 break;
4168 default:
4169 ret = HandleAction0PropertyDefault(buf, prop);
4170 break;
4174 return ret;
4178 * Define properties for signals
4179 * @param id Local ID (unused).
4180 * @param numinfo Number of subsequent IDs to change the property for.
4181 * @param prop The property to change.
4182 * @param buf The property value.
4183 * @return ChangeInfoResult.
4185 static ChangeInfoResult SignalsChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
4187 /* Properties which are handled per item */
4188 ChangeInfoResult ret = CIR_SUCCESS;
4189 for (int i = 0; i < numinfo; i++) {
4190 switch (prop) {
4191 case A0RPI_SIGNALS_ENABLE_PROGRAMMABLE_SIGNALS:
4192 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4193 AssignBit(_cur.grffile->new_signal_ctrl_flags, NSCF_PROGSIG, buf.ReadByte() != 0);
4194 break;
4196 case A0RPI_SIGNALS_ENABLE_NO_ENTRY_SIGNALS:
4197 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4198 AssignBit(_cur.grffile->new_signal_ctrl_flags, NSCF_NOENTRYSIG, buf.ReadByte() != 0);
4199 break;
4201 case A0RPI_SIGNALS_ENABLE_RESTRICTED_SIGNALS:
4202 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4203 AssignBit(_cur.grffile->new_signal_ctrl_flags, NSCF_RESTRICTEDSIG, buf.ReadByte() != 0);
4204 break;
4206 case A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR:
4207 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4208 AssignBit(_cur.grffile->new_signal_ctrl_flags, NSCF_RECOLOUR_ENABLED, buf.ReadByte() != 0);
4209 break;
4211 case A0RPI_SIGNALS_EXTRA_ASPECTS:
4212 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4213 _cur.grffile->new_signal_extra_aspects = std::min<uint8_t>(buf.ReadByte(), NEW_SIGNALS_MAX_EXTRA_ASPECT);
4214 break;
4216 case A0RPI_SIGNALS_NO_DEFAULT_STYLE:
4217 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4218 AssignBit(_cur.grffile->new_signal_style_mask, 0, buf.ReadByte() == 0);
4219 break;
4221 case A0RPI_SIGNALS_DEFINE_STYLE: {
4222 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4223 uint8_t local_id = buf.ReadByte();
4224 if (_num_new_signal_styles < MAX_NEW_SIGNAL_STYLES) {
4225 NewSignalStyle &style = _new_signal_styles[_num_new_signal_styles];
4226 style = {};
4227 _num_new_signal_styles++;
4228 SetBit(_cur.grffile->new_signal_style_mask, _num_new_signal_styles);
4229 style.grf_local_id = local_id;
4230 style.grffile = _cur.grffile;
4231 _cur.grffile->current_new_signal_style = &style;
4232 } else {
4233 _cur.grffile->current_new_signal_style = nullptr;
4235 break;
4238 case A0RPI_SIGNALS_STYLE_NAME: {
4239 if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
4240 uint16_t str = buf.ReadWord();
4241 if (_cur.grffile->current_new_signal_style != nullptr) {
4242 AddStringForMapping(str, &(_cur.grffile->current_new_signal_style->name));
4244 break;
4247 case A0RPI_SIGNALS_STYLE_NO_ASPECT_INCREASE: {
4248 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4249 uint8_t value = buf.ReadByte();
4250 if (_cur.grffile->current_new_signal_style != nullptr) {
4251 AssignBit(_cur.grffile->current_new_signal_style->style_flags, NSSF_NO_ASPECT_INC, value != 0);
4253 break;
4256 case A0RPI_SIGNALS_STYLE_ALWAYS_RESERVE_THROUGH: {
4257 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4258 uint8_t value = buf.ReadByte();
4259 if (_cur.grffile->current_new_signal_style != nullptr) {
4260 AssignBit(_cur.grffile->current_new_signal_style->style_flags, NSSF_ALWAYS_RESERVE_THROUGH, value != 0);
4262 break;
4265 case A0RPI_SIGNALS_STYLE_LOOKAHEAD_EXTRA_ASPECTS: {
4266 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4267 uint8_t value = buf.ReadByte();
4268 if (_cur.grffile->current_new_signal_style != nullptr) {
4269 SetBit(_cur.grffile->current_new_signal_style->style_flags, NSSF_LOOKAHEAD_ASPECTS_SET);
4270 _cur.grffile->current_new_signal_style->lookahead_extra_aspects = value;
4272 break;
4275 case A0RPI_SIGNALS_STYLE_LOOKAHEAD_SINGLE_SIGNAL_ONLY: {
4276 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4277 uint8_t value = buf.ReadByte();
4278 if (_cur.grffile->current_new_signal_style != nullptr) {
4279 AssignBit(_cur.grffile->current_new_signal_style->style_flags, NSSF_LOOKAHEAD_SINGLE_SIGNAL, value != 0);
4281 break;
4284 case A0RPI_SIGNALS_STYLE_SEMAPHORE_ENABLED: {
4285 if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
4286 uint32_t mask = buf.ReadDWord();
4287 if (_cur.grffile->current_new_signal_style != nullptr) {
4288 _cur.grffile->current_new_signal_style->semaphore_mask = (uint8_t)mask;
4290 break;
4293 case A0RPI_SIGNALS_STYLE_ELECTRIC_ENABLED: {
4294 if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
4295 uint32_t mask = buf.ReadDWord();
4296 if (_cur.grffile->current_new_signal_style != nullptr) {
4297 _cur.grffile->current_new_signal_style->electric_mask = (uint8_t)mask;
4299 break;
4302 case A0RPI_SIGNALS_STYLE_OPPOSITE_SIDE: {
4303 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4304 uint8_t value = buf.ReadByte();
4305 if (_cur.grffile->current_new_signal_style != nullptr) {
4306 AssignBit(_cur.grffile->current_new_signal_style->style_flags, NSSF_OPPOSITE_SIDE, value != 0);
4308 break;
4311 case A0RPI_SIGNALS_STYLE_COMBINED_NORMAL_SHUNT: {
4312 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4313 uint8_t value = buf.ReadByte();
4314 if (_cur.grffile->current_new_signal_style != nullptr) {
4315 AssignBit(_cur.grffile->current_new_signal_style->style_flags, NSSF_COMBINED_NORMAL_SHUNT, value != 0);
4317 break;
4320 case A0RPI_SIGNALS_STYLE_REALISTIC_BRAKING_ONLY: {
4321 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4322 uint8_t value = buf.ReadByte();
4323 if (_cur.grffile->current_new_signal_style != nullptr) {
4324 AssignBit(_cur.grffile->current_new_signal_style->style_flags, NSSF_REALISTIC_BRAKING_ONLY, value != 0);
4326 break;
4329 case A0RPI_SIGNALS_STYLE_BOTH_SIDES: {
4330 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4331 uint8_t value = buf.ReadByte();
4332 if (_cur.grffile->current_new_signal_style != nullptr) {
4333 AssignBit(_cur.grffile->current_new_signal_style->style_flags, NSSF_BOTH_SIDES, value != 0);
4335 break;
4338 default:
4339 ret = HandleAction0PropertyDefault(buf, prop);
4340 break;
4344 return ret;
4348 * Ignore properties for objects
4349 * @param prop The property to ignore.
4350 * @param buf The property value.
4351 * @return ChangeInfoResult.
4353 static ChangeInfoResult IgnoreObjectProperty(uint prop, ByteReader &buf)
4355 ChangeInfoResult ret = CIR_SUCCESS;
4357 switch (prop) {
4358 case 0x0B:
4359 case 0x0C:
4360 case 0x0D:
4361 case 0x12:
4362 case 0x14:
4363 case 0x16:
4364 case 0x17:
4365 case 0x18:
4366 buf.ReadByte();
4367 break;
4369 case 0x09:
4370 case 0x0A:
4371 case 0x10:
4372 case 0x11:
4373 case 0x13:
4374 case 0x15:
4375 buf.ReadWord();
4376 break;
4378 case 0x08:
4379 case 0x0E:
4380 case 0x0F:
4381 buf.ReadDWord();
4382 break;
4384 default:
4385 ret = HandleAction0PropertyDefault(buf, prop);
4386 break;
4389 return ret;
4393 * Define properties for objects
4394 * @param id Local ID of the object.
4395 * @param numinfo Number of subsequent objectIDs to change the property for.
4396 * @param prop The property to change.
4397 * @param buf The property value.
4398 * @return ChangeInfoResult.
4400 static ChangeInfoResult ObjectChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
4402 ChangeInfoResult ret = CIR_SUCCESS;
4404 if (id + numinfo > NUM_OBJECTS_PER_GRF) {
4405 GrfMsg(1, "ObjectChangeInfo: Too many objects loaded ({}), max ({}). Ignoring.", id + numinfo, NUM_OBJECTS_PER_GRF);
4406 return CIR_INVALID_ID;
4409 if (id + numinfo > _cur.grffile->objectspec.size()) {
4410 _cur.grffile->objectspec.resize(id + numinfo);
4413 for (int i = 0; i < numinfo; i++) {
4414 ObjectSpec *spec = _cur.grffile->objectspec[id + i].get();
4416 if (prop != 0x08 && spec == nullptr) {
4417 /* If the object property 08 is not yet set, ignore this property */
4418 ChangeInfoResult cir = IgnoreObjectProperty(prop, buf);
4419 if (cir > ret) ret = cir;
4420 continue;
4423 switch (prop) {
4424 case 0x08: { // Class ID
4425 /* Allocate space for this object. */
4426 if (spec == nullptr) {
4427 _cur.grffile->objectspec[id + i] = std::make_unique<ObjectSpec>();
4428 spec = _cur.grffile->objectspec[id + i].get();
4429 spec->views = 1; // Default for NewGRFs that don't set it.
4430 spec->size = OBJECT_SIZE_1X1; // Default for NewGRFs that manage to not set it (1x1)
4433 /* Swap classid because we read it in BE. */
4434 uint32_t classid = buf.ReadDWord();
4435 spec->class_index = ObjectClass::Allocate(BSWAP32(classid));
4436 break;
4439 case 0x09: { // Class name
4440 AddStringForMapping(buf.ReadWord(), spec, [](StringID str, ObjectSpec *spec) { ObjectClass::Get(spec->class_index)->name = str; });
4441 break;
4444 case 0x0A: // Object name
4445 AddStringForMapping(buf.ReadWord(), &spec->name);
4446 break;
4448 case 0x0B: // Climate mask
4449 spec->climate = buf.ReadByte();
4450 break;
4452 case 0x0C: // Size
4453 spec->size = buf.ReadByte();
4454 if (GB(spec->size, 0, 4) == 0 || GB(spec->size, 4, 4) == 0) {
4455 GrfMsg(0, "ObjectChangeInfo: Invalid object size requested (0x{:X}) for object id {}. Ignoring.", spec->size, id + i);
4456 spec->size = OBJECT_SIZE_1X1;
4458 break;
4460 case 0x0D: // Build cost multipler
4461 spec->build_cost_multiplier = buf.ReadByte();
4462 spec->clear_cost_multiplier = spec->build_cost_multiplier;
4463 break;
4465 case 0x0E: // Introduction date
4466 spec->introduction_date = CalTime::Date(static_cast<int32_t>(buf.ReadDWord()));
4467 break;
4469 case 0x0F: // End of life
4470 spec->end_of_life_date = CalTime::Date(static_cast<int32_t>(buf.ReadDWord()));
4471 break;
4473 case 0x10: // Flags
4474 spec->flags = (ObjectFlags)buf.ReadWord();
4475 _loaded_newgrf_features.has_2CC |= (spec->flags & OBJECT_FLAG_2CC_COLOUR) != 0;
4476 break;
4478 case 0x11: // Animation info
4479 spec->animation.frames = buf.ReadByte();
4480 spec->animation.status = buf.ReadByte();
4481 break;
4483 case 0x12: // Animation speed
4484 spec->animation.speed = buf.ReadByte();
4485 break;
4487 case 0x13: // Animation triggers
4488 spec->animation.triggers = buf.ReadWord();
4489 break;
4491 case 0x14: // Removal cost multiplier
4492 spec->clear_cost_multiplier = buf.ReadByte();
4493 break;
4495 case 0x15: // Callback mask
4496 spec->callback_mask = buf.ReadWord();
4497 break;
4499 case 0x16: // Building height
4500 spec->height = buf.ReadByte();
4501 break;
4503 case 0x17: // Views
4504 spec->views = buf.ReadByte();
4505 if (spec->views != 1 && spec->views != 2 && spec->views != 4) {
4506 GrfMsg(2, "ObjectChangeInfo: Invalid number of views ({}) for object id {}. Ignoring.", spec->views, id + i);
4507 spec->views = 1;
4509 break;
4511 case 0x18: // Amount placed on 256^2 map on map creation
4512 spec->generate_amount = buf.ReadByte();
4513 break;
4515 case A0RPI_OBJECT_USE_LAND_GROUND:
4516 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4517 spec->ctrl_flags &= ~OBJECT_CTRL_FLAG_USE_LAND_GROUND;
4518 if (buf.ReadByte() != 0) spec->ctrl_flags |= OBJECT_CTRL_FLAG_USE_LAND_GROUND;
4519 break;
4521 case A0RPI_OBJECT_EDGE_FOUNDATION_MODE:
4522 if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
4523 spec->ctrl_flags |= OBJECT_CTRL_FLAG_EDGE_FOUNDATION;
4524 for (int i = 0; i < 4; i++) {
4525 spec->edge_foundation[i] = buf.ReadByte();
4527 break;
4529 case A0RPI_OBJECT_FLOOD_RESISTANT:
4530 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4531 spec->ctrl_flags &= ~OBJECT_CTRL_FLAG_FLOOD_RESISTANT;
4532 if (buf.ReadByte() != 0) spec->ctrl_flags |= OBJECT_CTRL_FLAG_FLOOD_RESISTANT;
4533 break;
4535 case A0RPI_OBJECT_VIEWPORT_MAP_TYPE:
4536 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4537 spec->vport_map_type = (ObjectViewportMapType)buf.ReadByte();
4538 spec->ctrl_flags |= OBJECT_CTRL_FLAG_VPORT_MAP_TYPE;
4539 break;
4541 case A0RPI_OBJECT_VIEWPORT_MAP_SUBTYPE:
4542 if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
4543 spec->vport_map_subtype = buf.ReadWord();
4544 break;
4546 default:
4547 ret = HandleAction0PropertyDefault(buf, prop);
4548 break;
4552 return ret;
4556 * Define properties for railtypes
4557 * @param id ID of the railtype.
4558 * @param numinfo Number of subsequent IDs to change the property for.
4559 * @param prop The property to change.
4560 * @param buf The property value.
4561 * @return ChangeInfoResult.
4563 static ChangeInfoResult RailTypeChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
4565 ChangeInfoResult ret = CIR_SUCCESS;
4567 extern RailTypeInfo _railtypes[RAILTYPE_END];
4569 if (id + numinfo > RAILTYPE_END) {
4570 GrfMsg(1, "RailTypeChangeInfo: Rail type {} is invalid, max {}, ignoring", id + numinfo, RAILTYPE_END);
4571 return CIR_INVALID_ID;
4574 for (int i = 0; i < numinfo; i++) {
4575 RailType rt = _cur.grffile->railtype_map[id + i];
4576 if (rt == INVALID_RAILTYPE) return CIR_INVALID_ID;
4578 RailTypeInfo *rti = &_railtypes[rt];
4580 switch (prop) {
4581 case 0x08: // Label of rail type
4582 /* Skipped here as this is loaded during reservation stage. */
4583 buf.ReadDWord();
4584 break;
4586 case 0x09: { // Toolbar caption of railtype (sets name as well for backwards compatibility for grf ver < 8)
4587 uint16_t str = buf.ReadWord();
4588 AddStringForMapping(str, &rti->strings.toolbar_caption);
4589 if (_cur.grffile->grf_version < 8) {
4590 AddStringForMapping(str, &rti->strings.name);
4592 break;
4595 case 0x0A: // Menu text of railtype
4596 AddStringForMapping(buf.ReadWord(), &rti->strings.menu_text);
4597 break;
4599 case 0x0B: // Build window caption
4600 AddStringForMapping(buf.ReadWord(), &rti->strings.build_caption);
4601 break;
4603 case 0x0C: // Autoreplace text
4604 AddStringForMapping(buf.ReadWord(), &rti->strings.replace_text);
4605 break;
4607 case 0x0D: // New locomotive text
4608 AddStringForMapping(buf.ReadWord(), &rti->strings.new_loco);
4609 break;
4611 case 0x0E: // Compatible railtype list
4612 case 0x0F: // Powered railtype list
4613 case 0x18: // Railtype list required for date introduction
4614 case 0x19: // Introduced railtype list
4616 /* Rail type compatibility bits are added to the existing bits
4617 * to allow multiple GRFs to modify compatibility with the
4618 * default rail types. */
4619 int n = buf.ReadByte();
4620 for (int j = 0; j != n; j++) {
4621 RailTypeLabel label = buf.ReadDWord();
4622 RailType resolved_rt = GetRailTypeByLabel(BSWAP32(label), false);
4623 if (resolved_rt != INVALID_RAILTYPE) {
4624 switch (prop) {
4625 case 0x0F: SetBit(rti->powered_railtypes, resolved_rt); [[fallthrough]]; // Powered implies compatible.
4626 case 0x0E: SetBit(rti->compatible_railtypes, resolved_rt); break;
4627 case 0x18: SetBit(rti->introduction_required_railtypes, resolved_rt); break;
4628 case 0x19: SetBit(rti->introduces_railtypes, resolved_rt); break;
4632 break;
4635 case 0x10: // Rail Type flags
4636 rti->flags = (RailTypeFlags)buf.ReadByte();
4637 break;
4639 case 0x11: // Curve speed advantage
4640 rti->curve_speed = buf.ReadByte();
4641 break;
4643 case 0x12: // Station graphic
4644 rti->fallback_railtype = Clamp(buf.ReadByte(), 0, 2);
4645 break;
4647 case 0x13: // Construction cost factor
4648 rti->cost_multiplier = buf.ReadWord();
4649 break;
4651 case 0x14: // Speed limit
4652 rti->max_speed = buf.ReadWord();
4653 break;
4655 case 0x15: // Acceleration model
4656 rti->acceleration_type = Clamp(buf.ReadByte(), 0, 2);
4657 break;
4659 case 0x16: // Map colour
4660 rti->map_colour = buf.ReadByte();
4661 break;
4663 case 0x17: // Introduction date
4664 rti->introduction_date = CalTime::Date(static_cast<int32_t>(buf.ReadDWord()));
4665 break;
4667 case 0x1A: // Sort order
4668 rti->sorting_order = buf.ReadByte();
4669 break;
4671 case 0x1B: // Name of railtype (overridden by prop 09 for grf ver < 8)
4672 AddStringForMapping(buf.ReadWord(), &rti->strings.name);
4673 break;
4675 case 0x1C: // Maintenance cost factor
4676 rti->maintenance_multiplier = buf.ReadWord();
4677 break;
4679 case 0x1D: // Alternate rail type label list
4680 /* Skipped here as this is loaded during reservation stage. */
4681 for (int j = buf.ReadByte(); j != 0; j--) buf.ReadDWord();
4682 break;
4684 case A0RPI_RAILTYPE_ENABLE_PROGRAMMABLE_SIGNALS:
4685 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4686 AssignBit(rti->ctrl_flags, RTCF_PROGSIG, buf.ReadByte() != 0);
4687 break;
4689 case A0RPI_RAILTYPE_ENABLE_NO_ENTRY_SIGNALS:
4690 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4691 AssignBit(rti->ctrl_flags, RTCF_NOENTRYSIG, buf.ReadByte() != 0);
4692 break;
4694 case A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS:
4695 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4696 AssignBit(rti->ctrl_flags, RTCF_RESTRICTEDSIG, buf.ReadByte() != 0);
4697 break;
4699 case A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING:
4700 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4701 AssignBit(rti->ctrl_flags, RTCF_NOREALISTICBRAKING, buf.ReadByte() != 0);
4702 break;
4704 case A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR:
4705 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4706 AssignBit(rti->ctrl_flags, RTCF_RECOLOUR_ENABLED, buf.ReadByte() != 0);
4707 break;
4709 case A0RPI_RAILTYPE_EXTRA_ASPECTS:
4710 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4711 rti->signal_extra_aspects = std::min<uint8_t>(buf.ReadByte(), NEW_SIGNALS_MAX_EXTRA_ASPECT);
4712 break;
4714 default:
4715 ret = HandleAction0PropertyDefault(buf, prop);
4716 break;
4720 return ret;
4723 static ChangeInfoResult RailTypeReserveInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
4725 ChangeInfoResult ret = CIR_SUCCESS;
4727 extern RailTypeInfo _railtypes[RAILTYPE_END];
4729 if (id + numinfo > RAILTYPE_END) {
4730 GrfMsg(1, "RailTypeReserveInfo: Rail type {} is invalid, max {}, ignoring", id + numinfo, RAILTYPE_END);
4731 return CIR_INVALID_ID;
4734 for (int i = 0; i < numinfo; i++) {
4735 switch (prop) {
4736 case 0x08: // Label of rail type
4738 RailTypeLabel rtl = buf.ReadDWord();
4739 rtl = BSWAP32(rtl);
4741 RailType rt = GetRailTypeByLabel(rtl, false);
4742 if (rt == INVALID_RAILTYPE) {
4743 /* Set up new rail type */
4744 rt = AllocateRailType(rtl);
4747 _cur.grffile->railtype_map[id + i] = rt;
4748 break;
4751 case 0x09: // Toolbar caption of railtype
4752 case 0x0A: // Menu text
4753 case 0x0B: // Build window caption
4754 case 0x0C: // Autoreplace text
4755 case 0x0D: // New loco
4756 case 0x13: // Construction cost
4757 case 0x14: // Speed limit
4758 case 0x1B: // Name of railtype
4759 case 0x1C: // Maintenance cost factor
4760 buf.ReadWord();
4761 break;
4763 case 0x1D: // Alternate rail type label list
4764 if (_cur.grffile->railtype_map[id + i] != INVALID_RAILTYPE) {
4765 int n = buf.ReadByte();
4766 for (int j = 0; j != n; j++) {
4767 _railtypes[_cur.grffile->railtype_map[id + i]].alternate_labels.push_back(BSWAP32(buf.ReadDWord()));
4769 break;
4771 GrfMsg(1, "RailTypeReserveInfo: Ignoring property 1D for rail type {} because no label was set", id + i);
4772 [[fallthrough]];
4774 case 0x0E: // Compatible railtype list
4775 case 0x0F: // Powered railtype list
4776 case 0x18: // Railtype list required for date introduction
4777 case 0x19: // Introduced railtype list
4778 for (int j = buf.ReadByte(); j != 0; j--) buf.ReadDWord();
4779 break;
4781 case 0x10: // Rail Type flags
4782 case 0x11: // Curve speed advantage
4783 case 0x12: // Station graphic
4784 case 0x15: // Acceleration model
4785 case 0x16: // Map colour
4786 case 0x1A: // Sort order
4787 buf.ReadByte();
4788 break;
4790 case 0x17: // Introduction date
4791 buf.ReadDWord();
4792 break;
4794 case A0RPI_RAILTYPE_ENABLE_PROGRAMMABLE_SIGNALS:
4795 case A0RPI_RAILTYPE_ENABLE_NO_ENTRY_SIGNALS:
4796 case A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS:
4797 case A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING:
4798 case A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR:
4799 case A0RPI_RAILTYPE_EXTRA_ASPECTS:
4800 buf.Skip(buf.ReadExtendedByte());
4801 break;
4803 default:
4804 ret = HandleAction0PropertyDefault(buf, prop);
4805 break;
4809 return ret;
4813 * Define properties for roadtypes
4814 * @param id ID of the roadtype.
4815 * @param numinfo Number of subsequent IDs to change the property for.
4816 * @param prop The property to change.
4817 * @param buf The property value.
4818 * @return ChangeInfoResult.
4820 static ChangeInfoResult RoadTypeChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf, RoadTramType rtt)
4822 ChangeInfoResult ret = CIR_SUCCESS;
4824 extern RoadTypeInfo _roadtypes[ROADTYPE_END];
4825 std::array<RoadType, ROADTYPE_END> &type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map;
4827 if (id + numinfo > ROADTYPE_END) {
4828 GrfMsg(1, "RoadTypeChangeInfo: Road type {} is invalid, max {}, ignoring", id + numinfo, ROADTYPE_END);
4829 return CIR_INVALID_ID;
4832 for (int i = 0; i < numinfo; i++) {
4833 RoadType rt = type_map[id + i];
4834 if (rt == INVALID_ROADTYPE) return CIR_INVALID_ID;
4836 RoadTypeInfo *rti = &_roadtypes[rt];
4838 switch (prop) {
4839 case 0x08: // Label of road type
4840 /* Skipped here as this is loaded during reservation stage. */
4841 buf.ReadDWord();
4842 break;
4844 case 0x09: { // Toolbar caption of roadtype (sets name as well for backwards compatibility for grf ver < 8)
4845 uint16_t str = buf.ReadWord();
4846 AddStringForMapping(str, &rti->strings.toolbar_caption);
4847 break;
4850 case 0x0A: // Menu text of roadtype
4851 AddStringForMapping(buf.ReadWord(), &rti->strings.menu_text);
4852 break;
4854 case 0x0B: // Build window caption
4855 AddStringForMapping(buf.ReadWord(), &rti->strings.build_caption);
4856 break;
4858 case 0x0C: // Autoreplace text
4859 AddStringForMapping(buf.ReadWord(), &rti->strings.replace_text);
4860 break;
4862 case 0x0D: // New engine text
4863 AddStringForMapping(buf.ReadWord(), &rti->strings.new_engine);
4864 break;
4866 case 0x0F: // Powered roadtype list
4867 case 0x18: // Roadtype list required for date introduction
4868 case 0x19: { // Introduced roadtype list
4869 /* Road type compatibility bits are added to the existing bits
4870 * to allow multiple GRFs to modify compatibility with the
4871 * default road types. */
4872 int n = buf.ReadByte();
4873 for (int j = 0; j != n; j++) {
4874 RoadTypeLabel label = buf.ReadDWord();
4875 RoadType resolved_rt = GetRoadTypeByLabel(BSWAP32(label), false);
4876 if (resolved_rt != INVALID_ROADTYPE) {
4877 switch (prop) {
4878 case 0x0F:
4879 if (GetRoadTramType(resolved_rt) == rtt) {
4880 SetBit(rti->powered_roadtypes, resolved_rt);
4881 } else {
4882 GrfMsg(1, "RoadTypeChangeInfo: Powered road type list: Road type {} road/tram type does not match road type {}, ignoring", resolved_rt, rt);
4884 break;
4885 case 0x18: SetBit(rti->introduction_required_roadtypes, resolved_rt); break;
4886 case 0x19: SetBit(rti->introduces_roadtypes, resolved_rt); break;
4890 break;
4893 case 0x10: // Road Type flags
4894 rti->flags = (RoadTypeFlags)buf.ReadByte();
4895 break;
4897 case 0x13: // Construction cost factor
4898 rti->cost_multiplier = buf.ReadWord();
4899 break;
4901 case 0x14: // Speed limit
4902 rti->max_speed = buf.ReadWord();
4903 break;
4905 case 0x16: // Map colour
4906 rti->map_colour = buf.ReadByte();
4907 break;
4909 case 0x17: // Introduction date
4910 rti->introduction_date = CalTime::Date(static_cast<int32_t>(buf.ReadDWord()));
4911 break;
4913 case 0x1A: // Sort order
4914 rti->sorting_order = buf.ReadByte();
4915 break;
4917 case 0x1B: // Name of roadtype
4918 AddStringForMapping(buf.ReadWord(), &rti->strings.name);
4919 break;
4921 case 0x1C: // Maintenance cost factor
4922 rti->maintenance_multiplier = buf.ReadWord();
4923 break;
4925 case 0x1D: // Alternate road type label list
4926 /* Skipped here as this is loaded during reservation stage. */
4927 for (int j = buf.ReadByte(); j != 0; j--) buf.ReadDWord();
4928 break;
4930 case A0RPI_ROADTYPE_EXTRA_FLAGS:
4931 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4932 rti->extra_flags = (RoadTypeExtraFlags)buf.ReadByte();
4933 break;
4935 case A0RPI_ROADTYPE_COLLISION_MODE: {
4936 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
4937 uint8_t collision_mode = buf.ReadByte();
4938 if (collision_mode < RTCM_END) rti->collision_mode = (RoadTypeCollisionMode)collision_mode;
4939 break;
4942 default:
4943 ret = HandleAction0PropertyDefault(buf, prop);
4944 break;
4948 return ret;
4951 static ChangeInfoResult RoadTypeChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
4953 return RoadTypeChangeInfo(id, numinfo, prop, mapping_entry, buf, RTT_ROAD);
4956 static ChangeInfoResult TramTypeChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
4958 return RoadTypeChangeInfo(id, numinfo, prop, mapping_entry, buf, RTT_TRAM);
4962 static ChangeInfoResult RoadTypeReserveInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf, RoadTramType rtt)
4964 ChangeInfoResult ret = CIR_SUCCESS;
4966 extern RoadTypeInfo _roadtypes[ROADTYPE_END];
4967 std::array<RoadType, ROADTYPE_END> &type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map;
4969 if (id + numinfo > ROADTYPE_END) {
4970 GrfMsg(1, "RoadTypeReserveInfo: Road type {} is invalid, max {}, ignoring", id + numinfo, ROADTYPE_END);
4971 return CIR_INVALID_ID;
4974 for (int i = 0; i < numinfo; i++) {
4975 switch (prop) {
4976 case 0x08: { // Label of road type
4977 RoadTypeLabel rtl = buf.ReadDWord();
4978 rtl = BSWAP32(rtl);
4980 RoadType rt = GetRoadTypeByLabel(rtl, false);
4981 if (rt == INVALID_ROADTYPE) {
4982 /* Set up new road type */
4983 rt = AllocateRoadType(rtl, rtt);
4984 } else if (GetRoadTramType(rt) != rtt) {
4985 GrfMsg(1, "RoadTypeReserveInfo: Road type {} is invalid type (road/tram), ignoring", id + numinfo);
4986 return CIR_INVALID_ID;
4989 type_map[id + i] = rt;
4990 break;
4992 case 0x09: // Toolbar caption of roadtype
4993 case 0x0A: // Menu text
4994 case 0x0B: // Build window caption
4995 case 0x0C: // Autoreplace text
4996 case 0x0D: // New loco
4997 case 0x13: // Construction cost
4998 case 0x14: // Speed limit
4999 case 0x1B: // Name of roadtype
5000 case 0x1C: // Maintenance cost factor
5001 buf.ReadWord();
5002 break;
5004 case 0x1D: // Alternate road type label list
5005 if (type_map[id + i] != INVALID_ROADTYPE) {
5006 int n = buf.ReadByte();
5007 for (int j = 0; j != n; j++) {
5008 _roadtypes[type_map[id + i]].alternate_labels.push_back(BSWAP32(buf.ReadDWord()));
5010 break;
5012 GrfMsg(1, "RoadTypeReserveInfo: Ignoring property 1D for road type {} because no label was set", id + i);
5013 /* FALL THROUGH */
5015 case 0x0F: // Powered roadtype list
5016 case 0x18: // Roadtype list required for date introduction
5017 case 0x19: // Introduced roadtype list
5018 for (int j = buf.ReadByte(); j != 0; j--) buf.ReadDWord();
5019 break;
5021 case 0x10: // Road Type flags
5022 case 0x16: // Map colour
5023 case 0x1A: // Sort order
5024 buf.ReadByte();
5025 break;
5027 case 0x17: // Introduction date
5028 buf.ReadDWord();
5029 break;
5031 case A0RPI_ROADTYPE_EXTRA_FLAGS:
5032 buf.Skip(buf.ReadExtendedByte());
5033 break;
5035 case A0RPI_ROADTYPE_COLLISION_MODE:
5036 buf.Skip(buf.ReadExtendedByte());
5037 break;
5039 default:
5040 ret = HandleAction0PropertyDefault(buf, prop);
5041 break;
5045 return ret;
5048 static ChangeInfoResult RoadTypeReserveInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
5050 return RoadTypeReserveInfo(id, numinfo, prop, mapping_entry, buf, RTT_ROAD);
5053 static ChangeInfoResult TramTypeReserveInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
5055 return RoadTypeReserveInfo(id, numinfo, prop, mapping_entry, buf, RTT_TRAM);
5058 static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
5060 ChangeInfoResult ret = CIR_SUCCESS;
5062 if (airtid + numinfo > NUM_AIRPORTTILES_PER_GRF) {
5063 GrfMsg(1, "AirportTileChangeInfo: Too many airport tiles loaded ({}), max ({}). Ignoring.", airtid + numinfo, NUM_AIRPORTTILES_PER_GRF);
5064 return CIR_INVALID_ID;
5067 /* Allocate airport tile specs if they haven't been allocated already. */
5068 if (_cur.grffile->airtspec.size() < airtid + numinfo) _cur.grffile->airtspec.resize(airtid + numinfo);
5070 for (int i = 0; i < numinfo; i++) {
5071 AirportTileSpec *tsp = _cur.grffile->airtspec[airtid + i].get();
5073 if (prop != 0x08 && tsp == nullptr) {
5074 GrfMsg(2, "AirportTileChangeInfo: Attempt to modify undefined airport tile {}. Ignoring.", airtid + i);
5075 return CIR_INVALID_ID;
5078 switch (prop) {
5079 case 0x08: { // Substitute airport tile type
5080 uint8_t subs_id = buf.ReadByte();
5081 if (subs_id >= NEW_AIRPORTTILE_OFFSET) {
5082 /* The substitute id must be one of the original airport tiles. */
5083 GrfMsg(2, "AirportTileChangeInfo: Attempt to use new airport tile {} as substitute airport tile for {}. Ignoring.", subs_id, airtid + i);
5084 continue;
5087 /* Allocate space for this airport tile. */
5088 if (tsp == nullptr) {
5089 _cur.grffile->airtspec[airtid + i] = std::make_unique<AirportTileSpec>(*AirportTileSpec::Get(subs_id));
5090 tsp = _cur.grffile->airtspec[airtid + i].get();
5092 tsp->enabled = true;
5094 tsp->animation.status = ANIM_STATUS_NO_ANIMATION;
5096 tsp->grf_prop.local_id = airtid + i;
5097 tsp->grf_prop.subst_id = subs_id;
5098 tsp->grf_prop.grfid = _cur.grffile->grfid;
5099 tsp->grf_prop.grffile = _cur.grffile;
5100 _airporttile_mngr.AddEntityID(airtid + i, _cur.grffile->grfid, subs_id); // pre-reserve the tile slot
5102 break;
5105 case 0x09: { // Airport tile override
5106 uint8_t override = buf.ReadByte();
5108 /* The airport tile being overridden must be an original airport tile. */
5109 if (override >= NEW_AIRPORTTILE_OFFSET) {
5110 GrfMsg(2, "AirportTileChangeInfo: Attempt to override new airport tile {} with airport tile id {}. Ignoring.", override, airtid + i);
5111 continue;
5114 _airporttile_mngr.Add(airtid + i, _cur.grffile->grfid, override);
5115 break;
5118 case 0x0E: // Callback mask
5119 tsp->callback_mask = buf.ReadByte();
5120 break;
5122 case 0x0F: // Animation information
5123 tsp->animation.frames = buf.ReadByte();
5124 tsp->animation.status = buf.ReadByte();
5125 break;
5127 case 0x10: // Animation speed
5128 tsp->animation.speed = buf.ReadByte();
5129 break;
5131 case 0x11: // Animation triggers
5132 tsp->animation.triggers = buf.ReadByte();
5133 break;
5135 default:
5136 ret = HandleAction0PropertyDefault(buf, prop);
5137 break;
5141 return ret;
5145 * Ignore properties for roadstops
5146 * @param prop The property to ignore.
5147 * @param buf The property value.
5148 * @return ChangeInfoResult.
5150 static ChangeInfoResult IgnoreRoadStopProperty(uint prop, ByteReader &buf)
5152 ChangeInfoResult ret = CIR_SUCCESS;
5154 switch (prop) {
5155 case 0x09:
5156 case 0x0C:
5157 case 0x0F:
5158 case 0x11:
5159 buf.ReadByte();
5160 break;
5162 case 0x0A:
5163 case 0x0B:
5164 case 0x0E:
5165 case 0x10:
5166 case 0x15:
5167 buf.ReadWord();
5168 break;
5170 case 0x08:
5171 case 0x0D:
5172 case 0x12:
5173 buf.ReadDWord();
5174 break;
5176 default:
5177 ret = HandleAction0PropertyDefault(buf, prop);
5178 break;
5181 return ret;
5184 static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
5186 ChangeInfoResult ret = CIR_SUCCESS;
5188 if (id + numinfo > NUM_ROADSTOPS_PER_GRF) {
5189 GrfMsg(1, "RoadStopChangeInfo: RoadStop {} is invalid, max {}, ignoring", id + numinfo, NUM_ROADSTOPS_PER_GRF);
5190 return CIR_INVALID_ID;
5193 if (id + numinfo > _cur.grffile->roadstops.size()) {
5194 _cur.grffile->roadstops.resize(id + numinfo);
5197 for (int i = 0; i < numinfo; i++) {
5198 RoadStopSpec *rs = _cur.grffile->roadstops[id + i].get();
5200 if (rs == nullptr && prop != 0x08 && prop != A0RPI_ROADSTOP_CLASS_ID) {
5201 GrfMsg(1, "RoadStopChangeInfo: Attempt to modify undefined road stop {}, ignoring", id + i);
5202 ChangeInfoResult cir = IgnoreRoadStopProperty(prop, buf);
5203 if (cir > ret) ret = cir;
5204 continue;
5207 switch (prop) {
5208 case A0RPI_ROADSTOP_CLASS_ID:
5209 if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
5210 [[fallthrough]];
5211 case 0x08: { // Road Stop Class ID
5212 if (rs == nullptr) {
5213 _cur.grffile->roadstops[id + i] = std::make_unique<RoadStopSpec>();
5214 rs = _cur.grffile->roadstops[id + i].get();
5217 uint32_t classid = buf.ReadDWord();
5218 rs->class_index = RoadStopClass::Allocate(BSWAP32(classid));
5219 break;
5222 case A0RPI_ROADSTOP_STOP_TYPE:
5223 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
5224 [[fallthrough]];
5225 case 0x09: // Road stop type
5226 rs->stop_type = (RoadStopAvailabilityType)buf.ReadByte();
5227 break;
5229 case A0RPI_ROADSTOP_STOP_NAME:
5230 if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
5231 [[fallthrough]];
5232 case 0x0A: // Road Stop Name
5233 AddStringForMapping(buf.ReadWord(), &rs->name);
5234 break;
5236 case A0RPI_ROADSTOP_CLASS_NAME:
5237 if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
5238 [[fallthrough]];
5239 case 0x0B: // Road Stop Class name
5240 AddStringForMapping(buf.ReadWord(), rs, [](StringID str, RoadStopSpec *rs) { RoadStopClass::Get(rs->class_index)->name = str; });
5241 break;
5243 case A0RPI_ROADSTOP_DRAW_MODE:
5244 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
5245 [[fallthrough]];
5246 case 0x0C: // The draw mode
5247 rs->draw_mode = static_cast<RoadStopDrawMode>(buf.ReadByte());
5248 break;
5250 case A0RPI_ROADSTOP_TRIGGER_CARGOES:
5251 if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
5252 [[fallthrough]];
5253 case 0x0D: // Cargo types for random triggers
5254 rs->cargo_triggers = TranslateRefitMask(buf.ReadDWord());
5255 break;
5257 case A0RPI_ROADSTOP_ANIMATION_INFO:
5258 if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
5259 [[fallthrough]];
5260 case 0x0E: // Animation info
5261 rs->animation.frames = buf.ReadByte();
5262 rs->animation.status = buf.ReadByte();
5263 break;
5265 case A0RPI_ROADSTOP_ANIMATION_SPEED:
5266 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
5267 [[fallthrough]];
5268 case 0x0F: // Animation speed
5269 rs->animation.speed = buf.ReadByte();
5270 break;
5272 case A0RPI_ROADSTOP_ANIMATION_TRIGGERS:
5273 if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
5274 [[fallthrough]];
5275 case 0x10: // Animation triggers
5276 rs->animation.triggers = buf.ReadWord();
5277 break;
5279 case A0RPI_ROADSTOP_CALLBACK_MASK:
5280 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
5281 [[fallthrough]];
5282 case 0x11: // Callback mask
5283 rs->callback_mask = buf.ReadByte();
5284 break;
5286 case A0RPI_ROADSTOP_GENERAL_FLAGS:
5287 if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
5288 [[fallthrough]];
5289 case 0x12: // General flags
5290 rs->flags = (uint16_t)buf.ReadDWord(); // Future-proofing, size this as 4 bytes, but we only need two byte's worth of flags at present
5291 break;
5293 case A0RPI_ROADSTOP_MIN_BRIDGE_HEIGHT:
5294 if (MappedPropertyLengthMismatch(buf, 6, mapping_entry)) break;
5295 [[fallthrough]];
5296 case 0x13: // Minimum height for a bridge above
5297 SetBit(rs->internal_flags, RSIF_BRIDGE_HEIGHTS_SET);
5298 for (uint i = 0; i < 6; i++) {
5299 rs->bridge_height[i] = buf.ReadByte();
5301 break;
5303 case A0RPI_ROADSTOP_DISALLOWED_BRIDGE_PILLARS:
5304 if (MappedPropertyLengthMismatch(buf, 6, mapping_entry)) break;
5305 [[fallthrough]];
5306 case 0x14: // Disallowed bridge pillars
5307 SetBit(rs->internal_flags, RSIF_BRIDGE_DISALLOWED_PILLARS_SET);
5308 for (uint i = 0; i < 6; i++) {
5309 rs->bridge_disallowed_pillars[i] = buf.ReadByte();
5311 break;
5313 case A0RPI_ROADSTOP_COST_MULTIPLIERS:
5314 if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
5315 [[fallthrough]];
5316 case 0x15: // Cost multipliers
5317 rs->build_cost_multiplier = buf.ReadByte();
5318 rs->clear_cost_multiplier = buf.ReadByte();
5319 break;
5321 case A0RPI_ROADSTOP_HEIGHT:
5322 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
5323 [[fallthrough]];
5324 case 0x16: // Height
5325 rs->height = buf.ReadByte();
5326 break;
5328 default:
5329 ret = HandleAction0PropertyDefault(buf, prop);
5330 break;
5334 return ret;
5338 * Define properties for new landscape
5339 * @param id Landscape type.
5340 * @param numinfo Number of subsequent IDs to change the property for.
5341 * @param prop The property to change.
5342 * @param buf The property value.
5343 * @return ChangeInfoResult.
5345 static ChangeInfoResult NewLandscapeChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader &buf)
5347 /* Properties which are handled per item */
5348 ChangeInfoResult ret = CIR_SUCCESS;
5349 for (int i = 0; i < numinfo; i++) {
5350 switch (prop) {
5351 case A0RPI_NEWLANDSCAPE_ENABLE_RECOLOUR: {
5352 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
5353 bool enabled = (buf.ReadByte() != 0 ? 1 : 0);
5354 if (id == NLA3ID_CUSTOM_ROCKS) {
5355 SB(_cur.grffile->new_landscape_ctrl_flags, NLCF_ROCKS_RECOLOUR_ENABLED, 1, enabled);
5357 break;
5360 case A0RPI_NEWLANDSCAPE_ENABLE_DRAW_SNOWY_ROCKS: {
5361 if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
5362 bool enabled = (buf.ReadByte() != 0 ? 1 : 0);
5363 if (id == NLA3ID_CUSTOM_ROCKS) {
5364 SB(_cur.grffile->new_landscape_ctrl_flags, NLCF_ROCKS_DRAW_SNOWY_ENABLED, 1, enabled);
5366 break;
5369 default:
5370 ret = HandleAction0PropertyDefault(buf, prop);
5371 break;
5375 return ret;
5378 static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, GrfSpecFeature feature, int property)
5380 switch (cir) {
5381 default: NOT_REACHED();
5383 case CIR_DISABLED:
5384 /* Error has already been printed; just stop parsing */
5385 return true;
5387 case CIR_SUCCESS:
5388 return false;
5390 case CIR_UNHANDLED:
5391 GrfMsg(1, "{}: Ignoring property 0x{:02X} of feature {} (not implemented)", caller, property, GetFeatureString(feature));
5392 return false;
5394 case CIR_UNKNOWN:
5395 GrfMsg(0, "{}: Unknown property 0x{:02X} of feature {}, disabling", caller, property, GetFeatureString(feature));
5396 [[fallthrough]];
5398 case CIR_INVALID_ID: {
5399 /* No debug message for an invalid ID, as it has already been output */
5400 GRFError *error = DisableGrf(cir == CIR_INVALID_ID ? STR_NEWGRF_ERROR_INVALID_ID : STR_NEWGRF_ERROR_UNKNOWN_PROPERTY);
5401 if (cir != CIR_INVALID_ID) error->param_value[1] = property;
5402 return true;
5407 static GrfSpecFeatureRef ReadFeature(uint8_t raw_byte, bool allow_48 = false)
5409 if (unlikely(HasBit(_cur.grffile->ctrl_flags, GFCF_HAVE_FEATURE_ID_REMAP))) {
5410 const GRFFeatureMapRemapSet &remap = _cur.grffile->feature_id_remaps;
5411 if (remap.remapped_ids[raw_byte]) {
5412 auto iter = remap.mapping.find(raw_byte);
5413 const GRFFeatureMapRemapEntry &def = iter->second;
5414 if (def.feature == GSF_ERROR_ON_USE) {
5415 GrfMsg(0, "Error: Unimplemented mapped feature: {}, mapped to: {:02X}", def.name, raw_byte);
5416 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_FEATURE_ID);
5417 error->data = stredup(def.name);
5418 error->param_value[1] = GSF_INVALID;
5419 error->param_value[2] = raw_byte;
5420 } else if (def.feature == GSF_INVALID) {
5421 GrfMsg(2, "Ignoring unimplemented mapped feature: {}, mapped to: {:02X}", def.name, raw_byte);
5423 return { def.feature, raw_byte };
5427 GrfSpecFeature feature;
5428 if (raw_byte >= GSF_REAL_FEATURE_END && !(allow_48 && raw_byte == 0x48)) {
5429 feature = GSF_INVALID;
5430 } else {
5431 feature = static_cast<GrfSpecFeature>(raw_byte);
5433 return { feature, raw_byte };
5436 static const char *_feature_names[] = {
5437 "TRAINS",
5438 "ROADVEHICLES",
5439 "SHIPS",
5440 "AIRCRAFT",
5441 "STATIONS",
5442 "CANALS",
5443 "BRIDGES",
5444 "HOUSES",
5445 "GLOBALVAR",
5446 "INDUSTRYTILES",
5447 "INDUSTRIES",
5448 "CARGOES",
5449 "SOUNDFX",
5450 "AIRPORTS",
5451 "SIGNALS",
5452 "OBJECTS",
5453 "RAILTYPES",
5454 "AIRPORTTILES",
5455 "ROADTYPES",
5456 "TRAMTYPES",
5457 "ROADSTOPS",
5458 "NEWLANDSCAPE",
5459 "TOWN",
5461 static_assert(lengthof(_feature_names) == GSF_END);
5463 void GetFeatureStringFormatter::fmt_format_value(format_target &output) const
5465 if (this->feature.id < GSF_END) {
5466 output.format("0x{:02X} ({})", this->feature.raw_byte, _feature_names[this->feature.id]);
5467 } else {
5468 if (unlikely(HasBit(_cur.grffile->ctrl_flags, GFCF_HAVE_FEATURE_ID_REMAP))) {
5469 const GRFFeatureMapRemapSet &remap = _cur.grffile->feature_id_remaps;
5470 if (remap.remapped_ids[this->feature.raw_byte]) {
5471 auto iter = remap.mapping.find(this->feature.raw_byte);
5472 const GRFFeatureMapRemapEntry &def = iter->second;
5473 output.format("0x{:02X} ({})", this->feature.raw_byte, def.name);
5474 return;
5477 output.format("0x{:02X}", this->feature.raw_byte);
5481 GetFeatureStringFormatter GetFeatureString(GrfSpecFeatureRef feature)
5483 return GetFeatureStringFormatter(feature);
5486 GetFeatureStringFormatter GetFeatureString(GrfSpecFeature feature)
5488 uint8_t raw_byte = feature;
5489 if (feature >= GSF_REAL_FEATURE_END) {
5490 for (const auto &entry : _cur.grffile->feature_id_remaps.mapping) {
5491 if (entry.second.feature == feature) {
5492 raw_byte = entry.second.raw_id;
5493 break;
5497 return GetFeatureStringFormatter(GrfSpecFeatureRef{ feature, raw_byte });
5500 struct GRFFilePropertyDescriptor {
5501 int prop;
5502 const GRFFilePropertyRemapEntry *entry;
5504 GRFFilePropertyDescriptor(int prop, const GRFFilePropertyRemapEntry *entry)
5505 : prop(prop), entry(entry) {}
5508 static GRFFilePropertyDescriptor ReadAction0PropertyID(ByteReader &buf, uint8_t feature)
5510 uint8_t raw_prop = buf.ReadByte();
5511 const GRFFilePropertyRemapSet &remap = _cur.grffile->action0_property_remaps[feature];
5512 if (remap.remapped_ids[raw_prop]) {
5513 auto iter = remap.mapping.find(raw_prop);
5514 assert(iter != remap.mapping.end());
5515 const GRFFilePropertyRemapEntry &def = iter->second;
5516 int prop = def.id;
5517 if (prop == A0RPI_UNKNOWN_ERROR) {
5518 GrfMsg(0, "Error: Unimplemented mapped property: {}, feature: {}, mapped to: {:X}", def.name, GetFeatureString(def.feature), raw_prop);
5519 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY);
5520 error->data = stredup(def.name);
5521 error->param_value[1] = def.feature;
5522 error->param_value[2] = raw_prop;
5523 } else if (prop == A0RPI_UNKNOWN_IGNORE) {
5524 GrfMsg(2, "Ignoring unimplemented mapped property: {}, feature: {}, mapped to: {:X}", def.name, GetFeatureString(def.feature), raw_prop);
5525 } else if (prop == A0RPI_ID_EXTENSION) {
5526 uint8_t *outer_data = buf.Data();
5527 size_t outer_length = buf.ReadExtendedByte();
5528 uint16_t mapped_id = buf.ReadWord();
5529 uint8_t *inner_data = buf.Data();
5530 size_t inner_length = buf.ReadExtendedByte();
5531 if (inner_length + (inner_data - outer_data) != outer_length) {
5532 GrfMsg(2, "Ignoring extended ID property with malformed lengths: {}, feature: {}, mapped to: {:X}", def.name, GetFeatureString(def.feature), raw_prop);
5533 buf.ResetReadPosition(outer_data);
5534 return GRFFilePropertyDescriptor(A0RPI_UNKNOWN_IGNORE, &def);
5537 auto ext = _cur.grffile->action0_extended_property_remaps.find((((uint32_t)feature) << 16) | mapped_id);
5538 if (ext != _cur.grffile->action0_extended_property_remaps.end()) {
5539 buf.ResetReadPosition(inner_data);
5540 const GRFFilePropertyRemapEntry &ext_def = ext->second;
5541 prop = ext_def.id;
5542 if (prop == A0RPI_UNKNOWN_ERROR) {
5543 GrfMsg(0, "Error: Unimplemented mapped extended ID property: {}, feature: {}, mapped to: {:X} (via {:X})", ext_def.name, GetFeatureString(ext_def.feature), mapped_id, raw_prop);
5544 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY);
5545 error->data = stredup(ext_def.name);
5546 error->param_value[1] = ext_def.feature;
5547 error->param_value[2] = 0xE0000 | mapped_id;
5548 } else if (prop == A0RPI_UNKNOWN_IGNORE) {
5549 GrfMsg(2, "Ignoring unimplemented mapped extended ID property: {}, feature: {}, mapped to: {:X} (via {:X})", ext_def.name, GetFeatureString(ext_def.feature), mapped_id, raw_prop);
5551 return GRFFilePropertyDescriptor(prop, &ext_def);
5552 } else {
5553 GrfMsg(2, "Ignoring unknown extended ID property: {}, feature: {}, mapped to: {:X} (via {:X})", def.name, GetFeatureString(def.feature), mapped_id, raw_prop);
5554 buf.ResetReadPosition(outer_data);
5555 return GRFFilePropertyDescriptor(A0RPI_UNKNOWN_IGNORE, &def);
5558 return GRFFilePropertyDescriptor(prop, &def);
5559 } else {
5560 return GRFFilePropertyDescriptor(raw_prop, nullptr);
5564 /* Action 0x00 */
5565 static void FeatureChangeInfo(ByteReader &buf)
5567 /* <00> <feature> <num-props> <num-info> <id> (<property <new-info>)...
5569 * B feature
5570 * B num-props how many properties to change per vehicle/station
5571 * B num-info how many vehicles/stations to change
5572 * E id ID of first vehicle/station to change, if num-info is
5573 * greater than one, this one and the following
5574 * vehicles/stations will be changed
5575 * B property what property to change, depends on the feature
5576 * V new-info new bytes of info (variable size; depends on properties) */
5578 static const VCI_Handler handler[] = {
5579 /* GSF_TRAINS */ RailVehicleChangeInfo,
5580 /* GSF_ROADVEHICLES */ RoadVehicleChangeInfo,
5581 /* GSF_SHIPS */ ShipVehicleChangeInfo,
5582 /* GSF_AIRCRAFT */ AircraftVehicleChangeInfo,
5583 /* GSF_STATIONS */ StationChangeInfo,
5584 /* GSF_CANALS */ CanalChangeInfo,
5585 /* GSF_BRIDGES */ BridgeChangeInfo,
5586 /* GSF_HOUSES */ TownHouseChangeInfo,
5587 /* GSF_GLOBALVAR */ GlobalVarChangeInfo,
5588 /* GSF_INDUSTRYTILES */ IndustrytilesChangeInfo,
5589 /* GSF_INDUSTRIES */ IndustriesChangeInfo,
5590 /* GSF_CARGOES */ nullptr, // Cargo is handled during reservation
5591 /* GSF_SOUNDFX */ SoundEffectChangeInfo,
5592 /* GSF_AIRPORTS */ AirportChangeInfo,
5593 /* GSF_SIGNALS */ SignalsChangeInfo,
5594 /* GSF_OBJECTS */ ObjectChangeInfo,
5595 /* GSF_RAILTYPES */ RailTypeChangeInfo,
5596 /* GSF_AIRPORTTILES */ AirportTilesChangeInfo,
5597 /* GSF_ROADTYPES */ RoadTypeChangeInfo,
5598 /* GSF_TRAMTYPES */ TramTypeChangeInfo,
5599 /* GSF_ROADSTOPS */ RoadStopChangeInfo,
5600 /* GSF_NEWLANDSCAPE */ NewLandscapeChangeInfo,
5601 /* GSF_FAKE_TOWNS */ nullptr,
5603 static_assert(GSF_END == lengthof(handler));
5604 static_assert(lengthof(handler) == lengthof(_cur.grffile->action0_property_remaps), "Action 0 feature list length mismatch");
5606 GrfSpecFeatureRef feature_ref = ReadFeature(buf.ReadByte());
5607 GrfSpecFeature feature = feature_ref.id;
5608 uint8_t numprops = buf.ReadByte();
5609 uint numinfo = buf.ReadByte();
5610 uint engine = buf.ReadExtendedByte();
5612 if (feature >= GSF_END) {
5613 GrfMsg(1, "FeatureChangeInfo: Unsupported feature {} skipping", GetFeatureString(feature_ref));
5614 return;
5617 GrfMsg(6, "FeatureChangeInfo: Feature {}, {} properties, to apply to {}+{}",
5618 GetFeatureString(feature_ref), numprops, engine, numinfo);
5620 if (handler[feature] == nullptr) {
5621 if (feature != GSF_CARGOES) GrfMsg(1, "FeatureChangeInfo: Unsupported feature {}, skipping", GetFeatureString(feature_ref));
5622 return;
5625 /* Mark the feature as used by the grf */
5626 SetBit(_cur.grffile->grf_features, feature);
5628 while (numprops-- && buf.HasData()) {
5629 GRFFilePropertyDescriptor desc = ReadAction0PropertyID(buf, feature);
5631 ChangeInfoResult cir = handler[feature](engine, numinfo, desc.prop, desc.entry, buf);
5632 if (HandleChangeInfoResult("FeatureChangeInfo", cir, feature, desc.prop)) return;
5636 /* Action 0x00 (GLS_SAFETYSCAN) */
5637 static void SafeChangeInfo(ByteReader &buf)
5639 GrfSpecFeatureRef feature = ReadFeature(buf.ReadByte());
5640 uint8_t numprops = buf.ReadByte();
5641 uint numinfo = buf.ReadByte();
5642 buf.ReadExtendedByte(); // id
5644 if (feature.id == GSF_BRIDGES && numprops == 1) {
5645 GRFFilePropertyDescriptor desc = ReadAction0PropertyID(buf, feature.id);
5646 /* Bridge property 0x0D is redefinition of sprite layout tables, which
5647 * is considered safe. */
5648 if (desc.prop == 0x0D) return;
5649 } else if (feature.id == GSF_GLOBALVAR && numprops == 1) {
5650 GRFFilePropertyDescriptor desc = ReadAction0PropertyID(buf, feature.id);
5651 /* Engine ID Mappings are safe, if the source is static */
5652 if (desc.prop == 0x11) {
5653 bool is_safe = true;
5654 for (uint i = 0; i < numinfo; i++) {
5655 uint32_t s = buf.ReadDWord();
5656 buf.ReadDWord(); // dest
5657 const GRFConfig *grfconfig = GetGRFConfig(s);
5658 if (grfconfig != nullptr && !HasBit(grfconfig->flags, GCF_STATIC)) {
5659 is_safe = false;
5660 break;
5663 if (is_safe) return;
5667 SetBit(_cur.grfconfig->flags, GCF_UNSAFE);
5669 /* Skip remainder of GRF */
5670 _cur.skip_sprites = -1;
5673 /* Action 0x00 (GLS_RESERVE) */
5674 static void ReserveChangeInfo(ByteReader &buf)
5676 GrfSpecFeatureRef feature_ref = ReadFeature(buf.ReadByte());
5677 GrfSpecFeature feature = feature_ref.id;
5679 if (feature != GSF_CARGOES && feature != GSF_GLOBALVAR && feature != GSF_RAILTYPES && feature != GSF_ROADTYPES && feature != GSF_TRAMTYPES) return;
5681 uint8_t numprops = buf.ReadByte();
5682 uint8_t numinfo = buf.ReadByte();
5683 uint16_t index = buf.ReadExtendedByte();
5685 while (numprops-- && buf.HasData()) {
5686 GRFFilePropertyDescriptor desc = ReadAction0PropertyID(buf, feature);
5687 ChangeInfoResult cir = CIR_SUCCESS;
5689 switch (feature) {
5690 default: NOT_REACHED();
5691 case GSF_CARGOES:
5692 cir = CargoChangeInfo(index, numinfo, desc.prop, desc.entry, buf);
5693 break;
5695 case GSF_GLOBALVAR:
5696 cir = GlobalVarReserveInfo(index, numinfo, desc.prop, desc.entry, buf);
5697 break;
5699 case GSF_RAILTYPES:
5700 cir = RailTypeReserveInfo(index, numinfo, desc.prop, desc.entry, buf);
5701 break;
5703 case GSF_ROADTYPES:
5704 cir = RoadTypeReserveInfo(index, numinfo, desc.prop, desc.entry, buf);
5705 break;
5707 case GSF_TRAMTYPES:
5708 cir = TramTypeReserveInfo(index, numinfo, desc.prop, desc.entry, buf);
5709 break;
5712 if (HandleChangeInfoResult("ReserveChangeInfo", cir, feature, desc.prop)) return;
5716 /* Action 0x01 */
5717 static void NewSpriteSet(ByteReader &buf)
5719 /* Basic format: <01> <feature> <num-sets> <num-ent>
5720 * Extended format: <01> <feature> 00 <first-set> <num-sets> <num-ent>
5722 * B feature feature to define sprites for
5723 * 0, 1, 2, 3: veh-type, 4: train stations
5724 * E first-set first sprite set to define
5725 * B num-sets number of sprite sets (extended byte in extended format)
5726 * E num-ent how many entries per sprite set
5727 * For vehicles, this is the number of different
5728 * vehicle directions in each sprite set
5729 * Set num-dirs=8, unless your sprites are symmetric.
5730 * In that case, use num-dirs=4.
5733 GrfSpecFeatureRef feature_ref = ReadFeature(buf.ReadByte());
5734 GrfSpecFeature feature = feature_ref.id;
5735 uint16_t num_sets = buf.ReadByte();
5736 uint16_t first_set = 0;
5738 if (num_sets == 0 && buf.HasData(3)) {
5739 /* Extended Action1 format.
5740 * Some GRFs define zero sets of zero sprites, though there is actually no use in that. Ignore them. */
5741 first_set = buf.ReadExtendedByte();
5742 num_sets = buf.ReadExtendedByte();
5744 uint16_t num_ents = buf.ReadExtendedByte();
5746 if (feature >= GSF_END) {
5747 _cur.skip_sprites = num_sets * num_ents;
5748 GrfMsg(1, "NewSpriteSet: Unsupported feature {}, skipping {} sprites", GetFeatureString(feature_ref), _cur.skip_sprites);
5749 return;
5752 _cur.AddSpriteSets(feature, _cur.spriteid, first_set, num_sets, num_ents);
5754 GrfMsg(7, "New sprite set at {} of feature {}, consisting of {} sets with {} views each (total {})",
5755 _cur.spriteid, GetFeatureString(feature), num_sets, num_ents, num_sets * num_ents
5758 for (int i = 0; i < num_sets * num_ents; i++) {
5759 _cur.nfo_line++;
5760 LoadNextSprite(_cur.spriteid++, *_cur.file, _cur.nfo_line);
5764 /* Action 0x01 (SKIP) */
5765 static void SkipAct1(ByteReader &buf)
5767 buf.ReadByte();
5768 uint16_t num_sets = buf.ReadByte();
5770 if (num_sets == 0 && buf.HasData(3)) {
5771 /* Extended Action1 format.
5772 * Some GRFs define zero sets of zero sprites, though there is actually no use in that. Ignore them. */
5773 buf.ReadExtendedByte(); // first_set
5774 num_sets = buf.ReadExtendedByte();
5776 uint16_t num_ents = buf.ReadExtendedByte();
5778 _cur.skip_sprites = num_sets * num_ents;
5780 GrfMsg(3, "SkipAct1: Skipping {} sprites", _cur.skip_sprites);
5783 const CallbackResultSpriteGroup *NewCallbackResultSpriteGroupNoTransform(uint16_t result)
5785 const CallbackResultSpriteGroup *&ptr = _callback_result_cache[result];
5786 if (ptr == nullptr) {
5787 assert(CallbackResultSpriteGroup::CanAllocateItem());
5788 ptr = new CallbackResultSpriteGroup(result);
5790 return ptr;
5793 static const CallbackResultSpriteGroup *NewCallbackResultSpriteGroup(uint16_t groupid)
5795 uint16_t result = CallbackResultSpriteGroup::TransformResultValue(groupid, _cur.grffile->grf_version >= 8);
5796 return NewCallbackResultSpriteGroupNoTransform(result);
5799 static const SpriteGroup *GetGroupFromGroupIDNoCBResult(uint16_t setid, uint8_t type, uint16_t groupid)
5801 if ((size_t)groupid >= _cur.spritegroups.size() || _cur.spritegroups[groupid] == nullptr) {
5802 GrfMsg(1, "GetGroupFromGroupID(0x{:02X}:0x{:02X}): Groupid 0x{:04X} does not exist, leaving empty", setid, type, groupid);
5803 return nullptr;
5806 const SpriteGroup *result = _cur.spritegroups[groupid];
5807 if (likely(!HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) result = PruneTargetSpriteGroup(result);
5808 return result;
5811 /* Helper function to either create a callback or link to a previously
5812 * defined spritegroup. */
5813 static const SpriteGroup *GetGroupFromGroupID(uint16_t setid, uint8_t type, uint16_t groupid)
5815 if (HasBit(groupid, 15)) {
5816 return NewCallbackResultSpriteGroup(groupid);
5819 return GetGroupFromGroupIDNoCBResult(setid, type, groupid);
5822 static const SpriteGroup *GetGroupByID(uint16_t groupid)
5824 if ((size_t)groupid >= _cur.spritegroups.size()) return nullptr;
5826 const SpriteGroup *result = _cur.spritegroups[groupid];
5827 return result;
5831 * Helper function to either create a callback or a result sprite group.
5832 * @param feature GrfSpecFeature to define spritegroup for.
5833 * @param setid SetID of the currently being parsed Action2. (only for debug output)
5834 * @param type Type of the currently being parsed Action2. (only for debug output)
5835 * @param spriteid Raw value from the GRF for the new spritegroup; describes either the return value or the referenced spritegroup.
5836 * @return Created spritegroup.
5838 static const SpriteGroup *CreateGroupFromGroupID(uint8_t feature, uint16_t setid, uint8_t type, uint16_t spriteid)
5840 if (HasBit(spriteid, 15)) {
5841 return NewCallbackResultSpriteGroup(spriteid);
5844 const SpriteSetInfo sprite_set_info = _cur.GetSpriteSetInfo(feature, spriteid);
5846 if (!sprite_set_info.IsValid()) {
5847 GrfMsg(1, "CreateGroupFromGroupID(0x{:02X}:0x{:02X}): Sprite set {} invalid", setid, type, spriteid);
5848 return nullptr;
5851 SpriteID spriteset_start = sprite_set_info.GetSprite();
5852 uint num_sprites = sprite_set_info.GetNumEnts();
5854 /* Ensure that the sprites are loeded */
5855 assert(spriteset_start + num_sprites <= _cur.spriteid);
5857 assert(ResultSpriteGroup::CanAllocateItem());
5858 return new ResultSpriteGroup(spriteset_start, num_sprites);
5861 static void ProcessDeterministicSpriteGroupRanges(const std::vector<DeterministicSpriteGroupRange> &ranges, std::vector<DeterministicSpriteGroupRange> &ranges_out, const SpriteGroup *default_group)
5863 /* Sort ranges ascending. When ranges overlap, this may required clamping or splitting them */
5864 std::vector<uint32_t> bounds;
5865 bounds.reserve(ranges.size());
5866 for (uint i = 0; i < ranges.size(); i++) {
5867 bounds.push_back(ranges[i].low);
5868 if (ranges[i].high != UINT32_MAX) bounds.push_back(ranges[i].high + 1);
5870 std::sort(bounds.begin(), bounds.end());
5871 bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end());
5873 std::vector<const SpriteGroup *> target;
5874 target.reserve(bounds.size());
5875 for (uint j = 0; j < bounds.size(); ++j) {
5876 uint32_t v = bounds[j];
5877 const SpriteGroup *t = default_group;
5878 for (uint i = 0; i < ranges.size(); i++) {
5879 if (ranges[i].low <= v && v <= ranges[i].high) {
5880 t = ranges[i].group;
5881 break;
5884 target.push_back(t);
5886 assert(target.size() == bounds.size());
5888 for (uint j = 0; j < bounds.size(); ) {
5889 if (target[j] != default_group) {
5890 DeterministicSpriteGroupRange &r = ranges_out.emplace_back();
5891 r.group = target[j];
5892 r.low = bounds[j];
5893 while (j < bounds.size() && target[j] == r.group) {
5894 j++;
5896 r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX;
5897 } else {
5898 j++;
5903 static VarSpriteGroupScopeOffset ParseRelativeScopeByte(uint8_t relative)
5905 VarSpriteGroupScopeOffset var_scope_count = (GB(relative, 6, 2) << 8);
5906 if ((relative & 0xF) == 0) {
5907 SetBit(var_scope_count, 15);
5908 } else {
5909 var_scope_count |= (relative & 0xF);
5911 return var_scope_count;
5914 /* Action 0x02 */
5915 static void NewSpriteGroup(ByteReader &buf)
5917 /* <02> <feature> <set-id> <type/num-entries> <feature-specific-data...>
5919 * B feature see action 1
5920 * B set-id ID of this particular definition
5921 * This is an extended byte if feature "more_action2_ids" is tested for
5922 * B type/num-entries
5923 * if 80 or greater, this is a randomized or variational
5924 * list definition, see below
5925 * otherwise it specifies a number of entries, the exact
5926 * meaning depends on the feature
5927 * V feature-specific-data (huge mess, don't even look it up --pasky) */
5928 const SpriteGroup *act_group = nullptr;
5930 GrfSpecFeatureRef feature_ref = ReadFeature(buf.ReadByte());
5931 GrfSpecFeature feature = feature_ref.id;
5932 if (feature >= GSF_END) {
5933 GrfMsg(1, "NewSpriteGroup: Unsupported feature {}, skipping", GetFeatureString(feature_ref));
5934 return;
5937 uint16_t setid = HasBit(_cur.grffile->observed_feature_tests, GFTOF_MORE_ACTION2_IDS) ? buf.ReadExtendedByte() : buf.ReadByte();
5938 uint8_t type = buf.ReadByte();
5940 /* Sprite Groups are created here but they are allocated from a pool, so
5941 * we do not need to delete anything if there is an exception from the
5942 * ByteReader. */
5944 /* Decoded sprite type */
5945 enum SpriteType {
5946 STYPE_NORMAL,
5947 STYPE_DETERMINISTIC,
5948 STYPE_DETERMINISTIC_RELATIVE,
5949 STYPE_DETERMINISTIC_RELATIVE_2,
5950 STYPE_RANDOMIZED,
5951 STYPE_CB_FAILURE,
5953 SpriteType stype = STYPE_NORMAL;
5954 switch (type) {
5955 /* Deterministic Sprite Group */
5956 case 0x81: // Self scope, byte
5957 case 0x82: // Parent scope, byte
5958 case 0x85: // Self scope, word
5959 case 0x86: // Parent scope, word
5960 case 0x89: // Self scope, dword
5961 case 0x8A: // Parent scope, dword
5962 stype = STYPE_DETERMINISTIC;
5963 break;
5965 /* Randomized Sprite Group */
5966 case 0x80: // Self scope
5967 case 0x83: // Parent scope
5968 case 0x84: // Relative scope
5969 stype = STYPE_RANDOMIZED;
5970 break;
5972 /* Extension type */
5973 case 0x87:
5974 if (HasBit(_cur.grffile->observed_feature_tests, GFTOF_MORE_VARACTION2_TYPES)) {
5975 uint8_t subtype = buf.ReadByte();
5976 switch (subtype) {
5977 case 0:
5978 stype = STYPE_CB_FAILURE;
5979 break;
5981 case 1:
5982 stype = STYPE_DETERMINISTIC_RELATIVE;
5983 break;
5985 case 2:
5986 stype = STYPE_DETERMINISTIC_RELATIVE_2;
5987 break;
5989 default:
5990 GrfMsg(1, "NewSpriteGroup: Unknown 0x87 extension subtype {:02X} for feature {}, handling as CB failure", subtype, GetFeatureString(feature));
5991 stype = STYPE_CB_FAILURE;
5992 break;
5995 break;
5997 default:
5998 break;
6001 switch (stype) {
6002 /* Deterministic Sprite Group */
6003 case STYPE_DETERMINISTIC:
6004 case STYPE_DETERMINISTIC_RELATIVE:
6005 case STYPE_DETERMINISTIC_RELATIVE_2:
6007 VarSpriteGroupScopeOffset var_scope_count = 0;
6008 if (stype == STYPE_DETERMINISTIC_RELATIVE) {
6009 var_scope_count = ParseRelativeScopeByte(buf.ReadByte());
6010 } else if (stype == STYPE_DETERMINISTIC_RELATIVE_2) {
6011 uint8_t mode = buf.ReadByte();
6012 uint8_t offset = buf.ReadByte();
6013 bool invalid = false;
6014 if ((mode & 0x7F) >= VSGSRM_END) {
6015 invalid = true;
6017 if (HasBit(mode, 7)) {
6018 /* Use variable 0x100 */
6019 if (offset != 0) invalid = true;
6021 if (invalid) {
6022 GrfMsg(1, "NewSpriteGroup: Unknown 0x87 extension subtype 2 relative mode: {:02X} {:02X} for feature {}, handling as CB failure", mode, offset, GetFeatureString(feature));
6023 act_group = NewCallbackResultSpriteGroupNoTransform(CALLBACK_FAILED);
6024 break;
6026 var_scope_count = (mode << 8) | offset;
6029 uint8_t varadjust;
6030 uint8_t varsize;
6032 bool first_adjust = true;
6034 assert(DeterministicSpriteGroup::CanAllocateItem());
6035 DeterministicSpriteGroup *group = new DeterministicSpriteGroup();
6036 group->nfo_line = _cur.nfo_line;
6037 group->feature = feature;
6038 if (_action6_override_active) group->sg_flags |= SGF_ACTION6;
6039 act_group = group;
6041 if (stype == STYPE_DETERMINISTIC_RELATIVE || stype == STYPE_DETERMINISTIC_RELATIVE_2) {
6042 group->var_scope = (feature <= GSF_AIRCRAFT) ? VSG_SCOPE_RELATIVE : VSG_SCOPE_SELF;
6043 group->var_scope_count = var_scope_count;
6045 group->size = DSG_SIZE_DWORD;
6046 varsize = 4;
6047 } else {
6048 group->var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
6050 switch (GB(type, 2, 2)) {
6051 default: NOT_REACHED();
6052 case 0: group->size = DSG_SIZE_BYTE; varsize = 1; break;
6053 case 1: group->size = DSG_SIZE_WORD; varsize = 2; break;
6054 case 2: group->size = DSG_SIZE_DWORD; varsize = 4; break;
6058 const VarAction2AdjustInfo info = { feature, GetGrfSpecFeatureForScope(feature, group->var_scope), varsize };
6060 DeterministicSpriteGroupShadowCopy *shadow = nullptr;
6061 if (unlikely(HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) {
6062 shadow = &(_deterministic_sg_shadows[group]);
6064 static std::vector<DeterministicSpriteGroupAdjust> current_adjusts;
6065 current_adjusts.clear();
6067 VarAction2OptimiseState va2_opt_state;
6068 /* The initial value is always the constant 0 */
6069 va2_opt_state.inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO | VA2AIF_HAVE_CONSTANT;
6070 va2_opt_state.current_constant = 0;
6072 /* Loop through the var adjusts. Unfortunately we don't know how many we have
6073 * from the outset, so we shall have to keep reallocing. */
6074 do {
6075 DeterministicSpriteGroupAdjust &adjust = current_adjusts.emplace_back();
6077 /* The first var adjust doesn't have an operation specified, so we set it to add. */
6078 adjust.operation = first_adjust ? DSGA_OP_ADD : (DeterministicSpriteGroupAdjustOperation)buf.ReadByte();
6079 first_adjust = false;
6080 if (adjust.operation > DSGA_OP_END) adjust.operation = DSGA_OP_END;
6081 adjust.variable = buf.ReadByte();
6082 if (adjust.variable == 0x7E) {
6083 /* Link subroutine group */
6084 adjust.subroutine = GetGroupFromGroupIDNoCBResult(setid, type, HasBit(_cur.grffile->observed_feature_tests, GFTOF_MORE_ACTION2_IDS) ? buf.ReadExtendedByte() : buf.ReadByte());
6085 } else {
6086 adjust.parameter = IsInsideMM(adjust.variable, 0x60, 0x80) ? buf.ReadByte() : 0;
6089 varadjust = buf.ReadByte();
6090 adjust.shift_num = GB(varadjust, 0, 5);
6091 adjust.type = (DeterministicSpriteGroupAdjustType)GB(varadjust, 6, 2);
6092 adjust.and_mask = buf.ReadVarSize(varsize);
6094 if (adjust.variable == 0x11) {
6095 for (const GRFVariableMapEntry &remap : _cur.grffile->grf_variable_remaps) {
6096 if (remap.feature == info.scope_feature && remap.input_shift == adjust.shift_num && remap.input_mask == adjust.and_mask) {
6097 adjust.variable = remap.id;
6098 adjust.shift_num = remap.output_shift;
6099 adjust.and_mask = remap.output_mask;
6100 adjust.parameter = remap.output_param;
6101 break;
6104 } else if (adjust.variable == 0x7B && adjust.parameter == 0x11) {
6105 for (const GRFVariableMapEntry &remap : _cur.grffile->grf_variable_remaps) {
6106 if (remap.feature == info.scope_feature && remap.input_shift == adjust.shift_num && remap.input_mask == adjust.and_mask) {
6107 adjust.parameter = remap.id;
6108 adjust.shift_num = remap.output_shift;
6109 adjust.and_mask = remap.output_mask;
6110 break;
6115 if (info.scope_feature == GSF_ROADSTOPS && HasBit(_cur.grffile->observed_feature_tests, GFTOF_ROAD_STOPS)) {
6116 if (adjust.variable == 0x68) adjust.variable = A2VRI_ROADSTOP_INFO_NEARBY_TILES_EXT;
6117 if (adjust.variable == 0x7B && adjust.parameter == 0x68) adjust.parameter = A2VRI_ROADSTOP_INFO_NEARBY_TILES_EXT;
6120 if (adjust.type != DSGA_TYPE_NONE) {
6121 adjust.add_val = buf.ReadVarSize(varsize);
6122 adjust.divmod_val = buf.ReadVarSize(varsize);
6123 if (adjust.divmod_val == 0) adjust.divmod_val = 1; // Ensure that divide by zero cannot occur
6124 } else {
6125 adjust.add_val = 0;
6126 adjust.divmod_val = 0;
6128 if (unlikely(shadow != nullptr)) {
6129 shadow->adjusts.push_back(adjust);
6130 /* Pruning was turned off so that the unpruned target could be saved in the shadow, prune now */
6131 if (adjust.subroutine != nullptr) adjust.subroutine = PruneTargetSpriteGroup(adjust.subroutine);
6134 OptimiseVarAction2PreCheckAdjust(va2_opt_state, adjust);
6136 /* Continue reading var adjusts while bit 5 is set. */
6137 } while (HasBit(varadjust, 5));
6139 /* shrink_to_fit will be called later */
6140 group->adjusts.reserve(current_adjusts.size());
6142 for (const DeterministicSpriteGroupAdjust &adjust : current_adjusts) {
6143 group->adjusts.push_back(adjust);
6144 OptimiseVarAction2Adjust(va2_opt_state, info, group, group->adjusts.back());
6147 std::vector<DeterministicSpriteGroupRange> ranges;
6148 ranges.resize(buf.ReadByte());
6149 for (auto &range : ranges) {
6150 range.group = GetGroupFromGroupID(setid, type, buf.ReadWord());
6151 range.low = buf.ReadVarSize(varsize);
6152 range.high = buf.ReadVarSize(varsize);
6155 group->default_group = GetGroupFromGroupID(setid, type, buf.ReadWord());
6157 if (unlikely(shadow != nullptr)) {
6158 shadow->calculated_result = ranges.size() == 0;
6159 ProcessDeterministicSpriteGroupRanges(ranges, shadow->ranges, group->default_group);
6160 shadow->default_group = group->default_group;
6162 /* Pruning was turned off so that the unpruned targets could be saved in the shadow ranges, prune now */
6163 for (DeterministicSpriteGroupRange &range : ranges) {
6164 range.group = PruneTargetSpriteGroup(range.group);
6166 group->default_group = PruneTargetSpriteGroup(group->default_group);
6169 group->error_group = ranges.empty() ? group->default_group : ranges[0].group;
6170 /* nvar == 0 is a special case -- we turn our value into a callback result */
6171 if (ranges.empty()) group->dsg_flags |= DSGF_CALCULATED_RESULT;
6173 ProcessDeterministicSpriteGroupRanges(ranges, group->ranges, group->default_group);
6175 OptimiseVarAction2DeterministicSpriteGroup(va2_opt_state, info, group, current_adjusts);
6176 current_adjusts.clear();
6177 break;
6180 /* Randomized Sprite Group */
6181 case STYPE_RANDOMIZED:
6183 assert(RandomizedSpriteGroup::CanAllocateItem());
6184 RandomizedSpriteGroup *group = new RandomizedSpriteGroup();
6185 group->nfo_line = _cur.nfo_line;
6186 if (_action6_override_active) group->sg_flags |= SGF_ACTION6;
6187 act_group = group;
6188 group->var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
6190 if (HasBit(type, 2)) {
6191 if (feature <= GSF_AIRCRAFT) group->var_scope = VSG_SCOPE_RELATIVE;
6192 group->var_scope_count = ParseRelativeScopeByte(buf.ReadByte());
6195 uint8_t triggers = buf.ReadByte();
6196 group->triggers = GB(triggers, 0, 7);
6197 group->cmp_mode = HasBit(triggers, 7) ? RSG_CMP_ALL : RSG_CMP_ANY;
6198 group->lowest_randbit = buf.ReadByte();
6200 uint8_t num_groups = buf.ReadByte();
6201 if (!HasExactlyOneBit(num_groups)) {
6202 GrfMsg(1, "NewSpriteGroup: Random Action 2 nrand should be power of 2");
6205 group->groups.reserve(num_groups);
6206 for (uint i = 0; i < num_groups; i++) {
6207 group->groups.push_back(GetGroupFromGroupID(setid, type, buf.ReadWord()));
6210 if (unlikely(HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) {
6211 RandomizedSpriteGroupShadowCopy *shadow = &(_randomized_sg_shadows[group]);
6212 shadow->groups = group->groups;
6214 /* Pruning was turned off so that the unpruned targets could be saved in the shadow groups, prune now */
6215 for (const SpriteGroup *&group : group->groups) {
6216 group = PruneTargetSpriteGroup(group);
6220 break;
6223 case STYPE_CB_FAILURE:
6224 act_group = NewCallbackResultSpriteGroupNoTransform(CALLBACK_FAILED);
6225 break;
6227 /* Neither a variable or randomized sprite group... must be a real group */
6228 case STYPE_NORMAL:
6230 switch (feature) {
6231 case GSF_TRAINS:
6232 case GSF_ROADVEHICLES:
6233 case GSF_SHIPS:
6234 case GSF_AIRCRAFT:
6235 case GSF_STATIONS:
6236 case GSF_CANALS:
6237 case GSF_CARGOES:
6238 case GSF_AIRPORTS:
6239 case GSF_RAILTYPES:
6240 case GSF_ROADTYPES:
6241 case GSF_TRAMTYPES:
6242 case GSF_SIGNALS:
6243 case GSF_NEWLANDSCAPE:
6245 uint8_t num_loaded = type;
6246 uint8_t num_loading = buf.ReadByte();
6248 if (!_cur.HasValidSpriteSets(feature)) {
6249 GrfMsg(0, "NewSpriteGroup: No sprite set to work on! Skipping");
6250 return;
6253 if (num_loaded + num_loading == 0) {
6254 GrfMsg(1, "NewSpriteGroup: no result, skipping invalid RealSpriteGroup");
6255 break;
6258 GrfMsg(6, "NewSpriteGroup: New SpriteGroup 0x{:02X}, {} loaded, {} loading",
6259 setid, num_loaded, num_loading);
6261 if (num_loaded + num_loading == 0) {
6262 GrfMsg(1, "NewSpriteGroup: no result, skipping invalid RealSpriteGroup");
6263 break;
6266 if (num_loaded + num_loading == 1) {
6267 /* Avoid creating 'Real' sprite group if only one option. */
6268 uint16_t spriteid = buf.ReadWord();
6269 act_group = CreateGroupFromGroupID(feature, setid, type, spriteid);
6270 GrfMsg(8, "NewSpriteGroup: one result, skipping RealSpriteGroup = subset {}", spriteid);
6271 break;
6274 std::vector<uint16_t> loaded;
6275 std::vector<uint16_t> loading;
6277 loaded.reserve(num_loaded);
6278 for (uint i = 0; i < num_loaded; i++) {
6279 loaded.push_back(buf.ReadWord());
6280 GrfMsg(8, "NewSpriteGroup: + rg->loaded[{}] = subset {}", i, loaded[i]);
6283 loading.reserve(num_loading);
6284 for (uint i = 0; i < num_loading; i++) {
6285 loading.push_back(buf.ReadWord());
6286 GrfMsg(8, "NewSpriteGroup: + rg->loading[{}] = subset {}", i, loading[i]);
6289 bool loaded_same = !loaded.empty() && std::adjacent_find(loaded.begin(), loaded.end(), std::not_equal_to<>()) == loaded.end();
6290 bool loading_same = !loading.empty() && std::adjacent_find(loading.begin(), loading.end(), std::not_equal_to<>()) == loading.end();
6291 if (loaded_same && loading_same && loaded[0] == loading[0]) {
6292 /* Both lists only contain the same value, so don't create 'Real' sprite group */
6293 act_group = CreateGroupFromGroupID(feature, setid, type, loaded[0]);
6294 GrfMsg(8, "NewSpriteGroup: same result, skipping RealSpriteGroup = subset {}", loaded[0]);
6295 break;
6298 assert(RealSpriteGroup::CanAllocateItem());
6299 RealSpriteGroup *group = new RealSpriteGroup();
6300 group->nfo_line = _cur.nfo_line;
6301 if (_action6_override_active) group->sg_flags |= SGF_ACTION6;
6302 act_group = group;
6304 if (loaded_same && loaded.size() > 1) loaded.resize(1);
6305 group->loaded.reserve(loaded.size());
6306 for (uint16_t spriteid : loaded) {
6307 const SpriteGroup *t = CreateGroupFromGroupID(feature, setid, type, spriteid);
6308 group->loaded.push_back(t);
6311 if (loading_same && loading.size() > 1) loading.resize(1);
6312 group->loading.reserve(loading.size());
6313 for (uint16_t spriteid : loading) {
6314 const SpriteGroup *t = CreateGroupFromGroupID(feature, setid, type, spriteid);
6315 group->loading.push_back(t);
6318 break;
6321 case GSF_HOUSES:
6322 case GSF_AIRPORTTILES:
6323 case GSF_OBJECTS:
6324 case GSF_INDUSTRYTILES:
6325 case GSF_ROADSTOPS: {
6326 uint8_t num_building_sprites = std::max((uint8_t)1, type);
6328 assert(TileLayoutSpriteGroup::CanAllocateItem());
6329 TileLayoutSpriteGroup *group = new TileLayoutSpriteGroup();
6330 group->nfo_line = _cur.nfo_line;
6331 if (_action6_override_active) group->sg_flags |= SGF_ACTION6;
6332 act_group = group;
6334 /* On error, bail out immediately. Temporary GRF data was already freed */
6335 if (ReadSpriteLayout(buf, num_building_sprites, true, feature, false, type == 0, &group->dts)) return;
6336 break;
6339 case GSF_INDUSTRIES: {
6340 if (type > 2) {
6341 GrfMsg(1, "NewSpriteGroup: Unsupported industry production version {}, skipping", type);
6342 break;
6345 assert(IndustryProductionSpriteGroup::CanAllocateItem());
6346 IndustryProductionSpriteGroup *group = new IndustryProductionSpriteGroup();
6347 group->nfo_line = _cur.nfo_line;
6348 if (_action6_override_active) group->sg_flags |= SGF_ACTION6;
6349 act_group = group;
6350 group->version = type;
6351 if (type == 0) {
6352 group->num_input = INDUSTRY_ORIGINAL_NUM_INPUTS;
6353 for (uint i = 0; i < INDUSTRY_ORIGINAL_NUM_INPUTS; i++) {
6354 group->subtract_input[i] = (int16_t)buf.ReadWord(); // signed
6356 group->num_output = INDUSTRY_ORIGINAL_NUM_OUTPUTS;
6357 for (uint i = 0; i < INDUSTRY_ORIGINAL_NUM_OUTPUTS; i++) {
6358 group->add_output[i] = buf.ReadWord(); // unsigned
6360 group->again = buf.ReadByte();
6361 } else if (type == 1) {
6362 group->num_input = INDUSTRY_ORIGINAL_NUM_INPUTS;
6363 for (uint i = 0; i < INDUSTRY_ORIGINAL_NUM_INPUTS; i++) {
6364 group->subtract_input[i] = buf.ReadByte();
6366 group->num_output = INDUSTRY_ORIGINAL_NUM_OUTPUTS;
6367 for (uint i = 0; i < INDUSTRY_ORIGINAL_NUM_OUTPUTS; i++) {
6368 group->add_output[i] = buf.ReadByte();
6370 group->again = buf.ReadByte();
6371 } else if (type == 2) {
6372 group->num_input = buf.ReadByte();
6373 if (group->num_input > lengthof(group->subtract_input)) {
6374 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
6375 error->data = "too many inputs (max 16)";
6376 return;
6378 for (uint i = 0; i < group->num_input; i++) {
6379 uint8_t rawcargo = buf.ReadByte();
6380 CargoID cargo = GetCargoTranslation(rawcargo, _cur.grffile);
6381 if (!IsValidCargoID(cargo)) {
6382 /* The mapped cargo is invalid. This is permitted at this point,
6383 * as long as the result is not used. Mark it invalid so this
6384 * can be tested later. */
6385 group->version = 0xFF;
6386 } else if (std::find(group->cargo_input, group->cargo_input + i, cargo) != group->cargo_input + i) {
6387 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
6388 error->data = "duplicate input cargo";
6389 return;
6391 group->cargo_input[i] = cargo;
6392 group->subtract_input[i] = buf.ReadByte();
6394 group->num_output = buf.ReadByte();
6395 if (group->num_output > lengthof(group->add_output)) {
6396 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
6397 error->data = "too many outputs (max 16)";
6398 return;
6400 for (uint i = 0; i < group->num_output; i++) {
6401 uint8_t rawcargo = buf.ReadByte();
6402 CargoID cargo = GetCargoTranslation(rawcargo, _cur.grffile);
6403 if (!IsValidCargoID(cargo)) {
6404 /* Mark this result as invalid to use */
6405 group->version = 0xFF;
6406 } else if (std::find(group->cargo_output, group->cargo_output + i, cargo) != group->cargo_output + i) {
6407 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
6408 error->data = "duplicate output cargo";
6409 return;
6411 group->cargo_output[i] = cargo;
6412 group->add_output[i] = buf.ReadByte();
6414 group->again = buf.ReadByte();
6415 } else {
6416 NOT_REACHED();
6418 break;
6421 case GSF_FAKE_TOWNS:
6422 act_group = NewCallbackResultSpriteGroupNoTransform(CALLBACK_FAILED);
6423 break;
6425 /* Loading of Tile Layout and Production Callback groups would happen here */
6426 default: GrfMsg(1, "NewSpriteGroup: Unsupported feature {}, skipping", GetFeatureString(feature));
6431 if ((size_t)setid >= _cur.spritegroups.size()) _cur.spritegroups.resize(setid + 1);
6432 _cur.spritegroups[setid] = act_group;
6436 * Get the cargo translation table to use for the given GRF file.
6437 * @param grffile GRF file.
6438 * @returns Readonly cargo translation table to use.
6440 std::span<const CargoLabel> GetCargoTranslationTable(const GRFFile &grffile)
6442 /* Always use the translation table if it's installed. */
6443 if (!grffile.cargo_list.empty()) return grffile.cargo_list;
6445 /* Pre-v7 use climate-dependent "slot" table. */
6446 if (grffile.grf_version < 7) return GetClimateDependentCargoTranslationTable();
6448 /* Otherwise use climate-independent "bitnum" table. */
6449 return GetClimateIndependentCargoTranslationTable();
6452 static CargoID TranslateCargo(uint8_t feature, uint8_t ctype)
6454 /* Special cargo types for purchase list and stations */
6455 if ((feature == GSF_STATIONS || feature == GSF_ROADSTOPS) && ctype == 0xFE) return SpriteGroupCargo::SG_DEFAULT_NA;
6456 if (ctype == 0xFF) return SpriteGroupCargo::SG_PURCHASE;
6458 auto cargo_list = GetCargoTranslationTable(*_cur.grffile);
6460 /* Check if the cargo type is out of bounds of the cargo translation table */
6461 if (ctype >= cargo_list.size()) {
6462 GrfMsg(1, "TranslateCargo: Cargo type {} out of range (max {}), skipping.", ctype, (unsigned int)_cur.grffile->cargo_list.size() - 1);
6463 return INVALID_CARGO;
6466 /* Look up the cargo label from the translation table */
6467 CargoLabel cl = cargo_list[ctype];
6468 if (cl == CT_INVALID) {
6469 GrfMsg(5, "TranslateCargo: Cargo type {} not available in this climate, skipping.", ctype);
6470 return INVALID_CARGO;
6473 CargoID cid = GetCargoIDByLabel(cl);
6474 if (!IsValidCargoID(cid)) {
6475 GrfMsg(5, "TranslateCargo: Cargo '{:c}{:c}{:c}{:c}' unsupported, skipping.", GB(cl.base(), 24, 8), GB(cl.base(), 16, 8), GB(cl.base(), 8, 8), GB(cl.base(), 0, 8));
6476 return INVALID_CARGO;
6479 GrfMsg(6, "TranslateCargo: Cargo '{:c}{:c}{:c}{:c}' mapped to cargo type {}.", GB(cl.base(), 24, 8), GB(cl.base(), 16, 8), GB(cl.base(), 8, 8), GB(cl.base(), 0, 8), cid);
6480 return cid;
6484 static bool IsValidGroupID(uint16_t groupid, const char *function)
6486 if ((size_t)groupid >= _cur.spritegroups.size() || _cur.spritegroups[groupid] == nullptr) {
6487 GrfMsg(1, "{}: Spritegroup 0x{:04X} out of range or empty, skipping.", function, groupid);
6488 return false;
6491 return true;
6494 static void VehicleMapSpriteGroup(ByteReader &buf, uint8_t feature, uint8_t idcount)
6496 static std::vector<EngineID> last_engines; // Engine IDs are remembered in case the next action is a wagon override.
6497 bool wagover = false;
6499 /* Test for 'wagon override' flag */
6500 if (HasBit(idcount, 7)) {
6501 wagover = true;
6502 /* Strip off the flag */
6503 idcount = GB(idcount, 0, 7);
6505 if (last_engines.empty()) {
6506 GrfMsg(0, "VehicleMapSpriteGroup: WagonOverride: No engine to do override with");
6507 return;
6510 GrfMsg(6, "VehicleMapSpriteGroup: WagonOverride: {} engines, {} wagons", last_engines.size(), idcount);
6511 } else {
6512 last_engines.resize(idcount);
6515 TempBufferST<EngineID> engines(idcount);
6516 for (uint i = 0; i < idcount; i++) {
6517 Engine *e = GetNewEngine(_cur.grffile, (VehicleType)feature, buf.ReadExtendedByte());
6518 if (e == nullptr) {
6519 /* No engine could be allocated?!? Deal with it. Okay,
6520 * this might look bad. Also make sure this NewGRF
6521 * gets disabled, as a half loaded one is bad. */
6522 HandleChangeInfoResult("VehicleMapSpriteGroup", CIR_INVALID_ID, (GrfSpecFeature)0, 0);
6523 return;
6526 engines[i] = e->index;
6527 if (!wagover) last_engines[i] = engines[i];
6530 uint8_t cidcount = buf.ReadByte();
6531 for (uint c = 0; c < cidcount; c++) {
6532 uint8_t ctype = buf.ReadByte();
6533 uint16_t groupid = buf.ReadWord();
6534 if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) continue;
6536 GrfMsg(8, "VehicleMapSpriteGroup: * [{}] Cargo type 0x{:X}, group id 0x{:02X}", c, ctype, groupid);
6538 CargoID cid = TranslateCargo(feature, ctype);
6539 if (!IsValidCargoID(cid)) continue;
6541 for (uint i = 0; i < idcount; i++) {
6542 EngineID engine = engines[i];
6544 GrfMsg(7, "VehicleMapSpriteGroup: [{}] Engine {}...", i, engine);
6546 if (wagover) {
6547 SetWagonOverrideSprites(engine, cid, GetGroupByID(groupid), last_engines);
6548 } else {
6549 SetCustomEngineSprites(engine, cid, GetGroupByID(groupid));
6554 uint16_t groupid = buf.ReadWord();
6555 if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) return;
6557 GrfMsg(8, "-- Default group id 0x{:04X}", groupid);
6559 for (uint i = 0; i < idcount; i++) {
6560 EngineID engine = engines[i];
6562 if (wagover) {
6563 SetWagonOverrideSprites(engine, SpriteGroupCargo::SG_DEFAULT, GetGroupByID(groupid), last_engines);
6564 } else {
6565 SetCustomEngineSprites(engine, SpriteGroupCargo::SG_DEFAULT, GetGroupByID(groupid));
6566 SetEngineGRF(engine, _cur.grffile);
6572 static void CanalMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6574 TempBufferST<uint16_t> cfs(idcount);
6575 for (uint i = 0; i < idcount; i++) {
6576 cfs[i] = buf.ReadExtendedByte();
6579 uint8_t cidcount = buf.ReadByte();
6580 buf.Skip(cidcount * 3);
6582 uint16_t groupid = buf.ReadWord();
6583 if (!IsValidGroupID(groupid, "CanalMapSpriteGroup")) return;
6585 for (uint i = 0; i < idcount; i++) {
6586 uint16_t cf = cfs[i];
6588 if (cf >= CF_END) {
6589 GrfMsg(1, "CanalMapSpriteGroup: Canal subset {} out of range, skipping", cf);
6590 continue;
6593 _water_feature[cf].grffile = _cur.grffile;
6594 _water_feature[cf].group = GetGroupByID(groupid);
6599 static void StationMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6601 if (_cur.grffile->stations.empty()) {
6602 GrfMsg(1, "StationMapSpriteGroup: No stations defined, skipping");
6603 return;
6606 TempBufferST<uint16_t> stations(idcount);
6607 for (uint i = 0; i < idcount; i++) {
6608 stations[i] = buf.ReadExtendedByte();
6611 uint8_t cidcount = buf.ReadByte();
6612 for (uint c = 0; c < cidcount; c++) {
6613 uint8_t ctype = buf.ReadByte();
6614 uint16_t groupid = buf.ReadWord();
6615 if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) continue;
6617 ctype = TranslateCargo(GSF_STATIONS, ctype);
6618 if (ctype == INVALID_CARGO) continue;
6620 for (uint i = 0; i < idcount; i++) {
6621 StationSpec *statspec = stations[i] >= _cur.grffile->stations.size() ? nullptr : _cur.grffile->stations[stations[i]].get();
6623 if (statspec == nullptr) {
6624 GrfMsg(1, "StationMapSpriteGroup: Station with ID 0x{:X} undefined, skipping", stations[i]);
6625 continue;
6628 statspec->grf_prop.spritegroup[ctype] = GetGroupByID(groupid);
6632 uint16_t groupid = buf.ReadWord();
6633 if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) return;
6635 for (uint i = 0; i < idcount; i++) {
6636 StationSpec *statspec = stations[i] >= _cur.grffile->stations.size() ? nullptr : _cur.grffile->stations[stations[i]].get();
6638 if (statspec == nullptr) {
6639 GrfMsg(1, "StationMapSpriteGroup: Station with ID 0x{:X} undefined, skipping", stations[i]);
6640 continue;
6643 if (statspec->grf_prop.HasGrfFile()) {
6644 GrfMsg(1, "StationMapSpriteGroup: Station with ID 0x{:X} mapped multiple times, skipping", stations[i]);
6645 continue;
6648 statspec->grf_prop.spritegroup[SpriteGroupCargo::SG_DEFAULT] = GetGroupByID(groupid);
6649 statspec->grf_prop.grfid = _cur.grffile->grfid;
6650 statspec->grf_prop.grffile = _cur.grffile;
6651 statspec->grf_prop.local_id = stations[i];
6652 StationClass::Assign(statspec);
6657 static void TownHouseMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6659 if (_cur.grffile->housespec.empty()) {
6660 GrfMsg(1, "TownHouseMapSpriteGroup: No houses defined, skipping");
6661 return;
6664 TempBufferST<uint16_t> houses(idcount);
6665 for (uint i = 0; i < idcount; i++) {
6666 houses[i] = buf.ReadExtendedByte();
6669 /* Skip the cargo type section, we only care about the default group */
6670 uint8_t cidcount = buf.ReadByte();
6671 buf.Skip(cidcount * 3);
6673 uint16_t groupid = buf.ReadWord();
6674 if (!IsValidGroupID(groupid, "TownHouseMapSpriteGroup")) return;
6676 for (uint i = 0; i < idcount; i++) {
6677 HouseSpec *hs = houses[i] >= _cur.grffile->housespec.size() ? nullptr : _cur.grffile->housespec[houses[i]].get();
6679 if (hs == nullptr) {
6680 GrfMsg(1, "TownHouseMapSpriteGroup: House {} undefined, skipping.", houses[i]);
6681 continue;
6684 hs->grf_prop.spritegroup[0] = GetGroupByID(groupid);
6688 static void IndustryMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6690 if (_cur.grffile->industryspec.empty()) {
6691 GrfMsg(1, "IndustryMapSpriteGroup: No industries defined, skipping");
6692 return;
6695 TempBufferST<uint16_t> industries(idcount);
6696 for (uint i = 0; i < idcount; i++) {
6697 industries[i] = buf.ReadExtendedByte();
6700 /* Skip the cargo type section, we only care about the default group */
6701 uint8_t cidcount = buf.ReadByte();
6702 buf.Skip(cidcount * 3);
6704 uint16_t groupid = buf.ReadWord();
6705 if (!IsValidGroupID(groupid, "IndustryMapSpriteGroup")) return;
6707 for (uint i = 0; i < idcount; i++) {
6708 IndustrySpec *indsp = industries[i] >= _cur.grffile->industryspec.size() ? nullptr : _cur.grffile->industryspec[industries[i]].get();
6710 if (indsp == nullptr) {
6711 GrfMsg(1, "IndustryMapSpriteGroup: Industry {} undefined, skipping", industries[i]);
6712 continue;
6715 indsp->grf_prop.spritegroup[0] = GetGroupByID(groupid);
6719 static void IndustrytileMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6721 if (_cur.grffile->indtspec.empty()) {
6722 GrfMsg(1, "IndustrytileMapSpriteGroup: No industry tiles defined, skipping");
6723 return;
6726 TempBufferST<uint16_t> indtiles(idcount);
6727 for (uint i = 0; i < idcount; i++) {
6728 indtiles[i] = buf.ReadExtendedByte();
6731 /* Skip the cargo type section, we only care about the default group */
6732 uint8_t cidcount = buf.ReadByte();
6733 buf.Skip(cidcount * 3);
6735 uint16_t groupid = buf.ReadWord();
6736 if (!IsValidGroupID(groupid, "IndustrytileMapSpriteGroup")) return;
6738 for (uint i = 0; i < idcount; i++) {
6739 IndustryTileSpec *indtsp = indtiles[i] >= _cur.grffile->indtspec.size() ? nullptr : _cur.grffile->indtspec[indtiles[i]].get();
6741 if (indtsp == nullptr) {
6742 GrfMsg(1, "IndustrytileMapSpriteGroup: Industry tile {} undefined, skipping", indtiles[i]);
6743 continue;
6746 indtsp->grf_prop.spritegroup[0] = GetGroupByID(groupid);
6750 static void CargoMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6752 TempBufferST<uint16_t> cargoes(idcount);
6753 for (uint i = 0; i < idcount; i++) {
6754 cargoes[i] = buf.ReadExtendedByte();
6757 /* Skip the cargo type section, we only care about the default group */
6758 uint8_t cidcount = buf.ReadByte();
6759 buf.Skip(cidcount * 3);
6761 uint16_t groupid = buf.ReadWord();
6762 if (!IsValidGroupID(groupid, "CargoMapSpriteGroup")) return;
6764 for (uint i = 0; i < idcount; i++) {
6765 uint16_t cid = cargoes[i];
6767 if (cid >= NUM_CARGO) {
6768 GrfMsg(1, "CargoMapSpriteGroup: Cargo ID {} out of range, skipping", cid);
6769 continue;
6772 CargoSpec *cs = CargoSpec::Get(cid);
6773 cs->grffile = _cur.grffile;
6774 cs->group = GetGroupByID(groupid);
6778 static void SignalsMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6780 TempBufferST<uint16_t> ids(idcount);
6781 for (uint i = 0; i < idcount; i++) {
6782 ids[i] = buf.ReadExtendedByte();
6785 /* Skip the cargo type section, we only care about the default group */
6786 uint8_t cidcount = buf.ReadByte();
6787 buf.Skip(cidcount * 3);
6789 uint16_t groupid = buf.ReadWord();
6790 if (!IsValidGroupID(groupid, "SignalsMapSpriteGroup")) return;
6792 for (uint i = 0; i < idcount; i++) {
6793 uint16_t id = ids[i];
6795 switch (id) {
6796 case NSA3ID_CUSTOM_SIGNALS:
6797 _cur.grffile->new_signals_group = GetGroupByID(groupid);
6798 if (!HasBit(_cur.grffile->new_signal_ctrl_flags, NSCF_GROUPSET)) {
6799 SetBit(_cur.grffile->new_signal_ctrl_flags, NSCF_GROUPSET);
6800 _new_signals_grfs.push_back(_cur.grffile);
6802 break;
6804 default:
6805 GrfMsg(1, "SignalsMapSpriteGroup: ID not implemented: {}", id);
6806 break;
6811 static void ObjectMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6813 if (_cur.grffile->objectspec.empty()) {
6814 GrfMsg(1, "ObjectMapSpriteGroup: No object tiles defined, skipping");
6815 return;
6818 TempBufferST<uint16_t> objects(idcount);
6819 for (uint i = 0; i < idcount; i++) {
6820 objects[i] = buf.ReadExtendedByte();
6823 uint8_t cidcount = buf.ReadByte();
6824 for (uint c = 0; c < cidcount; c++) {
6825 uint8_t ctype = buf.ReadByte();
6826 uint16_t groupid = buf.ReadWord();
6827 if (!IsValidGroupID(groupid, "ObjectMapSpriteGroup")) continue;
6829 /* The only valid option here is purchase list sprite groups. */
6830 if (ctype != 0xFF) {
6831 GrfMsg(1, "ObjectMapSpriteGroup: Invalid cargo bitnum {} for objects, skipping.", ctype);
6832 continue;
6835 for (uint i = 0; i < idcount; i++) {
6836 ObjectSpec *spec = (objects[i] >= _cur.grffile->objectspec.size()) ? nullptr : _cur.grffile->objectspec[objects[i]].get();
6838 if (spec == nullptr) {
6839 GrfMsg(1, "ObjectMapSpriteGroup: Object with ID 0x{:X} undefined, skipping", objects[i]);
6840 continue;
6843 spec->grf_prop.spritegroup[OBJECT_SPRITE_GROUP_PURCHASE] = GetGroupByID(groupid);
6847 uint16_t groupid = buf.ReadWord();
6848 if (!IsValidGroupID(groupid, "ObjectMapSpriteGroup")) return;
6850 for (uint i = 0; i < idcount; i++) {
6851 ObjectSpec *spec = (objects[i] >= _cur.grffile->objectspec.size()) ? nullptr : _cur.grffile->objectspec[objects[i]].get();
6853 if (spec == nullptr) {
6854 GrfMsg(1, "ObjectMapSpriteGroup: Object with ID 0x{:X} undefined, skipping", objects[i]);
6855 continue;
6858 if (spec->grf_prop.HasGrfFile()) {
6859 GrfMsg(1, "ObjectMapSpriteGroup: Object with ID 0x{:X} mapped multiple times, skipping", objects[i]);
6860 continue;
6863 spec->grf_prop.spritegroup[OBJECT_SPRITE_GROUP_DEFAULT] = GetGroupByID(groupid);
6864 spec->grf_prop.grfid = _cur.grffile->grfid;
6865 spec->grf_prop.grffile = _cur.grffile;
6866 spec->grf_prop.local_id = objects[i];
6870 static void RailTypeMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6872 TempBufferST<uint8_t> railtypes(idcount);
6873 for (uint i = 0; i < idcount; i++) {
6874 uint16_t id = buf.ReadExtendedByte();
6875 railtypes[i] = id < RAILTYPE_END ? _cur.grffile->railtype_map[id] : INVALID_RAILTYPE;
6878 uint8_t cidcount = buf.ReadByte();
6879 for (uint c = 0; c < cidcount; c++) {
6880 uint8_t ctype = buf.ReadByte();
6881 uint16_t groupid = buf.ReadWord();
6882 if (!IsValidGroupID(groupid, "RailTypeMapSpriteGroup")) continue;
6884 if (ctype >= RTSG_END) continue;
6886 extern RailTypeInfo _railtypes[RAILTYPE_END];
6887 for (uint i = 0; i < idcount; i++) {
6888 if (railtypes[i] != INVALID_RAILTYPE) {
6889 RailTypeInfo *rti = &_railtypes[railtypes[i]];
6891 rti->grffile[ctype] = _cur.grffile;
6892 rti->group[ctype] = GetGroupByID(groupid);
6897 /* Railtypes do not use the default group. */
6898 buf.ReadWord();
6901 static void RoadTypeMapSpriteGroup(ByteReader &buf, uint8_t idcount, RoadTramType rtt)
6903 std::array<RoadType, ROADTYPE_END> &type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map;
6905 TempBufferST<uint8_t> roadtypes(idcount);
6906 for (uint i = 0; i < idcount; i++) {
6907 uint16_t id = buf.ReadExtendedByte();
6908 roadtypes[i] = id < ROADTYPE_END ? type_map[id] : INVALID_ROADTYPE;
6911 uint8_t cidcount = buf.ReadByte();
6912 for (uint c = 0; c < cidcount; c++) {
6913 uint8_t ctype = buf.ReadByte();
6914 uint16_t groupid = buf.ReadWord();
6915 if (!IsValidGroupID(groupid, "RoadTypeMapSpriteGroup")) continue;
6917 if (ctype >= ROTSG_END) continue;
6919 extern RoadTypeInfo _roadtypes[ROADTYPE_END];
6920 for (uint i = 0; i < idcount; i++) {
6921 if (roadtypes[i] != INVALID_ROADTYPE) {
6922 RoadTypeInfo *rti = &_roadtypes[roadtypes[i]];
6924 rti->grffile[ctype] = _cur.grffile;
6925 rti->group[ctype] = GetGroupByID(groupid);
6930 /* Roadtypes do not use the default group. */
6931 buf.ReadWord();
6934 static void AirportMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6936 if (_cur.grffile->airportspec.empty()) {
6937 GrfMsg(1, "AirportMapSpriteGroup: No airports defined, skipping");
6938 return;
6941 TempBufferST<uint16_t> airports(idcount);
6942 for (uint i = 0; i < idcount; i++) {
6943 airports[i] = buf.ReadExtendedByte();
6946 /* Skip the cargo type section, we only care about the default group */
6947 uint8_t cidcount = buf.ReadByte();
6948 buf.Skip(cidcount * 3);
6950 uint16_t groupid = buf.ReadWord();
6951 if (!IsValidGroupID(groupid, "AirportMapSpriteGroup")) return;
6953 for (uint i = 0; i < idcount; i++) {
6954 AirportSpec *as = airports[i] >= _cur.grffile->airportspec.size() ? nullptr : _cur.grffile->airportspec[airports[i]].get();
6956 if (as == nullptr) {
6957 GrfMsg(1, "AirportMapSpriteGroup: Airport {} undefined, skipping", airports[i]);
6958 continue;
6961 as->grf_prop.spritegroup[0] = GetGroupByID(groupid);
6965 static void AirportTileMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6967 if (_cur.grffile->airtspec.empty()) {
6968 GrfMsg(1, "AirportTileMapSpriteGroup: No airport tiles defined, skipping");
6969 return;
6972 TempBufferST<uint16_t> airptiles(idcount);
6973 for (uint i = 0; i < idcount; i++) {
6974 airptiles[i] = buf.ReadExtendedByte();
6977 /* Skip the cargo type section, we only care about the default group */
6978 uint8_t cidcount = buf.ReadByte();
6979 buf.Skip(cidcount * 3);
6981 uint16_t groupid = buf.ReadWord();
6982 if (!IsValidGroupID(groupid, "AirportTileMapSpriteGroup")) return;
6984 for (uint i = 0; i < idcount; i++) {
6985 AirportTileSpec *airtsp = airptiles[i] >= _cur.grffile->airtspec.size() ? nullptr : _cur.grffile->airtspec[airptiles[i]].get();
6987 if (airtsp == nullptr) {
6988 GrfMsg(1, "AirportTileMapSpriteGroup: Airport tile {} undefined, skipping", airptiles[i]);
6989 continue;
6992 airtsp->grf_prop.spritegroup[0] = GetGroupByID(groupid);
6996 static void RoadStopMapSpriteGroup(ByteReader &buf, uint8_t idcount)
6998 TempBufferST<uint16_t> roadstops(idcount);
6999 for (uint i = 0; i < idcount; i++) {
7000 roadstops[i] = buf.ReadExtendedByte();
7003 uint8_t cidcount = buf.ReadByte();
7004 for (uint c = 0; c < cidcount; c++) {
7005 uint8_t ctype = buf.ReadByte();
7006 uint16_t groupid = buf.ReadWord();
7007 if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) continue;
7009 ctype = TranslateCargo(GSF_ROADSTOPS, ctype);
7010 if (ctype == INVALID_CARGO) continue;
7012 for (uint i = 0; i < idcount; i++) {
7013 RoadStopSpec *roadstopspec = (roadstops[i] >= _cur.grffile->roadstops.size()) ? nullptr : _cur.grffile->roadstops[roadstops[i]].get();
7015 if (roadstopspec == nullptr) {
7016 GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x{:X} does not exist, skipping", roadstops[i]);
7017 continue;
7020 roadstopspec->grf_prop.spritegroup[ctype] = GetGroupByID(groupid);
7024 uint16_t groupid = buf.ReadWord();
7025 if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) return;
7027 if (_cur.grffile->roadstops.empty()) {
7028 GrfMsg(0, "RoadStopMapSpriteGroup: No roadstops defined, skipping.");
7029 return;
7032 for (uint i = 0; i < idcount; i++) {
7033 RoadStopSpec *roadstopspec = (roadstops[i] >= _cur.grffile->roadstops.size()) ? nullptr : _cur.grffile->roadstops[roadstops[i]].get();
7035 if (roadstopspec == nullptr) {
7036 GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x{:X} does not exist, skipping.", roadstops[i]);
7037 continue;
7040 if (roadstopspec->grf_prop.HasGrfFile()) {
7041 GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x{:X} mapped multiple times, skipping", roadstops[i]);
7042 continue;
7045 roadstopspec->grf_prop.spritegroup[SpriteGroupCargo::SG_DEFAULT] = GetGroupByID(groupid);
7046 roadstopspec->grf_prop.grfid = _cur.grffile->grfid;
7047 roadstopspec->grf_prop.grffile = _cur.grffile;
7048 roadstopspec->grf_prop.local_id = roadstops[i];
7049 RoadStopClass::Assign(roadstopspec);
7053 static void NewLandscapeMapSpriteGroup(ByteReader &buf, uint8_t idcount)
7055 TempBufferST<uint16_t> ids(idcount);
7056 for (uint i = 0; i < idcount; i++) {
7057 ids[i] = buf.ReadExtendedByte();
7060 /* Skip the cargo type section, we only care about the default group */
7061 uint8_t cidcount = buf.ReadByte();
7062 buf.Skip(cidcount * 3);
7064 uint16_t groupid = buf.ReadWord();
7065 if (!IsValidGroupID(groupid, "NewLandscapeMapSpriteGroup")) return;
7067 for (uint i = 0; i < idcount; i++) {
7068 uint16_t id = ids[i];
7070 switch (id) {
7071 case NLA3ID_CUSTOM_ROCKS:
7072 _cur.grffile->new_rocks_group = GetGroupByID(groupid);
7073 if (!HasBit(_cur.grffile->new_landscape_ctrl_flags, NLCF_ROCKS_SET)) {
7074 SetBit(_cur.grffile->new_landscape_ctrl_flags, NLCF_ROCKS_SET);
7075 _new_landscape_rocks_grfs.push_back(_cur.grffile);
7077 break;
7079 default:
7080 GrfMsg(1, "NewLandscapeMapSpriteGroup: ID not implemented: {}", id);
7081 break;
7086 /* Action 0x03 */
7087 static void FeatureMapSpriteGroup(ByteReader &buf)
7089 /* <03> <feature> <n-id> <ids>... <num-cid> [<cargo-type> <cid>]... <def-cid>
7090 * id-list := [<id>] [id-list]
7091 * cargo-list := <cargo-type> <cid> [cargo-list]
7093 * B feature see action 0
7094 * B n-id bits 0-6: how many IDs this definition applies to
7095 * bit 7: if set, this is a wagon override definition (see below)
7096 * E ids the IDs for which this definition applies
7097 * B num-cid number of cargo IDs (sprite group IDs) in this definition
7098 * can be zero, in that case the def-cid is used always
7099 * B cargo-type type of this cargo type (e.g. mail=2, wood=7, see below)
7100 * W cid cargo ID (sprite group ID) for this type of cargo
7101 * W def-cid default cargo ID (sprite group ID) */
7103 GrfSpecFeatureRef feature_ref = ReadFeature(buf.ReadByte());
7104 GrfSpecFeature feature = feature_ref.id;
7105 uint8_t idcount = buf.ReadByte();
7107 if (feature >= GSF_END) {
7108 GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature {}, skipping", GetFeatureString(feature_ref));
7109 return;
7112 /* If idcount is zero, this is a feature callback */
7113 if (idcount == 0) {
7114 /* Skip number of cargo ids? */
7115 buf.ReadByte();
7116 uint16_t groupid = buf.ReadWord();
7117 if (!IsValidGroupID(groupid, "FeatureMapSpriteGroup")) return;
7119 GrfMsg(6, "FeatureMapSpriteGroup: Adding generic feature callback for feature {}", GetFeatureString(feature_ref));
7121 AddGenericCallback(feature, _cur.grffile, GetGroupByID(groupid));
7122 return;
7125 /* Mark the feature as used by the grf (generic callbacks do not count) */
7126 SetBit(_cur.grffile->grf_features, feature);
7128 GrfMsg(6, "FeatureMapSpriteGroup: Feature {}, {} ids", GetFeatureString(feature_ref), idcount);
7130 switch (feature) {
7131 case GSF_TRAINS:
7132 case GSF_ROADVEHICLES:
7133 case GSF_SHIPS:
7134 case GSF_AIRCRAFT:
7135 VehicleMapSpriteGroup(buf, feature, idcount);
7136 return;
7138 case GSF_CANALS:
7139 CanalMapSpriteGroup(buf, idcount);
7140 return;
7142 case GSF_STATIONS:
7143 StationMapSpriteGroup(buf, idcount);
7144 return;
7146 case GSF_HOUSES:
7147 TownHouseMapSpriteGroup(buf, idcount);
7148 return;
7150 case GSF_INDUSTRIES:
7151 IndustryMapSpriteGroup(buf, idcount);
7152 return;
7154 case GSF_INDUSTRYTILES:
7155 IndustrytileMapSpriteGroup(buf, idcount);
7156 return;
7158 case GSF_CARGOES:
7159 CargoMapSpriteGroup(buf, idcount);
7160 return;
7162 case GSF_AIRPORTS:
7163 AirportMapSpriteGroup(buf, idcount);
7164 return;
7166 case GSF_SIGNALS:
7167 SignalsMapSpriteGroup(buf, idcount);
7168 break;
7170 case GSF_OBJECTS:
7171 ObjectMapSpriteGroup(buf, idcount);
7172 break;
7174 case GSF_RAILTYPES:
7175 RailTypeMapSpriteGroup(buf, idcount);
7176 break;
7178 case GSF_ROADTYPES:
7179 RoadTypeMapSpriteGroup(buf, idcount, RTT_ROAD);
7180 break;
7182 case GSF_TRAMTYPES:
7183 RoadTypeMapSpriteGroup(buf, idcount, RTT_TRAM);
7184 break;
7186 case GSF_AIRPORTTILES:
7187 AirportTileMapSpriteGroup(buf, idcount);
7188 return;
7190 case GSF_ROADSTOPS:
7191 RoadStopMapSpriteGroup(buf, idcount);
7192 return;
7194 case GSF_NEWLANDSCAPE:
7195 NewLandscapeMapSpriteGroup(buf, idcount);
7196 return;
7198 default:
7199 GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature {}, skipping", GetFeatureString(feature_ref));
7200 return;
7204 /* Action 0x04 */
7205 static void FeatureNewName(ByteReader &buf)
7207 /* <04> <veh-type> <language-id> <num-veh> <offset> <data...>
7209 * B veh-type see action 0 (as 00..07, + 0A
7210 * But IF veh-type = 48, then generic text
7211 * B language-id If bit 6 is set, This is the extended language scheme,
7212 * with up to 64 language.
7213 * Otherwise, it is a mapping where set bits have meaning
7214 * 0 = american, 1 = english, 2 = german, 3 = french, 4 = spanish
7215 * Bit 7 set means this is a generic text, not a vehicle one (or else)
7216 * B num-veh number of vehicles which are getting a new name
7217 * B/W offset number of the first vehicle that gets a new name
7218 * Byte : ID of vehicle to change
7219 * Word : ID of string to change/add
7220 * S data new texts, each of them zero-terminated, after
7221 * which the next name begins. */
7223 bool new_scheme = _cur.grffile->grf_version >= 7;
7225 GrfSpecFeatureRef feature_ref = ReadFeature(buf.ReadByte(), true);
7226 GrfSpecFeature feature = feature_ref.id;
7227 if (feature >= GSF_END && feature != 0x48) {
7228 GrfMsg(1, "FeatureNewName: Unsupported feature {}, skipping", GetFeatureString(feature_ref));
7229 return;
7232 uint8_t lang = buf.ReadByte();
7233 uint8_t num = buf.ReadByte();
7234 bool generic = HasBit(lang, 7);
7235 uint16_t id;
7236 if (generic) {
7237 id = buf.ReadWord();
7238 } else if (feature <= GSF_AIRCRAFT) {
7239 id = buf.ReadExtendedByte();
7240 } else {
7241 id = buf.ReadByte();
7244 ClrBit(lang, 7);
7246 uint16_t endid = id + num;
7248 GrfMsg(6, "FeatureNewName: About to rename engines {}..{} (feature {}) in language 0x{:02X}",
7249 id, endid, GetFeatureString(feature), lang);
7251 for (; id < endid && buf.HasData(); id++) {
7252 const std::string_view name = buf.ReadString();
7253 GrfMsg(8, "FeatureNewName: 0x{:04X} <- {}", id, StrMakeValid(name));
7255 switch (feature) {
7256 case GSF_TRAINS:
7257 case GSF_ROADVEHICLES:
7258 case GSF_SHIPS:
7259 case GSF_AIRCRAFT:
7260 if (!generic) {
7261 Engine *e = GetNewEngine(_cur.grffile, (VehicleType)feature, id, HasBit(_cur.grfconfig->flags, GCF_STATIC));
7262 if (e == nullptr) break;
7263 StringID string = AddGRFString(_cur.grffile->grfid, e->index, lang, new_scheme, false, name, e->info.string_id);
7264 e->info.string_id = string;
7265 } else {
7266 AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, true, name, STR_UNDEFINED);
7268 break;
7270 default:
7271 if (IsInsideMM(id, 0xD000, 0xD400) || IsInsideMM(id, 0xD800, 0x10000)) {
7272 AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, true, name, STR_UNDEFINED);
7273 break;
7276 switch (GB(id, 8, 8)) {
7277 case 0xC4: // Station class name
7278 if (GB(id, 0, 8) >= _cur.grffile->stations.size() || _cur.grffile->stations[GB(id, 0, 8)] == nullptr) {
7279 GrfMsg(1, "FeatureNewName: Attempt to name undefined station 0x{:X}, ignoring", GB(id, 0, 8));
7280 } else {
7281 StationClassID class_index = _cur.grffile->stations[GB(id, 0, 8)]->class_index;
7282 StationClass::Get(class_index)->name = AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
7284 break;
7286 case 0xC5: // Station name
7287 if (GB(id, 0, 8) >= _cur.grffile->stations.size() || _cur.grffile->stations[GB(id, 0, 8)] == nullptr) {
7288 GrfMsg(1, "FeatureNewName: Attempt to name undefined station 0x{:X}, ignoring", GB(id, 0, 8));
7289 } else {
7290 _cur.grffile->stations[GB(id, 0, 8)]->name = AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
7292 break;
7294 case 0xC7: // Airporttile name
7295 if (GB(id, 0, 8) >= _cur.grffile->airtspec.size() || _cur.grffile->airtspec[GB(id, 0, 8)] == nullptr) {
7296 GrfMsg(1, "FeatureNewName: Attempt to name undefined airport tile 0x{:X}, ignoring", GB(id, 0, 8));
7297 } else {
7298 _cur.grffile->airtspec[GB(id, 0, 8)]->name = AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
7300 break;
7302 case 0xC9: // House name
7303 if (GB(id, 0, 8) >= _cur.grffile->housespec.size() || _cur.grffile->housespec[GB(id, 0, 8)] == nullptr) {
7304 GrfMsg(1, "FeatureNewName: Attempt to name undefined house 0x{:X}, ignoring.", GB(id, 0, 8));
7305 } else {
7306 _cur.grffile->housespec[GB(id, 0, 8)]->building_name = AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
7308 break;
7310 default:
7311 GrfMsg(7, "FeatureNewName: Unsupported ID (0x{:04X})", id);
7312 break;
7314 break;
7320 * Sanitize incoming sprite offsets for Action 5 graphics replacements.
7321 * @param num The number of sprites to load.
7322 * @param offset Offset from the base.
7323 * @param max_sprites The maximum number of sprites that can be loaded in this action 5.
7324 * @param name Used for error warnings.
7325 * @return The number of sprites that is going to be skipped.
7327 static uint16_t SanitizeSpriteOffset(uint16_t &num, uint16_t offset, int max_sprites, const std::string_view name)
7330 if (offset >= max_sprites) {
7331 GrfMsg(1, "GraphicsNew: {} sprite offset must be less than {}, skipping", name, max_sprites);
7332 uint orig_num = num;
7333 num = 0;
7334 return orig_num;
7337 if (offset + num > max_sprites) {
7338 GrfMsg(4, "GraphicsNew: {} sprite overflow, truncating...", name);
7339 uint orig_num = num;
7340 num = std::max(max_sprites - offset, 0);
7341 return orig_num - num;
7344 return 0;
7348 /** The information about action 5 types. */
7349 static constexpr auto _action5_types = std::to_array<Action5Type>({
7350 /* Note: min_sprites should not be changed. Therefore these constants are directly here and not in sprites.h */
7351 /* 0x00 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x00" },
7352 /* 0x01 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x01" },
7353 /* 0x02 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x02" },
7354 /* 0x03 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x03" },
7355 /* 0x04 */ { A5BLOCK_ALLOW_OFFSET, SPR_SIGNALS_BASE, 1, PRESIGNAL_SEMAPHORE_AND_PBS_SPRITE_COUNT, "Signal graphics" },
7356 /* 0x05 */ { A5BLOCK_ALLOW_OFFSET, SPR_ELRAIL_BASE, 1, ELRAIL_SPRITE_COUNT, "Rail catenary graphics" },
7357 /* 0x06 */ { A5BLOCK_ALLOW_OFFSET, SPR_SLOPES_BASE, 1, NORMAL_AND_HALFTILE_FOUNDATION_SPRITE_COUNT, "Foundation graphics" },
7358 /* 0x07 */ { A5BLOCK_INVALID, 0, 75, 0, "TTDP GUI graphics" }, // Not used by OTTD.
7359 /* 0x08 */ { A5BLOCK_ALLOW_OFFSET, SPR_CANALS_BASE, 1, CANALS_SPRITE_COUNT, "Canal graphics" },
7360 /* 0x09 */ { A5BLOCK_ALLOW_OFFSET, SPR_ONEWAY_BASE, 1, ONEWAY_SPRITE_COUNT, "One way road graphics" },
7361 /* 0x0A */ { A5BLOCK_ALLOW_OFFSET, SPR_2CCMAP_BASE, 1, TWOCCMAP_SPRITE_COUNT, "2CC colour maps" },
7362 /* 0x0B */ { A5BLOCK_ALLOW_OFFSET, SPR_TRAMWAY_BASE, 1, TRAMWAY_SPRITE_COUNT, "Tramway graphics" },
7363 /* 0x0C */ { A5BLOCK_INVALID, 0, 133, 0, "Snowy temperate tree" }, // Not yet used by OTTD.
7364 /* 0x0D */ { A5BLOCK_FIXED, SPR_SHORE_BASE, 16, SPR_SHORE_SPRITE_COUNT, "Shore graphics" },
7365 /* 0x0E */ { A5BLOCK_INVALID, 0, 0, 0, "New Signals graphics" }, // Not yet used by OTTD.
7366 /* 0x0F */ { A5BLOCK_ALLOW_OFFSET, SPR_TRACKS_FOR_SLOPES_BASE, 1, TRACKS_FOR_SLOPES_SPRITE_COUNT, "Sloped rail track" },
7367 /* 0x10 */ { A5BLOCK_ALLOW_OFFSET, SPR_AIRPORTX_BASE, 1, AIRPORTX_SPRITE_COUNT, "Airport graphics" },
7368 /* 0x11 */ { A5BLOCK_ALLOW_OFFSET, SPR_ROADSTOP_BASE, 1, ROADSTOP_SPRITE_COUNT, "Road stop graphics" },
7369 /* 0x12 */ { A5BLOCK_ALLOW_OFFSET, SPR_AQUEDUCT_BASE, 1, AQUEDUCT_SPRITE_COUNT, "Aqueduct graphics" },
7370 /* 0x13 */ { A5BLOCK_ALLOW_OFFSET, SPR_AUTORAIL_BASE, 1, AUTORAIL_SPRITE_COUNT, "Autorail graphics" },
7371 /* 0x14 */ { A5BLOCK_INVALID, 0, 1, 0, "Flag graphics" }, // deprecated, no longer used.
7372 /* 0x15 */ { A5BLOCK_ALLOW_OFFSET, SPR_OPENTTD_BASE, 1, OPENTTD_SPRITE_COUNT, "OpenTTD GUI graphics" },
7373 /* 0x16 */ { A5BLOCK_ALLOW_OFFSET, SPR_AIRPORT_PREVIEW_BASE, 1, SPR_AIRPORT_PREVIEW_COUNT, "Airport preview graphics" },
7374 /* 0x17 */ { A5BLOCK_ALLOW_OFFSET, SPR_RAILTYPE_TUNNEL_BASE, 1, RAILTYPE_TUNNEL_BASE_COUNT, "Railtype tunnel base" },
7375 /* 0x18 */ { A5BLOCK_ALLOW_OFFSET, SPR_PALETTE_BASE, 1, PALETTE_SPRITE_COUNT, "Palette" },
7376 /* 0x19 */ { A5BLOCK_ALLOW_OFFSET, SPR_ROAD_WAYPOINTS_BASE, 1, ROAD_WAYPOINTS_SPRITE_COUNT, "Road waypoints" },
7380 * Get list of all action 5 types
7381 * @return Read-only span of action 5 type information.
7383 std::span<const Action5Type> GetAction5Types()
7385 return _action5_types;
7388 /* Action 0x05 */
7389 static void GraphicsNew(ByteReader &buf)
7391 /* <05> <graphics-type> <num-sprites> <other data...>
7393 * B graphics-type What set of graphics the sprites define.
7394 * E num-sprites How many sprites are in this set?
7395 * V other data Graphics type specific data. Currently unused. */
7397 uint8_t type = buf.ReadByte();
7398 uint16_t num = buf.ReadExtendedByte();
7399 uint16_t offset = HasBit(type, 7) ? buf.ReadExtendedByte() : 0;
7400 ClrBit(type, 7); // Clear the high bit as that only indicates whether there is an offset.
7402 const Action5Type *action5_type;
7403 const Action5TypeRemapSet &remap = _cur.grffile->action5_type_remaps;
7404 if (remap.remapped_ids[type]) {
7405 auto iter = remap.mapping.find(type);
7406 assert(iter != remap.mapping.end());
7407 const Action5TypeRemapEntry &def = iter->second;
7408 if (def.info == nullptr) {
7409 if (def.fallback_mode == GPMFM_ERROR_ON_USE) {
7410 GrfMsg(0, "Error: Unimplemented action 5 type: {}, mapped to: {:X}", def.name, type);
7411 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_ACTION5_TYPE);
7412 error->data = stredup(def.name);
7413 error->param_value[1] = type;
7414 } else if (def.fallback_mode == GPMFM_IGNORE) {
7415 GrfMsg(2, "Ignoring unimplemented action 5 type: {}, mapped to: {:X}", def.name, type);
7417 _cur.skip_sprites = num;
7418 return;
7419 } else {
7420 action5_type = def.info;
7422 } else {
7423 if ((type == 0x0D) && (num == 10) && HasBit(_cur.grfconfig->flags, GCF_SYSTEM)) {
7424 /* Special not-TTDP-compatible case used in openttd.grf
7425 * Missing shore sprites and initialisation of SPR_SHORE_BASE */
7426 GrfMsg(2, "GraphicsNew: Loading 10 missing shore sprites from extra grf.");
7427 LoadNextSprite(SPR_SHORE_BASE + 0, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_S
7428 LoadNextSprite(SPR_SHORE_BASE + 5, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_W
7429 LoadNextSprite(SPR_SHORE_BASE + 7, *_cur.file, _cur.nfo_line++); // SLOPE_WSE
7430 LoadNextSprite(SPR_SHORE_BASE + 10, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_N
7431 LoadNextSprite(SPR_SHORE_BASE + 11, *_cur.file, _cur.nfo_line++); // SLOPE_NWS
7432 LoadNextSprite(SPR_SHORE_BASE + 13, *_cur.file, _cur.nfo_line++); // SLOPE_ENW
7433 LoadNextSprite(SPR_SHORE_BASE + 14, *_cur.file, _cur.nfo_line++); // SLOPE_SEN
7434 LoadNextSprite(SPR_SHORE_BASE + 15, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_E
7435 LoadNextSprite(SPR_SHORE_BASE + 16, *_cur.file, _cur.nfo_line++); // SLOPE_EW
7436 LoadNextSprite(SPR_SHORE_BASE + 17, *_cur.file, _cur.nfo_line++); // SLOPE_NS
7437 if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ONLY_NEW;
7438 return;
7441 /* Supported type? */
7442 if ((type >= std::size(_action5_types)) || (_action5_types[type].block_type == A5BLOCK_INVALID)) {
7443 GrfMsg(2, "GraphicsNew: Custom graphics (type 0x{:02X}) sprite block of length {} (unimplemented, ignoring)", type, num);
7444 _cur.skip_sprites = num;
7445 return;
7448 action5_type = &_action5_types[type];
7451 /* Contrary to TTDP we allow always to specify too few sprites as we allow always an offset,
7452 * except for the long version of the shore type:
7453 * Ignore offset if not allowed */
7454 if ((action5_type->block_type != A5BLOCK_ALLOW_OFFSET) && (offset != 0)) {
7455 GrfMsg(1, "GraphicsNew: {} (type 0x{:02X}) do not allow an <offset> field. Ignoring offset.", action5_type->name, type);
7456 offset = 0;
7459 /* Ignore action5 if too few sprites are specified. (for TTDP compatibility)
7460 * This does not make sense, if <offset> is allowed */
7461 if ((action5_type->block_type == A5BLOCK_FIXED) && (num < action5_type->min_sprites)) {
7462 GrfMsg(1, "GraphicsNew: {} (type 0x{:02X}) count must be at least {}. Only {} were specified. Skipping.", action5_type->name, type, action5_type->min_sprites, num);
7463 _cur.skip_sprites = num;
7464 return;
7467 /* Load at most max_sprites sprites. Skip remaining sprites. (for compatibility with TTDP and future extensions) */
7468 uint16_t skip_num = SanitizeSpriteOffset(num, offset, action5_type->max_sprites, action5_type->name);
7469 SpriteID replace = action5_type->sprite_base + offset;
7471 /* Load <num> sprites starting from <replace>, then skip <skip_num> sprites. */
7472 GrfMsg(2, "GraphicsNew: Replacing sprites {} to {} of {} (type 0x{:02X}) at SpriteID 0x{:04X}", offset, offset + num - 1, action5_type->name, type, replace);
7474 if (type == 0x0D) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_5;
7476 if (type == 0x0B) {
7477 static const SpriteID depot_with_track_offset = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_TRAMWAY_BASE;
7478 static const SpriteID depot_no_track_offset = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_TRAMWAY_BASE;
7479 if (offset <= depot_with_track_offset && offset + num > depot_with_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_WITH_TRACK;
7480 if (offset <= depot_no_track_offset && offset + num > depot_no_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NO_TRACK;
7483 /* If the baseset or grf only provides sprites for flat tiles (pre #10282), duplicate those for use on slopes. */
7484 bool dup_oneway_sprites = ((type == 0x09) && (offset + num <= SPR_ONEWAY_SLOPE_N_OFFSET));
7486 for (uint16_t n = num; n > 0; n--) {
7487 _cur.nfo_line++;
7488 SpriteID load_index = (replace == 0 ? _cur.spriteid++ : replace++);
7489 LoadNextSprite(load_index, *_cur.file, _cur.nfo_line);
7490 if (dup_oneway_sprites) {
7491 DupSprite(load_index, load_index + SPR_ONEWAY_SLOPE_N_OFFSET);
7492 DupSprite(load_index, load_index + SPR_ONEWAY_SLOPE_S_OFFSET);
7496 if (type == 0x04 && ((_cur.grfconfig->ident.grfid & 0x00FFFFFF) == OPENTTD_GRAPHICS_BASE_GRF_ID ||
7497 _cur.grfconfig->ident.grfid == BSWAP32(0xFF4F4701) || _cur.grfconfig->ident.grfid == BSWAP32(0xFFFFFFFE))) {
7498 /* Signal graphics action 5: Fill duplicate signal sprite block if this is a baseset GRF or OpenGFX */
7499 const SpriteID end = offset + num;
7500 for (SpriteID i = offset; i < end; i++) {
7501 DupSprite(SPR_SIGNALS_BASE + i, SPR_DUP_SIGNALS_BASE + i);
7505 _cur.skip_sprites = skip_num;
7508 /* Action 0x05 (SKIP) */
7509 static void SkipAct5(ByteReader &buf)
7511 /* Ignore type byte */
7512 buf.ReadByte();
7514 /* Skip the sprites of this action */
7515 _cur.skip_sprites = buf.ReadExtendedByte();
7517 GrfMsg(3, "SkipAct5: Skipping {} sprites", _cur.skip_sprites);
7521 * Reads a variable common to VarAction2 and Action7/9/D.
7523 * Returns VarAction2 variable 'param' resp. Action7/9/D variable '0x80 + param'.
7524 * If a variable is not accessible from all four actions, it is handled in the action specific functions.
7526 * @param param variable number (as for VarAction2, for Action7/9/D you have to subtract 0x80 first).
7527 * @param value returns the value of the variable.
7528 * @param grffile NewGRF querying the variable
7529 * @return true iff the variable is known and the value is returned in 'value'.
7531 bool GetGlobalVariable(uint8_t param, uint32_t *value, const GRFFile *grffile)
7533 if (_sprite_group_resolve_check_veh_check) {
7534 switch (param) {
7535 case 0x00:
7536 case 0x02:
7537 case 0x09:
7538 case 0x0A:
7539 case 0x20:
7540 case 0x23:
7541 _sprite_group_resolve_check_veh_check = false;
7542 break;
7546 switch (param) {
7547 case 0x00: // current date
7548 *value = std::max<DateDelta>(CalTime::CurDate() - CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR, DateDelta{0}).base();
7549 return true;
7551 case 0x01: // current year
7552 *value = (Clamp(CalTime::CurYear(), CalTime::ORIGINAL_BASE_YEAR, CalTime::ORIGINAL_MAX_YEAR) - CalTime::ORIGINAL_BASE_YEAR).base();
7553 return true;
7555 case 0x02: { // detailed date information: month of year (bit 0-7), day of month (bit 8-12), leap year (bit 15), day of year (bit 16-24)
7556 CalTime::Date start_of_year = CalTime::ConvertYMDToDate(CalTime::CurYear(), 0, 1);
7557 *value = CalTime::CurMonth() | (CalTime::CurDay() - 1) << 8 | (CalTime::IsLeapYear(CalTime::CurYear()) ? 1 << 15 : 0) | (CalTime::CurDate() - start_of_year).base() << 16;
7558 return true;
7561 case 0x03: // current climate, 0=temp, 1=arctic, 2=trop, 3=toyland
7562 *value = _settings_game.game_creation.landscape;
7563 return true;
7565 case 0x06: // road traffic side, bit 4 clear=left, set=right
7566 *value = _settings_game.vehicle.road_side << 4;
7567 return true;
7569 case 0x09: // date fraction
7570 *value = CalTime::CurDateFract() * 885;
7571 return true;
7573 case 0x0A: // animation counter
7574 *value = GB(_scaled_tick_counter, 0, 16);
7575 return true;
7577 case 0x0B: { // TTDPatch version
7578 uint major = 2;
7579 uint minor = 6;
7580 uint revision = 1; // special case: 2.0.1 is 2.0.10
7581 uint build = 1382;
7582 *value = (major << 24) | (minor << 20) | (revision << 16) | build;
7583 return true;
7586 case 0x0D: // TTD Version, 00=DOS, 01=Windows
7587 *value = (_cur.grfconfig->palette & GRFP_USE_MASK) | grffile->var8D_overlay;
7588 return true;
7590 case 0x0E: // Y-offset for train sprites
7591 *value = _cur.grffile->traininfo_vehicle_pitch;
7592 return true;
7594 case 0x0F: // Rail track type cost factors
7595 *value = 0;
7596 SB(*value, 0, 8, GetRailTypeInfo(RAILTYPE_RAIL)->cost_multiplier); // normal rail
7597 if (_settings_game.vehicle.disable_elrails) {
7598 /* skip elrail multiplier - disabled */
7599 SB(*value, 8, 8, GetRailTypeInfo(RAILTYPE_MONO)->cost_multiplier); // monorail
7600 } else {
7601 SB(*value, 8, 8, GetRailTypeInfo(RAILTYPE_ELECTRIC)->cost_multiplier); // electified railway
7602 /* Skip monorail multiplier - no space in result */
7604 SB(*value, 16, 8, GetRailTypeInfo(RAILTYPE_MAGLEV)->cost_multiplier); // maglev
7605 return true;
7607 case 0x11: // current rail tool type
7608 *value = 0; // constant fake value to avoid desync
7609 return true;
7611 case 0x12: // Game mode
7612 *value = _game_mode;
7613 return true;
7615 /* case 0x13: // Tile refresh offset to left not implemented */
7616 /* case 0x14: // Tile refresh offset to right not implemented */
7617 /* case 0x15: // Tile refresh offset upwards not implemented */
7618 /* case 0x16: // Tile refresh offset downwards not implemented */
7619 /* case 0x17: // temperate snow line not implemented */
7621 case 0x1A: // Always -1
7622 *value = UINT_MAX;
7623 return true;
7625 case 0x1B: // Display options
7626 *value = 0x3F; // constant fake value to avoid desync
7627 return true;
7629 case 0x1D: // TTD Platform, 00=TTDPatch, 01=OpenTTD, also used for feature tests (bits 31..4)
7630 *value = 1 | grffile->var9D_overlay;
7631 return true;
7633 case 0x1E: // Miscellaneous GRF features
7634 *value = _misc_grf_features;
7636 /* Add the local flags */
7637 assert(!HasBit(*value, GMB_TRAIN_WIDTH_32_PIXELS));
7638 if (_cur.grffile->traininfo_vehicle_width == VEHICLEINFO_FULL_VEHICLE_WIDTH) SetBit(*value, GMB_TRAIN_WIDTH_32_PIXELS);
7639 return true;
7641 /* case 0x1F: // locale dependent settings not implemented to avoid desync */
7643 case 0x20: { // snow line height
7644 uint8_t snowline = GetSnowLine();
7645 if (_settings_game.game_creation.landscape == LT_ARCTIC && snowline <= _settings_game.construction.map_height_limit) {
7646 *value = Clamp(snowline * (grffile->grf_version >= 8 ? 1 : TILE_HEIGHT), 0, 0xFE);
7647 } else {
7648 /* No snow */
7649 *value = 0xFF;
7651 return true;
7654 case 0x21: // OpenTTD version
7655 *value = _openttd_newgrf_version;
7656 return true;
7658 case 0x22: // difficulty level
7659 *value = SP_CUSTOM;
7660 return true;
7662 case 0x23: // long format date
7663 *value = CalTime::CurDate().base();
7664 return true;
7666 case 0x24: // long format year
7667 *value = CalTime::CurYear().base();
7668 return true;
7670 default: return false;
7674 static uint32_t GetParamVal(uint8_t param, uint32_t *cond_val)
7676 /* First handle variable common with VarAction2 */
7677 uint32_t value;
7678 if (GetGlobalVariable(param - 0x80, &value, _cur.grffile)) return value;
7680 /* Non-common variable */
7681 switch (param) {
7682 case 0x84: { // GRF loading stage
7683 uint32_t res = 0;
7685 if (_cur.stage > GLS_INIT) SetBit(res, 0);
7686 if (_cur.stage == GLS_RESERVE) SetBit(res, 8);
7687 if (_cur.stage == GLS_ACTIVATION) SetBit(res, 9);
7688 return res;
7691 case 0x85: // TTDPatch flags, only for bit tests
7692 if (cond_val == nullptr) {
7693 /* Supported in Action 0x07 and 0x09, not 0x0D */
7694 return 0;
7695 } else {
7696 uint32_t index = *cond_val / 0x20;
7697 *cond_val %= 0x20;
7698 uint32_t param_val = 0;
7699 if (index < lengthof(_ttdpatch_flags)) {
7700 param_val = _ttdpatch_flags[index];
7701 if (!HasBit(_cur.grfconfig->flags, GCF_STATIC) && !HasBit(_cur.grfconfig->flags, GCF_SYSTEM)) {
7702 SetBit(_observed_ttdpatch_flags[index], *cond_val);
7705 return param_val;
7708 case 0x88: // GRF ID check
7709 return 0;
7711 /* case 0x99: Global ID offset not implemented */
7713 default:
7714 /* GRF Parameter */
7715 if (param < 0x80) return _cur.grffile->GetParam(param);
7717 /* In-game variable. */
7718 GrfMsg(1, "Unsupported in-game variable 0x{:02X}", param);
7719 return UINT_MAX;
7723 /* Action 0x06 */
7724 static void CfgApply(ByteReader &buf)
7726 /* <06> <param-num> <param-size> <offset> ... <FF>
7728 * B param-num Number of parameter to substitute (First = "zero")
7729 * Ignored if that parameter was not specified in newgrf.cfg
7730 * B param-size How many bytes to replace. If larger than 4, the
7731 * bytes of the following parameter are used. In that
7732 * case, nothing is applied unless *all* parameters
7733 * were specified.
7734 * B offset Offset into data from beginning of next sprite
7735 * to place where parameter is to be stored. */
7737 /* Preload the next sprite */
7738 SpriteFile &file = *_cur.file;
7739 size_t pos = file.GetPos();
7740 uint32_t num = file.GetContainerVersion() >= 2 ? file.ReadDword() : file.ReadWord();
7741 uint8_t type = file.ReadByte();
7743 /* Check if the sprite is a pseudo sprite. We can't operate on real sprites. */
7744 if (type != 0xFF) {
7745 GrfMsg(2, "CfgApply: Ignoring (next sprite is real, unsupported)");
7747 /* Reset the file position to the start of the next sprite */
7748 file.SeekTo(pos, SEEK_SET);
7749 return;
7752 /* Get (or create) the override for the next sprite. */
7753 GRFLocation location(_cur.grfconfig->ident.grfid, _cur.nfo_line + 1);
7754 std::unique_ptr<uint8_t[]> &preload_sprite = _grf_line_to_action6_sprite_override[location];
7756 /* Load new sprite data if it hasn't already been loaded. */
7757 if (preload_sprite == nullptr) {
7758 preload_sprite = std::make_unique<uint8_t[]>(num);
7759 file.ReadBlock(preload_sprite.get(), num);
7762 /* Reset the file position to the start of the next sprite */
7763 file.SeekTo(pos, SEEK_SET);
7765 /* Now perform the Action 0x06 on our data. */
7766 for (;;) {
7767 uint i;
7768 uint param_num;
7769 uint param_size;
7770 uint offset;
7771 bool add_value;
7773 /* Read the parameter to apply. 0xFF indicates no more data to change. */
7774 param_num = buf.ReadByte();
7775 if (param_num == 0xFF) break;
7777 /* Get the size of the parameter to use. If the size covers multiple
7778 * double words, sequential parameter values are used. */
7779 param_size = buf.ReadByte();
7781 /* Bit 7 of param_size indicates we should add to the original value
7782 * instead of replacing it. */
7783 add_value = HasBit(param_size, 7);
7784 param_size = GB(param_size, 0, 7);
7786 /* Where to apply the data to within the pseudo sprite data. */
7787 offset = buf.ReadExtendedByte();
7789 /* If the parameter is a GRF parameter (not an internal variable) check
7790 * if it (and all further sequential parameters) has been defined. */
7791 if (param_num < 0x80 && (param_num + (param_size - 1) / 4) >= std::size(_cur.grffile->param)) {
7792 GrfMsg(2, "CfgApply: Ignoring (param {} not set)", (param_num + (param_size - 1) / 4));
7793 break;
7796 GrfMsg(8, "CfgApply: Applying {} bytes from parameter 0x{:02X} at offset 0x{:04X}", param_size, param_num, offset);
7798 bool carry = false;
7799 for (i = 0; i < param_size && offset + i < num; i++) {
7800 uint32_t value = GetParamVal(param_num + i / 4, nullptr);
7801 /* Reset carry flag for each iteration of the variable (only really
7802 * matters if param_size is greater than 4) */
7803 if (i % 4 == 0) carry = false;
7805 if (add_value) {
7806 uint new_value = preload_sprite[offset + i] + GB(value, (i % 4) * 8, 8) + (carry ? 1 : 0);
7807 preload_sprite[offset + i] = GB(new_value, 0, 8);
7808 /* Check if the addition overflowed */
7809 carry = new_value >= 256;
7810 } else {
7811 preload_sprite[offset + i] = GB(value, (i % 4) * 8, 8);
7818 * Disable a static NewGRF when it is influencing another (non-static)
7819 * NewGRF as this could cause desyncs.
7821 * We could just tell the NewGRF querying that the file doesn't exist,
7822 * but that might give unwanted results. Disabling the NewGRF gives the
7823 * best result as no NewGRF author can complain about that.
7824 * @param c The NewGRF to disable.
7826 static void DisableStaticNewGRFInfluencingNonStaticNewGRFs(GRFConfig *c)
7828 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC, c);
7829 error->data = _cur.grfconfig->GetName();
7832 /* Action 0x07
7833 * Action 0x09 */
7834 static void SkipIf(ByteReader &buf)
7836 /* <07/09> <param-num> <param-size> <condition-type> <value> <num-sprites>
7838 * B param-num
7839 * B param-size
7840 * B condition-type
7841 * V value
7842 * B num-sprites */
7843 uint32_t cond_val = 0;
7844 uint32_t mask = 0;
7845 bool result;
7847 uint8_t param = buf.ReadByte();
7848 uint8_t paramsize = buf.ReadByte();
7849 uint8_t condtype = buf.ReadByte();
7851 if (condtype < 2) {
7852 /* Always 1 for bit tests, the given value should be ignored. */
7853 paramsize = 1;
7856 switch (paramsize) {
7857 case 8: cond_val = buf.ReadDWord(); mask = buf.ReadDWord(); break;
7858 case 4: cond_val = buf.ReadDWord(); mask = 0xFFFFFFFF; break;
7859 case 2: cond_val = buf.ReadWord(); mask = 0x0000FFFF; break;
7860 case 1: cond_val = buf.ReadByte(); mask = 0x000000FF; break;
7861 default: break;
7864 if (param < 0x80 && std::size(_cur.grffile->param) <= param) {
7865 GrfMsg(7, "SkipIf: Param {} undefined, skipping test", param);
7866 return;
7869 GrfMsg(7, "SkipIf: Test condtype {}, param 0x{:02X}, condval 0x{:08X}", condtype, param, cond_val);
7871 /* condtypes that do not use 'param' are always valid.
7872 * condtypes that use 'param' are either not valid for param 0x88, or they are only valid for param 0x88.
7874 if (condtype >= 0x0B) {
7875 /* Tests that ignore 'param' */
7876 switch (condtype) {
7877 case 0x0B: result = !IsValidCargoID(GetCargoIDByLabel(CargoLabel(BSWAP32(cond_val))));
7878 break;
7879 case 0x0C: result = IsValidCargoID(GetCargoIDByLabel(CargoLabel(BSWAP32(cond_val))));
7880 break;
7881 case 0x0D: result = GetRailTypeByLabel(BSWAP32(cond_val)) == INVALID_RAILTYPE;
7882 break;
7883 case 0x0E: result = GetRailTypeByLabel(BSWAP32(cond_val)) != INVALID_RAILTYPE;
7884 break;
7885 case 0x0F: {
7886 RoadType rt = GetRoadTypeByLabel(BSWAP32(cond_val));
7887 result = rt == INVALID_ROADTYPE || !RoadTypeIsRoad(rt);
7888 break;
7890 case 0x10: {
7891 RoadType rt = GetRoadTypeByLabel(BSWAP32(cond_val));
7892 result = rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt);
7893 break;
7895 case 0x11: {
7896 RoadType rt = GetRoadTypeByLabel(BSWAP32(cond_val));
7897 result = rt == INVALID_ROADTYPE || !RoadTypeIsTram(rt);
7898 break;
7900 case 0x12: {
7901 RoadType rt = GetRoadTypeByLabel(BSWAP32(cond_val));
7902 result = rt != INVALID_ROADTYPE && RoadTypeIsTram(rt);
7903 break;
7905 default: GrfMsg(1, "SkipIf: Unsupported condition type {:02X}. Ignoring", condtype); return;
7907 } else if (param == 0x88) {
7908 /* GRF ID checks */
7910 GRFConfig *c = GetGRFConfig(cond_val, mask);
7912 if (c != nullptr && HasBit(c->flags, GCF_STATIC) && !HasBit(_cur.grfconfig->flags, GCF_STATIC) && _networking) {
7913 DisableStaticNewGRFInfluencingNonStaticNewGRFs(c);
7914 c = nullptr;
7917 if (condtype != 10 && c == nullptr) {
7918 GrfMsg(7, "SkipIf: GRFID 0x{:08X} unknown, skipping test", BSWAP32(cond_val));
7919 return;
7922 switch (condtype) {
7923 /* Tests 0x06 to 0x0A are only for param 0x88, GRFID checks */
7924 case 0x06: // Is GRFID active?
7925 result = c->status == GCS_ACTIVATED;
7926 break;
7928 case 0x07: // Is GRFID non-active?
7929 result = c->status != GCS_ACTIVATED;
7930 break;
7932 case 0x08: // GRFID is not but will be active?
7933 result = c->status == GCS_INITIALISED;
7934 break;
7936 case 0x09: // GRFID is or will be active?
7937 result = c->status == GCS_ACTIVATED || c->status == GCS_INITIALISED;
7938 break;
7940 case 0x0A: // GRFID is not nor will be active
7941 /* This is the only condtype that doesn't get ignored if the GRFID is not found */
7942 result = c == nullptr || c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND;
7943 break;
7945 default: GrfMsg(1, "SkipIf: Unsupported GRF condition type {:02X}. Ignoring", condtype); return;
7947 } else if (param == 0x91 && (condtype == 0x02 || condtype == 0x03) && cond_val > 0) {
7948 const std::vector<uint32_t> &values = _cur.grffile->var91_values;
7949 /* condtype 0x02: skip if test result found
7950 * condtype 0x03: skip if test result not found
7952 bool found = std::find(values.begin(), values.end(), cond_val) != values.end();
7953 result = (found == (condtype == 0x02));
7954 } else {
7955 /* Tests that use 'param' and are not GRF ID checks. */
7956 uint32_t param_val = GetParamVal(param, &cond_val); // cond_val is modified for param == 0x85
7957 switch (condtype) {
7958 case 0x00: result = !!(param_val & (1 << cond_val));
7959 break;
7960 case 0x01: result = !(param_val & (1 << cond_val));
7961 break;
7962 case 0x02: result = (param_val & mask) == cond_val;
7963 break;
7964 case 0x03: result = (param_val & mask) != cond_val;
7965 break;
7966 case 0x04: result = (param_val & mask) < cond_val;
7967 break;
7968 case 0x05: result = (param_val & mask) > cond_val;
7969 break;
7970 default: GrfMsg(1, "SkipIf: Unsupported condition type {:02X}. Ignoring", condtype); return;
7974 if (!result) {
7975 GrfMsg(2, "SkipIf: Not skipping sprites, test was false");
7976 return;
7979 uint8_t numsprites = buf.ReadByte();
7981 /* numsprites can be a GOTO label if it has been defined in the GRF
7982 * file. The jump will always be the first matching label that follows
7983 * the current nfo_line. If no matching label is found, the first matching
7984 * label in the file is used. */
7985 const GRFLabel *choice = nullptr;
7986 for (const auto &label : _cur.grffile->labels) {
7987 if (label.label != numsprites) continue;
7989 /* Remember a goto before the current line */
7990 if (choice == nullptr) choice = &label;
7991 /* If we find a label here, this is definitely good */
7992 if (label.nfo_line > _cur.nfo_line) {
7993 choice = &label;
7994 break;
7998 if (choice != nullptr) {
7999 GrfMsg(2, "SkipIf: Jumping to label 0x{:X} at line {}, test was true", choice->label, choice->nfo_line);
8000 _cur.file->SeekTo(choice->pos, SEEK_SET);
8001 _cur.nfo_line = choice->nfo_line;
8002 return;
8005 GrfMsg(2, "SkipIf: Skipping {} sprites, test was true", numsprites);
8006 _cur.skip_sprites = numsprites;
8007 if (_cur.skip_sprites == 0) {
8008 /* Zero means there are no sprites to skip, so
8009 * we use -1 to indicate that all further
8010 * sprites should be skipped. */
8011 _cur.skip_sprites = -1;
8013 /* If an action 8 hasn't been encountered yet, disable the grf. */
8014 if (_cur.grfconfig->status != (_cur.stage < GLS_RESERVE ? GCS_INITIALISED : GCS_ACTIVATED)) {
8015 DisableGrf();
8021 /* Action 0x08 (GLS_FILESCAN) */
8022 static void ScanInfo(ByteReader &buf)
8024 uint8_t grf_version = buf.ReadByte();
8025 uint32_t grfid = buf.ReadDWord();
8026 std::string_view name = buf.ReadString();
8028 _cur.grfconfig->ident.grfid = grfid;
8030 if (grf_version < 2 || grf_version > 8) {
8031 SetBit(_cur.grfconfig->flags, GCF_INVALID);
8032 Debug(grf, 0, "{}: NewGRF \"{}\" (GRFID {:08X}) uses GRF version {}, which is incompatible with this version of OpenTTD.", _cur.grfconfig->GetDisplayPath(), StrMakeValid(name), BSWAP32(grfid), grf_version);
8035 /* GRF IDs starting with 0xFF are reserved for internal TTDPatch use */
8036 if (GB(grfid, 0, 8) == 0xFF) SetBit(_cur.grfconfig->flags, GCF_SYSTEM);
8038 AddGRFTextToList(_cur.grfconfig->name, 0x7F, grfid, false, name);
8040 if (buf.HasData()) {
8041 std::string_view info = buf.ReadString();
8042 AddGRFTextToList(_cur.grfconfig->info, 0x7F, grfid, true, info);
8045 /* GLS_INFOSCAN only looks for the action 8, so we can skip the rest of the file */
8046 _cur.skip_sprites = -1;
8049 /* Action 0x08 */
8050 static void GRFInfo(ByteReader &buf)
8052 /* <08> <version> <grf-id> <name> <info>
8054 * B version newgrf version, currently 06
8055 * 4*B grf-id globally unique ID of this .grf file
8056 * S name name of this .grf set
8057 * S info string describing the set, and e.g. author and copyright */
8059 uint8_t version = buf.ReadByte();
8060 uint32_t grfid = buf.ReadDWord();
8061 std::string_view name = buf.ReadString();
8063 if (_cur.stage < GLS_RESERVE && _cur.grfconfig->status != GCS_UNKNOWN) {
8064 DisableGrf(STR_NEWGRF_ERROR_MULTIPLE_ACTION_8);
8065 return;
8068 if (_cur.grffile->grfid != grfid) {
8069 Debug(grf, 0, "GRFInfo: GRFID {:08X} in FILESCAN stage does not match GRFID {:08X} in INIT/RESERVE/ACTIVATION stage", BSWAP32(_cur.grffile->grfid), BSWAP32(grfid));
8070 _cur.grffile->grfid = grfid;
8073 _cur.grffile->grf_version = version;
8074 _cur.grfconfig->status = _cur.stage < GLS_RESERVE ? GCS_INITIALISED : GCS_ACTIVATED;
8076 /* Do swap the GRFID for displaying purposes since people expect that */
8077 Debug(grf, 1, "GRFInfo: Loaded GRFv{} set {:08X} - {} (palette: {}, version: {})", version, BSWAP32(grfid), StrMakeValid(name), (_cur.grfconfig->palette & GRFP_USE_MASK) ? "Windows" : "DOS", _cur.grfconfig->version);
8081 * Check if a sprite ID range is within the GRM reversed range for the currently loading NewGRF.
8082 * @param first_sprite First sprite of range.
8083 * @param num_sprites Number of sprites in the range.
8084 * @return True iff the NewGRF has reserved a range equal to or greater than the provided range.
8086 static bool IsGRMReservedSprite(SpriteID first_sprite, uint16_t num_sprites)
8088 for (const auto &grm_sprite : _grm_sprites) {
8089 if (grm_sprite.first.grfid != _cur.grffile->grfid) continue;
8090 if (grm_sprite.second.first <= first_sprite && grm_sprite.second.first + grm_sprite.second.second >= first_sprite + num_sprites) return true;
8092 return false;
8095 /* Action 0x0A */
8096 static void SpriteReplace(ByteReader &buf)
8098 /* <0A> <num-sets> <set1> [<set2> ...]
8099 * <set>: <num-sprites> <first-sprite>
8101 * B num-sets How many sets of sprites to replace.
8102 * Each set:
8103 * B num-sprites How many sprites are in this set
8104 * W first-sprite First sprite number to replace */
8106 uint8_t num_sets = buf.ReadByte();
8108 for (uint i = 0; i < num_sets; i++) {
8109 uint8_t num_sprites = buf.ReadByte();
8110 uint16_t first_sprite = buf.ReadWord();
8112 GrfMsg(2, "SpriteReplace: [Set {}] Changing {} sprites, beginning with {}",
8113 i, num_sprites, first_sprite
8116 if (first_sprite + num_sprites >= SPR_OPENTTD_BASE) {
8117 /* Outside allowed range, check for GRM sprite reservations. */
8118 if (!IsGRMReservedSprite(first_sprite, num_sprites)) {
8119 GrfMsg(0, "SpriteReplace: [Set {}] Changing {} sprites, beginning with {}, above limit of {} and not within reserved range, ignoring.",
8120 i, num_sprites, first_sprite, SPR_OPENTTD_BASE);
8122 for (uint j = 0; j < num_sprites; j++) {
8123 _cur.nfo_line++;
8124 LoadNextSprite(INVALID_SPRITE_ID, *_cur.file, _cur.nfo_line);
8126 return;
8130 for (uint j = 0; j < num_sprites; j++) {
8131 SpriteID load_index = first_sprite + j;
8132 _cur.nfo_line++;
8133 if (load_index < (int)SPR_PROGSIGNAL_BASE || load_index >= (int)SPR_NEWGRFS_BASE) {
8134 LoadNextSprite(load_index, *_cur.file, _cur.nfo_line); // XXX
8135 } else {
8136 /* Skip sprite */
8137 GrfMsg(0, "SpriteReplace: Ignoring attempt to replace protected sprite ID: {}", load_index);
8138 LoadNextSprite(INVALID_SPRITE_ID, *_cur.file, _cur.nfo_line);
8141 /* Shore sprites now located at different addresses.
8142 * So detect when the old ones get replaced. */
8143 if (IsInsideMM(load_index, SPR_ORIGINALSHORE_START, SPR_ORIGINALSHORE_END + 1)) {
8144 if (_loaded_newgrf_features.shore != SHORE_REPLACE_ACTION_5) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_A;
8150 /* Action 0x0A (SKIP) */
8151 static void SkipActA(ByteReader &buf)
8153 uint8_t num_sets = buf.ReadByte();
8155 for (uint i = 0; i < num_sets; i++) {
8156 /* Skip the sprites this replaces */
8157 _cur.skip_sprites += buf.ReadByte();
8158 /* But ignore where they go */
8159 buf.ReadWord();
8162 GrfMsg(3, "SkipActA: Skipping {} sprites", _cur.skip_sprites);
8165 /* Action 0x0B */
8166 static void GRFLoadError(ByteReader &buf)
8168 /* <0B> <severity> <language-id> <message-id> [<message...> 00] [<data...>] 00 [<parnum>]
8170 * B severity 00: notice, continue loading grf file
8171 * 01: warning, continue loading grf file
8172 * 02: error, but continue loading grf file, and attempt
8173 * loading grf again when loading or starting next game
8174 * 03: error, abort loading and prevent loading again in
8175 * the future (only when restarting the patch)
8176 * B language-id see action 4, use 1F for built-in error messages
8177 * B message-id message to show, see below
8178 * S message for custom messages (message-id FF), text of the message
8179 * not present for built-in messages.
8180 * V data additional data for built-in (or custom) messages
8181 * B parnum parameter numbers to be shown in the message (maximum of 2) */
8183 static const StringID msgstr[] = {
8184 STR_NEWGRF_ERROR_VERSION_NUMBER,
8185 STR_NEWGRF_ERROR_DOS_OR_WINDOWS,
8186 STR_NEWGRF_ERROR_UNSET_SWITCH,
8187 STR_NEWGRF_ERROR_INVALID_PARAMETER,
8188 STR_NEWGRF_ERROR_LOAD_BEFORE,
8189 STR_NEWGRF_ERROR_LOAD_AFTER,
8190 STR_NEWGRF_ERROR_OTTD_VERSION_NUMBER,
8193 static const StringID sevstr[] = {
8194 STR_NEWGRF_ERROR_MSG_INFO,
8195 STR_NEWGRF_ERROR_MSG_WARNING,
8196 STR_NEWGRF_ERROR_MSG_ERROR,
8197 STR_NEWGRF_ERROR_MSG_FATAL
8200 uint8_t severity = buf.ReadByte();
8201 uint8_t lang = buf.ReadByte();
8202 uint8_t message_id = buf.ReadByte();
8204 /* Skip the error if it isn't valid for the current language. */
8205 if (!CheckGrfLangID(lang, _cur.grffile->grf_version)) return;
8207 /* Skip the error until the activation stage unless bit 7 of the severity
8208 * is set. */
8209 if (!HasBit(severity, 7) && _cur.stage == GLS_INIT) {
8210 GrfMsg(7, "GRFLoadError: Skipping non-fatal GRFLoadError in stage {}", _cur.stage);
8211 return;
8213 ClrBit(severity, 7);
8215 if (severity >= lengthof(sevstr)) {
8216 GrfMsg(7, "GRFLoadError: Invalid severity id {}. Setting to 2 (non-fatal error).", severity);
8217 severity = 2;
8218 } else if (severity == 3) {
8219 /* This is a fatal error, so make sure the GRF is deactivated and no
8220 * more of it gets loaded. */
8221 DisableGrf();
8223 /* Make sure we show fatal errors, instead of silly infos from before */
8224 _cur.grfconfig->error.reset();
8227 if (message_id >= lengthof(msgstr) && message_id != 0xFF) {
8228 GrfMsg(7, "GRFLoadError: Invalid message id.");
8229 return;
8232 if (buf.Remaining() <= 1) {
8233 GrfMsg(7, "GRFLoadError: No message data supplied.");
8234 return;
8237 /* For now we can only show one message per newgrf file. */
8238 if (_cur.grfconfig->error.has_value()) return;
8240 _cur.grfconfig->error = {sevstr[severity]};
8241 GRFError *error = &_cur.grfconfig->error.value();
8243 if (message_id == 0xFF) {
8244 /* This is a custom error message. */
8245 if (buf.HasData()) {
8246 std::string_view message = buf.ReadString();
8248 error->custom_message = TranslateTTDPatchCodes(_cur.grffile->grfid, lang, true, message, SCC_RAW_STRING_POINTER);
8249 } else {
8250 GrfMsg(7, "GRFLoadError: No custom message supplied.");
8251 error->custom_message.clear();
8253 } else {
8254 error->message = msgstr[message_id];
8257 if (buf.HasData()) {
8258 std::string_view data = buf.ReadString();
8260 error->data = TranslateTTDPatchCodes(_cur.grffile->grfid, lang, true, data);
8261 } else {
8262 GrfMsg(7, "GRFLoadError: No message data supplied.");
8263 error->data.clear();
8266 /* Only two parameter numbers can be used in the string. */
8267 for (uint i = 0; i < error->param_value.size() && buf.HasData(); i++) {
8268 uint param_number = buf.ReadByte();
8269 error->param_value[i] = _cur.grffile->GetParam(param_number);
8273 /* Action 0x0C */
8274 static void GRFComment(ByteReader &buf)
8276 /* <0C> [<ignored...>]
8278 * V ignored Anything following the 0C is ignored */
8280 if (!buf.HasData()) return;
8282 std::string_view text = buf.ReadString();
8283 GrfMsg(2, "GRFComment: {}", StrMakeValid(text));
8286 /* Action 0x0D (GLS_SAFETYSCAN) */
8287 static void SafeParamSet(ByteReader &buf)
8289 uint8_t target = buf.ReadByte();
8291 /* Writing GRF parameters and some bits of 'misc GRF features' are safe. */
8292 if (target < 0x80 || target == 0x9E) return;
8294 /* GRM could be unsafe, but as here it can only happen after other GRFs
8295 * are loaded, it should be okay. If the GRF tried to use the slots it
8296 * reserved, it would be marked unsafe anyway. GRM for (e.g. bridge)
8297 * sprites is considered safe. */
8299 SetBit(_cur.grfconfig->flags, GCF_UNSAFE);
8301 /* Skip remainder of GRF */
8302 _cur.skip_sprites = -1;
8306 static uint32_t GetPatchVariable(uint8_t param)
8308 switch (param) {
8309 /* start year - 1920 */
8310 case 0x0B: return (std::max(_settings_game.game_creation.starting_year, CalTime::ORIGINAL_BASE_YEAR) - CalTime::ORIGINAL_BASE_YEAR).base();
8312 /* freight trains weight factor */
8313 case 0x0E: return _settings_game.vehicle.freight_trains;
8315 /* empty wagon speed increase */
8316 case 0x0F: return 0;
8318 /* plane speed factor; our patch option is reversed from TTDPatch's,
8319 * the following is good for 1x, 2x and 4x (most common?) and...
8320 * well not really for 3x. */
8321 case 0x10:
8322 switch (_settings_game.vehicle.plane_speed) {
8323 default:
8324 case 4: return 1;
8325 case 3: return 2;
8326 case 2: return 2;
8327 case 1: return 4;
8331 /* 2CC colourmap base sprite */
8332 case 0x11: return SPR_2CCMAP_BASE;
8334 /* map size: format = -MABXYSS
8335 * M : the type of map
8336 * bit 0 : set : squared map. Bit 1 is now not relevant
8337 * clear : rectangle map. Bit 1 will indicate the bigger edge of the map
8338 * bit 1 : set : Y is the bigger edge. Bit 0 is clear
8339 * clear : X is the bigger edge.
8340 * A : minimum edge(log2) of the map
8341 * B : maximum edge(log2) of the map
8342 * XY : edges(log2) of each side of the map.
8343 * SS : combination of both X and Y, thus giving the size(log2) of the map
8345 case 0x13: {
8346 uint8_t map_bits = 0;
8347 uint8_t log_X = MapLogX() - 6; // subtraction is required to make the minimal size (64) zero based
8348 uint8_t log_Y = MapLogY() - 6;
8349 uint8_t max_edge = std::max(log_X, log_Y);
8351 if (log_X == log_Y) { // we have a squared map, since both edges are identical
8352 SetBit(map_bits, 0);
8353 } else {
8354 if (max_edge == log_Y) SetBit(map_bits, 1); // edge Y been the biggest, mark it
8357 return (map_bits << 24) | (std::min(log_X, log_Y) << 20) | (max_edge << 16) |
8358 (log_X << 12) | (log_Y << 8) | (log_X + log_Y);
8361 /* The maximum height of the map. */
8362 case 0x14:
8363 return _settings_game.construction.map_height_limit;
8365 /* Extra foundations base sprite */
8366 case 0x15:
8367 return SPR_SLOPES_BASE;
8369 /* Shore base sprite */
8370 case 0x16:
8371 return SPR_SHORE_BASE;
8373 /* Game map seed */
8374 case 0x17:
8375 return _settings_game.game_creation.generation_seed;
8377 default:
8378 GrfMsg(2, "ParamSet: Unknown Patch variable 0x{:02X}.", param);
8379 return 0;
8384 static uint32_t PerformGRM(uint32_t *grm, uint16_t num_ids, uint16_t count, uint8_t op, uint8_t target, const char *type)
8386 uint start = 0;
8387 uint size = 0;
8389 if (op == 6) {
8390 /* Return GRFID of set that reserved ID */
8391 return grm[_cur.grffile->GetParam(target)];
8394 /* With an operation of 2 or 3, we want to reserve a specific block of IDs */
8395 if (op == 2 || op == 3) start = _cur.grffile->GetParam(target);
8397 for (uint i = start; i < num_ids; i++) {
8398 if (grm[i] == 0) {
8399 size++;
8400 } else {
8401 if (op == 2 || op == 3) break;
8402 start = i + 1;
8403 size = 0;
8406 if (size == count) break;
8409 if (size == count) {
8410 /* Got the slot... */
8411 if (op == 0 || op == 3) {
8412 GrfMsg(2, "ParamSet: GRM: Reserving {} {} at {}", count, type, start);
8413 for (uint i = 0; i < count; i++) grm[start + i] = _cur.grffile->grfid;
8415 return start;
8418 /* Unable to allocate */
8419 if (op != 4 && op != 5) {
8420 /* Deactivate GRF */
8421 GrfMsg(0, "ParamSet: GRM: Unable to allocate {} {}, deactivating", count, type);
8422 DisableGrf(STR_NEWGRF_ERROR_GRM_FAILED);
8423 return UINT_MAX;
8426 GrfMsg(1, "ParamSet: GRM: Unable to allocate {} {}", count, type);
8427 return UINT_MAX;
8431 /** Action 0x0D: Set parameter */
8432 static void ParamSet(ByteReader &buf)
8434 /* <0D> <target> <operation> <source1> <source2> [<data>]
8436 * B target parameter number where result is stored
8437 * B operation operation to perform, see below
8438 * B source1 first source operand
8439 * B source2 second source operand
8440 * D data data to use in the calculation, not necessary
8441 * if both source1 and source2 refer to actual parameters
8443 * Operations
8444 * 00 Set parameter equal to source1
8445 * 01 Addition, source1 + source2
8446 * 02 Subtraction, source1 - source2
8447 * 03 Unsigned multiplication, source1 * source2 (both unsigned)
8448 * 04 Signed multiplication, source1 * source2 (both signed)
8449 * 05 Unsigned bit shift, source1 by source2 (source2 taken to be a
8450 * signed quantity; left shift if positive and right shift if
8451 * negative, source1 is unsigned)
8452 * 06 Signed bit shift, source1 by source2
8453 * (source2 like in 05, and source1 as well)
8456 uint8_t target = buf.ReadByte();
8457 uint8_t oper = buf.ReadByte();
8458 uint32_t src1 = buf.ReadByte();
8459 uint32_t src2 = buf.ReadByte();
8461 uint32_t data = 0;
8462 if (buf.Remaining() >= 4) data = buf.ReadDWord();
8464 /* You can add 80 to the operation to make it apply only if the target
8465 * is not defined yet. In this respect, a parameter is taken to be
8466 * defined if any of the following applies:
8467 * - it has been set to any value in the newgrf(w).cfg parameter list
8468 * - it OR A PARAMETER WITH HIGHER NUMBER has been set to any value by
8469 * an earlier action D */
8470 if (HasBit(oper, 7)) {
8471 if (target < 0x80 && target < std::size(_cur.grffile->param)) {
8472 GrfMsg(7, "ParamSet: Param {} already defined, skipping", target);
8473 return;
8476 oper = GB(oper, 0, 7);
8479 if (src2 == 0xFE) {
8480 if (GB(data, 0, 8) == 0xFF) {
8481 if (data == 0x0000FFFF) {
8482 /* Patch variables */
8483 src1 = GetPatchVariable(src1);
8484 } else {
8485 /* GRF Resource Management */
8486 uint8_t op = src1;
8487 GrfSpecFeatureRef feature_ref = ReadFeature(GB(data, 8, 8));
8488 GrfSpecFeature feature = feature_ref.id;
8489 uint16_t count = GB(data, 16, 16);
8491 if (_cur.stage == GLS_RESERVE) {
8492 if (feature == 0x08) {
8493 /* General sprites */
8494 if (op == 0) {
8495 /* Check if the allocated sprites will fit below the original sprite limit */
8496 if (_cur.spriteid + count >= 16384) {
8497 GrfMsg(0, "ParamSet: GRM: Unable to allocate {} sprites; try changing NewGRF order", count);
8498 DisableGrf(STR_NEWGRF_ERROR_GRM_FAILED);
8499 return;
8502 /* Reserve space at the current sprite ID */
8503 GrfMsg(4, "ParamSet: GRM: Allocated {} sprites at {}", count, _cur.spriteid);
8504 _grm_sprites[GRFLocation(_cur.grffile->grfid, _cur.nfo_line)] = std::make_pair(_cur.spriteid, count);
8505 _cur.spriteid += count;
8508 /* Ignore GRM result during reservation */
8509 src1 = 0;
8510 } else if (_cur.stage == GLS_ACTIVATION) {
8511 switch (feature) {
8512 case 0x00: // Trains
8513 case 0x01: // Road Vehicles
8514 case 0x02: // Ships
8515 case 0x03: // Aircraft
8516 if (!_settings_game.vehicle.dynamic_engines) {
8517 src1 = PerformGRM(&_grm_engines[_engine_offsets[feature]], _engine_counts[feature], count, op, target, "vehicles");
8518 if (_cur.skip_sprites == -1) return;
8519 } else {
8520 /* GRM does not apply for dynamic engine allocation. */
8521 switch (op) {
8522 case 2:
8523 case 3:
8524 src1 = _cur.grffile->GetParam(target);
8525 break;
8527 default:
8528 src1 = 0;
8529 break;
8532 break;
8534 case 0x08: // General sprites
8535 switch (op) {
8536 case 0: {
8537 /* Return space reserved during reservation stage */
8538 const auto &grm_alloc = _grm_sprites[GRFLocation(_cur.grffile->grfid, _cur.nfo_line)];
8539 src1 = grm_alloc.first;
8540 GrfMsg(4, "ParamSet: GRM: Using pre-allocated sprites at {} (count: {})", src1, grm_alloc.second);
8541 break;
8544 case 1:
8545 src1 = _cur.spriteid;
8546 break;
8548 default:
8549 GrfMsg(1, "ParamSet: GRM: Unsupported operation {} for general sprites", op);
8550 return;
8552 break;
8554 case 0x0B: // Cargo
8555 /* There are two ranges: one for cargo IDs and one for cargo bitmasks */
8556 src1 = PerformGRM(_grm_cargoes, NUM_CARGO * 2, count, op, target, "cargoes");
8557 if (_cur.skip_sprites == -1) return;
8558 break;
8560 default: GrfMsg(1, "ParamSet: GRM: Unsupported feature {}", GetFeatureString(feature_ref)); return;
8562 } else {
8563 /* Ignore GRM during initialization */
8564 src1 = 0;
8567 } else {
8568 /* Read another GRF File's parameter */
8569 const GRFFile *file = GetFileByGRFID(data);
8570 GRFConfig *c = GetGRFConfig(data);
8571 if (c != nullptr && HasBit(c->flags, GCF_STATIC) && !HasBit(_cur.grfconfig->flags, GCF_STATIC) && _networking) {
8572 /* Disable the read GRF if it is a static NewGRF. */
8573 DisableStaticNewGRFInfluencingNonStaticNewGRFs(c);
8574 src1 = 0;
8575 } else if (file == nullptr || c == nullptr || c->status == GCS_DISABLED) {
8576 src1 = 0;
8577 } else if (src1 == 0xFE) {
8578 src1 = c->version;
8579 } else {
8580 src1 = file->GetParam(src1);
8583 } else {
8584 /* The source1 and source2 operands refer to the grf parameter number
8585 * like in action 6 and 7. In addition, they can refer to the special
8586 * variables available in action 7, or they can be FF to use the value
8587 * of <data>. If referring to parameters that are undefined, a value
8588 * of 0 is used instead. */
8589 src1 = (src1 == 0xFF) ? data : GetParamVal(src1, nullptr);
8590 src2 = (src2 == 0xFF) ? data : GetParamVal(src2, nullptr);
8593 uint32_t res;
8594 switch (oper) {
8595 case 0x00:
8596 res = src1;
8597 break;
8599 case 0x01:
8600 res = src1 + src2;
8601 break;
8603 case 0x02:
8604 res = src1 - src2;
8605 break;
8607 case 0x03:
8608 res = src1 * src2;
8609 break;
8611 case 0x04:
8612 res = (int32_t)src1 * (int32_t)src2;
8613 break;
8615 case 0x05:
8616 if ((int32_t)src2 < 0) {
8617 res = src1 >> -(int32_t)src2;
8618 } else {
8619 res = src1 << (src2 & 0x1F); // Same behaviour as in EvalAdjustT, mask 'value' to 5 bits, which should behave the same on all architectures.
8621 break;
8623 case 0x06:
8624 if ((int32_t)src2 < 0) {
8625 res = (int32_t)src1 >> -(int32_t)src2;
8626 } else {
8627 res = (int32_t)src1 << (src2 & 0x1F); // Same behaviour as in EvalAdjustT, mask 'value' to 5 bits, which should behave the same on all architectures.
8629 break;
8631 case 0x07: // Bitwise AND
8632 res = src1 & src2;
8633 break;
8635 case 0x08: // Bitwise OR
8636 res = src1 | src2;
8637 break;
8639 case 0x09: // Unsigned division
8640 if (src2 == 0) {
8641 res = src1;
8642 } else {
8643 res = src1 / src2;
8645 break;
8647 case 0x0A: // Signed division
8648 if (src2 == 0) {
8649 res = src1;
8650 } else {
8651 res = (int32_t)src1 / (int32_t)src2;
8653 break;
8655 case 0x0B: // Unsigned modulo
8656 if (src2 == 0) {
8657 res = src1;
8658 } else {
8659 res = src1 % src2;
8661 break;
8663 case 0x0C: // Signed modulo
8664 if (src2 == 0) {
8665 res = src1;
8666 } else {
8667 res = (int32_t)src1 % (int32_t)src2;
8669 break;
8671 default: GrfMsg(0, "ParamSet: Unknown operation {}, skipping", oper); return;
8674 switch (target) {
8675 case 0x8E: // Y-Offset for train sprites
8676 _cur.grffile->traininfo_vehicle_pitch = res;
8677 break;
8679 case 0x8F: { // Rail track type cost factors
8680 extern RailTypeInfo _railtypes[RAILTYPE_END];
8681 _railtypes[RAILTYPE_RAIL].cost_multiplier = GB(res, 0, 8);
8682 if (_settings_game.vehicle.disable_elrails) {
8683 _railtypes[RAILTYPE_ELECTRIC].cost_multiplier = GB(res, 0, 8);
8684 _railtypes[RAILTYPE_MONO].cost_multiplier = GB(res, 8, 8);
8685 } else {
8686 _railtypes[RAILTYPE_ELECTRIC].cost_multiplier = GB(res, 8, 8);
8687 _railtypes[RAILTYPE_MONO].cost_multiplier = GB(res, 16, 8);
8689 _railtypes[RAILTYPE_MAGLEV].cost_multiplier = GB(res, 16, 8);
8690 break;
8693 /* not implemented */
8694 case 0x93: // Tile refresh offset to left -- Intended to allow support for larger sprites, not necessary for OTTD
8695 case 0x94: // Tile refresh offset to right
8696 case 0x95: // Tile refresh offset upwards
8697 case 0x96: // Tile refresh offset downwards
8698 case 0x97: // Snow line height -- Better supported by feature 8 property 10h (snow line table) TODO: implement by filling the entire snow line table with the given value
8699 case 0x99: // Global ID offset -- Not necessary since IDs are remapped automatically
8700 GrfMsg(7, "ParamSet: Skipping unimplemented target 0x{:02X}", target);
8701 break;
8703 case 0x9E: // Miscellaneous GRF features
8704 /* Set train list engine width */
8705 _cur.grffile->traininfo_vehicle_width = HasBit(res, GMB_TRAIN_WIDTH_32_PIXELS) ? VEHICLEINFO_FULL_VEHICLE_WIDTH : TRAININFO_DEFAULT_VEHICLE_WIDTH;
8706 /* Remove the local flags from the global flags */
8707 ClrBit(res, GMB_TRAIN_WIDTH_32_PIXELS);
8709 /* Only copy safe bits for static grfs */
8710 if (HasBit(_cur.grfconfig->flags, GCF_STATIC)) {
8711 uint32_t safe_bits = 0;
8712 SetBit(safe_bits, GMB_SECOND_ROCKY_TILE_SET);
8714 _misc_grf_features = (_misc_grf_features & ~safe_bits) | (res & safe_bits);
8715 } else {
8716 _misc_grf_features = res;
8718 break;
8720 case 0x9F: // locale-dependent settings
8721 GrfMsg(7, "ParamSet: Skipping unimplemented target 0x{:02X}", target);
8722 break;
8724 default:
8725 if (target < 0x80) {
8726 /* Resize (and fill with zeroes) if needed. */
8727 if (target >= std::size(_cur.grffile->param)) _cur.grffile->param.resize(target + 1);
8728 _cur.grffile->param[target] = res;
8729 } else {
8730 GrfMsg(7, "ParamSet: Skipping unknown target 0x{:02X}", target);
8732 break;
8736 /* Action 0x0E (GLS_SAFETYSCAN) */
8737 static void SafeGRFInhibit(ByteReader &buf)
8739 /* <0E> <num> <grfids...>
8741 * B num Number of GRFIDs that follow
8742 * D grfids GRFIDs of the files to deactivate */
8744 uint8_t num = buf.ReadByte();
8746 for (uint i = 0; i < num; i++) {
8747 uint32_t grfid = buf.ReadDWord();
8749 /* GRF is unsafe it if tries to deactivate other GRFs */
8750 if (grfid != _cur.grfconfig->ident.grfid) {
8751 SetBit(_cur.grfconfig->flags, GCF_UNSAFE);
8753 /* Skip remainder of GRF */
8754 _cur.skip_sprites = -1;
8756 return;
8761 /* Action 0x0E */
8762 static void GRFInhibit(ByteReader &buf)
8764 /* <0E> <num> <grfids...>
8766 * B num Number of GRFIDs that follow
8767 * D grfids GRFIDs of the files to deactivate */
8769 uint8_t num = buf.ReadByte();
8771 for (uint i = 0; i < num; i++) {
8772 uint32_t grfid = buf.ReadDWord();
8773 GRFConfig *file = GetGRFConfig(grfid);
8775 /* Unset activation flag */
8776 if (file != nullptr && file != _cur.grfconfig) {
8777 GrfMsg(2, "GRFInhibit: Deactivating file '{}'", file->GetDisplayPath());
8778 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_FORCEFULLY_DISABLED, file);
8779 error->data = _cur.grfconfig->GetName();
8784 /** Action 0x0F - Define Town names */
8785 static void FeatureTownName(ByteReader &buf)
8787 /* <0F> <id> <style-name> <num-parts> <parts>
8789 * B id ID of this definition in bottom 7 bits (final definition if bit 7 set)
8790 * V style-name Name of the style (only for final definition)
8791 * B num-parts Number of parts in this definition
8792 * V parts The parts */
8794 uint32_t grfid = _cur.grffile->grfid;
8796 GRFTownName *townname = AddGRFTownName(grfid);
8798 uint8_t id = buf.ReadByte();
8799 GrfMsg(6, "FeatureTownName: definition 0x{:02X}", id & 0x7F);
8801 if (HasBit(id, 7)) {
8802 /* Final definition */
8803 ClrBit(id, 7);
8804 bool new_scheme = _cur.grffile->grf_version >= 7;
8806 uint8_t lang = buf.ReadByte();
8807 StringID style = STR_UNDEFINED;
8809 do {
8810 ClrBit(lang, 7);
8812 std::string_view name = buf.ReadString();
8814 std::string lang_name = TranslateTTDPatchCodes(grfid, lang, false, name);
8815 GrfMsg(6, "FeatureTownName: lang 0x{:X} -> '{}'", lang, lang_name);
8817 style = AddGRFString(grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
8819 lang = buf.ReadByte();
8820 } while (lang != 0);
8821 townname->styles.emplace_back(style, id);
8824 uint8_t parts = buf.ReadByte();
8825 GrfMsg(6, "FeatureTownName: {} parts", parts);
8827 townname->partlists[id].reserve(parts);
8828 for (uint partnum = 0; partnum < parts; partnum++) {
8829 NamePartList &partlist = townname->partlists[id].emplace_back();
8830 uint8_t texts = buf.ReadByte();
8831 partlist.bitstart = buf.ReadByte();
8832 partlist.bitcount = buf.ReadByte();
8833 partlist.maxprob = 0;
8834 GrfMsg(6, "FeatureTownName: part {} contains {} texts and will use GB(seed, {}, {})", partnum, texts, partlist.bitstart, partlist.bitcount);
8836 partlist.parts.reserve(texts);
8837 for (uint textnum = 0; textnum < texts; textnum++) {
8838 NamePart &part = partlist.parts.emplace_back();
8839 part.prob = buf.ReadByte();
8841 if (HasBit(part.prob, 7)) {
8842 uint8_t ref_id = buf.ReadByte();
8843 if (ref_id >= GRFTownName::MAX_LISTS || townname->partlists[ref_id].empty()) {
8844 GrfMsg(0, "FeatureTownName: definition 0x{:02X} doesn't exist, deactivating", ref_id);
8845 DelGRFTownName(grfid);
8846 DisableGrf(STR_NEWGRF_ERROR_INVALID_ID);
8847 return;
8849 part.id = ref_id;
8850 GrfMsg(6, "FeatureTownName: part {}, text {}, uses intermediate definition 0x{:02X} (with probability {})", partnum, textnum, ref_id, part.prob & 0x7F);
8851 } else {
8852 std::string_view text = buf.ReadString();
8853 part.text = TranslateTTDPatchCodes(grfid, 0, false, text);
8854 GrfMsg(6, "FeatureTownName: part {}, text {}, '{}' (with probability {})", partnum, textnum, part.text, part.prob);
8856 partlist.maxprob += GB(part.prob, 0, 7);
8858 GrfMsg(6, "FeatureTownName: part {}, total probability {}", partnum, partlist.maxprob);
8862 /** Action 0x10 - Define goto label */
8863 static void DefineGotoLabel(ByteReader &buf)
8865 /* <10> <label> [<comment>]
8867 * B label The label to define
8868 * V comment Optional comment - ignored */
8870 uint8_t nfo_label = buf.ReadByte();
8872 _cur.grffile->labels.emplace_back(nfo_label, _cur.nfo_line, _cur.file->GetPos());
8874 GrfMsg(2, "DefineGotoLabel: GOTO target with label 0x{:02X}", nfo_label);
8878 * Process a sound import from another GRF file.
8879 * @param sound Destination for sound.
8881 static void ImportGRFSound(SoundEntry *sound)
8883 const GRFFile *file;
8884 uint32_t grfid = _cur.file->ReadDword();
8885 SoundID sound_id = _cur.file->ReadWord();
8887 file = GetFileByGRFID(grfid);
8888 if (file == nullptr || file->sound_offset == 0) {
8889 GrfMsg(1, "ImportGRFSound: Source file not available");
8890 return;
8893 if (sound_id >= file->num_sounds) {
8894 GrfMsg(1, "ImportGRFSound: Sound effect {} is invalid", sound_id);
8895 return;
8898 GrfMsg(2, "ImportGRFSound: Copying sound {} ({}) from file {:x}", sound_id, file->sound_offset + sound_id, grfid);
8900 *sound = *GetSound(file->sound_offset + sound_id);
8902 /* Reset volume and priority, which TTDPatch doesn't copy */
8903 sound->volume = SOUND_EFFECT_MAX_VOLUME;
8904 sound->priority = 0;
8908 * Load a sound from a file.
8909 * @param offs File offset to read sound from.
8910 * @param sound Destination for sound.
8912 static void LoadGRFSound(size_t offs, SoundEntry *sound)
8914 /* Set default volume and priority */
8915 sound->volume = SOUND_EFFECT_MAX_VOLUME;
8916 sound->priority = 0;
8918 if (offs != SIZE_MAX) {
8919 /* Sound is present in the NewGRF. */
8920 sound->file = _cur.file;
8921 sound->file_offset = offs;
8922 sound->source = SoundSource::NewGRF;
8923 sound->grf_container_ver = _cur.file->GetContainerVersion();
8927 /* Action 0x11 */
8928 static void GRFSound(ByteReader &buf)
8930 /* <11> <num>
8932 * W num Number of sound files that follow */
8934 uint16_t num = buf.ReadWord();
8935 if (num == 0) return;
8937 SoundEntry *sound;
8938 if (_cur.grffile->sound_offset == 0) {
8939 _cur.grffile->sound_offset = GetNumSounds();
8940 _cur.grffile->num_sounds = num;
8941 sound = AllocateSound(num);
8942 } else {
8943 sound = GetSound(_cur.grffile->sound_offset);
8946 SpriteFile &file = *_cur.file;
8947 uint8_t grf_container_version = file.GetContainerVersion();
8948 for (int i = 0; i < num; i++) {
8949 _cur.nfo_line++;
8951 /* Check whether the index is in range. This might happen if multiple action 11 are present.
8952 * While this is invalid, we do not check for this. But we should prevent it from causing bigger trouble */
8953 bool invalid = i >= _cur.grffile->num_sounds;
8955 size_t offs = file.GetPos();
8957 uint32_t len = grf_container_version >= 2 ? file.ReadDword() : file.ReadWord();
8958 uint8_t type = file.ReadByte();
8960 if (grf_container_version >= 2 && type == 0xFD) {
8961 /* Reference to sprite section. */
8962 if (invalid) {
8963 GrfMsg(1, "GRFSound: Sound index out of range (multiple Action 11?)");
8964 file.SkipBytes(len);
8965 } else if (len != 4) {
8966 GrfMsg(1, "GRFSound: Invalid sprite section import");
8967 file.SkipBytes(len);
8968 } else {
8969 uint32_t id = file.ReadDword();
8970 if (_cur.stage == GLS_INIT) LoadGRFSound(GetGRFSpriteOffset(id), sound + i);
8972 continue;
8975 if (type != 0xFF) {
8976 GrfMsg(1, "GRFSound: Unexpected RealSprite found, skipping");
8977 file.SkipBytes(7);
8978 SkipSpriteData(*_cur.file, type, len - 8);
8979 continue;
8982 if (invalid) {
8983 GrfMsg(1, "GRFSound: Sound index out of range (multiple Action 11?)");
8984 file.SkipBytes(len);
8987 uint8_t action = file.ReadByte();
8988 switch (action) {
8989 case 0xFF:
8990 /* Allocate sound only in init stage. */
8991 if (_cur.stage == GLS_INIT) {
8992 if (grf_container_version >= 2) {
8993 GrfMsg(1, "GRFSound: Inline sounds are not supported for container version >= 2");
8994 } else {
8995 LoadGRFSound(offs, sound + i);
8998 file.SkipBytes(len - 1); // already read <action>
8999 break;
9001 case 0xFE:
9002 if (_cur.stage == GLS_ACTIVATION) {
9003 /* XXX 'Action 0xFE' isn't really specified. It is only mentioned for
9004 * importing sounds, so this is probably all wrong... */
9005 if (file.ReadByte() != 0) GrfMsg(1, "GRFSound: Import type mismatch");
9006 ImportGRFSound(sound + i);
9007 } else {
9008 file.SkipBytes(len - 1); // already read <action>
9010 break;
9012 default:
9013 GrfMsg(1, "GRFSound: Unexpected Action {:x} found, skipping", action);
9014 file.SkipBytes(len - 1); // already read <action>
9015 break;
9020 /* Action 0x11 (SKIP) */
9021 static void SkipAct11(ByteReader &buf)
9023 /* <11> <num>
9025 * W num Number of sound files that follow */
9027 _cur.skip_sprites = buf.ReadWord();
9029 GrfMsg(3, "SkipAct11: Skipping {} sprites", _cur.skip_sprites);
9032 /** Action 0x12 */
9033 static void LoadFontGlyph(ByteReader &buf)
9035 /* <12> <num_def> <font_size> <num_char> <base_char>
9037 * B num_def Number of definitions
9038 * B font_size Size of font (0 = normal, 1 = small, 2 = large, 3 = mono)
9039 * B num_char Number of consecutive glyphs
9040 * W base_char First character index */
9042 uint8_t num_def = buf.ReadByte();
9044 for (uint i = 0; i < num_def; i++) {
9045 FontSize size = (FontSize)buf.ReadByte();
9046 uint8_t num_char = buf.ReadByte();
9047 uint16_t base_char = buf.ReadWord();
9049 if (size >= FS_END) {
9050 GrfMsg(1, "LoadFontGlyph: Size {} is not supported, ignoring", size);
9053 GrfMsg(7, "LoadFontGlyph: Loading {} glyph(s) at 0x{:04X} for size {}", num_char, base_char, size);
9055 for (uint c = 0; c < num_char; c++) {
9056 if (size < FS_END) SetUnicodeGlyph(size, base_char + c, _cur.spriteid);
9057 _cur.nfo_line++;
9058 LoadNextSprite(_cur.spriteid++, *_cur.file, _cur.nfo_line);
9063 /** Action 0x12 (SKIP) */
9064 static void SkipAct12(ByteReader &buf)
9066 /* <12> <num_def> <font_size> <num_char> <base_char>
9068 * B num_def Number of definitions
9069 * B font_size Size of font (0 = normal, 1 = small, 2 = large)
9070 * B num_char Number of consecutive glyphs
9071 * W base_char First character index */
9073 uint8_t num_def = buf.ReadByte();
9075 for (uint i = 0; i < num_def; i++) {
9076 /* Ignore 'size' byte */
9077 buf.ReadByte();
9079 /* Sum up number of characters */
9080 _cur.skip_sprites += buf.ReadByte();
9082 /* Ignore 'base_char' word */
9083 buf.ReadWord();
9086 GrfMsg(3, "SkipAct12: Skipping {} sprites", _cur.skip_sprites);
9089 /** Action 0x13 */
9090 static void TranslateGRFStrings(ByteReader &buf)
9092 /* <13> <grfid> <num-ent> <offset> <text...>
9094 * 4*B grfid The GRFID of the file whose texts are to be translated
9095 * B num-ent Number of strings
9096 * W offset First text ID
9097 * S text... Zero-terminated strings */
9099 uint32_t grfid = buf.ReadDWord();
9100 const GRFConfig *c = GetGRFConfig(grfid);
9101 if (c == nullptr || (c->status != GCS_INITIALISED && c->status != GCS_ACTIVATED)) {
9102 GrfMsg(7, "TranslateGRFStrings: GRFID 0x{:08X} unknown, skipping action 13", BSWAP32(grfid));
9103 return;
9106 if (c->status == GCS_INITIALISED) {
9107 /* If the file is not active but will be activated later, give an error
9108 * and disable this file. */
9109 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LOAD_AFTER);
9111 error->data = GetString(STR_NEWGRF_ERROR_AFTER_TRANSLATED_FILE);
9113 return;
9116 /* Since no language id is supplied for with version 7 and lower NewGRFs, this string has
9117 * to be added as a generic string, thus the language id of 0x7F. For this to work
9118 * new_scheme has to be true as well, which will also be implicitly the case for version 8
9119 * and higher. A language id of 0x7F will be overridden by a non-generic id, so this will
9120 * not change anything if a string has been provided specifically for this language. */
9121 uint8_t language = _cur.grffile->grf_version >= 8 ? buf.ReadByte() : 0x7F;
9122 uint8_t num_strings = buf.ReadByte();
9123 uint16_t first_id = buf.ReadWord();
9125 if (!((first_id >= 0xD000 && first_id + num_strings <= 0xD400) || (first_id >= 0xD800 && first_id + num_strings <= 0xE000))) {
9126 GrfMsg(7, "TranslateGRFStrings: Attempting to set out-of-range string IDs in action 13 (first: 0x{:04X}, number: 0x{:02X})", first_id, num_strings);
9127 return;
9130 for (uint i = 0; i < num_strings && buf.HasData(); i++) {
9131 std::string_view string = buf.ReadString();
9133 if (string.empty()) {
9134 GrfMsg(7, "TranslateGRFString: Ignoring empty string.");
9135 continue;
9138 AddGRFString(grfid, first_id + i, language, true, true, string, STR_UNDEFINED);
9142 /** Callback function for 'INFO'->'NAME' to add a translation to the newgrf name. */
9143 static bool ChangeGRFName(uint8_t langid, std::string_view str)
9145 AddGRFTextToList(_cur.grfconfig->name, langid, _cur.grfconfig->ident.grfid, false, str);
9146 return true;
9149 /** Callback function for 'INFO'->'DESC' to add a translation to the newgrf description. */
9150 static bool ChangeGRFDescription(uint8_t langid, std::string_view str)
9152 AddGRFTextToList(_cur.grfconfig->info, langid, _cur.grfconfig->ident.grfid, true, str);
9153 return true;
9156 /** Callback function for 'INFO'->'URL_' to set the newgrf url. */
9157 static bool ChangeGRFURL(uint8_t langid, std::string_view str)
9159 AddGRFTextToList(_cur.grfconfig->url, langid, _cur.grfconfig->ident.grfid, false, str);
9160 return true;
9163 /** Callback function for 'INFO'->'NPAR' to set the number of valid parameters. */
9164 static bool ChangeGRFNumUsedParams(size_t len, ByteReader &buf)
9166 if (len != 1) {
9167 GrfMsg(2, "StaticGRFInfo: expected only 1 byte for 'INFO'->'NPAR' but got {}, ignoring this field", len);
9168 buf.Skip(len);
9169 } else {
9170 _cur.grfconfig->num_valid_params = std::min(buf.ReadByte(), GRFConfig::MAX_NUM_PARAMS);
9172 return true;
9175 /** Callback function for 'INFO'->'PALS' to set the number of valid parameters. */
9176 static bool ChangeGRFPalette(size_t len, ByteReader &buf)
9178 if (len != 1) {
9179 GrfMsg(2, "StaticGRFInfo: expected only 1 byte for 'INFO'->'PALS' but got {}, ignoring this field", len);
9180 buf.Skip(len);
9181 } else {
9182 char data = buf.ReadByte();
9183 GRFPalette pal = GRFP_GRF_UNSET;
9184 switch (data) {
9185 case '*':
9186 case 'A': pal = GRFP_GRF_ANY; break;
9187 case 'W': pal = GRFP_GRF_WINDOWS; break;
9188 case 'D': pal = GRFP_GRF_DOS; break;
9189 default:
9190 GrfMsg(2, "StaticGRFInfo: unexpected value '{:02X}' for 'INFO'->'PALS', ignoring this field", data);
9191 break;
9193 if (pal != GRFP_GRF_UNSET) {
9194 _cur.grfconfig->palette &= ~GRFP_GRF_MASK;
9195 _cur.grfconfig->palette |= pal;
9198 return true;
9201 /** Callback function for 'INFO'->'BLTR' to set the blitter info. */
9202 static bool ChangeGRFBlitter(size_t len, ByteReader &buf)
9204 if (len != 1) {
9205 GrfMsg(2, "StaticGRFInfo: expected only 1 byte for 'INFO'->'BLTR' but got {}, ignoring this field", len);
9206 buf.Skip(len);
9207 } else {
9208 char data = buf.ReadByte();
9209 GRFPalette pal = GRFP_BLT_UNSET;
9210 switch (data) {
9211 case '8': pal = GRFP_BLT_UNSET; break;
9212 case '3': pal = GRFP_BLT_32BPP; break;
9213 default:
9214 GrfMsg(2, "StaticGRFInfo: unexpected value '{:02X}' for 'INFO'->'BLTR', ignoring this field", data);
9215 return true;
9217 _cur.grfconfig->palette &= ~GRFP_BLT_MASK;
9218 _cur.grfconfig->palette |= pal;
9220 return true;
9223 /** Callback function for 'INFO'->'VRSN' to the version of the NewGRF. */
9224 static bool ChangeGRFVersion(size_t len, ByteReader &buf)
9226 if (len != 4) {
9227 GrfMsg(2, "StaticGRFInfo: expected 4 bytes for 'INFO'->'VRSN' but got {}, ignoring this field", len);
9228 buf.Skip(len);
9229 } else {
9230 /* Set min_loadable_version as well (default to minimal compatibility) */
9231 _cur.grfconfig->version = _cur.grfconfig->min_loadable_version = buf.ReadDWord();
9233 return true;
9236 /** Callback function for 'INFO'->'MINV' to the minimum compatible version of the NewGRF. */
9237 static bool ChangeGRFMinVersion(size_t len, ByteReader &buf)
9239 if (len != 4) {
9240 GrfMsg(2, "StaticGRFInfo: expected 4 bytes for 'INFO'->'MINV' but got {}, ignoring this field", len);
9241 buf.Skip(len);
9242 } else {
9243 _cur.grfconfig->min_loadable_version = buf.ReadDWord();
9244 if (_cur.grfconfig->version == 0) {
9245 GrfMsg(2, "StaticGRFInfo: 'MINV' defined before 'VRSN' or 'VRSN' set to 0, ignoring this field");
9246 _cur.grfconfig->min_loadable_version = 0;
9248 if (_cur.grfconfig->version < _cur.grfconfig->min_loadable_version) {
9249 GrfMsg(2, "StaticGRFInfo: 'MINV' defined as {}, limiting it to 'VRSN'", _cur.grfconfig->min_loadable_version);
9250 _cur.grfconfig->min_loadable_version = _cur.grfconfig->version;
9253 return true;
9256 static GRFParameterInfo *_cur_parameter; ///< The parameter which info is currently changed by the newgrf.
9258 /** Callback function for 'INFO'->'PARAM'->param_num->'NAME' to set the name of a parameter. */
9259 static bool ChangeGRFParamName(uint8_t langid, std::string_view str)
9261 AddGRFTextToList(_cur_parameter->name, langid, _cur.grfconfig->ident.grfid, false, str);
9262 return true;
9265 /** Callback function for 'INFO'->'PARAM'->param_num->'DESC' to set the description of a parameter. */
9266 static bool ChangeGRFParamDescription(uint8_t langid, std::string_view str)
9268 AddGRFTextToList(_cur_parameter->desc, langid, _cur.grfconfig->ident.grfid, true, str);
9269 return true;
9272 /** Callback function for 'INFO'->'PARAM'->param_num->'TYPE' to set the typeof a parameter. */
9273 static bool ChangeGRFParamType(size_t len, ByteReader &buf)
9275 if (len != 1) {
9276 GrfMsg(2, "StaticGRFInfo: expected 1 byte for 'INFO'->'PARA'->'TYPE' but got {}, ignoring this field", len);
9277 buf.Skip(len);
9278 } else {
9279 uint8_t type = buf.ReadByte();
9280 if (type < PTYPE_END) {
9281 _cur_parameter->type = (GRFParameterType)type;
9282 } else {
9283 GrfMsg(3, "StaticGRFInfo: unknown parameter type {}, ignoring this field", type);
9286 return true;
9289 /** Callback function for 'INFO'->'PARAM'->param_num->'LIMI' to set the min/max value of a parameter. */
9290 static bool ChangeGRFParamLimits(size_t len, ByteReader &buf)
9292 if (_cur_parameter->type != PTYPE_UINT_ENUM) {
9293 GrfMsg(2, "StaticGRFInfo: 'INFO'->'PARA'->'LIMI' is only valid for parameters with type uint/enum, ignoring this field");
9294 buf.Skip(len);
9295 } else if (len != 8) {
9296 GrfMsg(2, "StaticGRFInfo: expected 8 bytes for 'INFO'->'PARA'->'LIMI' but got {}, ignoring this field", len);
9297 buf.Skip(len);
9298 } else {
9299 uint32_t min_value = buf.ReadDWord();
9300 uint32_t max_value = buf.ReadDWord();
9301 if (min_value <= max_value) {
9302 _cur_parameter->min_value = min_value;
9303 _cur_parameter->max_value = max_value;
9304 } else {
9305 GrfMsg(2, "StaticGRFInfo: 'INFO'->'PARA'->'LIMI' values are incoherent, ignoring this field");
9308 return true;
9311 /** Callback function for 'INFO'->'PARAM'->param_num->'MASK' to set the parameter and bits to use. */
9312 static bool ChangeGRFParamMask(size_t len, ByteReader &buf)
9314 if (len < 1 || len > 3) {
9315 GrfMsg(2, "StaticGRFInfo: expected 1 to 3 bytes for 'INFO'->'PARA'->'MASK' but got {}, ignoring this field", len);
9316 buf.Skip(len);
9317 } else {
9318 uint8_t param_nr = buf.ReadByte();
9319 if (param_nr >= GRFConfig::MAX_NUM_PARAMS) {
9320 GrfMsg(2, "StaticGRFInfo: invalid parameter number in 'INFO'->'PARA'->'MASK', param {}, ignoring this field", param_nr);
9321 buf.Skip(len - 1);
9322 } else {
9323 _cur_parameter->param_nr = param_nr;
9324 if (len >= 2) _cur_parameter->first_bit = std::min<uint8_t>(buf.ReadByte(), 31);
9325 if (len >= 3) _cur_parameter->num_bit = std::min<uint8_t>(buf.ReadByte(), 32 - _cur_parameter->first_bit);
9329 return true;
9332 /** Callback function for 'INFO'->'PARAM'->param_num->'DFLT' to set the default value. */
9333 static bool ChangeGRFParamDefault(size_t len, ByteReader &buf)
9335 if (len != 4) {
9336 GrfMsg(2, "StaticGRFInfo: expected 4 bytes for 'INFO'->'PARA'->'DEFA' but got {}, ignoring this field", len);
9337 buf.Skip(len);
9338 } else {
9339 _cur_parameter->def_value = buf.ReadDWord();
9341 _cur.grfconfig->has_param_defaults = true;
9342 return true;
9345 typedef bool (*DataHandler)(size_t, ByteReader &); ///< Type of callback function for binary nodes
9346 typedef bool (*TextHandler)(uint8_t, std::string_view str); ///< Type of callback function for text nodes
9347 typedef bool (*BranchHandler)(ByteReader &); ///< Type of callback function for branch nodes
9350 * Data structure to store the allowed id/type combinations for action 14. The
9351 * data can be represented as a tree with 3 types of nodes:
9352 * 1. Branch nodes (identified by 'C' for choice).
9353 * 2. Binary leaf nodes (identified by 'B').
9354 * 3. Text leaf nodes (identified by 'T').
9356 struct AllowedSubtags {
9357 /** Create empty subtags object used to identify the end of a list. */
9358 AllowedSubtags() :
9359 id(0),
9360 type(0)
9364 * Create a binary leaf node.
9365 * @param id The id for this node.
9366 * @param handler The callback function to call.
9368 AllowedSubtags(uint32_t id, DataHandler handler) :
9369 id(id),
9370 type('B')
9372 this->handler.data = handler;
9376 * Create a text leaf node.
9377 * @param id The id for this node.
9378 * @param handler The callback function to call.
9380 AllowedSubtags(uint32_t id, TextHandler handler) :
9381 id(id),
9382 type('T')
9384 this->handler.text = handler;
9388 * Create a branch node with a callback handler
9389 * @param id The id for this node.
9390 * @param handler The callback function to call.
9392 AllowedSubtags(uint32_t id, BranchHandler handler) :
9393 id(id),
9394 type('C')
9396 this->handler.call_handler = true;
9397 this->handler.u.branch = handler;
9401 * Create a branch node with a list of sub-nodes.
9402 * @param id The id for this node.
9403 * @param subtags Array with all valid subtags.
9405 AllowedSubtags(uint32_t id, AllowedSubtags *subtags) :
9406 id(id),
9407 type('C')
9409 this->handler.call_handler = false;
9410 this->handler.u.subtags = subtags;
9413 uint32_t id; ///< The identifier for this node
9414 uint8_t type; ///< The type of the node, must be one of 'C', 'B' or 'T'.
9415 union {
9416 DataHandler data; ///< Callback function for a binary node, only valid if type == 'B'.
9417 TextHandler text; ///< Callback function for a text node, only valid if type == 'T'.
9418 struct {
9419 union {
9420 BranchHandler branch; ///< Callback function for a branch node, only valid if type == 'C' && call_handler.
9421 AllowedSubtags *subtags; ///< Pointer to a list of subtags, only valid if type == 'C' && !call_handler.
9422 } u;
9423 bool call_handler; ///< True if there is a callback function for this node, false if there is a list of subnodes.
9425 } handler;
9428 static bool SkipUnknownInfo(ByteReader &buf, uint8_t type);
9429 static bool HandleNodes(ByteReader &buf, AllowedSubtags *tags);
9432 * Try to skip the current branch node and all subnodes.
9433 * This is suitable for use with AllowedSubtags.
9434 * @param buf Buffer.
9435 * @return True if we could skip the node, false if an error occurred.
9437 static bool SkipInfoChunk(ByteReader &buf)
9439 uint8_t type = buf.ReadByte();
9440 while (type != 0) {
9441 buf.ReadDWord(); // chunk ID
9442 if (!SkipUnknownInfo(buf, type)) return false;
9443 type = buf.ReadByte();
9445 return true;
9449 * Callback function for 'INFO'->'PARA'->param_num->'VALU' to set the names
9450 * of some parameter values (type uint/enum) or the names of some bits
9451 * (type bitmask). In both cases the format is the same:
9452 * Each subnode should be a text node with the value/bit number as id.
9454 static bool ChangeGRFParamValueNames(ByteReader &buf)
9456 uint8_t type = buf.ReadByte();
9457 while (type != 0) {
9458 uint32_t id = buf.ReadDWord();
9459 if (type != 'T' || id > _cur_parameter->max_value) {
9460 GrfMsg(2, "StaticGRFInfo: all child nodes of 'INFO'->'PARA'->param_num->'VALU' should have type 't' and the value/bit number as id");
9461 if (!SkipUnknownInfo(buf, type)) return false;
9462 type = buf.ReadByte();
9463 continue;
9466 uint8_t langid = buf.ReadByte();
9467 std::string_view name_string = buf.ReadString();
9469 auto val_name = _cur_parameter->value_names.find(id);
9470 if (val_name != _cur_parameter->value_names.end()) {
9471 AddGRFTextToList(val_name->second, langid, _cur.grfconfig->ident.grfid, false, name_string);
9472 } else {
9473 GRFTextList list;
9474 AddGRFTextToList(list, langid, _cur.grfconfig->ident.grfid, false, name_string);
9475 _cur_parameter->value_names[id] = list;
9478 type = buf.ReadByte();
9480 return true;
9483 /** Action14 parameter tags */
9484 AllowedSubtags _tags_parameters[] = {
9485 AllowedSubtags('NAME', ChangeGRFParamName),
9486 AllowedSubtags('DESC', ChangeGRFParamDescription),
9487 AllowedSubtags('TYPE', ChangeGRFParamType),
9488 AllowedSubtags('LIMI', ChangeGRFParamLimits),
9489 AllowedSubtags('MASK', ChangeGRFParamMask),
9490 AllowedSubtags('VALU', ChangeGRFParamValueNames),
9491 AllowedSubtags('DFLT', ChangeGRFParamDefault),
9492 AllowedSubtags()
9496 * Callback function for 'INFO'->'PARA' to set extra information about the
9497 * parameters. Each subnode of 'INFO'->'PARA' should be a branch node with
9498 * the parameter number as id. The first parameter has id 0. The maximum
9499 * parameter that can be changed is set by 'INFO'->'NPAR' which defaults to 80.
9501 static bool HandleParameterInfo(ByteReader &buf)
9503 uint8_t type = buf.ReadByte();
9504 while (type != 0) {
9505 uint32_t id = buf.ReadDWord();
9506 if (type != 'C' || id >= _cur.grfconfig->num_valid_params) {
9507 GrfMsg(2, "StaticGRFInfo: all child nodes of 'INFO'->'PARA' should have type 'C' and their parameter number as id");
9508 if (!SkipUnknownInfo(buf, type)) return false;
9509 type = buf.ReadByte();
9510 continue;
9513 if (id >= _cur.grfconfig->param_info.size()) {
9514 _cur.grfconfig->param_info.resize(id + 1);
9516 if (!_cur.grfconfig->param_info[id].has_value()) {
9517 _cur.grfconfig->param_info[id] = GRFParameterInfo(id);
9519 _cur_parameter = &_cur.grfconfig->param_info[id].value();
9520 /* Read all parameter-data and process each node. */
9521 if (!HandleNodes(buf, _tags_parameters)) return false;
9522 type = buf.ReadByte();
9524 return true;
9527 /** Action14 tags for the INFO node */
9528 AllowedSubtags _tags_info[] = {
9529 AllowedSubtags('NAME', ChangeGRFName),
9530 AllowedSubtags('DESC', ChangeGRFDescription),
9531 AllowedSubtags('URL_', ChangeGRFURL),
9532 AllowedSubtags('NPAR', ChangeGRFNumUsedParams),
9533 AllowedSubtags('PALS', ChangeGRFPalette),
9534 AllowedSubtags('BLTR', ChangeGRFBlitter),
9535 AllowedSubtags('VRSN', ChangeGRFVersion),
9536 AllowedSubtags('MINV', ChangeGRFMinVersion),
9537 AllowedSubtags('PARA', HandleParameterInfo),
9538 AllowedSubtags()
9542 /** Action14 feature test instance */
9543 struct GRFFeatureTest {
9544 const GRFFeatureInfo *feature;
9545 uint16_t min_version;
9546 uint16_t max_version;
9547 uint8_t platform_var_bit;
9548 uint32_t test_91_value;
9550 void Reset()
9552 this->feature = nullptr;
9553 this->min_version = 1;
9554 this->max_version = UINT16_MAX;
9555 this->platform_var_bit = 0;
9556 this->test_91_value = 0;
9559 void ExecuteTest()
9561 uint16_t version = (this->feature != nullptr) ? this->feature->version : 0;
9562 bool has_feature = (version >= this->min_version && version <= this->max_version);
9563 if (this->platform_var_bit > 0) {
9564 AssignBit(_cur.grffile->var9D_overlay, this->platform_var_bit, has_feature);
9565 GrfMsg(2, "Action 14 feature test: feature test: setting bit {} of var 0x9D to {}, {}", platform_var_bit, has_feature ? 1 : 0, _cur.grffile->var9D_overlay);
9567 if (this->test_91_value > 0) {
9568 if (has_feature) {
9569 GrfMsg(2, "Action 14 feature test: feature test: adding test value 0x{:X} to var 0x91", this->test_91_value);
9570 include(_cur.grffile->var91_values, this->test_91_value);
9571 } else {
9572 GrfMsg(2, "Action 14 feature test: feature test: not adding test value 0x{:X} to var 0x91", this->test_91_value);
9575 if (this->platform_var_bit == 0 && this->test_91_value == 0) {
9576 GrfMsg(2, "Action 14 feature test: feature test: doing nothing: {}", has_feature ? 1 : 0);
9578 if (this->feature != nullptr && this->feature->observation_flag != GFTOF_INVALID) {
9579 SetBit(_cur.grffile->observed_feature_tests, this->feature->observation_flag);
9584 static GRFFeatureTest _current_grf_feature_test;
9586 /** Callback function for 'FTST'->'NAME' to set the name of the feature being tested. */
9587 static bool ChangeGRFFeatureTestName(uint8_t langid, std::string_view str)
9589 extern const GRFFeatureInfo _grf_feature_list[];
9590 for (const GRFFeatureInfo *info = _grf_feature_list; info->name != nullptr; info++) {
9591 if (str == info->name) {
9592 _current_grf_feature_test.feature = info;
9593 GrfMsg(2, "Action 14 feature test: found feature named: '{}' (version: {}) in 'FTST'->'NAME'", StrMakeValid(str), info->version);
9594 return true;
9597 GrfMsg(2, "Action 14 feature test: could not find feature named: '{}' in 'FTST'->'NAME'", StrMakeValid(str));
9598 _current_grf_feature_test.feature = nullptr;
9599 return true;
9602 /** Callback function for 'FTST'->'MINV' to set the minimum version of the feature being tested. */
9603 static bool ChangeGRFFeatureMinVersion(size_t len, ByteReader &buf)
9605 if (len != 2) {
9606 GrfMsg(2, "Action 14 feature test: expected 2 bytes for 'FTST'->'MINV' but got {}, ignoring this field", len);
9607 buf.Skip(len);
9608 } else {
9609 _current_grf_feature_test.min_version = buf.ReadWord();
9611 return true;
9614 /** Callback function for 'FTST'->'MAXV' to set the maximum version of the feature being tested. */
9615 static bool ChangeGRFFeatureMaxVersion(size_t len, ByteReader &buf)
9617 if (len != 2) {
9618 GrfMsg(2, "Action 14 feature test: expected 2 bytes for 'FTST'->'MAXV' but got {}, ignoring this field", len);
9619 buf.Skip(len);
9620 } else {
9621 _current_grf_feature_test.max_version = buf.ReadWord();
9623 return true;
9626 /** Callback function for 'FTST'->'SETP' to set the bit number of global variable 9D (platform version) to set/unset with the result of the feature test. */
9627 static bool ChangeGRFFeatureSetPlatformVarBit(size_t len, ByteReader &buf)
9629 if (len != 1) {
9630 GrfMsg(2, "Action 14 feature test: expected 1 byte for 'FTST'->'SETP' but got {}, ignoring this field", len);
9631 buf.Skip(len);
9632 } else {
9633 uint8_t bit_number = buf.ReadByte();
9634 if (bit_number >= 4 && bit_number <= 31) {
9635 _current_grf_feature_test.platform_var_bit = bit_number;
9636 } else {
9637 GrfMsg(2, "Action 14 feature test: expected a bit number >= 4 and <= 32 for 'FTST'->'SETP' but got {}, ignoring this field", bit_number);
9640 return true;
9643 /** Callback function for 'FTST'->'SVAL' to add a test success result value for checking using global variable 91. */
9644 static bool ChangeGRFFeatureTestSuccessResultValue(size_t len, ByteReader &buf)
9646 if (len != 4) {
9647 GrfMsg(2, "Action 14 feature test: expected 4 bytes for 'FTST'->'SVAL' but got {}, ignoring this field", len);
9648 buf.Skip(len);
9649 } else {
9650 _current_grf_feature_test.test_91_value = buf.ReadDWord();
9652 return true;
9655 /** Action14 tags for the FTST node */
9656 AllowedSubtags _tags_ftst[] = {
9657 AllowedSubtags('NAME', ChangeGRFFeatureTestName),
9658 AllowedSubtags('MINV', ChangeGRFFeatureMinVersion),
9659 AllowedSubtags('MAXV', ChangeGRFFeatureMaxVersion),
9660 AllowedSubtags('SETP', ChangeGRFFeatureSetPlatformVarBit),
9661 AllowedSubtags('SVAL', ChangeGRFFeatureTestSuccessResultValue),
9662 AllowedSubtags()
9666 * Callback function for 'FTST' (feature test)
9668 static bool HandleFeatureTestInfo(ByteReader &buf)
9670 _current_grf_feature_test.Reset();
9671 HandleNodes(buf, _tags_ftst);
9672 _current_grf_feature_test.ExecuteTest();
9673 return true;
9676 /** Action14 Action0 property map action instance */
9677 struct GRFPropertyMapAction {
9678 const char *tag_name = nullptr;
9679 const char *descriptor = nullptr;
9681 GrfSpecFeature feature;
9682 int prop_id;
9683 int ext_prop_id;
9684 std::string name;
9685 GRFPropertyMapFallbackMode fallback_mode;
9686 uint8_t ttd_ver_var_bit;
9687 uint32_t test_91_value;
9688 uint8_t input_shift;
9689 uint8_t output_shift;
9690 uint input_mask;
9691 uint output_mask;
9692 uint output_param;
9694 void Reset(const char *tag, const char *desc)
9696 this->tag_name = tag;
9697 this->descriptor = desc;
9699 this->feature = GSF_INVALID;
9700 this->prop_id = -1;
9701 this->ext_prop_id = -1;
9702 this->name.clear();
9703 this->fallback_mode = GPMFM_IGNORE;
9704 this->ttd_ver_var_bit = 0;
9705 this->test_91_value = 0;
9706 this->input_shift = 0;
9707 this->output_shift = 0;
9708 this->input_mask = 0;
9709 this->output_mask = 0;
9710 this->output_param = 0;
9713 void ExecuteFeatureIDRemapping()
9715 if (this->prop_id < 0) {
9716 GrfMsg(2, "Action 14 {} remapping: no feature ID defined, doing nothing", this->descriptor);
9717 return;
9719 if (this->name.empty()) {
9720 GrfMsg(2, "Action 14 {} remapping: no name defined, doing nothing", this->descriptor);
9721 return;
9723 SetBit(_cur.grffile->ctrl_flags, GFCF_HAVE_FEATURE_ID_REMAP);
9724 bool success = false;
9725 const char *str = this->name.c_str();
9726 extern const GRFFeatureMapDefinition _grf_remappable_features[];
9727 for (const GRFFeatureMapDefinition *info = _grf_remappable_features; info->name != nullptr; info++) {
9728 if (strcmp(info->name, str) == 0) {
9729 GRFFeatureMapRemapEntry &entry = _cur.grffile->feature_id_remaps.Entry(this->prop_id);
9730 entry.name = info->name;
9731 entry.feature = info->feature;
9732 entry.raw_id = this->prop_id;
9733 success = true;
9734 break;
9737 if (this->ttd_ver_var_bit > 0) {
9738 AssignBit(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, success);
9740 if (this->test_91_value > 0 && success) {
9741 include(_cur.grffile->var91_values, this->test_91_value);
9743 if (!success) {
9744 if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
9745 GrfMsg(0, "Error: Unimplemented mapped {}: {}, mapped to: 0x{:02X}", this->descriptor, str, this->prop_id);
9746 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_FEATURE_ID);
9747 error->data = stredup(str);
9748 error->param_value[1] = GSF_INVALID;
9749 error->param_value[2] = this->prop_id;
9750 } else {
9751 const char *str_store = stredup(str);
9752 GrfMsg(2, "Unimplemented mapped {}: {}, mapped to: {:X}, {} on use",
9753 this->descriptor, str, this->prop_id, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
9754 _cur.grffile->remap_unknown_property_names.emplace_back(str_store);
9755 GRFFeatureMapRemapEntry &entry = _cur.grffile->feature_id_remaps.Entry(this->prop_id);
9756 entry.name = str_store;
9757 entry.feature = (this->fallback_mode == GPMFM_IGNORE) ? GSF_INVALID : GSF_ERROR_ON_USE;
9758 entry.raw_id = this->prop_id;
9763 void ExecutePropertyRemapping()
9765 if (this->feature == GSF_INVALID) {
9766 GrfMsg(2, "Action 14 {} remapping: no feature defined, doing nothing", this->descriptor);
9767 return;
9769 if (this->prop_id < 0 && this->ext_prop_id < 0) {
9770 GrfMsg(2, "Action 14 {} remapping: no property ID defined, doing nothing", this->descriptor);
9771 return;
9773 if (this->name.empty()) {
9774 GrfMsg(2, "Action 14 {} remapping: no name defined, doing nothing", this->descriptor);
9775 return;
9777 bool success = false;
9778 const char *str = this->name.c_str();
9779 extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[];
9780 for (const GRFPropertyMapDefinition *info = _grf_action0_remappable_properties; info->name != nullptr; info++) {
9781 if ((info->feature == GSF_INVALID || info->feature == this->feature) && strcmp(info->name, str) == 0) {
9782 if (this->prop_id > 0) {
9783 GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id);
9784 entry.name = info->name;
9785 entry.id = info->id;
9786 entry.feature = this->feature;
9787 entry.property_id = this->prop_id;
9789 if (this->ext_prop_id > 0) {
9790 GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_extended_property_remaps[(((uint32_t)this->feature) << 16) | this->ext_prop_id];
9791 entry.name = info->name;
9792 entry.id = info->id;
9793 entry.feature = this->feature;
9794 entry.extended = true;
9795 entry.property_id = this->ext_prop_id;
9797 success = true;
9798 break;
9801 if (this->ttd_ver_var_bit > 0) {
9802 AssignBit(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, success);
9804 if (this->test_91_value > 0 && success) {
9805 include(_cur.grffile->var91_values, this->test_91_value);
9807 if (!success) {
9808 uint mapped_to = (this->prop_id > 0) ? this->prop_id : this->ext_prop_id;
9809 const char *extended = (this->prop_id > 0) ? "" : " (extended)";
9810 if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
9811 GrfMsg(0, "Error: Unimplemented mapped {}: {}, feature: {}, mapped to: {:X}{}", this->descriptor, str, GetFeatureString(this->feature), mapped_to, extended);
9812 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY);
9813 error->data = stredup(str);
9814 error->param_value[1] = this->feature;
9815 error->param_value[2] = ((this->prop_id > 0) ? 0 : 0xE0000) | mapped_to;
9816 } else {
9817 const char *str_store = stredup(str);
9818 GrfMsg(2, "Unimplemented mapped {}: {}, feature: {}, mapped to: {:X}{}, {} on use",
9819 this->descriptor, str, GetFeatureString(this->feature), mapped_to, extended, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
9820 _cur.grffile->remap_unknown_property_names.emplace_back(str_store);
9821 if (this->prop_id > 0) {
9822 GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id);
9823 entry.name = str_store;
9824 entry.id = (this->fallback_mode == GPMFM_IGNORE) ? A0RPI_UNKNOWN_IGNORE : A0RPI_UNKNOWN_ERROR;
9825 entry.feature = this->feature;
9826 entry.property_id = this->prop_id;
9828 if (this->ext_prop_id > 0) {
9829 GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_extended_property_remaps[(((uint32_t)this->feature) << 16) | this->ext_prop_id];
9830 entry.name = str_store;
9831 entry.id = (this->fallback_mode == GPMFM_IGNORE) ? A0RPI_UNKNOWN_IGNORE : A0RPI_UNKNOWN_ERROR;;
9832 entry.feature = this->feature;
9833 entry.extended = true;
9834 entry.property_id = this->ext_prop_id;
9840 void ExecuteVariableRemapping()
9842 if (this->feature == GSF_INVALID) {
9843 GrfMsg(2, "Action 14 {} remapping: no feature defined, doing nothing", this->descriptor);
9844 return;
9846 if (this->name.empty()) {
9847 GrfMsg(2, "Action 14 {} remapping: no name defined, doing nothing", this->descriptor);
9848 return;
9850 bool success = false;
9851 const char *str = this->name.c_str();
9852 extern const GRFVariableMapDefinition _grf_action2_remappable_variables[];
9853 for (const GRFVariableMapDefinition *info = _grf_action2_remappable_variables; info->name != nullptr; info++) {
9854 if (info->feature == this->feature && strcmp(info->name, str) == 0) {
9855 _cur.grffile->grf_variable_remaps.push_back({ (uint16_t)info->id, (uint8_t)this->feature, this->input_shift, this->output_shift, this->input_mask, this->output_mask, this->output_param });
9856 success = true;
9857 break;
9860 if (this->ttd_ver_var_bit > 0) {
9861 AssignBit(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, success);
9863 if (this->test_91_value > 0 && success) {
9864 include(_cur.grffile->var91_values, this->test_91_value);
9866 if (!success) {
9867 GrfMsg(2, "Unimplemented mapped {}: {}, feature: {}, mapped to 0", this->descriptor, str, GetFeatureString(this->feature));
9871 void ExecuteAction5TypeRemapping()
9873 if (this->prop_id < 0) {
9874 GrfMsg(2, "Action 14 {} remapping: no type ID defined, doing nothing", this->descriptor);
9875 return;
9877 if (this->name.empty()) {
9878 GrfMsg(2, "Action 14 {} remapping: no name defined, doing nothing", this->descriptor);
9879 return;
9881 bool success = false;
9882 const char *str = this->name.c_str();
9883 extern const Action5TypeRemapDefinition _grf_action5_remappable_types[];
9884 for (const Action5TypeRemapDefinition *info = _grf_action5_remappable_types; info->name != nullptr; info++) {
9885 if (strcmp(info->name, str) == 0) {
9886 Action5TypeRemapEntry &entry = _cur.grffile->action5_type_remaps.Entry(this->prop_id);
9887 entry.name = info->name;
9888 entry.info = &(info->info);
9889 entry.type_id = this->prop_id;
9890 success = true;
9891 break;
9894 if (this->ttd_ver_var_bit > 0) {
9895 AssignBit(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, success);
9897 if (this->test_91_value > 0 && success) {
9898 include(_cur.grffile->var91_values, this->test_91_value);
9900 if (!success) {
9901 if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
9902 GrfMsg(0, "Error: Unimplemented mapped {}: {}, mapped to: {:X}", this->descriptor, str, this->prop_id);
9903 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_ACTION5_TYPE);
9904 error->data = stredup(str);
9905 error->param_value[1] = this->prop_id;
9906 } else {
9907 const char *str_store = stredup(str);
9908 GrfMsg(2, "Unimplemented mapped {}: {}, mapped to: {:X}, {} on use",
9909 this->descriptor, str, this->prop_id, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
9910 _cur.grffile->remap_unknown_property_names.emplace_back(str_store);
9911 Action5TypeRemapEntry &entry = _cur.grffile->action5_type_remaps.Entry(this->prop_id);
9912 entry.name = str_store;
9913 entry.info = nullptr;
9914 entry.type_id = this->prop_id;
9915 entry.fallback_mode = this->fallback_mode;
9921 static GRFPropertyMapAction _current_grf_property_map_action;
9923 /** Callback function for ->'NAME' to set the name of the item to be mapped. */
9924 static bool ChangePropertyRemapName(uint8_t langid, std::string_view str)
9926 _current_grf_property_map_action.name = str;
9927 return true;
9930 /** Callback function for ->'FEAT' to set which feature this mapping applies to. */
9931 static bool ChangePropertyRemapFeature(size_t len, ByteReader &buf)
9933 GRFPropertyMapAction &action = _current_grf_property_map_action;
9934 if (len != 1) {
9935 GrfMsg(2, "Action 14 {} mapping: expected 1 byte for '{}'->'FEAT' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
9936 buf.Skip(len);
9937 } else {
9938 GrfSpecFeatureRef feature = ReadFeature(buf.ReadByte());
9939 if (feature.id >= GSF_END) {
9940 GrfMsg(2, "Action 14 {} mapping: invalid feature ID: {}, in '{}'->'FEAT', ignoring this field", action.descriptor, GetFeatureString(feature), action.tag_name);
9941 } else {
9942 action.feature = feature.id;
9945 return true;
9948 /** Callback function for ->'PROP' to set the property ID to which this item is being mapped. */
9949 static bool ChangePropertyRemapPropertyId(size_t len, ByteReader &buf)
9951 GRFPropertyMapAction &action = _current_grf_property_map_action;
9952 if (len != 1) {
9953 GrfMsg(2, "Action 14 {} mapping: expected 1 byte for '{}'->'PROP' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
9954 buf.Skip(len);
9955 } else {
9956 action.prop_id = buf.ReadByte();
9958 return true;
9961 /** Callback function for ->'XPRP' to set the extended property ID to which this item is being mapped. */
9962 static bool ChangePropertyRemapExtendedPropertyId(size_t len, ByteReader &buf)
9964 GRFPropertyMapAction &action = _current_grf_property_map_action;
9965 if (len != 2) {
9966 GrfMsg(2, "Action 14 {} mapping: expected 2 bytes for '{}'->'XPRP' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
9967 buf.Skip(len);
9968 } else {
9969 action.ext_prop_id = buf.ReadWord();
9971 return true;
9974 /** Callback function for ->'FTID' to set the feature ID to which this feature is being mapped. */
9975 static bool ChangePropertyRemapFeatureId(size_t len, ByteReader &buf)
9977 GRFPropertyMapAction &action = _current_grf_property_map_action;
9978 if (len != 1) {
9979 GrfMsg(2, "Action 14 {} mapping: expected 1 byte for '{}'->'FTID' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
9980 buf.Skip(len);
9981 } else {
9982 action.prop_id = buf.ReadByte();
9984 return true;
9987 /** Callback function for ->'TYPE' to set the property ID to which this item is being mapped. */
9988 static bool ChangePropertyRemapTypeId(size_t len, ByteReader &buf)
9990 GRFPropertyMapAction &action = _current_grf_property_map_action;
9991 if (len != 1) {
9992 GrfMsg(2, "Action 14 {} mapping: expected 1 byte for '{}'->'TYPE' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
9993 buf.Skip(len);
9994 } else {
9995 uint8_t prop = buf.ReadByte();
9996 if (prop < 128) {
9997 action.prop_id = prop;
9998 } else {
9999 GrfMsg(2, "Action 14 {} mapping: expected a type < 128 for '{}'->'TYPE' but got {}, ignoring this field", action.descriptor, action.tag_name, prop);
10002 return true;
10005 /** Callback function for ->'FLBK' to set the fallback mode. */
10006 static bool ChangePropertyRemapSetFallbackMode(size_t len, ByteReader &buf)
10008 GRFPropertyMapAction &action = _current_grf_property_map_action;
10009 if (len != 1) {
10010 GrfMsg(2, "Action 14 {} mapping: expected 1 byte for '{}'->'FLBK' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
10011 buf.Skip(len);
10012 } else {
10013 GRFPropertyMapFallbackMode mode = (GRFPropertyMapFallbackMode) buf.ReadByte();
10014 if (mode < GPMFM_END) action.fallback_mode = mode;
10016 return true;
10018 /** Callback function for ->'SETT' to set the bit number of global variable 8D (TTD version) to set/unset with whether the remapping was successful. */
10019 static bool ChangePropertyRemapSetTTDVerVarBit(size_t len, ByteReader &buf)
10021 GRFPropertyMapAction &action = _current_grf_property_map_action;
10022 if (len != 1) {
10023 GrfMsg(2, "Action 14 {} mapping: expected 1 byte for '{}'->'SETT' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
10024 buf.Skip(len);
10025 } else {
10026 uint8_t bit_number = buf.ReadByte();
10027 if (bit_number >= 4 && bit_number <= 31) {
10028 action.ttd_ver_var_bit = bit_number;
10029 } else {
10030 GrfMsg(2, "Action 14 {} mapping: expected a bit number >= 4 and <= 32 for '{}'->'SETT' but got {}, ignoring this field", action.descriptor, action.tag_name, bit_number);
10033 return true;
10036 /** Callback function for >'SVAL' to add a success result value for checking using global variable 91. */
10037 static bool ChangePropertyRemapSuccessResultValue(size_t len, ByteReader &buf)
10039 GRFPropertyMapAction &action = _current_grf_property_map_action;
10040 if (len != 4) {
10041 GrfMsg(2, "Action 14 {} mapping: expected 4 bytes for '{}'->'SVAL' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
10042 buf.Skip(len);
10043 } else {
10044 action.test_91_value = buf.ReadDWord();
10046 return true;
10049 /** Callback function for ->'RSFT' to set the input shift value for variable remapping. */
10050 static bool ChangePropertyRemapSetInputShift(size_t len, ByteReader &buf)
10052 GRFPropertyMapAction &action = _current_grf_property_map_action;
10053 if (len != 1) {
10054 GrfMsg(2, "Action 14 {} mapping: expected 1 byte for '{}'->'RSFT' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
10055 buf.Skip(len);
10056 } else {
10057 uint8_t input_shift = buf.ReadByte();
10058 if (input_shift < 0x20) {
10059 action.input_shift = input_shift;
10060 } else {
10061 GrfMsg(2, "Action 14 {} mapping: expected a shift value < 0x20 for '{}'->'RSFT' but got {}, ignoring this field", action.descriptor, action.tag_name, input_shift);
10064 return true;
10067 /** Callback function for ->'VSFT' to set the output shift value for variable remapping. */
10068 static bool ChangePropertyRemapSetOutputShift(size_t len, ByteReader &buf)
10070 GRFPropertyMapAction &action = _current_grf_property_map_action;
10071 if (len != 1) {
10072 GrfMsg(2, "Action 14 {} mapping: expected 1 byte for '{}'->'VSFT' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
10073 buf.Skip(len);
10074 } else {
10075 uint8_t output_shift = buf.ReadByte();
10076 if (output_shift < 0x20) {
10077 action.output_shift = output_shift;
10078 } else {
10079 GrfMsg(2, "Action 14 {} mapping: expected a shift value < 0x20 for '{}'->'VSFT' but got {}, ignoring this field", action.descriptor, action.tag_name, output_shift);
10082 return true;
10085 /** Callback function for ->'RMSK' to set the input mask value for variable remapping. */
10086 static bool ChangePropertyRemapSetInputMask(size_t len, ByteReader &buf)
10088 GRFPropertyMapAction &action = _current_grf_property_map_action;
10089 if (len != 4) {
10090 GrfMsg(2, "Action 14 {} mapping: expected 4 bytes for '{}'->'RMSK' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
10091 buf.Skip(len);
10092 } else {
10093 action.input_mask = buf.ReadDWord();
10095 return true;
10098 /** Callback function for ->'VMSK' to set the output mask value for variable remapping. */
10099 static bool ChangePropertyRemapSetOutputMask(size_t len, ByteReader &buf)
10101 GRFPropertyMapAction &action = _current_grf_property_map_action;
10102 if (len != 4) {
10103 GrfMsg(2, "Action 14 {} mapping: expected 4 bytes for '{}'->'VMSK' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
10104 buf.Skip(len);
10105 } else {
10106 action.output_mask = buf.ReadDWord();
10108 return true;
10111 /** Callback function for ->'VPRM' to set the output parameter value for variable remapping. */
10112 static bool ChangePropertyRemapSetOutputParam(size_t len, ByteReader &buf)
10114 GRFPropertyMapAction &action = _current_grf_property_map_action;
10115 if (len != 4) {
10116 GrfMsg(2, "Action 14 {} mapping: expected 4 bytes for '{}'->'VPRM' but got {}, ignoring this field", action.descriptor, action.tag_name, len);
10117 buf.Skip(len);
10118 } else {
10119 action.output_param = buf.ReadDWord();
10121 return true;
10124 /** Action14 tags for the FIDM node */
10125 AllowedSubtags _tags_fidm[] = {
10126 AllowedSubtags('NAME', ChangePropertyRemapName),
10127 AllowedSubtags('FTID', ChangePropertyRemapFeatureId),
10128 AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
10129 AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
10130 AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
10131 AllowedSubtags()
10135 * Callback function for 'FIDM' (feature ID mapping)
10137 static bool HandleFeatureIDMap(ByteReader &buf)
10139 _current_grf_property_map_action.Reset("FIDM", "feature");
10140 HandleNodes(buf, _tags_fidm);
10141 _current_grf_property_map_action.ExecuteFeatureIDRemapping();
10142 return true;
10145 /** Action14 tags for the A0PM node */
10146 AllowedSubtags _tags_a0pm[] = {
10147 AllowedSubtags('NAME', ChangePropertyRemapName),
10148 AllowedSubtags('FEAT', ChangePropertyRemapFeature),
10149 AllowedSubtags('PROP', ChangePropertyRemapPropertyId),
10150 AllowedSubtags('XPRP', ChangePropertyRemapExtendedPropertyId),
10151 AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
10152 AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
10153 AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
10154 AllowedSubtags()
10158 * Callback function for 'A0PM' (action 0 property mapping)
10160 static bool HandleAction0PropertyMap(ByteReader &buf)
10162 _current_grf_property_map_action.Reset("A0PM", "property");
10163 HandleNodes(buf, _tags_a0pm);
10164 _current_grf_property_map_action.ExecutePropertyRemapping();
10165 return true;
10168 /** Action14 tags for the A2VM node */
10169 AllowedSubtags _tags_a2vm[] = {
10170 AllowedSubtags('NAME', ChangePropertyRemapName),
10171 AllowedSubtags('FEAT', ChangePropertyRemapFeature),
10172 AllowedSubtags('RSFT', ChangePropertyRemapSetInputShift),
10173 AllowedSubtags('RMSK', ChangePropertyRemapSetInputMask),
10174 AllowedSubtags('VSFT', ChangePropertyRemapSetOutputShift),
10175 AllowedSubtags('VMSK', ChangePropertyRemapSetOutputMask),
10176 AllowedSubtags('VPRM', ChangePropertyRemapSetOutputParam),
10177 AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
10178 AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
10179 AllowedSubtags()
10183 * Callback function for 'A2VM' (action 2 variable mapping)
10185 static bool HandleAction2VariableMap(ByteReader &buf)
10187 _current_grf_property_map_action.Reset("A2VM", "variable");
10188 HandleNodes(buf, _tags_a2vm);
10189 _current_grf_property_map_action.ExecuteVariableRemapping();
10190 return true;
10193 /** Action14 tags for the A5TM node */
10194 AllowedSubtags _tags_a5tm[] = {
10195 AllowedSubtags('NAME', ChangePropertyRemapName),
10196 AllowedSubtags('TYPE', ChangePropertyRemapTypeId),
10197 AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
10198 AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
10199 AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
10200 AllowedSubtags()
10204 * Callback function for 'A5TM' (action 5 type mapping)
10206 static bool HandleAction5TypeMap(ByteReader &buf)
10208 _current_grf_property_map_action.Reset("A5TM", "Action 5 type");
10209 HandleNodes(buf, _tags_a5tm);
10210 _current_grf_property_map_action.ExecuteAction5TypeRemapping();
10211 return true;
10214 /** Action14 root tags */
10215 AllowedSubtags _tags_root_static[] = {
10216 AllowedSubtags('INFO', _tags_info),
10217 AllowedSubtags('FTST', SkipInfoChunk),
10218 AllowedSubtags('FIDM', SkipInfoChunk),
10219 AllowedSubtags('A0PM', SkipInfoChunk),
10220 AllowedSubtags('A2VM', SkipInfoChunk),
10221 AllowedSubtags('A5TM', SkipInfoChunk),
10222 AllowedSubtags()
10225 /** Action14 root tags */
10226 AllowedSubtags _tags_root_feature_tests[] = {
10227 AllowedSubtags('INFO', SkipInfoChunk),
10228 AllowedSubtags('FTST', HandleFeatureTestInfo),
10229 AllowedSubtags('FIDM', HandleFeatureIDMap),
10230 AllowedSubtags('A0PM', HandleAction0PropertyMap),
10231 AllowedSubtags('A2VM', HandleAction2VariableMap),
10232 AllowedSubtags('A5TM', HandleAction5TypeMap),
10233 AllowedSubtags()
10238 * Try to skip the current node and all subnodes (if it's a branch node).
10239 * @param buf Buffer.
10240 * @param type The node type to skip.
10241 * @return True if we could skip the node, false if an error occurred.
10243 static bool SkipUnknownInfo(ByteReader &buf, uint8_t type)
10245 /* type and id are already read */
10246 switch (type) {
10247 case 'C': {
10248 uint8_t new_type = buf.ReadByte();
10249 while (new_type != 0) {
10250 buf.ReadDWord(); // skip the id
10251 if (!SkipUnknownInfo(buf, new_type)) return false;
10252 new_type = buf.ReadByte();
10254 break;
10257 case 'T':
10258 buf.ReadByte(); // lang
10259 buf.ReadString(); // actual text
10260 break;
10262 case 'B': {
10263 uint16_t size = buf.ReadWord();
10264 buf.Skip(size);
10265 break;
10268 default:
10269 return false;
10272 return true;
10276 * Handle the nodes of an Action14
10277 * @param type Type of node.
10278 * @param id ID.
10279 * @param buf Buffer.
10280 * @param subtags Allowed subtags.
10281 * @return Whether all tags could be handled.
10283 static bool HandleNode(uint8_t type, uint32_t id, ByteReader &buf, AllowedSubtags subtags[])
10285 uint i = 0;
10286 AllowedSubtags *tag;
10287 while ((tag = &subtags[i++])->type != 0) {
10288 if (tag->id != BSWAP32(id) || tag->type != type) continue;
10289 switch (type) {
10290 default: NOT_REACHED();
10292 case 'T': {
10293 uint8_t langid = buf.ReadByte();
10294 return tag->handler.text(langid, buf.ReadString());
10297 case 'B': {
10298 size_t len = buf.ReadWord();
10299 if (buf.Remaining() < len) return false;
10300 return tag->handler.data(len, buf);
10303 case 'C': {
10304 if (tag->handler.call_handler) {
10305 return tag->handler.u.branch(buf);
10307 return HandleNodes(buf, tag->handler.u.subtags);
10311 GrfMsg(2, "StaticGRFInfo: unknown type/id combination found, type={:c}, id={:x}", type, id);
10312 return SkipUnknownInfo(buf, type);
10316 * Handle the contents of a 'C' choice of an Action14
10317 * @param buf Buffer.
10318 * @param subtags List of subtags.
10319 * @return Whether the nodes could all be handled.
10321 static bool HandleNodes(ByteReader &buf, AllowedSubtags subtags[])
10323 uint8_t type = buf.ReadByte();
10324 while (type != 0) {
10325 uint32_t id = buf.ReadDWord();
10326 if (!HandleNode(type, id, buf, subtags)) return false;
10327 type = buf.ReadByte();
10329 return true;
10333 * Handle Action 0x14 (static info)
10334 * @param buf Buffer.
10336 static void StaticGRFInfo(ByteReader &buf)
10338 /* <14> <type> <id> <text/data...> */
10339 HandleNodes(buf, _tags_root_static);
10343 * Handle Action 0x14 (feature tests)
10344 * @param buf Buffer.
10346 static void Act14FeatureTest(ByteReader &buf)
10348 /* <14> <type> <id> <text/data...> */
10349 HandleNodes(buf, _tags_root_feature_tests);
10353 * Set the current NewGRF as unsafe for static use
10354 * @note Used during safety scan on unsafe actions.
10356 static void GRFUnsafe(ByteReader &)
10358 SetBit(_cur.grfconfig->flags, GCF_UNSAFE);
10360 /* Skip remainder of GRF */
10361 _cur.skip_sprites = -1;
10365 /** Initialize the TTDPatch flags */
10366 static void InitializeGRFSpecial()
10368 _ttdpatch_flags[0] = ((_settings_game.station.never_expire_airports ? 1U : 0U) << 0x0C) // keepsmallairport
10369 | (1U << 0x0D) // newairports
10370 | (1U << 0x0E) // largestations
10371 | ((_settings_game.construction.max_bridge_length > 16 ? 1U : 0U) << 0x0F) // longbridges
10372 | (0U << 0x10) // loadtime
10373 | (1U << 0x12) // presignals
10374 | (1U << 0x13) // extpresignals
10375 | ((_settings_game.vehicle.never_expire_vehicles ? 1U : 0U) << 0x16) // enginespersist
10376 | (1U << 0x1B) // multihead
10377 | (1U << 0x1D) // lowmemory
10378 | (1U << 0x1E); // generalfixes
10380 _ttdpatch_flags[1] = ((_settings_game.economy.station_noise_level ? 1U : 0U) << 0x07) // moreairports - based on units of noise
10381 | (1U << 0x08) // mammothtrains
10382 | (1U << 0x09) // trainrefit
10383 | (0U << 0x0B) // subsidiaries
10384 | ((_settings_game.order.gradual_loading ? 1U : 0U) << 0x0C) // gradualloading
10385 | (1U << 0x12) // unifiedmaglevmode - set bit 0 mode. Not revelant to OTTD
10386 | (1U << 0x13) // unifiedmaglevmode - set bit 1 mode
10387 | (1U << 0x14) // bridgespeedlimits
10388 | (1U << 0x16) // eternalgame
10389 | (1U << 0x17) // newtrains
10390 | (1U << 0x18) // newrvs
10391 | (1U << 0x19) // newships
10392 | (1U << 0x1A) // newplanes
10393 | ((_settings_game.construction.train_signal_side == 1 ? 1U : 0U) << 0x1B) // signalsontrafficside
10394 | ((_settings_game.vehicle.disable_elrails ? 0U : 1U) << 0x1C); // electrifiedrailway
10396 _ttdpatch_flags[2] = (1U << 0x01) // loadallgraphics - obsolote
10397 | (1U << 0x03) // semaphores
10398 | (1U << 0x0A) // newobjects
10399 | (0U << 0x0B) // enhancedgui
10400 | (0U << 0x0C) // newagerating
10401 | ((_settings_game.construction.build_on_slopes ? 1U : 0U) << 0x0D) // buildonslopes
10402 | (1U << 0x0E) // fullloadany
10403 | (1U << 0x0F) // planespeed
10404 | (0U << 0x10) // moreindustriesperclimate - obsolete
10405 | (0U << 0x11) // moretoylandfeatures
10406 | (1U << 0x12) // newstations
10407 | (1U << 0x13) // tracktypecostdiff
10408 | (1U << 0x14) // manualconvert
10409 | ((_settings_game.construction.build_on_slopes ? 1U : 0U) << 0x15) // buildoncoasts
10410 | (1U << 0x16) // canals
10411 | (1U << 0x17) // newstartyear
10412 | ((_settings_game.vehicle.freight_trains > 1 ? 1U : 0U) << 0x18) // freighttrains
10413 | (1U << 0x19) // newhouses
10414 | (1U << 0x1A) // newbridges
10415 | (1U << 0x1B) // newtownnames
10416 | (1U << 0x1C) // moreanimation
10417 | ((_settings_game.vehicle.wagon_speed_limits ? 1U : 0U) << 0x1D) // wagonspeedlimits
10418 | (1U << 0x1E) // newshistory
10419 | (0U << 0x1F); // custombridgeheads
10421 _ttdpatch_flags[3] = (0U << 0x00) // newcargodistribution
10422 | (1U << 0x01) // windowsnap
10423 | ((_settings_game.economy.allow_town_roads || _generating_world ? 0U : 1U) << 0x02) // townbuildnoroad
10424 | (1U << 0x03) // pathbasedsignalling
10425 | (0U << 0x04) // aichoosechance
10426 | (1U << 0x05) // resolutionwidth
10427 | (1U << 0x06) // resolutionheight
10428 | (1U << 0x07) // newindustries
10429 | ((_settings_game.order.improved_load ? 1U : 0U) << 0x08) // fifoloading
10430 | (0U << 0x09) // townroadbranchprob
10431 | (0U << 0x0A) // tempsnowline
10432 | (1U << 0x0B) // newcargo
10433 | (1U << 0x0C) // enhancemultiplayer
10434 | (1U << 0x0D) // onewayroads
10435 | (1U << 0x0E) // irregularstations
10436 | (1U << 0x0F) // statistics
10437 | (1U << 0x10) // newsounds
10438 | (1U << 0x11) // autoreplace
10439 | (1U << 0x12) // autoslope
10440 | (0U << 0x13) // followvehicle
10441 | (1U << 0x14) // trams
10442 | (0U << 0x15) // enhancetunnels
10443 | (1U << 0x16) // shortrvs
10444 | (1U << 0x17) // articulatedrvs
10445 | ((_settings_game.vehicle.dynamic_engines ? 1U : 0U) << 0x18) // dynamic engines
10446 | (1U << 0x1E) // variablerunningcosts
10447 | (1U << 0x1F); // any switch is on
10449 _ttdpatch_flags[4] = (1U << 0x00) // larger persistent storage
10450 | ((_settings_game.economy.inflation && !_settings_game.economy.disable_inflation_newgrf_flag ? 1U : 0U) << 0x01) // inflation is on
10451 | (1U << 0x02); // extended string range
10452 MemSetT(_observed_ttdpatch_flags, 0, lengthof(_observed_ttdpatch_flags));
10455 bool HasTTDPatchFlagBeenObserved(uint flag)
10457 uint index = flag / 0x20;
10458 flag %= 0x20;
10459 if (index >= lengthof(_ttdpatch_flags)) return false;
10460 return HasBit(_observed_ttdpatch_flags[index], flag);
10463 /** Reset and clear all NewGRF stations */
10464 static void ResetCustomStations()
10466 for (GRFFile * const file : _grf_files) {
10467 file->stations.clear();
10471 /** Reset and clear all NewGRF houses */
10472 static void ResetCustomHouses()
10474 for (GRFFile * const file : _grf_files) {
10475 file->housespec.clear();
10479 /** Reset and clear all NewGRF airports */
10480 static void ResetCustomAirports()
10482 for (GRFFile * const file : _grf_files) {
10483 file->airportspec.clear();
10484 file->airtspec.clear();
10488 /** Reset and clear all NewGRF industries */
10489 static void ResetCustomIndustries()
10491 for (GRFFile * const file : _grf_files) {
10492 file->industryspec.clear();
10493 file->indtspec.clear();
10497 /** Reset and clear all NewObjects */
10498 static void ResetCustomObjects()
10500 for (GRFFile * const file : _grf_files) {
10501 file->objectspec.clear();
10505 static void ResetCustomRoadStops()
10507 for (auto file : _grf_files) {
10508 file->roadstops.clear();
10512 /** Reset and clear all NewGRFs */
10513 static void ResetNewGRF()
10515 for (GRFFile * const file : _grf_files) {
10516 delete file;
10519 _grf_files.clear();
10520 _grf_file_map.clear();
10521 _cur.grffile = nullptr;
10522 _new_signals_grfs.clear();
10523 MemSetT<NewSignalStyle>(_new_signal_styles.data(), 0, MAX_NEW_SIGNAL_STYLES);
10524 _num_new_signal_styles = 0;
10525 _new_landscape_rocks_grfs.clear();
10528 /** Clear all NewGRF errors */
10529 static void ResetNewGRFErrors()
10531 for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
10532 c->error.reset();
10537 * Reset all NewGRF loaded data
10539 void ResetNewGRFData()
10541 CleanUpStrings();
10542 CleanUpGRFTownNames();
10544 /* Copy/reset original engine info data */
10545 SetupEngines();
10547 /* Copy/reset original bridge info data */
10548 ResetBridges();
10550 /* Reset rail type information */
10551 ResetRailTypes();
10553 /* Copy/reset original road type info data */
10554 ResetRoadTypes();
10556 /* Allocate temporary refit/cargo class data */
10557 _gted.resize(Engine::GetPoolSize());
10559 /* Fill rail type label temporary data for default trains */
10560 for (const Engine *e : Engine::IterateType(VEH_TRAIN)) {
10561 _gted[e->index].railtypelabel = GetRailTypeInfo(e->u.rail.railtype)->label;
10564 /* Reset GRM reservations */
10565 memset(&_grm_engines, 0, sizeof(_grm_engines));
10566 memset(&_grm_cargoes, 0, sizeof(_grm_cargoes));
10568 /* Reset generic feature callback lists */
10569 ResetGenericCallbacks();
10571 /* Reset price base data */
10572 ResetPriceBaseMultipliers();
10574 /* Reset the curencies array */
10575 ResetCurrencies();
10577 /* Reset the house array */
10578 ResetCustomHouses();
10579 ResetHouses();
10581 /* Reset the industries structures*/
10582 ResetCustomIndustries();
10583 ResetIndustries();
10585 /* Reset the objects. */
10586 ObjectClass::Reset();
10587 ResetCustomObjects();
10588 ResetObjects();
10590 /* Reset station classes */
10591 StationClass::Reset();
10592 ResetCustomStations();
10594 /* Reset airport-related structures */
10595 AirportClass::Reset();
10596 ResetCustomAirports();
10597 AirportSpec::ResetAirports();
10598 AirportTileSpec::ResetAirportTiles();
10600 /* Reset road stop classes */
10601 RoadStopClass::Reset();
10602 ResetCustomRoadStops();
10604 /* Reset canal sprite groups and flags */
10605 memset(_water_feature, 0, sizeof(_water_feature));
10607 /* Reset the snowline table. */
10608 ClearSnowLine();
10610 /* Reset NewGRF files */
10611 ResetNewGRF();
10613 /* Reset NewGRF errors. */
10614 ResetNewGRFErrors();
10616 /* Set up the default cargo types */
10617 SetupCargoForClimate(_settings_game.game_creation.landscape);
10619 /* Reset misc GRF features and train list display variables */
10620 _misc_grf_features = 0;
10622 _loaded_newgrf_features.has_2CC = false;
10623 _loaded_newgrf_features.used_liveries = 1 << LS_DEFAULT;
10624 _loaded_newgrf_features.shore = SHORE_REPLACE_NONE;
10625 _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NONE;
10627 /* Clear all GRF overrides */
10628 _grf_id_overrides.clear();
10630 InitializeSoundPool();
10631 _spritegroup_pool.CleanPool();
10632 _callback_result_cache.clear();
10633 _deterministic_sg_shadows.clear();
10634 _randomized_sg_shadows.clear();
10635 _grfs_loaded_with_sg_shadow_enable = HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW);
10639 * Reset NewGRF data which is stored persistently in savegames.
10641 void ResetPersistentNewGRFData()
10643 /* Reset override managers */
10644 _engine_mngr.ResetToDefaultMapping();
10645 _house_mngr.ResetMapping();
10646 _industry_mngr.ResetMapping();
10647 _industile_mngr.ResetMapping();
10648 _airport_mngr.ResetMapping();
10649 _airporttile_mngr.ResetMapping();
10653 * Construct the Cargo Mapping
10654 * @note This is the reverse of a cargo translation table
10656 static void BuildCargoTranslationMap()
10658 _cur.grffile->cargo_map.fill(UINT8_MAX);
10660 auto cargo_list = GetCargoTranslationTable(*_cur.grffile);
10662 for (const CargoSpec *cs : CargoSpec::Iterate()) {
10663 if (!cs->IsValid()) continue;
10665 /* Check the translation table for this cargo's label */
10666 int idx = find_index(cargo_list, cs->label);
10667 if (idx >= 0) _cur.grffile->cargo_map[cs->Index()] = idx;
10672 * Prepare loading a NewGRF file with its config
10673 * @param config The NewGRF configuration struct with name, id, parameters and alike.
10675 static void InitNewGRFFile(const GRFConfig *config)
10677 GRFFile *newfile = GetFileByFilename(config->filename);
10678 if (newfile != nullptr) {
10679 /* We already loaded it once. */
10680 _cur.grffile = newfile;
10681 return;
10684 newfile = new GRFFile(config);
10685 _cur.grffile = newfile;
10686 _grf_files.push_back(newfile);
10687 _grf_file_map[newfile->grfid] = newfile;
10691 * Constructor for GRFFile
10692 * @param config GRFConfig to copy name, grfid and parameters from.
10694 GRFFile::GRFFile(const GRFConfig *config)
10696 this->filename = config->filename;
10697 this->grfid = config->ident.grfid;
10699 /* Initialise local settings to defaults */
10700 this->traininfo_vehicle_pitch = 0;
10701 this->traininfo_vehicle_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
10703 this->new_signals_group = nullptr;
10704 this->new_signal_ctrl_flags = 0;
10705 this->new_signal_extra_aspects = 0;
10706 this->new_signal_style_mask = 1;
10707 this->current_new_signal_style = nullptr;
10709 this->new_rocks_group = nullptr;
10710 this->new_landscape_ctrl_flags = 0;
10712 /* Mark price_base_multipliers as 'not set' */
10713 for (Price i = PR_BEGIN; i < PR_END; i++) {
10714 this->price_base_multipliers[i] = INVALID_PRICE_MODIFIER;
10717 /* Initialise rail type map with default rail types */
10718 this->railtype_map.fill(INVALID_RAILTYPE);
10719 this->railtype_map[0] = RAILTYPE_RAIL;
10720 this->railtype_map[1] = RAILTYPE_ELECTRIC;
10721 this->railtype_map[2] = RAILTYPE_MONO;
10722 this->railtype_map[3] = RAILTYPE_MAGLEV;
10724 /* Initialise road type map with default road types */
10725 this->roadtype_map.fill(INVALID_ROADTYPE);
10726 this->roadtype_map[0] = ROADTYPE_ROAD;
10728 /* Initialise tram type map with default tram types */
10729 this->tramtype_map.fill(INVALID_ROADTYPE);
10730 this->tramtype_map[0] = ROADTYPE_TRAM;
10732 /* Copy the initial parameter list */
10733 this->param = config->param;
10737 * Find first cargo label that exists and is active from a list of cargo labels.
10738 * @param labels List of cargo labels.
10739 * @returns First cargo label in list that exists, or CT_INVALID if none exist.
10741 static CargoLabel GetActiveCargoLabel(const std::initializer_list<CargoLabel> &labels)
10743 for (const CargoLabel &label : labels) {
10744 CargoID cid = GetCargoIDByLabel(label);
10745 if (cid != INVALID_CARGO) return label;
10747 return CT_INVALID;
10751 * Get active cargo label from either a cargo label or climate-dependent mixed cargo type.
10752 * @param label Cargo label or climate-dependent mixed cargo type.
10753 * @returns Active cargo label, or CT_INVALID if cargo label is not active.
10755 static CargoLabel GetActiveCargoLabel(const std::variant<CargoLabel, MixedCargoType> &label)
10757 struct visitor {
10758 CargoLabel operator()(const CargoLabel &label) { return label; }
10759 CargoLabel operator()(const MixedCargoType &mixed)
10761 switch (mixed) {
10762 case MCT_LIVESTOCK_FRUIT: return GetActiveCargoLabel({CT_LIVESTOCK, CT_FRUIT});
10763 case MCT_GRAIN_WHEAT_MAIZE: return GetActiveCargoLabel({CT_GRAIN, CT_WHEAT, CT_MAIZE});
10764 case MCT_VALUABLES_GOLD_DIAMONDS: return GetActiveCargoLabel({CT_VALUABLES, CT_GOLD, CT_DIAMONDS});
10765 default: NOT_REACHED();
10770 return std::visit(visitor{}, label);
10774 * Precalculate refit masks from cargo classes for all vehicles.
10776 static void CalculateRefitMasks()
10778 CargoTypes original_known_cargoes = 0;
10779 for (CargoID cid = 0; cid != NUM_CARGO; ++cid) {
10780 if (IsDefaultCargo(cid)) SetBit(original_known_cargoes, cid);
10783 for (Engine *e : Engine::Iterate()) {
10784 EngineID engine = e->index;
10785 EngineInfo *ei = &e->info;
10786 bool only_defaultcargo; ///< Set if the vehicle shall carry only the default cargo
10788 /* Apply default cargo translation map if cargo type hasn't been set, either explicitly or by aircraft cargo handling. */
10789 if (!IsValidCargoID(e->info.cargo_type)) {
10790 e->info.cargo_type = GetCargoIDByLabel(GetActiveCargoLabel(e->info.cargo_label));
10793 /* If the NewGRF did not set any cargo properties, we apply default values. */
10794 if (_gted[engine].defaultcargo_grf == nullptr) {
10795 /* If the vehicle has any capacity, apply the default refit masks */
10796 if (e->type != VEH_TRAIN || e->u.rail.capacity != 0) {
10797 static constexpr uint8_t T = 1 << LT_TEMPERATE;
10798 static constexpr uint8_t A = 1 << LT_ARCTIC;
10799 static constexpr uint8_t S = 1 << LT_TROPIC;
10800 static constexpr uint8_t Y = 1 << LT_TOYLAND;
10801 static const struct DefaultRefitMasks {
10802 uint8_t climate;
10803 CargoLabel cargo_label;
10804 CargoClasses cargo_allowed;
10805 CargoClasses cargo_disallowed;
10806 } _default_refit_masks[] = {
10807 {T | A | S | Y, CT_PASSENGERS, CC_PASSENGERS, 0},
10808 {T | A | S , CT_MAIL, CC_MAIL, 0},
10809 {T | A | S , CT_VALUABLES, CC_ARMOURED, CC_LIQUID},
10810 { Y, CT_MAIL, CC_MAIL | CC_ARMOURED, CC_LIQUID},
10811 {T | A , CT_COAL, CC_BULK, 0},
10812 { S , CT_COPPER_ORE, CC_BULK, 0},
10813 { Y, CT_SUGAR, CC_BULK, 0},
10814 {T | A | S , CT_OIL, CC_LIQUID, 0},
10815 { Y, CT_COLA, CC_LIQUID, 0},
10816 {T , CT_GOODS, CC_PIECE_GOODS | CC_EXPRESS, CC_LIQUID | CC_PASSENGERS},
10817 { A | S , CT_GOODS, CC_PIECE_GOODS | CC_EXPRESS, CC_LIQUID | CC_PASSENGERS | CC_REFRIGERATED},
10818 { A | S , CT_FOOD, CC_REFRIGERATED, 0},
10819 { Y, CT_CANDY, CC_PIECE_GOODS | CC_EXPRESS, CC_LIQUID | CC_PASSENGERS},
10822 if (e->type == VEH_AIRCRAFT) {
10823 /* Aircraft default to "light" cargoes */
10824 _gted[engine].cargo_allowed = CC_PASSENGERS | CC_MAIL | CC_ARMOURED | CC_EXPRESS;
10825 _gted[engine].cargo_disallowed = CC_LIQUID;
10826 } else if (e->type == VEH_SHIP) {
10827 CargoLabel label = GetActiveCargoLabel(ei->cargo_label);
10828 switch (label.base()) {
10829 case CT_PASSENGERS.base():
10830 /* Ferries */
10831 _gted[engine].cargo_allowed = CC_PASSENGERS;
10832 _gted[engine].cargo_disallowed = 0;
10833 break;
10834 case CT_OIL.base():
10835 /* Tankers */
10836 _gted[engine].cargo_allowed = CC_LIQUID;
10837 _gted[engine].cargo_disallowed = 0;
10838 break;
10839 default:
10840 /* Cargo ships */
10841 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
10842 /* No tanker in toyland :( */
10843 _gted[engine].cargo_allowed = CC_MAIL | CC_ARMOURED | CC_EXPRESS | CC_BULK | CC_PIECE_GOODS | CC_LIQUID;
10844 _gted[engine].cargo_disallowed = CC_PASSENGERS;
10845 } else {
10846 _gted[engine].cargo_allowed = CC_MAIL | CC_ARMOURED | CC_EXPRESS | CC_BULK | CC_PIECE_GOODS;
10847 _gted[engine].cargo_disallowed = CC_LIQUID | CC_PASSENGERS;
10849 break;
10851 e->u.ship.old_refittable = true;
10852 } else if (e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON) {
10853 /* Train engines default to all cargoes, so you can build single-cargo consists with fast engines.
10854 * Trains loading multiple cargoes may start stations accepting unwanted cargoes. */
10855 _gted[engine].cargo_allowed = CC_PASSENGERS | CC_MAIL | CC_ARMOURED | CC_EXPRESS | CC_BULK | CC_PIECE_GOODS | CC_LIQUID;
10856 _gted[engine].cargo_disallowed = 0;
10857 } else {
10858 /* Train wagons and road vehicles are classified by their default cargo type */
10859 CargoLabel label = GetActiveCargoLabel(ei->cargo_label);
10860 for (const auto &drm : _default_refit_masks) {
10861 if (!HasBit(drm.climate, _settings_game.game_creation.landscape)) continue;
10862 if (drm.cargo_label != label) continue;
10864 _gted[engine].cargo_allowed = drm.cargo_allowed;
10865 _gted[engine].cargo_disallowed = drm.cargo_disallowed;
10866 break;
10869 /* All original cargoes have specialised vehicles, so exclude them */
10870 _gted[engine].ctt_exclude_mask = original_known_cargoes;
10873 _gted[engine].UpdateRefittability(_gted[engine].cargo_allowed != 0);
10875 if (IsValidCargoID(ei->cargo_type)) ClrBit(_gted[engine].ctt_exclude_mask, ei->cargo_type);
10878 /* Compute refittability */
10880 CargoTypes mask = 0;
10881 CargoTypes not_mask = 0;
10882 CargoTypes xor_mask = ei->refit_mask;
10884 /* If the original masks set by the grf are zero, the vehicle shall only carry the default cargo.
10885 * Note: After applying the translations, the vehicle may end up carrying no defined cargo. It becomes unavailable in that case. */
10886 only_defaultcargo = _gted[engine].refittability != GRFTempEngineData::NONEMPTY;
10888 if (_gted[engine].cargo_allowed != 0) {
10889 /* Build up the list of cargo types from the set cargo classes. */
10890 for (const CargoSpec *cs : CargoSpec::Iterate()) {
10891 if ((_gted[engine].cargo_allowed & cs->classes) != 0 && (_gted[engine].cargo_allowed_required & cs->classes) == _gted[engine].cargo_allowed_required) SetBit(mask, cs->Index());
10892 if (_gted[engine].cargo_disallowed & cs->classes) SetBit(not_mask, cs->Index());
10896 ei->refit_mask = ((mask & ~not_mask) ^ xor_mask) & _cargo_mask;
10898 /* Apply explicit refit includes/excludes. */
10899 ei->refit_mask |= _gted[engine].ctt_include_mask;
10900 ei->refit_mask &= ~_gted[engine].ctt_exclude_mask;
10902 /* Custom refit mask callback. */
10903 const GRFFile *file = _gted[e->index].defaultcargo_grf;
10904 if (file == nullptr) file = e->GetGRF();
10905 if (file != nullptr && HasBit(e->info.callback_mask, CBM_VEHICLE_CUSTOM_REFIT)) {
10906 for (const CargoSpec *cs : CargoSpec::Iterate()) {
10907 uint8_t local_slot = file->cargo_map[cs->Index()];
10908 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_CUSTOM_REFIT, cs->classes, local_slot, engine, nullptr);
10909 switch (callback) {
10910 case CALLBACK_FAILED:
10911 case 0:
10912 break; // Do nothing.
10913 case 1: SetBit(ei->refit_mask, cs->Index()); break;
10914 case 2: ClrBit(ei->refit_mask, cs->Index()); break;
10916 default: ErrorUnknownCallbackResult(file->grfid, CBID_VEHICLE_CUSTOM_REFIT, callback);
10922 /* Clear invalid cargoslots (from default vehicles or pre-NewCargo GRFs) */
10923 if (IsValidCargoID(ei->cargo_type) && !HasBit(_cargo_mask, ei->cargo_type)) ei->cargo_type = INVALID_CARGO;
10925 /* Ensure that the vehicle is either not refittable, or that the default cargo is one of the refittable cargoes.
10926 * Note: Vehicles refittable to no cargo are handle differently to vehicle refittable to a single cargo. The latter might have subtypes. */
10927 if (!only_defaultcargo && (e->type != VEH_SHIP || e->u.ship.old_refittable) && IsValidCargoID(ei->cargo_type) && !HasBit(ei->refit_mask, ei->cargo_type)) {
10928 ei->cargo_type = INVALID_CARGO;
10931 /* Check if this engine's cargo type is valid. If not, set to the first refittable
10932 * cargo type. Finally disable the vehicle, if there is still no cargo. */
10933 if (!IsValidCargoID(ei->cargo_type) && ei->refit_mask != 0) {
10934 /* Figure out which CTT to use for the default cargo, if it is 'first refittable'. */
10935 const GRFFile *file = _gted[engine].defaultcargo_grf;
10936 if (file == nullptr) file = e->GetGRF();
10937 if (file != nullptr && file->grf_version >= 8 && !file->cargo_list.empty()) {
10938 /* Use first refittable cargo from cargo translation table */
10939 uint8_t best_local_slot = UINT8_MAX;
10940 for (CargoID cargo_type : SetCargoBitIterator(ei->refit_mask)) {
10941 uint8_t local_slot = file->cargo_map[cargo_type];
10942 if (local_slot < best_local_slot) {
10943 best_local_slot = local_slot;
10944 ei->cargo_type = cargo_type;
10949 if (!IsValidCargoID(ei->cargo_type)) {
10950 /* Use first refittable cargo slot */
10951 ei->cargo_type = (CargoID)FindFirstBit(ei->refit_mask);
10954 if (!IsValidCargoID(ei->cargo_type) && e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON && e->u.rail.capacity == 0) {
10955 /* For train engines which do not carry cargo it does not matter if their cargo type is invalid.
10956 * Fallback to the first available instead, if the cargo type has not been changed (as indicated by
10957 * cargo_label not being CT_INVALID). */
10958 if (GetActiveCargoLabel(ei->cargo_label) != CT_INVALID) {
10959 ei->cargo_type = static_cast<CargoID>(FindFirstBit(_standard_cargo_mask));
10962 if (!IsValidCargoID(ei->cargo_type)) ei->climates = 0;
10964 /* Clear refit_mask for not refittable ships */
10965 if (e->type == VEH_SHIP && !e->u.ship.old_refittable) {
10966 ei->refit_mask = 0;
10971 /** Set to use the correct action0 properties for each canal feature */
10972 static void FinaliseCanals()
10974 for (uint i = 0; i < CF_END; i++) {
10975 if (_water_feature[i].grffile != nullptr) {
10976 _water_feature[i].callback_mask = _water_feature[i].grffile->canal_local_properties[i].callback_mask;
10977 _water_feature[i].flags = _water_feature[i].grffile->canal_local_properties[i].flags;
10982 /** Check for invalid engines */
10983 static void FinaliseEngineArray()
10985 for (Engine *e : Engine::Iterate()) {
10986 if (e->GetGRF() == nullptr) {
10987 const EngineIDMapping &eid = _engine_mngr.mappings[e->index];
10988 if (eid.grfid != INVALID_GRFID || eid.internal_id != eid.substitute_id) {
10989 e->info.string_id = STR_NEWGRF_INVALID_ENGINE;
10993 /* Do final mapping on variant engine ID. */
10994 if (e->info.variant_id != INVALID_ENGINE) {
10995 e->info.variant_id = GetNewEngineID(e->grf_prop.grffile, e->type, e->info.variant_id);
10998 if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
11000 /* Skip wagons, there livery is defined via the engine */
11001 if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) {
11002 LiveryScheme ls = GetEngineLiveryScheme(e->index, INVALID_ENGINE, nullptr);
11003 SetBit(_loaded_newgrf_features.used_liveries, ls);
11004 /* Note: For ships and roadvehicles we assume that they cannot be refitted between passenger and freight */
11006 if (e->type == VEH_TRAIN) {
11007 SetBit(_loaded_newgrf_features.used_liveries, LS_FREIGHT_WAGON);
11008 switch (ls) {
11009 case LS_STEAM:
11010 case LS_DIESEL:
11011 case LS_ELECTRIC:
11012 case LS_MONORAIL:
11013 case LS_MAGLEV:
11014 SetBit(_loaded_newgrf_features.used_liveries, LS_PASSENGER_WAGON_STEAM + ls - LS_STEAM);
11015 break;
11017 case LS_DMU:
11018 case LS_EMU:
11019 SetBit(_loaded_newgrf_features.used_liveries, LS_PASSENGER_WAGON_DIESEL + ls - LS_DMU);
11020 break;
11022 default: NOT_REACHED();
11028 /* Check engine variants don't point back on themselves (either directly or via a loop) then set appropriate flags
11029 * on variant engine. This is performed separately as all variant engines need to have been resolved. */
11030 for (Engine *e : Engine::Iterate()) {
11031 EngineID parent = e->info.variant_id;
11032 while (parent != INVALID_ENGINE) {
11033 parent = Engine::Get(parent)->info.variant_id;
11034 if (parent != e->index) continue;
11036 /* Engine looped back on itself, so clear the variant. */
11037 e->info.variant_id = INVALID_ENGINE;
11039 GrfMsg(1, "FinaliseEngineArray: Variant of engine {:x} in '{}' loops back on itself", _engine_mngr.mappings[e->index].internal_id, e->GetGRF()->filename);
11040 break;
11043 if (e->info.variant_id != INVALID_ENGINE) {
11044 Engine::Get(e->info.variant_id)->display_flags |= EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded;
11049 /** Check for invalid cargoes */
11050 void FinaliseCargoArray()
11052 for (CargoSpec &cs : CargoSpec::array) {
11053 if (cs.town_production_effect == INVALID_TPE) {
11054 /* Set default town production effect by cargo label. */
11055 switch (cs.label.base()) {
11056 case CT_PASSENGERS.base(): cs.town_production_effect = TPE_PASSENGERS; break;
11057 case CT_MAIL.base(): cs.town_production_effect = TPE_MAIL; break;
11058 default: cs.town_production_effect = TPE_NONE; break;
11061 if (!cs.IsValid()) {
11062 cs.name = cs.name_single = cs.units_volume = STR_NEWGRF_INVALID_CARGO;
11063 cs.quantifier = STR_NEWGRF_INVALID_CARGO_QUANTITY;
11064 cs.abbrev = STR_NEWGRF_INVALID_CARGO_ABBREV;
11070 * Check if a given housespec is valid and disable it if it's not.
11071 * The housespecs that follow it are used to check the validity of
11072 * multitile houses.
11073 * @param hs The housespec to check.
11074 * @param next1 The housespec that follows \c hs.
11075 * @param next2 The housespec that follows \c next1.
11076 * @param next3 The housespec that follows \c next2.
11077 * @param filename The filename of the newgrf this house was defined in.
11078 * @return Whether the given housespec is valid.
11080 static bool IsHouseSpecValid(HouseSpec *hs, const HouseSpec *next1, const HouseSpec *next2, const HouseSpec *next3, const std::string &filename)
11082 if (((hs->building_flags & BUILDING_HAS_2_TILES) != 0 &&
11083 (next1 == nullptr || !next1->enabled || (next1->building_flags & BUILDING_HAS_1_TILE) != 0)) ||
11084 ((hs->building_flags & BUILDING_HAS_4_TILES) != 0 &&
11085 (next2 == nullptr || !next2->enabled || (next2->building_flags & BUILDING_HAS_1_TILE) != 0 ||
11086 next3 == nullptr || !next3->enabled || (next3->building_flags & BUILDING_HAS_1_TILE) != 0))) {
11087 hs->enabled = false;
11088 if (!filename.empty()) Debug(grf, 1, "FinaliseHouseArray: {} defines house {} as multitile, but no suitable tiles follow. Disabling house.", filename, hs->grf_prop.local_id);
11089 return false;
11092 /* Some places sum population by only counting north tiles. Other places use all tiles causing desyncs.
11093 * As the newgrf specs define population to be zero for non-north tiles, we just disable the offending house.
11094 * If you want to allow non-zero populations somewhen, make sure to sum the population of all tiles in all places. */
11095 if (((hs->building_flags & BUILDING_HAS_2_TILES) != 0 && next1->population != 0) ||
11096 ((hs->building_flags & BUILDING_HAS_4_TILES) != 0 && (next2->population != 0 || next3->population != 0))) {
11097 hs->enabled = false;
11098 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);
11099 return false;
11102 /* Substitute type is also used for override, and having an override with a different size causes crashes.
11103 * This check should only be done for NewGRF houses because grf_prop.subst_id is not set for original houses.*/
11104 if (!filename.empty() && (hs->building_flags & BUILDING_HAS_1_TILE) != (HouseSpec::Get(hs->grf_prop.subst_id)->building_flags & BUILDING_HAS_1_TILE)) {
11105 hs->enabled = false;
11106 Debug(grf, 1, "FinaliseHouseArray: {} defines house {} with different house size then it's substitute type. Disabling house.", filename, hs->grf_prop.local_id);
11107 return false;
11110 /* Make sure that additional parts of multitile houses are not available. */
11111 if ((hs->building_flags & BUILDING_HAS_1_TILE) == 0 && (hs->building_availability & HZ_ZONALL) != 0 && (hs->building_availability & HZ_CLIMALL) != 0) {
11112 hs->enabled = false;
11113 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);
11114 return false;
11117 return true;
11121 * Make sure there is at least one house available in the year 0 for the given
11122 * climate / housezone combination.
11123 * @param bitmask The climate and housezone to check for. Exactly one climate
11124 * bit and one housezone bit should be set.
11126 static void EnsureEarlyHouse(HouseZones bitmask)
11128 CalTime::Year min_year = CalTime::MAX_YEAR;
11130 for (const auto &hs : HouseSpec::Specs()) {
11131 if (!hs.enabled) continue;
11132 if ((hs.building_availability & bitmask) != bitmask) continue;
11133 if (hs.min_year < min_year) min_year = hs.min_year;
11136 if (min_year == 0) return;
11138 for (auto &hs : HouseSpec::Specs()) {
11139 if (!hs.enabled) continue;
11140 if ((hs.building_availability & bitmask) != bitmask) continue;
11141 if (hs.min_year == min_year) hs.min_year = CalTime::MIN_YEAR;
11146 * Add all new houses to the house array. House properties can be set at any
11147 * time in the GRF file, so we can only add a house spec to the house array
11148 * after the file has finished loading. We also need to check the dates, due to
11149 * the TTDPatch behaviour described below that we need to emulate.
11151 static void FinaliseHouseArray()
11153 /* If there are no houses with start dates before 1930, then all houses
11154 * with start dates of 1930 have them reset to 0. This is in order to be
11155 * compatible with TTDPatch, where if no houses have start dates before
11156 * 1930 and the date is before 1930, the game pretends that this is 1930.
11157 * If there have been any houses defined with start dates before 1930 then
11158 * the dates are left alone.
11159 * On the other hand, why 1930? Just 'fix' the houses with the lowest
11160 * minimum introduction date to 0.
11162 for (GRFFile * const file : _grf_files) {
11163 if (file->housespec.empty()) continue;
11165 size_t num_houses = file->housespec.size();
11166 for (size_t i = 0; i < num_houses; i++) {
11167 HouseSpec *hs = file->housespec[i].get();
11169 if (hs == nullptr) continue;
11171 const HouseSpec *next1 = (i + 1 < num_houses ? file->housespec[i + 1].get() : nullptr);
11172 const HouseSpec *next2 = (i + 2 < num_houses ? file->housespec[i + 2].get() : nullptr);
11173 const HouseSpec *next3 = (i + 3 < num_houses ? file->housespec[i + 3].get() : nullptr);
11175 if (!IsHouseSpecValid(hs, next1, next2, next3, file->filename)) continue;
11177 _house_mngr.SetEntitySpec(hs);
11181 for (size_t i = 0; i < HouseSpec::Specs().size(); i++) {
11182 HouseSpec *hs = HouseSpec::Get(i);
11183 const HouseSpec *next1 = (i + 1 < NUM_HOUSES ? HouseSpec::Get(i + 1) : nullptr);
11184 const HouseSpec *next2 = (i + 2 < NUM_HOUSES ? HouseSpec::Get(i + 2) : nullptr);
11185 const HouseSpec *next3 = (i + 3 < NUM_HOUSES ? HouseSpec::Get(i + 3) : nullptr);
11187 /* We need to check all houses again to we are sure that multitile houses
11188 * did get consecutive IDs and none of the parts are missing. */
11189 if (!IsHouseSpecValid(hs, next1, next2, next3, std::string{})) {
11190 /* GetHouseNorthPart checks 3 houses that are directly before
11191 * it in the house pool. If any of those houses have multi-tile
11192 * flags set it assumes it's part of a multitile house. Since
11193 * we can have invalid houses in the pool marked as disabled, we
11194 * don't want to have them influencing valid tiles. As such set
11195 * building_flags to zero here to make sure any house following
11196 * this one in the pool is properly handled as 1x1 house. */
11197 hs->building_flags = TILE_NO_FLAG;
11200 /* Apply default cargo translation map for unset cargo slots */
11201 for (uint i = 0; i < lengthof(hs->accepts_cargo_label); ++i) {
11202 if (!IsValidCargoID(hs->accepts_cargo[i])) hs->accepts_cargo[i] = GetCargoIDByLabel(hs->accepts_cargo_label[i]);
11203 /* Disable acceptance if cargo type is invalid. */
11204 if (!IsValidCargoID(hs->accepts_cargo[i])) hs->cargo_acceptance[i] = 0;
11208 HouseZones climate_mask = (HouseZones)(1 << (_settings_game.game_creation.landscape + 12));
11209 EnsureEarlyHouse(HZ_ZON1 | climate_mask);
11210 EnsureEarlyHouse(HZ_ZON2 | climate_mask);
11211 EnsureEarlyHouse(HZ_ZON3 | climate_mask);
11212 EnsureEarlyHouse(HZ_ZON4 | climate_mask);
11213 EnsureEarlyHouse(HZ_ZON5 | climate_mask);
11215 if (_settings_game.game_creation.landscape == LT_ARCTIC) {
11216 EnsureEarlyHouse(HZ_ZON1 | HZ_SUBARTC_ABOVE);
11217 EnsureEarlyHouse(HZ_ZON2 | HZ_SUBARTC_ABOVE);
11218 EnsureEarlyHouse(HZ_ZON3 | HZ_SUBARTC_ABOVE);
11219 EnsureEarlyHouse(HZ_ZON4 | HZ_SUBARTC_ABOVE);
11220 EnsureEarlyHouse(HZ_ZON5 | HZ_SUBARTC_ABOVE);
11225 * Add all new industries to the industry array. Industry properties can be set at any
11226 * time in the GRF file, so we can only add a industry spec to the industry array
11227 * after the file has finished loading.
11229 static void FinaliseIndustriesArray()
11231 for (GRFFile * const file : _grf_files) {
11232 for (const auto &indsp : file->industryspec) {
11233 if (indsp == nullptr || !indsp->enabled) continue;
11235 _industry_mngr.SetEntitySpec(indsp.get());
11238 for (const auto &indtsp : file->indtspec) {
11239 if (indtsp != nullptr) {
11240 _industile_mngr.SetEntitySpec(indtsp.get());
11245 for (auto &indsp : _industry_specs) {
11246 if (indsp.enabled && indsp.grf_prop.HasGrfFile()) {
11247 for (auto &conflicting : indsp.conflicting) {
11248 conflicting = MapNewGRFIndustryType(conflicting, indsp.grf_prop.grfid);
11251 if (!indsp.enabled) {
11252 indsp.name = STR_NEWGRF_INVALID_INDUSTRYTYPE;
11255 /* Apply default cargo translation map for unset cargo slots */
11256 for (size_t i = 0; i < std::size(indsp.produced_cargo_label); ++i) {
11257 if (!IsValidCargoID(indsp.produced_cargo[i])) indsp.produced_cargo[i] = GetCargoIDByLabel(GetActiveCargoLabel(indsp.produced_cargo_label[i]));
11259 for (size_t i = 0; i < std::size(indsp.accepts_cargo_label); ++i) {
11260 if (!IsValidCargoID(indsp.accepts_cargo[i])) indsp.accepts_cargo[i] = GetCargoIDByLabel(GetActiveCargoLabel(indsp.accepts_cargo_label[i]));
11264 for (auto &indtsp : _industry_tile_specs) {
11265 /* Apply default cargo translation map for unset cargo slots */
11266 for (size_t i = 0; i < std::size(indtsp.accepts_cargo_label); ++i) {
11267 if (!IsValidCargoID(indtsp.accepts_cargo[i])) indtsp.accepts_cargo[i] = GetCargoIDByLabel(GetActiveCargoLabel(indtsp.accepts_cargo_label[i]));
11273 * Add all new objects to the object array. Object properties can be set at any
11274 * time in the GRF file, so we can only add an object spec to the object array
11275 * after the file has finished loading.
11277 static void FinaliseObjectsArray()
11279 for (GRFFile * const file : _grf_files) {
11280 for (auto &objectspec : file->objectspec) {
11281 if (objectspec != nullptr && objectspec->grf_prop.HasGrfFile() && objectspec->IsEnabled()) {
11282 _object_mngr.SetEntitySpec(objectspec.get());
11287 ObjectSpec::BindToClasses();
11291 * Add all new airports to the airport array. Airport properties can be set at any
11292 * time in the GRF file, so we can only add a airport spec to the airport array
11293 * after the file has finished loading.
11295 static void FinaliseAirportsArray()
11297 for (GRFFile * const file : _grf_files) {
11298 for (auto &as : file->airportspec) {
11299 if (as != nullptr && as->enabled) {
11300 _airport_mngr.SetEntitySpec(as.get());
11304 for (auto &ats : file->airtspec) {
11305 if (ats != nullptr && ats->enabled) {
11306 _airporttile_mngr.SetEntitySpec(ats.get());
11312 /* Here we perform initial decoding of some special sprites (as are they
11313 * described at http://www.ttdpatch.net/src/newgrf.txt, but this is only a very
11314 * partial implementation yet).
11315 * XXX: We consider GRF files trusted. It would be trivial to exploit OTTD by
11316 * a crafted invalid GRF file. We should tell that to the user somehow, or
11317 * better make this more robust in the future. */
11318 static void DecodeSpecialSprite(uint8_t *buf, uint num, GrfLoadingStage stage)
11320 /* XXX: There is a difference between staged loading in TTDPatch and
11321 * here. In TTDPatch, for some reason actions 1 and 2 are carried out
11322 * during stage 1, whilst action 3 is carried out during stage 2 (to
11323 * "resolve" cargo IDs... wtf). This is a little problem, because cargo
11324 * IDs are valid only within a given set (action 1) block, and may be
11325 * overwritten after action 3 associates them. But overwriting happens
11326 * in an earlier stage than associating, so... We just process actions
11327 * 1 and 2 in stage 2 now, let's hope that won't get us into problems.
11328 * --pasky
11329 * We need a pre-stage to set up GOTO labels of Action 0x10 because the grf
11330 * is not in memory and scanning the file every time would be too expensive.
11331 * In other stages we skip action 0x10 since it's already dealt with. */
11332 static const SpecialSpriteHandler handlers[][GLS_END] = {
11333 /* 0x00 */ { nullptr, SafeChangeInfo, nullptr, nullptr, ReserveChangeInfo, FeatureChangeInfo, },
11334 /* 0x01 */ { SkipAct1, SkipAct1, SkipAct1, SkipAct1, SkipAct1, NewSpriteSet, },
11335 /* 0x02 */ { nullptr, nullptr, nullptr, nullptr, nullptr, NewSpriteGroup, },
11336 /* 0x03 */ { nullptr, GRFUnsafe, nullptr, nullptr, nullptr, FeatureMapSpriteGroup, },
11337 /* 0x04 */ { nullptr, nullptr, nullptr, nullptr, nullptr, FeatureNewName, },
11338 /* 0x05 */ { SkipAct5, SkipAct5, SkipAct5, SkipAct5, SkipAct5, GraphicsNew, },
11339 /* 0x06 */ { nullptr, nullptr, nullptr, CfgApply, CfgApply, CfgApply, },
11340 /* 0x07 */ { nullptr, nullptr, nullptr, nullptr, SkipIf, SkipIf, },
11341 /* 0x08 */ { ScanInfo, nullptr, nullptr, GRFInfo, GRFInfo, GRFInfo, },
11342 /* 0x09 */ { nullptr, nullptr, nullptr, SkipIf, SkipIf, SkipIf, },
11343 /* 0x0A */ { SkipActA, SkipActA, SkipActA, SkipActA, SkipActA, SpriteReplace, },
11344 /* 0x0B */ { nullptr, nullptr, nullptr, GRFLoadError, GRFLoadError, GRFLoadError, },
11345 /* 0x0C */ { nullptr, nullptr, nullptr, GRFComment, nullptr, GRFComment, },
11346 /* 0x0D */ { nullptr, SafeParamSet, nullptr, ParamSet, ParamSet, ParamSet, },
11347 /* 0x0E */ { nullptr, SafeGRFInhibit, nullptr, GRFInhibit, GRFInhibit, GRFInhibit, },
11348 /* 0x0F */ { nullptr, GRFUnsafe, nullptr, FeatureTownName, nullptr, nullptr, },
11349 /* 0x10 */ { nullptr, nullptr, DefineGotoLabel, nullptr, nullptr, nullptr, },
11350 /* 0x11 */ { SkipAct11, GRFUnsafe, SkipAct11, GRFSound, SkipAct11, GRFSound, },
11351 /* 0x12 */ { SkipAct12, SkipAct12, SkipAct12, SkipAct12, SkipAct12, LoadFontGlyph, },
11352 /* 0x13 */ { nullptr, nullptr, nullptr, nullptr, nullptr, TranslateGRFStrings, },
11353 /* 0x14 */ { StaticGRFInfo, nullptr, nullptr, Act14FeatureTest,nullptr, nullptr, },
11356 GRFLocation location(_cur.grfconfig->ident.grfid, _cur.nfo_line);
11358 GRFLineToSpriteOverride::iterator it = _grf_line_to_action6_sprite_override.find(location);
11359 _action6_override_active = (it != _grf_line_to_action6_sprite_override.end());
11360 if (it == _grf_line_to_action6_sprite_override.end()) {
11361 /* No preloaded sprite to work with; read the
11362 * pseudo sprite content. */
11363 _cur.file->ReadBlock(buf, num);
11364 } else {
11365 /* Use the preloaded sprite data. */
11366 buf = it->second.get();
11367 GrfMsg(7, "DecodeSpecialSprite: Using preloaded pseudo sprite data");
11369 /* Skip the real (original) content of this action. */
11370 _cur.file->SeekTo(num, SEEK_CUR);
11373 ByteReader br(buf, buf + num);
11375 try {
11376 uint8_t action = br.ReadByte();
11378 if (action == 0xFF) {
11379 GrfMsg(2, "DecodeSpecialSprite: Unexpected data block, skipping");
11380 } else if (action == 0xFE) {
11381 GrfMsg(2, "DecodeSpecialSprite: Unexpected import block, skipping");
11382 } else if (action >= lengthof(handlers)) {
11383 GrfMsg(7, "DecodeSpecialSprite: Skipping unknown action 0x{:02X}", action);
11384 } else if (handlers[action][stage] == nullptr) {
11385 GrfMsg(7, "DecodeSpecialSprite: Skipping action 0x{:02X} in stage {}", action, stage);
11386 } else {
11387 GrfMsg(7, "DecodeSpecialSprite: Handling action 0x{:02X} in stage {}", action, stage);
11388 handlers[action][stage](br);
11390 } catch (...) {
11391 GrfMsg(1, "DecodeSpecialSprite: Tried to read past end of pseudo-sprite data");
11392 DisableGrf(STR_NEWGRF_ERROR_READ_BOUNDS);
11397 * Load a particular NewGRF from a SpriteFile.
11398 * @param config The configuration of the to be loaded NewGRF.
11399 * @param stage The loading stage of the NewGRF.
11400 * @param file The file to load the GRF data from.
11402 static void LoadNewGRFFileFromFile(GRFConfig *config, GrfLoadingStage stage, SpriteFile &file)
11404 _cur.file = &file;
11405 _cur.grfconfig = config;
11407 Debug(grf, 2, "LoadNewGRFFile: Reading NewGRF-file '{}'", config->GetDisplayPath());
11409 uint8_t grf_container_version = file.GetContainerVersion();
11410 if (grf_container_version == 0) {
11411 Debug(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format");
11412 return;
11415 if (stage == GLS_INIT || stage == GLS_ACTIVATION) {
11416 /* We need the sprite offsets in the init stage for NewGRF sounds
11417 * and in the activation stage for real sprites. */
11418 ReadGRFSpriteOffsets(file);
11419 } else {
11420 /* Skip sprite section offset if present. */
11421 if (grf_container_version >= 2) file.ReadDword();
11424 if (grf_container_version >= 2) {
11425 /* Read compression value. */
11426 uint8_t compression = file.ReadByte();
11427 if (compression != 0) {
11428 Debug(grf, 7, "LoadNewGRFFile: Unsupported compression format");
11429 return;
11433 /* Skip the first sprite; we don't care about how many sprites this
11434 * does contain; newest TTDPatches and George's longvehicles don't
11435 * neither, apparently. */
11436 uint32_t num = grf_container_version >= 2 ? file.ReadDword() : file.ReadWord();
11437 if (num == 4 && file.ReadByte() == 0xFF) {
11438 file.ReadDword();
11439 } else {
11440 Debug(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format");
11441 return;
11444 _cur.ClearDataForNextFile();
11446 ReusableBuffer<uint8_t> buf;
11448 while ((num = (grf_container_version >= 2 ? file.ReadDword() : file.ReadWord())) != 0) {
11449 uint8_t type = file.ReadByte();
11450 _cur.nfo_line++;
11452 if (type == 0xFF) {
11453 if (_cur.skip_sprites == 0) {
11454 DecodeSpecialSprite(buf.Allocate(num), num, stage);
11456 /* Stop all processing if we are to skip the remaining sprites */
11457 if (_cur.skip_sprites == -1) break;
11459 continue;
11460 } else {
11461 file.SkipBytes(num);
11463 } else {
11464 if (_cur.skip_sprites == 0) {
11465 GrfMsg(0, "LoadNewGRFFile: Unexpected sprite, disabling");
11466 DisableGrf(STR_NEWGRF_ERROR_UNEXPECTED_SPRITE);
11467 break;
11470 if (grf_container_version >= 2 && type == 0xFD) {
11471 /* Reference to data section. Container version >= 2 only. */
11472 file.SkipBytes(num);
11473 } else {
11474 file.SkipBytes(7);
11475 SkipSpriteData(file, type, num - 8);
11479 if (_cur.skip_sprites > 0) _cur.skip_sprites--;
11484 * Load a particular NewGRF.
11485 * @param config The configuration of the to be loaded NewGRF.
11486 * @param stage The loading stage of the NewGRF.
11487 * @param subdir The sub directory to find the NewGRF in.
11488 * @param temporary The NewGRF/sprite file is to be loaded temporarily and should be closed immediately,
11489 * contrary to loading the SpriteFile and having it cached by the SpriteCache.
11491 void LoadNewGRFFile(GRFConfig *config, GrfLoadingStage stage, Subdirectory subdir, bool temporary)
11493 const std::string &filename = config->filename;
11495 /* A .grf file is activated only if it was active when the game was
11496 * started. If a game is loaded, only its active .grfs will be
11497 * reactivated, unless "loadallgraphics on" is used. A .grf file is
11498 * considered active if its action 8 has been processed, i.e. its
11499 * action 8 hasn't been skipped using an action 7.
11501 * During activation, only actions 0, 1, 2, 3, 4, 5, 7, 8, 9, 0A and 0B are
11502 * carried out. All others are ignored, because they only need to be
11503 * processed once at initialization. */
11504 if (stage != GLS_FILESCAN && stage != GLS_SAFETYSCAN && stage != GLS_LABELSCAN) {
11505 _cur.grffile = GetFileByFilename(filename);
11506 if (_cur.grffile == nullptr) UserError("File '{}' lost in cache.\n", filename);
11507 if (stage == GLS_RESERVE && config->status != GCS_INITIALISED) return;
11508 if (stage == GLS_ACTIVATION && !HasBit(config->flags, GCF_RESERVED)) return;
11511 bool needs_palette_remap = config->palette & GRFP_USE_MASK;
11512 if (temporary) {
11513 SpriteFile temporarySpriteFile(filename, subdir, needs_palette_remap);
11514 LoadNewGRFFileFromFile(config, stage, temporarySpriteFile);
11515 } else {
11516 SpriteFile &file = OpenCachedSpriteFile(filename, subdir, needs_palette_remap);
11517 LoadNewGRFFileFromFile(config, stage, file);
11518 if (!HasBit(config->flags, GCF_SYSTEM)) file.flags |= SFF_USERGRF;
11519 if (config->ident.grfid == BSWAP32(0xFFFFFFFE)) file.flags |= SFF_OPENTTDGRF;
11524 * Relocates the old shore sprites at new positions.
11526 * 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)
11527 * 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)
11528 * 3. If a newgrf replaces shore sprites by Action5 any shore replacement by ActionA has no effect. (SHORE_REPLACE_ACTION_5)
11530 static void ActivateOldShore()
11532 /* Use default graphics, if no shore sprites were loaded.
11533 * Should not happen, as the base set's extra grf should include some. */
11534 if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_A;
11536 if (_loaded_newgrf_features.shore != SHORE_REPLACE_ACTION_5) {
11537 DupSprite(SPR_ORIGINALSHORE_START + 1, SPR_SHORE_BASE + 1); // SLOPE_W
11538 DupSprite(SPR_ORIGINALSHORE_START + 2, SPR_SHORE_BASE + 2); // SLOPE_S
11539 DupSprite(SPR_ORIGINALSHORE_START + 6, SPR_SHORE_BASE + 3); // SLOPE_SW
11540 DupSprite(SPR_ORIGINALSHORE_START + 0, SPR_SHORE_BASE + 4); // SLOPE_E
11541 DupSprite(SPR_ORIGINALSHORE_START + 4, SPR_SHORE_BASE + 6); // SLOPE_SE
11542 DupSprite(SPR_ORIGINALSHORE_START + 3, SPR_SHORE_BASE + 8); // SLOPE_N
11543 DupSprite(SPR_ORIGINALSHORE_START + 7, SPR_SHORE_BASE + 9); // SLOPE_NW
11544 DupSprite(SPR_ORIGINALSHORE_START + 5, SPR_SHORE_BASE + 12); // SLOPE_NE
11547 if (_loaded_newgrf_features.shore == SHORE_REPLACE_ACTION_A) {
11548 DupSprite(SPR_FLAT_GRASS_TILE + 16, SPR_SHORE_BASE + 0); // SLOPE_STEEP_S
11549 DupSprite(SPR_FLAT_GRASS_TILE + 17, SPR_SHORE_BASE + 5); // SLOPE_STEEP_W
11550 DupSprite(SPR_FLAT_GRASS_TILE + 7, SPR_SHORE_BASE + 7); // SLOPE_WSE
11551 DupSprite(SPR_FLAT_GRASS_TILE + 15, SPR_SHORE_BASE + 10); // SLOPE_STEEP_N
11552 DupSprite(SPR_FLAT_GRASS_TILE + 11, SPR_SHORE_BASE + 11); // SLOPE_NWS
11553 DupSprite(SPR_FLAT_GRASS_TILE + 13, SPR_SHORE_BASE + 13); // SLOPE_ENW
11554 DupSprite(SPR_FLAT_GRASS_TILE + 14, SPR_SHORE_BASE + 14); // SLOPE_SEN
11555 DupSprite(SPR_FLAT_GRASS_TILE + 18, SPR_SHORE_BASE + 15); // SLOPE_STEEP_E
11557 /* XXX - SLOPE_EW, SLOPE_NS are currently not used.
11558 * If they would be used somewhen, then these grass tiles will most like not look as needed */
11559 DupSprite(SPR_FLAT_GRASS_TILE + 5, SPR_SHORE_BASE + 16); // SLOPE_EW
11560 DupSprite(SPR_FLAT_GRASS_TILE + 10, SPR_SHORE_BASE + 17); // SLOPE_NS
11565 * Replocate the old tram depot sprites to the new position, if no new ones were loaded.
11567 static void ActivateOldTramDepot()
11569 if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK) {
11570 DupSprite(SPR_ROAD_DEPOT + 0, SPR_TRAMWAY_DEPOT_NO_TRACK + 0); // use road depot graphics for "no tracks"
11571 DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 1, SPR_TRAMWAY_DEPOT_NO_TRACK + 1);
11572 DupSprite(SPR_ROAD_DEPOT + 2, SPR_TRAMWAY_DEPOT_NO_TRACK + 2); // use road depot graphics for "no tracks"
11573 DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 3, SPR_TRAMWAY_DEPOT_NO_TRACK + 3);
11574 DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 4, SPR_TRAMWAY_DEPOT_NO_TRACK + 4);
11575 DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 5, SPR_TRAMWAY_DEPOT_NO_TRACK + 5);
11580 * Decide whether price base multipliers of grfs shall apply globally or only to the grf specifying them
11582 static void FinalisePriceBaseMultipliers()
11584 extern const PriceBaseSpec _price_base_specs[];
11585 /** Features, to which '_grf_id_overrides' applies. Currently vehicle features only. */
11586 static const uint32_t override_features = (1 << GSF_TRAINS) | (1 << GSF_ROADVEHICLES) | (1 << GSF_SHIPS) | (1 << GSF_AIRCRAFT);
11588 /* Evaluate grf overrides */
11589 int num_grfs = (uint)_grf_files.size();
11590 TempBufferST<int> grf_overrides(num_grfs);
11591 for (int i = 0; i < num_grfs; i++) {
11592 grf_overrides[i] = -1;
11594 GRFFile *source = _grf_files[i];
11595 auto it = _grf_id_overrides.find(source->grfid);
11596 if (it == std::end(_grf_id_overrides)) continue;
11597 uint32_t override = it->second;
11599 GRFFile *dest = GetFileByGRFID(override);
11600 if (dest == nullptr) continue;
11602 grf_overrides[i] = find_index(_grf_files, dest);
11603 assert(grf_overrides[i] >= 0);
11606 /* Override features and price base multipliers of earlier loaded grfs */
11607 for (int i = 0; i < num_grfs; i++) {
11608 if (grf_overrides[i] < 0 || grf_overrides[i] >= i) continue;
11609 GRFFile *source = _grf_files[i];
11610 GRFFile *dest = _grf_files[grf_overrides[i]];
11612 uint32_t features = (source->grf_features | dest->grf_features) & override_features;
11613 source->grf_features |= features;
11614 dest->grf_features |= features;
11616 for (Price p = PR_BEGIN; p < PR_END; p++) {
11617 /* No price defined -> nothing to do */
11618 if (!HasBit(features, _price_base_specs[p].grf_feature) || source->price_base_multipliers[p] == INVALID_PRICE_MODIFIER) continue;
11619 Debug(grf, 3, "'{}' overrides price base multiplier {} of '{}'", source->filename, p, dest->filename);
11620 dest->price_base_multipliers[p] = source->price_base_multipliers[p];
11624 /* Propagate features and price base multipliers of afterwards loaded grfs, if none is present yet */
11625 for (int i = num_grfs - 1; i >= 0; i--) {
11626 if (grf_overrides[i] < 0 || grf_overrides[i] <= i) continue;
11627 GRFFile *source = _grf_files[i];
11628 GRFFile *dest = _grf_files[grf_overrides[i]];
11630 uint32_t features = (source->grf_features | dest->grf_features) & override_features;
11631 source->grf_features |= features;
11632 dest->grf_features |= features;
11634 for (Price p = PR_BEGIN; p < PR_END; p++) {
11635 /* Already a price defined -> nothing to do */
11636 if (!HasBit(features, _price_base_specs[p].grf_feature) || dest->price_base_multipliers[p] != INVALID_PRICE_MODIFIER) continue;
11637 Debug(grf, 3, "Price base multiplier {} from '{}' propagated to '{}'", p, source->filename, dest->filename);
11638 dest->price_base_multipliers[p] = source->price_base_multipliers[p];
11642 /* The 'master grf' now have the correct multipliers. Assign them to the 'addon grfs' to make everything consistent. */
11643 for (int i = 0; i < num_grfs; i++) {
11644 if (grf_overrides[i] < 0) continue;
11645 GRFFile *source = _grf_files[i];
11646 GRFFile *dest = _grf_files[grf_overrides[i]];
11648 uint32_t features = (source->grf_features | dest->grf_features) & override_features;
11649 source->grf_features |= features;
11650 dest->grf_features |= features;
11652 for (Price p = PR_BEGIN; p < PR_END; p++) {
11653 if (!HasBit(features, _price_base_specs[p].grf_feature)) continue;
11654 if (source->price_base_multipliers[p] != dest->price_base_multipliers[p]) {
11655 Debug(grf, 3, "Price base multiplier {} from '{}' propagated to '{}'", p, dest->filename, source->filename);
11657 source->price_base_multipliers[p] = dest->price_base_multipliers[p];
11661 /* Apply fallback prices for grf version < 8 */
11662 for (GRFFile * const file : _grf_files) {
11663 if (file->grf_version >= 8) continue;
11664 PriceMultipliers &price_base_multipliers = file->price_base_multipliers;
11665 for (Price p = PR_BEGIN; p < PR_END; p++) {
11666 Price fallback_price = _price_base_specs[p].fallback_price;
11667 if (fallback_price != INVALID_PRICE && price_base_multipliers[p] == INVALID_PRICE_MODIFIER) {
11668 /* No price multiplier has been set.
11669 * So copy the multiplier from the fallback price, maybe a multiplier was set there. */
11670 price_base_multipliers[p] = price_base_multipliers[fallback_price];
11675 /* Decide local/global scope of price base multipliers */
11676 for (GRFFile * const file : _grf_files) {
11677 PriceMultipliers &price_base_multipliers = file->price_base_multipliers;
11678 for (Price p = PR_BEGIN; p < PR_END; p++) {
11679 if (price_base_multipliers[p] == INVALID_PRICE_MODIFIER) {
11680 /* No multiplier was set; set it to a neutral value */
11681 price_base_multipliers[p] = 0;
11682 } else {
11683 if (!HasBit(file->grf_features, _price_base_specs[p].grf_feature)) {
11684 /* The grf does not define any objects of the feature,
11685 * so it must be a difficulty setting. Apply it globally */
11686 Debug(grf, 3, "'{}' sets global price base multiplier {} to {}", file->filename, p, price_base_multipliers[p]);
11687 SetPriceBaseMultiplier(p, price_base_multipliers[p]);
11688 price_base_multipliers[p] = 0;
11689 } else {
11690 Debug(grf, 3, "'{}' sets local price base multiplier {} to {}", file->filename, p, price_base_multipliers[p]);
11697 extern void InitGRFTownGeneratorNames();
11699 /** Finish loading NewGRFs and execute needed post-processing */
11700 static void AfterLoadGRFs()
11702 ReleaseVarAction2OptimisationCaches();
11704 for (StringIDMapping &it : _string_to_grf_mapping) {
11705 StringID str = MapGRFStringID(it.grf, it.source);
11706 if (it.func == nullptr) {
11707 *reinterpret_cast<StringID *>(it.func_data) = str;
11708 } else {
11709 it.func(str, it.func_data);
11712 _string_to_grf_mapping.clear();
11714 /* Clear the action 6 override sprites. */
11715 _grf_line_to_action6_sprite_override.clear();
11717 /* Polish cargoes */
11718 FinaliseCargoArray();
11720 /* Pre-calculate all refit masks after loading GRF files. */
11721 CalculateRefitMasks();
11723 /* Polish engines */
11724 FinaliseEngineArray();
11726 /* Set the actually used Canal properties */
11727 FinaliseCanals();
11729 /* Add all new houses to the house array. */
11730 FinaliseHouseArray();
11732 /* Add all new industries to the industry array. */
11733 FinaliseIndustriesArray();
11735 /* Add all new objects to the object array. */
11736 FinaliseObjectsArray();
11738 InitializeSortedCargoSpecs();
11740 /* Sort the list of industry types. */
11741 SortIndustryTypes();
11743 /* Create dynamic list of industry legends for smallmap_gui.cpp */
11744 BuildIndustriesLegend();
11746 /* Build the routemap legend, based on the available cargos */
11747 BuildLinkStatsLegend();
11749 /* Add all new airports to the airports array. */
11750 FinaliseAirportsArray();
11751 BindAirportSpecs();
11753 /* Update the townname generators list */
11754 InitGRFTownGeneratorNames();
11756 /* Run all queued vehicle list order changes */
11757 CommitVehicleListOrderChanges();
11759 /* Load old shore sprites in new position, if they were replaced by ActionA */
11760 ActivateOldShore();
11762 /* Load old tram depot sprites in new position, if no new ones are present */
11763 ActivateOldTramDepot();
11765 /* Set up custom rail types */
11766 InitRailTypes();
11767 InitRoadTypes();
11768 InitRoadTypesCaches();
11770 for (Engine *e : Engine::IterateType(VEH_ROAD)) {
11771 if (_gted[e->index].rv_max_speed != 0) {
11772 /* Set RV maximum speed from the mph/0.8 unit value */
11773 e->u.road.max_speed = _gted[e->index].rv_max_speed * 4;
11776 RoadTramType rtt = HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? RTT_TRAM : RTT_ROAD;
11778 const GRFFile *file = e->GetGRF();
11779 if (file == nullptr || _gted[e->index].roadtramtype == 0) {
11780 e->u.road.roadtype = (rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
11781 continue;
11784 /* Remove +1 offset. */
11785 _gted[e->index].roadtramtype--;
11787 const std::vector<RoadTypeLabel> *list = (rtt == RTT_TRAM) ? &file->tramtype_list : &file->roadtype_list;
11788 if (_gted[e->index].roadtramtype < list->size())
11790 RoadTypeLabel rtl = (*list)[_gted[e->index].roadtramtype];
11791 RoadType rt = GetRoadTypeByLabel(rtl);
11792 if (rt != INVALID_ROADTYPE && GetRoadTramType(rt) == rtt) {
11793 e->u.road.roadtype = rt;
11794 continue;
11798 /* Road type is not available, so disable this engine */
11799 e->info.climates = 0;
11802 for (Engine *e : Engine::IterateType(VEH_TRAIN)) {
11803 RailType railtype = GetRailTypeByLabel(_gted[e->index].railtypelabel);
11804 if (railtype == INVALID_RAILTYPE) {
11805 /* Rail type is not available, so disable this engine */
11806 e->info.climates = 0;
11807 } else {
11808 e->u.rail.railtype = railtype;
11809 e->u.rail.intended_railtype = railtype;
11813 SetYearEngineAgingStops();
11815 FinalisePriceBaseMultipliers();
11817 /* Deallocate temporary loading data */
11818 _gted.clear();
11819 _grm_sprites.clear();
11821 ObjectClass::PrepareIndices();
11822 StationClass::PrepareIndices();
11823 AirportClass::PrepareIndices();
11824 RoadStopClass::PrepareIndices();
11828 * Load all the NewGRFs.
11829 * @param load_index The offset for the first sprite to add.
11830 * @param num_baseset Number of NewGRFs at the front of the list to look up in the baseset dir instead of the newgrf dir.
11832 void LoadNewGRF(SpriteID load_index, uint num_baseset)
11834 /* In case of networking we need to "sync" the start values
11835 * so all NewGRFs are loaded equally. For this we use the
11836 * start date of the game and we set the counters, etc. to
11837 * 0 so they're the same too. */
11838 CalTime::State cal_state = CalTime::Detail::now;
11839 EconTime::State econ_state = EconTime::Detail::now;
11840 uint8_t tick_skip_counter = DateDetail::_tick_skip_counter;
11841 uint64_t tick_counter = _tick_counter;
11842 uint64_t scaled_tick_counter = _scaled_tick_counter;
11843 StateTicks state_ticks = _state_ticks;
11844 StateTicksDelta state_ticks_offset = DateDetail::_state_ticks_offset;
11845 uint8_t display_opt = _display_opt;
11847 if (_networking) {
11848 CalTime::Detail::now = CalTime::Detail::NewState(_settings_game.game_creation.starting_year);
11849 EconTime::Detail::now = EconTime::Detail::NewState(ToEconTimeCast(_settings_game.game_creation.starting_year));
11850 _tick_counter = 0;
11851 _scaled_tick_counter = 0;
11852 _state_ticks = StateTicks{0};
11853 _display_opt = 0;
11854 UpdateCachedSnowLine();
11855 RecalculateStateTicksOffset();
11858 InitializeGRFSpecial();
11860 ResetNewGRFData();
11863 * Reset the status of all files, so we can 'retry' to load them.
11864 * This is needed when one for example rearranges the NewGRFs in-game
11865 * and a previously disabled NewGRF becomes usable. If it would not
11866 * be reset, the NewGRF would remain disabled even though it should
11867 * have been enabled.
11869 for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
11870 if (c->status != GCS_NOT_FOUND) c->status = GCS_UNKNOWN;
11871 if (_settings_client.gui.newgrf_disable_big_gui && (c->ident.grfid == BSWAP32(0x52577801) || c->ident.grfid == BSWAP32(0x55464970))) {
11872 c->status = GCS_DISABLED;
11876 _cur.spriteid = load_index;
11878 /* Load newgrf sprites
11879 * in each loading stage, (try to) open each file specified in the config
11880 * and load information from it. */
11881 for (GrfLoadingStage stage = GLS_LABELSCAN; stage <= GLS_ACTIVATION; stage++) {
11882 /* Set activated grfs back to will-be-activated between reservation- and activation-stage.
11883 * This ensures that action7/9 conditions 0x06 - 0x0A work correctly. */
11884 for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
11885 if (c->status == GCS_ACTIVATED) c->status = GCS_INITIALISED;
11888 if (stage == GLS_RESERVE) {
11889 static const std::pair<uint32_t, uint32_t> default_grf_overrides[] = {
11890 { BSWAP32(0x44442202), BSWAP32(0x44440111) }, // UKRS addons modifies UKRS
11891 { BSWAP32(0x6D620402), BSWAP32(0x6D620401) }, // DBSetXL ECS extension modifies DBSetXL
11892 { BSWAP32(0x4D656f20), BSWAP32(0x4D656F17) }, // LV4cut modifies LV4
11894 for (const auto &grf_override : default_grf_overrides) {
11895 SetNewGRFOverride(grf_override.first, grf_override.second);
11899 uint num_grfs = 0;
11900 uint num_non_static = 0;
11902 _cur.stage = stage;
11903 for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
11904 if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND) continue;
11905 if (stage > GLS_INIT && HasBit(c->flags, GCF_INIT_ONLY)) continue;
11907 Subdirectory subdir = num_grfs < num_baseset ? BASESET_DIR : NEWGRF_DIR;
11908 if (!FioCheckFileExists(c->filename, subdir)) {
11909 Debug(grf, 0, "NewGRF file is missing '{}'; disabling", c->filename);
11910 c->status = GCS_NOT_FOUND;
11911 continue;
11914 if (stage == GLS_LABELSCAN) InitNewGRFFile(c);
11916 if (!HasBit(c->flags, GCF_STATIC) && !HasBit(c->flags, GCF_SYSTEM)) {
11917 if (num_non_static == MAX_NON_STATIC_GRF_COUNT) {
11918 Debug(grf, 0, "'{}' is not loaded as the maximum number of non-static GRFs has been reached", c->filename);
11919 c->status = GCS_DISABLED;
11920 c->error = {STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED};
11921 continue;
11923 num_non_static++;
11926 num_grfs++;
11928 LoadNewGRFFile(c, stage, subdir, false);
11929 if (stage == GLS_RESERVE) {
11930 SetBit(c->flags, GCF_RESERVED);
11931 } else if (stage == GLS_ACTIVATION) {
11932 ClrBit(c->flags, GCF_RESERVED);
11933 assert_msg(GetFileByGRFID(c->ident.grfid) == _cur.grffile, "{:08X}", BSWAP32(c->ident.grfid));
11934 ClearTemporaryNewGRFData(_cur.grffile);
11935 BuildCargoTranslationMap();
11936 HandleVarAction2OptimisationPasses();
11937 Debug(sprite, 2, "LoadNewGRF: Currently {} sprites are loaded", _cur.spriteid);
11938 } else if (stage == GLS_INIT && HasBit(c->flags, GCF_INIT_ONLY)) {
11939 /* We're not going to activate this, so free whatever data we allocated */
11940 ClearTemporaryNewGRFData(_cur.grffile);
11945 /* Pseudo sprite processing is finished; free temporary stuff */
11946 _cur.ClearDataForNextFile();
11947 _callback_result_cache.clear();
11949 /* Call any functions that should be run after GRFs have been loaded. */
11950 AfterLoadGRFs();
11952 /* Now revert back to the original situation */
11953 CalTime::Detail::now = cal_state;
11954 EconTime::Detail::now = econ_state;
11955 DateDetail::_tick_skip_counter = tick_skip_counter;
11956 _tick_counter = tick_counter;
11957 _scaled_tick_counter = scaled_tick_counter;
11958 _state_ticks = state_ticks;
11959 DateDetail::_state_ticks_offset = state_ticks_offset;
11960 _display_opt = display_opt;
11961 UpdateCachedSnowLine();
11965 * Returns amount of user selected NewGRFs files.
11967 uint CountSelectedGRFs(GRFConfig *grfconf)
11969 uint i = 0;
11971 /* Find last entry in the list */
11972 for (const GRFConfig *list = grfconf; list != nullptr; list = list->next) {
11973 if (!HasBit(list->flags, GCF_STATIC) && !HasBit(list->flags, GCF_SYSTEM)) i++;
11975 return i;
11978 const char *GetExtendedVariableNameById(int id)
11980 extern const GRFVariableMapDefinition _grf_action2_remappable_variables[];
11981 for (const GRFVariableMapDefinition *info = _grf_action2_remappable_variables; info->name != nullptr; info++) {
11982 if (id == info->id) {
11983 return info->name;
11987 extern const GRFNameOnlyVariableMapDefinition _grf_action2_internal_variable_names[];
11988 for (const GRFNameOnlyVariableMapDefinition *info = _grf_action2_internal_variable_names; info->name != nullptr; info++) {
11989 if (id == info->id) {
11990 return info->name;
11994 return nullptr;
11997 static bool IsLabelPrintable(uint32_t l)
11999 for (uint i = 0; i < 4; i++) {
12000 if ((l & 0xFF) < 0x20 || (l & 0xFF) > 0x7F) return false;
12001 l >>= 8;
12003 return true;
12006 const char *NewGRFLabelDumper::Label(uint32_t label)
12008 if (IsLabelPrintable(label)) {
12009 format_to_fixed_z::format_to(this->buffer, lastof(this->buffer), "{:c}{:c}{:c}{:c}", label >> 24, label >> 16, label >> 8, label);
12010 } else {
12011 format_to_fixed_z::format_to(this->buffer, lastof(this->buffer), "0x{:08X}", BSWAP32(label));
12013 return this->buffer;