diff --git a/src/cargotype.cpp b/src/cargotype.cpp index 5593b82bf8..63253847be 100644 --- a/src/cargotype.cpp +++ b/src/cargotype.cpp @@ -75,3 +75,14 @@ bool CargoSpec::IsValid() const { return bitnum != INVALID_CARGO; } + + +CargoID GetCargoIDByLabel(CargoLabel cl) +{ + for (CargoID c = 0; c < lengthof(_cargo); c++) { + if (_cargo[c].label == cl) return c; + } + + /* No matching label was found, so it is invalid */ + return CT_INVALID; +} diff --git a/src/cargotype.h b/src/cargotype.h index 0e43795173..a584aef98a 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -47,6 +47,8 @@ void SetupCargoForClimate(LandscapeID l); const CargoSpec *GetCargo(CargoID c); /* Get the cargo ID of a cargo bitnum */ CargoID GetCargoIDByBitnum(byte bitnum); +/* Get the cargo ID with the cargo label */ +CargoID GetCargoIDByLabel(CargoLabel cl); #endif /* CARGOTYPE_H */ diff --git a/src/newgrf.cpp b/src/newgrf.cpp index d05e52635b..1c005bf97d 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -68,6 +68,10 @@ static byte *_preload_sprite = NULL; /* Set if any vehicle is loaded which uses 2cc (two company colours) */ bool _have_2cc = false; +/* Default cargo translation table. By default there are 27 possible cargo types */ +static const uint _default_cargo_max = 27; +static CargoLabel _default_cargo_list[_default_cargo_max]; + typedef enum GrfDataType { GDT_SOUND, @@ -1189,6 +1193,12 @@ static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, i } break; + case 0x09: /* Cargo translation table */ + /* This is loaded during the initialisation stage, so just skip it here. */ + /* Each entry is 4 bytes. */ + buf += numinfo * 4; + break; + case 0x0A: // Currency display names FOR_EACH_OBJECT { uint curidx = GetNewgrfCurrencyIdConverted(gvid + i); @@ -1273,7 +1283,6 @@ static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, i } break; - case 0x09: // Cargo translation table case 0x10: // 12 * 32 * B Snow line height table default: ret = true; @@ -1501,6 +1510,55 @@ static void SafeChangeInfo(byte *buf, int len) _skip_sprites = -1; } +/* Action 0x00 (GLS_INIT) */ +static void InitChangeInfo(byte *buf, int len) +{ + byte *bufend = buf + len; + uint8 feature; + uint8 numprops; + uint8 numinfo; + uint8 index; + + if (len == 1) { + grfmsg(8, "Silently ignoring one-byte special sprite 0x00"); + return; + } + + if (!check_length(len, 6, "InitChangeInfo")) return; + buf++; + feature = grf_load_byte(&buf); + numprops = grf_load_byte(&buf); + numinfo = grf_load_byte(&buf); + index = grf_load_byte(&buf); + + while (numprops-- && buf < bufend) { + uint8 prop = grf_load_byte(&buf); + + switch (feature) { + case GSF_GLOBALVAR: + switch (prop) { + case 0x09: /* Cargo Translation Table */ + if (index != 0) { + grfmsg(1, "InitChangeInfo: Cargo translation table must start at zero"); + return; + } + + free(_cur_grffile->cargo_list); + _cur_grffile->cargo_max = numinfo; + _cur_grffile->cargo_list = MallocT(numinfo); + + int i; + FOR_EACH_OBJECT { + CargoLabel cl = grf_load_dword(&buf); + _cur_grffile->cargo_list[i] = BSWAP32(cl); + } + break; + } + break; + } + } +} + #undef FOR_EACH_OBJECT /** @@ -1834,6 +1892,38 @@ static void NewSpriteGroup(byte *buf, int len) _cur_grffile->spritegroups[setid] = group; } +static CargoID TranslateCargo(uint8 feature, uint8 ctype) +{ + /* Special cargo types for purchase list and stations */ + if (feature == GSF_STATION && ctype == 0xFE) return GC_DEFAULT_NA; + if (ctype == 0xFF) return GC_PURCHASE; + + /* Check if the cargo type is out of bounds of the cargo translation table */ + if (ctype >= (_cur_grffile->cargo_max == 0 ? _default_cargo_max : _cur_grffile->cargo_max)) { + grfmsg(1, "FeatureMapSpriteGroup: Cargo type %d out of range (max %d), skipping.", ctype, (_cur_grffile->cargo_max == 0 ? _default_cargo_max : _cur_grffile->cargo_max) - 1); + return CT_INVALID; + } + + /* Look up the cargo label from the translation table */ + CargoLabel cl = _cur_grffile->cargo_max == 0 ? _default_cargo_list[ctype] : _cur_grffile->cargo_list[ctype]; + if (cl == 0) { + grfmsg(5, "FeatureMapSpriteGroup: Cargo type %d not available in this climate, skipping.", ctype); + return CT_INVALID; + } + + ctype = GetCargoIDByLabel(cl); + if (ctype == CT_INVALID) { + grfmsg(5, "FeatureMapSpriteGroup: 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; + } + + /* Remap back to global cargo */ + ctype = GetCargo(ctype)->bitnum; + + grfmsg(6, "FeatureMapSpriteGroup: Cargo '%c%c%c%c' mapped to cargo type %d.", GB(cl, 24, 8), GB(cl, 16, 8), GB(cl, 8, 8), GB(cl, 0, 8), ctype); + return ctype; +} + /* Action 0x03 */ static void FeatureMapSpriteGroup(byte *buf, int len) { @@ -1905,13 +1995,8 @@ static void FeatureMapSpriteGroup(byte *buf, int len) return; } - if (ctype == 0xFE) ctype = GC_DEFAULT_NA; - if (ctype == 0xFF) ctype = GC_PURCHASE; - - if (ctype >= NUM_GLOBAL_CID) { - grfmsg(1, "FeatureMapSpriteGroup: Cargo type %d out of range, skipping.", ctype); - continue; - } + ctype = TranslateCargo(feature, ctype); + if (ctype == CT_INVALID) continue; statspec->spritegroup[ctype] = _cur_grffile->spritegroups[groupid]; } @@ -1987,12 +2072,8 @@ static void FeatureMapSpriteGroup(byte *buf, int len) return; } - if (ctype == GC_INVALID) ctype = GC_PURCHASE; - - if (ctype >= NUM_GLOBAL_CID) { - grfmsg(1, "FeatureMapSpriteGroup: Cargo type %d out of range, skipping.", ctype); - continue; - } + ctype = TranslateCargo(feature, ctype); + if (ctype == CT_INVALID) continue; if (wagover) { SetWagonOverrideSprites(engine, ctype, _cur_grffile->spritegroups[groupid], last_engines, last_engines_count); @@ -3589,6 +3670,13 @@ static void ResetNewGRFData(void) /* Set up the default cargo types */ SetupCargoForClimate(_opt.landscape); + /* Generate default cargo translation table */ + memset(_default_cargo_list, 0, sizeof(_default_cargo_list)); + for (CargoID c = 0; c != NUM_CARGO; c++) { + const CargoSpec *cs = GetCargo(c); + if (cs->IsValid()) _default_cargo_list[cs->bitnum] = cs->label; + } + /* Reset misc GRF features and train list display variables */ _misc_grf_features = 0; _traininfo_vehicle_pitch = 0; @@ -3755,7 +3843,7 @@ static void DecodeSpecialSprite(uint num, GrfLoadingStage stage) * is not in memory and scanning the file every time would be too expensive. * In other stages we skip action 0x10 since it's already dealt with. */ static const SpecialSpriteHandler handlers[][GLS_END] = { - /* 0x00 */ { NULL, SafeChangeInfo, NULL, NULL, FeatureChangeInfo, }, + /* 0x00 */ { NULL, SafeChangeInfo, NULL, InitChangeInfo, FeatureChangeInfo, }, /* 0x01 */ { NULL, GRFUnsafe, NULL, NULL, NewSpriteSet, }, /* 0x02 */ { NULL, GRFUnsafe, NULL, NULL, NewSpriteGroup, }, /* 0x03 */ { NULL, GRFUnsafe, NULL, NULL, FeatureMapSpriteGroup, }, diff --git a/src/newgrf.h b/src/newgrf.h index 697ef9fc02..964f533ae8 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -6,6 +6,7 @@ #include "station.h" #include "newgrf_config.h" #include "helpers.hpp" +#include "cargotype.h" typedef enum GrfLoadingStage { GLS_FILESCAN, @@ -60,6 +61,9 @@ typedef struct GRFFile { uint param_end; /// one more than the highest set parameter GRFLabel *label; ///< Pointer to the first label. This is a linked list, not an array. + + uint8 cargo_max; + CargoLabel *cargo_list; } GRFFile; extern GRFFile *_first_grffile;