File diff r27123:c9e33a591e8d → r27124:237b340de564
src/newgrf.cpp
Show inline comments
 
@@ -576,25 +576,25 @@ StringID MapGRFStringID(uint32 grfid, St
 
}
 

	
 
static std::map<uint32, uint32> _grf_id_overrides;
 

	
 
/**
 
 * Set the override for a NewGRF
 
 * @param source_grfid The grfID which wants to override another NewGRF.
 
 * @param target_grfid The grfID which is being overridden.
 
 */
 
static void SetNewGRFOverride(uint32 source_grfid, uint32 target_grfid)
 
{
 
	_grf_id_overrides[source_grfid] = target_grfid;
 
	GrfMsg(5, "SetNewGRFOverride: Added override of {:#X} to {:#X}", BSWAP32(source_grfid), BSWAP32(target_grfid));
 
	GrfMsg(5, "SetNewGRFOverride: Added override of 0x{:X} to 0x{:X}", BSWAP32(source_grfid), BSWAP32(target_grfid));
 
}
 

	
 
/**
 
 * Returns the engine associated to a certain internal_id, resp. allocates it.
 
 * @param file NewGRF that wants to change the engine.
 
 * @param type Vehicle type.
 
 * @param internal_id Engine ID inside the NewGRF.
 
 * @param static_access If the engine is not present, return nullptr instead of allocating a new engine. (Used for static Action 0x04).
 
 * @return The requested engine.
 
 */
 
static Engine *GetNewEngine(const GRFFile *file, VehicleType type, uint16 internal_id, bool static_access = false)
 
{
 
@@ -853,40 +853,40 @@ static bool ReadSpriteLayout(ByteReader 
 
	TileLayoutFlags valid_flags = TLF_KNOWN_FLAGS;
 
	if (!allow_var10) valid_flags &= ~TLF_VAR10_FLAGS;
 
	dts->Allocate(num_building_sprites); // allocate before reading groundsprite flags
 

	
 
	std::vector<uint16> max_sprite_offset(num_building_sprites + 1, 0);
 
	std::vector<uint16> max_palette_offset(num_building_sprites + 1, 0);
 

	
 
	/* Groundsprite */
 
	TileLayoutFlags flags = ReadSpriteLayoutSprite(buf, has_flags, false, use_cur_spritesets, feature, &dts->ground, max_sprite_offset.data(), max_palette_offset.data());
 
	if (_cur.skip_sprites < 0) return true;
 

	
 
	if (flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS)) {
 
		GrfMsg(1, "ReadSpriteLayout: Spritelayout uses invalid flag {:#X} for ground sprite", flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS));
 
		GrfMsg(1, "ReadSpriteLayout: Spritelayout uses invalid flag 0x{:X} for ground sprite", flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS));
 
		DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
 
		return true;
 
	}
 

	
 
	ReadSpriteLayoutRegisters(buf, flags, false, dts, 0);
 
	if (_cur.skip_sprites < 0) return true;
 

	
 
	for (uint i = 0; i < num_building_sprites; i++) {
 
		DrawTileSeqStruct *seq = const_cast<DrawTileSeqStruct*>(&dts->seq[i]);
 

	
 
		flags = ReadSpriteLayoutSprite(buf, has_flags, false, use_cur_spritesets, feature, &seq->image, max_sprite_offset.data() + i + 1, max_palette_offset.data() + i + 1);
 
		if (_cur.skip_sprites < 0) return true;
 

	
 
		if (flags & ~valid_flags) {
 
			GrfMsg(1, "ReadSpriteLayout: Spritelayout uses unknown flag {:#X}", flags & ~valid_flags);
 
			GrfMsg(1, "ReadSpriteLayout: Spritelayout uses unknown flag 0x{:X}", flags & ~valid_flags);
 
			DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
 
			return true;
 
		}
 

	
 
		seq->delta_x = buf->ReadByte();
 
		seq->delta_y = buf->ReadByte();
 

	
 
		if (!no_z_position) seq->delta_z = buf->ReadByte();
 

	
 
		if (seq->IsParentSprite()) {
 
			seq->size_x = buf->ReadByte();
 
			seq->size_y = buf->ReadByte();
 
@@ -959,25 +959,25 @@ static CargoTypes TranslateRefitMask(uin
 
static void ConvertTTDBasePrice(uint32 base_pointer, const char *error_location, Price *index)
 
{
 
	/* Special value for 'none' */
 
	if (base_pointer == 0) {
 
		*index = INVALID_PRICE;
 
		return;
 
	}
 

	
 
	static const uint32 start = 0x4B34; ///< Position of first base price
 
	static const uint32 size  = 6;      ///< Size of each base price record
 

	
 
	if (base_pointer < start || (base_pointer - start) % size != 0 || (base_pointer - start) / size >= PR_END) {
 
		GrfMsg(1, "{}: Unsupported running cost base {:#04X}, ignoring", error_location, base_pointer);
 
		GrfMsg(1, "{}: Unsupported running cost base 0x{:04X}, ignoring", error_location, base_pointer);
 
		return;
 
	}
 

	
 
	*index = (Price)((base_pointer - start) / size);
 
}
 

	
 
/** Possible return values for the FeatureChangeInfo functions */
 
enum ChangeInfoResult {
 
	CIR_SUCCESS,    ///< Variable was parsed and read
 
	CIR_DISABLED,   ///< GRF was disabled due to error
 
	CIR_UNHANDLED,  ///< Variable was parsed but unread
 
	CIR_UNKNOWN,    ///< Variable is unknown
 
@@ -2127,25 +2127,25 @@ static ChangeInfoResult StationChangeInf
 
 * Define properties for water features
 
 * @param id Type of the first water feature.
 
 * @param numinfo Number of subsequent water feature ids to change the property for.
 
 * @param prop The property to change.
 
 * @param buf The property value.
 
 * @return ChangeInfoResult.
 
 */
 
static ChangeInfoResult CanalChangeInfo(uint id, int numinfo, int prop, ByteReader *buf)
 
{
 
	ChangeInfoResult ret = CIR_SUCCESS;
 

	
 
	if (id + numinfo > CF_END) {
 
		GrfMsg(1, "CanalChangeInfo: Canal feature {:#02X} is invalid, max {}, ignoring", id + numinfo, CF_END);
 
		GrfMsg(1, "CanalChangeInfo: Canal feature 0x{:02X} is invalid, max {}, ignoring", id + numinfo, CF_END);
 
		return CIR_INVALID_ID;
 
	}
 

	
 
	for (int i = 0; i < numinfo; i++) {
 
		CanalProperties *cp = &_cur.grffile->canal_local_properties[id + i];
 

	
 
		switch (prop) {
 
			case 0x08:
 
				cp->callback_mask = buf->ReadByte();
 
				break;
 

	
 
			case 0x09:
 
@@ -4108,25 +4108,25 @@ static ChangeInfoResult ObjectChangeInfo
 

	
 
			case 0x0A: // Object name
 
				AddStringForMapping(buf->ReadWord(), &spec->name);
 
				break;
 

	
 
			case 0x0B: // Climate mask
 
				spec->climate = buf->ReadByte();
 
				break;
 

	
 
			case 0x0C: // Size
 
				spec->size = buf->ReadByte();
 
				if (GB(spec->size, 0, 4) == 0 || GB(spec->size, 4, 4) == 0) {
 
					GrfMsg(0, "ObjectChangeInfo: Invalid object size requested ({:#X}) for object id {}. Ignoring.", spec->size, id + i);
 
					GrfMsg(0, "ObjectChangeInfo: Invalid object size requested (0x{:X}) for object id {}. Ignoring.", spec->size, id + i);
 
					spec->size = OBJECT_SIZE_1X1;
 
				}
 
				break;
 

	
 
			case 0x0D: // Build cost multipler
 
				spec->build_cost_multiplier = buf->ReadByte();
 
				spec->clear_cost_multiplier = spec->build_cost_multiplier;
 
				break;
 

	
 
			case 0x0E: // Introduction date
 
				spec->introduction_date = buf->ReadDWord();
 
				break;
 
@@ -4842,29 +4842,29 @@ static bool HandleChangeInfoResult(const
 
{
 
	switch (cir) {
 
		default: NOT_REACHED();
 

	
 
		case CIR_DISABLED:
 
			/* Error has already been printed; just stop parsing */
 
			return true;
 

	
 
		case CIR_SUCCESS:
 
			return false;
 

	
 
		case CIR_UNHANDLED:
 
			GrfMsg(1, "{}: Ignoring property {:#02X} of feature {:#02X} (not implemented)", caller, property, feature);
 
			GrfMsg(1, "{}: Ignoring property 0x{:02X} of feature 0x{:02X} (not implemented)", caller, property, feature);
 
			return false;
 

	
 
		case CIR_UNKNOWN:
 
			GrfMsg(0, "{}: Unknown property {:#02X} of feature {:#02X}, disabling", caller, property, feature);
 
			GrfMsg(0, "{}: Unknown property 0x{:02X} of feature 0x{:02X}, disabling", caller, property, feature);
 
			FALLTHROUGH;
 

	
 
		case CIR_INVALID_ID: {
 
			/* No debug message for an invalid ID, as it has already been output */
 
			GRFError *error = DisableGrf(cir == CIR_INVALID_ID ? STR_NEWGRF_ERROR_INVALID_ID : STR_NEWGRF_ERROR_UNKNOWN_PROPERTY);
 
			if (cir != CIR_INVALID_ID) error->param_value[1] = property;
 
			return true;
 
		}
 
	}
 
}
 

	
 
/* Action 0x00 */
 
@@ -4903,33 +4903,33 @@ static void FeatureChangeInfo(ByteReader
 
		/* GSF_ROADTYPES */     RoadTypeChangeInfo,
 
		/* GSF_TRAMTYPES */     TramTypeChangeInfo,
 
		/* GSF_ROADSTOPS */     RoadStopChangeInfo,
 
	};
 
	static_assert(GSF_END == lengthof(handler));
 

	
 
	uint8 feature  = buf->ReadByte();
 
	uint8 numprops = buf->ReadByte();
 
	uint numinfo  = buf->ReadByte();
 
	uint engine   = buf->ReadExtendedByte();
 

	
 
	if (feature >= GSF_END) {
 
		GrfMsg(1, "FeatureChangeInfo: Unsupported feature {:#02X}, skipping", feature);
 
		GrfMsg(1, "FeatureChangeInfo: Unsupported feature 0x{:02X}, skipping", feature);
 
		return;
 
	}
 

	
 
	GrfMsg(6, "FeatureChangeInfo: Feature {:#02X}, {} properties, to apply to {}+{}",
 
	GrfMsg(6, "FeatureChangeInfo: Feature 0x{:02X}, {} properties, to apply to {}+{}",
 
	               feature, numprops, engine, numinfo);
 

	
 
	if (handler[feature] == nullptr) {
 
		if (feature != GSF_CARGOES) GrfMsg(1, "FeatureChangeInfo: Unsupported feature {:#02X}, skipping", feature);
 
		if (feature != GSF_CARGOES) GrfMsg(1, "FeatureChangeInfo: Unsupported feature 0x{:02X}, skipping", feature);
 
		return;
 
	}
 

	
 
	/* Mark the feature as used by the grf */
 
	SetBit(_cur.grffile->grf_features, feature);
 

	
 
	while (numprops-- && buf->HasData()) {
 
		uint8 prop = buf->ReadByte();
 

	
 
		ChangeInfoResult cir = handler[feature](engine, numinfo, prop, buf);
 
		if (HandleChangeInfoResult("FeatureChangeInfo", cir, feature, prop)) return;
 
	}
 
@@ -5036,31 +5036,31 @@ static void NewSpriteSet(ByteReader *buf
 
	uint16 first_set = 0;
 

	
 
	if (num_sets == 0 && buf->HasData(3)) {
 
		/* Extended Action1 format.
 
		 * Some GRFs define zero sets of zero sprites, though there is actually no use in that. Ignore them. */
 
		first_set = buf->ReadExtendedByte();
 
		num_sets = buf->ReadExtendedByte();
 
	}
 
	uint16 num_ents = buf->ReadExtendedByte();
 

	
 
	if (feature >= GSF_END) {
 
		_cur.skip_sprites = num_sets * num_ents;
 
		GrfMsg(1, "NewSpriteSet: Unsupported feature {:#02X}, skipping {} sprites", feature, _cur.skip_sprites);
 
		GrfMsg(1, "NewSpriteSet: Unsupported feature 0x{:02X}, skipping {} sprites", feature, _cur.skip_sprites);
 
		return;
 
	}
 

	
 
	_cur.AddSpriteSets(feature, _cur.spriteid, first_set, num_sets, num_ents);
 

	
 
	GrfMsg(7, "New sprite set at {} of feature {:#02X}, consisting of {} sets with {} views each (total {})",
 
	GrfMsg(7, "New sprite set at {} of feature 0x{:02X}, consisting of {} sets with {} views each (total {})",
 
		_cur.spriteid, feature, num_sets, num_ents, num_sets * num_ents
 
	);
 

	
 
	for (int i = 0; i < num_sets * num_ents; i++) {
 
		_cur.nfo_line++;
 
		LoadNextSprite(_cur.spriteid++, *_cur.file, _cur.nfo_line);
 
	}
 
}
 

	
 
/* Action 0x01 (SKIP) */
 
static void SkipAct1(ByteReader *buf)
 
{
 
@@ -5081,48 +5081,48 @@ static void SkipAct1(ByteReader *buf)
 
}
 

	
 
/* Helper function to either create a callback or link to a previously
 
 * defined spritegroup. */
 
static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 groupid)
 
{
 
	if (HasBit(groupid, 15)) {
 
		assert(CallbackResultSpriteGroup::CanAllocateItem());
 
		return new CallbackResultSpriteGroup(groupid, _cur.grffile->grf_version >= 8);
 
	}
 

	
 
	if (groupid > MAX_SPRITEGROUP || _cur.spritegroups[groupid] == nullptr) {
 
		GrfMsg(1, "GetGroupFromGroupID({:#02X}:{:#02X}): Groupid {:#04X} does not exist, leaving empty", setid, type, groupid);
 
		GrfMsg(1, "GetGroupFromGroupID(0x{:02X}:0x{:02X}): Groupid 0x{:04X} does not exist, leaving empty", setid, type, groupid);
 
		return nullptr;
 
	}
 

	
 
	return _cur.spritegroups[groupid];
 
}
 

	
 
/**
 
 * Helper function to either create a callback or a result sprite group.
 
 * @param feature GrfSpecFeature to define spritegroup for.
 
 * @param setid SetID of the currently being parsed Action2. (only for debug output)
 
 * @param type Type of the currently being parsed Action2. (only for debug output)
 
 * @param spriteid Raw value from the GRF for the new spritegroup; describes either the return value or the referenced spritegroup.
 
 * @return Created spritegroup.
 
 */
 
static const SpriteGroup *CreateGroupFromGroupID(byte feature, byte setid, byte type, uint16 spriteid)
 
{
 
	if (HasBit(spriteid, 15)) {
 
		assert(CallbackResultSpriteGroup::CanAllocateItem());
 
		return new CallbackResultSpriteGroup(spriteid, _cur.grffile->grf_version >= 8);
 
	}
 

	
 
	if (!_cur.IsValidSpriteSet(feature, spriteid)) {
 
		GrfMsg(1, "CreateGroupFromGroupID({:#02X}:{:#02X}): Sprite set {} invalid", setid, type, spriteid);
 
		GrfMsg(1, "CreateGroupFromGroupID(0x{:02X}:0x{:02X}): Sprite set {} invalid", setid, type, spriteid);
 
		return nullptr;
 
	}
 

	
 
	SpriteID spriteset_start = _cur.GetSprite(feature, spriteid);
 
	uint num_sprites = _cur.GetNumEnts(feature, spriteid);
 

	
 
	/* Ensure that the sprites are loeded */
 
	assert(spriteset_start + num_sprites <= _cur.spriteid);
 

	
 
	assert(ResultSpriteGroup::CanAllocateItem());
 
	return new ResultSpriteGroup(spriteset_start, num_sprites);
 
}
 
@@ -5135,25 +5135,25 @@ static void NewSpriteGroup(ByteReader *b
 
	 * B feature       see action 1
 
	 * B set-id        ID of this particular definition
 
	 * B type/num-entries
 
	 *                 if 80 or greater, this is a randomized or variational
 
	 *                 list definition, see below
 
	 *                 otherwise it specifies a number of entries, the exact
 
	 *                 meaning depends on the feature
 
	 * V feature-specific-data (huge mess, don't even look it up --pasky) */
 
	const SpriteGroup *act_group = nullptr;
 

	
 
	uint8 feature = buf->ReadByte();
 
	if (feature >= GSF_END) {
 
		GrfMsg(1, "NewSpriteGroup: Unsupported feature {:#02X}, skipping", feature);
 
		GrfMsg(1, "NewSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
 
		return;
 
	}
 

	
 
	uint8 setid   = buf->ReadByte();
 
	uint8 type    = buf->ReadByte();
 

	
 
	/* Sprite Groups are created here but they are allocated from a pool, so
 
	 * we do not need to delete anything if there is an exception from the
 
	 * ByteReader. */
 

	
 
	switch (type) {
 
		/* Deterministic Sprite Group */
 
@@ -5312,25 +5312,25 @@ static void NewSpriteGroup(ByteReader *b
 
				case GSF_RAILTYPES:
 
				case GSF_ROADTYPES:
 
				case GSF_TRAMTYPES:
 
				{
 
					byte num_loaded  = type;
 
					byte num_loading = buf->ReadByte();
 

	
 
					if (!_cur.HasValidSpriteSets(feature)) {
 
						GrfMsg(0, "NewSpriteGroup: No sprite set to work on! Skipping");
 
						return;
 
					}
 

	
 
					GrfMsg(6, "NewSpriteGroup: New SpriteGroup {:#02X}, {} loaded, {} loading",
 
					GrfMsg(6, "NewSpriteGroup: New SpriteGroup 0x{:02X}, {} loaded, {} loading",
 
							setid, num_loaded, num_loading);
 

	
 
					if (num_loaded + num_loading == 0) {
 
						GrfMsg(1, "NewSpriteGroup: no result, skipping invalid RealSpriteGroup");
 
						break;
 
					}
 

	
 
					if (num_loaded + num_loading == 1) {
 
						/* Avoid creating 'Real' sprite group if only one option. */
 
						uint16 spriteid = buf->ReadWord();
 
						act_group = CreateGroupFromGroupID(feature, setid, type, spriteid);
 
						GrfMsg(8, "NewSpriteGroup: one result, skipping RealSpriteGroup = subset {}", spriteid);
 
@@ -5468,25 +5468,25 @@ static void NewSpriteGroup(ByteReader *b
 
							}
 
							group->cargo_output[i] = cargo;
 
							group->add_output[i] = buf->ReadByte();
 
						}
 
						group->again = buf->ReadByte();
 
					} else {
 
						NOT_REACHED();
 
					}
 
					break;
 
				}
 

	
 
				/* Loading of Tile Layout and Production Callback groups would happen here */
 
				default: GrfMsg(1, "NewSpriteGroup: Unsupported feature {:#02X}, skipping", feature);
 
				default: GrfMsg(1, "NewSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
 
			}
 
		}
 
	}
 

	
 
	_cur.spritegroups[setid] = act_group;
 
}
 

	
 
static CargoID TranslateCargo(uint8 feature, uint8 ctype)
 
{
 
	if (feature == GSF_OBJECTS) {
 
		switch (ctype) {
 
			case 0:    return 0;
 
@@ -5536,25 +5536,25 @@ static CargoID TranslateCargo(uint8 feat
 
		GrfMsg(5, "TranslateCargo: Cargo '{:c}{:c}{:c}{:c}' unsupported, skipping.", GB(cl, 24, 8), GB(cl, 16, 8), GB(cl, 8, 8), GB(cl, 0, 8));
 
		return CT_INVALID;
 
	}
 

	
 
	GrfMsg(6, "TranslateCargo: Cargo '{:c}{:c}{:c}{:c}' mapped to cargo type {}.", GB(cl, 24, 8), GB(cl, 16, 8), GB(cl, 8, 8), GB(cl, 0, 8), ctype);
 
	return ctype;
 
}
 

	
 

	
 
static bool IsValidGroupID(uint16 groupid, const char *function)
 
{
 
	if (groupid > MAX_SPRITEGROUP || _cur.spritegroups[groupid] == nullptr) {
 
		GrfMsg(1, "{}: Spritegroup {:#04X} out of range or empty, skipping.", function, groupid);
 
		GrfMsg(1, "{}: Spritegroup 0x{:04X} out of range or empty, skipping.", function, groupid);
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
static void VehicleMapSpriteGroup(ByteReader *buf, byte feature, uint8 idcount)
 
{
 
	static EngineID *last_engines;
 
	static uint last_engines_count;
 
	bool wagover = false;
 

	
 
@@ -5590,46 +5590,46 @@ static void VehicleMapSpriteGroup(ByteRe
 
		}
 

	
 
		engines.push_back(e->index);
 
		if (!wagover) last_engines[i] = engines[i];
 
	}
 

	
 
	uint8 cidcount = buf->ReadByte();
 
	for (uint c = 0; c < cidcount; c++) {
 
		uint8 ctype = buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) continue;
 

	
 
		GrfMsg(8, "VehicleMapSpriteGroup: * [{}] Cargo type {:#X}, group id {:#02X}", c, ctype, groupid);
 
		GrfMsg(8, "VehicleMapSpriteGroup: * [{}] Cargo type 0x{:X}, group id 0x{:02X}", c, ctype, groupid);
 

	
 
		ctype = TranslateCargo(feature, ctype);
 
		if (ctype == CT_INVALID) continue;
 

	
 
		for (uint i = 0; i < idcount; i++) {
 
			EngineID engine = engines[i];
 

	
 
			GrfMsg(7, "VehicleMapSpriteGroup: [{}] Engine {}...", i, engine);
 

	
 
			if (wagover) {
 
				SetWagonOverrideSprites(engine, ctype, _cur.spritegroups[groupid], last_engines, last_engines_count);
 
			} else {
 
				SetCustomEngineSprites(engine, ctype, _cur.spritegroups[groupid]);
 
			}
 
		}
 
	}
 

	
 
	uint16 groupid = buf->ReadWord();
 
	if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) return;
 

	
 
	GrfMsg(8, "-- Default group id {:#04X}", groupid);
 
	GrfMsg(8, "-- Default group id 0x{:04X}", groupid);
 

	
 
	for (uint i = 0; i < idcount; i++) {
 
		EngineID engine = engines[i];
 

	
 
		if (wagover) {
 
			SetWagonOverrideSprites(engine, CT_DEFAULT, _cur.spritegroups[groupid], last_engines, last_engines_count);
 
		} else {
 
			SetCustomEngineSprites(engine, CT_DEFAULT, _cur.spritegroups[groupid]);
 
			SetEngineGRF(engine, _cur.grffile);
 
		}
 
	}
 
}
 
@@ -5678,45 +5678,45 @@ static void StationMapSpriteGroup(ByteRe
 
	for (uint c = 0; c < cidcount; c++) {
 
		uint8 ctype = buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) continue;
 

	
 
		ctype = TranslateCargo(GSF_STATIONS, ctype);
 
		if (ctype == CT_INVALID) continue;
 

	
 
		for (auto &station : stations) {
 
			StationSpec *statspec = station >= _cur.grffile->stations.size() ? nullptr : _cur.grffile->stations[station].get();
 

	
 
			if (statspec == nullptr) {
 
				GrfMsg(1, "StationMapSpriteGroup: Station with ID {:#02X} does not exist, skipping", station);
 
				GrfMsg(1, "StationMapSpriteGroup: Station with ID 0x{:02X} does not exist, skipping", station);
 
				continue;
 
			}
 

	
 
			statspec->grf_prop.spritegroup[ctype] = _cur.spritegroups[groupid];
 
		}
 
	}
 

	
 
	uint16 groupid = buf->ReadWord();
 
	if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) return;
 

	
 
	for (auto &station : stations) {
 
		StationSpec *statspec = station >= _cur.grffile->stations.size() ? nullptr : _cur.grffile->stations[station].get();
 

	
 
		if (statspec == nullptr) {
 
			GrfMsg(1, "StationMapSpriteGroup: Station with ID {:#02X} does not exist, skipping", station);
 
			GrfMsg(1, "StationMapSpriteGroup: Station with ID 0x{:02X} does not exist, skipping", station);
 
			continue;
 
		}
 

	
 
		if (statspec->grf_prop.grffile != nullptr) {
 
			GrfMsg(1, "StationMapSpriteGroup: Station with ID {:#02X} mapped multiple times, skipping", station);
 
			GrfMsg(1, "StationMapSpriteGroup: Station with ID 0x{:02X} mapped multiple times, skipping", station);
 
			continue;
 
		}
 

	
 
		statspec->grf_prop.spritegroup[CT_DEFAULT] = _cur.spritegroups[groupid];
 
		statspec->grf_prop.grffile = _cur.grffile;
 
		statspec->grf_prop.local_id = station;
 
		StationClass::Assign(statspec);
 
	}
 
}
 

	
 

	
 
static void TownHouseMapSpriteGroup(ByteReader *buf, uint8 idcount)
 
@@ -5859,45 +5859,45 @@ static void ObjectMapSpriteGroup(ByteRea
 
	for (uint c = 0; c < cidcount; c++) {
 
		uint8 ctype = buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "ObjectMapSpriteGroup")) continue;
 

	
 
		ctype = TranslateCargo(GSF_OBJECTS, ctype);
 
		if (ctype == CT_INVALID) continue;
 

	
 
		for (auto &object : objects) {
 
			ObjectSpec *spec = object >= _cur.grffile->objectspec.size() ? nullptr : _cur.grffile->objectspec[object].get();
 

	
 
			if (spec == nullptr) {
 
				GrfMsg(1, "ObjectMapSpriteGroup: Object with ID {:#02X} undefined, skipping", object);
 
				GrfMsg(1, "ObjectMapSpriteGroup: Object with ID 0x{:02X} undefined, skipping", object);
 
				continue;
 
			}
 

	
 
			spec->grf_prop.spritegroup[ctype] = _cur.spritegroups[groupid];
 
		}
 
	}
 

	
 
	uint16 groupid = buf->ReadWord();
 
	if (!IsValidGroupID(groupid, "ObjectMapSpriteGroup")) return;
 

	
 
	for (auto &object : objects) {
 
		ObjectSpec *spec = object >= _cur.grffile->objectspec.size() ? nullptr : _cur.grffile->objectspec[object].get();
 

	
 
		if (spec == nullptr) {
 
			GrfMsg(1, "ObjectMapSpriteGroup: Object with ID {:#02X} undefined, skipping", object);
 
			GrfMsg(1, "ObjectMapSpriteGroup: Object with ID 0x{:02X} undefined, skipping", object);
 
			continue;
 
		}
 

	
 
		if (spec->grf_prop.grffile != nullptr) {
 
			GrfMsg(1, "ObjectMapSpriteGroup: Object with ID {:#02X} mapped multiple times, skipping", object);
 
			GrfMsg(1, "ObjectMapSpriteGroup: Object with ID 0x{:02X} mapped multiple times, skipping", object);
 
			continue;
 
		}
 

	
 
		spec->grf_prop.spritegroup[0] = _cur.spritegroups[groupid];
 
		spec->grf_prop.grffile        = _cur.grffile;
 
		spec->grf_prop.local_id       = object;
 
	}
 
}
 

	
 
static void RailTypeMapSpriteGroup(ByteReader *buf, uint8 idcount)
 
{
 
	std::vector<uint8> railtypes;
 
@@ -6045,45 +6045,45 @@ static void RoadStopMapSpriteGroup(ByteR
 
	for (uint c = 0; c < cidcount; c++) {
 
		uint8 ctype = buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) continue;
 

	
 
		ctype = TranslateCargo(GSF_ROADSTOPS, ctype);
 
		if (ctype == CT_INVALID) continue;
 

	
 
		for (auto &roadstop : roadstops) {
 
			RoadStopSpec *roadstopspec = roadstop >= _cur.grffile->roadstops.size() ? nullptr : _cur.grffile->roadstops[roadstop].get();
 

	
 
			if (roadstopspec == nullptr) {
 
				GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID {:#02X} does not exist, skipping", roadstop);
 
				GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x{:02X} does not exist, skipping", roadstop);
 
				continue;
 
			}
 

	
 
			roadstopspec->grf_prop.spritegroup[ctype] = _cur.spritegroups[groupid];
 
		}
 
	}
 

	
 
	uint16 groupid = buf->ReadWord();
 
	if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) return;
 

	
 
	for (auto &roadstop : roadstops) {
 
		RoadStopSpec *roadstopspec = roadstop >= _cur.grffile->roadstops.size() ? nullptr : _cur.grffile->roadstops[roadstop].get();
 

	
 
		if (roadstopspec == nullptr) {
 
			GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID {:#02X} does not exist, skipping.", roadstop);
 
			GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x{:02X} does not exist, skipping.", roadstop);
 
			continue;
 
		}
 

	
 
		if (roadstopspec->grf_prop.grffile != nullptr) {
 
			GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID {:#02X} mapped multiple times, skipping", roadstop);
 
			GrfMsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x{:02X} mapped multiple times, skipping", roadstop);
 
			continue;
 
		}
 

	
 
		roadstopspec->grf_prop.spritegroup[CT_DEFAULT] = _cur.spritegroups[groupid];
 
		roadstopspec->grf_prop.grffile = _cur.grffile;
 
		roadstopspec->grf_prop.local_id = roadstop;
 
		RoadStopClass::Assign(roadstopspec);
 
	}
 
}
 

	
 
/* Action 0x03 */
 
static void FeatureMapSpriteGroup(ByteReader *buf)
 
@@ -6097,45 +6097,45 @@ static void FeatureMapSpriteGroup(ByteRe
 
	 *                 bit 7: if set, this is a wagon override definition (see below)
 
	 * B ids           the IDs for which this definition applies
 
	 * B num-cid       number of cargo IDs (sprite group IDs) in this definition
 
	 *                 can be zero, in that case the def-cid is used always
 
	 * B cargo-type    type of this cargo type (e.g. mail=2, wood=7, see below)
 
	 * W cid           cargo ID (sprite group ID) for this type of cargo
 
	 * W def-cid       default cargo ID (sprite group ID) */
 

	
 
	uint8 feature = buf->ReadByte();
 
	uint8 idcount = buf->ReadByte();
 

	
 
	if (feature >= GSF_END) {
 
		GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature {:#02X}, skipping", feature);
 
		GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
 
		return;
 
	}
 

	
 
	/* If idcount is zero, this is a feature callback */
 
	if (idcount == 0) {
 
		/* Skip number of cargo ids? */
 
		buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "FeatureMapSpriteGroup")) return;
 

	
 
		GrfMsg(6, "FeatureMapSpriteGroup: Adding generic feature callback for feature {:#02X}", feature);
 
		GrfMsg(6, "FeatureMapSpriteGroup: Adding generic feature callback for feature 0x{:02X}", feature);
 

	
 
		AddGenericCallback(feature, _cur.grffile, _cur.spritegroups[groupid]);
 
		return;
 
	}
 

	
 
	/* Mark the feature as used by the grf (generic callbacks do not count) */
 
	SetBit(_cur.grffile->grf_features, feature);
 

	
 
	GrfMsg(6, "FeatureMapSpriteGroup: Feature {:#02X}, {} ids", feature, idcount);
 
	GrfMsg(6, "FeatureMapSpriteGroup: Feature 0x{:02X}, {} ids", feature, idcount);
 

	
 
	switch (feature) {
 
		case GSF_TRAINS:
 
		case GSF_ROADVEHICLES:
 
		case GSF_SHIPS:
 
		case GSF_AIRCRAFT:
 
			VehicleMapSpriteGroup(buf, feature, idcount);
 
			return;
 

	
 
		case GSF_CANALS:
 
			CanalMapSpriteGroup(buf, idcount);
 
			return;
 
@@ -6180,25 +6180,25 @@ static void FeatureMapSpriteGroup(ByteRe
 
			RoadTypeMapSpriteGroup(buf, idcount, RTT_TRAM);
 
			break;
 

	
 
		case GSF_AIRPORTTILES:
 
			AirportTileMapSpriteGroup(buf, idcount);
 
			return;
 

	
 
		case GSF_ROADSTOPS:
 
			RoadStopMapSpriteGroup(buf, idcount);
 
			return;
 

	
 
		default:
 
			GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature {:#02X}, skipping", feature);
 
			GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
 
			return;
 
	}
 
}
 

	
 
/* Action 0x04 */
 
static void FeatureNewName(ByteReader *buf)
 
{
 
	/* <04> <veh-type> <language-id> <num-veh> <offset> <data...>
 
	 *
 
	 * B veh-type      see action 0 (as 00..07, + 0A
 
	 *                 But IF veh-type = 48, then generic text
 
	 * B language-id   If bit 6 is set, This is the extended language scheme,
 
@@ -6208,50 +6208,50 @@ static void FeatureNewName(ByteReader *b
 
	 *                 Bit 7 set means this is a generic text, not a vehicle one (or else)
 
	 * B num-veh       number of vehicles which are getting a new name
 
	 * B/W offset      number of the first vehicle that gets a new name
 
	 *                 Byte : ID of vehicle to change
 
	 *                 Word : ID of string to change/add
 
	 * S data          new texts, each of them zero-terminated, after
 
	 *                 which the next name begins. */
 

	
 
	bool new_scheme = _cur.grffile->grf_version >= 7;
 

	
 
	uint8 feature  = buf->ReadByte();
 
	if (feature >= GSF_END && feature != 0x48) {
 
		GrfMsg(1, "FeatureNewName: Unsupported feature {:#02X}, skipping", feature);
 
		GrfMsg(1, "FeatureNewName: Unsupported feature 0x{:02X}, skipping", feature);
 
		return;
 
	}
 

	
 
	uint8 lang     = buf->ReadByte();
 
	uint8 num      = buf->ReadByte();
 
	bool generic   = HasBit(lang, 7);
 
	uint16 id;
 
	if (generic) {
 
		id = buf->ReadWord();
 
	} else if (feature <= GSF_AIRCRAFT) {
 
		id = buf->ReadExtendedByte();
 
	} else {
 
		id = buf->ReadByte();
 
	}
 

	
 
	ClrBit(lang, 7);
 

	
 
	uint16 endid = id + num;
 

	
 
	GrfMsg(6, "FeatureNewName: About to rename engines {}..{} (feature {:#02X}) in language {:#02X}",
 
	GrfMsg(6, "FeatureNewName: About to rename engines {}..{} (feature 0x{:02X}) in language 0x{:02X}",
 
	               id, endid, feature, lang);
 

	
 
	for (; id < endid && buf->HasData(); id++) {
 
		const char *name = buf->ReadString();
 
		GrfMsg(8, "FeatureNewName: {:#04X} <- {}", id, name);
 
		GrfMsg(8, "FeatureNewName: 0x{:04X} <- {}", id, name);
 

	
 
		switch (feature) {
 
			case GSF_TRAINS:
 
			case GSF_ROADVEHICLES:
 
			case GSF_SHIPS:
 
			case GSF_AIRCRAFT:
 
				if (!generic) {
 
					Engine *e = GetNewEngine(_cur.grffile, (VehicleType)feature, id, HasBit(_cur.grfconfig->flags, GCF_STATIC));
 
					if (e == nullptr) break;
 
					StringID string = AddGRFString(_cur.grffile->grfid, e->index, lang, new_scheme, false, name, e->info.string_id);
 
					e->info.string_id = string;
 
				} else {
 
@@ -6259,57 +6259,57 @@ static void FeatureNewName(ByteReader *b
 
				}
 
				break;
 

	
 
			default:
 
				if (IsInsideMM(id, 0xD000, 0xD400) || IsInsideMM(id, 0xD800, 0x10000)) {
 
					AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, true, name, STR_UNDEFINED);
 
					break;
 
				}
 

	
 
				switch (GB(id, 8, 8)) {
 
					case 0xC4: // Station class name
 
						if (GB(id, 0, 8) >= _cur.grffile->stations.size() || _cur.grffile->stations[GB(id, 0, 8)] == nullptr) {
 
							GrfMsg(1, "FeatureNewName: Attempt to name undefined station {:#X}, ignoring", GB(id, 0, 8));
 
							GrfMsg(1, "FeatureNewName: Attempt to name undefined station 0x{:X}, ignoring", GB(id, 0, 8));
 
						} else {
 
							StationClassID cls_id = _cur.grffile->stations[GB(id, 0, 8)]->cls_id;
 
							StationClass::Get(cls_id)->name = AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
 
						}
 
						break;
 

	
 
					case 0xC5: // Station name
 
						if (GB(id, 0, 8) >= _cur.grffile->stations.size() || _cur.grffile->stations[GB(id, 0, 8)] == nullptr) {
 
							GrfMsg(1, "FeatureNewName: Attempt to name undefined station {:#X}, ignoring", GB(id, 0, 8));
 
							GrfMsg(1, "FeatureNewName: Attempt to name undefined station 0x{:X}, ignoring", GB(id, 0, 8));
 
						} else {
 
							_cur.grffile->stations[GB(id, 0, 8)]->name = AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
 
						}
 
						break;
 

	
 
					case 0xC7: // Airporttile name
 
						if (GB(id, 0, 8) >= _cur.grffile->airtspec.size() || _cur.grffile->airtspec[GB(id, 0, 8)] == nullptr) {
 
							GrfMsg(1, "FeatureNewName: Attempt to name undefined airport tile {:#X}, ignoring", GB(id, 0, 8));
 
							GrfMsg(1, "FeatureNewName: Attempt to name undefined airport tile 0x{:X}, ignoring", GB(id, 0, 8));
 
						} else {
 
							_cur.grffile->airtspec[GB(id, 0, 8)]->name = AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
 
						}
 
						break;
 

	
 
					case 0xC9: // House name
 
						if (GB(id, 0, 8) >= _cur.grffile->housespec.size() || _cur.grffile->housespec[GB(id, 0, 8)] == nullptr) {
 
							GrfMsg(1, "FeatureNewName: Attempt to name undefined house {:#X}, ignoring.", GB(id, 0, 8));
 
							GrfMsg(1, "FeatureNewName: Attempt to name undefined house 0x{:X}, ignoring.", GB(id, 0, 8));
 
						} else {
 
							_cur.grffile->housespec[GB(id, 0, 8)]->building_name = AddGRFString(_cur.grffile->grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
 
						}
 
						break;
 

	
 
					default:
 
						GrfMsg(7, "FeatureNewName: Unsupported ID ({:#04X})", id);
 
						GrfMsg(7, "FeatureNewName: Unsupported ID (0x{:04X})", id);
 
						break;
 
				}
 
				break;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Sanitize incoming sprite offsets for Action 5 graphics replacements.
 
 * @param num         The number of sprites to load.
 
 * @param offset      Offset from the base.
 
 * @param max_sprites The maximum number of sprites that can be loaded in this action 5.
 
@@ -6407,53 +6407,53 @@ static void GraphicsNew(ByteReader *buf)
 
		LoadNextSprite(SPR_SHORE_BASE + 11, *_cur.file, _cur.nfo_line++); // SLOPE_NWS
 
		LoadNextSprite(SPR_SHORE_BASE + 13, *_cur.file, _cur.nfo_line++); // SLOPE_ENW
 
		LoadNextSprite(SPR_SHORE_BASE + 14, *_cur.file, _cur.nfo_line++); // SLOPE_SEN
 
		LoadNextSprite(SPR_SHORE_BASE + 15, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_E
 
		LoadNextSprite(SPR_SHORE_BASE + 16, *_cur.file, _cur.nfo_line++); // SLOPE_EW
 
		LoadNextSprite(SPR_SHORE_BASE + 17, *_cur.file, _cur.nfo_line++); // SLOPE_NS
 
		if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ONLY_NEW;
 
		return;
 
	}
 

	
 
	/* Supported type? */
 
	if ((type >= lengthof(_action5_types)) || (_action5_types[type].block_type == A5BLOCK_INVALID)) {
 
		GrfMsg(2, "GraphicsNew: Custom graphics (type {:#02X}) sprite block of length {} (unimplemented, ignoring)", type, num);
 
		GrfMsg(2, "GraphicsNew: Custom graphics (type 0x{:02X}) sprite block of length {} (unimplemented, ignoring)", type, num);
 
		_cur.skip_sprites = num;
 
		return;
 
	}
 

	
 
	const Action5Type *action5_type = &_action5_types[type];
 

	
 
	/* Contrary to TTDP we allow always to specify too few sprites as we allow always an offset,
 
	 * except for the long version of the shore type:
 
	 * Ignore offset if not allowed */
 
	if ((action5_type->block_type != A5BLOCK_ALLOW_OFFSET) && (offset != 0)) {
 
		GrfMsg(1, "GraphicsNew: {} (type {:#02X}) do not allow an <offset> field. Ignoring offset.", action5_type->name, type);
 
		GrfMsg(1, "GraphicsNew: {} (type 0x{:02X}) do not allow an <offset> field. Ignoring offset.", action5_type->name, type);
 
		offset = 0;
 
	}
 

	
 
	/* Ignore action5 if too few sprites are specified. (for TTDP compatibility)
 
	 * This does not make sense, if <offset> is allowed */
 
	if ((action5_type->block_type == A5BLOCK_FIXED) && (num < action5_type->min_sprites)) {
 
		GrfMsg(1, "GraphicsNew: {} (type {:#02X}) count must be at least {}. Only {} were specified. Skipping.", action5_type->name, type, action5_type->min_sprites, num);
 
		GrfMsg(1, "GraphicsNew: {} (type 0x{:02X}) count must be at least {}. Only {} were specified. Skipping.", action5_type->name, type, action5_type->min_sprites, num);
 
		_cur.skip_sprites = num;
 
		return;
 
	}
 

	
 
	/* Load at most max_sprites sprites. Skip remaining sprites. (for compatibility with TTDP and future extensions) */
 
	uint16 skip_num = SanitizeSpriteOffset(num, offset, action5_type->max_sprites, action5_type->name);
 
	SpriteID replace = action5_type->sprite_base + offset;
 

	
 
	/* Load <num> sprites starting from <replace>, then skip <skip_num> sprites. */
 
	GrfMsg(2, "GraphicsNew: Replacing sprites {} to {} of {} (type {:#02X}) at SpriteID {:#04X}", offset, offset + num - 1, action5_type->name, type, replace);
 
	GrfMsg(2, "GraphicsNew: Replacing sprites {} to {} of {} (type 0x{:02X}) at SpriteID 0x{:04X}", offset, offset + num - 1, action5_type->name, type, replace);
 

	
 
	if (type == 0x0D) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_5;
 

	
 
	if (type == 0x0B) {
 
		static const SpriteID depot_with_track_offset = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_TRAMWAY_BASE;
 
		static const SpriteID depot_no_track_offset = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_TRAMWAY_BASE;
 
		if (offset <= depot_with_track_offset && offset + num > depot_with_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_WITH_TRACK;
 
		if (offset <= depot_no_track_offset && offset + num > depot_no_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NO_TRACK;
 
	}
 

	
 
	/* If the baseset or grf only provides sprites for flat tiles (pre #10282), duplicate those for use on slopes. */
 
	bool dup_oneway_sprites = ((type == 0x09) && (offset + num <= SPR_ONEWAY_SLOPE_N_OFFSET));
 
@@ -6655,25 +6655,25 @@ static uint32 GetParamVal(byte param, ui
 
			}
 

	
 
		case 0x88: // GRF ID check
 
			return 0;
 

	
 
		/* case 0x99: Global ID offset not implemented */
 

	
 
		default:
 
			/* GRF Parameter */
 
			if (param < 0x80) return _cur.grffile->GetParam(param);
 

	
 
			/* In-game variable. */
 
			GrfMsg(1, "Unsupported in-game variable {:#02X}", param);
 
			GrfMsg(1, "Unsupported in-game variable 0x{:02X}", param);
 
			return UINT_MAX;
 
	}
 
}
 

	
 
/* Action 0x06 */
 
static void CfgApply(ByteReader *buf)
 
{
 
	/* <06> <param-num> <param-size> <offset> ... <FF>
 
	 *
 
	 * B param-num     Number of parameter to substitute (First = "zero")
 
	 *                 Ignored if that parameter was not specified in newgrf.cfg
 
	 * B param-size    How many bytes to replace.  If larger than 4, the
 
@@ -6737,25 +6737,25 @@ static void CfgApply(ByteReader *buf)
 
		param_size = GB(param_size, 0, 7);
 

	
 
		/* Where to apply the data to within the pseudo sprite data. */
 
		offset     = buf->ReadExtendedByte();
 

	
 
		/* If the parameter is a GRF parameter (not an internal variable) check
 
		 * if it (and all further sequential parameters) has been defined. */
 
		if (param_num < 0x80 && (param_num + (param_size - 1) / 4) >= _cur.grffile->param_end) {
 
			GrfMsg(2, "CfgApply: Ignoring (param {} not set)", (param_num + (param_size - 1) / 4));
 
			break;
 
		}
 

	
 
		GrfMsg(8, "CfgApply: Applying {} bytes from parameter {:#02X} at offset {:#04X}", param_size, param_num, offset);
 
		GrfMsg(8, "CfgApply: Applying {} bytes from parameter 0x{:02X} at offset 0x{:04X}", param_size, param_num, offset);
 

	
 
		bool carry = false;
 
		for (i = 0; i < param_size && offset + i < num; i++) {
 
			uint32 value = GetParamVal(param_num + i / 4, nullptr);
 
			/* Reset carry flag for each iteration of the variable (only really
 
			 * matters if param_size is greater than 4) */
 
			if (i % 4 == 0) carry = false;
 

	
 
			if (add_value) {
 
				uint new_value = preload_sprite[offset + i] + GB(value, (i % 4) * 8, 8) + (carry ? 1 : 0);
 
				preload_sprite[offset + i] = GB(new_value, 0, 8);
 
				/* Check if the addition overflowed */
 
@@ -6810,25 +6810,25 @@ static void SkipIf(ByteReader *buf)
 
		case 8: cond_val = buf->ReadDWord(); mask = buf->ReadDWord(); break;
 
		case 4: cond_val = buf->ReadDWord(); mask = 0xFFFFFFFF; break;
 
		case 2: cond_val = buf->ReadWord();  mask = 0x0000FFFF; break;
 
		case 1: cond_val = buf->ReadByte();  mask = 0x000000FF; break;
 
		default: break;
 
	}
 

	
 
	if (param < 0x80 && _cur.grffile->param_end <= param) {
 
		GrfMsg(7, "SkipIf: Param {} undefined, skipping test", param);
 
		return;
 
	}
 

	
 
	GrfMsg(7, "SkipIf: Test condtype {}, param {:#02X}, condval {:#08X}", condtype, param, cond_val);
 
	GrfMsg(7, "SkipIf: Test condtype {}, param 0x{:02X}, condval 0x{:08X}", condtype, param, cond_val);
 

	
 
	/* condtypes that do not use 'param' are always valid.
 
	 * condtypes that use 'param' are either not valid for param 0x88, or they are only valid for param 0x88.
 
	 */
 
	if (condtype >= 0x0B) {
 
		/* Tests that ignore 'param' */
 
		switch (condtype) {
 
			case 0x0B: result = GetCargoIDByLabel(BSWAP32(cond_val)) == CT_INVALID;
 
				break;
 
			case 0x0C: result = GetCargoIDByLabel(BSWAP32(cond_val)) != CT_INVALID;
 
				break;
 
			case 0x0D: result = GetRailTypeByLabel(BSWAP32(cond_val)) == INVALID_RAILTYPE;
 
@@ -6859,25 +6859,25 @@ static void SkipIf(ByteReader *buf)
 
		}
 
	} else if (param == 0x88) {
 
		/* GRF ID checks */
 

	
 
		GRFConfig *c = GetGRFConfig(cond_val, mask);
 

	
 
		if (c != nullptr && HasBit(c->flags, GCF_STATIC) && !HasBit(_cur.grfconfig->flags, GCF_STATIC) && _networking) {
 
			DisableStaticNewGRFInfluencingNonStaticNewGRFs(c);
 
			c = nullptr;
 
		}
 

	
 
		if (condtype != 10 && c == nullptr) {
 
			GrfMsg(7, "SkipIf: GRFID {:#08X} unknown, skipping test", BSWAP32(cond_val));
 
			GrfMsg(7, "SkipIf: GRFID 0x{:08X} unknown, skipping test", BSWAP32(cond_val));
 
			return;
 
		}
 

	
 
		switch (condtype) {
 
			/* Tests 0x06 to 0x0A are only for param 0x88, GRFID checks */
 
			case 0x06: // Is GRFID active?
 
				result = c->status == GCS_ACTIVATED;
 
				break;
 

	
 
			case 0x07: // Is GRFID non-active?
 
				result = c->status != GCS_ACTIVATED;
 
				break;
 
@@ -6933,25 +6933,25 @@ static void SkipIf(ByteReader *buf)
 
		if (label.label != numsprites) continue;
 

	
 
		/* Remember a goto before the current line */
 
		if (choice == nullptr) choice = &label;
 
		/* If we find a label here, this is definitely good */
 
		if (label.nfo_line > _cur.nfo_line) {
 
			choice = &label;
 
			break;
 
		}
 
	}
 

	
 
	if (choice != nullptr) {
 
		GrfMsg(2, "SkipIf: Jumping to label {:#X} at line {}, test was true", choice->label, choice->nfo_line);
 
		GrfMsg(2, "SkipIf: Jumping to label 0x{:X} at line {}, test was true", choice->label, choice->nfo_line);
 
		_cur.file->SeekTo(choice->pos, SEEK_SET);
 
		_cur.nfo_line = choice->nfo_line;
 
		return;
 
	}
 

	
 
	GrfMsg(2, "SkipIf: Skipping {} sprites, test was true", numsprites);
 
	_cur.skip_sprites = numsprites;
 
	if (_cur.skip_sprites == 0) {
 
		/* Zero means there are no sprites to skip, so
 
		 * we use -1 to indicate that all further
 
		 * sprites should be skipped. */
 
		_cur.skip_sprites = -1;
 
@@ -7279,25 +7279,25 @@ static uint32 GetPatchVariable(uint8 par
 
		case 0x15:
 
			return SPR_SLOPES_BASE;
 

	
 
		/* Shore base sprite */
 
		case 0x16:
 
			return SPR_SHORE_BASE;
 

	
 
		/* Game map seed */
 
		case 0x17:
 
			return _settings_game.game_creation.generation_seed;
 

	
 
		default:
 
			GrfMsg(2, "ParamSet: Unknown Patch variable {:#02X}.", param);
 
			GrfMsg(2, "ParamSet: Unknown Patch variable 0x{:02X}.", param);
 
			return 0;
 
	}
 
}
 

	
 

	
 
static uint32 PerformGRM(uint32 *grm, uint16 num_ids, uint16 count, uint8 op, uint8 target, const char *type)
 
{
 
	uint start = 0;
 
	uint size  = 0;
 

	
 
	if (op == 6) {
 
		/* Return GRFID of set that reserved ID */
 
@@ -7458,25 +7458,25 @@ static void ParamSet(ByteReader *buf)
 
								default:
 
									GrfMsg(1, "ParamSet: GRM: Unsupported operation {} for general sprites", op);
 
									return;
 
							}
 
							break;
 

	
 
						case 0x0B: // Cargo
 
							/* There are two ranges: one for cargo IDs and one for cargo bitmasks */
 
							src1 = PerformGRM(_grm_cargoes, NUM_CARGO * 2, count, op, target, "cargoes");
 
							if (_cur.skip_sprites == -1) return;
 
							break;
 

	
 
						default: GrfMsg(1, "ParamSet: GRM: Unsupported feature {:#X}", feature); return;
 
						default: GrfMsg(1, "ParamSet: GRM: Unsupported feature 0x{:X}", feature); return;
 
					}
 
				} else {
 
					/* Ignore GRM during initialization */
 
					src1 = 0;
 
				}
 
			}
 
		} else {
 
			/* Read another GRF File's parameter */
 
			const GRFFile *file = GetFileByGRFID(data);
 
			GRFConfig *c = GetGRFConfig(data);
 
			if (c != nullptr && HasBit(c->flags, GCF_STATIC) && !HasBit(_cur.grfconfig->flags, GCF_STATIC) && _networking) {
 
				/* Disable the read GRF if it is a static NewGRF. */
 
@@ -7598,55 +7598,55 @@ static void ParamSet(ByteReader *buf)
 
			}
 
			_railtypes[RAILTYPE_MAGLEV].cost_multiplier = GB(res, 16, 8);
 
			break;
 
		}
 

	
 
		/* not implemented */
 
		case 0x93: // Tile refresh offset to left -- Intended to allow support for larger sprites, not necessary for OTTD
 
		case 0x94: // Tile refresh offset to right
 
		case 0x95: // Tile refresh offset upwards
 
		case 0x96: // Tile refresh offset downwards
 
		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
 
		case 0x99: // Global ID offset -- Not necessary since IDs are remapped automatically
 
			GrfMsg(7, "ParamSet: Skipping unimplemented target {:#02X}", target);
 
			GrfMsg(7, "ParamSet: Skipping unimplemented target 0x{:02X}", target);
 
			break;
 

	
 
		case 0x9E: // Miscellaneous GRF features
 
			/* Set train list engine width */
 
			_cur.grffile->traininfo_vehicle_width = HasBit(res, GMB_TRAIN_WIDTH_32_PIXELS) ? VEHICLEINFO_FULL_VEHICLE_WIDTH : TRAININFO_DEFAULT_VEHICLE_WIDTH;
 
			/* Remove the local flags from the global flags */
 
			ClrBit(res, GMB_TRAIN_WIDTH_32_PIXELS);
 

	
 
			/* Only copy safe bits for static grfs */
 
			if (HasBit(_cur.grfconfig->flags, GCF_STATIC)) {
 
				uint32 safe_bits = 0;
 
				SetBit(safe_bits, GMB_SECOND_ROCKY_TILE_SET);
 

	
 
				_misc_grf_features = (_misc_grf_features & ~safe_bits) | (res & safe_bits);
 
			} else {
 
				_misc_grf_features = res;
 
			}
 
			break;
 

	
 
		case 0x9F: // locale-dependent settings
 
			GrfMsg(7, "ParamSet: Skipping unimplemented target {:#02X}", target);
 
			GrfMsg(7, "ParamSet: Skipping unimplemented target 0x{:02X}", target);
 
			break;
 

	
 
		default:
 
			if (target < 0x80) {
 
				_cur.grffile->param[target] = res;
 
				/* param is zeroed by default */
 
				if (target + 1U > _cur.grffile->param_end) _cur.grffile->param_end = target + 1;
 
			} else {
 
				GrfMsg(7, "ParamSet: Skipping unknown target {:#02X}", target);
 
				GrfMsg(7, "ParamSet: Skipping unknown target 0x{:02X}", target);
 
			}
 
			break;
 
	}
 
}
 

	
 
/* Action 0x0E (GLS_SAFETYSCAN) */
 
static void SafeGRFInhibit(ByteReader *buf)
 
{
 
	/* <0E> <num> <grfids...>
 
	 *
 
	 * B num           Number of GRFIDs that follow
 
	 * D grfids        GRFIDs of the files to deactivate */
 
@@ -7697,41 +7697,41 @@ static void FeatureTownName(ByteReader *
 
	/* <0F> <id> <style-name> <num-parts> <parts>
 
	 *
 
	 * B id          ID of this definition in bottom 7 bits (final definition if bit 7 set)
 
	 * V style-name  Name of the style (only for final definition)
 
	 * B num-parts   Number of parts in this definition
 
	 * V parts       The parts */
 

	
 
	uint32 grfid = _cur.grffile->grfid;
 

	
 
	GRFTownName *townname = AddGRFTownName(grfid);
 

	
 
	byte id = buf->ReadByte();
 
	GrfMsg(6, "FeatureTownName: definition {:#02X}", id & 0x7F);
 
	GrfMsg(6, "FeatureTownName: definition 0x{:02X}", id & 0x7F);
 

	
 
	if (HasBit(id, 7)) {
 
		/* Final definition */
 
		ClrBit(id, 7);
 
		bool new_scheme = _cur.grffile->grf_version >= 7;
 

	
 
		byte lang = buf->ReadByte();
 
		StringID style = STR_UNDEFINED;
 

	
 
		do {
 
			ClrBit(lang, 7);
 

	
 
			const char *name = buf->ReadString();
 

	
 
			std::string lang_name = TranslateTTDPatchCodes(grfid, lang, false, name);
 
			GrfMsg(6, "FeatureTownName: lang {:#X} -> '{}'", lang, lang_name.c_str());
 
			GrfMsg(6, "FeatureTownName: lang 0x{:X} -> '{}'", lang, lang_name.c_str());
 

	
 
			style = AddGRFString(grfid, id, lang, new_scheme, false, name, STR_UNDEFINED);
 

	
 
			lang = buf->ReadByte();
 
		} while (lang != 0);
 
		townname->styles.emplace_back(style, id);
 
	}
 

	
 
	uint8 parts = buf->ReadByte();
 
	GrfMsg(6, "FeatureTownName: {} parts", parts);
 

	
 
	townname->partlists[id].reserve(parts);
 
@@ -7742,55 +7742,55 @@ static void FeatureTownName(ByteReader *
 
		partlist.bitcount = buf->ReadByte();
 
		partlist.maxprob  = 0;
 
		GrfMsg(6, "FeatureTownName: part {} contains {} texts and will use GB(seed, {}, {})", partnum, texts, partlist.bitstart, partlist.bitcount);
 

	
 
		partlist.parts.reserve(texts);
 
		for (uint textnum = 0; textnum < texts; textnum++) {
 
			NamePart &part = partlist.parts.emplace_back();
 
			part.prob = buf->ReadByte();
 

	
 
			if (HasBit(part.prob, 7)) {
 
				byte ref_id = buf->ReadByte();
 
				if (ref_id >= GRFTownName::MAX_LISTS || townname->partlists[ref_id].empty()) {
 
					GrfMsg(0, "FeatureTownName: definition {:#02X} doesn't exist, deactivating", ref_id);
 
					GrfMsg(0, "FeatureTownName: definition 0x{:02X} doesn't exist, deactivating", ref_id);
 
					DelGRFTownName(grfid);
 
					DisableGrf(STR_NEWGRF_ERROR_INVALID_ID);
 
					return;
 
				}
 
				part.id = ref_id;
 
				GrfMsg(6, "FeatureTownName: part {}, text {}, uses intermediate definition {:#02X} (with probability {})", partnum, textnum, ref_id, part.prob & 0x7F);
 
				GrfMsg(6, "FeatureTownName: part {}, text {}, uses intermediate definition 0x{:02X} (with probability {})", partnum, textnum, ref_id, part.prob & 0x7F);
 
			} else {
 
				const char *text = buf->ReadString();
 
				part.text = TranslateTTDPatchCodes(grfid, 0, false, text);
 
				GrfMsg(6, "FeatureTownName: part {}, text {}, '{}' (with probability {})", partnum, textnum, part.text.c_str(), part.prob);
 
			}
 
			partlist.maxprob += GB(part.prob, 0, 7);
 
		}
 
		GrfMsg(6, "FeatureTownName: part {}, total probability {}", partnum, partlist.maxprob);
 
	}
 
}
 

	
 
/** Action 0x10 - Define goto label */
 
static void DefineGotoLabel(ByteReader *buf)
 
{
 
	/* <10> <label> [<comment>]
 
	 *
 
	 * B label      The label to define
 
	 * V comment    Optional comment - ignored */
 

	
 
	byte nfo_label = buf->ReadByte();
 

	
 
	_cur.grffile->labels.emplace_back(nfo_label, _cur.nfo_line, _cur.file->GetPos());
 

	
 
	GrfMsg(2, "DefineGotoLabel: GOTO target with label {:#02X}", nfo_label);
 
	GrfMsg(2, "DefineGotoLabel: GOTO target with label 0x{:02X}", nfo_label);
 
}
 

	
 
/**
 
 * Process a sound import from another GRF file.
 
 * @param sound Destination for sound.
 
 */
 
static void ImportGRFSound(SoundEntry *sound)
 
{
 
	const GRFFile *file;
 
	uint32 grfid = _cur.file->ReadDword();
 
	SoundID sound_id = _cur.file->ReadWord();
 

	
 
@@ -7950,25 +7950,25 @@ static void LoadFontGlyph(ByteReader *bu
 

	
 
	uint8 num_def = buf->ReadByte();
 

	
 
	for (uint i = 0; i < num_def; i++) {
 
		FontSize size    = (FontSize)buf->ReadByte();
 
		uint8  num_char  = buf->ReadByte();
 
		uint16 base_char = buf->ReadWord();
 

	
 
		if (size >= FS_END) {
 
			GrfMsg(1, "LoadFontGlyph: Size {} is not supported, ignoring", size);
 
		}
 

	
 
		GrfMsg(7, "LoadFontGlyph: Loading {} glyph(s) at {:#04X} for size {}", num_char, base_char, size);
 
		GrfMsg(7, "LoadFontGlyph: Loading {} glyph(s) at 0x{:04X} for size {}", num_char, base_char, size);
 

	
 
		for (uint c = 0; c < num_char; c++) {
 
			if (size < FS_END) SetUnicodeGlyph(size, base_char + c, _cur.spriteid);
 
			_cur.nfo_line++;
 
			LoadNextSprite(_cur.spriteid++, *_cur.file, _cur.nfo_line);
 
		}
 
	}
 
}
 

	
 
/** Action 0x12 (SKIP) */
 
static void SkipAct12(ByteReader *buf)
 
{
 
@@ -7999,49 +7999,49 @@ static void SkipAct12(ByteReader *buf)
 
static void TranslateGRFStrings(ByteReader *buf)
 
{
 
	/* <13> <grfid> <num-ent> <offset> <text...>
 
	 *
 
	 * 4*B grfid     The GRFID of the file whose texts are to be translated
 
	 * B   num-ent   Number of strings
 
	 * W   offset    First text ID
 
	 * S   text...   Zero-terminated strings */
 

	
 
	uint32 grfid = buf->ReadDWord();
 
	const GRFConfig *c = GetGRFConfig(grfid);
 
	if (c == nullptr || (c->status != GCS_INITIALISED && c->status != GCS_ACTIVATED)) {
 
		GrfMsg(7, "TranslateGRFStrings: GRFID {:#08X} unknown, skipping action 13", BSWAP32(grfid));
 
		GrfMsg(7, "TranslateGRFStrings: GRFID 0x{:08X} unknown, skipping action 13", BSWAP32(grfid));
 
		return;
 
	}
 

	
 
	if (c->status == GCS_INITIALISED) {
 
		/* If the file is not active but will be activated later, give an error
 
		 * and disable this file. */
 
		GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LOAD_AFTER);
 

	
 
		error->data = GetString(STR_NEWGRF_ERROR_AFTER_TRANSLATED_FILE);
 

	
 
		return;
 
	}
 

	
 
	/* Since no language id is supplied for with version 7 and lower NewGRFs, this string has
 
	 * to be added as a generic string, thus the language id of 0x7F. For this to work
 
	 * new_scheme has to be true as well, which will also be implicitly the case for version 8
 
	 * and higher. A language id of 0x7F will be overridden by a non-generic id, so this will
 
	 * not change anything if a string has been provided specifically for this language. */
 
	byte language = _cur.grffile->grf_version >= 8 ? buf->ReadByte() : 0x7F;
 
	byte num_strings = buf->ReadByte();
 
	uint16 first_id  = buf->ReadWord();
 

	
 
	if (!((first_id >= 0xD000 && first_id + num_strings <= 0xD400) || (first_id >= 0xD800 && first_id + num_strings <= 0xE000))) {
 
		GrfMsg(7, "TranslateGRFStrings: Attempting to set out-of-range string IDs in action 13 (first: {:#04X}, number: {:#02X})", first_id, num_strings);
 
		GrfMsg(7, "TranslateGRFStrings: Attempting to set out-of-range string IDs in action 13 (first: 0x{:04X}, number: 0x{:02X})", first_id, num_strings);
 
		return;
 
	}
 

	
 
	for (uint i = 0; i < num_strings && buf->HasData(); i++) {
 
		const char *string = buf->ReadString();
 

	
 
		if (StrEmpty(string)) {
 
			GrfMsg(7, "TranslateGRFString: Ignoring empty string.");
 
			continue;
 
		}
 

	
 
		AddGRFString(grfid, first_id + i, language, true, true, string, STR_UNDEFINED);
 
@@ -9491,29 +9491,29 @@ static void DecodeSpecialSprite(byte *bu
 

	
 
	ByteReader br(buf, buf + num);
 
	ByteReader *bufp = &br;
 

	
 
	try {
 
		byte action = bufp->ReadByte();
 

	
 
		if (action == 0xFF) {
 
			GrfMsg(2, "DecodeSpecialSprite: Unexpected data block, skipping");
 
		} else if (action == 0xFE) {
 
			GrfMsg(2, "DecodeSpecialSprite: Unexpected import block, skipping");
 
		} else if (action >= lengthof(handlers)) {
 
			GrfMsg(7, "DecodeSpecialSprite: Skipping unknown action {:#02X}", action);
 
			GrfMsg(7, "DecodeSpecialSprite: Skipping unknown action 0x{:02X}", action);
 
		} else if (handlers[action][stage] == nullptr) {
 
			GrfMsg(7, "DecodeSpecialSprite: Skipping action {:#02X} in stage {}", action, stage);
 
			GrfMsg(7, "DecodeSpecialSprite: Skipping action 0x{:02X} in stage {}", action, stage);
 
		} else {
 
			GrfMsg(7, "DecodeSpecialSprite: Handling action {:#02X} in stage {}", action, stage);
 
			GrfMsg(7, "DecodeSpecialSprite: Handling action 0x{:02X} in stage {}", action, stage);
 
			handlers[action][stage](bufp);
 
		}
 
	} catch (...) {
 
		GrfMsg(1, "DecodeSpecialSprite: Tried to read past end of pseudo-sprite data");
 
		DisableGrf(STR_NEWGRF_ERROR_READ_BOUNDS);
 
	}
 
}
 

	
 
/**
 
 * Load a particular NewGRF from a SpriteFile.
 
 * @param config The configuration of the to be loaded NewGRF.
 
 * @param stage  The loading stage of the NewGRF.