tron@2186: /* $Id$ */ tron@2186: rubidium@10429: /** @file newgrf.cpp Base of all NewGRF support. */ belugas@6674: truelight@0: #include "stdafx.h" truelight@0: truelight@0: #include truelight@0: Darkvater@1891: #include "openttd.h" tron@1299: #include "debug.h" truelight@0: #include "fileio.h" rubidium@9282: #include "engine_func.h" peter1138@10382: #include "engine_base.h" tron@2342: #include "spritecache.h" darkvater@405: #include "sprite.h" dominik@452: #include "newgrf.h" tron@2159: #include "variables.h" peter1138@2478: #include "bridge.h" maedhros@6658: #include "town.h" peter1138@2962: #include "newgrf_engine.h" belugas@3601: #include "newgrf_text.h" peter1138@5108: #include "fontcache.h" belugas@4377: #include "currency.h" maedhros@6669: #include "landscape.h" peter1138@5228: #include "newgrf_config.h" maedhros@6658: #include "newgrf_house.h" peter1138@4656: #include "newgrf_sound.h" peter1138@3595: #include "newgrf_spritegroup.h" rubidium@9283: #include "newgrf_station.h" peter1138@6417: #include "cargotype.h" belugas@7029: #include "industry.h" peter1138@7079: #include "newgrf_canal.h" belugas@7125: #include "newgrf_commons.h" glx@7452: #include "newgrf_townname.h" rubidium@7729: #include "newgrf_industries.h" rubidium@8268: #include "gfxinit.h" smatz@10402: #include "rev.h" rubidium@8301: #include "fios.h" rubidium@8599: #include "rail.h" rubidium@8610: #include "strings_func.h" rubidium@8619: #include "gfx_func.h" rubidium@8636: #include "date_func.h" rubidium@8640: #include "vehicle_func.h" rubidium@8653: #include "sound_func.h" rubidium@8710: #include "string_func.h" rubidium@8732: #include "road_func.h" rubidium@8750: #include "player_base.h" rubidium@8766: #include "settings_type.h" rubidium@11020: #include "network/network.h" belugas@8887: #include "map_func.h" peter1138@10382: #include peter1138@3595: rubidium@8760: #include "table/strings.h" rubidium@8760: #include "table/sprites.h" rubidium@8760: #include "table/town_land.h" rubidium@8760: #include "table/build_industry.h" rubidium@8760: #include "table/landscape_sprite.h" rubidium@8760: truelight@0: /* TTDPatch extended GRF format codec truelight@0: * (c) Petr Baudis 2004 (GPL'd) celestar@356: * Changes by Florian octo Forster are (c) by the OpenTTD development team. celestar@356: * truelight@0: * Contains portions of documentation by TTDPatch team. truelight@0: * Thanks especially to Josef Drexler for the documentation as well as a lot truelight@0: * of help at #tycoon. Also thanks to Michael Blunck for is GRF files which truelight@0: * served as subject to the initial testing of this codec. */ truelight@0: dominik@455: tron@2342: static int _skip_sprites; // XXX tron@2342: static uint _file_index; // XXX truelight@0: tron@2340: static GRFFile *_cur_grffile; tron@1477: GRFFile *_first_grffile; peter1138@3707: static SpriteID _cur_spriteid; peter1138@5225: static GrfLoadingStage _cur_stage; Darkvater@3561: static uint32 _nfo_line; truelight@0: peter1138@5228: static GRFConfig *_cur_grfconfig; peter1138@5228: peter1138@3814: /* Miscellaneous GRF features, set by Action 0x0D, parameter 0x9E */ peter1138@3814: static byte _misc_grf_features = 0; peter1138@3814: darkvater@363: /* 32 * 8 = 256 flags. Apparently TTDPatch uses this many.. */ darkvater@363: static uint32 _ttdpatch_flags[8]; darkvater@363: peter1138@3895: /* Used by Action 0x06 to preload a pseudo sprite and modify its content */ peter1138@3895: static byte *_preload_sprite = NULL; peter1138@3895: belugas@7265: /* Indicates which are the newgrf features currently loaded ingame */ belugas@7410: GRFLoadedFeatures _loaded_newgrf_features; darkvater@363: rubidium@6574: enum GrfDataType { peter1138@4656: GDT_SOUND, rubidium@6574: }; peter1138@4656: peter1138@4656: static byte _grf_data_blocks; peter1138@4656: static GrfDataType _grf_data_type; peter1138@4656: peter1138@4656: glx@10465: typedef void (*SpecialSpriteHandler)(byte *buf, size_t len); truelight@0: peter1138@3768: enum { peter1138@3768: MAX_STATIONS = 256, peter1138@3768: }; peter1138@3768: peter1138@10298: /* Temporary data used when loading only */ peter1138@10298: struct GRFTempEngineData { peter1138@10298: uint16 cargo_allowed; peter1138@10298: uint16 cargo_disallowed; peter1138@10298: }; peter1138@10298: peter1138@10298: static GRFTempEngineData *_gted; truelight@0: peter1138@10382: /* Contains the GRF ID of the owner of a vehicle if it has been reserved. peter1138@10382: * GRM for vehicles is only used if dynamic engine allocation is disabled, peter1138@10382: * so 256 is the number of original engines. */ peter1138@10382: static uint32 _grm_engines[256]; peter1138@4961: peter1138@7277: /* Contains the GRF ID of the owner of a cargo if it has been reserved */ peter1138@9171: static uint32 _grm_cargos[NUM_CARGO * 2]; peter1138@7277: peter1138@10431: struct GRFLocation { peter1138@10416: uint32 grfid; peter1138@10416: uint32 nfoline; peter1138@10416: peter1138@10431: GRFLocation(uint32 grfid, uint32 nfoline) : grfid(grfid), nfoline(nfoline) { } peter1138@10431: peter1138@10431: bool operator<(const GRFLocation &other) const peter1138@10416: { peter1138@10416: return this->grfid < other.grfid || (this->grfid == other.grfid && this->nfoline < other.nfoline); peter1138@10416: } peter1138@10416: }; peter1138@10416: peter1138@10431: static std::map _grm_sprites; peter1138@10416: Darkvater@5568: /** DEBUG() function dedicated to newGRF debugging messages Darkvater@5568: * Function is essentialy the same as DEBUG(grf, severity, ...) with the Darkvater@5568: * addition of file:line information when parsing grf files. Darkvater@5568: * NOTE: for the above reason(s) grfmsg() should ONLY be used for Darkvater@5568: * loading/parsing grf files, not for runtime debug messages as there Darkvater@5568: * is no file information available during that time. Darkvater@5568: * @param severity debugging severity level, see debug.h belugas@6977: * @param str message in printf() format */ Darkvater@5568: void CDECL grfmsg(int severity, const char *str, ...) truelight@0: { darkvater@65: char buf[1024]; truelight@0: va_list va; truelight@0: truelight@0: va_start(va, str); tron@1477: vsnprintf(buf, sizeof(buf), str, va); truelight@0: va_end(va); celestar@372: Darkvater@5568: DEBUG(grf, severity, "[%s:%d] %s", _cur_grfconfig->filename, _nfo_line, buf); truelight@0: } truelight@0: glx@10465: static inline bool check_length(size_t real, size_t wanted, const char *str) Darkvater@5567: { peter1138@5841: if (real >= wanted) return true; Darkvater@5567: grfmsg(0, "%s: Invalid pseudo sprite length %d (expected %d)!", str, real, wanted); peter1138@5841: return false; Darkvater@5567: } celestar@357: tron@500: static inline byte grf_load_byte(byte **buf) darkvater@368: { truelight@0: return *(*buf)++; truelight@0: } truelight@0: truelight@0: static uint16 grf_load_word(byte **buf) truelight@0: { Darkvater@3711: uint16 val = grf_load_byte(buf); Darkvater@3711: return val | (grf_load_byte(buf) << 8); truelight@0: } truelight@0: tron@2346: static uint16 grf_load_extended(byte** buf) tron@2346: { tron@2346: uint16 val; tron@2346: val = grf_load_byte(buf); tron@2346: if (val == 0xFF) val = grf_load_word(buf); tron@2346: return val; tron@2346: } tron@2346: miham@2324: static uint32 grf_load_dword(byte **buf) truelight@0: { Darkvater@3711: uint32 val = grf_load_word(buf); Darkvater@3711: return val | (grf_load_word(buf) << 16); truelight@0: } truelight@0: peter1138@3668: static uint32 grf_load_var(byte size, byte **buf) peter1138@3668: { peter1138@3668: switch (size) { peter1138@3668: case 1: return grf_load_byte(buf); peter1138@3668: case 2: return grf_load_word(buf); peter1138@3668: case 4: return grf_load_dword(buf); peter1138@3668: default: peter1138@3668: NOT_REACHED(); peter1138@3668: return 0; peter1138@3668: } peter1138@3668: } celestar@357: maedhros@6416: static const char *grf_load_string(byte **buf, size_t max_len) maedhros@6416: { maedhros@6416: const char *string = *(const char **)buf; maedhros@6416: size_t string_length = ttd_strnlen(string, max_len); maedhros@6416: maedhros@6416: if (string_length == max_len) { maedhros@6416: /* String was not NUL terminated, so make sure it is now. */ maedhros@6416: (*buf)[string_length - 1] = '\0'; maedhros@6416: grfmsg(7, "String was not terminated with a zero byte."); maedhros@6416: } else { maedhros@6416: /* Increase the string length to include the NUL byte. */ maedhros@6416: string_length++; maedhros@6416: } maedhros@6416: *buf += string_length; maedhros@6416: maedhros@6416: return string; maedhros@6416: } maedhros@6416: tron@1477: static GRFFile *GetFileByGRFID(uint32 grfid) darkvater@366: { tron@1477: GRFFile *file; darkvater@366: tron@1477: for (file = _first_grffile; file != NULL; file = file->next) { tron@1477: if (file->grfid == grfid) break; tron@1477: } darkvater@366: return file; darkvater@366: } darkvater@366: tron@1477: static GRFFile *GetFileByFilename(const char *filename) darkvater@366: { tron@1477: GRFFile *file; darkvater@366: tron@1477: for (file = _first_grffile; file != NULL; file = file->next) { tron@1477: if (strcmp(file->filename, filename) == 0) break; tron@1477: } darkvater@366: return file; darkvater@366: } darkvater@366: rubidium@11117: /** Reset all NewGRFData that was used only while processing data */ rubidium@11117: static void ClearTemporaryNewGRFData() rubidium@11117: { rubidium@11117: /* Clear the GOTO labels used for GRF processing */ rubidium@11117: for (GRFLabel *l = _cur_grffile->label; l != NULL;) { rubidium@11117: GRFLabel *l2 = l->next; rubidium@11117: free(l); rubidium@11117: l = l2; rubidium@11117: } rubidium@11117: _cur_grffile->label = NULL; rubidium@11117: rubidium@11117: /* Clear the list of spritegroups */ rubidium@11117: free(_cur_grffile->spritegroups); rubidium@11117: _cur_grffile->spritegroups = NULL; rubidium@11117: _cur_grffile->spritegroups_count = 0; rubidium@11117: } rubidium@11117: darkvater@366: rubidium@10219: typedef std::map StringIDToGRFIDMapping; rubidium@10219: StringIDToGRFIDMapping _string_to_grf_mapping; rubidium@10219: maedhros@6658: /** Used when setting an object's property to map to the GRF's strings maedhros@6658: * while taking in consideration the "drift" between TTDPatch string system and OpenTTD's one belugas@6977: * @param grfid Id of the grf file maedhros@6658: * @param str StringID that we want to have the equivalent in OoenTTD maedhros@6658: * @return the properly adjusted StringID maedhros@6658: */ glx@8111: StringID MapGRFStringID(uint32 grfid, StringID str) maedhros@6658: { glx@8375: /* StringID table for TextIDs 0x4E->0x6D */ glx@8375: static StringID units_volume[] = { glx@8375: STR_NOTHING, STR_PASSENGERS, STR_TONS, STR_BAGS, glx@8375: STR_LITERS, STR_ITEMS, STR_CRATES, STR_TONS, glx@8375: STR_TONS, STR_TONS, STR_TONS, STR_BAGS, glx@8375: STR_TONS, STR_TONS, STR_TONS, STR_BAGS, glx@8375: STR_TONS, STR_TONS, STR_BAGS, STR_LITERS, glx@8375: STR_TONS, STR_LITERS, STR_TONS, STR_NOTHING, glx@8375: STR_BAGS, STR_LITERS, STR_TONS, STR_NOTHING, glx@8375: STR_TONS, STR_NOTHING, STR_LITERS, STR_NOTHING glx@8375: }; peter1138@9151: maedhros@6658: /* 0xD0 and 0xDC stand for all the TextIDs in the range belugas@6674: * of 0xD000 (misc graphics texts) and 0xDC00 (misc persistent texts). maedhros@6658: * These strings are unique to each grf file, and thus require to be used with the maedhros@6658: * grfid in which they are declared */ peter1138@9151: switch (GB(str, 8, 8)) { peter1138@9151: case 0xD0: case 0xD1: case 0xD2: case 0xD3: peter1138@9151: case 0xDC: peter1138@9151: return GetGRFStringID(grfid, str); peter1138@9151: peter1138@9151: case 0xD4: case 0xD5: case 0xD6: case 0xD7: peter1138@9151: /* Strings embedded via 0x81 have 0x400 added to them (no real peter1138@9151: * explanation why...) */ peter1138@9151: return GetGRFStringID(grfid, str - 0x400); peter1138@9151: peter1138@9151: default: break; maedhros@6658: } peter1138@9151: glx@8098: #define TEXID_TO_STRINGID(begin, end, stringid) if (str >= begin && str <= end) return str + (stringid - begin) peter1138@6685: /* We have some changes in our cargo strings, resulting in some missing. */ glx@8098: TEXID_TO_STRINGID(0x000E, 0x002D, STR_000E); glx@8098: TEXID_TO_STRINGID(0x002E, 0x004D, STR_002E); glx@8375: if (str >= 0x004E && str <= 0x006D) str = units_volume[str - 0x004E]; glx@8098: TEXID_TO_STRINGID(0x006E, 0x008D, STR_QUANTITY_NOTHING); glx@8098: TEXID_TO_STRINGID(0x008E, 0x00AD, STR_ABBREV_NOTHING); peter1138@6685: maedhros@8646: /* Map building names according to our lang file changes. There are several maedhros@8646: * ranges of house ids, all of which need to be remapped to allow newgrfs maedhros@8646: * to use original house names. */ glx@8098: TEXID_TO_STRINGID(0x200F, 0x201F, STR_200F_TALL_OFFICE_BLOCK); maedhros@8646: TEXID_TO_STRINGID(0x2036, 0x2041, STR_2036_COTTAGES); maedhros@8646: TEXID_TO_STRINGID(0x2059, 0x205C, STR_2059_IGLOO); glx@8098: glx@8098: /* Same thing for industries, since the introduction of 4 new strings above STR_482A_PRODUCTION_LAST_MONTH */ glx@8098: TEXID_TO_STRINGID(0x482A, 0x483B, STR_482A_PRODUCTION_LAST_MONTH); glx@8098: #undef TEXTID_TO_STRINGID maedhros@6658: rubidium@8101: if (str == STR_NULL) return STR_EMPTY; rubidium@8101: maedhros@6658: return str; maedhros@6658: } maedhros@6658: peter1138@6701: static uint8 MapDOSColour(uint8 colour) peter1138@6701: { peter1138@6701: if (_use_dos_palette) return colour; peter1138@6701: peter1138@6701: if (colour < 10) { peter1138@6701: static uint8 dos_to_win_colour_map[] = { 0, 215, 216, 136, 88, 106, 32, 33, 40, 245 }; peter1138@6701: return dos_to_win_colour_map[colour]; peter1138@6701: } peter1138@6701: peter1138@6701: if (colour >= 245 && colour < 254) return colour - 28; peter1138@6701: peter1138@6701: return colour; peter1138@6701: } peter1138@6701: peter1138@10382: static std::map _grf_id_overrides; peter1138@10382: peter1138@10382: static void SetNewGRFOverride(uint32 source_grfid, uint32 target_grfid) peter1138@10382: { peter1138@10382: _grf_id_overrides[source_grfid] = target_grfid; peter1138@10382: grfmsg(5, "SetNewGRFOverride: Added override of 0x%X to 0x%X", BSWAP32(source_grfid), BSWAP32(target_grfid)); peter1138@10382: } peter1138@10382: peter1138@10382: static Engine *GetNewEngine(const GRFFile *file, VehicleType type, uint16 internal_id) peter1138@10382: { peter1138@10382: /* Hack for add-on GRFs that need to modify another GRF's engines. This lets peter1138@10382: * them use the same engine slots. */ peter1138@10382: const GRFFile *grf_match = NULL; rubidium@10775: if (_settings_game.vehicle.dynamic_engines) { peter1138@10382: uint32 override = _grf_id_overrides[file->grfid]; peter1138@10382: if (override != 0) { peter1138@10382: grf_match = GetFileByGRFID(override); peter1138@10382: if (grf_match == NULL) { peter1138@10382: grfmsg(5, "Tried mapping from GRFID %x to %x but target is not loaded", BSWAP32(file->grfid), BSWAP32(override)); peter1138@10382: } else { peter1138@10382: grfmsg(5, "Mapping from GRFID %x to %x", BSWAP32(file->grfid), BSWAP32(override)); peter1138@10382: } peter1138@10382: } peter1138@10382: } peter1138@10382: peter1138@10382: /* Check if this vehicle is already defined... */ peter1138@10382: Engine *e = NULL; peter1138@10382: FOR_ALL_ENGINES(e) { rubidium@10775: if (_settings_game.vehicle.dynamic_engines && e->grffile != NULL && e->grffile != file && e->grffile != grf_match) continue; peter1138@10382: if (e->type != type) continue; peter1138@10382: if (e->internal_id != internal_id) continue; peter1138@10382: peter1138@10382: if (e->grffile == NULL) { peter1138@10382: e->grffile = file; peter1138@10382: grfmsg(5, "Replaced engine at index %d for GRFID %x, type %d, index %d", e->index, BSWAP32(file->grfid), type, internal_id); peter1138@10382: } peter1138@10382: return e; peter1138@10382: } peter1138@10382: peter1138@10382: uint engine_pool_size = GetEnginePoolSize(); peter1138@10382: peter1138@10382: /* ... it's not, so create a new one based off an existing engine */ peter1138@10382: e = new Engine(type, internal_id); peter1138@10382: e->grffile = file; peter1138@10382: peter1138@10382: if (engine_pool_size != GetEnginePoolSize()) { peter1138@10382: /* Resize temporary engine data ... */ peter1138@10382: _gted = ReallocT(_gted, GetEnginePoolSize()); peter1138@10382: peter1138@10382: /* and blank the new block. */ peter1138@10382: size_t len = (GetEnginePoolSize() - engine_pool_size) * sizeof(*_gted); peter1138@10382: memset(_gted + engine_pool_size, 0, len); peter1138@10382: } peter1138@10382: peter1138@10382: grfmsg(5, "Created new engine at index %d for GRFID %x, type %d, index %d", e->index, BSWAP32(file->grfid), type, internal_id); peter1138@10382: peter1138@10382: return e; peter1138@10382: } peter1138@10382: peter1138@10390: EngineID GetNewEngineID(const GRFFile *file, VehicleType type, uint16 internal_id) peter1138@10390: { peter1138@10390: extern uint32 GetNewGRFOverride(uint32 grfid); peter1138@10390: peter1138@10390: const GRFFile *grf_match = NULL; rubidium@10775: if (_settings_game.vehicle.dynamic_engines) { peter1138@10390: uint32 override = _grf_id_overrides[file->grfid]; peter1138@10390: if (override != 0) grf_match = GetFileByGRFID(override); peter1138@10390: } peter1138@10390: peter1138@10390: const Engine *e = NULL; peter1138@10390: FOR_ALL_ENGINES(e) { rubidium@10775: if (_settings_game.vehicle.dynamic_engines && e->grffile != file && (grf_match == NULL || e->grffile != grf_match)) continue; peter1138@10390: if (e->type != type) continue; peter1138@10390: if (e->internal_id != internal_id) continue; peter1138@10390: peter1138@10390: return e->index; peter1138@10390: } peter1138@10390: peter1138@10390: return INVALID_ENGINE; peter1138@10390: } peter1138@10390: belugas@9073: /** Map the colour modifiers of TTDPatch to those that Open is using. belugas@9073: * @param grf_sprite pointer to the structure been modified belugas@9073: */ belugas@9073: static void MapSpriteMappingRecolour(PalSpriteID *grf_sprite) belugas@9073: { belugas@9073: if (HasBit(grf_sprite->pal, 14)) { belugas@9073: ClrBit(grf_sprite->pal, 14); belugas@9073: SetBit(grf_sprite->sprite, SPRITE_MODIFIER_OPAQUE); belugas@9073: } belugas@9073: belugas@9073: if (HasBit(grf_sprite->sprite, 14)) { belugas@9073: ClrBit(grf_sprite->sprite, 14); belugas@9073: SetBit(grf_sprite->sprite, PALETTE_MODIFIER_TRANSPARENT); belugas@9073: } belugas@9073: belugas@9073: if (HasBit(grf_sprite->sprite, 15)) { belugas@9073: ClrBit(grf_sprite->sprite, 15); belugas@9073: SetBit(grf_sprite->sprite, PALETTE_MODIFIER_COLOR); belugas@9073: } belugas@9073: } maedhros@6658: darkvater@360: typedef bool (*VCI_Handler)(uint engine, int numinfo, int prop, byte **buf, int len); truelight@0: darkvater@360: static bool RailVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len) truelight@0: { truelight@0: byte *buf = *bufp; darkvater@360: bool ret = false; truelight@0: maedhros@7655: for (int i = 0; i < numinfo; i++) { peter1138@10382: Engine *e = GetNewEngine(_cur_grffile, VEH_TRAIN, engine + i); peter1138@10382: EngineInfo *ei = &e->info; peter1138@10382: RailVehicleInfo *rvi = &e->u.rail; maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x05: { // Track type truelight@0: uint8 tracktype = grf_load_byte(&buf); truelight@0: peter1138@3026: switch (tracktype) { maedhros@7655: case 0: rvi->railtype = rvi->engclass >= 2 ? RAILTYPE_ELECTRIC : RAILTYPE_RAIL; break; maedhros@7655: case 1: rvi->railtype = RAILTYPE_MONO; break; maedhros@7655: case 2: rvi->railtype = RAILTYPE_MAGLEV; break; peter1138@3026: default: Darkvater@5568: grfmsg(1, "RailVehicleChangeInfo: Invalid track type %d specified, ignoring", tracktype); peter1138@3026: break; peter1138@3026: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x08: // AI passenger service peter1138@8945: /* Tells the AI that this engine is designed for maedhros@7658: * passenger services and shouldn't be used for freight. */ peter1138@8945: rvi->ai_passenger_only = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x09: { // Speed (1 unit is 1 kmh) truelight@0: uint16 speed = grf_load_word(&buf); tron@3033: if (speed == 0xFFFF) speed = 0; truelight@0: maedhros@7655: rvi->max_speed = speed; maedhros@7655: } break; maedhros@7655: peter1138@10530: case 0x0B: // Power peter1138@10530: rvi->power = grf_load_word(&buf); peter1138@10837: peter1138@10837: /* Set engine / wagon state based on power */ peter1138@10837: if (rvi->power != 0) { peter1138@10837: if (rvi->railveh_type == RAILVEH_WAGON) { peter1138@10837: rvi->railveh_type = RAILVEH_SINGLEHEAD; peter1138@10837: } peter1138@10837: } else { peter1138@10837: rvi->railveh_type = RAILVEH_WAGON; peter1138@10837: } peter1138@10530: break; peter1138@10530: peter1138@10530: case 0x0D: // Running cost factor peter1138@10530: rvi->running_cost = grf_load_byte(&buf); peter1138@10530: break; maedhros@7655: maedhros@7655: case 0x0E: { // Running cost base truelight@0: uint32 base = grf_load_dword(&buf); truelight@0: peter1138@9122: /* These magic numbers are used in GRFs to specify the base cost: peter1138@9122: * http://wiki.ttdpatch.net/tiki-index.php?page=BaseCosts peter1138@9122: */ peter1138@9122: if (base == 0) { peter1138@9122: rvi->running_cost_class = 0xFF; peter1138@9122: } else if (base < 0x4B34 || base > 0x4C54 || (base - 0x4B34) % 6 != 0) { peter1138@9122: grfmsg(1, "RailVehicleChangeInfo: Unsupported running cost base 0x%04X, ignoring", base); peter1138@9122: } else { peter1138@9122: /* Convert the magic number to an index into the price data */ peter1138@9122: rvi->running_cost_class = (base - 0x4B34) / 6; truelight@0: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x12: { // Sprite ID truelight@0: uint8 spriteid = grf_load_byte(&buf); truelight@0: peter1138@2895: /* TTD sprite IDs point to a location in a 16bit array, but we use it peter1138@2895: * as an array index, so we need it to be half the original value. */ tron@3033: if (spriteid < 0xFD) spriteid >>= 1; peter1138@2895: maedhros@7655: rvi->image_index = spriteid; maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x13: { // Dual-headed truelight@0: uint8 dual = grf_load_byte(&buf); truelight@0: celestar@372: if (dual != 0) { maedhros@7655: rvi->railveh_type = RAILVEH_MULTIHEAD; truelight@0: } else { maedhros@7655: rvi->railveh_type = rvi->power == 0 ? peter1138@6126: RAILVEH_WAGON : RAILVEH_SINGLEHEAD; truelight@0: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x14: // Cargo capacity maedhros@7655: rvi->capacity = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x15: { // Cargo type truelight@0: uint8 ctype = grf_load_byte(&buf); truelight@0: skidd13@8424: if (ctype < NUM_CARGO && HasBit(_cargo_mask, ctype)) { maedhros@7655: rvi->cargo_type = ctype; peter1138@2846: } else { maedhros@7655: rvi->cargo_type = CT_INVALID; peter1138@6468: grfmsg(2, "RailVehicleChangeInfo: Invalid cargo type %d, using first refittable", ctype); peter1138@2846: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x16: // Weight maedhros@7655: SB(rvi->weight, 0, 8, grf_load_byte(&buf)); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x17: // Cost factor maedhros@7655: rvi->base_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x18: // AI rank maedhros@7655: rvi->ai_rank = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x19: { // Engine traction type maedhros@7655: /* What do the individual numbers mean? maedhros@7655: * 0x00 .. 0x07: Steam maedhros@7655: * 0x08 .. 0x27: Diesel maedhros@7655: * 0x28 .. 0x31: Electric maedhros@7655: * 0x32 .. 0x37: Monorail maedhros@7655: * 0x38 .. 0x41: Maglev maedhros@7655: */ truelight@0: uint8 traction = grf_load_byte(&buf); rubidium@7077: EngineClass engclass; truelight@0: tron@3033: if (traction <= 0x07) { rubidium@7077: engclass = EC_STEAM; tron@3033: } else if (traction <= 0x27) { rubidium@7077: engclass = EC_DIESEL; celestar@3355: } else if (traction <= 0x31) { rubidium@7077: engclass = EC_ELECTRIC; rubidium@7081: } else if (traction <= 0x37) { rubidium@7081: engclass = EC_MONORAIL; tron@3033: } else if (traction <= 0x41) { rubidium@7081: engclass = EC_MAGLEV; tron@3033: } else { truelight@0: break; tron@3033: } maedhros@7655: if (rvi->railtype == RAILTYPE_RAIL && engclass >= EC_ELECTRIC) rvi->railtype = RAILTYPE_ELECTRIC; maedhros@7655: if (rvi->railtype == RAILTYPE_ELECTRIC && engclass < EC_ELECTRIC) rvi->railtype = RAILTYPE_RAIL; maedhros@7655: maedhros@7655: rvi->engclass = engclass; maedhros@7655: } break; maedhros@7655: peter1138@10382: case 0x1A: // Alter purchase list sort order peter1138@10382: AlterRailVehListOrder(e->index, grf_load_byte(&buf)); peter1138@10382: break; maedhros@7655: maedhros@7655: case 0x1B: // Powered wagons power bonus maedhros@7655: rvi->pow_wag_power = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1C: // Refit cost maedhros@7655: ei->refit_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1D: // Refit cargo maedhros@7655: ei->refit_mask = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1E: // Callback maedhros@7655: ei->callbackmask = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1F: // Tractive effort coefficient maedhros@7655: rvi->tractive_effort = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: glx@8287: case 0x20: // Air drag glx@8287: /** @todo Air drag for trains. */ glx@8287: grf_load_byte(&buf); glx@8287: ret = true; glx@8287: break; glx@8287: maedhros@7655: case 0x21: // Shorter vehicle maedhros@7655: rvi->shorten_factor = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x22: // Visual effect maedhros@7658: /** @see note in engine.h about rvi->visual_effect */ maedhros@7655: rvi->visual_effect = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x23: // Powered wagons weight bonus maedhros@7655: rvi->pow_wag_weight = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x24: { // High byte of vehicle weight peter1138@2542: byte weight = grf_load_byte(&buf); peter1138@2542: peter1138@2545: if (weight > 4) { Darkvater@5568: grfmsg(2, "RailVehicleChangeInfo: Nonsensical weight of %d tons, ignoring", weight << 8); peter1138@2542: } else { maedhros@7655: SB(rvi->weight, 8, 8, weight); peter1138@2542: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x25: // User-defined bit mask to set when checking veh. var. 42 maedhros@7655: rvi->user_def_data = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x26: // Retire vehicle early maedhros@7655: ei->retire_early = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x27: // Miscellaneous flags maedhros@7655: ei->misc_flags = grf_load_byte(&buf); skidd13@8424: _loaded_newgrf_features.has_2CC |= HasBit(ei->misc_flags, EF_USES_2CC); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x28: // Cargo classes allowed peter1138@10382: _gted[e->index].cargo_allowed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x29: // Cargo classes disallowed peter1138@10382: _gted[e->index].cargo_disallowed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x2A: // Long format introduction date (days since year 0) maedhros@7655: ei->base_intro = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } truelight@0: } maedhros@7655: truelight@0: *bufp = buf; truelight@0: return ret; truelight@0: } truelight@0: celestar@378: static bool RoadVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len) celestar@378: { celestar@378: byte *buf = *bufp; celestar@378: bool ret = false; celestar@378: maedhros@7655: for (int i = 0; i < numinfo; i++) { peter1138@10382: Engine *e = GetNewEngine(_cur_grffile, VEH_ROAD, engine + i); peter1138@10382: EngineInfo *ei = &e->info; peter1138@10382: RoadVehicleInfo *rvi = &e->u.road; maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x08: // Speed (1 unit is 0.5 kmh) maedhros@7655: rvi->max_speed = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x09: // Running cost factor maedhros@7655: rvi->running_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: peter1138@9122: case 0x0A: { // Running cost base peter1138@9122: uint32 base= grf_load_dword(&buf); peter1138@9122: peter1138@9122: /* These magic numbers are used in GRFs to specify the base cost: peter1138@9122: * http://wiki.ttdpatch.net/tiki-index.php?page=BaseCosts peter1138@9122: */ peter1138@9122: if (base == 0) { peter1138@9122: rvi->running_cost_class = 0xFF; peter1138@9122: } else if (base < 0x4B34 || base > 0x4C54 || (base - 0x4B34) % 6 != 0) { peter1138@9122: grfmsg(1, "RailVehicleChangeInfo: Unsupported running cost base 0x%04X, ignoring", base); peter1138@9122: } else { peter1138@9122: /* Convert the magic number to an index into the price data */ peter1138@9122: rvi->running_cost_class = (base - 0x4B34) / 6; peter1138@9122: } peter1138@9122: peter1138@9122: break; peter1138@9122: } maedhros@7655: maedhros@7655: case 0x0E: { // Sprite ID celestar@378: uint8 spriteid = grf_load_byte(&buf); celestar@378: belugas@6674: /* cars have different custom id in the GRF file */ tron@3033: if (spriteid == 0xFF) spriteid = 0xFD; tron@3033: tron@3033: if (spriteid < 0xFD) spriteid >>= 1; peter1138@2895: maedhros@7655: rvi->image_index = spriteid; maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0F: // Cargo capacity maedhros@7655: rvi->capacity = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x10: { // Cargo type celestar@378: uint8 cargo = grf_load_byte(&buf); celestar@378: skidd13@8424: if (cargo < NUM_CARGO && HasBit(_cargo_mask, cargo)) { maedhros@7655: rvi->cargo_type = cargo; peter1138@2846: } else { maedhros@7655: rvi->cargo_type = CT_INVALID; peter1138@6468: grfmsg(2, "RoadVehicleChangeInfo: Invalid cargo type %d, using first refittable", cargo); peter1138@2846: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x11: // Cost factor maedhros@7655: rvi->base_cost = grf_load_byte(&buf); // ?? is it base_cost? maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x12: // SFX maedhros@7655: rvi->sfx = (SoundFx)grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x13: // Power in 10hp maedhros@7655: case 0x14: // Weight in 1/4 tons maedhros@7655: case 0x15: // Speed in mph*0.8 maedhros@7658: /** @todo Support for road vehicles realistic power maedhros@7655: * computations (called rvpower in TTDPatch) is just maedhros@7655: * missing in OTTD yet. --pasky */ maedhros@7655: grf_load_byte(&buf); maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x16: // Cargos available for refitting maedhros@7655: ei->refit_mask = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x17: // Callback mask maedhros@7655: ei->callbackmask = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: glx@8287: case 0x18: // Tractive effort glx@8287: case 0x19: // Air drag glx@8287: /** @todo Tractive effort and air drag for road vehicles. */ glx@8287: grf_load_byte(&buf); glx@8287: ret = true; glx@8287: break; glx@8287: maedhros@7655: case 0x1A: // Refit cost maedhros@7655: ei->refit_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1B: // Retire vehicle early maedhros@7655: ei->retire_early = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1C: // Miscellaneous flags maedhros@7655: ei->misc_flags = grf_load_byte(&buf); skidd13@8424: _loaded_newgrf_features.has_2CC |= HasBit(ei->misc_flags, EF_USES_2CC); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1D: // Cargo classes allowed peter1138@10382: _gted[e->index].cargo_allowed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1E: // Cargo classes disallowed peter1138@10382: _gted[e->index].cargo_disallowed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1F: // Long format introduction date (days since year 0) maedhros@7655: ei->base_intro = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } celestar@378: } celestar@378: celestar@378: *bufp = buf; celestar@378: return ret; celestar@378: } celestar@378: darkvater@360: static bool ShipVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len) truelight@0: { truelight@0: byte *buf = *bufp; darkvater@360: bool ret = false; truelight@0: maedhros@7655: for (int i = 0; i < numinfo; i++) { peter1138@10382: Engine *e = GetNewEngine(_cur_grffile, VEH_SHIP, engine + i); peter1138@10382: EngineInfo *ei = &e->info; peter1138@10382: ShipVehicleInfo *svi = &e->u.ship; maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x08: { // Sprite ID truelight@0: uint8 spriteid = grf_load_byte(&buf); truelight@0: belugas@6674: /* ships have different custom id in the GRF file */ tron@3033: if (spriteid == 0xFF) spriteid = 0xFD; tron@3033: tron@3033: if (spriteid < 0xFD) spriteid >>= 1; peter1138@2895: maedhros@7655: svi->image_index = spriteid; maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x09: // Refittable maedhros@7655: svi->refittable = (grf_load_byte(&buf) != 0); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0A: // Cost factor maedhros@7655: svi->base_cost = grf_load_byte(&buf); // ?? is it base_cost? maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0B: // Speed (1 unit is 0.5 kmh) maedhros@7655: svi->max_speed = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0C: { // Cargo type truelight@0: uint8 cargo = grf_load_byte(&buf); truelight@0: skidd13@8424: if (cargo < NUM_CARGO && HasBit(_cargo_mask, cargo)) { maedhros@7655: svi->cargo_type = cargo; peter1138@2846: } else { maedhros@7655: svi->cargo_type = CT_INVALID; peter1138@6468: grfmsg(2, "ShipVehicleChangeInfo: Invalid cargo type %d, using first refittable", cargo); peter1138@2846: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0D: // Cargo capacity maedhros@7655: svi->capacity = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0F: // Running cost factor maedhros@7655: svi->running_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x10: // SFX maedhros@7655: svi->sfx = (SoundFx)grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x11: // Cargos available for refitting maedhros@7655: ei->refit_mask = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x12: // Callback mask maedhros@7655: ei->callbackmask = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x13: // Refit cost maedhros@7655: ei->refit_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: glx@8287: case 0x14: // Ocean speed fraction glx@8287: case 0x15: // Canal speed fraction glx@8287: /** @todo Speed fractions for ships on oceans and canals */ glx@8287: grf_load_byte(&buf); glx@8287: ret = true; glx@8287: break; glx@8287: maedhros@7655: case 0x16: // Retire vehicle early maedhros@7655: ei->retire_early = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x17: // Miscellaneous flags maedhros@7655: ei->misc_flags = grf_load_byte(&buf); skidd13@8424: _loaded_newgrf_features.has_2CC |= HasBit(ei->misc_flags, EF_USES_2CC); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x18: // Cargo classes allowed peter1138@10382: _gted[e->index].cargo_allowed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x19: // Cargo classes disallowed peter1138@10382: _gted[e->index].cargo_disallowed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1A: // Long format introduction date (days since year 0) maedhros@7655: ei->base_intro = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } truelight@0: } truelight@0: truelight@0: *bufp = buf; truelight@0: return ret; truelight@0: } truelight@0: darkvater@381: static bool AircraftVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len) darkvater@381: { darkvater@381: byte *buf = *bufp; darkvater@381: bool ret = false; darkvater@381: maedhros@7655: for (int i = 0; i < numinfo; i++) { peter1138@10382: Engine *e = GetNewEngine(_cur_grffile, VEH_AIRCRAFT, engine + i); peter1138@10382: EngineInfo *ei = &e->info; peter1138@10382: AircraftVehicleInfo *avi = &e->u.air; maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x08: { // Sprite ID darkvater@381: uint8 spriteid = grf_load_byte(&buf); darkvater@381: belugas@6674: /* aircraft have different custom id in the GRF file */ tron@3033: if (spriteid == 0xFF) spriteid = 0xFD; tron@3033: tron@3033: if (spriteid < 0xFD) spriteid >>= 1; peter1138@2895: maedhros@7655: avi->image_index = spriteid; maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x09: // Helicopter peter1138@4926: if (grf_load_byte(&buf) == 0) { maedhros@7655: avi->subtype = AIR_HELI; peter1138@4926: } else { maedhros@7655: SB(avi->subtype, 0, 1, 1); // AIR_CTOL peter1138@4926: } maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0A: // Large maedhros@7655: SB(avi->subtype, 1, 1, (grf_load_byte(&buf) != 0 ? 1 : 0)); // AIR_FAST maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0B: // Cost factor maedhros@7655: avi->base_cost = grf_load_byte(&buf); // ?? is it base_cost? maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0C: // Speed (1 unit is 8 mph, we translate to 1 unit is 1 km/h) maedhros@7655: avi->max_speed = (grf_load_byte(&buf) * 129) / 10; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0D: // Acceleration maedhros@7655: avi->acceleration = (grf_load_byte(&buf) * 129) / 10; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0E: // Running cost factor maedhros@7655: avi->running_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0F: // Passenger capacity maedhros@7655: avi->passenger_capacity = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x11: // Mail capacity maedhros@7655: avi->mail_capacity = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x12: // SFX maedhros@7655: avi->sfx = (SoundFx)grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x13: // Cargos available for refitting maedhros@7655: ei->refit_mask = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x14: // Callback mask maedhros@7655: ei->callbackmask = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x15: // Refit cost maedhros@7655: ei->refit_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x16: // Retire vehicle early maedhros@7655: ei->retire_early = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x17: // Miscellaneous flags maedhros@7655: ei->misc_flags = grf_load_byte(&buf); skidd13@8424: _loaded_newgrf_features.has_2CC |= HasBit(ei->misc_flags, EF_USES_2CC); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x18: // Cargo classes allowed peter1138@10382: _gted[e->index].cargo_allowed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x19: // Cargo classes disallowed peter1138@10382: _gted[e->index].cargo_disallowed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1A: // Long format introduction date (days since year 0) maedhros@7655: ei->base_intro = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } darkvater@381: } darkvater@381: darkvater@381: *bufp = buf; darkvater@381: return ret; darkvater@381: } darkvater@381: celestar@389: static bool StationChangeInfo(uint stid, int numinfo, int prop, byte **bufp, int len) celestar@389: { celestar@389: byte *buf = *bufp; peter1138@3505: bool ret = false; celestar@389: peter1138@3768: if (stid + numinfo > MAX_STATIONS) { Darkvater@5568: grfmsg(1, "StationChangeInfo: Station %u is invalid, max %u, ignoring", stid + numinfo, MAX_STATIONS); peter1138@3768: return false; peter1138@3768: } peter1138@3768: peter1138@3501: /* Allocate station specs if necessary */ KUDr@5860: if (_cur_grffile->stations == NULL) _cur_grffile->stations = CallocT(MAX_STATIONS); peter1138@3768: maedhros@7655: for (int i = 0; i < numinfo; i++) { maedhros@7655: StationSpec *statspec = _cur_grffile->stations[stid + i]; maedhros@7655: maedhros@7655: /* Check that the station we are modifying is defined. */ maedhros@7655: if (statspec == NULL && prop != 0x08) { maedhros@7655: grfmsg(2, "StationChangeInfo: Attempt to modify undefined station %u, ignoring", stid + i); maedhros@7655: continue; peter1138@3501: } maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x08: { // Class ID maedhros@7655: StationSpec **spec = &_cur_grffile->stations[stid + i]; maedhros@7655: peter1138@3768: /* Property 0x08 is special; it is where the station is allocated */ maedhros@7655: if (*spec == NULL) *spec = CallocT(1); peter1138@3768: Darkvater@3711: /* Swap classid because we read it in BE meaning WAYP or DFLT */ peter1138@6607: uint32 classid = grf_load_dword(&buf); maedhros@7655: (*spec)->sclass = AllocateStationClass(BSWAP32(classid)); maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x09: // Define sprite layout belugas@3676: statspec->tiles = grf_load_extended(&buf); KUDr@5860: statspec->renderdata = CallocT(statspec->tiles); peter1138@3740: statspec->copied_renderdata = false; peter1138@3740: peter1138@6607: for (uint t = 0; t < statspec->tiles; t++) { belugas@3676: DrawTileSprites *dts = &statspec->renderdata[t]; peter1138@3570: uint seq_count = 0; peter1138@2625: peter1138@3737: dts->seq = NULL; frosch@9067: dts->ground.sprite = grf_load_word(&buf); frosch@9067: dts->ground.pal = grf_load_word(&buf); frosch@9067: if (dts->ground.sprite == 0) continue; frosch@9067: if (HasBit(dts->ground.pal, 15)) { frosch@9067: ClrBit(dts->ground.pal, 15); frosch@9067: SetBit(dts->ground.sprite, SPRITE_MODIFIER_USE_OFFSET); peter1138@5919: } belugas@9073: belugas@9073: MapSpriteMappingRecolour(&dts->ground); peter1138@2625: celestar@389: while (buf < *bufp + len) { celestar@389: DrawTileSeqStruct *dtss; celestar@389: belugas@6674: /* no relative bounding box support */ KUDr@5860: dts->seq = ReallocT((DrawTileSeqStruct*)dts->seq, ++seq_count); celestar@389: dtss = (DrawTileSeqStruct*) &dts->seq[seq_count - 1]; celestar@389: celestar@389: dtss->delta_x = grf_load_byte(&buf); celestar@389: if ((byte) dtss->delta_x == 0x80) break; celestar@389: dtss->delta_y = grf_load_byte(&buf); celestar@389: dtss->delta_z = grf_load_byte(&buf); tron@4230: dtss->size_x = grf_load_byte(&buf); tron@4230: dtss->size_y = grf_load_byte(&buf); tron@4230: dtss->size_z = grf_load_byte(&buf); frosch@9066: dtss->image.sprite = grf_load_word(&buf); frosch@9066: dtss->image.pal = grf_load_word(&buf); peter1138@3771: peter1138@3775: /* Remap flags as ours collide */ frosch@9066: if (HasBit(dtss->image.pal, 15)) { frosch@9066: ClrBit(dtss->image.pal, 15); frosch@9066: SetBit(dtss->image.sprite, SPRITE_MODIFIER_USE_OFFSET); peter1138@8654: } belugas@9073: belugas@9073: MapSpriteMappingRecolour(&dtss->image); celestar@389: } celestar@389: } maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0A: { // Copy sprite layout darkvater@398: byte srcid = grf_load_byte(&buf); peter1138@3768: const StationSpec *srcstatspec = _cur_grffile->stations[srcid]; darkvater@398: belugas@3676: statspec->tiles = srcstatspec->tiles; peter1138@3738: statspec->renderdata = srcstatspec->renderdata; peter1138@3740: statspec->copied_renderdata = true; maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0B: // Callback mask maedhros@7655: statspec->callbackmask = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0C: // Disallowed number of platforms maedhros@7655: statspec->disallowed_platforms = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0D: // Disallowed platform lengths maedhros@7655: statspec->disallowed_lengths = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0E: // Define custom layout peter1138@5060: statspec->copied_layouts = false; peter1138@5060: celestar@389: while (buf < *bufp + len) { celestar@389: byte length = grf_load_byte(&buf); celestar@389: byte number = grf_load_byte(&buf); tron@449: StationLayout layout; peter1138@3570: uint l, p; celestar@389: tron@449: if (length == 0 || number == 0) break; tron@449: tron@449: //debug("l %d > %d ?", length, stat->lengths); belugas@3676: if (length > statspec->lengths) { KUDr@5860: statspec->platforms = ReallocT(statspec->platforms, length); belugas@3676: memset(statspec->platforms + statspec->lengths, 0, length - statspec->lengths); belugas@3676: KUDr@5860: statspec->layouts = ReallocT(statspec->layouts, length); belugas@3676: memset(statspec->layouts + statspec->lengths, 0, belugas@3676: (length - statspec->lengths) * sizeof(*statspec->layouts)); belugas@3676: belugas@3676: statspec->lengths = length; tron@449: } tron@449: l = length - 1; // index is zero-based tron@449: tron@449: //debug("p %d > %d ?", number, stat->platforms[l]); belugas@3676: if (number > statspec->platforms[l]) { KUDr@5860: statspec->layouts[l] = ReallocT(statspec->layouts[l], number); belugas@6674: /* We expect NULL being 0 here, but C99 guarantees that. */ belugas@3676: memset(statspec->layouts[l] + statspec->platforms[l], 0, belugas@3676: (number - statspec->platforms[l]) * sizeof(**statspec->layouts)); belugas@3676: belugas@3676: statspec->platforms[l] = number; tron@449: } tron@449: truelight@542: p = 0; KUDr@5860: layout = MallocT(length * number); tron@3033: for (l = 0; l < length; l++) { tron@3033: for (p = 0; p < number; p++) { tron@449: layout[l * number + p] = grf_load_byte(&buf); tron@3033: } tron@3033: } tron@449: tron@449: l--; tron@449: p--; belugas@3676: free(statspec->layouts[l][p]); belugas@3676: statspec->layouts[l][p] = layout; celestar@389: } maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0F: { // Copy custom layout peter1138@5060: byte srcid = grf_load_byte(&buf); peter1138@5060: const StationSpec *srcstatspec = _cur_grffile->stations[srcid]; peter1138@5060: peter1138@5060: statspec->lengths = srcstatspec->lengths; peter1138@5060: statspec->platforms = srcstatspec->platforms; peter1138@5060: statspec->layouts = srcstatspec->layouts; peter1138@5060: statspec->copied_layouts = true; maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x10: // Little/lots cargo threshold maedhros@7655: statspec->cargo_threshold = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x11: // Pylon placement maedhros@7655: statspec->pylons = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x12: // Cargo types for random triggers maedhros@7655: statspec->cargo_triggers = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x13: // General flags maedhros@7655: statspec->flags = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x14: // Overhead wire placement maedhros@7655: statspec->wires = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x15: // Blocked tiles maedhros@7655: statspec->blocked = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: peter1138@10266: case 0x16: // Animation info peter1138@10266: statspec->anim_frames = grf_load_byte(&buf); peter1138@10266: statspec->anim_status = grf_load_byte(&buf); peter1138@10266: break; peter1138@10266: peter1138@10266: case 0x17: // Animation speed peter1138@10266: statspec->anim_speed = grf_load_byte(&buf); peter1138@10266: break; peter1138@10266: peter1138@10266: case 0x18: // Animation triggers peter1138@10266: statspec->anim_triggers = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } celestar@389: } celestar@389: celestar@389: *bufp = buf; celestar@389: return ret; celestar@389: } celestar@389: peter1138@8868: static bool CanalChangeInfo(uint id, int numinfo, int prop, byte **bufp, int len) peter1138@8868: { peter1138@8868: byte *buf = *bufp; peter1138@8868: bool ret = false; peter1138@8868: peter1138@8868: if (id + numinfo > CF_END) { peter1138@8868: grfmsg(1, "CanalChangeInfo: Canal feature %u is invalid, max %u, ignoreing", id + numinfo, CF_END); peter1138@8868: return false; peter1138@8868: } peter1138@8868: peter1138@8868: for (int i = 0; i < numinfo; i++) { peter1138@8868: WaterFeature *wf = &_water_feature[id + i]; peter1138@8868: peter1138@8868: switch (prop) { peter1138@8868: case 0x08: peter1138@8868: wf->callbackmask = grf_load_byte(&buf); peter1138@8868: break; peter1138@8868: peter1138@8868: case 0x09: peter1138@8868: wf->flags = grf_load_byte(&buf); peter1138@8868: break; peter1138@8868: peter1138@8868: default: peter1138@8868: ret = true; peter1138@8868: break; peter1138@8868: } peter1138@8868: } peter1138@8868: peter1138@8868: *bufp = buf; peter1138@8868: return ret; peter1138@8868: } peter1138@8868: peter1138@2478: static bool BridgeChangeInfo(uint brid, int numinfo, int prop, byte **bufp, int len) peter1138@2478: { peter1138@2478: byte *buf = *bufp; peter1138@3504: bool ret = false; peter1138@2478: maedhros@7655: if (brid + numinfo > MAX_BRIDGES) { maedhros@7655: grfmsg(1, "BridgeChangeInfo: Bridge %u is invalid, max %u, ignoring", brid + numinfo, MAX_BRIDGES); maedhros@7655: return false; maedhros@7655: } maedhros@7655: maedhros@7655: for (int i = 0; i < numinfo; i++) { belugas@9031: BridgeSpec *bridge = &_bridge[brid + i]; maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x08: // Year of availability maedhros@7655: bridge->avail_year = ORIGINAL_BASE_YEAR + grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x09: // Minimum length maedhros@7655: bridge->min_length = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0A: // Maximum length maedhros@7655: bridge->max_length = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0B: // Cost factor maedhros@7655: bridge->price = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0C: // Maximum speed maedhros@7655: bridge->speed = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0D: { // Bridge sprite tables peter1138@2478: byte tableid = grf_load_byte(&buf); peter1138@2478: byte numtables = grf_load_byte(&buf); peter1138@2478: peter1138@2478: if (bridge->sprite_table == NULL) { Darkvater@3555: /* Allocate memory for sprite table pointers and zero out */ KUDr@5860: bridge->sprite_table = CallocT(7); peter1138@2478: } peter1138@2478: Darkvater@3555: for (; numtables-- != 0; tableid++) { Darkvater@3555: if (tableid >= 7) { // skip invalid data Darkvater@5568: grfmsg(1, "BridgeChangeInfo: Table %d >= 7, skipping", tableid); peter1138@6607: for (byte sprite = 0; sprite < 32; sprite++) grf_load_dword(&buf); Darkvater@3555: continue; peter1138@2478: } Darkvater@3555: Darkvater@3555: if (bridge->sprite_table[tableid] == NULL) { KUDr@5860: bridge->sprite_table[tableid] = MallocT(32); Darkvater@3555: } Darkvater@3555: peter1138@6607: for (byte sprite = 0; sprite < 32; sprite++) { peter1138@5919: SpriteID image = grf_load_word(&buf); peter1138@5919: SpriteID pal = grf_load_word(&buf); peter1138@5919: peter1138@5919: bridge->sprite_table[tableid][sprite].sprite = image; peter1138@5919: bridge->sprite_table[tableid][sprite].pal = pal; belugas@9073: belugas@9073: MapSpriteMappingRecolour(&bridge->sprite_table[tableid][sprite]); peter1138@5919: } peter1138@2478: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0E: // Flags; bit 0 - disable far pillars maedhros@7655: bridge->flags = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0F: // Long format year of availability (year since year 0) skidd13@8418: bridge->avail_year = Clamp(grf_load_dword(&buf), MIN_YEAR, MAX_YEAR); maedhros@7655: break; maedhros@7655: belugas@8990: case 0x10: { // purchase string belugas@8990: StringID newone = GetGRFStringID(_cur_grffile->grfid, grf_load_word(&buf)); belugas@8990: if (newone != STR_UNDEFINED) bridge->material = newone; belugas@8990: } break; belugas@8990: belugas@9036: case 0x11: // description of bridge with rails or roads belugas@9036: case 0x12: { belugas@8990: StringID newone = GetGRFStringID(_cur_grffile->grfid, grf_load_word(&buf)); belugas@9036: if (newone != STR_UNDEFINED) bridge->transport_name[prop - 0x11] = newone; belugas@8990: } break; belugas@8990: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } peter1138@2478: } peter1138@2478: peter1138@2478: *bufp = buf; peter1138@2478: return ret; peter1138@2478: } celestar@389: maedhros@6658: static bool TownHouseChangeInfo(uint hid, int numinfo, int prop, byte **bufp, int len) maedhros@6658: { maedhros@6658: byte *buf = *bufp; maedhros@6658: bool ret = false; maedhros@6658: maedhros@7657: if (hid + numinfo > HOUSE_MAX) { maedhros@7657: grfmsg(1, "TownHouseChangeInfo: Too many houses loaded (%u), max (%u). Ignoring.", hid + numinfo, HOUSE_MAX); maedhros@6658: return false; maedhros@6658: } maedhros@6658: maedhros@6658: /* Allocate house specs if they haven't been allocated already. */ maedhros@6658: if (_cur_grffile->housespec == NULL) { maedhros@6658: _cur_grffile->housespec = CallocT(HOUSE_MAX); maedhros@6658: } maedhros@6658: maedhros@7655: for (int i = 0; i < numinfo; i++) { maedhros@7655: HouseSpec *housespec = _cur_grffile->housespec[hid + i]; maedhros@7655: maedhros@7655: if (prop != 0x08 && housespec == NULL) { maedhros@7655: grfmsg(2, "TownHouseChangeInfo: Attempt to modify undefined house %u. Ignoring.", hid + i); maedhros@7655: continue; maedhros@6658: } maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x08: { // Substitute building type, and definition of a new house maedhros@7655: HouseSpec **house = &_cur_grffile->housespec[hid + i]; maedhros@6658: byte subs_id = grf_load_byte(&buf); maedhros@6658: maedhros@6658: if (subs_id == 0xFF) { maedhros@6658: /* Instead of defining a new house, a substitute house id maedhros@6658: * of 0xFF disables the old house with the current id. */ maedhros@6658: _house_specs[hid + i].enabled = false; maedhros@6658: continue; maedhros@6658: } else if (subs_id >= NEW_HOUSE_OFFSET) { maedhros@6658: /* The substitute id must be one of the original houses. */ maedhros@6658: grfmsg(2, "TownHouseChangeInfo: Attempt to use new house %u as substitute house for %u. Ignoring.", subs_id, hid + i); maedhros@6658: return false; maedhros@6658: } maedhros@6658: maedhros@6658: /* Allocate space for this house. */ maedhros@7655: if (*house == NULL) *house = CallocT(1); maedhros@7655: maedhros@7655: housespec = *house; maedhros@7655: maedhros@7655: memcpy(housespec, &_house_specs[subs_id], sizeof(_house_specs[subs_id])); maedhros@7655: maedhros@7655: housespec->enabled = true; maedhros@7655: housespec->local_id = hid + i; maedhros@7655: housespec->substitute_id = subs_id; maedhros@7655: housespec->grffile = _cur_grffile; maedhros@7655: housespec->random_colour[0] = 0x04; // those 4 random colours are the base colour maedhros@7655: housespec->random_colour[1] = 0x08; // for all new houses maedhros@7655: housespec->random_colour[2] = 0x0C; // they stand for red, blue, orange and green maedhros@7655: housespec->random_colour[3] = 0x06; maedhros@6658: maedhros@7927: /* Make sure that the third cargo type is valid in this maedhros@7927: * climate. This can cause problems when copying the properties maedhros@7927: * of a house that accepts food, where the new house is valid maedhros@7927: * in the temperate climate. */ maedhros@7927: if (!GetCargo(housespec->accepts_cargo[2])->IsValid()) { maedhros@7927: housespec->cargo_acceptance[2] = 0; maedhros@7927: } maedhros@7927: maedhros@7658: /** maedhros@7658: * New houses do not (currently) expect to have a default start maedhros@7658: * date before 1930, as this breaks the build date stuff. maedhros@7658: * @see FinaliseHouseArray() for more details. maedhros@7658: */ belugas@10817: if (housespec->min_year < 1930) housespec->min_year = 1930; maedhros@7655: maedhros@7655: _loaded_newgrf_features.has_newhouses = true; maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x09: // Building flags maedhros@7655: housespec->building_flags = (BuildingFlags)grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0A: { // Availability years maedhros@6658: uint16 years = grf_load_word(&buf); belugas@10817: housespec->min_year = GB(years, 0, 8) > 150 ? MAX_YEAR : ORIGINAL_BASE_YEAR + GB(years, 0, 8); belugas@10817: housespec->max_year = GB(years, 8, 8) > 150 ? MAX_YEAR : ORIGINAL_BASE_YEAR + GB(years, 8, 8); maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0B: // Population maedhros@7655: housespec->population = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0C: // Mail generation multiplier maedhros@7655: housespec->mail_generation = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0D: // Passenger acceptance maedhros@7655: case 0x0E: // Mail acceptance maedhros@7655: housespec->cargo_acceptance[prop - 0x0D] = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0F: { // Goods/candy, food/fizzy drinks acceptance maedhros@6658: int8 goods = grf_load_byte(&buf); belugas@6667: belugas@6667: /* If value of goods is negative, it means in fact food or, if in toyland, fizzy_drink acceptance. belugas@6667: * Else, we have "standard" 3rd cargo type, goods or candy, for toyland once more */ rubidium@10775: CargoID cid = (goods >= 0) ? ((_settings_game.game_creation.landscape == LT_TOYLAND) ? CT_CANDY : CT_GOODS) : rubidium@10775: ((_settings_game.game_creation.landscape == LT_TOYLAND) ? CT_FIZZY_DRINKS : CT_FOOD); belugas@6667: maedhros@7927: /* Make sure the cargo type is valid in this climate. */ maedhros@7927: if (!GetCargo(cid)->IsValid()) goods = 0; maedhros@7927: maedhros@7927: housespec->accepts_cargo[2] = cid; maedhros@7655: housespec->cargo_acceptance[2] = abs(goods); // but we do need positive value here maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x10: // Local authority rating decrease on removal maedhros@7655: housespec->remove_rating_decrease = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x11: // Removal cost multiplier maedhros@7655: housespec->removal_cost = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x12: // Building name ID rubidium@10219: housespec->building_name = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&housespec->building_name] = _cur_grffile->grfid; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x13: // Building availability mask maedhros@7655: housespec->building_availability = (HouseZones)grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x14: // House callback flags maedhros@7655: housespec->callback_mask = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x15: { // House override byte maedhros@6658: byte override = grf_load_byte(&buf); maedhros@6658: maedhros@6658: /* The house being overridden must be an original house. */ maedhros@6658: if (override >= NEW_HOUSE_OFFSET) { maedhros@7656: grfmsg(2, "TownHouseChangeInfo: Attempt to override new house %u with house id %u. Ignoring.", override, hid + i); maedhros@7655: continue; maedhros@6658: } maedhros@6658: glx@8369: _house_mngr.Add(hid + i, _cur_grffile->grfid, override); maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x16: // Periodic refresh multiplier maedhros@7655: housespec->processing_time = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x17: // Four random colours to use maedhros@7655: for (uint j = 0; j < 4; j++) housespec->random_colour[j] = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x18: // Relative probability of appearing maedhros@7655: housespec->probability = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x19: // Extra flags maedhros@7655: housespec->extra_flags = (HouseExtraFlags)grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1A: // Animation frames maedhros@7655: housespec->animation_frames = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1B: // Animation speed skidd13@8418: housespec->animation_speed = Clamp(grf_load_byte(&buf), 2, 16); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1C: // Class of the building type maedhros@7655: housespec->class_id = AllocateHouseClassID(grf_load_byte(&buf), _cur_grffile->grfid); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1D: // Callback flags 2 maedhros@7655: housespec->callback_mask |= (grf_load_byte(&buf) << 8); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1E: { // Accepted cargo types peter1138@6957: uint32 cargotypes = grf_load_dword(&buf); peter1138@6957: peter1138@6957: /* Check if the cargo types should not be changed */ peter1138@6957: if (cargotypes == 0xFFFFFFFF) break; peter1138@6957: peter1138@6957: for (uint j = 0; j < 3; j++) { peter1138@6957: /* Get the cargo number from the 'list' */ peter1138@6957: uint8 cargo_part = GB(cargotypes, 8 * j, 8); peter1138@6957: CargoID cargo = GetCargoTranslation(cargo_part, _cur_grffile); peter1138@6957: peter1138@6957: if (cargo == CT_INVALID) { peter1138@6957: /* Disable acceptance of invalid cargo type */ maedhros@7655: housespec->cargo_acceptance[j] = 0; peter1138@6957: } else { maedhros@7655: housespec->accepts_cargo[j] = cargo; peter1138@6957: } peter1138@6957: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x1F: // Minimum life span maedhros@7655: housespec->minimum_life = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: glx@8287: case 0x20: { // @todo Cargo acceptance watch list glx@8287: byte count = grf_load_byte(&buf); glx@8287: for (byte j = 0; j < count; j++) grf_load_byte(&buf); glx@8287: ret = true; glx@8287: } break; glx@8287: belugas@10886: case 0x21: // long introduction year belugas@10886: housespec->min_year = grf_load_word(&buf); belugas@10886: break; belugas@10886: belugas@10886: case 0x22: // long maximum year belugas@10886: housespec->max_year = grf_load_word(&buf); belugas@10886: break; belugas@10886: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } maedhros@6658: } maedhros@6658: maedhros@6658: *bufp = buf; maedhros@6658: return ret; maedhros@6658: } maedhros@6658: peter1138@2506: static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, int len) peter1138@2506: { peter1138@2506: byte *buf = *bufp; peter1138@2506: bool ret = false; peter1138@2506: maedhros@7655: for (int i = 0; i < numinfo; i++) { maedhros@7655: switch (prop) { maedhros@7658: case 0x08: { // Cost base factor peter1138@2506: byte factor = grf_load_byte(&buf); peter1138@2508: uint price = gvid + i; peter1138@2506: peter1138@2508: if (price < NUM_PRICES) { peter1138@2508: SetPriceBaseMultiplier(price, factor); peter1138@2508: } else { Darkvater@5568: grfmsg(1, "GlobalVarChangeInfo: Price %d out of range, ignoring", price); peter1138@2508: } maedhros@7655: } break; maedhros@7655: maedhros@8974: case 0x09: // Cargo translation table maedhros@8974: /* This is loaded during the reservation stage, so just skip it here. */ maedhros@8974: /* Each entry is 4 bytes. */ maedhros@8974: buf += 4; maedhros@8974: break; maedhros@7655: maedhros@7655: case 0x0A: { // Currency display names belugas@4625: uint curidx = GetNewgrfCurrencyIdConverted(gvid + i); belugas@4625: StringID newone = GetGRFStringID(_cur_grffile->grfid, grf_load_word(&buf)); belugas@4377: belugas@4504: if ((newone != STR_UNDEFINED) && (curidx < NUM_CURRENCY)) { belugas@4504: _currency_specs[curidx].name = newone; belugas@4377: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0B: { // Currency multipliers belugas@4625: uint curidx = GetNewgrfCurrencyIdConverted(gvid + i); belugas@4377: uint32 rate = grf_load_dword(&buf); belugas@4377: belugas@4377: if (curidx < NUM_CURRENCY) { belugas@4602: /* TTDPatch uses a multiple of 1000 for its conversion calculations, belugas@4602: * which OTTD does not. For this reason, divide grf value by 1000, belugas@4602: * to be compatible */ belugas@4602: _currency_specs[curidx].rate = rate / 1000; belugas@4377: } else { Darkvater@5568: grfmsg(1, "GlobalVarChangeInfo: Currency multipliers %d out of range, ignoring", curidx); belugas@4377: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0C: { // Currency options belugas@4625: uint curidx = GetNewgrfCurrencyIdConverted(gvid + i); belugas@4377: uint16 options = grf_load_word(&buf); belugas@4377: belugas@4377: if (curidx < NUM_CURRENCY) { belugas@4377: _currency_specs[curidx].separator = GB(options, 0, 8); belugas@4602: /* By specifying only one bit, we prevent errors, belugas@4602: * since newgrf specs said that only 0 and 1 can be set for symbol_pos */ belugas@4602: _currency_specs[curidx].symbol_pos = GB(options, 8, 1); belugas@4377: } else { Darkvater@5568: grfmsg(1, "GlobalVarChangeInfo: Currency option %d out of range, ignoring", curidx); belugas@4377: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0D: { // Currency prefix symbol belugas@4625: uint curidx = GetNewgrfCurrencyIdConverted(gvid + i); belugas@4377: uint32 tempfix = grf_load_dword(&buf); belugas@4377: belugas@4377: if (curidx < NUM_CURRENCY) { maedhros@7658: memcpy(_currency_specs[curidx].prefix, &tempfix, 4); belugas@4377: _currency_specs[curidx].prefix[4] = 0; belugas@4377: } else { Darkvater@5568: grfmsg(1, "GlobalVarChangeInfo: Currency symbol %d out of range, ignoring", curidx); belugas@4377: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0E: { // Currency suffix symbol belugas@4625: uint curidx = GetNewgrfCurrencyIdConverted(gvid + i); belugas@4377: uint32 tempfix = grf_load_dword(&buf); belugas@4377: belugas@4377: if (curidx < NUM_CURRENCY) { rubidium@6987: memcpy(&_currency_specs[curidx].suffix, &tempfix, 4); belugas@4377: _currency_specs[curidx].suffix[4] = 0; belugas@4377: } else { Darkvater@5568: grfmsg(1, "GlobalVarChangeInfo: Currency symbol %d out of range, ignoring", curidx); belugas@4377: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x0F: { // Euro introduction dates belugas@4625: uint curidx = GetNewgrfCurrencyIdConverted(gvid + i); belugas@4377: Year year_euro = grf_load_word(&buf); belugas@4377: belugas@4377: if (curidx < NUM_CURRENCY) { belugas@4377: _currency_specs[curidx].to_euro = year_euro; belugas@4377: } else { Darkvater@5568: grfmsg(1, "GlobalVarChangeInfo: Euro intro date %d out of range, ignoring", curidx); belugas@4377: } maedhros@7655: } break; maedhros@7655: maedhros@7658: case 0x10: // Snow line height table maedhros@7655: if (numinfo > 1 || IsSnowLineSet()) { maedhros@7655: grfmsg(1, "GlobalVarChangeInfo: The snowline can only be set once (%d)", numinfo); maedhros@7655: } else if (len < SNOW_LINE_MONTHS * SNOW_LINE_DAYS) { maedhros@7655: grfmsg(1, "GlobalVarChangeInfo: Not enough entries set in the snowline table (%d)", len); maedhros@7655: } else { maedhros@7655: byte table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS]; maedhros@7655: maedhros@7655: for (uint i = 0; i < SNOW_LINE_MONTHS; i++) { maedhros@7655: for (uint j = 0; j < SNOW_LINE_DAYS; j++) { maedhros@7655: table[i][j] = grf_load_byte(&buf); maedhros@7655: } maedhros@6669: } maedhros@7655: SetSnowLine(table); maedhros@6669: } maedhros@7655: break; maedhros@7655: peter1138@10382: case 0x11: // GRF match for engine allocation peter1138@10382: /* This is loaded during the reservation stage, so just skip it here. */ peter1138@10382: /* Each entry is 8 bytes. */ peter1138@10382: buf += 8; peter1138@10382: break; peter1138@10382: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } peter1138@2506: } peter1138@3504: peter1138@2506: *bufp = buf; peter1138@2506: return ret; peter1138@2506: } peter1138@2506: peter1138@6685: static bool CargoChangeInfo(uint cid, int numinfo, int prop, byte **bufp, int len) peter1138@6685: { maedhros@7655: byte *buf = *bufp; maedhros@7655: bool ret = false; maedhros@7655: peter1138@6685: if (cid + numinfo > NUM_CARGO) { peter1138@6685: grfmsg(2, "CargoChangeInfo: Cargo type %d out of range (max %d)", cid + numinfo, NUM_CARGO - 1); peter1138@6685: return false; peter1138@6685: } peter1138@6685: maedhros@7655: for (int i = 0; i < numinfo; i++) { maedhros@7655: CargoSpec *cs = &_cargo[cid + i]; maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x08: /* Bit number of cargo */ maedhros@7655: cs->bitnum = grf_load_byte(&buf); peter1138@6685: if (cs->IsValid()) { glx@8214: cs->grfid = _cur_grffile->grfid; skidd13@8427: SetBit(_cargo_mask, cid + i); peter1138@6685: } else { skidd13@8425: ClrBit(_cargo_mask, cid + i); peter1138@6685: } maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x09: /* String ID for cargo type name */ rubidium@10219: cs->name = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&cs->name] = _cur_grffile->grfid; peter1138@7772: break; peter1138@7772: peter1138@7772: case 0x0A: /* String for 1 unit of cargo */ rubidium@10219: cs->name_single = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&cs->name_single] = _cur_grffile->grfid; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0B: maedhros@7655: /* String for units of cargo. This is different in OpenTTD to TTDPatch maedhros@7655: * (e.g. 10 tonnes of coal) */ rubidium@10219: cs->units_volume = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&cs->units_volume] = _cur_grffile->grfid; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0C: /* String for quantity of cargo (e.g. 10 tonnes of coal) */ rubidium@10219: cs->quantifier = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&cs->quantifier] = _cur_grffile->grfid; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0D: /* String for two letter cargo abbreviation */ rubidium@10219: cs->abbrev = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&cs->abbrev] = _cur_grffile->grfid; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0E: /* Sprite ID for cargo icon */ maedhros@7655: cs->sprite = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0F: /* Weight of one unit of cargo */ maedhros@7655: cs->weight = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x10: /* Used for payment calculation */ maedhros@7655: cs->transit_days[0] = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x11: /* Used for payment calculation */ maedhros@7655: cs->transit_days[1] = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x12: /* Base cargo price */ maedhros@7655: cs->initial_payment = grf_load_dword(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x13: /* Colour for station rating bars */ maedhros@7655: cs->rating_colour = MapDOSColour(grf_load_byte(&buf)); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x14: /* Colour for cargo graph */ maedhros@7655: cs->legend_colour = MapDOSColour(grf_load_byte(&buf)); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x15: /* Freight status */ maedhros@7658: cs->is_freight = (grf_load_byte(&buf) != 0); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x16: /* Cargo classes */ maedhros@7655: cs->classes = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x17: /* Cargo label */ maedhros@7655: cs->label = grf_load_dword(&buf); maedhros@7655: cs->label = BSWAP32(cs->label); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x18: { /* Town growth substitute type */ peter1138@6685: uint8 substitute_type = grf_load_byte(&buf); maedhros@7655: peter1138@6685: switch (substitute_type) { maedhros@7655: case 0x00: cs->town_effect = TE_PASSENGERS; break; maedhros@7655: case 0x02: cs->town_effect = TE_MAIL; break; maedhros@7655: case 0x05: cs->town_effect = TE_GOODS; break; maedhros@7655: case 0x09: cs->town_effect = TE_WATER; break; maedhros@7655: case 0x0B: cs->town_effect = TE_FOOD; break; peter1138@6685: default: peter1138@6685: grfmsg(1, "CargoChangeInfo: Unknown town growth substitute value %d, setting to none.", substitute_type); maedhros@7655: case 0xFF: cs->town_effect = TE_NONE; break; peter1138@6685: } maedhros@7655: } break; maedhros@7655: maedhros@7655: case 0x19: /* Town growth coefficient */ maedhros@7655: cs->multipliertowngrowth = grf_load_word(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x1A: /* Bitmask of callbacks to use */ maedhros@7655: cs->callback_mask = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } peter1138@6685: } peter1138@6685: peter1138@6685: *bufp = buf; peter1138@6685: return ret; peter1138@6685: } peter1138@6685: peter1138@6685: peter1138@4656: static bool SoundEffectChangeInfo(uint sid, int numinfo, int prop, byte **bufp, int len) peter1138@4656: { peter1138@4656: byte *buf = *bufp; peter1138@4656: bool ret = false; peter1138@4656: peter1138@4656: if (_cur_grffile->sound_offset == 0) { Darkvater@5568: grfmsg(1, "SoundEffectChangeInfo: No effects defined, skipping"); peter1138@4656: return false; peter1138@4656: } peter1138@4656: maedhros@7655: for (int i = 0; i < numinfo; i++) { maedhros@7655: uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds(); maedhros@7655: maedhros@7655: if (sound >= GetNumSounds()) { maedhros@7655: grfmsg(1, "SoundEffectChangeInfo: Sound %d not defined (max %d)", sound, GetNumSounds()); maedhros@7655: continue; maedhros@7655: } maedhros@7655: maedhros@7655: switch (prop) { maedhros@7655: case 0x08: // Relative volume maedhros@7655: GetSound(sound)->volume = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x09: // Priority maedhros@7655: GetSound(sound)->priority = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x0A: { // Override old sound peter1138@4656: uint orig_sound = grf_load_byte(&buf); peter1138@4656: maedhros@7655: if (orig_sound >= GetNumSounds()) { maedhros@7655: grfmsg(1, "SoundEffectChangeInfo: Original sound %d not defined (max %d)", orig_sound, GetNumSounds()); peter1138@4656: } else { peter1138@4656: FileEntry *newfe = GetSound(sound); peter1138@4656: FileEntry *oldfe = GetSound(orig_sound); peter1138@4656: peter1138@4656: /* Literally copy the data of the new sound over the original */ tron@5024: *oldfe = *newfe; peter1138@4656: } maedhros@7655: } break; maedhros@7655: maedhros@7655: default: maedhros@7655: ret = true; maedhros@7655: break; maedhros@7655: } peter1138@4656: } peter1138@4656: peter1138@4656: *bufp = buf; peter1138@4656: return ret; peter1138@4656: } peter1138@4656: belugas@7713: static bool IndustrytilesChangeInfo(uint indtid, int numinfo, int prop, byte **bufp, int len) belugas@7713: { belugas@7713: byte *buf = *bufp; belugas@7713: bool ret = false; belugas@7713: belugas@7713: if (indtid + numinfo > NUM_INDUSTRYTILES) { belugas@7713: grfmsg(1, "IndustryTilesChangeInfo: Too many industry tiles loaded (%u), max (%u). Ignoring.", indtid + numinfo, NUM_INDUSTRYTILES); belugas@7713: return false; belugas@7713: } belugas@7713: belugas@7713: /* Allocate industry tile specs if they haven't been allocated already. */ belugas@7713: if (_cur_grffile->indtspec == NULL) { belugas@7713: _cur_grffile->indtspec = CallocT(NUM_INDUSTRYTILES); belugas@7713: } belugas@7713: belugas@7713: for (int i = 0; i < numinfo; i++) { belugas@7713: IndustryTileSpec *tsp = _cur_grffile->indtspec[indtid + i]; belugas@7713: belugas@7713: if (prop != 0x08 && tsp == NULL) { belugas@7713: grfmsg(2, "IndustryTilesChangeInfo: Attempt to modify undefined industry tile %u. Ignoring.", indtid + i); belugas@7713: continue; belugas@7713: } belugas@7713: belugas@7713: switch (prop) { belugas@7713: case 0x08: { // Substitute industry tile type belugas@7713: IndustryTileSpec **tilespec = &_cur_grffile->indtspec[indtid + i]; belugas@7713: byte subs_id = grf_load_byte(&buf); belugas@7713: belugas@7955: if (subs_id >= NEW_INDUSTRYTILEOFFSET) { belugas@7713: /* The substitute id must be one of the original industry tile. */ belugas@7713: grfmsg(2, "IndustryTilesChangeInfo: Attempt to use new industry tile %u as substitute industry tile for %u. Ignoring.", subs_id, indtid + i); belugas@7713: return false; belugas@7713: } belugas@7713: belugas@7713: /* Allocate space for this industry. */ belugas@7713: if (*tilespec == NULL) { belugas@7713: int tempid; belugas@7713: *tilespec = CallocT(1); belugas@7713: tsp = *tilespec; belugas@7713: belugas@7713: memcpy(tsp, &_industry_tile_specs[subs_id], sizeof(_industry_tile_specs[subs_id])); belugas@7713: tsp->enabled = true; belugas@8600: belugas@8600: /* A copied tile should not have the animation infos copied too. belugas@8600: * The anim_state should be left untouched, though belugas@8600: * It is up to the author to animate them himself */ belugas@8600: tsp->anim_production = INDUSTRYTILE_NOANIM; belugas@8600: tsp->anim_next = INDUSTRYTILE_NOANIM; belugas@8600: belugas@7713: tsp->grf_prop.local_id = indtid + i; belugas@7713: tsp->grf_prop.subst_id = subs_id; belugas@7713: tsp->grf_prop.grffile = _cur_grffile; belugas@7713: tempid = _industile_mngr.AddEntityID(indtid + i, _cur_grffile->grfid, subs_id); // pre-reserve the tile slot belugas@7713: } belugas@7713: } break; belugas@7713: belugas@7713: case 0x09: { // Industry tile override belugas@7713: byte ovrid = grf_load_byte(&buf); belugas@7713: belugas@7713: /* The industry being overridden must be an original industry. */ belugas@7713: if (ovrid >= NEW_INDUSTRYTILEOFFSET) { belugas@7713: grfmsg(2, "IndustryTilesChangeInfo: Attempt to override new industry tile %u with industry tile id %u. Ignoring.", ovrid, indtid + i); belugas@7713: return false; belugas@7713: } belugas@7713: glx@8369: _industile_mngr.Add(indtid + i, _cur_grffile->grfid, ovrid); belugas@7713: } break; belugas@7713: belugas@7713: case 0x0A: // Tile acceptance belugas@7713: case 0x0B: belugas@7713: case 0x0C: { belugas@7713: uint16 acctp = grf_load_word(&buf); glx@8484: tsp->accepts_cargo[prop - 0x0A] = GetCargoTranslation(GB(acctp, 0, 8), _cur_grffile); belugas@8127: tsp->acceptance[prop - 0x0A] = GB(acctp, 8, 8); belugas@7713: } break; belugas@7713: belugas@7713: case 0x0D: // Land shape flags belugas@7713: tsp->slopes_refused = (Slope)grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x0E: // Callback flags belugas@7713: tsp->callback_flags = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x0F: // Animation information rubidium@7725: tsp->animation_info = grf_load_word(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x10: // Animation speed rubidium@7725: tsp->animation_speed = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x11: // Triggers for callback 25 rubidium@7725: tsp->animation_triggers = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x12: // Special flags rubidium@7725: tsp->animation_special_flags = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: default: belugas@7713: ret = true; belugas@7713: break; belugas@7713: } belugas@7713: } belugas@7713: belugas@7713: *bufp = buf; belugas@7713: return ret; belugas@7713: } belugas@7713: belugas@7713: static bool IndustriesChangeInfo(uint indid, int numinfo, int prop, byte **bufp, int len) belugas@7713: { belugas@7713: byte *buf = *bufp; belugas@7713: bool ret = false; belugas@7713: belugas@7713: if (indid + numinfo > NUM_INDUSTRYTYPES) { belugas@7713: grfmsg(1, "IndustriesChangeInfo: Too many industries loaded (%u), max (%u). Ignoring.", indid + numinfo, NUM_INDUSTRYTYPES); belugas@7713: return false; belugas@7713: } belugas@7713: belugas@7713: grfmsg(1, "IndustriesChangeInfo: newid %u", indid); belugas@7713: belugas@7713: /* Allocate industry specs if they haven't been allocated already. */ belugas@7713: if (_cur_grffile->industryspec == NULL) { belugas@7713: _cur_grffile->industryspec = CallocT(NUM_INDUSTRYTYPES); belugas@7713: } belugas@7713: belugas@7713: for (int i = 0; i < numinfo; i++) { belugas@7713: IndustrySpec *indsp = _cur_grffile->industryspec[indid + i]; belugas@7713: belugas@7713: if (prop != 0x08 && indsp == NULL) { belugas@7713: grfmsg(2, "IndustriesChangeInfo: Attempt to modify undefined industry %u. Ignoring.", indid + i); belugas@7713: continue; belugas@7713: } belugas@7713: belugas@7713: switch (prop) { belugas@7713: case 0x08: { // Substitute industry type belugas@7713: IndustrySpec **indspec = &_cur_grffile->industryspec[indid + i]; belugas@7713: byte subs_id = grf_load_byte(&buf); belugas@7713: belugas@7713: if (subs_id == 0xFF) { belugas@7713: /* Instead of defining a new industry, a substitute industry id belugas@7713: * of 0xFF disables the old industry with the current id. */ belugas@7713: _industry_specs[indid + i].enabled = false; belugas@7713: continue; belugas@7713: } else if (subs_id >= NEW_INDUSTRYOFFSET) { belugas@7713: /* The substitute id must be one of the original industry. */ belugas@7713: grfmsg(2, "_industry_specs: Attempt to use new industry %u as substitute industry for %u. Ignoring.", subs_id, indid + i); belugas@7713: return false; belugas@7713: } belugas@7713: belugas@7713: /* Allocate space for this industry. belugas@7713: * Only need to do it once. If ever it is called again, it should not belugas@7713: * do anything */ belugas@7713: if (*indspec == NULL) { belugas@7713: *indspec = CallocT(1); belugas@7713: indsp = *indspec; belugas@7713: belugas@7713: memcpy(indsp, &_origin_industry_specs[subs_id], sizeof(_industry_specs[subs_id])); belugas@7713: indsp->enabled = true; belugas@7713: indsp->grf_prop.local_id = indid + i; belugas@7713: indsp->grf_prop.subst_id = subs_id; belugas@7713: indsp->grf_prop.grffile = _cur_grffile; belugas@8212: /* If the grf industry needs to check its surounding upon creation, it should belugas@8212: * rely on callbacks, not on the original placement functions */ belugas@8212: indsp->check_proc = CHECK_NOTHING; belugas@7713: } belugas@7713: } break; belugas@7713: belugas@7713: case 0x09: { // Industry type override belugas@7713: byte ovrid = grf_load_byte(&buf); belugas@7713: belugas@7713: /* The industry being overridden must be an original industry. */ belugas@7713: if (ovrid >= NEW_INDUSTRYOFFSET) { belugas@7713: grfmsg(2, "IndustriesChangeInfo: Attempt to override new industry %u with industry id %u. Ignoring.", ovrid, indid + i); belugas@7713: return false; belugas@7713: } belugas@7713: indsp->grf_prop.override = ovrid; glx@8369: _industry_mngr.Add(indid + i, _cur_grffile->grfid, ovrid); belugas@7713: } break; belugas@7713: belugas@7713: case 0x0A: { // Set industry layout(s) belugas@7713: indsp->num_table = grf_load_byte(&buf); // Number of layaouts belugas@7713: uint32 defsize = grf_load_dword(&buf); // Total size of the definition belugas@7713: IndustryTileTable **tile_table = CallocT(indsp->num_table); // Table with tiles to compose an industry belugas@7713: IndustryTileTable *itt = CallocT(defsize); // Temporary array to read the tile layouts from the GRF belugas@7713: int size; belugas@7713: IndustryTileTable *copy_from; belugas@7713: belugas@7713: for (byte j = 0; j < indsp->num_table; j++) { belugas@7713: for (int k = 0;; k++) { belugas@7713: itt[k].ti.x = grf_load_byte(&buf); // Offsets from northermost tile belugas@7713: belugas@7713: if (itt[k].ti.x == 0xFE && k == 0) { belugas@7713: /* This means we have to borrow the layout from an old industry */ belugas@7713: IndustryType type = grf_load_byte(&buf); //industry holding required layout belugas@7713: byte laynbr = grf_load_byte(&buf); //layout number to borrow belugas@7713: belugas@7713: copy_from = (IndustryTileTable*)_origin_industry_specs[type].table[laynbr]; belugas@7713: for (size = 1;; size++) { belugas@8242: if (copy_from[size - 1].ti.x == -0x80 && copy_from[size - 1].ti.y == 0) break; belugas@7713: } belugas@7713: break; belugas@7713: } belugas@7713: belugas@7713: itt[k].ti.y = grf_load_byte(&buf); // Or table definition finalisation belugas@7713: belugas@7713: if (itt[k].ti.x == 0 && itt[k].ti.y == 0x80) { belugas@7713: /* Not the same terminator. The one we are using is rather belugas@7713: x= -80, y = x . So, adjust it. */ belugas@7713: itt[k].ti.x = -0x80; belugas@7713: itt[k].ti.y = 0; belugas@7713: itt[k].gfx = 0; belugas@7713: belugas@7713: size = k + 1; belugas@7713: copy_from = itt; belugas@7713: break; belugas@7713: } belugas@7713: belugas@7713: itt[k].gfx = grf_load_byte(&buf); belugas@7713: belugas@7713: if (itt[k].gfx == 0xFE) { belugas@7713: /* Use a new tile from this GRF */ belugas@7713: int local_tile_id = grf_load_word(&buf); belugas@7713: belugas@7713: /* Read the ID from the _industile_mngr. */ belugas@7713: int tempid = _industile_mngr.GetID(local_tile_id, _cur_grffile->grfid); belugas@7713: belugas@7713: if (tempid == INVALID_INDUSTRYTILE) { belugas@7713: grfmsg(2, "IndustriesChangeInfo: Attempt to use industry tile %u with industry id %u, not yet defined. Ignoring.", local_tile_id, indid); belugas@7713: } else { belugas@7713: /* Declared as been valid, can be used */ belugas@7713: itt[k].gfx = tempid; belugas@7713: size = k + 1; belugas@7713: copy_from = itt; belugas@7713: } belugas@8126: } else if (itt[k].gfx == 0xFF) { belugas@8126: itt[k].ti.x = (int8)GB(itt[k].ti.x, 0, 8); belugas@8126: itt[k].ti.y = (int8)GB(itt[k].ti.y, 0, 8); belugas@8129: } belugas@7713: } belugas@7713: tile_table[j] = CallocT(size); belugas@7713: memcpy(tile_table[j], copy_from, sizeof(*copy_from) * size); belugas@7713: } belugas@7713: /* Install final layout construction in the industry spec */ belugas@7713: indsp->table = tile_table; skidd13@8427: SetBit(indsp->cleanup_flag, 1); belugas@7713: free(itt); belugas@7713: } break; belugas@7713: belugas@7713: case 0x0B: // Industry production flags belugas@7713: indsp->life_type = (IndustryLifeType)grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x0C: // Industry closure message rubidium@10219: indsp->closure_text = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&indsp->closure_text] = _cur_grffile->grfid; belugas@7713: break; belugas@7713: belugas@7713: case 0x0D: // Production increase message rubidium@10219: indsp->production_up_text = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&indsp->production_up_text] = _cur_grffile->grfid; belugas@7713: break; belugas@7713: belugas@7713: case 0x0E: // Production decrease message rubidium@10219: indsp->production_down_text = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&indsp->production_down_text] = _cur_grffile->grfid; belugas@7713: break; belugas@7713: belugas@7713: case 0x0F: // Fund cost multiplier belugas@7713: indsp->cost_multiplier = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x10: // Production cargo types belugas@7713: for (byte j = 0; j < 2; j++) { glx@8484: indsp->produced_cargo[j] = GetCargoTranslation(grf_load_byte(&buf), _cur_grffile); belugas@7713: } belugas@7713: break; belugas@7713: belugas@7713: case 0x11: // Acceptance cargo types belugas@7713: for (byte j = 0; j < 3; j++) { glx@8484: indsp->accepts_cargo[j] = GetCargoTranslation(grf_load_byte(&buf), _cur_grffile); belugas@7713: } belugas@7713: grf_load_byte(&buf); // Unnused, eat it up belugas@7713: break; belugas@7713: belugas@7713: case 0x12: // Production multipliers belugas@7713: case 0x13: belugas@7713: indsp->production_rate[prop - 0x12] = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x14: // Minimal amount of cargo distributed belugas@7713: indsp->minimal_cargo = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x15: { // Random sound effects belugas@7713: indsp->number_of_sounds = grf_load_byte(&buf); belugas@7713: uint8 *sounds = MallocT(indsp->number_of_sounds); belugas@7713: belugas@7713: for (uint8 j = 0; j < indsp->number_of_sounds; j++) sounds[j] = grf_load_byte(&buf); belugas@7713: indsp->random_sounds = sounds; skidd13@8427: SetBit(indsp->cleanup_flag, 0); belugas@7713: } break; belugas@7713: belugas@7713: case 0x16: // Conflicting industry types belugas@7713: for (byte j = 0; j < 3; j++) indsp->conflicting[j] = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x17: // Probability in random game rubidium@10775: indsp->appear_creation[_settings_game.game_creation.landscape] = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x18: // Probability during gameplay rubidium@10775: indsp->appear_ingame[_settings_game.game_creation.landscape] = grf_load_byte(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x19: // Map color belugas@7713: indsp->map_colour = MapDOSColour(grf_load_byte(&buf)); belugas@7713: break; belugas@7713: belugas@7713: case 0x1A: // Special industry flags to define special behavior glx@8253: indsp->behaviour = (IndustryBehaviour)grf_load_dword(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x1B: // New industry text ID rubidium@10219: indsp->new_industry_text = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&indsp->new_industry_text] = _cur_grffile->grfid; belugas@7713: break; belugas@7713: belugas@7713: case 0x1C: // Input cargo multipliers for the three input cargo types belugas@7713: case 0x1D: belugas@7713: case 0x1E: { belugas@7713: uint32 multiples = grf_load_dword(&buf); rubidium@10229: indsp->input_cargo_multiplier[prop - 0x1C][0] = GB(multiples, 0, 15); rubidium@10229: indsp->input_cargo_multiplier[prop - 0x1C][1] = GB(multiples, 15, 15); belugas@7713: } break; belugas@7713: belugas@7713: case 0x1F: // Industry name rubidium@10219: indsp->name = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&indsp->name] = _cur_grffile->grfid; belugas@7713: break; belugas@7713: belugas@7713: case 0x20: // Prospecting success chance belugas@7713: indsp->prospecting_chance = grf_load_dword(&buf); belugas@7713: break; belugas@7713: belugas@7713: case 0x21: // Callback flags belugas@7713: case 0x22: { // Callback additional flags belugas@7713: byte aflag = grf_load_byte(&buf); belugas@7713: SB(indsp->callback_flags, (prop - 0x21) * 8, 8, aflag); belugas@7713: } break; belugas@7713: belugas@8474: case 0x23: // removal cost multiplier belugas@8474: indsp->removal_cost_multiplier = grf_load_dword(&buf); belugas@8474: break; belugas@8474: belugas@8966: case 0x24: // name for nearby station rubidium@10219: indsp->station_name = grf_load_word(&buf); rubidium@10219: _string_to_grf_mapping[&indsp->station_name] = _cur_grffile->grfid; belugas@8966: break; belugas@8966: belugas@7713: default: belugas@7713: ret = true; belugas@7713: break; belugas@7713: } belugas@7713: } belugas@7713: belugas@7713: *bufp = buf; belugas@7713: return ret; belugas@7713: } belugas@7713: celestar@356: /* Action 0x00 */ glx@10465: static void FeatureChangeInfo(byte *buf, size_t len) truelight@0: { truelight@0: byte *bufend = buf + len; truelight@0: truelight@0: /* <00> ()... truelight@0: * truelight@0: * B feature 0, 1, 2 or 3 for trains, road vehicles, ships or planes truelight@0: * 4 for defining new train station sets truelight@0: * B num-props how many properties to change per vehicle/station truelight@0: * B num-info how many vehicles/stations to change glx@8388: * E id ID of first vehicle/station to change, if num-info is truelight@0: * greater than one, this one and the following truelight@0: * vehicles/stations will be changed truelight@0: * B property what property to change, depends on the feature truelight@0: * V new-info new bytes of info (variable size; depends on properties) */ darkvater@391: /* TODO: Bridges, town houses. */ truelight@0: peter1138@2438: static const VCI_Handler handler[] = { belugas@3825: /* GSF_TRAIN */ RailVehicleChangeInfo, belugas@3825: /* GSF_ROAD */ RoadVehicleChangeInfo, belugas@3825: /* GSF_SHIP */ ShipVehicleChangeInfo, belugas@3825: /* GSF_AIRCRAFT */ AircraftVehicleChangeInfo, belugas@3825: /* GSF_STATION */ StationChangeInfo, peter1138@8868: /* GSF_CANAL */ CanalChangeInfo, belugas@3825: /* GSF_BRIDGE */ BridgeChangeInfo, maedhros@6658: /* GSF_TOWNHOUSE */ TownHouseChangeInfo, maedhros@8974: /* GSF_GLOBALVAR */ GlobalVarChangeInfo, belugas@7713: /* GSF_INDUSTRYTILES */IndustrytilesChangeInfo, belugas@7713: /* GSF_INDUSTRIES */ IndustriesChangeInfo, peter1138@6685: /* GSF_CARGOS */ NULL, /* Cargo is handled during reservation */ peter1138@4656: /* GSF_SOUNDFX */ SoundEffectChangeInfo, truelight@0: }; darkvater@360: peter1138@5841: if (!check_length(len, 6, "FeatureChangeInfo")) return; peter1138@3714: buf++; peter1138@6607: uint8 feature = grf_load_byte(&buf); peter1138@6607: uint8 numprops = grf_load_byte(&buf); belugas@6614: uint numinfo = grf_load_byte(&buf); glx@8388: uint engine = grf_load_extended(&buf); truelight@193: Darkvater@5568: grfmsg(6, "FeatureChangeInfo: feature %d, %d properties, to apply to %d+%d", celestar@378: feature, numprops, engine, numinfo); celestar@378: peter1138@2440: if (feature >= lengthof(handler) || handler[feature] == NULL) { Darkvater@5568: grfmsg(1, "FeatureChangeInfo: Unsupported feature %d, skipping", feature); darkvater@391: return; darkvater@391: } darkvater@391: celestar@357: while (numprops-- && buf < bufend) { celestar@357: uint8 prop = grf_load_byte(&buf); tron@2421: bool ignoring = false; celestar@357: tron@2421: switch (feature) { tron@2421: case GSF_TRAIN: tron@2421: case GSF_ROAD: tron@2421: case GSF_SHIP: maedhros@7655: case GSF_AIRCRAFT: { maedhros@7655: bool handled = true; maedhros@7655: maedhros@7655: for (uint i = 0; i < numinfo; i++) { peter1138@10382: Engine *e = GetNewEngine(_cur_grffile, (VehicleType)feature, engine + i); peter1138@10382: EngineInfo *ei = &e->info; maedhros@7655: maedhros@7655: /* Common properties for vehicles */ maedhros@7655: switch (prop) { maedhros@7655: case 0x00: // Introduction date maedhros@7655: ei->base_intro = grf_load_word(&buf) + DAYS_TILL_ORIGINAL_BASE_YEAR; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x02: // Decay speed peter1138@10837: ei->decay_speed = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x03: // Vehicle life maedhros@7655: ei->lifelength = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x04: // Model life maedhros@7655: ei->base_life = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x06: // Climates available maedhros@7655: ei->climates = grf_load_byte(&buf); peter1138@10382: // XXX sometimes a grf wants hidden vehicles :o peter1138@10382: if (ei->climates == 0) ei->climates = 0x80; maedhros@7655: break; maedhros@7655: maedhros@7655: case 0x07: // Loading speed maedhros@7655: /* Hyronymus explained me what does maedhros@7655: * this mean and insists on having a maedhros@7655: * credit ;-). --pasky */ maedhros@7655: ei->load_amount = grf_load_byte(&buf); maedhros@7655: break; maedhros@7655: maedhros@7655: default: maedhros@7655: handled = false; maedhros@7655: break; maedhros@7655: } tron@2421: } maedhros@7655: maedhros@7655: if (handled) break; maedhros@7655: } /* FALL THROUGH */ tron@2421: tron@2421: default: peter1138@6607: if (handler[feature](engine, numinfo, prop, &buf, bufend - buf)) { tron@2421: ignoring = true; peter1138@6607: } tron@2421: break; celestar@357: } tron@2421: belugas@8570: if (ignoring) grfmsg(1, "FeatureChangeInfo: Ignoring property 0x%02X of feature 0x%02X (not implemented)", prop, feature); truelight@0: } truelight@0: } truelight@0: peter1138@5776: /* Action 0x00 (GLS_SAFETYSCAN) */ glx@10465: static void SafeChangeInfo(byte *buf, size_t len) peter1138@5776: { peter1138@5841: if (!check_length(len, 6, "SafeChangeInfo")) return; peter1138@5776: buf++; peter1138@6607: uint8 feature = grf_load_byte(&buf); peter1138@6607: uint8 numprops = grf_load_byte(&buf); glx@8388: grf_load_byte(&buf); // num-info glx@8388: grf_load_extended(&buf); // id peter1138@5776: peter1138@5776: if (feature == GSF_BRIDGE && numprops == 1) { peter1138@5776: uint8 prop = grf_load_byte(&buf); peter1138@5776: /* Bridge property 0x0D is redefinition of sprite layout tables, which peter1138@5776: * is considered safe. */ peter1138@5776: if (prop == 0x0D) return; peter1138@5776: } peter1138@5776: skidd13@8427: SetBit(_cur_grfconfig->flags, GCF_UNSAFE); peter1138@5776: peter1138@5776: /* Skip remainder of GRF */ peter1138@5776: _skip_sprites = -1; peter1138@5776: } peter1138@5776: peter1138@6685: /* Action 0x00 (GLS_RESERVE) */ glx@10465: static void ReserveChangeInfo(byte *buf, size_t len) peter1138@6685: { peter1138@6685: byte *bufend = buf + len; peter1138@6685: maedhros@8974: if (!check_length(len, 6, "ReserveChangeInfo")) return; peter1138@6685: buf++; peter1138@6685: uint8 feature = grf_load_byte(&buf); peter1138@6685: glx@8437: if (feature != GSF_CARGOS && feature != GSF_GLOBALVAR) return; peter1138@6685: peter1138@6685: uint8 numprops = grf_load_byte(&buf); peter1138@6685: uint8 numinfo = grf_load_byte(&buf); glx@8388: uint8 index = grf_load_extended(&buf); peter1138@6685: peter1138@6685: while (numprops-- && buf < bufend) { peter1138@6685: uint8 prop = grf_load_byte(&buf); glx@8437: bool ignoring = false; glx@8437: glx@8437: switch (feature) { glx@8437: default: NOT_REACHED(); glx@8437: case GSF_CARGOS: glx@8437: ignoring = CargoChangeInfo(index, numinfo, prop, &buf, bufend - buf); glx@8437: break; glx@8437: case GSF_GLOBALVAR: maedhros@8974: switch (prop) { maedhros@8974: case 0x09: // Cargo Translation Table maedhros@8974: if (index != 0) { maedhros@8974: grfmsg(1, "ReserveChangeInfo: Cargo translation table must start at zero"); maedhros@8974: return; maedhros@8974: } maedhros@8974: maedhros@8974: free(_cur_grffile->cargo_list); maedhros@8974: _cur_grffile->cargo_max = numinfo; maedhros@8974: _cur_grffile->cargo_list = MallocT(numinfo); maedhros@8974: maedhros@8974: for (uint i = 0; i < numinfo; i++) { maedhros@8974: CargoLabel cl = grf_load_dword(&buf); maedhros@8974: _cur_grffile->cargo_list[i] = BSWAP32(cl); maedhros@8974: } maedhros@8974: break; peter1138@10382: peter1138@10382: case 0x11: // GRF match for engine allocation peter1138@10382: for (uint i = 0; i < numinfo; i++) { peter1138@10382: uint32 s = grf_load_dword(&buf); peter1138@10382: uint32 t = grf_load_dword(&buf); peter1138@10382: SetNewGRFOverride(s, t); peter1138@10382: } peter1138@10382: break; maedhros@8974: } glx@8437: break; peter1138@6685: } glx@8437: glx@8437: if (ignoring) grfmsg(2, "ReserveChangeInfo: Ignoring property 0x%02X (not implemented)", prop); peter1138@6685: } peter1138@6685: } peter1138@6685: hackykid@1883: /** hackykid@1883: * Creates a spritegroup representing a callback result hackykid@1883: * @param value The value that was used to represent this callback result hackykid@1883: * @return A spritegroup representing that callback result hackykid@1883: */ peter1138@4893: static const SpriteGroup* NewCallBackResultSpriteGroup(uint16 value) hackykid@1883: { peter1138@3595: SpriteGroup *group = AllocateSpriteGroup(); hackykid@1883: peter1138@2488: group->type = SGT_CALLBACK; hackykid@1883: belugas@6674: /* Old style callback results have the highest byte 0xFF so signify it is a callback result belugas@6674: * New style ones only have the highest bit set (allows 15-bit results, instead of just 8) */ tron@3033: if ((value >> 8) == 0xFF) { tron@3033: value &= ~0xFF00; tron@3033: } else { hackykid@1883: value &= ~0x8000; tron@3033: } hackykid@1883: peter1138@2488: group->g.callback.result = value; hackykid@1883: hackykid@1883: return group; hackykid@1883: } truelight@0: peter1138@2489: /** peter1138@2489: * Creates a spritegroup representing a sprite number result. belugas@6977: * @param sprite The sprite number. belugas@6977: * @param num_sprites The number of sprites per set. peter1138@2489: * @return A spritegroup representing the sprite number result. peter1138@2489: */ peter1138@4893: static const SpriteGroup* NewResultSpriteGroup(SpriteID sprite, byte num_sprites) peter1138@2489: { peter1138@3595: SpriteGroup *group = AllocateSpriteGroup(); peter1138@2489: group->type = SGT_RESULT; peter1138@3668: group->g.result.sprite = sprite; peter1138@3668: group->g.result.num_sprites = num_sprites; peter1138@2489: return group; peter1138@2489: } peter1138@2489: celestar@356: /* Action 0x01 */ glx@10465: static void NewSpriteSet(byte *buf, size_t len) truelight@0: { truelight@0: /* <01> truelight@0: * truelight@0: * B feature feature to define sprites for truelight@0: * 0, 1, 2, 3: veh-type, 4: train stations truelight@0: * B num-sets number of sprite sets tron@2346: * E num-ent how many entries per sprite set truelight@0: * For vehicles, this is the number of different truelight@0: * vehicle directions in each sprite set truelight@0: * Set num-dirs=8, unless your sprites are symmetric. truelight@0: * In that case, use num-dirs=4. peter1138@6607: */ truelight@0: peter1138@5841: if (!check_length(len, 4, "NewSpriteSet")) return; tron@2346: buf++; peter1138@6607: uint8 feature = grf_load_byte(&buf); peter1138@6607: uint8 num_sets = grf_load_byte(&buf); peter1138@6607: uint16 num_ents = grf_load_extended(&buf); truelight@0: tron@2342: _cur_grffile->spriteset_start = _cur_spriteid; celestar@389: _cur_grffile->spriteset_feature = feature; tron@2342: _cur_grffile->spriteset_numsets = num_sets; tron@2342: _cur_grffile->spriteset_numents = num_ents; tron@2342: Darkvater@5568: grfmsg(7, "New sprite set at %d of type %d, consisting of %d sets with %d views each (total %d)", tron@2342: _cur_spriteid, feature, num_sets, num_ents, num_sets * num_ents tron@2342: ); tron@2342: peter1138@8778: for (int i = 0; i < num_sets * num_ents; i++) { rubidium@8449: _nfo_line++; truelight@7404: LoadNextSprite(_cur_spriteid++, _file_index, _nfo_line); tron@2342: } truelight@0: } truelight@0: peter1138@7072: /* Action 0x01 (SKIP) */ glx@10465: static void SkipAct1(byte *buf, size_t len) peter1138@7072: { peter1138@7072: if (!check_length(len, 4, "SkipAct1")) return; peter1138@7072: buf++; peter1138@7072: grf_load_byte(&buf); peter1138@7072: uint8 num_sets = grf_load_byte(&buf); peter1138@7072: uint16 num_ents = grf_load_extended(&buf); peter1138@7072: peter1138@7072: _skip_sprites = num_sets * num_ents; peter1138@7072: peter1138@7072: grfmsg(3, "SkipAct1: Skipping %d sprites", _skip_sprites); peter1138@7072: } peter1138@7072: peter1138@3666: /* Helper function to either create a callback or link to a previously peter1138@3666: * defined spritegroup. */ peter1138@4893: static const SpriteGroup* GetGroupFromGroupID(byte setid, byte type, uint16 groupid) peter1138@3666: { skidd13@8424: if (HasBit(groupid, 15)) return NewCallBackResultSpriteGroup(groupid); peter1138@3666: peter1138@3666: if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) { belugas@6973: grfmsg(1, "GetGroupFromGroupID(0x%02X:0x%02X): Groupid 0x%04X does not exist, leaving empty", setid, type, groupid); peter1138@3666: return NULL; peter1138@3666: } peter1138@3666: peter1138@3666: return _cur_grffile->spritegroups[groupid]; peter1138@3666: } peter1138@3666: peter1138@3666: /* Helper function to either create a callback or a result sprite group. */ peter1138@4893: static const SpriteGroup* CreateGroupFromGroupID(byte feature, byte setid, byte type, uint16 spriteid, uint16 num_sprites) peter1138@3666: { skidd13@8424: if (HasBit(spriteid, 15)) return NewCallBackResultSpriteGroup(spriteid); peter1138@3666: peter1138@3667: if (spriteid >= _cur_grffile->spriteset_numsets) { belugas@6973: grfmsg(1, "CreateGroupFromGroupID(0x%02X:0x%02X): Sprite set %u invalid, max %u", setid, type, spriteid, _cur_grffile->spriteset_numsets); peter1138@3667: return NULL; peter1138@3667: } peter1138@3667: peter1138@3666: /* Check if the sprite is within range. This can fail if the Action 0x01 peter1138@3666: * is skipped, as TTDPatch mandates that Action 0x02s must be processed. peter1138@3666: * We don't have that rule, but must live by the Patch... */ peter1138@3666: if (_cur_grffile->spriteset_start + spriteid * num_sprites + num_sprites > _cur_spriteid) { belugas@6973: grfmsg(1, "CreateGroupFromGroupID(0x%02X:0x%02X): Real Sprite IDs 0x%04X - 0x%04X do not (all) exist (max 0x%04X), leaving empty", peter1138@3666: setid, type, peter1138@3666: _cur_grffile->spriteset_start + spriteid * num_sprites, peter1138@3666: _cur_grffile->spriteset_start + spriteid * num_sprites + num_sprites - 1, _cur_spriteid - 1); peter1138@3666: return NULL; peter1138@3666: } peter1138@3666: peter1138@3803: if (feature != _cur_grffile->spriteset_feature) { belugas@6973: grfmsg(1, "CreateGroupFromGroupID(0x%02X:0x%02X): Sprite set feature 0x%02X does not match action feature 0x%02X, skipping", peter1138@7381: setid, type, peter1138@3803: _cur_grffile->spriteset_feature, feature); peter1138@3803: return NULL; peter1138@3803: } peter1138@3803: peter1138@3666: return NewResultSpriteGroup(_cur_grffile->spriteset_start + spriteid * num_sprites, num_sprites); peter1138@3666: } peter1138@3666: celestar@356: /* Action 0x02 */ glx@10465: static void NewSpriteGroup(byte *buf, size_t len) truelight@0: { truelight@0: /* <02> truelight@0: * truelight@0: * B feature see action 1 truelight@0: * B set-id ID of this particular definition truelight@0: * B type/num-entries truelight@0: * if 80 or greater, this is a randomized or variational truelight@0: * list definition, see below truelight@0: * otherwise it specifies a number of entries, the exact truelight@0: * meaning depends on the feature truelight@0: * V feature-specific-data (huge mess, don't even look it up --pasky) */ peter1138@3633: SpriteGroup *group = NULL; peter1138@3633: byte *bufend = buf + len; truelight@193: peter1138@5841: if (!check_length(len, 5, "NewSpriteGroup")) return; peter1138@3633: buf++; peter1138@3633: peter1138@6607: uint8 feature = grf_load_byte(&buf); peter1138@6607: uint8 setid = grf_load_byte(&buf); peter1138@6607: uint8 type = grf_load_byte(&buf); truelight@0: peter1138@2444: if (setid >= _cur_grffile->spritegroups_count) { belugas@6674: /* Allocate memory for new sprite group references. */ KUDr@5860: _cur_grffile->spritegroups = ReallocT(_cur_grffile->spritegroups, setid + 1); belugas@6674: /* Initialise new space to NULL */ peter1138@2488: for (; _cur_grffile->spritegroups_count < (setid + 1); _cur_grffile->spritegroups_count++) peter1138@2488: _cur_grffile->spritegroups[_cur_grffile->spritegroups_count] = NULL; peter1138@2444: } peter1138@2444: peter1138@3633: switch (type) { peter1138@3633: /* Deterministic Sprite Group */ peter1138@3633: case 0x81: // Self scope, byte peter1138@3633: case 0x82: // Parent scope, byte peter1138@3668: case 0x85: // Self scope, word peter1138@3668: case 0x86: // Parent scope, word peter1138@3668: case 0x89: // Self scope, dword peter1138@3668: case 0x8A: // Parent scope, dword peter1138@3633: { peter1138@3668: byte varadjust; peter1138@3668: byte varsize; peter1138@3668: peter1138@3668: /* Check we can load the var size parameter */ peter1138@5841: if (!check_length(bufend - buf, 1, "NewSpriteGroup (Deterministic) (1)")) return; peter1138@3633: peter1138@3633: group = AllocateSpriteGroup(); peter1138@3633: group->type = SGT_DETERMINISTIC; skidd13@8424: group->g.determ.var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF; peter1138@3668: peter1138@3668: switch (GB(type, 2, 2)) { peter1138@3672: default: NOT_REACHED(); peter1138@3668: case 0: group->g.determ.size = DSG_SIZE_BYTE; varsize = 1; break; peter1138@3668: case 1: group->g.determ.size = DSG_SIZE_WORD; varsize = 2; break; peter1138@3668: case 2: group->g.determ.size = DSG_SIZE_DWORD; varsize = 4; break; peter1138@3633: } peter1138@3633: peter1138@5841: if (!check_length(bufend - buf, 5 + varsize, "NewSpriteGroup (Deterministic) (2)")) return; peter1138@3668: peter1138@3668: /* Loop through the var adjusts. Unfortunately we don't know how many we have peter1138@3668: * from the outset, so we shall have to keep reallocing. */ peter1138@3668: do { peter1138@3668: DeterministicSpriteGroupAdjust *adjust; peter1138@3668: peter1138@3668: if (group->g.determ.num_adjusts > 0) { peter1138@5841: if (!check_length(bufend - buf, 2 + varsize + 3, "NewSpriteGroup (Deterministic) (3)")) return; peter1138@3668: } peter1138@3668: peter1138@3668: group->g.determ.num_adjusts++; KUDr@5860: group->g.determ.adjusts = ReallocT(group->g.determ.adjusts, group->g.determ.num_adjusts); peter1138@3668: peter1138@3668: adjust = &group->g.determ.adjusts[group->g.determ.num_adjusts - 1]; peter1138@3668: peter1138@3668: /* The first var adjust doesn't have an operation specified, so we set it to add. */ rubidium@5838: adjust->operation = group->g.determ.num_adjusts == 1 ? DSGA_OP_ADD : (DeterministicSpriteGroupAdjustOperation)grf_load_byte(&buf); peter1138@3668: adjust->variable = grf_load_byte(&buf); maedhros@5868: if (adjust->variable == 0x7E) { maedhros@5868: /* Link subroutine group */ maedhros@5868: adjust->subroutine = GetGroupFromGroupID(setid, type, grf_load_byte(&buf)); maedhros@5868: } else { skidd13@8450: adjust->parameter = IsInsideMM(adjust->variable, 0x60, 0x80) ? grf_load_byte(&buf) : 0; maedhros@5868: } peter1138@3668: peter1138@3668: varadjust = grf_load_byte(&buf); peter1138@3668: adjust->shift_num = GB(varadjust, 0, 5); rubidium@5838: adjust->type = (DeterministicSpriteGroupAdjustType)GB(varadjust, 6, 2); peter1138@3668: adjust->and_mask = grf_load_var(varsize, &buf); peter1138@3668: peter1138@3668: if (adjust->type != DSGA_TYPE_NONE) { peter1138@3668: adjust->add_val = grf_load_var(varsize, &buf); peter1138@3668: adjust->divmod_val = grf_load_var(varsize, &buf); peter1138@3668: } else { peter1138@3668: adjust->add_val = 0; peter1138@3668: adjust->divmod_val = 0; peter1138@3668: } peter1138@3668: peter1138@3668: /* Continue reading var adjusts while bit 5 is set. */ skidd13@8424: } while (HasBit(varadjust, 5)); peter1138@3668: peter1138@3668: group->g.determ.num_ranges = grf_load_byte(&buf); peter1138@7155: if (group->g.determ.num_ranges > 0) group->g.determ.ranges = CallocT(group->g.determ.num_ranges); peter1138@3668: peter1138@5841: if (!check_length(bufend - buf, 2 + (2 + 2 * varsize) * group->g.determ.num_ranges, "NewSpriteGroup (Deterministic)")) return; peter1138@3668: peter1138@6607: for (uint i = 0; i < group->g.determ.num_ranges; i++) { peter1138@3668: group->g.determ.ranges[i].group = GetGroupFromGroupID(setid, type, grf_load_word(&buf)); peter1138@3668: group->g.determ.ranges[i].low = grf_load_var(varsize, &buf); peter1138@3668: group->g.determ.ranges[i].high = grf_load_var(varsize, &buf); peter1138@3633: } peter1138@3633: peter1138@3668: group->g.determ.default_group = GetGroupFromGroupID(setid, type, grf_load_word(&buf)); peter1138@3633: break; tron@445: } tron@445: peter1138@3633: /* Randomized Sprite Group */ peter1138@3633: case 0x80: // Self scope peter1138@3633: case 0x83: // Parent scope glx@9252: case 0x84: // Relative scope peter1138@3633: { glx@9252: if (!check_length(bufend - buf, HasBit(type, 2) ? 8 : 7, "NewSpriteGroup (Randomized) (1)")) return; peter1138@3633: peter1138@3633: group = AllocateSpriteGroup(); peter1138@3633: group->type = SGT_RANDOMIZED; skidd13@8424: group->g.random.var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF; peter1138@3668: glx@9254: if (HasBit(type, 2)) { glx@9254: if (feature <= GSF_AIRCRAFT) group->g.random.var_scope = VSG_SCOPE_RELATIVE; glx@9252: group->g.random.count = grf_load_byte(&buf); glx@9252: } glx@9252: peter1138@6607: uint8 triggers = grf_load_byte(&buf); peter1138@3668: group->g.random.triggers = GB(triggers, 0, 7); skidd13@8424: group->g.random.cmp_mode = HasBit(triggers, 7) ? RSG_CMP_ALL : RSG_CMP_ANY; peter1138@3668: group->g.random.lowest_randbit = grf_load_byte(&buf); peter1138@3668: group->g.random.num_groups = grf_load_byte(&buf); KUDr@5860: group->g.random.groups = CallocT(group->g.random.num_groups); peter1138@3668: peter1138@5841: if (!check_length(bufend - buf, 2 * group->g.random.num_groups, "NewSpriteGroup (Randomized) (2)")) return; peter1138@3668: peter1138@6607: for (uint i = 0; i < group->g.random.num_groups; i++) { peter1138@3668: group->g.random.groups[i] = GetGroupFromGroupID(setid, type, grf_load_word(&buf)); peter1138@3633: } peter1138@3633: peter1138@3633: break; peter1138@2489: } peter1138@3633: peter1138@3668: /* Neither a variable or randomized sprite group... must be a real group */ peter1138@3633: default: peter1138@3633: { peter1138@3803: peter1138@3803: peter1138@3668: switch (feature) { peter1138@3668: case GSF_TRAIN: peter1138@3668: case GSF_ROAD: peter1138@3668: case GSF_SHIP: peter1138@3668: case GSF_AIRCRAFT: peter1138@3668: case GSF_STATION: peter1138@7079: case GSF_CANAL: peter1138@6691: case GSF_CARGOS: peter1138@3668: { peter1138@3668: byte sprites = _cur_grffile->spriteset_numents; peter1138@3668: byte num_loaded = type; peter1138@3668: byte num_loading = grf_load_byte(&buf); peter1138@3668: peter1138@3668: if (_cur_grffile->spriteset_start == 0) { Darkvater@5568: grfmsg(0, "NewSpriteGroup: No sprite set to work on! Skipping"); peter1138@3668: return; peter1138@3668: } peter1138@3668: peter1138@5841: if (!check_length(bufend - buf, 2 * num_loaded + 2 * num_loading, "NewSpriteGroup (Real) (1)")) return; peter1138@3668: peter1138@3668: group = AllocateSpriteGroup(); peter1138@3668: group->type = SGT_REAL; peter1138@3668: peter1138@3668: group->g.real.num_loaded = num_loaded; peter1138@3668: group->g.real.num_loading = num_loading; KUDr@5860: if (num_loaded > 0) group->g.real.loaded = CallocT(num_loaded); KUDr@5860: if (num_loading > 0) group->g.real.loading = CallocT(num_loading); peter1138@3668: Darkvater@5568: grfmsg(6, "NewSpriteGroup: New SpriteGroup 0x%02X, %u views, %u loaded, %u loading", peter1138@3668: setid, sprites, num_loaded, num_loading); peter1138@3668: peter1138@6607: for (uint i = 0; i < num_loaded; i++) { peter1138@3668: uint16 spriteid = grf_load_word(&buf); peter1138@3803: group->g.real.loaded[i] = CreateGroupFromGroupID(feature, setid, type, spriteid, sprites); Darkvater@5568: grfmsg(8, "NewSpriteGroup: + rg->loaded[%i] = subset %u", i, spriteid); peter1138@3668: } peter1138@3668: peter1138@6607: for (uint i = 0; i < num_loading; i++) { peter1138@3668: uint16 spriteid = grf_load_word(&buf); peter1138@3803: group->g.real.loading[i] = CreateGroupFromGroupID(feature, setid, type, spriteid, sprites); Darkvater@5568: grfmsg(8, "NewSpriteGroup: + rg->loading[%i] = subset %u", i, spriteid); peter1138@3668: } peter1138@3668: peter1138@3668: break; peter1138@3668: } peter1138@3668: belugas@7670: case GSF_TOWNHOUSE: belugas@7670: case GSF_INDUSTRYTILES: { maedhros@6658: byte sprites = _cur_grffile->spriteset_numents; maedhros@6658: byte num_sprites = max((uint8)1, type); maedhros@6658: uint i; maedhros@6658: maedhros@6658: group = AllocateSpriteGroup(); maedhros@6658: group->type = SGT_TILELAYOUT; maedhros@6658: group->g.layout.num_sprites = sprites; maedhros@6658: group->g.layout.dts = CallocT(1); maedhros@6658: maedhros@6658: /* Groundsprite */ frosch@9067: group->g.layout.dts->ground.sprite = grf_load_word(&buf); frosch@9067: group->g.layout.dts->ground.pal = grf_load_word(&buf); belugas@9073: maedhros@6658: /* Remap transparent/colour modifier bits */ belugas@9073: MapSpriteMappingRecolour(&group->g.layout.dts->ground); belugas@9073: frosch@9067: if (HasBit(group->g.layout.dts->ground.pal, 15)) { maedhros@6658: /* Bit 31 set means this is a custom sprite, so rewrite it to the maedhros@6658: * last spriteset defined. */ frosch@9067: SpriteID sprite = _cur_grffile->spriteset_start + GB(group->g.layout.dts->ground.sprite, 0, 14) * sprites; frosch@9067: SB(group->g.layout.dts->ground.sprite, 0, SPRITE_WIDTH, sprite); frosch@9067: ClrBit(group->g.layout.dts->ground.pal, 15); maedhros@6658: } maedhros@6658: maedhros@6658: group->g.layout.dts->seq = CallocT(num_sprites + 1); maedhros@6658: maedhros@6658: for (i = 0; i < num_sprites; i++) { maedhros@6658: DrawTileSeqStruct *seq = (DrawTileSeqStruct*)&group->g.layout.dts->seq[i]; maedhros@6658: frosch@9066: seq->image.sprite = grf_load_word(&buf); frosch@9066: seq->image.pal = grf_load_word(&buf); maedhros@6658: seq->delta_x = grf_load_byte(&buf); maedhros@6658: seq->delta_y = grf_load_byte(&buf); maedhros@6658: belugas@9073: MapSpriteMappingRecolour(&seq->image); belugas@9073: frosch@9066: if (HasBit(seq->image.pal, 15)) { maedhros@6658: /* Bit 31 set means this is a custom sprite, so rewrite it to the maedhros@6658: * last spriteset defined. */ frosch@9066: SpriteID sprite = _cur_grffile->spriteset_start + GB(seq->image.sprite, 0, 14) * sprites; frosch@9066: SB(seq->image.sprite, 0, SPRITE_WIDTH, sprite); frosch@9066: ClrBit(seq->image.pal, 15); maedhros@6658: } maedhros@6658: maedhros@6658: if (type > 0) { maedhros@6658: seq->delta_z = grf_load_byte(&buf); maedhros@6658: if ((byte)seq->delta_z == 0x80) continue; maedhros@6658: } maedhros@6658: maedhros@6658: seq->size_x = grf_load_byte(&buf); maedhros@6658: seq->size_y = grf_load_byte(&buf); maedhros@6658: seq->size_z = grf_load_byte(&buf); maedhros@6658: } maedhros@6658: maedhros@6658: /* Set the terminator value. */ maedhros@6658: ((DrawTileSeqStruct*)group->g.layout.dts->seq)[i].delta_x = (byte)0x80; maedhros@6658: maedhros@6658: break; maedhros@6658: } maedhros@6658: rubidium@7664: case GSF_INDUSTRIES: { rubidium@7664: if (type > 1) { rubidium@7664: grfmsg(1, "NewSpriteGroup: Unsupported industry production version %d, skipping", type); rubidium@7664: break; rubidium@7664: } rubidium@7664: rubidium@7664: group = AllocateSpriteGroup(); rubidium@7664: group->type = SGT_INDUSTRY_PRODUCTION; rubidium@7664: group->g.indprod.version = type; rubidium@7664: if (type == 0) { rubidium@7664: for (uint i = 0; i < 3; i++) { rubidium@7664: group->g.indprod.substract_input[i] = grf_load_word(&buf); rubidium@7664: } rubidium@7664: for (uint i = 0; i < 2; i++) { rubidium@7664: group->g.indprod.add_output[i] = grf_load_word(&buf); rubidium@7664: } rubidium@7664: group->g.indprod.again = grf_load_byte(&buf); rubidium@7664: } else { rubidium@7664: for (uint i = 0; i < 3; i++) { rubidium@7664: group->g.indprod.substract_input[i] = grf_load_byte(&buf); rubidium@7664: } rubidium@7664: for (uint i = 0; i < 2; i++) { rubidium@7664: group->g.indprod.add_output[i] = grf_load_byte(&buf); rubidium@7664: } rubidium@7664: group->g.indprod.again = grf_load_byte(&buf); rubidium@7664: } rubidium@7664: break; rubidium@7664: } rubidium@7664: peter1138@3668: /* Loading of Tile Layout and Production Callback groups would happen here */ Darkvater@5568: default: grfmsg(1, "NewSpriteGroup: Unsupported feature %d, skipping", feature); peter1138@3633: } peter1138@2489: } truelight@0: } peter1138@2444: peter1138@2444: _cur_grffile->spritegroups[setid] = group; truelight@0: } truelight@0: peter1138@6469: static CargoID TranslateCargo(uint8 feature, uint8 ctype) peter1138@6469: { peter1138@6469: /* Special cargo types for purchase list and stations */ peter1138@6474: if (feature == GSF_STATION && ctype == 0xFE) return CT_DEFAULT_NA; peter1138@6474: if (ctype == 0xFF) return CT_PURCHASE; peter1138@6469: peter1138@6687: if (_cur_grffile->cargo_max == 0) { peter1138@6687: /* No cargo table, so use bitnum values */ peter1138@6687: if (ctype >= 32) { belugas@6973: grfmsg(1, "TranslateCargo: Cargo bitnum %d out of range (max 31), skipping.", ctype); peter1138@6687: return CT_INVALID; peter1138@6687: } peter1138@6687: peter1138@6687: for (CargoID c = 0; c < NUM_CARGO; c++) { peter1138@6687: const CargoSpec *cs = GetCargo(c); peter1138@6687: if (!cs->IsValid()) continue; peter1138@6687: peter1138@6687: if (cs->bitnum == ctype) { belugas@6973: grfmsg(6, "TranslateCargo: Cargo bitnum %d mapped to cargo type %d.", ctype, c); peter1138@6687: return c; peter1138@6687: } peter1138@6687: } peter1138@6687: belugas@6973: grfmsg(5, "TranslateCargo: Cargo bitnum %d not available in this climate, skipping.", ctype); peter1138@6687: return CT_INVALID; peter1138@6687: } peter1138@6687: peter1138@6469: /* Check if the cargo type is out of bounds of the cargo translation table */ peter1138@6687: if (ctype >= _cur_grffile->cargo_max) { belugas@6973: grfmsg(1, "TranslateCargo: Cargo type %d out of range (max %d), skipping.", ctype, _cur_grffile->cargo_max - 1); peter1138@6469: return CT_INVALID; peter1138@6469: } peter1138@6469: peter1138@6469: /* Look up the cargo label from the translation table */ peter1138@6687: CargoLabel cl = _cur_grffile->cargo_list[ctype]; peter1138@6469: if (cl == 0) { belugas@6973: grfmsg(5, "TranslateCargo: Cargo type %d not available in this climate, skipping.", ctype); peter1138@6469: return CT_INVALID; peter1138@6469: } peter1138@6469: peter1138@6469: ctype = GetCargoIDByLabel(cl); peter1138@6469: if (ctype == CT_INVALID) { belugas@6973: 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)); peter1138@6469: return CT_INVALID; peter1138@6469: } peter1138@6469: belugas@6973: grfmsg(6, "TranslateCargo: 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); peter1138@6469: return ctype; peter1138@6469: } peter1138@6469: peter1138@6689: peter1138@10812: static bool IsValidGroupID(uint16 groupid, const char *function) peter1138@10812: { peter1138@10812: if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) { peter1138@10812: grfmsg(1, "%s: Spriteset 0x%04X out of range (maximum 0x%02X) or empty, skipping.", function, groupid, _cur_grffile->spritegroups_count - 1); peter1138@10812: return false; peter1138@10812: } peter1138@10812: peter1138@10812: return true; peter1138@10812: } peter1138@10812: peter1138@10813: static void VehicleMapSpriteGroup(byte *buf, byte feature, uint8 idcount) truelight@0: { peter1138@8694: static EngineID *last_engines; peter1138@8694: static uint last_engines_count; peter1138@10813: bool wagover = false; peter1138@10813: peter1138@10813: /* Test for 'wagon override' flag */ peter1138@10813: if (HasBit(idcount, 7)) { peter1138@10813: wagover = true; peter1138@10813: /* Strip off the flag */ peter1138@10813: idcount = GB(idcount, 0, 7); peter1138@10813: celestar@372: if (last_engines_count == 0) { belugas@6973: grfmsg(0, "VehicleMapSpriteGroup: WagonOverride: No engine to do override with"); celestar@372: return; celestar@372: } peter1138@6689: belugas@6973: grfmsg(6, "VehicleMapSpriteGroup: WagonOverride: %u engines, %u wagons", celestar@379: last_engines_count, idcount); peter1138@10813: } else { peter1138@10813: if (last_engines_count != idcount) { peter1138@10813: last_engines = ReallocT(last_engines, idcount); peter1138@10813: last_engines_count = idcount; peter1138@10813: } celestar@372: } darkvater@398: smatz@10905: EngineID *engines = AllocaM(EngineID, idcount); peter1138@6607: for (uint i = 0; i < idcount; i++) { peter1138@10929: engines[i] = GetNewEngine(_cur_grffile, (VehicleType)feature, grf_load_extended(&buf))->index; peter1138@10813: if (!wagover) last_engines[i] = engines[i]; peter1138@10813: } peter1138@10813: peter1138@10813: uint8 cidcount = grf_load_byte(&buf); peter1138@10813: for (uint c = 0; c < cidcount; c++) { peter1138@10813: uint8 ctype = grf_load_byte(&buf); peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10813: if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) continue; peter1138@10813: peter1138@10813: grfmsg(8, "VehicleMapSpriteGroup: * [%d] Cargo type 0x%X, group id 0x%02X", c, ctype, groupid); peter1138@10813: peter1138@10813: ctype = TranslateCargo(feature, ctype); peter1138@10813: if (ctype == CT_INVALID) continue; peter1138@10813: peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: EngineID engine = engines[i]; peter1138@10813: peter1138@10813: grfmsg(7, "VehicleMapSpriteGroup: [%d] Engine %d...", i, engine); peter1138@5338: celestar@379: if (wagover) { peter1138@4869: SetWagonOverrideSprites(engine, ctype, _cur_grffile->spritegroups[groupid], last_engines, last_engines_count); celestar@357: } else { peter1138@2444: SetCustomEngineSprites(engine, ctype, _cur_grffile->spritegroups[groupid]); celestar@357: } truelight@0: } celestar@357: } truelight@0: peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10813: if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) return; peter1138@10813: peter1138@10813: grfmsg(8, "-- Default group id 0x%04X", groupid); peter1138@10813: peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: EngineID engine = engines[i]; peter1138@10813: peter1138@10813: if (wagover) { peter1138@10813: SetWagonOverrideSprites(engine, CT_DEFAULT, _cur_grffile->spritegroups[groupid], last_engines, last_engines_count); peter1138@10813: } else { peter1138@10813: SetCustomEngineSprites(engine, CT_DEFAULT, _cur_grffile->spritegroups[groupid]); peter1138@10813: SetEngineGRF(engine, _cur_grffile); truelight@0: } truelight@0: } truelight@0: } truelight@0: peter1138@6689: peter1138@10813: static void CanalMapSpriteGroup(byte *buf, uint8 idcount) peter1138@7079: { smatz@10905: CanalFeature *cfs = AllocaM(CanalFeature, idcount); peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: cfs[i] = (CanalFeature)grf_load_byte(&buf); peter1138@10813: } peter1138@10813: peter1138@10813: uint8 cidcount = grf_load_byte(&buf); peter1138@10813: buf += cidcount * 3; peter1138@10813: peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10812: if (!IsValidGroupID(groupid, "CanalMapSpriteGroup")) return; peter1138@7079: peter1138@7079: for (uint i = 0; i < idcount; i++) { peter1138@10813: CanalFeature cf = cfs[i]; peter1138@7079: peter1138@7079: if (cf >= CF_END) { peter1138@7079: grfmsg(1, "CanalMapSpriteGroup: Canal subset %d out of range, skipping", cf); peter1138@7079: continue; peter1138@7079: } peter1138@7079: peter1138@8868: _water_feature[cf].group = _cur_grffile->spritegroups[groupid]; peter1138@7079: } peter1138@7079: } peter1138@7079: peter1138@7079: peter1138@10813: static void StationMapSpriteGroup(byte *buf, uint8 idcount) peter1138@6689: { smatz@10905: uint8 *stations = AllocaM(uint8, idcount); peter1138@6689: for (uint i = 0; i < idcount; i++) { peter1138@10813: stations[i] = grf_load_byte(&buf); peter1138@10813: } peter1138@10813: peter1138@10813: uint8 cidcount = grf_load_byte(&buf); peter1138@10813: for (uint c = 0; c < cidcount; c++) { peter1138@10813: uint8 ctype = grf_load_byte(&buf); peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10813: if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) continue; peter1138@10813: peter1138@10813: ctype = TranslateCargo(GSF_STATION, ctype); peter1138@10813: if (ctype == CT_INVALID) continue; peter1138@10813: peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: StationSpec *statspec = _cur_grffile->stations[stations[i]]; peter1138@10813: peter1138@10813: if (statspec == NULL) { peter1138@10813: grfmsg(1, "StationMapSpriteGroup: Station with ID 0x%02X does not exist, skipping", stations[i]); peter1138@10813: return; peter1138@10813: } peter1138@6689: peter1138@6689: statspec->spritegroup[ctype] = _cur_grffile->spritegroups[groupid]; peter1138@6689: } peter1138@6689: } peter1138@6689: peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10813: if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) return; peter1138@10813: peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: StationSpec *statspec = _cur_grffile->stations[stations[i]]; peter1138@10813: peter1138@10813: if (statspec == NULL) { peter1138@10813: grfmsg(1, "StationMapSpriteGroup: Station with ID 0x%02X does not exist, skipping", stations[i]); peter1138@10813: continue; peter1138@6689: } peter1138@10813: peter1138@10813: statspec->spritegroup[CT_DEFAULT] = _cur_grffile->spritegroups[groupid]; peter1138@10813: statspec->grffile = _cur_grffile; peter1138@10813: statspec->localidx = stations[i]; peter1138@10813: SetCustomStationSpec(statspec); peter1138@6689: } peter1138@6689: } peter1138@6689: peter1138@6689: peter1138@10813: static void TownHouseMapSpriteGroup(byte *buf, uint8 idcount) peter1138@6689: { smatz@10905: uint8 *houses = AllocaM(uint8, idcount); peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: houses[i] = grf_load_byte(&buf); peter1138@10813: } peter1138@10813: peter1138@10813: /* Skip the cargo type section, we only care about the default group */ peter1138@10813: uint8 cidcount = grf_load_byte(&buf); peter1138@10813: buf += cidcount * 3; peter1138@10813: peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10812: if (!IsValidGroupID(groupid, "TownHouseMapSpriteGroup")) return; peter1138@6689: peter1138@6689: for (uint i = 0; i < idcount; i++) { peter1138@10813: HouseSpec *hs = _cur_grffile->housespec[houses[i]]; peter1138@6689: peter1138@6689: if (hs == NULL) { belugas@6973: grfmsg(1, "TownHouseMapSpriteGroup: Too many houses defined, skipping"); peter1138@6689: return; peter1138@6689: } peter1138@6689: peter1138@6689: hs->spritegroup = _cur_grffile->spritegroups[groupid]; peter1138@6689: } peter1138@6689: } peter1138@6689: peter1138@10813: static void IndustryMapSpriteGroup(byte *buf, uint8 idcount) belugas@7713: { smatz@10905: uint8 *industries = AllocaM(uint8, idcount); peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: industries[i] = grf_load_byte(&buf); peter1138@10813: } peter1138@10813: peter1138@10813: /* Skip the cargo type section, we only care about the default group */ peter1138@10813: uint8 cidcount = grf_load_byte(&buf); peter1138@10813: buf += cidcount * 3; peter1138@10813: peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10812: if (!IsValidGroupID(groupid, "IndustryMapSpriteGroup")) return; belugas@7713: belugas@7713: for (uint i = 0; i < idcount; i++) { peter1138@10813: IndustrySpec *indsp = _cur_grffile->industryspec[industries[i]]; belugas@7713: belugas@7713: if (indsp == NULL) { belugas@7713: grfmsg(1, "IndustryMapSpriteGroup: Too many industries defined, skipping"); belugas@7713: return; belugas@7713: } belugas@7713: belugas@7713: indsp->grf_prop.spritegroup = _cur_grffile->spritegroups[groupid]; belugas@7713: } belugas@7713: } belugas@7713: peter1138@10813: static void IndustrytileMapSpriteGroup(byte *buf, uint8 idcount) belugas@7713: { smatz@10905: uint8 *indtiles = AllocaM(uint8, idcount); peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: indtiles[i] = grf_load_byte(&buf); peter1138@10813: } peter1138@10813: peter1138@10813: /* Skip the cargo type section, we only care about the default group */ peter1138@10813: uint8 cidcount = grf_load_byte(&buf); peter1138@10813: buf += cidcount * 3; peter1138@10813: peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10812: if (!IsValidGroupID(groupid, "IndustrytileMapSpriteGroup")) return; belugas@7713: belugas@7713: for (uint i = 0; i < idcount; i++) { peter1138@10813: IndustryTileSpec *indtsp = _cur_grffile->indtspec[indtiles[i]]; belugas@7713: belugas@7713: if (indtsp == NULL) { belugas@7713: grfmsg(1, "IndustrytileMapSpriteGroup: Too many industry tiles defined, skipping"); belugas@7713: return; belugas@7713: } belugas@7713: belugas@7713: indtsp->grf_prop.spritegroup = _cur_grffile->spritegroups[groupid]; belugas@7713: } belugas@7713: } peter1138@6691: peter1138@10813: static void CargoMapSpriteGroup(byte *buf, uint8 idcount) peter1138@6691: { smatz@10905: CargoID *cargos = AllocaM(CargoID, idcount); peter1138@10813: for (uint i = 0; i < idcount; i++) { peter1138@10813: cargos[i] = grf_load_byte(&buf); peter1138@10813: } peter1138@10813: peter1138@10813: /* Skip the cargo type section, we only care about the default group */ peter1138@10813: uint8 cidcount = grf_load_byte(&buf); peter1138@10813: buf += cidcount * 3; peter1138@10813: peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@10812: if (!IsValidGroupID(groupid, "CargoMapSpriteGroup")) return; peter1138@6691: peter1138@6691: for (uint i = 0; i < idcount; i++) { peter1138@10813: CargoID cid = cargos[i]; peter1138@6691: peter1138@6691: if (cid >= NUM_CARGO) { peter1138@10813: grfmsg(1, "CargoMapSpriteGroup: Cargo ID %d out of range, skipping", cid); peter1138@6691: continue; peter1138@6691: } peter1138@6691: peter1138@6691: CargoSpec *cs = &_cargo[cid]; peter1138@6691: cs->grfid = _cur_grffile->grfid; peter1138@6691: cs->group = _cur_grffile->spritegroups[groupid]; peter1138@6691: } peter1138@6691: } peter1138@6691: peter1138@6691: peter1138@6689: /* Action 0x03 */ glx@10465: static void FeatureMapSpriteGroup(byte *buf, size_t len) peter1138@6689: { peter1138@6689: /* <03> ... [ ]... peter1138@6689: * id-list := [] [id-list] peter1138@6689: * cargo-list := [cargo-list] peter1138@6689: * peter1138@6689: * B feature see action 0 peter1138@6689: * B n-id bits 0-6: how many IDs this definition applies to peter1138@6689: * bit 7: if set, this is a wagon override definition (see below) peter1138@6689: * B ids the IDs for which this definition applies peter1138@6689: * B num-cid number of cargo IDs (sprite group IDs) in this definition peter1138@6689: * can be zero, in that case the def-cid is used always peter1138@6689: * B cargo-type type of this cargo type (e.g. mail=2, wood=7, see below) peter1138@6689: * W cid cargo ID (sprite group ID) for this type of cargo peter1138@6689: * W def-cid default cargo ID (sprite group ID) */ peter1138@6689: peter1138@9040: if (_cur_grffile->spritegroups == 0) { peter1138@9040: grfmsg(1, "FeatureMapSpriteGroup: No sprite groups to work on! Skipping"); peter1138@9040: return; peter1138@9040: } peter1138@9040: peter1138@6689: if (!check_length(len, 6, "FeatureMapSpriteGroup")) return; peter1138@6689: peter1138@10813: buf++; peter1138@10813: uint8 feature = grf_load_byte(&buf); peter1138@10813: uint8 idcount = grf_load_byte(&buf); peter1138@6689: peter1138@6689: /* If idcount is zero, this is a feature callback */ peter1138@6689: if (idcount == 0) { peter1138@10813: /* Skip number of cargo ids? */ peter1138@10813: grf_load_byte(&buf); peter1138@10813: uint16 groupid = grf_load_word(&buf); peter1138@9040: peter1138@9040: grfmsg(6, "FeatureMapSpriteGroup: Adding generic feature callback for feature %d", feature); peter1138@9040: peter1138@9040: AddGenericCallback(feature, _cur_grffile, _cur_grffile->spritegroups[groupid]); peter1138@6689: return; peter1138@6689: } peter1138@6689: peter1138@10813: grfmsg(6, "FeatureMapSpriteGroup: Feature %d, %d ids", feature, idcount); peter1138@6689: peter1138@6689: switch (feature) { peter1138@6689: case GSF_TRAIN: peter1138@6689: case GSF_ROAD: peter1138@6689: case GSF_SHIP: peter1138@6689: case GSF_AIRCRAFT: peter1138@10813: VehicleMapSpriteGroup(buf, feature, idcount); peter1138@6689: return; peter1138@6689: peter1138@7079: case GSF_CANAL: peter1138@10813: CanalMapSpriteGroup(buf, idcount); peter1138@7079: return; peter1138@7079: peter1138@6689: case GSF_STATION: peter1138@10813: StationMapSpriteGroup(buf, idcount); peter1138@6689: return; peter1138@6689: peter1138@6689: case GSF_TOWNHOUSE: peter1138@10813: TownHouseMapSpriteGroup(buf, idcount); peter1138@6689: return; peter1138@6689: belugas@7713: case GSF_INDUSTRIES: peter1138@10813: IndustryMapSpriteGroup(buf, idcount); belugas@7713: return; belugas@7713: belugas@7713: case GSF_INDUSTRYTILES: peter1138@10813: IndustrytileMapSpriteGroup(buf, idcount); belugas@7713: return; belugas@7713: peter1138@6691: case GSF_CARGOS: peter1138@10813: CargoMapSpriteGroup(buf, idcount); peter1138@6691: return; peter1138@6691: peter1138@6689: default: peter1138@6689: grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature %d, skipping", feature); peter1138@6689: return; peter1138@6689: } peter1138@6689: } peter1138@6689: celestar@356: /* Action 0x04 */ glx@10465: static void FeatureNewName(byte *buf, size_t len) truelight@0: { truelight@0: /* <04> truelight@0: * belugas@3601: * B veh-type see action 0 (as 00..07, + 0A belugas@3601: * But IF veh-type = 48, then generic text belugas@3601: * B language-id If bit 6 is set, This is the extended language scheme, belugas@3601: with up to 64 language. belugas@3601: Otherwise, it is a mapping where set bits have meaning belugas@3601: 0 = american, 1 = english, 2 = german, 3 = french, 4 = spanish belugas@3601: Bit 7 set means this is a generic text, not a vehicle one (or else) truelight@0: * B num-veh number of vehicles which are getting a new name tron@2346: * B/W offset number of the first vehicle that gets a new name belugas@3601: * Byte : ID of vehicle to change belugas@3601: * Word : ID of string to change/add truelight@0: * S data new texts, each of them zero-terminated, after truelight@0: * which the next name begins. */ peter1138@6607: peter1138@3643: bool new_scheme = _cur_grffile->grf_version >= 7; truelight@0: peter1138@5841: if (!check_length(len, 6, "FeatureNewName")) return; tron@2346: buf++; peter1138@6607: uint8 feature = grf_load_byte(&buf); peter1138@6607: uint8 lang = grf_load_byte(&buf); peter1138@6607: uint8 num = grf_load_byte(&buf); skidd13@8424: bool generic = HasBit(lang, 7); peter1138@10929: uint16 id; peter1138@10929: if (generic) { peter1138@10929: id = grf_load_word(&buf); peter1138@10929: } else if (feature <= GSF_AIRCRAFT) { peter1138@10929: id = grf_load_extended(&buf); peter1138@10929: } else { peter1138@10929: id = grf_load_byte(&buf); peter1138@10929: } peter1138@4992: skidd13@8425: ClrBit(lang, 7); tron@2346: peter1138@6607: uint16 endid = id + num; truelight@0: Darkvater@5568: grfmsg(6, "FeatureNewName: About to rename engines %d..%d (feature %d) in language 0x%02X", celestar@379: id, endid, feature, lang); celestar@379: peter1138@4992: len -= generic ? 6 : 5; peter1138@4992: celestar@357: for (; id < endid && len > 0; id++) { maedhros@6416: const char *name = grf_load_string(&buf, len); maedhros@6416: size_t name_length = strlen(name) + 1; maedhros@6416: maedhros@6416: len -= (int)name_length; maedhros@6416: peter1138@7103: grfmsg(8, "FeatureNewName: 0x%04X <- %s", id, name); peter1138@7103: peter1138@7103: switch (feature) { peter1138@7103: case GSF_TRAIN: peter1138@7103: case GSF_ROAD: peter1138@7103: case GSF_SHIP: peter1138@7103: case GSF_AIRCRAFT: peter1138@10820: if (!generic) { peter1138@10382: Engine *e = GetNewEngine(_cur_grffile, (VehicleType)feature, id); peter1138@10382: StringID string = AddGRFString(_cur_grffile->grfid, e->index, lang, new_scheme, name, e->info.string_id); peter1138@10382: e->info.string_id = string; peter1138@7103: } else { peter1138@7103: AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, id); peter1138@3614: } peter1138@7103: break; peter1138@7103: belugas@7678: case GSF_INDUSTRIES: { belugas@7678: AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED); belugas@7678: break; belugas@7678: } belugas@7678: peter1138@7103: case GSF_TOWNHOUSE: peter1138@7103: default: peter1138@7103: switch (GB(id, 8, 8)) { peter1138@7103: case 0xC4: // Station class name peter1138@7103: if (_cur_grffile->stations == NULL || _cur_grffile->stations[GB(id, 0, 8)] == NULL) { peter1138@7103: grfmsg(1, "FeatureNewName: Attempt to name undefined station 0x%X, ignoring", GB(id, 0, 8)); peter1138@7103: } else { peter1138@7103: StationClassID sclass = _cur_grffile->stations[GB(id, 0, 8)]->sclass; peter1138@7103: SetStationClassName(sclass, AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED)); maedhros@6658: } peter1138@7103: break; peter1138@7103: peter1138@7103: case 0xC5: // Station name peter1138@7103: if (_cur_grffile->stations == NULL || _cur_grffile->stations[GB(id, 0, 8)] == NULL) { peter1138@7103: grfmsg(1, "FeatureNewName: Attempt to name undefined station 0x%X, ignoring", GB(id, 0, 8)); peter1138@7103: } else { peter1138@7103: _cur_grffile->stations[GB(id, 0, 8)]->name = AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED); peter1138@7103: } peter1138@7103: break; peter1138@7103: peter1138@7103: case 0xC9: // House name peter1138@7103: if (_cur_grffile->housespec == NULL || _cur_grffile->housespec[GB(id, 0, 8)] == NULL) { peter1138@7103: grfmsg(1, "FeatureNewName: Attempt to name undefined house 0x%X, ignoring.", GB(id, 0, 8)); peter1138@7103: } else { rubidium@10219: _cur_grffile->housespec[GB(id, 0, 8)]->building_name = AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED); peter1138@7103: } peter1138@7103: break; peter1138@7103: peter1138@7103: case 0xD0: peter1138@9151: case 0xD1: peter1138@9151: case 0xD2: peter1138@9151: case 0xD3: peter1138@7103: case 0xDC: peter1138@7103: AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED); peter1138@7103: break; peter1138@7103: peter1138@7103: default: peter1138@7103: grfmsg(7, "FeatureNewName: Unsupported ID (0x%04X)", id); peter1138@7103: break; peter1138@7103: } peter1138@7103: break; peter1138@3642: peter1138@3642: #if 0 belugas@3601: case GSF_CANAL : belugas@3601: case GSF_BRIDGE : belugas@3601: AddGRFString(_cur_spriteid, id, lang, name); rubidium@6987: switch (GB(id, 8, 8)) { belugas@6674: case 0xC9: // House name belugas@3601: default: Darkvater@5568: grfmsg(7, "FeatureNewName: Unsupported ID (0x%04X)", id); belugas@3601: } belugas@3601: break; belugas@3601: belugas@3601: default : Darkvater@5568: grfmsg(7, "FeatureNewName: Unsupported feature (0x%02X)", feature); belugas@3601: break; belugas@3601: #endif celestar@379: } truelight@0: } truelight@0: } truelight@0: rubidium@8378: /** rubidium@8378: * Sanitize incoming sprite offsets for Action 5 graphics replacements. rubidium@8378: * @param num the number of sprites to load. rubidium@8378: * @param offset offset from the base. rubidium@8378: * @param max_sprites the maximum number of sprites that can be loaded in this action 5. rubidium@8378: * @param name used for error warnings. rubidium@8378: * @return the number of sprites that is going to be skipped rubidium@8378: */ rubidium@8378: static uint16 SanitizeSpriteOffset(uint16& num, uint16 offset, int max_sprites, const char *name) rubidium@8378: { rubidium@8378: rubidium@8378: if (offset >= max_sprites) { rubidium@8378: grfmsg(1, "GraphicsNew: %s sprite offset must be less than %i, skipping", name, max_sprites); rubidium@8378: uint orig_num = num; rubidium@8378: num = 0; rubidium@8378: return orig_num; rubidium@8378: } rubidium@8378: rubidium@8378: if (offset + num > max_sprites) { rubidium@8378: grfmsg(4, "GraphicsNew: %s sprite overflow, truncating...", name); rubidium@8378: uint orig_num = num; rubidium@8378: num = max(max_sprites - offset, 0); rubidium@8378: return orig_num - num; rubidium@8378: } rubidium@8378: rubidium@8378: return 0; rubidium@8378: } rubidium@8378: celestar@356: /* Action 0x05 */ glx@10465: static void GraphicsNew(byte *buf, size_t len) truelight@0: { truelight@0: /* <05> truelight@0: * truelight@0: * B graphics-type What set of graphics the sprites define. tron@2346: * E num-sprites How many sprites are in this set? truelight@0: * V other data Graphics type specific data. Currently unused. */ truelight@0: /* TODO */ celestar@356: frosch@8739: enum Action5BlockType { frosch@8739: A5BLOCK_FIXED, ///< Only allow replacing a whole block of sprites. (TTDP compatible) frosch@8739: A5BLOCK_ALLOW_OFFSET, ///< Allow replacing any subset by specifiing an offset. frosch@8739: A5BLOCK_INVALID, ///< unknown/not-implemented type frosch@8739: }; frosch@8739: struct Action5Type { frosch@8739: Action5BlockType block_type; ///< How is this Action5 type processed? frosch@8739: SpriteID sprite_base; ///< Load the sprites starting from this sprite. frosch@8739: uint16 min_sprites; ///< If the Action5 contains less sprites, the whole block will be ignored. frosch@8739: uint16 max_sprites; ///< If the Action5 contains more sprites, only the first max_sprites sprites will be used. frosch@8739: const char *name; ///< Name for error messages. frosch@8739: }; frosch@8739: frosch@8739: static const Action5Type action5_types[] = { frosch@8739: /* Note: min_sprites should not be changed. Therefore these constants are directly here and not in sprites.h */ rubidium@10917: /* 0x00 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x00" }, rubidium@10917: /* 0x01 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x01" }, rubidium@10917: /* 0x02 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x02" }, rubidium@10917: /* 0x03 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x03" }, rubidium@10917: /* 0x04 */ { A5BLOCK_FIXED, SPR_SIGNALS_BASE, 48, PRESIGNAL_SEMAPHORE_AND_PBS_SPRITE_COUNT, "Signal graphics" }, rubidium@10917: /* 0x05 */ { A5BLOCK_FIXED, SPR_ELRAIL_BASE, 48, ELRAIL_SPRITE_COUNT, "Catenary graphics" }, rubidium@10917: /* 0x06 */ { A5BLOCK_FIXED, SPR_SLOPES_BASE, 74, NORMAL_AND_HALFTILE_FOUNDATION_SPRITE_COUNT, "Foundation graphics" }, rubidium@10917: /* 0x07 */ { A5BLOCK_INVALID, 0, 75, 0, "TTDP GUI graphics" }, // Not used by OTTD. rubidium@10917: /* 0x08 */ { A5BLOCK_FIXED, SPR_CANALS_BASE, 65, CANALS_SPRITE_COUNT, "Canal graphics" }, rubidium@10917: /* 0x09 */ { A5BLOCK_FIXED, SPR_ONEWAY_BASE, 6, ONEWAY_SPRITE_COUNT, "One way road graphics" }, rubidium@10917: /* 0x0A */ { A5BLOCK_FIXED, SPR_2CCMAP_BASE, 256, TWOCCMAP_SPRITE_COUNT, "2CC colour maps" }, rubidium@10917: /* 0x0B */ { A5BLOCK_FIXED, SPR_TRAMWAY_BASE, 113, TRAMWAY_SPRITE_COUNT, "Tramway graphics" }, rubidium@10917: /* 0x0C */ { A5BLOCK_INVALID, 0, 133, 0, "Snowy temperate tree" }, // Not yet used by OTTD. rubidium@10917: /* 0x0D */ { A5BLOCK_FIXED, SPR_SHORE_BASE, 16, SPR_SHORE_SPRITE_COUNT, "Shore graphics" }, rubidium@10917: /* 0x0E */ { A5BLOCK_INVALID, 0, 0, 0, "New Signals graphics" }, // Not yet used by OTTD. rubidium@10917: /* 0x0F */ { A5BLOCK_FIXED, SPR_TRACKS_FOR_SLOPES_BASE, 12, TRACKS_FOR_SLOPES_SPRITE_COUNT, "Sloped rail track" }, rubidium@10917: /* 0x10 */ { A5BLOCK_FIXED, SPR_AIRPORTX_BASE, 15, AIRPORTX_SPRITE_COUNT, "Airport graphics" }, rubidium@10917: /* 0x11 */ { A5BLOCK_FIXED, SPR_ROADSTOP_BASE, 8, ROADSTOP_SPRITE_COUNT, "Road stop graphics" }, rubidium@10917: /* 0x12 */ { A5BLOCK_FIXED, SPR_AQUEDUCT_BASE, 8, AQUEDUCT_SPRITE_COUNT, "Aqueduct graphics" }, rubidium@10917: /* 0x13 */ { A5BLOCK_FIXED, SPR_AUTORAIL_BASE, 55, AUTORAIL_SPRITE_COUNT, "Autorail graphics" }, rubidium@10917: /* 0x14 */ { A5BLOCK_ALLOW_OFFSET, SPR_FLAGS_BASE, 1, FLAGS_SPRITE_COUNT, "Flag graphics" }, rubidium@10917: /* 0x15 */ { A5BLOCK_ALLOW_OFFSET, SPR_OPENTTD_BASE, 1, OPENTTD_SPRITE_COUNT, "OpenTTD GUI graphics" }, frosch@8739: }; celestar@356: peter1138@5841: if (!check_length(len, 2, "GraphicsNew")) return; tron@2346: buf++; peter1138@6607: uint8 type = grf_load_byte(&buf); peter1138@6607: uint16 num = grf_load_extended(&buf); skidd13@8424: uint16 offset = HasBit(type, 7) ? grf_load_extended(&buf) : 0; skidd13@8425: ClrBit(type, 7); // Clear the high bit as that only indicates whether there is an offset. celestar@356: frosch@8739: if ((type == 0x0D) && (num == 10) && _cur_grffile->is_ottdfile) { frosch@8739: /* Special not-TTDP-compatible case used in openttd(d/w).grf frosch@8739: * Missing shore sprites and initialisation of SPR_SHORE_BASE */ frosch@8739: grfmsg(2, "GraphicsNew: Loading 10 missing shore sprites from openttd(d/w).grf."); peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 0, _file_index, _nfo_line++); // SLOPE_STEEP_S peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 5, _file_index, _nfo_line++); // SLOPE_STEEP_W peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 7, _file_index, _nfo_line++); // SLOPE_WSE peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 10, _file_index, _nfo_line++); // SLOPE_STEEP_N peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 11, _file_index, _nfo_line++); // SLOPE_NWS peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 13, _file_index, _nfo_line++); // SLOPE_ENW peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 14, _file_index, _nfo_line++); // SLOPE_SEN peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 15, _file_index, _nfo_line++); // SLOPE_STEEP_E peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 16, _file_index, _nfo_line++); // SLOPE_EW peter1138@10821: LoadNextSprite(SPR_SHORE_BASE + 17, _file_index, _nfo_line++); // SLOPE_NS frosch@8899: if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ONLY_NEW; frosch@8739: return; peter1138@3638: } peter1138@3638: frosch@8739: /* Supported type? */ frosch@8739: if ((type >= lengthof(action5_types)) || (action5_types[type].block_type == A5BLOCK_INVALID)) { frosch@8739: grfmsg(2, "GraphicsNew: Custom graphics (type 0x%02X) sprite block of length %u (unimplemented, ignoring)", type, num); frosch@8739: _skip_sprites = num; frosch@8739: return; peter1138@3708: } peter1138@3638: frosch@8739: const Action5Type *action5_type = &action5_types[type]; frosch@8739: frosch@8739: /* Ignore offset if not allowed */ frosch@8739: if ((action5_type->block_type != A5BLOCK_ALLOW_OFFSET) && (offset != 0)) { frosch@8739: grfmsg(1, "GraphicsNew: %s (type 0x%02X) do not allow an field. Ignoring offset.", action5_type->name, type); frosch@8739: offset = 0; frosch@8739: } frosch@8739: frosch@8739: /* Ignore action5 if too few sprites are specified. (for TTDP compatibility) frosch@8739: * This does not make sense, if is allowed */ frosch@8739: if ((action5_type->block_type == A5BLOCK_FIXED) && (num < action5_type->min_sprites)) { frosch@8739: grfmsg(1, "GraphicsNew: %s (type 0x%02X) count must be at least %d. Only %d were specified. Skipping.", action5_type->name, type, action5_type->min_sprites, num); frosch@8739: _skip_sprites = num; frosch@8739: return; frosch@8739: } frosch@8739: frosch@8739: /* Load at most max_sprites sprites. Skip remaining sprites. (for compatibility with TTDP and future extentions) */ frosch@8739: uint16 skip_num = SanitizeSpriteOffset(num, offset, action5_type->max_sprites, action5_type->name); frosch@8739: SpriteID replace = action5_type->sprite_base + offset; frosch@8739: frosch@8739: /* Load sprites starting from , then skip sprites. */ frosch@8739: grfmsg(2, "GraphicsNew: Replacing sprites %d to %d of %s (type 0x%02X) at SpriteID 0x%04X", offset, offset + num - 1, action5_type->name, type, replace); frosch@8739: peter1138@3638: for (; num > 0; num--) { rubidium@8449: _nfo_line++; truelight@7404: LoadNextSprite(replace == 0 ? _cur_spriteid++ : replace++, _file_index, _nfo_line); peter1138@3638: } rubidium@8378: frosch@8899: if (type == 0x0D) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_5; frosch@8899: rubidium@8378: _skip_sprites = skip_num; truelight@0: } truelight@0: peter1138@7025: /* Action 0x05 (SKIP) */ glx@10465: static void SkipAct5(byte *buf, size_t len) peter1138@7025: { peter1138@7025: if (!check_length(len, 2, "SkipAct5")) return; peter1138@7025: buf++; peter1138@7025: peter1138@7025: /* Ignore type byte */ peter1138@7025: grf_load_byte(&buf); peter1138@7025: peter1138@7025: /* Skip the sprites of this action */ peter1138@7025: _skip_sprites = grf_load_extended(&buf); peter1138@7025: peter1138@7025: grfmsg(3, "SkipAct5: Skipping %d sprites", _skip_sprites); peter1138@7025: } peter1138@7025: frosch@9042: /** frosch@9042: * Reads a variable common to VarAction2 and Action7/9/D. frosch@9042: * frosch@9042: * Returns VarAction2 variable 'param' resp. Action7/9/D variable '0x80 + param'. frosch@9042: * If a variable is not accessible from all four actions, it is handled in the action specific functions. frosch@9042: * frosch@9042: * @param param variable number (as for VarAction2, for Action7/9/D you have to subtract 0x80 first). frosch@9042: * @param value returns the value of the variable. frosch@9042: * @return true iff the variable is known and the value is returned in 'value'. frosch@9042: */ frosch@9042: bool GetGlobalVariable(byte param, uint32 *value) peter1138@3806: { peter1138@3806: switch (param) { frosch@9042: case 0x00: // current date frosch@9042: *value = max(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0); frosch@9042: return true; frosch@9042: frosch@9042: case 0x01: // current year frosch@9042: *value = Clamp(_cur_year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR; frosch@9042: return true; frosch@9042: frosch@11038: case 0x02: { // detailed date information: month of year (bit 0-7), day of month (bit 8-12), leap year (bit 15), day of year (bit 16-24) frosch@11038: YearMonthDay ymd; frosch@11038: ConvertDateToYMD(_date, &ymd); frosch@11038: Date start_of_year = ConvertYMDToDate(ymd.year, 0, 1); frosch@11038: *value = ymd.month | (ymd.day - 1) << 8 | (IsLeapYear(ymd.year) ? 1 << 15 : 0) | (_date - start_of_year) << 16; frosch@9042: return true; frosch@11038: } frosch@9042: frosch@9042: case 0x03: // current climate, 0=temp, 1=arctic, 2=trop, 3=toyland rubidium@10775: *value = _settings_game.game_creation.landscape; frosch@9042: return true; frosch@9042: frosch@9042: case 0x06: // road traffic side, bit 4 clear=left, set=right rubidium@10775: *value = _settings_game.vehicle.road_side << 4; frosch@9042: return true; frosch@9042: frosch@9042: case 0x09: // date fraction frosch@9042: *value = _date_fract; frosch@9042: return true; frosch@9042: frosch@9042: case 0x0A: // animation counter frosch@9042: *value = _tick_counter; frosch@9042: return true; frosch@9042: frosch@9042: case 0x0B: { // TTDPatch version frosch@9042: uint major = 2; frosch@9042: uint minor = 6; frosch@9042: uint revision = 1; // special case: 2.0.1 is 2.0.10 frosch@9042: uint build = 1382; frosch@9042: *value = (major << 24) | (minor << 20) | (revision << 16) | build; frosch@9042: return true; frosch@9042: } frosch@9042: frosch@9042: case 0x0D: // TTD Version, 00=DOS, 01=Windows frosch@9042: *value = !_use_dos_palette; frosch@9042: return true; frosch@9042: frosch@9042: case 0x0E: // Y-offset for train sprites frosch@9042: *value = _traininfo_vehicle_pitch; frosch@9042: return true; frosch@9042: frosch@9042: case 0x0F: // Rail track type cost factors frosch@9042: *value = 0; frosch@9042: SB(*value, 0, 8, _railtype_cost_multiplier[0]); // normal rail rubidium@10775: if (_settings_game.vehicle.disable_elrails) { frosch@9042: /* skip elrail multiplier - disabled */ frosch@9042: SB(*value, 8, 8, _railtype_cost_multiplier[2]); // monorail frosch@9042: } else { frosch@9042: SB(*value, 8, 8, _railtype_cost_multiplier[1]); // electified railway frosch@9042: /* Skip monorail multiplier - no space in result */ frosch@9042: } frosch@9042: SB(*value, 16, 8, _railtype_cost_multiplier[3]); // maglev frosch@9042: return true; frosch@9042: frosch@9042: case 0x11: // current rail tool type frosch@9042: *value = 0; frosch@9042: return true; frosch@9042: frosch@9042: case 0x12: // Game mode frosch@9042: *value = _game_mode; frosch@9042: return true; frosch@9042: frosch@9042: /* case 0x13: // Tile refresh offset to left not implemented */ frosch@9042: /* case 0x14: // Tile refresh offset to right not implemented */ frosch@9042: /* case 0x15: // Tile refresh offset upwards not implemented */ frosch@9042: /* case 0x16: // Tile refresh offset downwards not implemented */ frosch@9042: /* case 0x17: // temperate snow line not implemented */ frosch@9042: frosch@9042: case 0x1A: // Always -1 frosch@9042: *value = UINT_MAX; frosch@9042: return true; frosch@9042: frosch@9042: case 0x1B: // Display options frosch@9042: *value = GB(_display_opt, 0, 6); frosch@9042: return true; frosch@9042: frosch@9042: case 0x1D: // TTD Platform, 00=TTDPatch, 01=OpenTTD frosch@9042: *value = 1; frosch@9042: return true; frosch@9042: frosch@9042: case 0x1E: // Miscellaneous GRF features frosch@9042: *value = _misc_grf_features; frosch@9042: return true; frosch@9042: frosch@9042: /* case 0x1F: // locale dependent settings not implemented */ frosch@9042: frosch@9042: case 0x20: // snow line height rubidium@10775: *value = _settings_game.game_creation.landscape == LT_ARCTIC ? GetSnowLine() : 0xFF; frosch@9042: return true; frosch@9042: smatz@10402: case 0x21: // OpenTTD version frosch@9042: *value = _openttd_newgrf_version; frosch@9042: return true; frosch@9042: belugas@9249: case 0x22: // difficulty level rubidium@10775: *value = _settings_game.difficulty.diff_level; belugas@9249: return true; belugas@9249: belugas@10825: case 0x23: // long format date belugas@10825: *value = _date; belugas@10825: return true; belugas@10825: belugas@10825: case 0x24: // long format year belugas@10825: *value = _cur_year; belugas@10825: return true; belugas@10825: frosch@9042: default: return false; frosch@9042: } frosch@9042: } frosch@9042: frosch@9042: static uint32 GetParamVal(byte param, uint32 *cond_val) frosch@9042: { frosch@9042: /* First handle variable common with VarAction2 */ frosch@9042: uint32 value; frosch@9042: if (GetGlobalVariable(param - 0x80, &value)) return value; frosch@9042: frosch@9042: /* Non-common variable */ frosch@9042: switch (param) { peter1138@7309: case 0x84: { // GRF loading stage peter1138@7309: uint32 res = 0; peter1138@7309: skidd13@8427: if (_cur_stage > GLS_INIT) SetBit(res, 0); skidd13@8427: if (_cur_stage == GLS_RESERVE) SetBit(res, 8); skidd13@8427: if (_cur_stage == GLS_ACTIVATION) SetBit(res, 9); peter1138@7309: return res; peter1138@7309: } peter1138@3806: belugas@6674: case 0x85: // TTDPatch flags, only for bit tests peter1138@3811: if (cond_val == NULL) { peter1138@3811: /* Supported in Action 0x07 and 0x09, not 0x0D */ peter1138@3811: return 0; peter1138@3811: } else { peter1138@3811: uint32 param_val = _ttdpatch_flags[*cond_val / 0x20]; peter1138@3811: *cond_val %= 0x20; peter1138@3811: return param_val; peter1138@3811: } peter1138@3806: belugas@6674: case 0x88: // GRF ID check peter1138@3806: return 0; peter1138@3806: frosch@9042: /* case 0x99: Global ID offest not implemented */ rubidium@8276: peter1138@3806: default: peter1138@3806: /* GRF Parameter */ peter1138@3806: if (param < 0x80) return _cur_grffile->param[param]; peter1138@3806: peter1138@3806: /* In-game variable. */ Darkvater@5568: grfmsg(1, "Unsupported in-game variable 0x%02X", param); rubidium@5838: return UINT_MAX; peter1138@3806: } peter1138@3806: } peter1138@3806: peter1138@3895: /* Action 0x06 */ glx@10465: static void CfgApply(byte *buf, size_t len) peter1138@3895: { peter1138@3895: /* <06> ... peter1138@3895: * peter1138@3895: * B param-num Number of parameter to substitute (First = "zero") peter1138@3895: * Ignored if that parameter was not specified in newgrf.cfg peter1138@3895: * B param-size How many bytes to replace. If larger than 4, the peter1138@3895: * bytes of the following parameter are used. In that peter1138@3895: * case, nothing is applied unless *all* parameters peter1138@3895: * were specified. peter1138@3895: * B offset Offset into data from beginning of next sprite peter1138@3895: * to place where parameter is to be stored. */ peter1138@3895: peter1138@3895: /* Preload the next sprite */ rubidium@10751: size_t pos = FioGetPos(); peter1138@3895: uint16 num = FioReadWord(); peter1138@3895: uint8 type = FioReadByte(); peter1138@3895: peter1138@3895: /* Check if the sprite is a pseudo sprite. We can't operate on real sprites. */ peter1138@3895: if (type == 0xFF) { KUDr@5860: _preload_sprite = MallocT(num); peter1138@3895: FioReadBlock(_preload_sprite, num); peter1138@3895: } peter1138@3895: peter1138@3895: /* Reset the file position to the start of the next sprite */ peter1138@3895: FioSeekTo(pos, SEEK_SET); peter1138@3895: peter1138@3895: if (type != 0xFF) { Darkvater@5568: grfmsg(2, "CfgApply: Ignoring (next sprite is real, unsupported)"); peter1138@3895: return; peter1138@3895: } peter1138@3895: peter1138@3895: /* Now perform the Action 0x06 on our data. */ peter1138@3895: buf++; peter1138@3895: peter1138@3895: for (;;) { peter1138@3895: uint i; peter1138@3895: uint param_num; peter1138@3895: uint param_size; peter1138@3895: uint offset; peter1138@3895: bool add_value; peter1138@3895: peter1138@3895: /* Read the parameter to apply. 0xFF indicates no more data to change. */ peter1138@3895: param_num = grf_load_byte(&buf); peter1138@3895: if (param_num == 0xFF) break; peter1138@3895: peter1138@3895: /* Get the size of the parameter to use. If the size covers multiple peter1138@3895: * double words, sequential parameter values are used. */ peter1138@3895: param_size = grf_load_byte(&buf); peter1138@3895: peter1138@3895: /* Bit 7 of param_size indicates we should add to the original value peter1138@3895: * instead of replacing it. */ skidd13@8424: add_value = HasBit(param_size, 7); peter1138@3895: param_size = GB(param_size, 0, 7); peter1138@3895: peter1138@3895: /* Where to apply the data to within the pseudo sprite data. */ peter1138@3895: offset = grf_load_extended(&buf); peter1138@3895: peter1138@3895: /* If the parameter is a GRF parameter (not an internal variable) check peter1138@3895: * if it (and all further sequential parameters) has been defined. */ peter1138@3895: if (param_num < 0x80 && (param_num + (param_size - 1) / 4) >= _cur_grffile->param_end) { Darkvater@5568: grfmsg(2, "CfgApply: Ignoring (param %d not set)", (param_num + (param_size - 1) / 4)); peter1138@3895: break; peter1138@3895: } peter1138@3895: Darkvater@5568: grfmsg(8, "CfgApply: Applying %u bytes from parameter 0x%02X at offset 0x%04X", param_size, param_num, offset); peter1138@3895: peter1138@7148: bool carry = false; peter1138@3895: for (i = 0; i < param_size; i++) { peter1138@3895: uint32 value = GetParamVal(param_num + i / 4, NULL); peter1138@7148: /* Reset carry flag for each iteration of the variable (only really peter1138@7148: * matters if param_size is greater than 4) */ peter1138@7148: if (i == 0) carry = false; peter1138@3895: peter1138@3895: if (add_value) { peter1138@7148: uint new_value = _preload_sprite[offset + i] + GB(value, (i % 4) * 8, 8) + (carry ? 1 : 0); peter1138@7148: _preload_sprite[offset + i] = GB(new_value, 0, 8); peter1138@7148: /* Check if the addition overflowed */ peter1138@7148: carry = new_value >= 256; peter1138@3895: } else { peter1138@3895: _preload_sprite[offset + i] = GB(value, (i % 4) * 8, 8); peter1138@3895: } peter1138@3895: } peter1138@3895: } peter1138@3895: } peter1138@3895: rubidium@11020: /** rubidium@11020: * Disable a static NewGRF when it is influencing another (non-static) rubidium@11020: * NewGRF as this could cause desyncs. rubidium@11020: * rubidium@11020: * We could just tell the NewGRF querying that the file doesn't exist, rubidium@11020: * but that might give unwanted results. Disabling the NewGRF gives the rubidium@11020: * best result as no NewGRF author can complain about that. rubidium@11020: * @param c the NewGRF to disable. rubidium@11020: */ rubidium@11020: static void DisableStaticNewGRFInfluencingNonStaticNewGRFs(GRFConfig *c) rubidium@11020: { rubidium@11020: if (c->error != NULL) { rubidium@11020: free(c->error->custom_message); rubidium@11020: free(c->error->data); rubidium@11020: free(c->error); rubidium@11020: } rubidium@11020: c->status = GCS_DISABLED; rubidium@11020: c->error = CallocT(1); rubidium@11020: c->error->data = strdup(_cur_grfconfig->name); rubidium@11020: c->error->severity = STR_NEWGRF_ERROR_MSG_FATAL; rubidium@11020: c->error->message = STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC; rubidium@11117: rubidium@11117: ClearTemporaryNewGRFData(); rubidium@11117: _skip_sprites = -1; rubidium@11020: } rubidium@11020: celestar@356: /* Action 0x07 */ celestar@356: /* Action 0x09 */ glx@10465: static void SkipIf(byte *buf, size_t len) truelight@0: { truelight@0: /* <07/09> truelight@0: * truelight@0: * B param-num truelight@0: * B param-size truelight@0: * B condition-type truelight@0: * V value truelight@0: * B num-sprites */ truelight@0: /* TODO: More params. More condition types. */ tron@2336: uint32 cond_val = 0; peter1138@3806: uint32 mask = 0; darkvater@360: bool result; truelight@0: peter1138@5841: if (!check_length(len, 6, "SkipIf")) return; peter1138@3714: buf++; peter1138@6607: uint8 param = grf_load_byte(&buf); peter1138@6607: uint8 paramsize = grf_load_byte(&buf); peter1138@6607: uint8 condtype = grf_load_byte(&buf); truelight@0: darkvater@359: if (condtype < 2) { tron@1611: /* Always 1 for bit tests, the given value should be ignored. */ darkvater@359: paramsize = 1; darkvater@359: } darkvater@359: tron@1611: switch (paramsize) { peter1138@3806: case 4: cond_val = grf_load_dword(&buf); mask = 0xFFFFFFFF; break; peter1138@3806: case 2: cond_val = grf_load_word(&buf); mask = 0x0000FFFF; break; peter1138@3806: case 1: cond_val = grf_load_byte(&buf); mask = 0x000000FF; break; tron@1611: default: break; tron@1611: } darkvater@359: peter1138@3806: if (param < 0x80 && _cur_grffile->param_end <= param) { belugas@6973: grfmsg(7, "SkipIf: Param %d undefined, skipping test", param); peter1138@3806: return; celestar@357: } truelight@0: peter1138@6607: uint32 param_val = GetParamVal(param, &cond_val); peter1138@3806: belugas@6973: grfmsg(7, "SkipIf: Test condtype %d, param 0x%08X, condval 0x%08X", condtype, param_val, cond_val); peter1138@5703: rubidium@8304: /* rubidium@8304: * Parameter (variable in specs) 0x88 can only have GRF ID checking rubidium@8304: * conditions, except conditions 0x0B and 0x0C (cargo availability) rubidium@8304: * as those ignore the parameter. So, when the condition type is rubidium@8304: * either of those, the specific variable 0x88 code is skipped, so rubidium@8304: * the "general" code for the cargo availability conditions kicks in. rubidium@8304: */ rubidium@8304: if (param == 0x88 && condtype != 0x0B && condtype != 0x0C) { peter1138@5703: /* GRF ID checks */ peter1138@5703: rubidium@11020: GRFConfig *c = GetGRFConfig(cond_val); rubidium@11020: rubidium@11020: if (c != NULL && HasBit(c->flags, GCF_STATIC) && !HasBit(_cur_grfconfig->flags, GCF_STATIC) && c->status != GCS_DISABLED && _networking) { rubidium@11020: DisableStaticNewGRFInfluencingNonStaticNewGRFs(c); rubidium@11020: c = NULL; rubidium@11020: } peter1138@5703: peter1138@5703: if (condtype != 10 && c == NULL) { belugas@6973: grfmsg(7, "SkipIf: GRFID 0x%08X unknown, skipping test", BSWAP32(cond_val)); peter1138@5703: return; peter1138@5234: } peter1138@5234: peter1138@5703: switch (condtype) { rubidium@8304: /* Tests 0x06 to 0x0A are only for param 0x88, GRFID checks */ rubidium@8304: case 0x06: // Is GRFID active? maedhros@6555: result = c->status == GCS_ACTIVATED; peter1138@5703: break; peter1138@5703: rubidium@8304: case 0x07: // Is GRFID non-active? maedhros@6555: result = c->status != GCS_ACTIVATED; peter1138@5703: break; peter1138@5703: rubidium@8304: case 0x08: // GRFID is not but will be active? maedhros@6555: result = c->status == GCS_INITIALISED; peter1138@5703: break; peter1138@5703: rubidium@8304: case 0x09: // GRFID is or will be active? maedhros@6555: result = c->status == GCS_ACTIVATED || c->status == GCS_INITIALISED; peter1138@5703: break; peter1138@5703: rubidium@8304: case 0x0A: // GRFID is not nor will be active peter1138@5703: /* This is the only condtype that doesn't get ignored if the GRFID is not found */ maedhros@6555: result = c == NULL || c->flags == GCS_DISABLED || c->status == GCS_NOT_FOUND; peter1138@5703: break; peter1138@5703: rubidium@8304: default: grfmsg(1, "SkipIf: Unsupported GRF condition type %02X. Ignoring", condtype); return; peter1138@5234: } peter1138@5703: } else { peter1138@5703: /* Parameter or variable tests */ peter1138@5703: switch (condtype) { rubidium@8304: case 0x00: result = !!(param_val & (1 << cond_val)); rubidium@8304: break; rubidium@8304: case 0x01: result = !(param_val & (1 << cond_val)); rubidium@8304: break; rubidium@8304: case 0x02: result = (param_val & mask) == cond_val; rubidium@8304: break; rubidium@8304: case 0x03: result = (param_val & mask) != cond_val; rubidium@8304: break; rubidium@8304: case 0x04: result = (param_val & mask) < cond_val; rubidium@8304: break; rubidium@8304: case 0x05: result = (param_val & mask) > cond_val; rubidium@8304: break; rubidium@8304: case 0x0B: result = GetCargoIDByLabel(BSWAP32(cond_val)) == CT_INVALID; rubidium@8304: break; rubidium@8304: case 0x0C: result = GetCargoIDByLabel(BSWAP32(cond_val)) != CT_INVALID; rubidium@8304: break; rubidium@8304: rubidium@8304: default: grfmsg(1, "SkipIf: Unsupported condition type %02X. Ignoring", condtype); return; peter1138@5234: } celestar@357: } celestar@357: pasky@1607: if (!result) { belugas@6973: grfmsg(2, "SkipIf: Not skipping sprites, test was false"); celestar@357: return; darkvater@359: } darkvater@359: peter1138@6607: uint8 numsprites = grf_load_byte(&buf); Darkvater@3561: Darkvater@3561: /* numsprites can be a GOTO label if it has been defined in the GRF Darkvater@3561: * file. The jump will always be the first matching label that follows Darkvater@3561: * the current nfo_line. If no matching label is found, the first matching Darkvater@3561: * label in the file is used. */ peter1138@6607: GRFLabel *choice = NULL; peter1138@6607: for (GRFLabel *label = _cur_grffile->label; label != NULL; label = label->next) { Darkvater@3561: if (label->label != numsprites) continue; Darkvater@3561: Darkvater@3561: /* Remember a goto before the current line */ Darkvater@3561: if (choice == NULL) choice = label; Darkvater@3561: /* If we find a label here, this is definitely good */ Darkvater@3561: if (label->nfo_line > _nfo_line) { Darkvater@3561: choice = label; Darkvater@3561: break; Darkvater@3561: } Darkvater@3561: } Darkvater@3561: Darkvater@3561: if (choice != NULL) { belugas@6973: grfmsg(2, "SkipIf: Jumping to label 0x%0X at line %d, test was true", choice->label, choice->nfo_line); Darkvater@3561: FioSeekTo(choice->pos, SEEK_SET); Darkvater@3561: _nfo_line = choice->nfo_line; Darkvater@3561: return; Darkvater@3561: } Darkvater@3561: belugas@6973: grfmsg(2, "SkipIf: Skipping %d sprites, test was true", numsprites); celestar@357: _skip_sprites = numsprites; celestar@357: if (_skip_sprites == 0) { celestar@357: /* Zero means there are no sprites to skip, so celestar@357: * we use -1 to indicate that all further celestar@357: * sprites should be skipped. */ celestar@357: _skip_sprites = -1; maedhros@6422: maedhros@6422: /* If an action 8 hasn't been encountered yet, disable the grf. */ rubidium@8585: if (_cur_grfconfig->status != GCS_ACTIVATED) { maedhros@7421: _cur_grfconfig->status = GCS_DISABLED; rubidium@11117: ClearTemporaryNewGRFData(); maedhros@7421: } truelight@0: } truelight@0: } truelight@0: peter1138@5317: peter1138@5753: /* Action 0x08 (GLS_FILESCAN) */ glx@10465: static void ScanInfo(byte *buf, size_t len) peter1138@5228: { peter1138@6607: if (!check_length(len, 8, "Info")) return; peter1138@6607: buf++; peter1138@6607: grf_load_byte(&buf); peter1138@6607: uint32 grfid = grf_load_dword(&buf); peter1138@5228: peter1138@5228: _cur_grfconfig->grfid = grfid; peter1138@5317: peter1138@5329: /* GRF IDs starting with 0xFF are reserved for internal TTDPatch use */ skidd13@8427: if (GB(grfid, 24, 8) == 0xFF) SetBit(_cur_grfconfig->flags, GCF_SYSTEM); peter1138@5329: peter1138@5317: len -= 6; peter1138@6607: const char *name = grf_load_string(&buf, len); peter1138@9152: _cur_grfconfig->name = TranslateTTDPatchCodes(grfid, name); rubidium@6512: rubidium@6512: len -= strlen(name) + 1; rubidium@6512: if (len > 0) { peter1138@6607: const char *info = grf_load_string(&buf, len); peter1138@9152: _cur_grfconfig->info = TranslateTTDPatchCodes(grfid, info); peter1138@5317: } peter1138@5329: peter1138@5329: /* GLS_INFOSCAN only looks for the action 8, so we can skip the rest of the file */ peter1138@5228: _skip_sprites = -1; peter1138@5228: } peter1138@5228: peter1138@3640: /* Action 0x08 */ glx@10465: static void GRFInfo(byte *buf, size_t len) truelight@0: { truelight@0: /* <08> truelight@0: * truelight@0: * B version newgrf version, currently 06 truelight@0: * 4*B grf-id globally unique ID of this .grf file truelight@0: * S name name of this .grf set truelight@0: * S info string describing the set, and e.g. author and copyright */ peter1138@6607: peter1138@6607: if (!check_length(len, 8, "GRFInfo")) return; peter1138@6607: buf++; peter1138@6607: uint8 version = grf_load_byte(&buf); peter1138@6607: uint32 grfid = grf_load_dword(&buf); peter1138@6607: const char *name = grf_load_string(&buf, len - 6); darkvater@366: darkvater@366: _cur_grffile->grfid = grfid; peter1138@3640: _cur_grffile->grf_version = version; peter1138@7302: _cur_grfconfig->status = _cur_stage < GLS_RESERVE ? GCS_INITIALISED : GCS_ACTIVATED; darkvater@366: Darkvater@3711: /* Do swap the GRFID for displaying purposes since people expect that */ belugas@6973: DEBUG(grf, 1, "GRFInfo: Loaded GRFv%d set %08lX - %s", version, BSWAP32(grfid), name); truelight@0: } truelight@0: peter1138@3640: /* Action 0x0A */ glx@10465: static void SpriteReplace(byte *buf, size_t len) truelight@0: { truelight@0: /* <0A> [ ...] truelight@0: * : truelight@0: * truelight@0: * B num-sets How many sets of sprites to replace. truelight@0: * Each set: truelight@0: * B num-sprites How many sprites are in this set truelight@0: * W first-sprite First sprite number to replace */ darkvater@361: belugas@6674: buf++; // skip action byte peter1138@6607: uint8 num_sets = grf_load_byte(&buf); peter1138@6607: peter1138@6607: for (uint i = 0; i < num_sets; i++) { tron@2342: uint8 num_sprites = grf_load_byte(&buf); tron@2342: uint16 first_sprite = grf_load_word(&buf); darkvater@398: Darkvater@5568: grfmsg(2, "SpriteReplace: [Set %d] Changing %d sprites, beginning with %d", tron@2342: i, num_sprites, first_sprite tron@2342: ); darkvater@361: peter1138@6607: for (uint j = 0; j < num_sprites; j++) { belugas@8679: int load_index = first_sprite + j; rubidium@8449: _nfo_line++; belugas@8679: LoadNextSprite(load_index, _file_index, _nfo_line); // XXX belugas@8679: frosch@8899: /* Shore sprites now located at different addresses. frosch@8899: * So detect when the old ones get replaced. */ frosch@8899: if (IsInsideMM(load_index, SPR_ORIGINALSHORE_START, SPR_ORIGINALSHORE_END + 1)) { frosch@8899: if (_loaded_newgrf_features.shore != SHORE_REPLACE_ACTION_5) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_A; frosch@8899: } darkvater@361: } darkvater@361: } truelight@0: } truelight@0: peter1138@7025: /* Action 0x0A (SKIP) */ glx@10465: static void SkipActA(byte *buf, size_t len) peter1138@7025: { peter1138@7025: buf++; peter1138@7025: uint8 num_sets = grf_load_byte(&buf); peter1138@7025: peter1138@7025: for (uint i = 0; i < num_sets; i++) { peter1138@7025: /* Skip the sprites this replaces */ peter1138@7025: _skip_sprites += grf_load_byte(&buf); peter1138@7025: /* But ignore where they go */ peter1138@7025: grf_load_word(&buf); peter1138@7025: } peter1138@7025: peter1138@7025: grfmsg(3, "SkipActA: Skipping %d sprites", _skip_sprites); peter1138@7025: } peter1138@7025: peter1138@3715: /* Action 0x0B */ glx@10465: static void GRFLoadError(byte *buf, size_t len) truelight@0: { truelight@0: /* <0B> [ 00] [] 00 [] truelight@0: * truelight@0: * B severity 00: notice, contine loading grf file truelight@0: * 01: warning, continue loading grf file truelight@0: * 02: error, but continue loading grf file, and attempt truelight@0: * loading grf again when loading or starting next game truelight@0: * 03: error, abort loading and prevent loading again in truelight@0: * the future (only when restarting the patch) truelight@0: * B language-id see action 4, use 1F for built-in error messages truelight@0: * B message-id message to show, see below truelight@0: * S message for custom messages (message-id FF), text of the message truelight@0: * not present for built-in messages. truelight@0: * V data additional data for built-in (or custom) messages maedhros@6465: * B parnum parameter numbers to be shown in the message (maximum of 2) */ maedhros@6465: maedhros@6465: static const StringID msgstr[] = { maedhros@6465: STR_NEWGRF_ERROR_VERSION_NUMBER, maedhros@6465: STR_NEWGRF_ERROR_DOS_OR_WINDOWS, maedhros@6465: STR_NEWGRF_ERROR_UNSET_SWITCH, maedhros@6465: STR_NEWGRF_ERROR_INVALID_PARAMETER, maedhros@6465: STR_NEWGRF_ERROR_LOAD_BEFORE, rubidium@8276: STR_NEWGRF_ERROR_LOAD_AFTER, rubidium@8276: STR_NEWGRF_ERROR_OTTD_VERSION_NUMBER, truelight@0: }; Darkvater@5567: maedhros@6465: static const StringID sevstr[] = { maedhros@6465: STR_NEWGRF_ERROR_MSG_INFO, maedhros@6465: STR_NEWGRF_ERROR_MSG_WARNING, maedhros@6465: STR_NEWGRF_ERROR_MSG_ERROR, maedhros@6465: STR_NEWGRF_ERROR_MSG_FATAL Darkvater@5567: }; truelight@0: maedhros@6465: if (!check_length(len, 6, "GRFLoadError")) return; maedhros@6465: maedhros@6465: /* For now we can only show one message per newgrf file. */ maedhros@6465: if (_cur_grfconfig->error != NULL) return; maedhros@6416: belugas@6674: buf++; // Skip the action byte. maedhros@6421: byte severity = grf_load_byte(&buf); maedhros@6465: byte lang = grf_load_byte(&buf); maedhros@6421: byte message_id = grf_load_byte(&buf); maedhros@6421: len -= 4; maedhros@6421: maedhros@7369: /* Skip the error if it isn't valid for the current language. */ maedhros@7369: if (!CheckGrfLangID(lang, _cur_grffile->grf_version)) return; maedhros@7369: maedhros@6421: /* Skip the error until the activation stage unless bit 7 of the severity maedhros@6421: * is set. */ skidd13@8424: if (!HasBit(severity, 7) && _cur_stage == GLS_INIT) { peter1138@7301: grfmsg(7, "GRFLoadError: Skipping non-fatal GRFLoadError in stage %d", _cur_stage); miham@441: return; peter1138@3588: } skidd13@8425: ClrBit(severity, 7); maedhros@6421: maedhros@6421: if (severity >= lengthof(sevstr)) { maedhros@6465: grfmsg(7, "GRFLoadError: Invalid severity id %d. Setting to 2 (non-fatal error).", severity); maedhros@6421: severity = 2; maedhros@6421: } else if (severity == 3) { maedhros@6421: /* This is a fatal error, so make sure the GRF is deactivated and no maedhros@6421: * more of it gets loaded. */ maedhros@6555: _cur_grfconfig->status = GCS_DISABLED; rubidium@11117: ClearTemporaryNewGRFData(); maedhros@6421: _skip_sprites = -1; maedhros@6421: } maedhros@6421: maedhros@6421: if (message_id >= lengthof(msgstr) && message_id != 0xFF) { maedhros@6465: grfmsg(7, "GRFLoadError: Invalid message id."); maedhros@6421: return; maedhros@6421: } maedhros@6421: maedhros@6421: if (len <= 1) { maedhros@6465: grfmsg(7, "GRFLoadError: No message data supplied."); maedhros@6421: return; maedhros@6421: } maedhros@6421: maedhros@6465: GRFError *error = CallocT(1); maedhros@6465: maedhros@6465: error->severity = sevstr[severity]; maedhros@6465: maedhros@6465: if (message_id == 0xFF) { maedhros@6465: /* This is a custom error message. */ maedhros@6465: const char *message = grf_load_string(&buf, len); maedhros@6465: len -= (strlen(message) + 1); maedhros@6465: peter1138@9152: error->custom_message = TranslateTTDPatchCodes(_cur_grffile->grfid, message); maedhros@6465: } else { maedhros@6465: error->message = msgstr[message_id]; maedhros@6429: } maedhros@6429: maedhros@6465: if (len > 0) { maedhros@6465: const char *data = grf_load_string(&buf, len); maedhros@6465: len -= (strlen(data) + 1); maedhros@6465: peter1138@9152: error->data = TranslateTTDPatchCodes(_cur_grffile->grfid, data); maedhros@6465: } maedhros@6465: maedhros@6465: /* Only two parameter numbers can be used in the string. */ maedhros@6465: uint i = 0; maedhros@6465: for (; i < 2 && len > 0; i++) { maedhros@6465: error->param_number[i] = grf_load_byte(&buf); maedhros@6465: len--; maedhros@6465: } maedhros@6465: error->num_params = i; maedhros@6465: maedhros@6465: _cur_grfconfig->error = error; truelight@0: } truelight@0: peter1138@3715: /* Action 0x0C */ glx@10465: static void GRFComment(byte *buf, size_t len) truelight@0: { truelight@0: /* <0C> [] truelight@0: * truelight@0: * V ignored Anything following the 0C is ignored */ Darkvater@3561: Darkvater@3561: if (len == 1) return; Darkvater@3561: glx@10465: size_t text_len = len - 1; tron@6433: const char *text = (const char*)(buf + 1); tron@6433: grfmsg(2, "GRFComment: %.*s", text_len, text); truelight@0: } truelight@0: peter1138@5776: /* Action 0x0D (GLS_SAFETYSCAN) */ glx@10465: static void SafeParamSet(byte *buf, size_t len) peter1138@5776: { peter1138@5841: if (!check_length(len, 5, "SafeParamSet")) return; peter1138@5776: buf++; peter1138@6607: uint8 target = grf_load_byte(&buf); peter1138@5776: peter1138@5776: /* Only writing GRF parameters is considered safe */ peter1138@5776: if (target < 0x80) return; peter1138@5776: peter1138@5776: /* GRM could be unsafe, but as here it can only happen after other GRFs peter1138@5776: * are loaded, it should be okay. If the GRF tried to use the slots it peter1138@5776: * reserved, it would be marked unsafe anyway. GRM for (e.g. bridge) peter1138@5776: * sprites is considered safe. */ peter1138@5776: skidd13@8427: SetBit(_cur_grfconfig->flags, GCF_UNSAFE); peter1138@5776: peter1138@5776: /* Skip remainder of GRF */ peter1138@5776: _skip_sprites = -1; peter1138@5776: } peter1138@5776: peter1138@7011: peter1138@7011: static uint32 GetPatchVariable(uint8 param) peter1138@7011: { peter1138@7011: switch (param) { peter1138@7011: /* start year - 1920 */ rubidium@10775: case 0x0B: return max(_settings_game.game_creation.starting_year, ORIGINAL_BASE_YEAR) - ORIGINAL_BASE_YEAR; belugas@8887: peter1138@7011: /* freight trains weight factor */ rubidium@10775: case 0x0E: return _settings_game.vehicle.freight_trains; belugas@8887: peter1138@7011: /* empty wagon speed increase */ peter1138@7011: case 0x0F: return 0; belugas@8887: peter1138@9145: /* plane speed factor; our patch option is reversed from TTDPatch's, peter1138@9145: * the following is good for 1x, 2x and 4x (most common?) and... peter1138@9145: * well not really for 3x. */ peter1138@9145: case 0x10: rubidium@10775: switch (_settings_game.vehicle.plane_speed) { peter1138@9145: default: peter1138@9145: case 4: return 1; peter1138@9145: case 3: return 2; peter1138@9145: case 2: return 2; peter1138@9145: case 1: return 4; peter1138@9145: } peter1138@9145: belugas@8887: peter1138@7011: /* 2CC colormap base sprite */ peter1138@7011: case 0x11: return SPR_2CCMAP_BASE; peter1138@7011: belugas@8887: /* map size: format = -MABXYSS belugas@8887: * M : the type of map belugas@8887: * bit 0 : set : squared map. Bit 1 is now not relevant belugas@8887: * clear : rectangle map. Bit 1 will indicate the bigger edge of the map belugas@8887: * bit 1 : set : Y is the bigger edge. Bit 0 is clear belugas@8887: * clear : X is the bigger edge. belugas@8887: * A : minimum edge(log2) of the map belugas@8887: * B : maximum edge(log2) of the map belugas@8887: * XY : edges(log2) of each side of the map. belugas@8887: * SS : combination of both X and Y, thus giving the size(log2) of the map belugas@8887: */ belugas@8887: case 0x13: { belugas@8887: byte map_bits = 0; belugas@8889: byte log_X = MapLogX() - 6; // substraction is required to make the minimal size (64) zero based belugas@8887: byte log_Y = MapLogY() - 6; belugas@8887: byte max_edge = max(log_X, log_Y); belugas@8887: belugas@8887: if (log_X == log_Y) { // we have a squared map, since both edges are identical belugas@8887: SetBit(map_bits ,0); belugas@8887: } else { belugas@8887: if (max_edge == log_Y) SetBit(map_bits, 1); // edge Y been the biggest, mark it belugas@8887: } belugas@8887: belugas@8887: return (map_bits << 24) | (min(log_X, log_Y) << 20) | (max_edge << 16) | belugas@8887: (log_X << 12) | (log_Y << 8) | (log_X + log_Y); belugas@8887: } belugas@8887: peter1138@7011: default: peter1138@7011: grfmsg(2, "ParamSet: Unknown Patch variable 0x%02X.", param); peter1138@7011: return 0; peter1138@7011: } peter1138@7011: } peter1138@7011: peter1138@7011: peter1138@7277: static uint32 PerformGRM(uint32 *grm, uint16 num_ids, uint16 count, uint8 op, uint8 target, const char *type) peter1138@7277: { peter1138@7277: uint start = 0; peter1138@7277: uint size = 0; peter1138@7277: peter1138@7277: if (op == 6) { peter1138@7277: /* Return GRFID of set that reserved ID */ peter1138@7277: return grm[_cur_grffile->param[target]]; peter1138@7277: } peter1138@7277: peter1138@7277: /* With an operation of 2 or 3, we want to reserve a specific block of IDs */ peter1138@7277: if (op == 2 || op == 3) start = _cur_grffile->param[target]; peter1138@7277: peter1138@7277: for (uint i = start; i < num_ids; i++) { peter1138@7277: if (grm[i] == 0) { peter1138@7277: size++; peter1138@7277: } else { peter1138@7277: if (op == 2 || op == 3) break; peter1138@7277: start = i + 1; peter1138@7277: size = 0; peter1138@7277: } peter1138@7277: peter1138@7277: if (size == count) break; peter1138@7277: } peter1138@7277: peter1138@7277: if (size == count) { peter1138@7277: /* Got the slot... */ peter1138@7277: if (op == 0 || op == 3) { peter1138@7277: grfmsg(2, "ParamSet: GRM: Reserving %d %s at %d", count, type, start); peter1138@7277: for (uint i = 0; i < count; i++) grm[start + i] = _cur_grffile->grfid; peter1138@7277: } peter1138@7277: return start; peter1138@7277: } peter1138@7277: peter1138@7277: /* Unable to allocate */ peter1138@7277: if (op != 4 && op != 5) { peter1138@7277: /* Deactivate GRF */ peter1138@7277: grfmsg(0, "ParamSet: GRM: Unable to allocate %d %s, deactivating", count, type); peter1138@7277: _cur_grfconfig->status = GCS_DISABLED; rubidium@11117: ClearTemporaryNewGRFData(); peter1138@7277: _skip_sprites = -1; peter1138@7277: return UINT_MAX; peter1138@7277: } peter1138@7277: peter1138@7277: grfmsg(1, "ParamSet: GRM: Unable to allocate %d %s", count, type); peter1138@7277: return UINT_MAX; peter1138@7277: } peter1138@7277: peter1138@7277: celestar@356: /* Action 0x0D */ glx@10465: static void ParamSet(byte *buf, size_t len) truelight@0: { truelight@0: /* <0D> [] truelight@0: * truelight@0: * B target parameter number where result is stored truelight@0: * B operation operation to perform, see below truelight@0: * B source1 first source operand truelight@0: * B source2 second source operand truelight@0: * D data data to use in the calculation, not necessary truelight@0: * if both source1 and source2 refer to actual parameters truelight@0: * truelight@0: * Operations truelight@0: * 00 Set parameter equal to source1 truelight@0: * 01 Addition, source1 + source2 truelight@0: * 02 Subtraction, source1 - source2 truelight@0: * 03 Unsigned multiplication, source1 * source2 (both unsigned) truelight@0: * 04 Signed multiplication, source1 * source2 (both signed) truelight@0: * 05 Unsigned bit shift, source1 by source2 (source2 taken to be a truelight@0: * signed quantity; left shift if positive and right shift if truelight@0: * negative, source1 is unsigned) truelight@0: * 06 Signed bit shift, source1 by source2 truelight@0: * (source2 like in 05, and source1 as well) truelight@0: */ darkvater@362: peter1138@5841: if (!check_length(len, 5, "ParamSet")) return; bjarni@418: buf++; peter1138@6607: uint8 target = grf_load_byte(&buf); peter1138@6607: uint8 oper = grf_load_byte(&buf); peter1138@6607: uint32 src1 = grf_load_byte(&buf); peter1138@6607: uint32 src2 = grf_load_byte(&buf); peter1138@6607: peter1138@6607: uint32 data = 0; tron@2345: if (len >= 8) data = grf_load_dword(&buf); darkvater@398: darkvater@362: /* You can add 80 to the operation to make it apply only if the target darkvater@362: * is not defined yet. In this respect, a parameter is taken to be darkvater@362: * defined if any of the following applies: darkvater@362: * - it has been set to any value in the newgrf(w).cfg parameter list darkvater@362: * - it OR A PARAMETER WITH HIGHER NUMBER has been set to any value by darkvater@362: * an earlier action D */ skidd13@8424: if (HasBit(oper, 7)) { peter1138@3807: if (target < 0x80 && target < _cur_grffile->param_end) { belugas@6973: grfmsg(7, "ParamSet: Param %u already defined, skipping", target); darkvater@362: return; peter1138@3807: } peter1138@3807: peter1138@6607: oper = GB(oper, 0, 7); darkvater@362: } darkvater@362: peter1138@3808: if (src2 == 0xFE) { peter1138@3808: if (GB(data, 0, 8) == 0xFF) { peter1138@3808: if (data == 0x0000FFFF) { peter1138@3808: /* Patch variables */ peter1138@7011: src1 = GetPatchVariable(src1); peter1138@3808: } else { peter1138@3808: /* GRF Resource Management */ peter1138@10416: uint8 op = src1; peter1138@10416: uint8 feature = GB(data, 8, 8); peter1138@10416: uint16 count = GB(data, 16, 16); peter1138@10416: peter1138@10416: if (_cur_stage == GLS_RESERVE) { peter1138@10416: if (feature == 0x08) { peter1138@10416: /* General sprites */ peter1138@10416: if (op == 0) { peter1138@10416: /* Check if the allocated sprites will fit below the original sprite limit */ peter1138@10416: if (_cur_spriteid + count >= 16384) { peter1138@10416: grfmsg(0, "ParamSet: GRM: Unable to allocate %d sprites; try changing NewGRF order", count); peter1138@10416: _cur_grfconfig->status = GCS_DISABLED; rubidium@11117: ClearTemporaryNewGRFData(); peter1138@10416: _skip_sprites = -1; peter1138@10416: return; peter1138@10416: } peter1138@10416: peter1138@10416: /* Reserve space at the current sprite ID */ peter1138@10416: grfmsg(4, "ParamSet: GRM: Allocated %d sprites at %d", count, _cur_spriteid); peter1138@10431: _grm_sprites[GRFLocation(_cur_grffile->grfid, _nfo_line)] = _cur_spriteid; peter1138@10416: _cur_spriteid += count; peter1138@10416: } peter1138@10416: } peter1138@10416: /* Ignore GRM result during reservation */ peter1138@4961: src1 = 0; peter1138@10416: } else if (_cur_stage == GLS_ACTIVATION) { peter1138@4961: switch (feature) { belugas@6674: case 0x00: // Trains belugas@6674: case 0x01: // Road Vehicles belugas@6674: case 0x02: // Ships belugas@6674: case 0x03: // Aircraft rubidium@10775: if (!_settings_game.vehicle.dynamic_engines) { peter1138@10382: src1 = PerformGRM(&_grm_engines[_engine_offsets[feature]], _engine_counts[feature], count, op, target, "vehicles"); peter1138@10382: if (_skip_sprites == -1) return; peter1138@10382: } else { peter1138@10382: // GRM does not apply for dynamic engine allocation. peter1138@10382: switch (op) { peter1138@10382: case 2: peter1138@10382: case 3: peter1138@10382: src1 = _cur_grffile->param[target]; peter1138@10382: break; peter1138@10382: peter1138@10382: default: peter1138@10382: src1 = 0; peter1138@10382: break; peter1138@10382: } peter1138@10382: } peter1138@4961: break; peter1138@4961: belugas@6674: case 0x08: // General sprites peter1138@4961: switch (op) { peter1138@4961: case 0: peter1138@10416: /* Return space reserved during reservation stage */ peter1138@10431: src1 = _grm_sprites[GRFLocation(_cur_grffile->grfid, _nfo_line)]; peter1138@10416: grfmsg(4, "ParamSet: GRM: Using pre-allocated sprites at %d", src1); peter1138@4961: break; peter1138@4961: peter1138@4961: case 1: peter1138@4961: src1 = _cur_spriteid; peter1138@4961: break; peter1138@4961: peter1138@4961: default: belugas@6973: grfmsg(1, "ParamSet: GRM: Unsupported operation %d for general sprites", op); peter1138@4961: return; peter1138@4961: } peter1138@4961: break; peter1138@4961: peter1138@7277: case 0x0B: // Cargo rubidium@8186: /* There are two ranges: one for cargo IDs and one for cargo bitmasks */ rubidium@8186: src1 = PerformGRM(_grm_cargos, NUM_CARGO * 2, count, op, target, "cargos"); peter1138@7277: if (_skip_sprites == -1) return; peter1138@7277: break; peter1138@7277: belugas@6973: default: grfmsg(1, "ParamSet: GRM: Unsupported feature 0x%X", feature); return; peter1138@4961: } peter1138@10416: } else { peter1138@10416: /* Ignore GRM during initialization */ peter1138@10416: src1 = 0; peter1138@4961: } peter1138@3808: } peter1138@3808: } else { peter1138@3808: /* Read another GRF File's parameter */ peter1138@3808: const GRFFile *file = GetFileByGRFID(data); rubidium@11020: GRFConfig *c = GetGRFConfig(data); rubidium@11020: if (c != NULL && HasBit(c->status, GCF_STATIC) && !HasBit(_cur_grfconfig->status, GCF_STATIC) && _networking) { rubidium@11020: /* Disable the read GRF if it is a static NewGRF. */ rubidium@11020: DisableStaticNewGRFInfluencingNonStaticNewGRFs(c); rubidium@11020: src1 = 0; rubidium@11020: } else if (file == NULL || src1 >= file->param_end || (c != NULL && c->status == GCS_DISABLED)) { peter1138@3808: src1 = 0; peter1138@3808: } else { peter1138@3808: src1 = file->param[src1]; peter1138@3808: } peter1138@3808: } darkvater@362: } else { peter1138@3808: /* The source1 and source2 operands refer to the grf parameter number peter1138@3808: * like in action 6 and 7. In addition, they can refer to the special peter1138@3808: * variables available in action 7, or they can be FF to use the value peter1138@3808: * of . If referring to parameters that are undefined, a value peter1138@3808: * of 0 is used instead. */ peter1138@3811: src1 = (src1 == 0xFF) ? data : GetParamVal(src1, NULL); peter1138@3811: src2 = (src2 == 0xFF) ? data : GetParamVal(src2, NULL); darkvater@362: } darkvater@362: darkvater@362: /* TODO: You can access the parameters of another GRF file by using darkvater@362: * source2=FE, source1=the other GRF's parameter number and data=GRF darkvater@362: * ID. This is only valid with operation 00 (set). If the GRF ID darkvater@362: * cannot be found, a value of 0 is used for the parameter value darkvater@362: * instead. */ darkvater@362: peter1138@6607: uint32 res; darkvater@362: switch (oper) { darkvater@362: case 0x00: tron@2345: res = src1; darkvater@362: break; tron@2345: tron@2345: case 0x01: tron@2345: res = src1 + src2; darkvater@362: break; tron@2345: tron@2345: case 0x02: tron@2345: res = src1 - src2; tron@2345: break; tron@2345: darkvater@362: case 0x03: tron@2345: res = src1 * src2; darkvater@362: break; tron@2345: darkvater@362: case 0x04: tron@2345: res = (int32)src1 * (int32)src2; darkvater@362: break; tron@2345: darkvater@362: case 0x05: tron@4077: if ((int32)src2 < 0) { tron@2345: res = src1 >> -(int32)src2; tron@4077: } else { tron@2345: res = src1 << src2; tron@4077: } darkvater@362: break; tron@2345: darkvater@362: case 0x06: tron@4077: if ((int32)src2 < 0) { tron@2345: res = (int32)src1 >> -(int32)src2; tron@4077: } else { tron@2345: res = (int32)src1 << src2; tron@4077: } darkvater@362: break; tron@2345: belugas@6674: case 0x07: // Bitwise AND peter1138@2442: res = src1 & src2; peter1138@2442: break; peter1138@2442: belugas@6674: case 0x08: // Bitwise OR peter1138@2442: res = src1 | src2; peter1138@2442: break; peter1138@2442: belugas@6674: case 0x09: // Unsigned division peter1138@2442: if (src2 == 0) { peter1138@2442: res = src1; peter1138@2442: } else { peter1138@2442: res = src1 / src2; peter1138@2442: } peter1138@2442: break; peter1138@2442: belugas@6674: case 0x0A: // Signed divison peter1138@2442: if (src2 == 0) { peter1138@2442: res = src1; peter1138@2442: } else { peter1138@2442: res = (int32)src1 / (int32)src2; peter1138@2442: } peter1138@2442: break; peter1138@2442: belugas@6674: case 0x0B: // Unsigned modulo peter1138@2442: if (src2 == 0) { peter1138@2442: res = src1; peter1138@2442: } else { peter1138@2442: res = src1 % src2; peter1138@2442: } peter1138@2442: break; peter1138@2442: belugas@6674: case 0x0C: // Signed modulo peter1138@2442: if (src2 == 0) { peter1138@2442: res = src1; peter1138@2442: } else { peter1138@2442: res = (int32)src1 % (int32)src2; peter1138@2442: } peter1138@2442: break; peter1138@2442: Darkvater@5568: default: grfmsg(0, "ParamSet: Unknown operation %d, skipping", oper); return; tron@2345: } tron@2345: tron@2345: switch (target) { tron@2345: case 0x8E: // Y-Offset for train sprites tron@2345: _traininfo_vehicle_pitch = res; tron@2345: break; tron@2345: maedhros@8226: case 0x8F: // Rail track type cost factors maedhros@8226: _railtype_cost_multiplier[0] = GB(res, 0, 8); rubidium@10775: if (_settings_game.vehicle.disable_elrails) { maedhros@8226: _railtype_cost_multiplier[1] = GB(res, 0, 8); maedhros@8226: _railtype_cost_multiplier[2] = GB(res, 8, 8); maedhros@8226: } else { maedhros@8226: _railtype_cost_multiplier[1] = GB(res, 8, 8); maedhros@8226: _railtype_cost_multiplier[2] = GB(res, 16, 8); maedhros@8226: } maedhros@8226: _railtype_cost_multiplier[3] = GB(res, 16, 8); maedhros@8226: break; maedhros@8226: belugas@6674: /* @todo implement */ tron@2345: case 0x93: // Tile refresh offset to left tron@2345: case 0x94: // Tile refresh offset to right tron@2345: case 0x95: // Tile refresh offset upwards tron@2345: case 0x96: // Tile refresh offset downwards tron@2345: case 0x97: // Snow line height tron@2345: case 0x99: // Global ID offset Darkvater@5568: grfmsg(7, "ParamSet: Skipping unimplemented target 0x%02X", target); tron@2345: break; tron@2345: belugas@6674: case 0x9E: // Miscellaneous GRF features peter1138@3814: _misc_grf_features = res; peter1138@3845: /* Set train list engine width */ maedhros@7037: _traininfo_vehicle_width = HasGrfMiscBit(GMB_TRAIN_WIDTH_32_PIXELS) ? 32 : 29; peter1138@3814: break; peter1138@3814: frosch@9042: case 0x9F: // locale-dependent settings frosch@9042: grfmsg(7, "ParamSet: Skipping unimplemented target 0x%02X", target); frosch@9042: break; frosch@9042: tron@2345: default: tron@2345: if (target < 0x80) { tron@2345: _cur_grffile->param[target] = res; tron@2345: if (target + 1U > _cur_grffile->param_end) _cur_grffile->param_end = target + 1; tron@2345: } else { Darkvater@5568: grfmsg(7, "ParamSet: Skipping unknown target 0x%02X", target); tron@2345: } tron@2345: break; darkvater@362: } truelight@0: } truelight@0: peter1138@5753: /* Action 0x0E (GLS_SAFETYSCAN) */ glx@10465: static void SafeGRFInhibit(byte *buf, size_t len) peter1138@5753: { peter1138@5753: /* <0E> peter1138@5753: * peter1138@5753: * B num Number of GRFIDs that follow peter1138@5753: * D grfids GRFIDs of the files to deactivate */ peter1138@5753: peter1138@6607: if (!check_length(len, 2, "GRFInhibit")) return; peter1138@6607: buf++; peter1138@6607: uint8 num = grf_load_byte(&buf); peter1138@6607: if (!check_length(len, 2 + 4 * num, "GRFInhibit")) return; peter1138@6607: peter1138@6607: for (uint i = 0; i < num; i++) { peter1138@5753: uint32 grfid = grf_load_dword(&buf); peter1138@5753: peter1138@5753: /* GRF is unsafe it if tries to deactivate other GRFs */ peter1138@5753: if (grfid != _cur_grfconfig->grfid) { skidd13@8427: SetBit(_cur_grfconfig->flags, GCF_UNSAFE); peter1138@5753: peter1138@5753: /* Skip remainder of GRF */ peter1138@5753: _skip_sprites = -1; peter1138@5753: peter1138@5753: return; peter1138@5753: } peter1138@5753: } peter1138@5753: } peter1138@5753: peter1138@3715: /* Action 0x0E */ glx@10465: static void GRFInhibit(byte *buf, size_t len) truelight@0: { truelight@0: /* <0E> truelight@0: * truelight@0: * B num Number of GRFIDs that follow truelight@0: * D grfids GRFIDs of the files to deactivate */ darkvater@367: peter1138@6607: if (!check_length(len, 2, "GRFInhibit")) return; peter1138@6607: buf++; peter1138@6607: uint8 num = grf_load_byte(&buf); peter1138@6607: if (!check_length(len, 2 + 4 * num, "GRFInhibit")) return; peter1138@6607: peter1138@6607: for (uint i = 0; i < num; i++) { darkvater@367: uint32 grfid = grf_load_dword(&buf); peter1138@5333: GRFConfig *file = GetGRFConfig(grfid); darkvater@367: darkvater@367: /* Unset activation flag */ peter1138@5557: if (file != NULL && file != _cur_grfconfig) { Darkvater@5568: grfmsg(2, "GRFInhibit: Deactivating file '%s'", file->filename); maedhros@6555: file->status = GCS_DISABLED; darkvater@367: } darkvater@367: } truelight@0: } truelight@0: glx@7452: /* Action 0x0F */ glx@10465: static void FeatureTownName(byte *buf, size_t len) glx@7452: { glx@7452: /* <0F> glx@7452: * glx@7452: * B id ID of this definition in bottom 7 bits (final definition if bit 7 set) glx@7452: * V style-name Name of the style (only for final definition) glx@7452: * B num-parts Number of parts in this definition glx@7452: * V parts The parts */ glx@7452: glx@7452: if (!check_length(len, 1, "FeatureTownName: definition ID")) return; glx@7452: buf++; len--; glx@7452: glx@7452: uint32 grfid = _cur_grffile->grfid; glx@7452: glx@7452: GRFTownName *townname = AddGRFTownName(grfid); glx@7452: glx@7452: byte id = grf_load_byte(&buf); glx@7452: len--; glx@7452: grfmsg(6, "FeatureTownName: definition 0x%02X", id & 0x7F); glx@7452: skidd13@8424: if (HasBit(id, 7)) { glx@7452: /* Final definition */ skidd13@8425: ClrBit(id, 7); glx@7452: bool new_scheme = _cur_grffile->grf_version >= 7; glx@7452: glx@7452: if (!check_length(len, 1, "FeatureTownName: lang_id")) return; glx@7452: byte lang = grf_load_byte(&buf); glx@7452: len--; glx@7452: glx@7452: byte nb_gen = townname->nb_gen; glx@7452: do { skidd13@8425: ClrBit(lang, 7); glx@7452: glx@7452: if (!check_length(len, 1, "FeatureTownName: style name")) return; glx@7452: const char *name = grf_load_string(&buf, len); glx@7452: len -= strlen(name) + 1; peter1138@9152: grfmsg(6, "FeatureTownName: lang 0x%X -> '%s'", lang, TranslateTTDPatchCodes(grfid, name)); glx@7452: glx@7452: townname->name[nb_gen] = AddGRFString(grfid, id, lang, new_scheme, name, STR_UNDEFINED); glx@7452: glx@7452: if (!check_length(len, 1, "FeatureTownName: lang_id")) return; glx@7452: lang = grf_load_byte(&buf); glx@7452: len--; glx@7452: } while (lang != 0); glx@7452: townname->id[nb_gen] = id; glx@7452: townname->nb_gen++; glx@7452: } glx@7452: glx@7452: if (!check_length(len, 1, "FeatureTownName: number of parts")) return; glx@7452: byte nb = grf_load_byte(&buf); glx@7452: len--; glx@7452: grfmsg(6, "FeatureTownName: %d parts", nb, nb); glx@7452: glx@7452: townname->nbparts[id] = nb; glx@7452: townname->partlist[id] = CallocT(nb); glx@7452: glx@7452: for (int i = 0; i < nb; i++) { glx@7452: if (!check_length(len, 3, "FeatureTownName: parts header")) return; glx@7452: byte nbtext = grf_load_byte(&buf); glx@7452: townname->partlist[id][i].bitstart = grf_load_byte(&buf); glx@7452: townname->partlist[id][i].bitcount = grf_load_byte(&buf); glx@7452: townname->partlist[id][i].maxprob = 0; glx@7452: townname->partlist[id][i].partcount = nbtext; glx@7452: townname->partlist[id][i].parts = CallocT(nbtext); glx@7452: len -= 3; glx@7452: grfmsg(6, "FeatureTownName: part %d contains %d texts and will use GB(seed, %d, %d)", i, nbtext, townname->partlist[id][i].bitstart, townname->partlist[id][i].bitcount); glx@7452: glx@7452: for (int j = 0; j < nbtext; j++) { glx@7452: if (!check_length(len, 2, "FeatureTownName: part")) return; glx@7452: byte prob = grf_load_byte(&buf); glx@7452: len--; glx@7452: skidd13@8424: if (HasBit(prob, 7)) { glx@7452: byte ref_id = grf_load_byte(&buf); glx@7452: len--; glx@7452: glx@7452: if (townname->nbparts[ref_id] == 0) { glx@7452: grfmsg(0, "FeatureTownName: definition 0x%02X doesn't exist, deactivating", ref_id); glx@7452: DelGRFTownName(grfid); glx@7452: _cur_grfconfig->status = GCS_DISABLED; rubidium@11117: ClearTemporaryNewGRFData(); glx@7452: _skip_sprites = -1; glx@7452: return; glx@7452: } glx@7452: glx@7452: grfmsg(6, "FeatureTownName: part %d, text %d, uses intermediate definition 0x%02X (with probability %d)", i, j, ref_id, prob & 0x7F); glx@7452: townname->partlist[id][i].parts[j].data.id = ref_id; glx@7452: } else { glx@7452: const char *text = grf_load_string(&buf, len); glx@7452: len -= strlen(text) + 1; peter1138@9152: townname->partlist[id][i].parts[j].data.text = TranslateTTDPatchCodes(grfid, text); glx@7452: grfmsg(6, "FeatureTownName: part %d, text %d, '%s' (with probability %d)", i, j, townname->partlist[id][i].parts[j].data.text, prob); glx@7452: } glx@7452: townname->partlist[id][i].parts[j].prob = prob; glx@7452: townname->partlist[id][i].maxprob += GB(prob, 0, 7); glx@7452: } glx@7452: grfmsg(6, "FeatureTownName: part %d, total probability %d", i, townname->partlist[id][i].maxprob); glx@7452: } glx@7452: } glx@7452: peter1138@3715: /* Action 0x10 */ glx@10465: static void DefineGotoLabel(byte *buf, size_t len) Darkvater@3561: { Darkvater@3561: /* <10>