|
@@ -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.
|