# HG changeset patch # User smatz # Date 2009-01-04 15:32:25 # Node ID 99cb9a95b4cf9a8272d2a59e6fc86ad8e2d65bfd # Parent e200b86ce378835a17ec4c2c51c7bb15613dae21 (svn r14828) -Codechange: move most of save/load-specific code to separate files diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -648,10 +648,6 @@ > - - @@ -692,10 +688,6 @@ > - - @@ -1044,6 +1036,10 @@ > + + @@ -1360,10 +1356,6 @@ > - - @@ -2009,6 +2001,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - @@ -689,10 +685,6 @@ > - - @@ -1041,6 +1033,10 @@ > + + @@ -1357,10 +1353,6 @@ > - - @@ -2006,6 +1998,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + index].state](c); } - - -static const SaveLoad _company_ai_desc[] = { - SLE_VAR(CompanyAI, state, SLE_UINT8), - SLE_VAR(CompanyAI, tick, SLE_UINT8), - SLE_CONDVAR(CompanyAI, state_counter, SLE_FILE_U16 | SLE_VAR_U32, 0, 12), - SLE_CONDVAR(CompanyAI, state_counter, SLE_UINT32, 13, SL_MAX_VERSION), - SLE_VAR(CompanyAI, timeout_counter, SLE_UINT16), - - SLE_VAR(CompanyAI, state_mode, SLE_UINT8), - SLE_VAR(CompanyAI, banned_tile_count, SLE_UINT8), - SLE_VAR(CompanyAI, railtype_to_use, SLE_UINT8), - - SLE_VAR(CompanyAI, cargo_type, SLE_UINT8), - SLE_VAR(CompanyAI, num_wagons, SLE_UINT8), - SLE_VAR(CompanyAI, build_kind, SLE_UINT8), - SLE_VAR(CompanyAI, num_build_rec, SLE_UINT8), - SLE_VAR(CompanyAI, num_loco_to_build, SLE_UINT8), - SLE_VAR(CompanyAI, num_want_fullload, SLE_UINT8), - - SLE_VAR(CompanyAI, route_type_mask, SLE_UINT8), - - SLE_CONDVAR(CompanyAI, start_tile_a, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(CompanyAI, start_tile_a, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(CompanyAI, cur_tile_a, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(CompanyAI, cur_tile_a, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_VAR(CompanyAI, start_dir_a, SLE_UINT8), - SLE_VAR(CompanyAI, cur_dir_a, SLE_UINT8), - - SLE_CONDVAR(CompanyAI, start_tile_b, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(CompanyAI, start_tile_b, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(CompanyAI, cur_tile_b, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(CompanyAI, cur_tile_b, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_VAR(CompanyAI, start_dir_b, SLE_UINT8), - SLE_VAR(CompanyAI, cur_dir_b, SLE_UINT8), - - SLE_REF(CompanyAI, cur_veh, REF_VEHICLE), - - SLE_ARR(CompanyAI, wagon_list, SLE_UINT16, 9), - SLE_ARR(CompanyAI, order_list_blocks, SLE_UINT8, 20), - SLE_ARR(CompanyAI, banned_tiles, SLE_UINT16, 16), - - SLE_CONDNULL(64, 2, SL_MAX_VERSION), - SLE_END() -}; - -static const SaveLoad _company_ai_build_rec_desc[] = { - SLE_CONDVAR(AiBuildRec, spec_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(AiBuildRec, spec_tile, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(AiBuildRec, use_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(AiBuildRec, use_tile, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_VAR(AiBuildRec, rand_rng, SLE_UINT8), - SLE_VAR(AiBuildRec, cur_building_rule, SLE_UINT8), - SLE_VAR(AiBuildRec, unk6, SLE_UINT8), - SLE_VAR(AiBuildRec, unk7, SLE_UINT8), - SLE_VAR(AiBuildRec, buildcmd_a, SLE_UINT8), - SLE_VAR(AiBuildRec, buildcmd_b, SLE_UINT8), - SLE_VAR(AiBuildRec, direction, SLE_UINT8), - SLE_VAR(AiBuildRec, cargo, SLE_UINT8), - SLE_END() -}; - - -void SaveLoad_AI(CompanyID company) -{ - CompanyAI *cai = &_companies_ai[company]; - SlObject(cai, _company_ai_desc); - for (int i = 0; i != cai->num_build_rec; i++) { - SlObject(&cai->src + i, _company_ai_build_rec_desc); - } -} diff --git a/src/ai/default/default.h b/src/ai/default/default.h --- a/src/ai/default/default.h +++ b/src/ai/default/default.h @@ -10,7 +10,6 @@ #include "../../rail_type.h" void AiDoGameLoop(Company *c); -void SaveLoad_AI(CompanyID company); struct AiBuildRec { TileIndex spec_tile; diff --git a/src/ai/trolly/trolly.h b/src/ai/trolly/trolly.h --- a/src/ai/trolly/trolly.h +++ b/src/ai/trolly/trolly.h @@ -10,6 +10,7 @@ #include "../../vehicle_type.h" #include "../../date_type.h" #include "../../engine_type.h" +#include "../../direction_type.h" /* * These defines can be altered to change the behavoir of the AI diff --git a/src/aircraft.h b/src/aircraft.h --- a/src/aircraft.h +++ b/src/aircraft.h @@ -99,6 +99,11 @@ void UpdateAirplanesOnNewStation(const S */ void UpdateAircraftCache(Vehicle *v); +void AircraftLeaveHangar(Vehicle *v); +void AircraftNextAirportPos_and_Order(Vehicle *v); +void SetAircraftPosition(Vehicle *v, int x, int y, int z); +byte GetAircraftFlyingAltitude(const Vehicle *v); + /** * This class 'wraps' Vehicle; you do not actually instantiate this class. * You create a Vehicle using AllocateVehicle, so it is added to the pool diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -82,9 +82,6 @@ static bool AirportFindFreeTerminal(Vehi static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc); static void CrashAirplane(Vehicle *v); -void AircraftNextAirportPos_and_Order(Vehicle *v); -static byte GetAircraftFlyingAltitude(const Vehicle *v); - static const SpriteID _aircraft_sprite[] = { 0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD, 0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5, @@ -727,7 +724,7 @@ static void HelicopterTickHandler(Vehicl EndVehicleMove(u); } -static void SetAircraftPosition(Vehicle *v, int x, int y, int z) +void SetAircraftPosition(Vehicle *v, int x, int y, int z) { v->x_pos = x; v->y_pos = y; @@ -883,7 +880,7 @@ static int UpdateAircraftSpeed(Vehicle * * @param v The vehicle. Should be an aircraft * @returns Altitude in pixel units */ -static byte GetAircraftFlyingAltitude(const Vehicle *v) +byte GetAircraftFlyingAltitude(const Vehicle *v) { /* Make sure Aircraft fly no lower so that they don't conduct * CFITs (controlled flight into terrain) @@ -1433,7 +1430,7 @@ void AircraftNextAirportPos_and_Order(Ve v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, apc); } -static void AircraftLeaveHangar(Vehicle *v) +void AircraftLeaveHangar(Vehicle *v) { v->cur_speed = 0; v->subspeed = 0; @@ -2096,42 +2093,6 @@ Station *GetTargetAirportIfValid(const V return st->airport_tile == INVALID_TILE ? NULL : st; } -/** need to be called to load aircraft from old version */ -void UpdateOldAircraft() -{ - /* set airport_flags to 0 for all airports just to be sure */ - Station *st; - FOR_ALL_STATIONS(st) { - st->airport_flags = 0; // reset airport - } - - Vehicle *v_oldstyle; - FOR_ALL_VEHICLES(v_oldstyle) { - /* airplane has another vehicle with subtype 4 (shadow), helicopter also has 3 (rotor) - * skip those */ - if (v_oldstyle->type == VEH_AIRCRAFT && IsNormalAircraft(v_oldstyle)) { - /* airplane in terminal stopped doesn't hurt anyone, so goto next */ - if (v_oldstyle->vehstatus & VS_STOPPED && v_oldstyle->u.air.state == 0) { - v_oldstyle->u.air.state = HANGAR; - continue; - } - - AircraftLeaveHangar(v_oldstyle); // make airplane visible if it was in a depot for example - v_oldstyle->vehstatus &= ~VS_STOPPED; // make airplane moving - v_oldstyle->u.air.state = FLYING; - AircraftNextAirportPos_and_Order(v_oldstyle); // move it to the entry point of the airport - GetNewVehiclePosResult gp = GetNewVehiclePos(v_oldstyle); - v_oldstyle->tile = 0; // aircraft in air is tile=0 - - /* correct speed of helicopter-rotors */ - if (v_oldstyle->subtype == AIR_HELICOPTER) v_oldstyle->Next()->Next()->cur_speed = 32; - - /* set new position x,y,z */ - SetAircraftPosition(v_oldstyle, gp.x, gp.y, GetAircraftFlyingAltitude(v_oldstyle)); - } - } -} - /** * Updates the status of the Aircraft heading or in the station * @param st Station been updated diff --git a/src/animated_tile.cpp b/src/animated_tile.cpp --- a/src/animated_tile.cpp +++ b/src/animated_tile.cpp @@ -4,7 +4,6 @@ #include "stdafx.h" #include "openttd.h" -#include "saveload.h" #include "landscape.h" #include "core/alloc_func.hpp" #include "functions.h" @@ -14,7 +13,7 @@ TileIndex *_animated_tile_list = NULL; /** The number of animated tiles in the current state. */ uint _animated_tile_count = 0; /** The number of slots for animated tiles allocated currently. */ -static uint _animated_tile_allocated = 0; +uint _animated_tile_allocated = 0; /** * Removes the given tile from the animated tile table. @@ -90,46 +89,3 @@ void InitializeAnimatedTiles() _animated_tile_count = 0; _animated_tile_allocated = 256; } - -/** - * Save the ANIT chunk. - */ -static void Save_ANIT() -{ - SlSetLength(_animated_tile_count * sizeof(*_animated_tile_list)); - SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32); -} - -/** - * Load the ANIT chunk; the chunk containing the animated tiles. - */ -static void Load_ANIT() -{ - /* Before version 80 we did NOT have a variable length animated tile table */ - if (CheckSavegameVersion(80)) { - /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */ - SlArray(_animated_tile_list, 256, CheckSavegameVersion(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32); - - for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) { - if (_animated_tile_list[_animated_tile_count] == 0) break; - } - return; - } - - _animated_tile_count = (uint)SlGetFieldLength() / sizeof(*_animated_tile_list); - - /* Determine a nice rounded size for the amount of allocated tiles */ - _animated_tile_allocated = 256; - while (_animated_tile_allocated < _animated_tile_count) _animated_tile_allocated *= 2; - - _animated_tile_list = ReallocT(_animated_tile_list, _animated_tile_allocated); - SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32); -} - -/** - * "Definition" imported by the saveload code to be able to load and save - * the animated tile table. - */ -extern const ChunkHandler _animated_tile_chunk_handlers[] = { - { 'ANIT', Save_ANIT, Load_ANIT, CH_RIFF | CH_LAST}, -}; diff --git a/src/autoreplace.cpp b/src/autoreplace.cpp --- a/src/autoreplace.cpp +++ b/src/autoreplace.cpp @@ -6,7 +6,6 @@ #include "openttd.h" #include "debug.h" #include "command_func.h" -#include "saveload.h" #include "group.h" #include "autoreplace_base.h" #include "oldpool_func.h" @@ -102,46 +101,6 @@ CommandCost RemoveEngineReplacement(Engi return CMD_ERROR; } -static const SaveLoad _engine_renew_desc[] = { - SLE_VAR(EngineRenew, from, SLE_UINT16), - SLE_VAR(EngineRenew, to, SLE_UINT16), - - SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS), - SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, 60, SL_MAX_VERSION), - SLE_END() -}; - -static void Save_ERNW() -{ - EngineRenew *er; - - FOR_ALL_ENGINE_RENEWS(er) { - SlSetArrayIndex(er->index); - SlObject(er, _engine_renew_desc); - } -} - -static void Load_ERNW() -{ - int index; - - while ((index = SlIterateArray()) != -1) { - EngineRenew *er = new (index) EngineRenew(); - SlObject(er, _engine_renew_desc); - - /* Advanced vehicle lists, ungrouped vehicles got added */ - if (CheckSavegameVersion(60)) { - er->group_id = ALL_GROUP; - } else if (CheckSavegameVersion(71)) { - if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP; - } - } -} - -extern const ChunkHandler _autoreplace_chunk_handlers[] = { - { 'ERNW', Save_ERNW, Load_ERNW, CH_ARRAY | CH_LAST}, -}; - void InitializeEngineRenews() { /* Clean the engine renew pool and create 1 block in it */ diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -3,10 +3,8 @@ /** @file cargopacket.cpp Implementation of the cargo packets */ #include "stdafx.h" -#include "openttd.h" #include "station_base.h" #include "cargopacket.h" -#include "saveload.h" #include "oldpool_func.h" /* Initialize the cargopacket-pool */ @@ -43,42 +41,6 @@ bool CargoPacket::SameSource(const Cargo return this->source_xy == cp->source_xy && this->days_in_transit == cp->days_in_transit && this->paid_for == cp->paid_for; } -static const SaveLoad _cargopacket_desc[] = { - SLE_VAR(CargoPacket, source, SLE_UINT16), - SLE_VAR(CargoPacket, source_xy, SLE_UINT32), - SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32), - SLE_VAR(CargoPacket, count, SLE_UINT16), - SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8), - SLE_VAR(CargoPacket, feeder_share, SLE_INT64), - SLE_VAR(CargoPacket, paid_for, SLE_BOOL), - - SLE_END() -}; - -static void Save_CAPA() -{ - CargoPacket *cp; - - FOR_ALL_CARGOPACKETS(cp) { - SlSetArrayIndex(cp->index); - SlObject(cp, _cargopacket_desc); - } -} - -static void Load_CAPA() -{ - int index; - - while ((index = SlIterateArray()) != -1) { - CargoPacket *cp = new (index) CargoPacket(); - SlObject(cp, _cargopacket_desc); - } -} - -extern const ChunkHandler _cargopacket_chunk_handlers[] = { - { 'CAPA', Save_CAPA, Load_CAPA, CH_ARRAY | CH_LAST}, -}; - /* * * Cargo list implementation diff --git a/src/cheat.cpp b/src/cheat.cpp --- a/src/cheat.cpp +++ b/src/cheat.cpp @@ -3,7 +3,6 @@ /** @file cheat.cpp Handling (loading/saving/initializing) of cheats. */ #include "stdafx.h" -#include "saveload.h" #include "cheat_type.h" Cheats _cheats; @@ -13,31 +12,6 @@ void InitializeCheats() memset(&_cheats, 0, sizeof(Cheats)); } -static void Save_CHTS() -{ - /* Cannot use lengthof because _cheats is of type Cheats, not Cheat */ - byte count = sizeof(_cheats) / sizeof(Cheat); - Cheat *cht = (Cheat*) &_cheats; - Cheat *cht_last = &cht[count]; - - SlSetLength(count * 2); - for (; cht != cht_last; cht++) { - SlWriteByte(cht->been_used); - SlWriteByte(cht->value); - } -} - -static void Load_CHTS() -{ - Cheat *cht = (Cheat*)&_cheats; - size_t count = SlGetFieldLength() / 2; - - for (uint i = 0; i < count; i++) { - cht[i].been_used = (SlReadByte() != 0); - cht[i].value = (SlReadByte() != 0); - } -} - bool CheatHasBeenUsed() { /* Cannot use lengthof because _cheats is of type Cheats, not Cheat */ @@ -50,8 +24,3 @@ bool CheatHasBeenUsed() return false; } - - -extern const ChunkHandler _cheat_chunk_handlers[] = { - { 'CHTS', Save_CHTS, Load_CHTS, CH_RIFF | CH_LAST} -}; diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -10,7 +10,7 @@ #include "company_func.h" #include "gfx_func.h" #include "date_func.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "window_gui.h" #include "newgrf.h" #include "settings_type.h" diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -10,7 +10,6 @@ #include "company_gui.h" #include "town.h" #include "news_func.h" -#include "saveload.h" #include "command_func.h" #include "network/network.h" #include "network/network_func.h" @@ -111,74 +110,6 @@ void DrawCompanyIcon(CompanyID c, int x, } /** - * Converts an old company manager's face format to the new company manager's face format - * - * Meaning of the bits in the old face (some bits are used in several times): - * - 4 and 5: chin - * - 6 to 9: eyebrows - * - 10 to 13: nose - * - 13 to 15: lips (also moustache for males) - * - 16 to 19: hair - * - 20 to 22: eye color - * - 20 to 27: tie, ear rings etc. - * - 28 to 30: glasses - * - 19, 26 and 27: race (bit 27 set and bit 19 equal to bit 26 = black, otherwise white) - * - 31: gender (0 = male, 1 = female) - * - * @param face the face in the old format - * @return the face in the new format - */ -CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face) -{ - CompanyManagerFace cmf = 0; - GenderEthnicity ge = GE_WM; - - if (HasBit(face, 31)) SetBit(ge, GENDER_FEMALE); - if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) SetBit(ge, ETHNICITY_BLACK); - - SetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, ge, ge); - SetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge, GB(face, 28, 3) <= 1); - SetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge, HasBit(ge, ETHNICITY_BLACK) ? 0 : ClampU(GB(face, 20, 3), 5, 7) - 5); - SetCompanyManagerFaceBits(cmf, CMFV_CHIN, ge, ScaleCompanyManagerFaceValue(CMFV_CHIN, ge, GB(face, 4, 2))); - SetCompanyManagerFaceBits(cmf, CMFV_EYEBROWS, ge, ScaleCompanyManagerFaceValue(CMFV_EYEBROWS, ge, GB(face, 6, 4))); - SetCompanyManagerFaceBits(cmf, CMFV_HAIR, ge, ScaleCompanyManagerFaceValue(CMFV_HAIR, ge, GB(face, 16, 4))); - SetCompanyManagerFaceBits(cmf, CMFV_JACKET, ge, ScaleCompanyManagerFaceValue(CMFV_JACKET, ge, GB(face, 20, 2))); - SetCompanyManagerFaceBits(cmf, CMFV_COLLAR, ge, ScaleCompanyManagerFaceValue(CMFV_COLLAR, ge, GB(face, 22, 2))); - SetCompanyManagerFaceBits(cmf, CMFV_GLASSES, ge, GB(face, 28, 1)); - - uint lips = GB(face, 10, 4); - if (!HasBit(ge, GENDER_FEMALE) && lips < 4) { - SetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge, true); - SetCompanyManagerFaceBits(cmf, CMFV_MOUSTACHE, ge, max(lips, 1U) - 1); - } else { - if (!HasBit(ge, GENDER_FEMALE)) { - lips = lips * 15 / 16; - lips -= 3; - if (HasBit(ge, ETHNICITY_BLACK) && lips > 8) lips = 0; - } else { - lips = ScaleCompanyManagerFaceValue(CMFV_LIPS, ge, lips); - } - SetCompanyManagerFaceBits(cmf, CMFV_LIPS, ge, lips); - - uint nose = GB(face, 13, 3); - if (ge == GE_WF) { - nose = (nose * 3 >> 3) * 3 >> 2; // There is 'hole' in the nose sprites for females - } else { - nose = ScaleCompanyManagerFaceValue(CMFV_NOSE, ge, nose); - } - SetCompanyManagerFaceBits(cmf, CMFV_NOSE, ge, nose); - } - - uint tie_earring = GB(face, 24, 4); - if (!HasBit(ge, GENDER_FEMALE) || tie_earring < 3) { // Not all females have an earring - if (HasBit(ge, GENDER_FEMALE)) SetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge, true); - SetCompanyManagerFaceBits(cmf, CMFV_TIE_EARRING, ge, HasBit(ge, GENDER_FEMALE) ? tie_earring : ScaleCompanyManagerFaceValue(CMFV_TIE_EARRING, ge, tie_earring / 2)); - } - - return cmf; -} - -/** * Checks whether a company manager's face is a valid encoding. * Unused bits are not enforced to be 0. * @param cmf the fact to check @@ -936,159 +867,3 @@ CommandCost CmdCompanyCtrl(TileIndex til return CommandCost(); } - -/* Save/load of companies */ -static const SaveLoad _company_desc[] = { - SLE_VAR(Company, name_2, SLE_UINT32), - SLE_VAR(Company, name_1, SLE_STRINGID), - SLE_CONDSTR(Company, name, SLE_STR, 0, 84, SL_MAX_VERSION), - - SLE_VAR(Company, president_name_1, SLE_UINT16), - SLE_VAR(Company, president_name_2, SLE_UINT32), - SLE_CONDSTR(Company, president_name, SLE_STR, 0, 84, SL_MAX_VERSION), - - SLE_VAR(Company, face, SLE_UINT32), - - /* money was changed to a 64 bit field in savegame version 1. */ - SLE_CONDVAR(Company, money, SLE_VAR_I64 | SLE_FILE_I32, 0, 0), - SLE_CONDVAR(Company, money, SLE_INT64, 1, SL_MAX_VERSION), - - SLE_CONDVAR(Company, current_loan, SLE_VAR_I64 | SLE_FILE_I32, 0, 64), - SLE_CONDVAR(Company, current_loan, SLE_INT64, 65, SL_MAX_VERSION), - - SLE_VAR(Company, colour, SLE_UINT8), - SLE_VAR(Company, money_fraction, SLE_UINT8), - SLE_CONDVAR(Company, avail_railtypes, SLE_UINT8, 0, 57), - SLE_VAR(Company, block_preview, SLE_UINT8), - - SLE_CONDVAR(Company, cargo_types, SLE_FILE_U16 | SLE_VAR_U32, 0, 93), - SLE_CONDVAR(Company, cargo_types, SLE_UINT32, 94, SL_MAX_VERSION), - SLE_CONDVAR(Company, location_of_HQ, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Company, location_of_HQ, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Company, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Company, last_build_coordinate, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Company, inaugurated_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Company, inaugurated_year, SLE_INT32, 31, SL_MAX_VERSION), - - SLE_ARR(Company, share_owners, SLE_UINT8, 4), - - SLE_VAR(Company, num_valid_stat_ent, SLE_UINT8), - - SLE_VAR(Company, quarters_of_bankrupcy, SLE_UINT8), - SLE_CONDVAR(Company, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, 0, 103), - SLE_CONDVAR(Company, bankrupt_asked, SLE_UINT16, 104, SL_MAX_VERSION), - SLE_VAR(Company, bankrupt_timeout, SLE_INT16), - SLE_CONDVAR(Company, bankrupt_value, SLE_VAR_I64 | SLE_FILE_I32, 0, 64), - SLE_CONDVAR(Company, bankrupt_value, SLE_INT64, 65, SL_MAX_VERSION), - - /* yearly expenses was changed to 64-bit in savegame version 2. */ - SLE_CONDARR(Company, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, 0, 1), - SLE_CONDARR(Company, yearly_expenses, SLE_INT64, 3 * 13, 2, SL_MAX_VERSION), - - SLE_CONDVAR(Company, is_ai, SLE_BOOL, 2, SL_MAX_VERSION), - SLE_CONDNULL(1, 4, 99), - - /* Engine renewal settings */ - SLE_CONDNULL(512, 16, 18), - SLE_CONDREF(Company, engine_renew_list, REF_ENGINE_RENEWS, 19, SL_MAX_VERSION), - SLE_CONDVAR(Company, engine_renew, SLE_BOOL, 16, SL_MAX_VERSION), - SLE_CONDVAR(Company, engine_renew_months, SLE_INT16, 16, SL_MAX_VERSION), - SLE_CONDVAR(Company, engine_renew_money, SLE_UINT32, 16, SL_MAX_VERSION), - SLE_CONDVAR(Company, renew_keep_length, SLE_BOOL, 2, SL_MAX_VERSION), // added with 16.1, but was blank since 2 - - /* reserve extra space in savegame here. (currently 63 bytes) */ - SLE_CONDNULL(63, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static const SaveLoad _company_economy_desc[] = { - /* these were changed to 64-bit in savegame format 2 */ - SLE_CONDVAR(CompanyEconomyEntry, income, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), - SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, 2, SL_MAX_VERSION), - SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), - SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, 2, SL_MAX_VERSION), - SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), - SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, 2, SL_MAX_VERSION), - - SLE_VAR(CompanyEconomyEntry, delivered_cargo, SLE_INT32), - SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), - - SLE_END() -}; - -static const SaveLoad _company_livery_desc[] = { - SLE_CONDVAR(Livery, in_use, SLE_BOOL, 34, SL_MAX_VERSION), - SLE_CONDVAR(Livery, colour1, SLE_UINT8, 34, SL_MAX_VERSION), - SLE_CONDVAR(Livery, colour2, SLE_UINT8, 34, SL_MAX_VERSION), - SLE_END() -}; - -static void SaveLoad_PLYR(Company *c) -{ - int i; - - SlObject(c, _company_desc); - - /* Write AI? */ - if (!IsHumanCompany(c->index)) { - SaveLoad_AI(c->index); - } - - /* Write economy */ - SlObject(&c->cur_economy, _company_economy_desc); - - /* Write old economy entries. */ - for (i = 0; i < c->num_valid_stat_ent; i++) { - SlObject(&c->old_economy[i], _company_economy_desc); - } - - /* Write each livery entry. */ - int num_liveries = CheckSavegameVersion(63) ? LS_END - 4 : (CheckSavegameVersion(85) ? LS_END - 2: LS_END); - for (i = 0; i < num_liveries; i++) { - SlObject(&c->livery[i], _company_livery_desc); - } - - if (num_liveries < LS_END) { - /* We want to insert some liveries somewhere in between. This means some have to be moved. */ - memmove(&c->livery[LS_FREIGHT_WAGON], &c->livery[LS_PASSENGER_WAGON_MONORAIL], (LS_END - LS_FREIGHT_WAGON) * sizeof(c->livery[0])); - c->livery[LS_PASSENGER_WAGON_MONORAIL] = c->livery[LS_MONORAIL]; - c->livery[LS_PASSENGER_WAGON_MAGLEV] = c->livery[LS_MAGLEV]; - } - - if (num_liveries == LS_END - 4) { - /* Copy bus/truck liveries over to trams */ - c->livery[LS_PASSENGER_TRAM] = c->livery[LS_BUS]; - c->livery[LS_FREIGHT_TRAM] = c->livery[LS_TRUCK]; - } -} - -static void Save_PLYR() -{ - Company *c; - FOR_ALL_COMPANIES(c) { - SlSetArrayIndex(c->index); - SlAutolength((AutolengthProc*)SaveLoad_PLYR, c); - } -} - -static void Load_PLYR() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Company *c = new (index) Company(); - SaveLoad_PLYR(c); - _company_colours[index] = c->colour; - - /* This is needed so an AI is attached to a loaded AI */ - if (c->is_ai && (!_networking || _network_server) && _ai.enabled) { - /* Clear the memory of the new AI, otherwise we might be doing wrong things. */ - memset(&_companies_ainew[index], 0, sizeof(CompanyAiNew)); - AI_StartNewAI(c->index); - } - } -} - -extern const ChunkHandler _company_chunk_handlers[] = { - { 'PLYR', Save_PLYR, Load_PLYR, CH_ARRAY | CH_LAST}, -}; diff --git a/src/company_manager_face.h b/src/company_manager_face.h --- a/src/company_manager_face.h +++ b/src/company_manager_face.h @@ -7,6 +7,8 @@ #include "core/random_func.hpp" #include "core/bitmath_func.hpp" +#include "table/sprites.h" +#include "company_type.h" /** The gender/race combinations that we have faces for */ enum GenderEthnicity { @@ -228,7 +230,6 @@ static inline SpriteID GetCompanyManager } void DrawCompanyManagerFace(CompanyManagerFace face, int color, int x, int y); -CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face); bool IsValidCompanyManagerFace(CompanyManagerFace cmf); #endif /* COMPANY_MANAGER_FACE_H */ diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -8,7 +8,7 @@ #include "debug.h" #include "engine_func.h" #include "landscape.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "variables.h" #include "network/network.h" #include "network/network_func.h" diff --git a/src/date.cpp b/src/date.cpp --- a/src/date.cpp +++ b/src/date.cpp @@ -15,7 +15,7 @@ #include "vehicle_base.h" #include "debug.h" #include "rail_gui.h" -#include "saveload.h" +#include "saveload/saveload.h" Year _cur_year; ///< Current year, starting at 0 Month _cur_month; ///< Current month (0..11) diff --git a/src/depot.cpp b/src/depot.cpp --- a/src/depot.cpp +++ b/src/depot.cpp @@ -3,10 +3,8 @@ /** @file depot.cpp Handling of depots. */ #include "stdafx.h" -#include "openttd.h" #include "depot_base.h" #include "landscape.h" -#include "saveload.h" #include "order_func.h" #include "window_func.h" #include "oldpool_func.h" @@ -51,35 +49,3 @@ void InitializeDepots() _Depot_pool.CleanPool(); _Depot_pool.AddBlockToPool(); } - - -static const SaveLoad _depot_desc[] = { - SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Depot, xy, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_VAR(Depot, town_index, SLE_UINT16), - SLE_END() -}; - -static void Save_DEPT() -{ - Depot *depot; - - FOR_ALL_DEPOTS(depot) { - SlSetArrayIndex(depot->index); - SlObject(depot, _depot_desc); - } -} - -static void Load_DEPT() -{ - int index; - - while ((index = SlIterateArray()) != -1) { - Depot *depot = new (index) Depot(); - SlObject(depot, _depot_desc); - } -} - -extern const ChunkHandler _depot_chunk_handlers[] = { - { 'DEPT', Save_DEPT, Load_DEPT, CH_ARRAY | CH_LAST}, -}; diff --git a/src/economy.cpp b/src/economy.cpp --- a/src/economy.cpp +++ b/src/economy.cpp @@ -9,7 +9,6 @@ #include "company_base.h" #include "company_func.h" #include "command_func.h" -#include "saveload.h" #include "industry.h" #include "industry_map.h" #include "town.h" @@ -767,7 +766,7 @@ void SetPriceBaseMultiplier(uint price, * Initialize the variables that will maintain the daily industry change system. * @param init_counter specifies if the counter is required to be initialized */ -static void StartupIndustryDailyChanges(bool init_counter) +void StartupIndustryDailyChanges(bool init_counter) { uint map_size = MapLogX() + MapLogY(); /* After getting map size, it needs to be scaled appropriately and divided by 31, @@ -1121,37 +1120,6 @@ no_add:; InvalidateWindow(WC_SUBSIDIES_LIST, 0); } -static const SaveLoad _subsidies_desc[] = { - SLE_VAR(Subsidy, cargo_type, SLE_UINT8), - SLE_VAR(Subsidy, age, SLE_UINT8), - SLE_CONDVAR(Subsidy, from, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), - SLE_CONDVAR(Subsidy, from, SLE_UINT16, 5, SL_MAX_VERSION), - SLE_CONDVAR(Subsidy, to, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), - SLE_CONDVAR(Subsidy, to, SLE_UINT16, 5, SL_MAX_VERSION), - SLE_END() -}; - -static void Save_SUBS() -{ - int i; - Subsidy *s; - - for (i = 0; i != lengthof(_subsidies); i++) { - s = &_subsidies[i]; - if (s->cargo_type != CT_INVALID) { - SlSetArrayIndex(i); - SlObject(s, _subsidies_desc); - } - } -} - -static void Load_SUBS() -{ - int index; - while ((index = SlIterateArray()) != -1) - SlObject(&_subsidies[index], _subsidies_desc); -} - Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type) { const CargoSpec *cs = GetCargo(cargo_type); @@ -1994,54 +1962,3 @@ CommandCost CmdBuyCompany(TileIndex tile } return CommandCost(EXPENSES_OTHER, c->bankrupt_value); } - -/** Prices */ -static void SaveLoad_PRIC() -{ - int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64; - SlArray(&_price, NUM_PRICES, vt); - SlArray(&_price_frac, NUM_PRICES, SLE_UINT16); -} - -/** Cargo payment rates */ -static void SaveLoad_CAPR() -{ - uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO; - int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64; - SlArray(&_cargo_payment_rates, num_cargo, vt); - SlArray(&_cargo_payment_rates_frac, num_cargo, SLE_UINT16); -} - -static const SaveLoad _economy_desc[] = { - SLE_CONDVAR(Economy, max_loan, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), - SLE_CONDVAR(Economy, max_loan, SLE_INT64, 65, SL_MAX_VERSION), - SLE_CONDVAR(Economy, max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), - SLE_CONDVAR(Economy, max_loan_unround, SLE_INT64, 65, SL_MAX_VERSION), - SLE_CONDVAR(Economy, max_loan_unround_fract, SLE_UINT16, 70, SL_MAX_VERSION), - SLE_VAR(Economy, fluct, SLE_INT16), - SLE_VAR(Economy, interest_rate, SLE_UINT8), - SLE_VAR(Economy, infl_amount, SLE_UINT8), - SLE_VAR(Economy, infl_amount_pr, SLE_UINT8), - SLE_CONDVAR(Economy, industry_daily_change_counter, SLE_UINT32, 102, SL_MAX_VERSION), - SLE_END() -}; - -/** Economy variables */ -static void Save_ECMY() -{ - SlObject(&_economy, _economy_desc); -} - -/** Economy variables */ -static void Load_ECMY() -{ - SlObject(&_economy, _economy_desc); - StartupIndustryDailyChanges(CheckSavegameVersion(102)); // old savegames will need to be initialized -} - -extern const ChunkHandler _economy_chunk_handlers[] = { - { 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH}, - { 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH}, - { 'SUBS', Save_SUBS, Load_SUBS, CH_ARRAY}, - { 'ECMY', Save_ECMY, Load_ECMY, CH_RIFF | CH_LAST}, -}; diff --git a/src/economy_func.h b/src/economy_func.h --- a/src/economy_func.h +++ b/src/economy_func.h @@ -34,6 +34,7 @@ Pair SetupSubsidyDecodeParam(const Subsi void DeleteSubsidyWithTown(TownID index); void DeleteSubsidyWithIndustry(IndustryID index); void DeleteSubsidyWithStation(StationID index); +void StartupIndustryDailyChanges(bool init_counter); Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type); uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount); diff --git a/src/effectvehicle.cpp b/src/effectvehicle.cpp --- a/src/effectvehicle.cpp +++ b/src/effectvehicle.cpp @@ -15,7 +15,6 @@ #include "gfx_func.h" #include "news_func.h" #include "command_func.h" -#include "saveload.h" #include "company_func.h" #include "debug.h" #include "vehicle_gui.h" diff --git a/src/engine.cpp b/src/engine.cpp --- a/src/engine.cpp +++ b/src/engine.cpp @@ -9,7 +9,6 @@ #include "company_func.h" #include "command_func.h" #include "news_func.h" -#include "saveload.h" #include "variables.h" #include "train.h" #include "aircraft.h" @@ -27,7 +26,6 @@ #include "oldpool_func.h" #include "core/alloc_func.hpp" #include "vehicle_func.h" -#include #include "table/strings.h" #include "table/engines.h" @@ -613,112 +611,3 @@ CargoID GetEngineCargoType(EngineID engi default: NOT_REACHED(); return CT_INVALID; } } - -static const SaveLoad _engine_desc[] = { - SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Engine, intro_date, SLE_INT32, 31, SL_MAX_VERSION), - SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Engine, age, SLE_INT32, 31, SL_MAX_VERSION), - SLE_VAR(Engine, reliability, SLE_UINT16), - SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16), - SLE_VAR(Engine, reliability_start, SLE_UINT16), - SLE_VAR(Engine, reliability_max, SLE_UINT16), - SLE_VAR(Engine, reliability_final, SLE_UINT16), - SLE_VAR(Engine, duration_phase_1, SLE_UINT16), - SLE_VAR(Engine, duration_phase_2, SLE_UINT16), - SLE_VAR(Engine, duration_phase_3, SLE_UINT16), - - SLE_VAR(Engine, lifelength, SLE_UINT8), - SLE_VAR(Engine, flags, SLE_UINT8), - SLE_VAR(Engine, preview_company_rank,SLE_UINT8), - SLE_VAR(Engine, preview_wait, SLE_UINT8), - SLE_CONDNULL(1, 0, 44), - SLE_CONDVAR(Engine, company_avail, SLE_FILE_U8 | SLE_VAR_U16, 0, 103), - SLE_CONDVAR(Engine, company_avail, SLE_UINT16, 104, SL_MAX_VERSION), - SLE_CONDSTR(Engine, name, SLE_STR, 0, 84, SL_MAX_VERSION), - - /* reserve extra space in savegame here. (currently 16 bytes) */ - SLE_CONDNULL(16, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static std::map _temp_engine; - -Engine *GetTempDataEngine(EngineID index) -{ - return &_temp_engine[index]; -} - -static void Save_ENGN() -{ - Engine *e; - FOR_ALL_ENGINES(e) { - SlSetArrayIndex(e->index); - SlObject(e, _engine_desc); - } -} - -static void Load_ENGN() -{ - /* As engine data is loaded before engines are initialized we need to load - * this information into a temporary array. This is then copied into the - * engine pool after processing NewGRFs by CopyTempEngineData(). */ - int index; - while ((index = SlIterateArray()) != -1) { - Engine *e = GetTempDataEngine(index); - SlObject(e, _engine_desc); - } -} - -/** - * Copy data from temporary engine array into the real engine pool. - */ -void CopyTempEngineData() -{ - Engine *e; - FOR_ALL_ENGINES(e) { - if (e->index >= _temp_engine.size()) break; - - const Engine *se = GetTempDataEngine(e->index); - e->intro_date = se->intro_date; - e->age = se->age; - e->reliability = se->reliability; - e->reliability_spd_dec = se->reliability_spd_dec; - e->reliability_start = se->reliability_start; - e->reliability_max = se->reliability_max; - e->reliability_final = se->reliability_final; - e->duration_phase_1 = se->duration_phase_1; - e->duration_phase_2 = se->duration_phase_2; - e->duration_phase_3 = se->duration_phase_3; - e->lifelength = se->lifelength; - e->flags = se->flags; - e->preview_company_rank= se->preview_company_rank; - e->preview_wait = se->preview_wait; - e->company_avail = se->company_avail; - if (se->name != NULL) e->name = strdup(se->name); - } - - /* Get rid of temporary data */ - _temp_engine.clear(); -} - -static void Load_ENGS() -{ - /* Load old separate String ID list into a temporary array. This - * was always 256 entries. */ - StringID names[256]; - - SlArray(names, lengthof(names), SLE_STRINGID); - - /* Copy each string into the temporary engine array. */ - for (EngineID engine = 0; engine < lengthof(names); engine++) { - Engine *e = GetTempDataEngine(engine); - e->name = CopyFromOldName(names[engine]); - } -} - -extern const ChunkHandler _engine_chunk_handlers[] = { - { 'ENGN', Save_ENGN, Load_ENGN, CH_ARRAY }, - { 'ENGS', NULL, Load_ENGS, CH_RIFF | CH_LAST }, -}; diff --git a/src/gamelog.cpp b/src/gamelog.cpp --- a/src/gamelog.cpp +++ b/src/gamelog.cpp @@ -4,7 +4,7 @@ #include "stdafx.h" #include "openttd.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "core/alloc_func.hpp" #include "core/bitmath_func.hpp" #include "core/math_func.hpp" @@ -13,13 +13,15 @@ #include "string_func.h" #include "settings_type.h" #include "newgrf_config.h" -#include -#include #include "gamelog.h" +#include "gamelog_internal.h" #include "console_func.h" #include "debug.h" #include "rev.h" +#include +#include + extern const uint16 SAVEGAME_VERSION; ///< current savegame version extern SavegameType _savegame_type; ///< type of savegame we are loading @@ -28,79 +30,11 @@ extern uint32 _ttdp_version; ///< ve extern uint16 _sl_version; ///< the major savegame version identifier extern byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! -/** Type of logged change */ -enum GamelogChangeType { - GLCT_MODE, ///< Scenario editor x Game, different landscape - GLCT_REVISION, ///< Changed game revision string - GLCT_OLDVER, ///< Loaded from savegame without logged data - GLCT_PATCH, ///< Non-networksafe patch value changed - GLCT_GRFADD, ///< Removed GRF - GLCT_GRFREM, ///< Added GRF - GLCT_GRFCOMPAT, ///< Loading compatible GRF - GLCT_GRFPARAM, ///< GRF parameter changed - GLCT_GRFMOVE, ///< GRF order changed - GLCT_GRFBUG, ///< GRF bug triggered - GLCT_END, ///< So we know how many GLCTs are there - GLCT_NONE = 0xFF, ///< In savegames, end of list -}; - - -/** Contains information about one logged change */ -struct LoggedChange { - GamelogChangeType ct; ///< Type of change logged in this struct - union { - struct { - byte mode; ///< new game mode - Editor x Game - byte landscape; ///< landscape (temperate, arctic, ...) - } mode; - struct { - char text[NETWORK_REVISION_LENGTH]; ///< revision string, _openttd_revision - uint32 newgrf; ///< _openttd_newgrf_version - uint16 slver; ///< _sl_version - byte modified; ///< _openttd_revision_modified - } revision; - struct { - uint32 type; ///< type of savegame, @see SavegameType - uint32 version; ///< major and minor version OR ttdp version - } oldver; - GRFIdentifier grfadd; ///< ID and md5sum of added GRF - struct { - uint32 grfid; ///< ID of removed GRF - } grfrem; - GRFIdentifier grfcompat; ///< ID and new md5sum of changed GRF - struct { - uint32 grfid; ///< ID of GRF with changed parameters - } grfparam; - struct { - uint32 grfid; ///< ID of moved GRF - int32 offset; ///< offset, positive = move down - } grfmove; - struct { - char *name; ///< name of the patch - int32 oldval; ///< old value - int32 newval; ///< new value - } patch; - struct { - uint64 data; ///< additional data - uint32 grfid; ///< ID of problematic GRF - byte bug; ///< type of bug, @see enum GRFBugs - } grfbug; - }; -}; - - -/** Contains information about one logged action that caused at least one logged change */ -struct LoggedAction { - LoggedChange *change; ///< First logged change in this action - uint32 changes; ///< Number of changes in this action - GamelogActionType at; ///< Type of action - uint16 tick; ///< Tick when it happened -}; static GamelogActionType _gamelog_action_type = GLAT_NONE; ///< action to record if anything changes -static LoggedAction *_gamelog_action = NULL; ///< first logged action -static uint _gamelog_actions = 0; ///< number of actions +LoggedAction *_gamelog_action = NULL; ///< first logged action +uint _gamelog_actions = 0; ///< number of actions static LoggedAction *_current_action = NULL; ///< current action we are logging, NULL when there is no action active @@ -728,155 +662,3 @@ void GamelogGRFUpdate(const GRFConfig *o free(ol); free(nl); } - - -static const SaveLoad _glog_action_desc[] = { - SLE_VAR(LoggedAction, tick, SLE_UINT16), - SLE_END() -}; - -static const SaveLoad _glog_mode_desc[] = { - SLE_VAR(LoggedChange, mode.mode, SLE_UINT8), - SLE_VAR(LoggedChange, mode.landscape, SLE_UINT8), - SLE_END() -}; - -static const SaveLoad _glog_revision_desc[] = { - SLE_ARR(LoggedChange, revision.text, SLE_UINT8, NETWORK_REVISION_LENGTH), - SLE_VAR(LoggedChange, revision.newgrf, SLE_UINT32), - SLE_VAR(LoggedChange, revision.slver, SLE_UINT16), - SLE_VAR(LoggedChange, revision.modified, SLE_UINT8), - SLE_END() -}; - -static const SaveLoad _glog_oldver_desc[] = { - SLE_VAR(LoggedChange, oldver.type, SLE_UINT32), - SLE_VAR(LoggedChange, oldver.version, SLE_UINT32), - SLE_END() -}; - -static const SaveLoad _glog_patch_desc[] = { - SLE_STR(LoggedChange, patch.name, SLE_STR, 128), - SLE_VAR(LoggedChange, patch.oldval, SLE_INT32), - SLE_VAR(LoggedChange, patch.newval, SLE_INT32), - SLE_END() -}; - -static const SaveLoad _glog_grfadd_desc[] = { - SLE_VAR(LoggedChange, grfadd.grfid, SLE_UINT32 ), - SLE_ARR(LoggedChange, grfadd.md5sum, SLE_UINT8, 16), - SLE_END() -}; - -static const SaveLoad _glog_grfrem_desc[] = { - SLE_VAR(LoggedChange, grfrem.grfid, SLE_UINT32), - SLE_END() -}; - -static const SaveLoad _glog_grfcompat_desc[] = { - SLE_VAR(LoggedChange, grfcompat.grfid, SLE_UINT32 ), - SLE_ARR(LoggedChange, grfcompat.md5sum, SLE_UINT8, 16), - SLE_END() -}; - -static const SaveLoad _glog_grfparam_desc[] = { - SLE_VAR(LoggedChange, grfparam.grfid, SLE_UINT32), - SLE_END() -}; - -static const SaveLoad _glog_grfmove_desc[] = { - SLE_VAR(LoggedChange, grfmove.grfid, SLE_UINT32), - SLE_VAR(LoggedChange, grfmove.offset, SLE_INT32), - SLE_END() -}; - -static const SaveLoad _glog_grfbug_desc[] = { - SLE_VAR(LoggedChange, grfbug.data, SLE_UINT64), - SLE_VAR(LoggedChange, grfbug.grfid, SLE_UINT32), - SLE_VAR(LoggedChange, grfbug.bug, SLE_UINT8), - SLE_END() -}; - -static const SaveLoad *_glog_desc[] = { - _glog_mode_desc, - _glog_revision_desc, - _glog_oldver_desc, - _glog_patch_desc, - _glog_grfadd_desc, - _glog_grfrem_desc, - _glog_grfcompat_desc, - _glog_grfparam_desc, - _glog_grfmove_desc, - _glog_grfbug_desc, -}; - -assert_compile(lengthof(_glog_desc) == GLCT_END); - -static void Load_GLOG() -{ - assert(_gamelog_action == NULL); - assert(_gamelog_actions == 0); - - GamelogActionType at; - while ((at = (GamelogActionType)SlReadByte()) != GLAT_NONE) { - _gamelog_action = ReallocT(_gamelog_action, _gamelog_actions + 1); - LoggedAction *la = &_gamelog_action[_gamelog_actions++]; - - la->at = at; - - SlObject(la, _glog_action_desc); // has to be saved after 'DATE'! - la->change = NULL; - la->changes = 0; - - GamelogChangeType ct; - while ((ct = (GamelogChangeType)SlReadByte()) != GLCT_NONE) { - la->change = ReallocT(la->change, la->changes + 1); - - LoggedChange *lc = &la->change[la->changes++]; - /* for SLE_STR, pointer has to be valid! so make it NULL */ - memset(lc, 0, sizeof(*lc)); - lc->ct = ct; - - assert((uint)ct < GLCT_END); - - SlObject(lc, _glog_desc[ct]); - } - } -} - -static void Save_GLOG() -{ - const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; - size_t length = 0; - - for (const LoggedAction *la = _gamelog_action; la != laend; la++) { - const LoggedChange *lcend = &la->change[la->changes]; - for (LoggedChange *lc = la->change; lc != lcend; lc++) { - assert((uint)lc->ct < lengthof(_glog_desc)); - length += SlCalcObjLength(lc, _glog_desc[lc->ct]) + 1; - } - length += 4; - } - length++; - - SlSetLength(length); - - for (LoggedAction *la = _gamelog_action; la != laend; la++) { - SlWriteByte(la->at); - SlObject(la, _glog_action_desc); - - const LoggedChange *lcend = &la->change[la->changes]; - for (LoggedChange *lc = la->change; lc != lcend; lc++) { - SlWriteByte(lc->ct); - assert((uint)lc->ct < GLCT_END); - SlObject(lc, _glog_desc[lc->ct]); - } - SlWriteByte(GLCT_NONE); - } - SlWriteByte(GLAT_NONE); -} - - -extern const ChunkHandler _gamelog_chunk_handlers[] = { - { 'GLOG', Save_GLOG, Load_GLOG, CH_RIFF | CH_LAST } -}; diff --git a/src/gamelog_internal.h b/src/gamelog_internal.h new file mode 100644 --- /dev/null +++ b/src/gamelog_internal.h @@ -0,0 +1,82 @@ +/* $Id$ */ + +/** @file gamelog_internal.h Declaration shared among gamelog.cpp and saveload/gamelog_sl.cpp */ + +#ifndef GAMELOG_INTERNAL_H +#define GAMELOG_INTERNAL_H + +#include "network/core/config.h" + +/** Type of logged change */ +enum GamelogChangeType { + GLCT_MODE, ///< Scenario editor x Game, different landscape + GLCT_REVISION, ///< Changed game revision string + GLCT_OLDVER, ///< Loaded from savegame without logged data + GLCT_PATCH, ///< Non-networksafe patch value changed + GLCT_GRFADD, ///< Removed GRF + GLCT_GRFREM, ///< Added GRF + GLCT_GRFCOMPAT, ///< Loading compatible GRF + GLCT_GRFPARAM, ///< GRF parameter changed + GLCT_GRFMOVE, ///< GRF order changed + GLCT_GRFBUG, ///< GRF bug triggered + GLCT_END, ///< So we know how many GLCTs are there + GLCT_NONE = 0xFF, ///< In savegames, end of list +}; + + +/** Contains information about one logged change */ +struct LoggedChange { + GamelogChangeType ct; ///< Type of change logged in this struct + union { + struct { + byte mode; ///< new game mode - Editor x Game + byte landscape; ///< landscape (temperate, arctic, ...) + } mode; + struct { + char text[NETWORK_REVISION_LENGTH]; ///< revision string, _openttd_revision + uint32 newgrf; ///< _openttd_newgrf_version + uint16 slver; ///< _sl_version + byte modified; ///< _openttd_revision_modified + } revision; + struct { + uint32 type; ///< type of savegame, @see SavegameType + uint32 version; ///< major and minor version OR ttdp version + } oldver; + GRFIdentifier grfadd; ///< ID and md5sum of added GRF + struct { + uint32 grfid; ///< ID of removed GRF + } grfrem; + GRFIdentifier grfcompat; ///< ID and new md5sum of changed GRF + struct { + uint32 grfid; ///< ID of GRF with changed parameters + } grfparam; + struct { + uint32 grfid; ///< ID of moved GRF + int32 offset; ///< offset, positive = move down + } grfmove; + struct { + char *name; ///< name of the patch + int32 oldval; ///< old value + int32 newval; ///< new value + } patch; + struct { + uint64 data; ///< additional data + uint32 grfid; ///< ID of problematic GRF + byte bug; ///< type of bug, @see enum GRFBugs + } grfbug; + }; +}; + + +/** Contains information about one logged action that caused at least one logged change */ +struct LoggedAction { + LoggedChange *change; ///< First logged change in this action + uint32 changes; ///< Number of changes in this action + GamelogActionType at; ///< Type of action + uint16 tick; ///< Tick when it happened +}; + +extern LoggedAction *_gamelog_action; +extern uint _gamelog_actions; + +#endif /* GAMELOG_INTERNAL_H */ diff --git a/src/genworld.cpp b/src/genworld.cpp --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -26,7 +26,7 @@ #include "newgrf_storage.h" #include "water.h" #include "tilehighlight_func.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "table/sprites.h" diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -6,7 +6,6 @@ #include "openttd.h" #include "variables.h" #include "command_func.h" -#include "saveload.h" #include "debug.h" #include "group.h" #include "train.h" @@ -421,40 +420,3 @@ void RemoveAllGroupsForCompany(const Com if (company == g->owner) delete g; } } - - -static const SaveLoad _group_desc[] = { - SLE_CONDVAR(Group, name, SLE_NAME, 0, 83), - SLE_CONDSTR(Group, name, SLE_STR, 0, 84, SL_MAX_VERSION), - SLE_VAR(Group, num_vehicle, SLE_UINT16), - SLE_VAR(Group, owner, SLE_UINT8), - SLE_VAR(Group, vehicle_type, SLE_UINT8), - SLE_VAR(Group, replace_protection, SLE_BOOL), - SLE_END() -}; - - -static void Save_GROUP(void) -{ - Group *g; - - FOR_ALL_GROUPS(g) { - SlSetArrayIndex(g->index); - SlObject(g, _group_desc); - } -} - - -static void Load_GROUP(void) -{ - int index; - - while ((index = SlIterateArray()) != -1) { - Group *g = new (index) Group(); - SlObject(g, _group_desc); - } -} - -extern const ChunkHandler _group_chunk_handlers[] = { - { 'GRPS', Save_GROUP, Load_GROUP, CH_ARRAY | CH_LAST}, -}; diff --git a/src/heightmap.cpp b/src/heightmap.cpp --- a/src/heightmap.cpp +++ b/src/heightmap.cpp @@ -10,7 +10,7 @@ #include "void_map.h" #include "debug.h" #include "gui.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "bmp.h" #include "gfx_func.h" #include "core/alloc_func.hpp" diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -14,7 +14,6 @@ #include "industry.h" #include "town.h" #include "news_func.h" -#include "saveload.h" #include "variables.h" #include "cheat_func.h" #include "genworld.h" @@ -2397,146 +2396,3 @@ extern const TileTypeProcs _tile_type_in GetFoundation_Industry, /* get_foundation_proc */ TerraformTile_Industry, /* terraform_tile_proc */ }; - -static const SaveLoad _industry_desc[] = { - SLE_CONDVAR(Industry, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Industry, xy, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_VAR(Industry, width, SLE_UINT8), - SLE_VAR(Industry, height, SLE_UINT8), - SLE_REF(Industry, town, REF_TOWN), - SLE_CONDNULL( 2, 0, 60), ///< used to be industry's produced_cargo - SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, 78, SL_MAX_VERSION), - SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, 70, SL_MAX_VERSION), - SLE_ARR(Industry, produced_cargo_waiting, SLE_UINT16, 2), - SLE_ARR(Industry, production_rate, SLE_UINT8, 2), - SLE_CONDNULL( 3, 0, 60), ///< used to be industry's accepts_cargo - SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, 78, SL_MAX_VERSION), - SLE_VAR(Industry, prod_level, SLE_UINT8), - SLE_ARR(Industry, this_month_production, SLE_UINT16, 2), - SLE_ARR(Industry, this_month_transported, SLE_UINT16, 2), - SLE_ARR(Industry, last_month_pct_transported, SLE_UINT8, 2), - SLE_ARR(Industry, last_month_production, SLE_UINT16, 2), - SLE_ARR(Industry, last_month_transported, SLE_UINT16, 2), - - SLE_VAR(Industry, counter, SLE_UINT16), - - SLE_VAR(Industry, type, SLE_UINT8), - SLE_VAR(Industry, owner, SLE_UINT8), - SLE_VAR(Industry, random_color, SLE_UINT8), - SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, 31, SL_MAX_VERSION), - SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8), - - SLE_CONDVAR(Industry, founder, SLE_UINT8, 70, SL_MAX_VERSION), - SLE_CONDVAR(Industry, construction_date, SLE_INT32, 70, SL_MAX_VERSION), - SLE_CONDVAR(Industry, construction_type, SLE_UINT8, 70, SL_MAX_VERSION), - SLE_CONDVAR(Industry, last_cargo_accepted_at, SLE_INT32, 70, SL_MAX_VERSION), - SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, 73, SL_MAX_VERSION), - - SLE_CONDARRX(cpp_offsetof(Industry, psa) + cpp_offsetof(Industry::PersistentStorage, storage), SLE_UINT32, 16, 76, SL_MAX_VERSION), - - SLE_CONDVAR(Industry, random_triggers, SLE_UINT8, 82, SL_MAX_VERSION), - SLE_CONDVAR(Industry, random, SLE_UINT16, 82, SL_MAX_VERSION), - - /* reserve extra space in savegame here. (currently 32 bytes) */ - SLE_CONDNULL(32, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static void Save_INDY() -{ - Industry *ind; - - /* Write the industries */ - FOR_ALL_INDUSTRIES(ind) { - SlSetArrayIndex(ind->index); - SlObject(ind, _industry_desc); - } -} - -/* Save and load the mapping between the industry/tile id on the map, and the grf file - * it came from. */ -static const SaveLoad _industries_id_mapping_desc[] = { - SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), - SLE_VAR(EntityIDMapping, entity_id, SLE_UINT8), - SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT8), - SLE_END() -}; - -static void Save_IIDS() -{ - uint i; - uint j = _industry_mngr.GetMaxMapping(); - - for (i = 0; i < j; i++) { - SlSetArrayIndex(i); - SlObject(&_industry_mngr.mapping_ID[i], _industries_id_mapping_desc); - } -} - -static void Save_TIDS() -{ - uint i; - uint j = _industile_mngr.GetMaxMapping(); - - for (i = 0; i < j; i++) { - SlSetArrayIndex(i); - SlObject(&_industile_mngr.mapping_ID[i], _industries_id_mapping_desc); - } -} - -static void Load_INDY() -{ - int index; - - ResetIndustryCounts(); - - while ((index = SlIterateArray()) != -1) { - Industry *i = new (index) Industry(); - SlObject(i, _industry_desc); - IncIndustryTypeCount(i->type); - } -} - -static void Load_IIDS() -{ - int index; - uint max_id; - - /* clear the current mapping stored. - * This will create the manager if ever it is not yet done */ - _industry_mngr.ResetMapping(); - - /* get boundary for the temporary map loader NUM_INDUSTRYTYPES? */ - max_id = _industry_mngr.GetMaxMapping(); - - while ((index = SlIterateArray()) != -1) { - if ((uint)index >= max_id) break; - SlObject(&_industry_mngr.mapping_ID[index], _industries_id_mapping_desc); - } -} - -static void Load_TIDS() -{ - int index; - uint max_id; - - /* clear the current mapping stored. - * This will create the manager if ever it is not yet done */ - _industile_mngr.ResetMapping(); - - /* get boundary for the temporary map loader NUM_INDUSTILES? */ - max_id = _industile_mngr.GetMaxMapping(); - - while ((index = SlIterateArray()) != -1) { - if ((uint)index >= max_id) break; - SlObject(&_industile_mngr.mapping_ID[index], _industries_id_mapping_desc); - } -} - -extern const ChunkHandler _industry_chunk_handlers[] = { - { 'INDY', Save_INDY, Load_INDY, CH_ARRAY}, - { 'IIDS', Save_IIDS, Load_IIDS, CH_ARRAY}, - { 'TIDS', Save_TIDS, Load_TIDS, CH_ARRAY | CH_LAST}, -}; diff --git a/src/misc.cpp b/src/misc.cpp --- a/src/misc.cpp +++ b/src/misc.cpp @@ -7,7 +7,6 @@ #include "currency.h" #include "landscape.h" #include "news_func.h" -#include "saveload.h" #include "vehicle_gui.h" #include "variables.h" #include "cheat_func.h" @@ -134,290 +133,3 @@ void InitializeLandscapeVariables(bool o _cargo_payment_rates_frac[i] = 0; } } - -static const SaveLoadGlobVarList _date_desc[] = { - SLEG_CONDVAR(_date, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), - SLEG_CONDVAR(_date, SLE_INT32, 31, SL_MAX_VERSION), - SLEG_VAR(_date_fract, SLE_UINT16), - SLEG_VAR(_tick_counter, SLE_UINT16), - SLEG_VAR(_vehicle_id_ctr_day, SLE_UINT16), - SLEG_VAR(_age_cargo_skip_counter, SLE_UINT8), - SLE_CONDNULL(1, 0, 45), - SLEG_CONDVAR(_cur_tileloop_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLEG_CONDVAR(_cur_tileloop_tile, SLE_UINT32, 6, SL_MAX_VERSION), - SLEG_VAR(_disaster_delay, SLE_UINT16), - SLEG_VAR(_station_tick_ctr, SLE_UINT16), - SLEG_VAR(_random.state[0], SLE_UINT32), - SLEG_VAR(_random.state[1], SLE_UINT32), - SLEG_CONDVAR(_cur_town_ctr, SLE_FILE_U8 | SLE_VAR_U32, 0, 9), - SLEG_CONDVAR(_cur_town_ctr, SLE_UINT32, 10, SL_MAX_VERSION), - SLEG_VAR(_cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32), - SLEG_VAR(_next_competitor_start, SLE_FILE_U16 | SLE_VAR_U32), - SLEG_VAR(_trees_tick_ctr, SLE_UINT8), - SLEG_CONDVAR(_pause_game, SLE_UINT8, 4, SL_MAX_VERSION), - SLEG_CONDVAR(_cur_town_iter, SLE_UINT32, 11, SL_MAX_VERSION), - SLEG_END() -}; - -/* Save load date related variables as well as persistent tick counters - * XXX: currently some unrelated stuff is just put here */ -static void SaveLoad_DATE() -{ - SlGlobList(_date_desc); -} - - -static const SaveLoadGlobVarList _view_desc[] = { - SLEG_CONDVAR(_saved_scrollpos_x, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), - SLEG_CONDVAR(_saved_scrollpos_x, SLE_INT32, 6, SL_MAX_VERSION), - SLEG_CONDVAR(_saved_scrollpos_y, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), - SLEG_CONDVAR(_saved_scrollpos_y, SLE_INT32, 6, SL_MAX_VERSION), - SLEG_VAR(_saved_scrollpos_zoom, SLE_UINT8), - SLEG_END() -}; - -static void SaveLoad_VIEW() -{ - SlGlobList(_view_desc); -} - -static uint32 _map_dim_x; -static uint32 _map_dim_y; - -static const SaveLoadGlobVarList _map_dimensions[] = { - SLEG_CONDVAR(_map_dim_x, SLE_UINT32, 6, SL_MAX_VERSION), - SLEG_CONDVAR(_map_dim_y, SLE_UINT32, 6, SL_MAX_VERSION), - SLEG_END() -}; - -static void Save_MAPS() -{ - _map_dim_x = MapSizeX(); - _map_dim_y = MapSizeY(); - SlGlobList(_map_dimensions); -} - -static void Load_MAPS() -{ - SlGlobList(_map_dimensions); - AllocateMap(_map_dim_x, _map_dim_y); -} - -enum { - MAP_SL_BUF_SIZE = 4096 -}; - -static void Load_MAPT() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type_height = buf[j]; - } -} - -static void Save_MAPT() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - SlSetLength(size); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].type_height; - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - } -} - -static void Load_MAP1() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; - } -} - -static void Save_MAP1() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - SlSetLength(size); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m1; - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - } -} - -static void Load_MAP2() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf, MAP_SL_BUF_SIZE, - /* In those versions the m2 was 8 bits */ - CheckSavegameVersion(5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16 - ); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j]; - } -} - -static void Save_MAP2() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - SlSetLength(size * sizeof(uint16)); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m2; - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT16); - } -} - -static void Load_MAP3() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; - } -} - -static void Save_MAP3() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - SlSetLength(size); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m3; - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - } -} - -static void Load_MAP4() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; - } -} - -static void Save_MAP4() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - SlSetLength(size); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m4; - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - } -} - -static void Load_MAP5() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; - } -} - -static void Save_MAP5() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - SlSetLength(size); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m5; - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - } -} - -static void Load_MAP6() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - if (CheckSavegameVersion(42)) { - for (TileIndex i = 0; i != size;) { - /* 1024, otherwise we overflow on 64x64 maps! */ - SlArray(buf, 1024, SLE_UINT8); - for (uint j = 0; j != 1024; j++) { - _m[i++].m6 = GB(buf[j], 0, 2); - _m[i++].m6 = GB(buf[j], 2, 2); - _m[i++].m6 = GB(buf[j], 4, 2); - _m[i++].m6 = GB(buf[j], 6, 2); - } - } - } else { - for (TileIndex i = 0; i != size;) { - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m6 = buf[j]; - } - } -} - -static void Save_MAP6() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - SlSetLength(size); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m6; - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - } -} - -static void Load_MAP7() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; - } -} - -static void Save_MAP7() -{ - SmallStackSafeStackAlloc buf; - TileIndex size = MapSize(); - - SlSetLength(size); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m7; - SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - } -} - -extern const ChunkHandler _misc_chunk_handlers[] = { - { 'MAPS', Save_MAPS, Load_MAPS, CH_RIFF }, - { 'MAPT', Save_MAPT, Load_MAPT, CH_RIFF }, - { 'MAPO', Save_MAP1, Load_MAP1, CH_RIFF }, - { 'MAP2', Save_MAP2, Load_MAP2, CH_RIFF }, - { 'M3LO', Save_MAP3, Load_MAP3, CH_RIFF }, - { 'M3HI', Save_MAP4, Load_MAP4, CH_RIFF }, - { 'MAP5', Save_MAP5, Load_MAP5, CH_RIFF }, - { 'MAPE', Save_MAP6, Load_MAP6, CH_RIFF }, - { 'MAP7', Save_MAP7, Load_MAP7, CH_RIFF }, - - { 'DATE', SaveLoad_DATE, SaveLoad_DATE, CH_RIFF}, - { 'VIEW', SaveLoad_VIEW, SaveLoad_VIEW, CH_RIFF | CH_LAST}, -}; diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -9,7 +9,7 @@ #include "landscape.h" #include "newgrf.h" #include "newgrf_text.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "tile_map.h" #include "gui.h" #include "window_gui.h" diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -12,7 +12,7 @@ #include "network_client.h" #include "network_gamelist.h" #include "network_gui.h" -#include "../saveload.h" +#include "../saveload/saveload.h" #include "../command_func.h" #include "../console_func.h" #include "../variables.h" diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -17,7 +17,7 @@ #include "network_udp.h" #include "../console_func.h" #include "../command_func.h" -#include "../saveload.h" +#include "../saveload/saveload.h" #include "../station_base.h" #include "../variables.h" #include "../genworld.h" diff --git a/src/newgrf.h b/src/newgrf.h --- a/src/newgrf.h +++ b/src/newgrf.h @@ -121,7 +121,7 @@ extern GRFLoadedFeatures _loaded_newgrf_ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage); void LoadNewGRF(uint load_index, uint file_index); -void ReloadNewGRFData(); // in openttd.cpp +void ReloadNewGRFData(); // in saveload/afterload.cpp void CDECL grfmsg(int severity, const char *str, ...); diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -6,7 +6,6 @@ #include "openttd.h" #include "debug.h" #include "variables.h" -#include "saveload.h" #include "md5.h" #include "newgrf.h" #include "newgrf_config.h" @@ -495,48 +494,3 @@ bool GRFConfig::IsOpenTTDBaseGRF() const { return (this->grfid & 0x00FFFFFF) == OPENTTD_GRAPHICS_BASE_GRF_ID; } - - -static const SaveLoad _grfconfig_desc[] = { - SLE_STR(GRFConfig, filename, SLE_STR, 0x40), - SLE_VAR(GRFConfig, grfid, SLE_UINT32), - SLE_ARR(GRFConfig, md5sum, SLE_UINT8, 16), - SLE_ARR(GRFConfig, param, SLE_UINT32, 0x80), - SLE_VAR(GRFConfig, num_params, SLE_UINT8), - SLE_CONDVAR(GRFConfig, windows_paletted, SLE_BOOL, 101, SL_MAX_VERSION), - SLE_END() -}; - - -static void Save_NGRF() -{ - int index = 0; - - for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) { - if (HasBit(c->flags, GCF_STATIC)) continue; - SlSetArrayIndex(index++); - SlObject(c, _grfconfig_desc); - } -} - - -static void Load_NGRF() -{ - ClearGRFConfigList(&_grfconfig); - while (SlIterateArray() != -1) { - GRFConfig *c = CallocT(1); - SlObject(c, _grfconfig_desc); - if (CheckSavegameVersion(101)) c->windows_paletted = (_use_palette == PAL_WINDOWS); - AppendToGRFConfigList(&_grfconfig, c); - } - - /* Append static NewGRF configuration */ - AppendStaticGRFConfigs(&_grfconfig); -} - -extern const ChunkHandler _newgrf_chunk_handlers[] = { - { 'NGRF', Save_NGRF, Load_NGRF, CH_ARRAY | CH_LAST } -}; - - - diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -34,53 +34,6 @@ static HouseClassMapping _class_mapping[ HouseOverrideManager _house_mngr(NEW_HOUSE_OFFSET, HOUSE_MAX, INVALID_HOUSE_ID); -/** - * Check and update town and house values. - * - * Checked are the HouseIDs. Updated are the - * town population the number of houses per - * town, the town radius and the max passengers - * of the town. - */ -void UpdateHousesAndTowns() -{ - Town *town; - InitializeBuildingCounts(); - - /* Reset town population and num_houses */ - FOR_ALL_TOWNS(town) { - town->population = 0; - town->num_houses = 0; - } - - for (TileIndex t = 0; t < MapSize(); t++) { - HouseID house_id; - - if (!IsTileType(t, MP_HOUSE)) continue; - - house_id = GetHouseType(t); - if (!GetHouseSpecs(house_id)->enabled && house_id >= NEW_HOUSE_OFFSET) { - /* The specs for this type of house are not available any more, so - * replace it with the substitute original house type. */ - house_id = _house_mngr.GetSubstituteID(house_id); - SetHouseType(t, house_id); - } - - town = GetTownByTile(t); - IncreaseBuildingCount(town, house_id); - if (IsHouseCompleted(t)) town->population += GetHouseSpecs(house_id)->population; - - /* Increase the number of houses for every house, but only once. */ - if (GetHouseNorthPart(house_id) == 0) town->num_houses++; - } - - /* Update the population and num_house dependant values */ - FOR_ALL_TOWNS(town) { - UpdateTownRadius(town); - UpdateTownMaxPass(town); - } -} - HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid) { /* Start from 1 because 0 means that no class has been assigned. */ diff --git a/src/newgrf_house.h b/src/newgrf_house.h --- a/src/newgrf_house.h +++ b/src/newgrf_house.h @@ -7,6 +7,7 @@ #include "town_type.h" #include "newgrf_callbacks.h" +#include "tile_cmd.h" /** * Makes class IDs unique to each GRF file. @@ -26,8 +27,6 @@ struct HouseClassMapping { uint8 class_id; ////< The class id within the grf file }; -void UpdateHousesAndTowns(); - HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid); void InitializeBuildingCounts(); diff --git a/src/newgrf_station.h b/src/newgrf_station.h --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -13,6 +13,7 @@ #include "strings_type.h" #include "sprite.h" #include "direction_type.h" +#include "newgrf.h" enum StationClassID { STAT_CLASS_BEGIN = 0, ///< the lowest valid value diff --git a/src/oldloader.cpp b/src/oldloader.cpp deleted file mode 100644 --- a/src/oldloader.cpp +++ /dev/null @@ -1,1734 +0,0 @@ -/* $Id$ */ - -/** @file oldloader.cpp Loading of old TTD(patch) savegames. */ - -#include "stdafx.h" -#include "openttd.h" -#include "station_map.h" -#include "town.h" -#include "industry.h" -#include "company_func.h" -#include "company_base.h" -#include "aircraft.h" -#include "roadveh.h" -#include "ship.h" -#include "train.h" -#include "signs_base.h" -#include "debug.h" -#include "depot_base.h" -#include "newgrf_config.h" -#include "ai/ai.h" -#include "ai/default/default.h" -#include "zoom_func.h" -#include "functions.h" -#include "date_func.h" -#include "vehicle_func.h" -#include "variables.h" -#include "saveload.h" -#include "strings_func.h" -#include "effectvehicle_base.h" - -#include "table/strings.h" - -enum { - HEADER_SIZE = 49, - BUFFER_SIZE = 4096, - - OLD_MAP_SIZE = 256 * 256 -}; - -struct LoadgameState { - FILE *file; - - uint chunk_size; - - bool decoding; - byte decode_char; - - uint buffer_count; - uint buffer_cur; - byte buffer[BUFFER_SIZE]; - - uint total_read; - bool failed; -}; - -/* OldChunk-Type */ -enum OldChunkType { - OC_SIMPLE = 0, - OC_NULL = 1, - OC_CHUNK = 2, - OC_ASSERT = 3, - /* 8 bits allocated (256 max) */ - - OC_VAR_I8 = 1 << 8, - OC_VAR_U8 = 2 << 8, - OC_VAR_I16 = 3 << 8, - OC_VAR_U16 = 4 << 8, - OC_VAR_I32 = 5 << 8, - OC_VAR_U32 = 6 << 8, - OC_VAR_I64 = 7 << 8, - /* 8 bits allocated (256 max) */ - - OC_FILE_I8 = 1 << 16, - OC_FILE_U8 = 2 << 16, - OC_FILE_I16 = 3 << 16, - OC_FILE_U16 = 4 << 16, - OC_FILE_I32 = 5 << 16, - OC_FILE_U32 = 6 << 16, - /* 8 bits allocated (256 max) */ - - OC_INT8 = OC_VAR_I8 | OC_FILE_I8, - OC_UINT8 = OC_VAR_U8 | OC_FILE_U8, - OC_INT16 = OC_VAR_I16 | OC_FILE_I16, - OC_UINT16 = OC_VAR_U16 | OC_FILE_U16, - OC_INT32 = OC_VAR_I32 | OC_FILE_I32, - OC_UINT32 = OC_VAR_U32 | OC_FILE_U32, - - OC_TILE = OC_VAR_U32 | OC_FILE_U16, - - /** - * Dereference the pointer once before writing to it, - * so we do not have to use big static arrays. - */ - OC_DEREFERENCE_POINTER = 1 << 31, - - OC_END = 0 ///< End of the whole chunk, all 32 bits set to zero -}; - -DECLARE_ENUM_AS_BIT_SET(OldChunkType); - -typedef bool OldChunkProc(LoadgameState *ls, int num); - -struct OldChunks { - OldChunkType type; ///< Type of field - uint32 amount; ///< Amount of fields - - void *ptr; ///< Pointer where to save the data (may only be set if offset is 0) - uint offset; ///< Offset from basepointer (may only be set if ptr is NULL) - OldChunkProc *proc; ///< Pointer to function that is called with OC_CHUNK -}; - -/* If it fails, check lines above.. */ -assert_compile(sizeof(TileIndex) == 4); - -extern SavegameType _savegame_type; -extern uint32 _ttdp_version; - -static uint32 _bump_assert_value; -static bool _read_ttdpatch_flags; - -static OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 8);} -static OldChunkType GetOldChunkVarType(OldChunkType type) {return (OldChunkType)(GB(type, 8, 8) << 8);} -static OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);} - -static inline byte CalcOldVarLen(OldChunkType type) -{ - static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8}; - byte length = GB(type, 8, 8); - assert(length != 0 && length < lengthof(type_mem_size)); - return type_mem_size[length]; -} - -/** - * - * Reads a byte from a file (do not call yourself, use ReadByte()) - * - */ -static byte ReadByteFromFile(LoadgameState *ls) -{ - /* To avoid slow reads, we read BUFFER_SIZE of bytes per time - and just return a byte per time */ - if (ls->buffer_cur >= ls->buffer_count) { - /* Read some new bytes from the file */ - int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file); - - /* We tried to read, but there is nothing in the file anymore.. */ - if (count == 0) { - DEBUG(oldloader, 0, "Read past end of file, loading failed"); - ls->failed = true; - } - - ls->buffer_count = count; - ls->buffer_cur = 0; - } - - return ls->buffer[ls->buffer_cur++]; -} - -/** - * - * Reads a byte from the buffer and decompress if needed - * - */ -static byte ReadByte(LoadgameState *ls) -{ - /* Old savegames have a nice compression algorithm (RLE) - which means that we have a chunk, which starts with a length - byte. If that byte is negative, we have to repeat the next byte - that many times ( + 1). Else, we need to read that amount of bytes. - Works pretty good if you have many zero's behind eachother */ - - if (ls->chunk_size == 0) { - /* Read new chunk */ - int8 new_byte = ReadByteFromFile(ls); - - if (new_byte < 0) { - /* Repeat next char for new_byte times */ - ls->decoding = true; - ls->decode_char = ReadByteFromFile(ls); - ls->chunk_size = -new_byte + 1; - } else { - ls->decoding = false; - ls->chunk_size = new_byte + 1; - } - } - - ls->total_read++; - ls->chunk_size--; - - return ls->decoding ? ls->decode_char : ReadByteFromFile(ls); -} - -static inline uint16 ReadUint16(LoadgameState *ls) -{ - byte x = ReadByte(ls); - return x | ReadByte(ls) << 8; -} - -static inline uint32 ReadUint32(LoadgameState *ls) -{ - uint16 x = ReadUint16(ls); - return x | ReadUint16(ls) << 16; -} - -/** - * - * Loads a chunk from the old savegame - * - */ -static bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks) -{ - const OldChunks *chunk = chunks; - byte *base_ptr = (byte*)base; - - while (chunk->type != OC_END) { - byte *ptr = (byte*)chunk->ptr; - if ((chunk->type & OC_DEREFERENCE_POINTER) != 0) ptr = *(byte**)ptr; - - for (uint i = 0; i < chunk->amount; i++) { - if (ls->failed) return false; - - /* Handle simple types */ - if (GetOldChunkType(chunk->type) != 0) { - switch (GetOldChunkType(chunk->type)) { - /* Just read the byte and forget about it */ - case OC_NULL: ReadByte(ls); break; - - case OC_CHUNK: - /* Call function, with 'i' as parameter to tell which item we - * are going to read */ - if (!chunk->proc(ls, i)) return false; - break; - - case OC_ASSERT: - DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value); - if (ls->total_read != chunk->offset + _bump_assert_value) ls->failed = true; - default: break; - } - } else { - uint64 res = 0; - - /* Reading from the file: bits 16 to 23 have the FILE type */ - switch (GetOldChunkFileType(chunk->type)) { - case OC_FILE_I8: res = (int8)ReadByte(ls); break; - case OC_FILE_U8: res = ReadByte(ls); break; - case OC_FILE_I16: res = (int16)ReadUint16(ls); break; - case OC_FILE_U16: res = ReadUint16(ls); break; - case OC_FILE_I32: res = (int32)ReadUint32(ls); break; - case OC_FILE_U32: res = ReadUint32(ls); break; - default: NOT_REACHED(); - } - - /* Sanity check */ - assert(base_ptr != NULL || chunk->ptr != NULL); - - /* Writing to the var: bits 8 to 15 have the VAR type */ - if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset; - - /* Write the data */ - switch (GetOldChunkVarType(chunk->type)) { - case OC_VAR_I8: *(int8 *)ptr = GB(res, 0, 8); break; - case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break; - case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break; - case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break; - case OC_VAR_I32:*(int32 *)ptr = res; break; - case OC_VAR_U32:*(uint32*)ptr = res; break; - case OC_VAR_I64:*(int64 *)ptr = res; break; - default: NOT_REACHED(); - } - - /* Increase pointer base for arrays when looping */ - if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type); - } - } - - chunk++; - } - - return true; -} - -/** - * - * Initialize some data before reading - * - */ -static void InitLoading(LoadgameState *ls) -{ - ls->chunk_size = 0; - ls->total_read = 0; - ls->failed = false; - - ls->decoding = false; - ls->decode_char = 0; - - ls->buffer_cur = 0; - ls->buffer_count = 0; - memset(ls->buffer, 0, BUFFER_SIZE); - - _bump_assert_value = 0; - - _savegame_type = SGT_TTD; - _ttdp_version = 0; - - _read_ttdpatch_flags = false; -} - - -/* - * Begin -- Stuff to fix the savegames to be OpenTTD compatible - */ - -extern uint32 GetOldTownName(uint32 townnameparts, byte old_town_name_type); - -static void FixOldTowns() -{ - Town *town; - - /* Convert town-names if needed */ - FOR_ALL_TOWNS(town) { - if (IsInsideMM(town->townnametype, 0x20C1, 0x20C3)) { - town->townnametype = SPECSTR_TOWNNAME_ENGLISH + _settings_game.game_creation.town_name; - town->townnameparts = GetOldTownName(town->townnameparts, _settings_game.game_creation.town_name); - } - } -} - -static StringID *_old_vehicle_names = NULL; - -static void FixOldVehicles() -{ - Vehicle* v; - - FOR_ALL_VEHICLES(v) { - v->name = CopyFromOldName(_old_vehicle_names[v->index]); - - /* We haven't used this bit for stations for ages */ - if (v->type == VEH_ROAD && - v->u.road.state != RVSB_IN_DEPOT && - v->u.road.state != RVSB_WORMHOLE) { - ClrBit(v->u.road.state, RVS_IS_STOPPING); - } - - /* The subtype should be 0, but it sometimes isn't :( */ - if (v->type == VEH_ROAD) v->subtype = 0; - - /* Sometimes primary vehicles would have a nothing (invalid) order - * or vehicles that could not have an order would still have a - * (loading) order which causes assertions and the like later on. - */ - if (!IsCompanyBuildableVehicleType(v) || - (v->IsPrimaryVehicle() && v->current_order.IsType(OT_NOTHING))) { - v->current_order.MakeDummy(); - } - - /* Shared orders are fixed in AfterLoadVehicles now */ - } -} - -/* - * End -- Stuff to fix the savegames to be OpenTTD compatible - */ - - -/* Help: - * - OCL_SVAR: load 'type' to offset 'offset' in a struct of type 'base', which must also - * be given via base in LoadChunk() as real pointer - * - OCL_VAR: load 'type' to a global var - * - OCL_END: every struct must end with this - * - OCL_NULL: read 'amount' of bytes and send them to /dev/null or something - * - OCL_CHUNK: load an other proc to load a part of the savegame, 'amount' times - * - OCL_ASSERT: to check if we are really at the place we expect to be.. because old savegames are too binary to be sure ;) - */ -#define OCL_SVAR(type, base, offset) { type, 1, NULL, (uint)cpp_offsetof(base, offset), NULL } -#define OCL_VAR(type, amount, pointer) { type, amount, pointer, 0, NULL } -#define OCL_END() { OC_END, 0, NULL, 0, NULL } -#define OCL_NULL(amount) { OC_NULL, amount, NULL, 0, NULL } -#define OCL_CHUNK(amount, proc) { OC_CHUNK, amount, NULL, 0, proc } -#define OCL_ASSERT(size) { OC_ASSERT, 1, NULL, size, NULL } - -/* The savegames has some hard-coded pointers, because it always enters the same - piece of memory.. we don't.. so we need to remap ;) - Old Towns are 94 bytes big - Old Orders are 2 bytes big */ -#define REMAP_TOWN_IDX(x) ((x) - (0x0459154 - 0x0458EF0)) / 94 -#define REMAP_ORDER_IDX(x) ((x) - (0x045AB08 - 0x0458EF0)) / 2 - -extern TileIndex *_animated_tile_list; -extern uint _animated_tile_count; -extern char *_old_name_array; - -static byte _old_vehicle_multiplier; -static uint8 *_old_map3; -static uint32 _old_town_index; -static uint16 _old_string_id; -static uint16 _old_string_id_2; -static uint16 _old_extra_chunk_nums; - -static void ReadTTDPatchFlags() -{ - if (_read_ttdpatch_flags) return; - - _read_ttdpatch_flags = true; - - /* TTDPatch misuses _old_map3 for flags.. read them! */ - _old_vehicle_multiplier = _old_map3[0]; - /* Somehow.... there was an error in some savegames, so 0 becomes 1 - and 1 becomes 2. The rest of the values are okay */ - if (_old_vehicle_multiplier < 2) _old_vehicle_multiplier++; - - _old_vehicle_names = MallocT(_old_vehicle_multiplier * 850); - - /* TTDPatch increases the Vehicle-part in the middle of the game, - so if the multipler is anything else but 1, the assert fails.. - bump the assert value so it doesn't! - (1 multipler == 850 vehicles - 1 vehicle == 128 bytes */ - _bump_assert_value = (_old_vehicle_multiplier - 1) * 850 * 128; - - for (uint i = 0; i < 17; i++) { // check tile 0, too - if (_old_map3[i] != 0) _savegame_type = SGT_TTDP1; - } - - /* Check if we have a modern TTDPatch savegame (has extra data all around) */ - if (memcmp(&_old_map3[0x1FFFA], "TTDp", 4) == 0) _savegame_type = SGT_TTDP2; - - _old_extra_chunk_nums = _old_map3[_savegame_type == SGT_TTDP2 ? 0x1FFFE : 0x2]; - - /* Clean the misused places */ - for (uint i = 0; i < 17; i++) _old_map3[i] = 0; - for (uint i = 0x1FE00; i < 0x20000; i++) _old_map3[i] = 0; - - if (_savegame_type == SGT_TTDP2) DEBUG(oldloader, 2, "Found TTDPatch game"); - - DEBUG(oldloader, 3, "Vehicle-multiplier is set to %d (%d vehicles)", _old_vehicle_multiplier, _old_vehicle_multiplier * 850); -} - -static const OldChunks town_chunk[] = { - OCL_SVAR( OC_TILE, Town, xy ), - OCL_NULL( 2 ), ///< population, no longer in use - OCL_SVAR( OC_UINT16, Town, townnametype ), - OCL_SVAR( OC_UINT32, Town, townnameparts ), - OCL_SVAR( OC_UINT8, Town, grow_counter ), - OCL_NULL( 1 ), ///< sort_index, no longer in use - OCL_NULL( 4 ), ///< sign-coordinates, no longer in use - OCL_NULL( 2 ), ///< namewidth, no longer in use - OCL_SVAR( OC_UINT16, Town, flags12 ), - OCL_NULL( 10 ), ///< radius, no longer in use - - OCL_SVAR( OC_UINT16, Town, ratings[0] ), - OCL_SVAR( OC_UINT16, Town, ratings[1] ), - OCL_SVAR( OC_UINT16, Town, ratings[2] ), - OCL_SVAR( OC_UINT16, Town, ratings[3] ), - OCL_SVAR( OC_UINT16, Town, ratings[4] ), - OCL_SVAR( OC_UINT16, Town, ratings[5] ), - OCL_SVAR( OC_UINT16, Town, ratings[6] ), - OCL_SVAR( OC_UINT16, Town, ratings[7] ), - - /* XXX - This is pretty odd.. we read 32bit, but only write 16bit.. sure there is - nothing changed ? ? */ - OCL_SVAR( OC_FILE_U32 | OC_VAR_U16, Town, have_ratings ), - OCL_SVAR( OC_FILE_U32 | OC_VAR_U16, Town, statues ), - OCL_NULL( 2 ), ///< num_houses, no longer in use - OCL_SVAR( OC_UINT8, Town, time_until_rebuild ), - OCL_SVAR( OC_UINT8, Town, growth_rate ), - - OCL_SVAR( OC_UINT16, Town, new_max_pass ), - OCL_SVAR( OC_UINT16, Town, new_max_mail ), - OCL_SVAR( OC_UINT16, Town, new_act_pass ), - OCL_SVAR( OC_UINT16, Town, new_act_mail ), - OCL_SVAR( OC_UINT16, Town, max_pass ), - OCL_SVAR( OC_UINT16, Town, max_mail ), - OCL_SVAR( OC_UINT16, Town, act_pass ), - OCL_SVAR( OC_UINT16, Town, act_mail ), - - OCL_SVAR( OC_UINT8, Town, pct_pass_transported ), - OCL_SVAR( OC_UINT8, Town, pct_mail_transported ), - - OCL_SVAR( OC_UINT16, Town, new_act_food ), - OCL_SVAR( OC_UINT16, Town, new_act_water ), - OCL_SVAR( OC_UINT16, Town, act_food ), - OCL_SVAR( OC_UINT16, Town, act_water ), - - OCL_SVAR( OC_UINT8, Town, road_build_months ), - OCL_SVAR( OC_UINT8, Town, fund_buildings_months ), - - OCL_NULL( 8 ), ///< some junk at the end of the record - - OCL_END() -}; -static bool LoadOldTown(LoadgameState *ls, int num) -{ - Town *t = new (num) Town(); - if (!LoadChunk(ls, t, town_chunk)) return false; - - if (t->xy == 0) t->xy = INVALID_TILE; - - return true; -} - -static uint16 _old_order; -static const OldChunks order_chunk[] = { - OCL_VAR ( OC_UINT16, 1, &_old_order ), - OCL_END() -}; - -static bool LoadOldOrder(LoadgameState *ls, int num) -{ - if (!LoadChunk(ls, NULL, order_chunk)) return false; - - new (num) Order(UnpackOldOrder(_old_order)); - - /* Relink the orders to eachother (in TTD(Patch) the orders for one - vehicle are behind eachother, with an invalid order (OT_NOTHING) as indication that - it is the last order */ - if (num > 0 && GetOrder(num)->IsValid()) - GetOrder(num - 1)->next = GetOrder(num); - - return true; -} - -static bool LoadOldAnimTileList(LoadgameState *ls, int num) -{ - /* This is sligthly hackish - we must load a chunk into an array whose - * address isn't static, but instead pointed to by _animated_tile_list. - * To achieve that, create an OldChunks list on the stack on the fly. - * The list cannot be static because the value of _animated_tile_list - * can change between calls. */ - - const OldChunks anim_chunk[] = { - OCL_VAR ( OC_TILE, 256, _animated_tile_list ), - OCL_END () - }; - - if (!LoadChunk(ls, NULL, anim_chunk)) return false; - - /* Update the animated tile counter by counting till the first zero in the array */ - for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) { - if (_animated_tile_list[_animated_tile_count] == 0) break; - } - - return true; -} - -static const OldChunks depot_chunk[] = { - OCL_SVAR( OC_TILE, Depot, xy ), - OCL_VAR ( OC_UINT32, 1, &_old_town_index ), - OCL_END() -}; - -static bool LoadOldDepot(LoadgameState *ls, int num) -{ - Depot *d = new (num) Depot(); - if (!LoadChunk(ls, d, depot_chunk)) return false; - - if (d->xy != 0) { - GetDepot(num)->town_index = REMAP_TOWN_IDX(_old_town_index); - } else { - d->xy = INVALID_TILE; - } - - return true; -} - -static int32 _old_price; -static uint16 _old_price_frac; -static const OldChunks price_chunk[] = { - OCL_VAR ( OC_INT32, 1, &_old_price ), - OCL_VAR ( OC_UINT16, 1, &_old_price_frac ), - OCL_END() -}; - -static bool LoadOldPrice(LoadgameState *ls, int num) -{ - if (!LoadChunk(ls, NULL, price_chunk)) return false; - - /* We use a struct to store the prices, but they are ints in a row.. - so just access the struct as an array of int32's */ - ((Money*)&_price)[num] = _old_price; - _price_frac[num] = _old_price_frac; - - return true; -} - -static const OldChunks cargo_payment_rate_chunk[] = { - OCL_VAR ( OC_INT32, 1, &_old_price ), - OCL_VAR ( OC_UINT16, 1, &_old_price_frac ), - - OCL_NULL( 2 ), ///< Junk - OCL_END() -}; - -static bool LoadOldCargoPaymentRate(LoadgameState *ls, int num) -{ - if (!LoadChunk(ls, NULL, cargo_payment_rate_chunk)) return false; - - _cargo_payment_rates[num] = -_old_price; - _cargo_payment_rates_frac[num] = _old_price_frac; - - return true; -} - -static uint _current_station_id; -static uint16 _waiting_acceptance; -static uint8 _cargo_source; -static uint8 _cargo_days; - -static const OldChunks goods_chunk[] = { - OCL_VAR ( OC_UINT16, 1, &_waiting_acceptance ), - OCL_SVAR( OC_UINT8, GoodsEntry, days_since_pickup ), - OCL_SVAR( OC_UINT8, GoodsEntry, rating ), - OCL_VAR ( OC_UINT8, 1, &_cargo_source ), - OCL_VAR ( OC_UINT8, 1, &_cargo_days ), - OCL_SVAR( OC_UINT8, GoodsEntry, last_speed ), - OCL_SVAR( OC_UINT8, GoodsEntry, last_age ), - - OCL_END() -}; - -static bool LoadOldGood(LoadgameState *ls, int num) -{ - Station *st = GetStation(_current_station_id); - GoodsEntry *ge = &st->goods[num]; - bool ret = LoadChunk(ls, ge, goods_chunk); - if (!ret) return false; - - SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); - SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, _cargo_source != 0xFF); - if (GB(_waiting_acceptance, 0, 12) != 0) { - CargoPacket *cp = new CargoPacket(); - cp->source = (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source; - cp->count = GB(_waiting_acceptance, 0, 12); - cp->days_in_transit = _cargo_days; - ge->cargo.Append(cp); - } - return ret; -} - -static const OldChunks station_chunk[] = { - OCL_SVAR( OC_TILE, Station, xy ), - OCL_VAR ( OC_UINT32, 1, &_old_town_index ), - - OCL_NULL( 4 ), ///< bus/lorry tile - OCL_SVAR( OC_TILE, Station, train_tile ), - OCL_SVAR( OC_TILE, Station, airport_tile ), - OCL_SVAR( OC_TILE, Station, dock_tile ), - OCL_SVAR( OC_UINT8, Station, trainst_w ), - - OCL_NULL( 1 ), ///< sort-index, no longer in use - OCL_NULL( 2 ), ///< sign-width, no longer in use - - OCL_VAR ( OC_UINT16, 1, &_old_string_id ), - - OCL_NULL( 4 ), ///< sign left/top, no longer in use - - OCL_SVAR( OC_UINT16, Station, had_vehicle_of_type ), - - OCL_CHUNK( 12, LoadOldGood ), - - OCL_SVAR( OC_UINT8, Station, time_since_load ), - OCL_SVAR( OC_UINT8, Station, time_since_unload ), - OCL_SVAR( OC_UINT8, Station, delete_ctr ), - OCL_SVAR( OC_UINT8, Station, owner ), - OCL_SVAR( OC_UINT8, Station, facilities ), - OCL_SVAR( OC_UINT8, Station, airport_type ), - /* Bus/truck status, no longer in use - * Blocked months - * Unknown - */ - OCL_NULL( 4 ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Station, airport_flags ), - OCL_NULL( 2 ), ///< last_vehicle. now last_vehicle_type - - OCL_NULL( 4 ), ///< Junk at end of chunk - - OCL_END() -}; -static bool LoadOldStation(LoadgameState *ls, int num) -{ - Station *st = new (num) Station(); - _current_station_id = num; - - if (!LoadChunk(ls, st, station_chunk)) - return false; - - if (st->xy != 0) { - st->town = GetTown(REMAP_TOWN_IDX(_old_town_index)); - st->string_id = RemapOldStringID(_old_string_id); - } else { - st->xy = INVALID_TILE; - } - - return true; -} - -static const OldChunks industry_chunk[] = { - OCL_SVAR( OC_TILE, Industry, xy ), - OCL_VAR ( OC_UINT32, 1, &_old_town_index ), - OCL_SVAR( OC_UINT8, Industry, width ), - OCL_SVAR( OC_UINT8, Industry, height ), - OCL_NULL( 2 ), ///< used to be industry's produced_cargo - - OCL_SVAR( OC_UINT16, Industry, produced_cargo_waiting[0] ), - OCL_SVAR( OC_UINT16, Industry, produced_cargo_waiting[1] ), - - OCL_SVAR( OC_UINT8, Industry, production_rate[0] ), - OCL_SVAR( OC_UINT8, Industry, production_rate[1] ), - - OCL_NULL( 3 ), ///< used to be industry's accepts_cargo - - OCL_SVAR( OC_UINT8, Industry, prod_level ), - - OCL_SVAR( OC_UINT16, Industry, this_month_production[0] ), - OCL_SVAR( OC_UINT16, Industry, this_month_production[1] ), - OCL_SVAR( OC_UINT16, Industry, this_month_transported[0] ), - OCL_SVAR( OC_UINT16, Industry, this_month_transported[1] ), - - OCL_SVAR( OC_UINT8, Industry, last_month_pct_transported[0] ), - OCL_SVAR( OC_UINT8, Industry, last_month_pct_transported[1] ), - - OCL_SVAR( OC_UINT16, Industry, last_month_production[0] ), - OCL_SVAR( OC_UINT16, Industry, last_month_production[1] ), - OCL_SVAR( OC_UINT16, Industry, last_month_transported[0] ), - OCL_SVAR( OC_UINT16, Industry, last_month_transported[1] ), - - OCL_SVAR( OC_UINT8, Industry, type ), - OCL_SVAR( OC_UINT8, Industry, owner ), - OCL_SVAR( OC_UINT8, Industry, random_color ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Industry, last_prod_year ), - OCL_SVAR( OC_UINT16, Industry, counter ), - OCL_SVAR( OC_UINT8, Industry, was_cargo_delivered ), - - OCL_NULL( 9 ), ///< Random junk at the end of this chunk - - OCL_END() -}; - -static bool LoadOldIndustry(LoadgameState *ls, int num) -{ - Industry *i = new (num) Industry(); - if (!LoadChunk(ls, i, industry_chunk)) return false; - - if (i->xy != 0) { - i->town = GetTown(REMAP_TOWN_IDX(_old_town_index)); - IncIndustryTypeCount(i->type); - } else { - i->xy = INVALID_TILE; - } - - return true; -} - -static CompanyID _current_company_id; -static int32 _old_yearly; - -static const OldChunks _company_yearly_chunk[] = { - OCL_VAR( OC_INT32, 1, &_old_yearly ), - OCL_END() -}; - -static bool OldCompanyYearly(LoadgameState *ls, int num) -{ - int i; - Company *c = GetCompany(_current_company_id); - - for (i = 0; i < 13; i++) { - if (!LoadChunk(ls, NULL, _company_yearly_chunk)) return false; - - c->yearly_expenses[num][i] = _old_yearly; - } - - return true; -} - -static const OldChunks _company_economy_chunk[] = { - OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, income ), - OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, expenses ), - OCL_SVAR( OC_INT32, CompanyEconomyEntry, delivered_cargo ), - OCL_SVAR( OC_INT32, CompanyEconomyEntry, performance_history ), - OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, company_value ), - - OCL_END() -}; - -static bool OldCompanyEconomy(LoadgameState *ls, int num) -{ - int i; - Company *c = GetCompany(_current_company_id); - - if (!LoadChunk(ls, &c->cur_economy, _company_economy_chunk)) return false; - - /* Don't ask, but the number in TTD(Patch) are inversed to OpenTTD */ - c->cur_economy.income = -c->cur_economy.income; - c->cur_economy.expenses = -c->cur_economy.expenses; - - for (i = 0; i < 24; i++) { - if (!LoadChunk(ls, &c->old_economy[i], _company_economy_chunk)) return false; - - c->old_economy[i].income = -c->old_economy[i].income; - c->old_economy[i].expenses = -c->old_economy[i].expenses; - } - - return true; -} - -static const OldChunks _company_ai_build_rec_chunk[] = { - OCL_SVAR( OC_TILE, AiBuildRec, spec_tile ), - OCL_SVAR( OC_TILE, AiBuildRec, use_tile ), - OCL_SVAR( OC_UINT8, AiBuildRec, rand_rng ), - OCL_SVAR( OC_UINT8, AiBuildRec, cur_building_rule ), - OCL_SVAR( OC_UINT8, AiBuildRec, unk6 ), - OCL_SVAR( OC_UINT8, AiBuildRec, unk7 ), - OCL_SVAR( OC_UINT8, AiBuildRec, buildcmd_a ), - OCL_SVAR( OC_UINT8, AiBuildRec, buildcmd_b ), - OCL_SVAR( OC_UINT8, AiBuildRec, direction ), - OCL_SVAR( OC_UINT8, AiBuildRec, cargo ), - - OCL_NULL( 8 ), ///< Junk... - - OCL_END() -}; - -static bool OldLoadAIBuildRec(LoadgameState *ls, int num) -{ - Company *c = GetCompany(_current_company_id); - - switch (num) { - case 0: return LoadChunk(ls, &_companies_ai[c->index].src, _company_ai_build_rec_chunk); - case 1: return LoadChunk(ls, &_companies_ai[c->index].dst, _company_ai_build_rec_chunk); - case 2: return LoadChunk(ls, &_companies_ai[c->index].mid1, _company_ai_build_rec_chunk); - case 3: return LoadChunk(ls, &_companies_ai[c->index].mid2, _company_ai_build_rec_chunk); - } - - return false; -} -static const OldChunks _company_ai_chunk[] = { - OCL_SVAR( OC_UINT8, CompanyAI, state ), - OCL_NULL( 1 ), ///< Junk - OCL_SVAR( OC_UINT8, CompanyAI, state_mode ), - OCL_SVAR( OC_UINT16, CompanyAI, state_counter ), - OCL_SVAR( OC_UINT16, CompanyAI, timeout_counter ), - - OCL_CHUNK( 4, OldLoadAIBuildRec ), - - OCL_NULL( 20 ), ///< More junk - - OCL_SVAR( OC_UINT8, CompanyAI, cargo_type ), - OCL_SVAR( OC_UINT8, CompanyAI, num_wagons ), - OCL_SVAR( OC_UINT8, CompanyAI, build_kind ), - OCL_SVAR( OC_UINT8, CompanyAI, num_build_rec ), - OCL_SVAR( OC_UINT8, CompanyAI, num_loco_to_build ), - OCL_SVAR( OC_UINT8, CompanyAI, num_want_fullload ), - - OCL_NULL( 14 ), ///< Oh no more junk :| - - OCL_NULL( 2 ), ///< Loco-id, not used - - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[0] ), - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[1] ), - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[2] ), - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[3] ), - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[4] ), - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[5] ), - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[6] ), - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[7] ), - OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[8] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[0] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[1] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[2] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[3] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[4] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[5] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[6] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[7] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[8] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[9] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[10] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[11] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[12] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[13] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[14] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[15] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[16] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[17] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[18] ), - OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[19] ), - - OCL_SVAR( OC_UINT16, CompanyAI, start_tile_a ), - OCL_SVAR( OC_UINT16, CompanyAI, start_tile_b ), - OCL_SVAR( OC_UINT16, CompanyAI, cur_tile_a ), - OCL_SVAR( OC_UINT16, CompanyAI, cur_tile_b ), - - OCL_SVAR( OC_UINT8, CompanyAI, start_dir_a ), - OCL_SVAR( OC_UINT8, CompanyAI, start_dir_b ), - OCL_SVAR( OC_UINT8, CompanyAI, cur_dir_a ), - OCL_SVAR( OC_UINT8, CompanyAI, cur_dir_b ), - - OCL_SVAR( OC_UINT8, CompanyAI, banned_tile_count ), - - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[0] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[0] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[1] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[1] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[2] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[2] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[3] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[3] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[4] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[4] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[5] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[5] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[6] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[6] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[7] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[7] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[8] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[8] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[9] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[9] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[10] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[10] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[11] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[11] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[12] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[12] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[13] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[13] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[14] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[14] ), - OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[15] ), - OCL_SVAR( OC_UINT8, CompanyAI, banned_val[15] ), - - OCL_SVAR( OC_UINT8, CompanyAI, railtype_to_use ), - OCL_SVAR( OC_UINT8, CompanyAI, route_type_mask ), - - OCL_END() -}; - -static bool OldCompanyAI(LoadgameState *ls, int num) -{ - return LoadChunk(ls, &_companies_ai[_current_company_id], _company_ai_chunk); -} - -uint8 ai_tick; -static const OldChunks _company_chunk[] = { - OCL_VAR ( OC_UINT16, 1, &_old_string_id ), - OCL_SVAR( OC_UINT32, Company, name_2 ), - OCL_SVAR( OC_UINT32, Company, face ), - OCL_VAR ( OC_UINT16, 1, &_old_string_id_2 ), - OCL_SVAR( OC_UINT32, Company, president_name_2 ), - - OCL_SVAR( OC_INT32, Company, money ), - OCL_SVAR( OC_INT32, Company, current_loan ), - - OCL_SVAR( OC_UINT8, Company, colour ), - OCL_SVAR( OC_UINT8, Company, money_fraction ), - OCL_SVAR( OC_UINT8, Company, quarters_of_bankrupcy ), - OCL_SVAR( OC_UINT8, Company, bankrupt_asked ), - OCL_SVAR( OC_FILE_U32 | OC_VAR_I64, Company, bankrupt_value ), - OCL_SVAR( OC_UINT16, Company, bankrupt_timeout ), - - OCL_SVAR( OC_UINT32, Company, cargo_types ), - - OCL_CHUNK( 3, OldCompanyYearly ), - OCL_CHUNK( 1, OldCompanyEconomy ), - - OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Company, inaugurated_year), - OCL_SVAR( OC_TILE, Company, last_build_coordinate ), - OCL_SVAR( OC_UINT8, Company, num_valid_stat_ent ), - - OCL_CHUNK( 1, OldCompanyAI ), - - OCL_SVAR( OC_UINT8, Company, block_preview ), - OCL_VAR( OC_UINT8, 1, &ai_tick ), - OCL_SVAR( OC_UINT8, Company, avail_railtypes ), - OCL_SVAR( OC_TILE, Company, location_of_HQ ), - OCL_SVAR( OC_UINT8, Company, share_owners[0] ), - OCL_SVAR( OC_UINT8, Company, share_owners[1] ), - OCL_SVAR( OC_UINT8, Company, share_owners[2] ), - OCL_SVAR( OC_UINT8, Company, share_owners[3] ), - - OCL_NULL( 8 ), ///< junk at end of chunk - - OCL_END() -}; - -static bool LoadOldCompany(LoadgameState *ls, int num) -{ - Company *c = new (num) Company(); - - _current_company_id = (CompanyID)num; - - if (!LoadChunk(ls, c, _company_chunk)) return false; - - if (_old_string_id == 0) { - delete c; - return true; - } - - c->name_1 = RemapOldStringID(_old_string_id); - c->president_name_1 = RemapOldStringID(_old_string_id_2); - _companies_ai[_current_company_id].tick = ai_tick; - - if (num == 0) { - /* If the first company has no name, make sure we call it UNNAMED */ - if (c->name_1 == 0) - c->name_1 = STR_SV_UNNAMED; - } else { - /* Beside some multiplayer maps (1 on 1), which we don't official support, - * all other companys are an AI.. mark them as such */ - c->is_ai = true; - } - - /* Sometimes it is better to not ask.. in old scenarios, the money - * was always 893288 pounds. In the newer versions this is correct, - * but correct for those oldies - * Ps: this also means that if you had exact 893288 pounds, you will go back - * to 100000.. this is a very VERY small chance ;) */ - if (c->money == 893288) c->money = c->current_loan = 100000; - - _company_colours[num] = c->colour; - c->inaugurated_year -= ORIGINAL_BASE_YEAR; - - /* State 20 for AI companies is sell vehicle. Since the AI struct is not - * really figured out as of now, _companies_ai[c->index].cur_veh; needed for 'sell vehicle' - * is NULL and the function will crash. To fix this, just change the state - * to some harmless state, like 'loop vehicle'; 1 */ - if (!IsHumanCompany((CompanyID)num) && _companies_ai[c->index].state == 20) _companies_ai[c->index].state = 1; - - if (c->is_ai && (!_networking || _network_server) && _ai.enabled) - AI_StartNewAI(c->index); - - return true; -} - -static uint32 _old_order_ptr; -static uint16 _old_next_ptr; -static uint32 _current_vehicle_id; - -static const OldChunks vehicle_train_chunk[] = { - OCL_SVAR( OC_UINT8, VehicleRail, track ), - OCL_SVAR( OC_UINT8, VehicleRail, force_proceed ), - OCL_SVAR( OC_UINT16, VehicleRail, crash_anim_pos ), - OCL_SVAR( OC_UINT8, VehicleRail, railtype ), - - OCL_NULL( 5 ), ///< Junk - - OCL_END() -}; - -static const OldChunks vehicle_road_chunk[] = { - OCL_SVAR( OC_UINT8, VehicleRoad, state ), - OCL_SVAR( OC_UINT8, VehicleRoad, frame ), - OCL_SVAR( OC_UINT16, VehicleRoad, blocked_ctr ), - OCL_SVAR( OC_UINT8, VehicleRoad, overtaking ), - OCL_SVAR( OC_UINT8, VehicleRoad, overtaking_ctr ), - OCL_SVAR( OC_UINT16, VehicleRoad, crashed_ctr ), - OCL_SVAR( OC_UINT8, VehicleRoad, reverse_ctr ), - - OCL_NULL( 1 ), ///< Junk - - OCL_END() -}; - -static const OldChunks vehicle_ship_chunk[] = { - OCL_SVAR( OC_UINT8, VehicleShip, state ), - - OCL_NULL( 9 ), ///< Junk - - OCL_END() -}; - -static const OldChunks vehicle_air_chunk[] = { - OCL_SVAR( OC_UINT8, VehicleAir, pos ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, VehicleAir, targetairport ), - OCL_SVAR( OC_UINT16, VehicleAir, crashed_counter ), - OCL_SVAR( OC_UINT8, VehicleAir, state ), - - OCL_NULL( 5 ), ///< Junk - - OCL_END() -}; - -static const OldChunks vehicle_effect_chunk[] = { - OCL_SVAR( OC_UINT16, VehicleEffect, animation_state ), - OCL_SVAR( OC_UINT8, VehicleEffect, animation_substate ), - - OCL_NULL( 7 ), // Junk - - OCL_END() -}; - -static const OldChunks vehicle_disaster_chunk[] = { - OCL_SVAR( OC_UINT16, VehicleDisaster, image_override ), - OCL_SVAR( OC_UINT16, VehicleDisaster, big_ufo_destroyer_target ), - - OCL_NULL( 6 ), ///< Junk - - OCL_END() -}; - -static const OldChunks vehicle_empty_chunk[] = { - OCL_NULL( 10 ), ///< Junk - - OCL_END() -}; - -static bool LoadOldVehicleUnion(LoadgameState *ls, int num) -{ - Vehicle *v = GetVehicle(_current_vehicle_id); - uint temp = ls->total_read; - bool res; - - switch (v->type) { - default: NOT_REACHED(); - case VEH_INVALID : res = LoadChunk(ls, NULL, vehicle_empty_chunk); break; - case VEH_TRAIN : res = LoadChunk(ls, &v->u.rail, vehicle_train_chunk); break; - case VEH_ROAD : res = LoadChunk(ls, &v->u.road, vehicle_road_chunk); break; - case VEH_SHIP : res = LoadChunk(ls, &v->u.ship, vehicle_ship_chunk); break; - case VEH_AIRCRAFT: res = LoadChunk(ls, &v->u.air, vehicle_air_chunk); break; - case VEH_EFFECT : res = LoadChunk(ls, &v->u.effect, vehicle_effect_chunk); break; - case VEH_DISASTER: res = LoadChunk(ls, &v->u.disaster, vehicle_disaster_chunk); break; - } - - /* This chunk size should always be 10 bytes */ - if (ls->total_read - temp != 10) { - DEBUG(oldloader, 0, "Assert failed in VehicleUnion: invalid chunk size"); - return false; - } - - return res; -} - -static uint16 _cargo_count; - -static const OldChunks vehicle_chunk[] = { - OCL_SVAR( OC_UINT8, Vehicle, subtype ), - - OCL_NULL( 2 ), ///< Hash, calculated automatically - OCL_NULL( 2 ), ///< Index, calculated automatically - - OCL_VAR ( OC_UINT32, 1, &_old_order_ptr ), - OCL_VAR ( OC_UINT16, 1, &_old_order ), - - OCL_NULL ( 1 ), ///< num_orders, now calculated - OCL_SVAR( OC_UINT8, Vehicle, cur_order_index ), - OCL_SVAR( OC_TILE, Vehicle, dest_tile ), - OCL_SVAR( OC_UINT16, Vehicle, load_unload_time_rem ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, date_of_last_service ), - OCL_SVAR( OC_UINT16, Vehicle, service_interval ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, last_station_visited ), - OCL_SVAR( OC_UINT8, Vehicle, tick_counter ), - OCL_SVAR( OC_UINT16, Vehicle, max_speed ), - - OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, x_pos ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, y_pos ), - OCL_SVAR( OC_UINT8, Vehicle, z_pos ), - OCL_SVAR( OC_UINT8, Vehicle, direction ), - OCL_NULL( 2 ), ///< x_offs and y_offs, calculated automatically - OCL_NULL( 2 ), ///< x_extent and y_extent, calculated automatically - OCL_NULL( 1 ), ///< z_extent, calculated automatically - - OCL_SVAR( OC_UINT8, Vehicle, owner ), - OCL_SVAR( OC_TILE, Vehicle, tile ), - OCL_SVAR( OC_UINT16, Vehicle, cur_image ), - - OCL_NULL( 8 ), ///< Vehicle sprite box, calculated automatically - - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, Vehicle, vehstatus ), - OCL_SVAR( OC_UINT16, Vehicle, cur_speed ), - OCL_SVAR( OC_UINT8, Vehicle, subspeed ), - OCL_SVAR( OC_UINT8, Vehicle, acceleration ), - OCL_SVAR( OC_UINT8, Vehicle, progress ), - - OCL_SVAR( OC_UINT8, Vehicle, cargo_type ), - OCL_SVAR( OC_UINT16, Vehicle, cargo_cap ), - OCL_VAR ( OC_UINT16, 1, &_cargo_count ), - OCL_VAR ( OC_UINT8, 1, &_cargo_source ), - OCL_VAR ( OC_UINT8, 1, &_cargo_days ), - - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, age ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, max_age ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Vehicle, build_year ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, unitnumber ), - - OCL_SVAR( OC_UINT16, Vehicle, engine_type ), - - OCL_SVAR( OC_UINT8, Vehicle, spritenum ), - OCL_SVAR( OC_UINT8, Vehicle, day_counter ), - - OCL_SVAR( OC_UINT8, Vehicle, breakdowns_since_last_service ), - OCL_SVAR( OC_UINT8, Vehicle, breakdown_ctr ), - OCL_SVAR( OC_UINT8, Vehicle, breakdown_delay ), - OCL_SVAR( OC_UINT8, Vehicle, breakdown_chance ), - - OCL_SVAR( OC_UINT16, Vehicle, reliability ), - OCL_SVAR( OC_UINT16, Vehicle, reliability_spd_dec ), - - OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, Vehicle, profit_this_year ), - OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, Vehicle, profit_last_year ), - - OCL_VAR ( OC_UINT16, 1, &_old_next_ptr ), - - OCL_SVAR( OC_UINT32, Vehicle, value ), - - OCL_VAR ( OC_UINT16, 1, &_old_string_id ), - - OCL_CHUNK( 1, LoadOldVehicleUnion ), - - OCL_NULL( 20 ), ///< Junk at end of struct (TTDPatch has some data in it) - - OCL_END() -}; - -bool LoadOldVehicle(LoadgameState *ls, int num) -{ - uint i; - - /* Read the TTDPatch flags, because we need some info from it */ - ReadTTDPatchFlags(); - - for (i = 0; i < _old_vehicle_multiplier; i++) { - _current_vehicle_id = num * _old_vehicle_multiplier + i; - - /* Read the vehicle type and allocate the right vehicle */ - Vehicle *v; - switch (ReadByte(ls)) { - default: NOT_REACHED(); - case 0x00 /*VEH_INVALID */: v = new (_current_vehicle_id) InvalidVehicle(); break; - case 0x10 /*VEH_TRAIN */: v = new (_current_vehicle_id) Train(); break; - case 0x11 /*VEH_ROAD */: v = new (_current_vehicle_id) RoadVehicle(); break; - case 0x12 /*VEH_SHIP */: v = new (_current_vehicle_id) Ship(); break; - case 0x13 /*VEH_AIRCRAFT*/: v = new (_current_vehicle_id) Aircraft(); break; - case 0x14 /*VEH_EFFECT */: v = new (_current_vehicle_id) EffectVehicle(); break; - case 0x15 /*VEH_DISASTER*/: v = new (_current_vehicle_id) DisasterVehicle(); break; - } - if (!LoadChunk(ls, v, vehicle_chunk)) return false; - - /* This should be consistent, else we have a big problem... */ - if (v->index != _current_vehicle_id) { - DEBUG(oldloader, 0, "Loading failed - vehicle-array is invalid"); - return false; - } - - if (_old_order_ptr != 0 && _old_order_ptr != 0xFFFFFFFF) { - uint old_id = REMAP_ORDER_IDX(_old_order_ptr); - /* There is a maximum of 5000 orders in old savegames, so *if* - * we go over that limit something is very wrong. In that case - * we just assume there are no orders for the vehicle. - */ - if (old_id < 5000) v->orders.old = GetOrder(old_id); - } - v->current_order.AssignOrder(UnpackOldOrder(_old_order)); - - /* For some reason we need to correct for this */ - switch (v->spritenum) { - case 0xfd: break; - case 0xff: v->spritenum = 0xfe; break; - default: v->spritenum >>= 1; break; - } - - if (_old_next_ptr != 0xFFFF) v->next = GetVehiclePoolSize() <= _old_next_ptr ? new (_old_next_ptr) InvalidVehicle() : GetVehicle(_old_next_ptr); - - _old_vehicle_names[_current_vehicle_id] = RemapOldStringID(_old_string_id); - v->name = NULL; - - /* Vehicle-subtype is different in TTD(Patch) */ - if (v->type == VEH_EFFECT) v->subtype = v->subtype >> 1; - - if (_cargo_count != 0) { - CargoPacket *cp = new CargoPacket((_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, _cargo_count); - cp->days_in_transit = _cargo_days; - v->cargo.Append(cp); - } - } - - return true; -} - -static const OldChunks sign_chunk[] = { - OCL_VAR ( OC_UINT16, 1, &_old_string_id ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Sign, x ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Sign, y ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_I8, Sign, z ), - - OCL_NULL( 6 ), ///< Width of sign, no longer in use - - OCL_END() -}; - -static bool LoadOldSign(LoadgameState *ls, int num) -{ - Sign *si = new (num) Sign(); - if (!LoadChunk(ls, si, sign_chunk)) return false; - - _old_string_id = RemapOldStringID(_old_string_id); - si->name = CopyFromOldName(_old_string_id); - - return true; -} - -static const OldChunks engine_chunk[] = { - OCL_SVAR( OC_UINT16, Engine, company_avail ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Engine, intro_date ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Engine, age ), - OCL_SVAR( OC_UINT16, Engine, reliability ), - OCL_SVAR( OC_UINT16, Engine, reliability_spd_dec ), - OCL_SVAR( OC_UINT16, Engine, reliability_start ), - OCL_SVAR( OC_UINT16, Engine, reliability_max ), - OCL_SVAR( OC_UINT16, Engine, reliability_final ), - OCL_SVAR( OC_UINT16, Engine, duration_phase_1 ), - OCL_SVAR( OC_UINT16, Engine, duration_phase_2 ), - OCL_SVAR( OC_UINT16, Engine, duration_phase_3 ), - - OCL_SVAR( OC_UINT8, Engine, lifelength ), - OCL_SVAR( OC_UINT8, Engine, flags ), - OCL_SVAR( OC_UINT8, Engine, preview_company_rank ), - OCL_SVAR( OC_UINT8, Engine, preview_wait ), - - OCL_NULL( 2 ), ///< Junk - - OCL_END() -}; - -static bool LoadOldEngine(LoadgameState *ls, int num) -{ - Engine *e = GetTempDataEngine(num); - return LoadChunk(ls, e, engine_chunk); -} - -static bool LoadOldEngineName(LoadgameState *ls, int num) -{ - Engine *e = GetTempDataEngine(num); - e->name = CopyFromOldName(RemapOldStringID(ReadUint16(ls))); - return true; -} - -static const OldChunks subsidy_chunk[] = { - OCL_SVAR( OC_UINT8, Subsidy, cargo_type ), - OCL_SVAR( OC_UINT8, Subsidy, age ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Subsidy, from ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Subsidy, to ), - - OCL_END() -}; - -static inline bool LoadOldSubsidy(LoadgameState *ls, int num) -{ - return LoadChunk(ls, &_subsidies[num], subsidy_chunk); -} - -static const OldChunks game_difficulty_chunk[] = { - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, max_no_competitors ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_start_time ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, number_towns ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, number_industries ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, DifficultySettings, max_loan ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, initial_interest ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, vehicle_costs ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_speed ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_intelligence ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, vehicle_breakdowns ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, subsidy_multiplier ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, construction_cost ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, terrain_type ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, quantity_sea_lakes ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, economy ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, line_reverse_mode ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, disasters ), - OCL_END() -}; - -static inline bool LoadOldGameDifficulty(LoadgameState *ls, int num) -{ - bool ret = LoadChunk(ls, &_settings_game.difficulty, game_difficulty_chunk); - _settings_game.difficulty.max_loan *= 1000; - return ret; -} - - -static bool LoadOldMapPart1(LoadgameState *ls, int num) -{ - uint i; - - for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m1 = ReadByte(ls); - } - for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m2 = ReadByte(ls); - } - for (i = 0; i < OLD_MAP_SIZE; i++) { - _old_map3[i * 2] = ReadByte(ls); - _old_map3[i * 2 + 1] = ReadByte(ls); - } - for (i = 0; i < OLD_MAP_SIZE / 4; i++) { - byte b = ReadByte(ls); - _m[i * 4 + 0].m6 = GB(b, 0, 2); - _m[i * 4 + 1].m6 = GB(b, 2, 2); - _m[i * 4 + 2].m6 = GB(b, 4, 2); - _m[i * 4 + 3].m6 = GB(b, 6, 2); - } - - return !ls->failed; -} - -static bool LoadOldMapPart2(LoadgameState *ls, int num) -{ - uint i; - - for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].type_height = ReadByte(ls); - } - for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m5 = ReadByte(ls); - } - - return !ls->failed; -} - -static bool LoadTTDPatchExtraChunks(LoadgameState *ls, int num) -{ - ReadTTDPatchFlags(); - - DEBUG(oldloader, 2, "Found %d extra chunk(s)", _old_extra_chunk_nums); - - for (int i = 0; i != _old_extra_chunk_nums; i++) { - uint16 id = ReadUint16(ls); - uint32 len = ReadUint32(ls); - - switch (id) { - /* List of GRFIDs, used in the savegame. 0x8004 is the new ID - * They are saved in a 'GRFID:4 active:1' format, 5 bytes for each entry */ - case 0x2: - case 0x8004: { - /* Skip the first element: TTDP hack for the Action D special variables (FFFF0000 01) */ - ReadUint32(ls); ReadByte(ls); len -= 5; - - ClearGRFConfigList(&_grfconfig); - while (len != 0) { - uint32 grfid = ReadUint32(ls); - - if (ReadByte(ls) == 1) { - GRFConfig *c = CallocT(1); - c->grfid = grfid; - c->filename = strdup("TTDP game, no information"); - - AppendToGRFConfigList(&_grfconfig, c); - DEBUG(oldloader, 3, "TTDPatch game using GRF file with GRFID %0X", BSWAP32(c->grfid)); - } - len -= 5; - }; - - /* Append static NewGRF configuration */ - AppendStaticGRFConfigs(&_grfconfig); - } break; - - /* TTDPatch version and configuration */ - case 0x3: - _ttdp_version = ReadUint32(ls); - DEBUG(oldloader, 3, "Game saved with TTDPatch version %d.%d.%d r%d", - GB(_ttdp_version, 24, 8), GB(_ttdp_version, 20, 4), GB(_ttdp_version, 16, 4), GB(_ttdp_version, 0, 16)); - len -= 4; - while (len-- != 0) ReadByte(ls); // skip the configuration - break; - - default: - DEBUG(oldloader, 4, "Skipping unknown extra chunk %X", id); - while (len-- != 0) ReadByte(ls); - break; - } - } - - return !ls->failed; -} - -extern TileIndex _cur_tileloop_tile; -static uint32 _old_cur_town_ctr; -static const OldChunks main_chunk[] = { - OCL_ASSERT( 0 ), - OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_date ), - OCL_VAR ( OC_UINT16, 1, &_date_fract ), - OCL_NULL( 600 ), ///< TextEffects - OCL_VAR ( OC_UINT32, 2, &_random.state ), - - OCL_ASSERT( 0x264 ), - OCL_CHUNK( 70, LoadOldTown ), - OCL_ASSERT( 0x1C18 ), - OCL_CHUNK(5000, LoadOldOrder ), - OCL_ASSERT( 0x4328 ), - - OCL_CHUNK( 1, LoadOldAnimTileList ), - OCL_NULL( 4 ), ///< old end-of-order-list-pointer, no longer in use - - OCL_CHUNK( 255, LoadOldDepot ), - OCL_ASSERT( 0x4B26 ), - - OCL_VAR ( OC_UINT32, 1, &_old_cur_town_ctr ), - OCL_NULL( 2 ), ///< timer_counter, no longer in use - OCL_NULL( 2 ), ///< land_code, no longer in use - - OCL_VAR ( OC_FILE_U16 | OC_VAR_U8, 1, &_age_cargo_skip_counter ), - OCL_VAR ( OC_UINT16, 1, &_tick_counter ), - OCL_VAR ( OC_TILE, 1, &_cur_tileloop_tile ), - - OCL_CHUNK( 49, LoadOldPrice ), - OCL_CHUNK( 12, LoadOldCargoPaymentRate ), - - OCL_ASSERT( 0x4CBA ), - - OCL_CHUNK( 1, LoadOldMapPart1 ), - - OCL_ASSERT( 0x48CBA ), - - OCL_CHUNK(250, LoadOldStation ), - OCL_CHUNK( 90, LoadOldIndustry ), - OCL_CHUNK( 8, LoadOldCompany ), - - OCL_ASSERT( 0x547F2 ), - - OCL_CHUNK( 850, LoadOldVehicle ), - - OCL_ASSERT( 0x6F0F2 ), - - OCL_VAR ( OC_UINT8 | OC_DEREFERENCE_POINTER, 32 * 500, &_old_name_array ), - - OCL_NULL( 0x2000 ), ///< Old hash-table, no longer in use - - OCL_CHUNK( 40, LoadOldSign ), - OCL_CHUNK(256, LoadOldEngine ), - - OCL_VAR ( OC_UINT16, 1, &_vehicle_id_ctr_day ), - - OCL_CHUNK( 8, LoadOldSubsidy ), - - OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_next_competitor_start ), - OCL_VAR ( OC_FILE_I16 | OC_VAR_I32, 1, &_saved_scrollpos_x ), - OCL_VAR ( OC_FILE_I16 | OC_VAR_I32, 1, &_saved_scrollpos_y ), - OCL_VAR ( OC_FILE_U16 | OC_VAR_U8, 1, &_saved_scrollpos_zoom ), - - OCL_VAR ( OC_FILE_U32 | OC_VAR_I64, 1, &_economy.max_loan ), - OCL_VAR ( OC_FILE_U32 | OC_VAR_I64, 1, &_economy.max_loan_unround ), - OCL_VAR ( OC_INT16, 1, &_economy.fluct ), - - OCL_VAR ( OC_UINT16, 1, &_disaster_delay ), - - OCL_NULL( 144 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables - - OCL_CHUNK(256, LoadOldEngineName ), - - OCL_NULL( 144 ), ///< AI cargo-stuff, calculated in InitializeLandscapeVariables - OCL_NULL( 2 ), ///< Company indexes of companies, no longer in use - - OCL_VAR ( OC_FILE_U8 | OC_VAR_U16, 1, &_station_tick_ctr ), - - OCL_VAR ( OC_UINT8, 1, &_settings_game.locale.currency ), - OCL_VAR ( OC_UINT8, 1, &_settings_game.locale.units ), - OCL_VAR ( OC_FILE_U8 | OC_VAR_U32, 1, &_cur_company_tick_index ), - - OCL_NULL( 2 ), ///< Date stuff, calculated automatically - OCL_NULL( 8 ), ///< Company colors, calculated automatically - - OCL_VAR ( OC_UINT8, 1, &_economy.infl_amount ), - OCL_VAR ( OC_UINT8, 1, &_economy.infl_amount_pr ), - OCL_VAR ( OC_UINT8, 1, &_economy.interest_rate ), - OCL_NULL( 1 ), // available airports - OCL_VAR ( OC_UINT8, 1, &_settings_game.vehicle.road_side ), - OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.town_name ), - - OCL_CHUNK( 1, LoadOldGameDifficulty ), - - OCL_ASSERT( 0x77130 ), - - OCL_VAR ( OC_UINT8, 1, &_settings_game.difficulty.diff_level ), - OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.landscape ), - OCL_VAR ( OC_UINT8, 1, &_trees_tick_ctr ), - - OCL_NULL( 1 ), ///< Custom vehicle types yes/no, no longer used - OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.snow_line ), - - OCL_NULL( 32 ), ///< new_industry_randtable, no longer used (because of new design) - OCL_NULL( 36 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables - - OCL_ASSERT( 0x77179 ), - - OCL_CHUNK( 1, LoadOldMapPart2 ), - - OCL_ASSERT( 0x97179 ), - - /* Below any (if available) extra chunks from TTDPatch can follow */ - OCL_CHUNK(1, LoadTTDPatchExtraChunks), - - OCL_END() -}; - -static bool LoadOldMain(LoadgameState *ls) -{ - int i; - - /* The first 49 is the name of the game + checksum, skip it */ - fseek(ls->file, HEADER_SIZE, SEEK_SET); - - DEBUG(oldloader, 3, "Reading main chunk..."); - /* Load the biggest chunk */ - _old_map3 = MallocT(OLD_MAP_SIZE * 2); - _old_vehicle_names = NULL; - if (!LoadChunk(ls, NULL, main_chunk)) { - DEBUG(oldloader, 0, "Loading failed"); - free(_old_map3); - free(_old_vehicle_names); - return false; - } - DEBUG(oldloader, 3, "Done, converting game data..."); - - /* Fix some general stuff */ - _settings_game.game_creation.landscape = _settings_game.game_creation.landscape & 0xF; - - /* Remap some pointers */ - _cur_town_ctr = REMAP_TOWN_IDX(_old_cur_town_ctr); - - /* _old_map3 is changed in _map3_lo and _map3_hi */ - for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m3 = _old_map3[i * 2]; - _m[i].m4 = _old_map3[i * 2 + 1]; - } - - for (i = 0; i < OLD_MAP_SIZE; i ++) { - switch (GetTileType(i)) { - case MP_STATION: - _m[i].m4 = 0; // We do not understand this TTDP station mapping (yet) - switch (_m[i].m5) { - /* We have drive through stops at a totally different place */ - case 0x53: case 0x54: _m[i].m5 += 170 - 0x53; break; // Bus drive through - case 0x57: case 0x58: _m[i].m5 += 168 - 0x57; break; // Truck drive through - case 0x55: case 0x56: _m[i].m5 += 170 - 0x55; break; // Bus tram stop - case 0x59: case 0x5A: _m[i].m5 += 168 - 0x59; break; // Truck tram stop - default: break; - } - break; - - case MP_RAILWAY: - /* We save presignals different from TTDPatch, convert them */ - if (GetRailTileType(i) == RAIL_TILE_SIGNALS) { - /* This byte is always zero in TTD for this type of tile */ - if (_m[i].m4) /* Convert the presignals to our own format */ - _m[i].m4 = (_m[i].m4 >> 1) & 7; - } - /* TTDPatch stores PBS things in L6 and all elsewhere; so we'll just - * clear it for ourselves and let OTTD's rebuild PBS itself */ - _m[i].m4 &= 0xF; /* Only keep the lower four bits; upper four is PBS */ - break; - - case MP_WATER: - if (GetWaterClass(i) == 3) MakeRiver(i, Random()); - break; - - default: - break; - } - } - - /* Make sure the available engines are really available, otherwise - * we will get a "new vehicle"-spree. */ - Engine *e; - FOR_ALL_ENGINES(e) { - if (_date >= (e->intro_date + 365)) { - e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE; - e->company_avail = (CompanyMask)-1; - } - } - - /* Fix the game to be compatible with OpenTTD */ - FixOldTowns(); - FixOldVehicles(); - - /* We have a new difficulty setting */ - _settings_game.difficulty.town_council_tolerance = Clamp(_settings_game.difficulty.diff_level, 0, 2); - - DEBUG(oldloader, 3, "Finished converting game data"); - DEBUG(oldloader, 1, "TTD(Patch) savegame successfully converted"); - - free(_old_map3); - free(_old_vehicle_names); - - return true; -} - -bool LoadOldSaveGame(const char *file) -{ - LoadgameState ls; - - DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame"); - - InitLoading(&ls); - - /* Open file */ - ls.file = fopen(file, "rb"); - - if (ls.file == NULL) { - DEBUG(oldloader, 0, "Cannot open file '%s'", file); - return false; - } - - /* Load the main chunk */ - if (!LoadOldMain(&ls)) return false; - - fclose(ls.file); - - /* Some old TTD(Patch) savegames could have buoys at tile 0 - * (without assigned station struct) - * MakeWater() can be used as long as sea has the same - * format as old savegames (eg. everything is zeroed) */ - MakeWater(0); - - _pause_game = 2; - - return true; -} - -void GetOldSaveGameName(char *title, const char *path, const char *file) -{ - char filename[MAX_PATH]; - FILE *f; - - snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, file); - f = fopen(filename, "rb"); - title[0] = '\0'; - title[48] = '\0'; - - if (f == NULL) return; - - if (fread(title, 1, 48, f) != 48) snprintf(title, 48, "Corrupt file"); - - fclose(f); -} diff --git a/src/openttd.cpp b/src/openttd.cpp --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -25,7 +25,7 @@ #include "window_func.h" #include "debug.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "landscape.h" #include "company_func.h" #include "company_base.h" @@ -96,7 +96,6 @@ void IncreaseDate(); void DoPaletteAnimations(); void MusicLoop(); void ResetMusic(); -void ResetOldNames(); void ProcessAsyncSaveFinish(); void CallWindowTickEvent(); @@ -1182,1566 +1181,3 @@ void GameLoop() _sound_driver->MainLoop(); MusicLoop(); } - -static void ConvertTownOwner() -{ - for (TileIndex tile = 0; tile != MapSize(); tile++) { - switch (GetTileType(tile)) { - case MP_ROAD: - if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m3, 7)) { - _m[tile].m3 = OWNER_TOWN; - } - /* FALLTHROUGH */ - - case MP_TUNNELBRIDGE: - if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN); - break; - - default: break; - } - } -} - -/* since savegame version 4.1, exclusive transport rights are stored at towns */ -static void UpdateExclusiveRights() -{ - Town *t; - - FOR_ALL_TOWNS(t) { - t->exclusivity = INVALID_COMPANY; - } - - /* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete) - * could be implemented this way: - * 1.) Go through all stations - * Build an array town_blocked[ town_id ][ company_id ] - * that stores if at least one station in that town is blocked for a company - * 2.) Go through that array, if you find a town that is not blocked for - * one company, but for all others, then give him exclusivity. - */ -} - -static const byte convert_currency[] = { - 0, 1, 12, 8, 3, - 10, 14, 19, 4, 5, - 9, 11, 13, 6, 17, - 16, 22, 21, 7, 15, - 18, 2, 20, }; - -/* since savegame version 4.2 the currencies are arranged differently */ -static void UpdateCurrencies() -{ - _settings_game.locale.currency = convert_currency[_settings_game.locale.currency]; -} - -/* Up to revision 1413 the invisible tiles at the southern border have not been - * MP_VOID, even though they should have. This is fixed by this function - */ -static void UpdateVoidTiles() -{ - uint i; - - for (i = 0; i < MapMaxY(); ++i) MakeVoid(i * MapSizeX() + MapMaxX()); - for (i = 0; i < MapSizeX(); ++i) MakeVoid(MapSizeX() * MapMaxY() + i); -} - -/* since savegame version 6.0 each sign has an "owner", signs without owner (from old games are set to 255) */ -static void UpdateSignOwner() -{ - Sign *si; - - FOR_ALL_SIGNS(si) si->owner = OWNER_NONE; -} - -extern void UpdateOldAircraft(); - - -static inline RailType UpdateRailType(RailType rt, RailType min) -{ - return rt >= min ? (RailType)(rt + 1): rt; -} - -/** - * Initialization of the windows and several kinds of caches. - * This is not done directly in AfterLoadGame because these - * functions require that all saveload conversions have been - * done. As people tend to add savegame conversion stuff after - * the intialization of the windows and caches quite some bugs - * had been made. - * Moving this out of there is both cleaner and less bug-prone. - * - * @return true if everything went according to plan, otherwise false. - */ -static bool InitializeWindowsAndCaches() -{ - /* Initialize windows */ - ResetWindowSystem(); - SetupColorsAndInitialWindow(); - - extern void ResetViewportAfterLoadGame(); - ResetViewportAfterLoadGame(); - - /* Update coordinates of the signs. */ - UpdateAllStationVirtCoord(); - UpdateAllSignVirtCoords(); - UpdateAllTownVirtCoords(); - UpdateAllWaypointSigns(); - - Company *c; - FOR_ALL_COMPANIES(c) { - /* For each company, verify (while loading a scenario) that the inauguration date is the current year and set it - * accordingly if it is not the case. No need to set it on companies that are not been used already, - * thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */ - if (_file_to_saveload.filetype == FT_SCENARIO && c->inaugurated_year != MIN_YEAR) { - c->inaugurated_year = _cur_year; - } - } - - SetCachedEngineCounts(); - - /* Towns have a noise controlled number of airports system - * So each airport's noise value must be added to the town->noise_reached value - * Reset each town's noise_reached value to '0' before. */ - UpdateAirportsNoise(); - - CheckTrainsLengths(); - - return true; -} - -/** - * Signal handler used to give a user a more useful report for crashes during - * the savegame loading process; especially when there's problems with the - * NewGRFs that are required by the savegame. - * @param unused well... unused - */ -void CDECL HandleSavegameLoadCrash(int unused) -{ - char buffer[8192]; - char *p = buffer; - p += seprintf(p, lastof(buffer), - "Loading your savegame caused OpenTTD to crash.\n" - "This is most likely caused by a missing NewGRF or a NewGRF that has been\n" - "loaded as replacement for a missing NewGRF. OpenTTD cannot easily\n" - "determine whether a replacement NewGRF is of a newer or older version.\n" - "It will load a NewGRF with the same GRF ID as the missing NewGRF. This\n" - "means that if the author makes incompatible NewGRFs with the same GRF ID\n" - "OpenTTD cannot magically do the right thing. In most cases OpenTTD will\n" - "load the savegame and not crash, but this is an exception.\n" - "Please load the savegame with the appropriate NewGRFs. When loading a\n" - "savegame still crashes when all NewGRFs are found you should file a\n" - "bug report. The missing NewGRFs are:\n"); - - for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) { - if (HasBit(c->flags, GCF_COMPATIBLE)) { - char buf[40]; - md5sumToString(buf, lastof(buf), c->md5sum); - p += seprintf(p, lastof(buffer), "NewGRF %08X (%s) not found; checksum %s. Tried another NewGRF with same GRF ID\n", BSWAP32(c->grfid), c->filename, buf); - } - if (c->status == GCS_NOT_FOUND) { - char buf[40]; - md5sumToString(buf, lastof(buf), c->md5sum); - p += seprintf(p, lastof(buffer), "NewGRF %08X (%s) not found; checksum %s\n", BSWAP32(c->grfid), c->filename, buf); - } - } - - ShowInfo(buffer); -} - -bool AfterLoadGame() -{ - typedef void (CDECL *SignalHandlerPointer)(int); - SignalHandlerPointer prev_segfault = signal(SIGSEGV, HandleSavegameLoadCrash); - SignalHandlerPointer prev_abort = signal(SIGABRT, HandleSavegameLoadCrash); - - TileIndex map_size = MapSize(); - Company *c; - - if (CheckSavegameVersion(98)) GamelogOldver(); - - GamelogTestRevision(); - GamelogTestMode(); - - if (CheckSavegameVersion(98)) GamelogGRFAddList(_grfconfig); - - /* in very old versions, size of train stations was stored differently */ - if (CheckSavegameVersion(2)) { - Station *st; - FOR_ALL_STATIONS(st) { - if (st->train_tile != 0 && st->trainst_h == 0) { - extern SavegameType _savegame_type; - uint n = _savegame_type == SGT_OTTD ? 4 : 3; // OTTD uses 4 bits per dimensions, TTD 3 bits - uint w = GB(st->trainst_w, n, n); - uint h = GB(st->trainst_w, 0, n); - - if (GetRailStationAxis(st->train_tile) != AXIS_X) Swap(w, h); - - st->trainst_w = w; - st->trainst_h = h; - - assert(GetStationIndex(st->train_tile + TileDiffXY(w - 1, h - 1)) == st->index); - } - } - } - - /* in version 2.1 of the savegame, town owner was unified. */ - if (CheckSavegameVersionOldStyle(2, 1)) ConvertTownOwner(); - - /* from version 4.1 of the savegame, exclusive rights are stored at towns */ - if (CheckSavegameVersionOldStyle(4, 1)) UpdateExclusiveRights(); - - /* from version 4.2 of the savegame, currencies are in a different order */ - if (CheckSavegameVersionOldStyle(4, 2)) UpdateCurrencies(); - - /* from version 6.1 of the savegame, signs have an "owner" */ - if (CheckSavegameVersionOldStyle(6, 1)) UpdateSignOwner(); - - /* In old version there seems to be a problem that water is owned by - * OWNER_NONE, not OWNER_WATER.. I can't replicate it for the current - * (4.3) version, so I just check when versions are older, and then - * walk through the whole map.. */ - if (CheckSavegameVersionOldStyle(4, 3)) { - for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_WATER) && GetTileOwner(t) >= MAX_COMPANIES) { - SetTileOwner(t, OWNER_WATER); - } - } - } - - if (CheckSavegameVersion(84)) { - FOR_ALL_COMPANIES(c) { - c->name = CopyFromOldName(c->name_1); - if (c->name != NULL) c->name_1 = STR_SV_UNNAMED; - c->president_name = CopyFromOldName(c->president_name_1); - if (c->president_name != NULL) c->president_name_1 = SPECSTR_PRESIDENT_NAME; - } - - Station *st; - FOR_ALL_STATIONS(st) { - st->name = CopyFromOldName(st->string_id); - /* generating new name would be too much work for little effect, use the station name fallback */ - if (st->name != NULL) st->string_id = STR_SV_STNAME_FALLBACK; - } - - Town *t; - FOR_ALL_TOWNS(t) { - t->name = CopyFromOldName(t->townnametype); - if (t->name != NULL) t->townnametype = SPECSTR_TOWNNAME_START + _settings_game.game_creation.town_name; - } - - Waypoint *wp; - FOR_ALL_WAYPOINTS(wp) { - wp->name = CopyFromOldName(wp->string); - wp->string = STR_EMPTY; - } - - for (uint i = 0; i < GetSignPoolSize(); i++) { - /* invalid signs are determined by si->ower == INVALID_COMPANY now */ - Sign *si = GetSign(i); - if (!si->IsValid() && si->name != NULL) { - si->owner = OWNER_NONE; - } - } - } - - /* From this point the old names array is cleared. */ - ResetOldNames(); - - if (CheckSavegameVersion(106)) { - /* no station is determined by 'tile == INVALID_TILE' now (instead of '0') */ - Station *st; - FOR_ALL_STATIONS(st) { - if (st->airport_tile == 0) st->airport_tile = INVALID_TILE; - if (st->dock_tile == 0) st->dock_tile = INVALID_TILE; - if (st->train_tile == 0) st->train_tile = INVALID_TILE; - } - - /* the same applies to Company::location_of_HQ */ - Company *c; - FOR_ALL_COMPANIES(c) { - if (c->location_of_HQ == 0 || (CheckSavegameVersion(4) && c->location_of_HQ == 0xFFFF)) { - c->location_of_HQ = INVALID_TILE; - } - } - } - - /* convert road side to my format. */ - if (_settings_game.vehicle.road_side) _settings_game.vehicle.road_side = 1; - - /* Check if all NewGRFs are present, we are very strict in MP mode */ - GRFListCompatibility gcf_res = IsGoodGRFConfigList(); - if (_networking && gcf_res != GLC_ALL_GOOD) { - SetSaveLoadError(STR_NETWORK_ERR_CLIENT_NEWGRF_MISMATCH); - /* Restore the signals */ - signal(SIGSEGV, prev_segfault); - signal(SIGABRT, prev_abort); - return false; - } - - switch (gcf_res) { - case GLC_COMPATIBLE: _switch_mode_errorstr = STR_NEWGRF_COMPATIBLE_LOAD_WARNING; break; - case GLC_NOT_FOUND: _switch_mode_errorstr = STR_NEWGRF_DISABLED_WARNING; _pause_game = -1; break; - default: break; - } - - /* Update current year - * must be done before loading sprites as some newgrfs check it */ - SetDate(_date); - - /* Force dynamic engines off when loading older savegames */ - if (CheckSavegameVersion(95)) _settings_game.vehicle.dynamic_engines = 0; - - /* Load the sprites */ - GfxLoadSprites(); - LoadStringWidthTable(); - - /* Copy temporary data to Engine pool */ - CopyTempEngineData(); - - /* Connect front and rear engines of multiheaded trains and converts - * subtype to the new format */ - if (CheckSavegameVersionOldStyle(17, 1)) ConvertOldMultiheadToNew(); - - /* Connect front and rear engines of multiheaded trains */ - ConnectMultiheadedTrains(); - - /* reinit the landscape variables (landscape might have changed) */ - InitializeLandscapeVariables(true); - - /* Update all vehicles */ - AfterLoadVehicles(true); - - /* Update all waypoints */ - if (CheckSavegameVersion(12)) FixOldWaypoints(); - - /* in version 2.2 of the savegame, we have new airports */ - if (CheckSavegameVersionOldStyle(2, 2)) UpdateOldAircraft(); - - AfterLoadTown(); - - /* make sure there is a town in the game */ - if (_game_mode == GM_NORMAL && !ClosestTownFromTile(0, UINT_MAX)) { - SetSaveLoadError(STR_NO_TOWN_IN_SCENARIO); - /* Restore the signals */ - signal(SIGSEGV, prev_segfault); - signal(SIGABRT, prev_abort); - return false; - } - - /* The void tiles on the southern border used to belong to a wrong class (pre 4.3). - * This problem appears in savegame version 21 too, see r3455. But after loading the - * savegame and saving again, the buggy map array could be converted to new savegame - * version. It didn't show up before r12070. */ - if (CheckSavegameVersion(87)) UpdateVoidTiles(); - - /* If Load Scenario / New (Scenario) Game is used, - * a company does not exist yet. So create one here. - * 1 exeption: network-games. Those can have 0 companies - * But this exeption is not true for non dedicated network_servers! */ - if (!IsValidCompanyID(COMPANY_FIRST) && (!_networking || (_networking && _network_server && !_network_dedicated))) - DoStartupNewCompany(false); - - if (CheckSavegameVersion(72)) { - /* Locks/shiplifts in very old savegames had OWNER_WATER as owner */ - for (TileIndex t = 0; t < MapSize(); t++) { - switch (GetTileType(t)) { - default: break; - - case MP_WATER: - if (GetWaterTileType(t) == WATER_TILE_LOCK && GetTileOwner(t) == OWNER_WATER) SetTileOwner(t, OWNER_NONE); - break; - - case MP_STATION: { - if (HasBit(_m[t].m6, 3)) SetBit(_m[t].m6, 2); - StationGfx gfx = GetStationGfx(t); - StationType st; - if ( IsInsideMM(gfx, 0, 8)) { // Railway station - st = STATION_RAIL; - SetStationGfx(t, gfx - 0); - } else if (IsInsideMM(gfx, 8, 67)) { // Airport - st = STATION_AIRPORT; - SetStationGfx(t, gfx - 8); - } else if (IsInsideMM(gfx, 67, 71)) { // Truck - st = STATION_TRUCK; - SetStationGfx(t, gfx - 67); - } else if (IsInsideMM(gfx, 71, 75)) { // Bus - st = STATION_BUS; - SetStationGfx(t, gfx - 71); - } else if (gfx == 75) { // Oil rig - st = STATION_OILRIG; - SetStationGfx(t, gfx - 75); - } else if (IsInsideMM(gfx, 76, 82)) { // Dock - st = STATION_DOCK; - SetStationGfx(t, gfx - 76); - } else if (gfx == 82) { // Buoy - st = STATION_BUOY; - SetStationGfx(t, gfx - 82); - } else if (IsInsideMM(gfx, 83, 168)) { // Extended airport - st = STATION_AIRPORT; - SetStationGfx(t, gfx - 83 + 67 - 8); - } else if (IsInsideMM(gfx, 168, 170)) { // Drive through truck - st = STATION_TRUCK; - SetStationGfx(t, gfx - 168 + GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET); - } else if (IsInsideMM(gfx, 170, 172)) { // Drive through bus - st = STATION_BUS; - SetStationGfx(t, gfx - 170 + GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET); - } else { - /* Restore the signals */ - signal(SIGSEGV, prev_segfault); - signal(SIGABRT, prev_abort); - return false; - } - SB(_m[t].m6, 3, 3, st); - } break; - } - } - } - - for (TileIndex t = 0; t < map_size; t++) { - switch (GetTileType(t)) { - case MP_STATION: { - Station *st = GetStationByTile(t); - - /* Set up station spread; buoys do not have one */ - if (!IsBuoy(t)) st->rect.BeforeAddTile(t, StationRect::ADD_FORCE); - - switch (GetStationType(t)) { - case STATION_TRUCK: - case STATION_BUS: - if (CheckSavegameVersion(6)) { - /* From this version on there can be multiple road stops of the - * same type per station. Convert the existing stops to the new - * internal data structure. */ - RoadStop *rs = new RoadStop(t); - if (rs == NULL) error("Too many road stops in savegame"); - - RoadStop **head = - IsTruckStop(t) ? &st->truck_stops : &st->bus_stops; - *head = rs; - } - break; - - case STATION_OILRIG: { - /* Very old savegames sometimes have phantom oil rigs, i.e. - * an oil rig which got shut down, but not completly removed from - * the map - */ - TileIndex t1 = TILE_ADDXY(t, 0, 1); - if (IsTileType(t1, MP_INDUSTRY) && - GetIndustryGfx(t1) == GFX_OILRIG_1) { - /* The internal encoding of oil rigs was changed twice. - * It was 3 (till 2.2) and later 5 (till 5.1). - * Setting it unconditionally does not hurt. - */ - GetStationByTile(t)->airport_type = AT_OILRIG; - } else { - DeleteOilRig(t); - } - break; - } - - default: break; - } - break; - } - - default: break; - } - } - - /* In version 6.1 we put the town index in the map-array. To do this, we need - * to use m2 (16bit big), so we need to clean m2, and that is where this is - * all about ;) */ - if (CheckSavegameVersionOldStyle(6, 1)) { - for (TileIndex t = 0; t < map_size; t++) { - switch (GetTileType(t)) { - case MP_HOUSE: - _m[t].m4 = _m[t].m2; - SetTownIndex(t, CalcClosestTownFromTile(t, UINT_MAX)->index); - break; - - case MP_ROAD: - _m[t].m4 |= (_m[t].m2 << 4); - if ((GB(_m[t].m5, 4, 2) == ROAD_TILE_CROSSING ? (Owner)_m[t].m3 : GetTileOwner(t)) == OWNER_TOWN) { - SetTownIndex(t, CalcClosestTownFromTile(t, UINT_MAX)->index); - } else { - SetTownIndex(t, 0); - } - break; - - default: break; - } - } - } - - /* From version 9.0, we update the max passengers of a town (was sometimes negative - * before that. */ - if (CheckSavegameVersion(9)) { - Town *t; - FOR_ALL_TOWNS(t) UpdateTownMaxPass(t); - } - - /* From version 16.0, we included autorenew on engines, which are now saved, but - * of course, we do need to initialize them for older savegames. */ - if (CheckSavegameVersion(16)) { - FOR_ALL_COMPANIES(c) { - c->engine_renew_list = NULL; - c->engine_renew = false; - c->engine_renew_months = -6; - c->engine_renew_money = 100000; - } - - /* When loading a game, _local_company is not yet set to the correct value. - * However, in a dedicated server we are a spectator, so nothing needs to - * happen. In case we are not a dedicated server, the local company always - * becomes company 0, unless we are in the scenario editor where all the - * companies are 'invalid'. - */ - if (!_network_dedicated && IsValidCompanyID(COMPANY_FIRST)) { - c = GetCompany(COMPANY_FIRST); - c->engine_renew = _settings_client.gui.autorenew; - c->engine_renew_months = _settings_client.gui.autorenew_months; - c->engine_renew_money = _settings_client.gui.autorenew_money; - } - } - - if (CheckSavegameVersion(48)) { - for (TileIndex t = 0; t < map_size; t++) { - switch (GetTileType(t)) { - case MP_RAILWAY: - if (IsPlainRailTile(t)) { - /* Swap ground type and signal type for plain rail tiles, so the - * ground type uses the same bits as for depots and waypoints. */ - uint tmp = GB(_m[t].m4, 0, 4); - SB(_m[t].m4, 0, 4, GB(_m[t].m2, 0, 4)); - SB(_m[t].m2, 0, 4, tmp); - } else if (HasBit(_m[t].m5, 2)) { - /* Split waypoint and depot rail type and remove the subtype. */ - ClrBit(_m[t].m5, 2); - ClrBit(_m[t].m5, 6); - } - break; - - case MP_ROAD: - /* Swap m3 and m4, so the track type for rail crossings is the - * same as for normal rail. */ - Swap(_m[t].m3, _m[t].m4); - break; - - default: break; - } - } - } - - if (CheckSavegameVersion(61)) { - /* Added the RoadType */ - bool old_bridge = CheckSavegameVersion(42); - for (TileIndex t = 0; t < map_size; t++) { - switch(GetTileType(t)) { - case MP_ROAD: - SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2)); - switch (GetRoadTileType(t)) { - default: NOT_REACHED(); - case ROAD_TILE_NORMAL: - SB(_m[t].m4, 0, 4, GB(_m[t].m5, 0, 4)); - SB(_m[t].m4, 4, 4, 0); - SB(_m[t].m6, 2, 4, 0); - break; - case ROAD_TILE_CROSSING: - SB(_m[t].m4, 5, 2, GB(_m[t].m5, 2, 2)); - break; - case ROAD_TILE_DEPOT: break; - } - SetRoadTypes(t, ROADTYPES_ROAD); - break; - - case MP_STATION: - if (IsRoadStop(t)) SetRoadTypes(t, ROADTYPES_ROAD); - break; - - case MP_TUNNELBRIDGE: - /* Middle part of "old" bridges */ - if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break; - if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { - SetRoadTypes(t, ROADTYPES_ROAD); - } - break; - - default: break; - } - } - } - - if (CheckSavegameVersion(42)) { - Vehicle* v; - - for (TileIndex t = 0; t < map_size; t++) { - if (MayHaveBridgeAbove(t)) ClearBridgeMiddle(t); - if (IsBridgeTile(t)) { - if (HasBit(_m[t].m5, 6)) { // middle part - Axis axis = (Axis)GB(_m[t].m5, 0, 1); - - if (HasBit(_m[t].m5, 5)) { // transport route under bridge? - if (GB(_m[t].m5, 3, 2) == TRANSPORT_RAIL) { - MakeRailNormal( - t, - GetTileOwner(t), - axis == AXIS_X ? TRACK_BIT_Y : TRACK_BIT_X, - GetRailType(t) - ); - } else { - TownID town = IsTileOwner(t, OWNER_TOWN) ? ClosestTownFromTile(t, UINT_MAX)->index : 0; - - MakeRoadNormal( - t, - axis == AXIS_X ? ROAD_Y : ROAD_X, - ROADTYPES_ROAD, - town, - GetTileOwner(t), OWNER_NONE, OWNER_NONE - ); - } - } else { - if (GB(_m[t].m5, 3, 2) == 0) { - MakeClear(t, CLEAR_GRASS, 3); - } else { - if (GetTileSlope(t, NULL) != SLOPE_FLAT) { - MakeShore(t); - } else { - if (GetTileOwner(t) == OWNER_WATER) { - MakeWater(t); - } else { - MakeCanal(t, GetTileOwner(t), Random()); - } - } - } - } - SetBridgeMiddle(t, axis); - } else { // ramp - Axis axis = (Axis)GB(_m[t].m5, 0, 1); - uint north_south = GB(_m[t].m5, 5, 1); - DiagDirection dir = ReverseDiagDir(XYNSToDiagDir(axis, north_south)); - TransportType type = (TransportType)GB(_m[t].m5, 1, 2); - - _m[t].m5 = 1 << 7 | type << 2 | dir; - } - } - } - - FOR_ALL_VEHICLES(v) { - if (v->type != VEH_TRAIN && v->type != VEH_ROAD) continue; - if (IsBridgeTile(v->tile)) { - DiagDirection dir = GetTunnelBridgeDirection(v->tile); - - if (dir != DirToDiagDir(v->direction)) continue; - switch (dir) { - default: NOT_REACHED(); - case DIAGDIR_NE: if ((v->x_pos & 0xF) != 0) continue; break; - case DIAGDIR_SE: if ((v->y_pos & 0xF) != TILE_SIZE - 1) continue; break; - case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break; - case DIAGDIR_NW: if ((v->y_pos & 0xF) != 0) continue; break; - } - } else if (v->z_pos > GetSlopeZ(v->x_pos, v->y_pos)) { - v->tile = GetNorthernBridgeEnd(v->tile); - } else { - continue; - } - if (v->type == VEH_TRAIN) { - v->u.rail.track = TRACK_BIT_WORMHOLE; - } else { - v->u.road.state = RVSB_WORMHOLE; - } - } - } - - /* Elrails got added in rev 24 */ - if (CheckSavegameVersion(24)) { - Vehicle *v; - RailType min_rail = RAILTYPE_ELECTRIC; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN) { - RailType rt = RailVehInfo(v->engine_type)->railtype; - - v->u.rail.railtype = rt; - if (rt == RAILTYPE_ELECTRIC) min_rail = RAILTYPE_RAIL; - } - } - - /* .. so we convert the entire map from normal to elrail (so maintain "fairness") */ - for (TileIndex t = 0; t < map_size; t++) { - switch (GetTileType(t)) { - case MP_RAILWAY: - SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); - break; - - case MP_ROAD: - if (IsLevelCrossing(t)) { - SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); - } - break; - - case MP_STATION: - if (IsRailwayStation(t)) { - SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); - } - break; - - case MP_TUNNELBRIDGE: - if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) { - SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); - } - break; - - default: - break; - } - } - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) TrainConsistChanged(v, true); - } - - } - - /* In version 16.1 of the savegame a company can decide if trains, which get - * replaced, shall keep their old length. In all prior versions, just default - * to false */ - if (CheckSavegameVersionOldStyle(16, 1)) { - FOR_ALL_COMPANIES(c) c->renew_keep_length = false; - } - - /* In version 17, ground type is moved from m2 to m4 for depots and - * waypoints to make way for storing the index in m2. The custom graphics - * id which was stored in m4 is now saved as a grf/id reference in the - * waypoint struct. */ - if (CheckSavegameVersion(17)) { - Waypoint *wp; - - FOR_ALL_WAYPOINTS(wp) { - if (wp->deleted == 0) { - const StationSpec *statspec = NULL; - - if (HasBit(_m[wp->xy].m3, 4)) - statspec = GetCustomStationSpec(STAT_CLASS_WAYP, _m[wp->xy].m4 + 1); - - if (statspec != NULL) { - wp->stat_id = _m[wp->xy].m4 + 1; - wp->grfid = statspec->grffile->grfid; - wp->localidx = statspec->localidx; - } else { - /* No custom graphics set, so set to default. */ - wp->stat_id = 0; - wp->grfid = 0; - wp->localidx = 0; - } - - /* Move ground type bits from m2 to m4. */ - _m[wp->xy].m4 = GB(_m[wp->xy].m2, 0, 4); - /* Store waypoint index in the tile. */ - _m[wp->xy].m2 = wp->index; - } - } - } else { - /* As of version 17, we recalculate the custom graphic ID of waypoints - * from the GRF ID / station index. */ - AfterLoadWaypoints(); - } - - /* From version 15, we moved a semaphore bit from bit 2 to bit 3 in m4, making - * room for PBS. Now in version 21 move it back :P. */ - if (CheckSavegameVersion(21) && !CheckSavegameVersion(15)) { - for (TileIndex t = 0; t < map_size; t++) { - switch (GetTileType(t)) { - case MP_RAILWAY: - if (HasSignals(t)) { - /* convert PBS signals to combo-signals */ - if (HasBit(_m[t].m2, 2)) SetSignalType(t, TRACK_X, SIGTYPE_COMBO); - - /* move the signal variant back */ - SetSignalVariant(t, TRACK_X, HasBit(_m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); - ClrBit(_m[t].m2, 3); - } - - /* Clear PBS reservation on track */ - if (!IsRailDepotTile(t)) { - SB(_m[t].m4, 4, 4, 0); - } else { - ClrBit(_m[t].m3, 6); - } - break; - - case MP_ROAD: /* Clear PBS reservation on crossing */ - if (IsLevelCrossing(t)) ClrBit(_m[t].m5, 0); - break; - - case MP_STATION: /* Clear PBS reservation on station */ - ClrBit(_m[t].m3, 6); - break; - - default: break; - } - } - } - - if (CheckSavegameVersion(25)) { - Vehicle *v; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_ROAD) { - v->vehstatus &= ~0x40; - v->u.road.slot = NULL; - v->u.road.slot_age = 0; - } - } - } else { - Vehicle *v; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_ROAD && v->u.road.slot != NULL) v->u.road.slot->num_vehicles++; - } - } - - if (CheckSavegameVersion(26)) { - Station *st; - FOR_ALL_STATIONS(st) { - st->last_vehicle_type = VEH_INVALID; - } - } - - YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK); - - if (CheckSavegameVersion(34)) FOR_ALL_COMPANIES(c) ResetCompanyLivery(c); - - FOR_ALL_COMPANIES(c) { - c->avail_railtypes = GetCompanyRailtypes(c->index); - c->avail_roadtypes = GetCompanyRoadtypes(c->index); - } - - if (!CheckSavegameVersion(27)) AfterLoadStations(); - - /* Time starts at 0 instead of 1920. - * Account for this in older games by adding an offset */ - if (CheckSavegameVersion(31)) { - Station *st; - Waypoint *wp; - Engine *e; - Industry *i; - Vehicle *v; - - _date += DAYS_TILL_ORIGINAL_BASE_YEAR; - _cur_year += ORIGINAL_BASE_YEAR; - - FOR_ALL_STATIONS(st) st->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR; - FOR_ALL_WAYPOINTS(wp) wp->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR; - FOR_ALL_ENGINES(e) e->intro_date += DAYS_TILL_ORIGINAL_BASE_YEAR; - FOR_ALL_COMPANIES(c) c->inaugurated_year += ORIGINAL_BASE_YEAR; - FOR_ALL_INDUSTRIES(i) i->last_prod_year += ORIGINAL_BASE_YEAR; - - FOR_ALL_VEHICLES(v) { - v->date_of_last_service += DAYS_TILL_ORIGINAL_BASE_YEAR; - v->build_year += ORIGINAL_BASE_YEAR; - } - } - - /* From 32 on we save the industry who made the farmland. - * To give this prettyness to old savegames, we remove all farmfields and - * plant new ones. */ - if (CheckSavegameVersion(32)) { - Industry *i; - - for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) { - /* remove fields */ - MakeClear(t, CLEAR_GRASS, 3); - } else if (IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)) { - /* remove fences around fields */ - SetFenceSE(t, 0); - SetFenceSW(t, 0); - } - } - - FOR_ALL_INDUSTRIES(i) { - uint j; - - if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_ON_BUILT) { - for (j = 0; j != 50; j++) PlantRandomFarmField(i); - } - } - } - - /* Setting no refit flags to all orders in savegames from before refit in orders were added */ - if (CheckSavegameVersion(36)) { - Order *order; - Vehicle *v; - - FOR_ALL_ORDERS(order) { - order->SetRefit(CT_NO_REFIT); - } - - FOR_ALL_VEHICLES(v) { - v->current_order.SetRefit(CT_NO_REFIT); - } - } - - /* from version 38 we have optional elrails, since we cannot know the - * preference of a user, let elrails enabled; it can be disabled manually */ - if (CheckSavegameVersion(38)) _settings_game.vehicle.disable_elrails = false; - /* do the same as when elrails were enabled/disabled manually just now */ - SettingsDisableElrail(_settings_game.vehicle.disable_elrails); - InitializeRailGUI(); - - /* From version 53, the map array was changed for house tiles to allow - * space for newhouses grf features. A new byte, m7, was also added. */ - if (CheckSavegameVersion(53)) { - for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_HOUSE)) { - if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { - /* Move the construction stage from m3[7..6] to m5[5..4]. - * The construction counter does not have to move. */ - SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2)); - SB(_m[t].m3, 6, 2, 0); - - /* The "house is completed" bit is now in m6[2]. */ - SetHouseCompleted(t, false); - } else { - /* The "lift has destination" bit has been moved from - * m5[7] to m7[0]. */ - SB(_me[t].m7, 0, 1, HasBit(_m[t].m5, 7)); - ClrBit(_m[t].m5, 7); - - /* The "lift is moving" bit has been removed, as it does - * the same job as the "lift has destination" bit. */ - ClrBit(_m[t].m1, 7); - - /* The position of the lift goes from m1[7..0] to m6[7..2], - * making m1 totally free, now. The lift position does not - * have to be a full byte since the maximum value is 36. */ - SetLiftPosition(t, GB(_m[t].m1, 0, 6 )); - - _m[t].m1 = 0; - _m[t].m3 = 0; - SetHouseCompleted(t, true); - } - } - } - } - - /* Check and update house and town values */ - UpdateHousesAndTowns(); - - if (CheckSavegameVersion(43)) { - for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_INDUSTRY)) { - switch (GetIndustryGfx(t)) { - case GFX_POWERPLANT_SPARKS: - SetIndustryAnimationState(t, GB(_m[t].m1, 2, 5)); - break; - - case GFX_OILWELL_ANIMATED_1: - case GFX_OILWELL_ANIMATED_2: - case GFX_OILWELL_ANIMATED_3: - SetIndustryAnimationState(t, GB(_m[t].m1, 0, 2)); - break; - - case GFX_COAL_MINE_TOWER_ANIMATED: - case GFX_COPPER_MINE_TOWER_ANIMATED: - case GFX_GOLD_MINE_TOWER_ANIMATED: - SetIndustryAnimationState(t, _m[t].m1); - break; - - default: /* No animation states to change */ - break; - } - } - } - } - - if (CheckSavegameVersion(44)) { - Vehicle *v; - /* If we remove a station while cargo from it is still enroute, payment calculation will assume - * 0, 0 to be the source of the cargo, resulting in very high payments usually. v->source_xy - * stores the coordinates, preserving them even if the station is removed. However, if a game is loaded - * where this situation exists, the cargo-source information is lost. in this case, we set the source - * to the current tile of the vehicle to prevent excessive profits - */ - FOR_ALL_VEHICLES(v) { - const CargoList::List *packets = v->cargo.Packets(); - for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { - CargoPacket *cp = *it; - cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : v->tile; - cp->loaded_at_xy = cp->source_xy; - } - v->cargo.InvalidateCache(); - } - - /* Store position of the station where the goods come from, so there - * are no very high payments when stations get removed. However, if the - * station where the goods came from is already removed, the source - * information is lost. In that case we set it to the position of this - * station */ - Station *st; - FOR_ALL_STATIONS(st) { - for (CargoID c = 0; c < NUM_CARGO; c++) { - GoodsEntry *ge = &st->goods[c]; - - const CargoList::List *packets = ge->cargo.Packets(); - for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { - CargoPacket *cp = *it; - cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : st->xy; - cp->loaded_at_xy = cp->source_xy; - } - } - } - } - - if (CheckSavegameVersion(45)) { - Vehicle *v; - /* Originally just the fact that some cargo had been paid for was - * stored to stop people cheating and cashing in several times. This - * wasn't enough though as it was cleared when the vehicle started - * loading again, even if it didn't actually load anything, so now the - * amount of cargo that has been paid for is stored. */ - FOR_ALL_VEHICLES(v) { - const CargoList::List *packets = v->cargo.Packets(); - for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { - CargoPacket *cp = *it; - cp->paid_for = HasBit(v->vehicle_flags, 2); - } - ClrBit(v->vehicle_flags, 2); - v->cargo.InvalidateCache(); - } - } - - /* Buoys do now store the owner of the previous water tile, which can never - * be OWNER_NONE. So replace OWNER_NONE with OWNER_WATER. */ - if (CheckSavegameVersion(46)) { - Station *st; - FOR_ALL_STATIONS(st) { - if (st->IsBuoy() && IsTileOwner(st->xy, OWNER_NONE) && TileHeight(st->xy) == 0) SetTileOwner(st->xy, OWNER_WATER); - } - } - - if (CheckSavegameVersion(50)) { - Vehicle *v; - /* Aircraft units changed from 8 mph to 1 km/h */ - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_AIRCRAFT && v->subtype <= AIR_AIRCRAFT) { - const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type); - v->cur_speed *= 129; - v->cur_speed /= 10; - v->max_speed = avi->max_speed; - v->acceleration = avi->acceleration; - } - } - } - - if (CheckSavegameVersion(49)) FOR_ALL_COMPANIES(c) c->face = ConvertFromOldCompanyManagerFace(c->face); - - if (CheckSavegameVersion(52)) { - for (TileIndex t = 0; t < map_size; t++) { - if (IsStatueTile(t)) { - _m[t].m2 = CalcClosestTownFromTile(t, UINT_MAX)->index; - } - } - } - - /* A patch option containing the proportion of towns that grow twice as - * fast was added in version 54. From version 56 this is now saved in the - * town as cities can be built specifically in the scenario editor. */ - if (CheckSavegameVersion(56)) { - Town *t; - - FOR_ALL_TOWNS(t) { - if (_settings_game.economy.larger_towns != 0 && (t->index % _settings_game.economy.larger_towns) == 0) { - t->larger_town = true; - } - } - } - - if (CheckSavegameVersion(57)) { - Vehicle *v; - /* Added a FIFO queue of vehicles loading at stations */ - FOR_ALL_VEHICLES(v) { - if ((v->type != VEH_TRAIN || IsFrontEngine(v)) && // for all locs - !(v->vehstatus & (VS_STOPPED | VS_CRASHED)) && // not stopped or crashed - v->current_order.IsType(OT_LOADING)) { // loading - GetStation(v->last_station_visited)->loading_vehicles.push_back(v); - - /* The loading finished flag is *only* set when actually completely - * finished. Because the vehicle is loading, it is not finished. */ - ClrBit(v->vehicle_flags, VF_LOADING_FINISHED); - } - } - } else if (CheckSavegameVersion(59)) { - /* For some reason non-loading vehicles could be in the station's loading vehicle list */ - - Station *st; - FOR_ALL_STATIONS(st) { - std::list::iterator iter; - for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end();) { - Vehicle *v = *iter; - iter++; - if (!v->current_order.IsType(OT_LOADING)) st->loading_vehicles.remove(v); - } - } - } - - if (CheckSavegameVersion(58)) { - /* patch difficulty number_industries other than zero get bumped to +1 - * since a new option (very low at position1) has been added */ - if (_settings_game.difficulty.number_industries > 0) { - _settings_game.difficulty.number_industries++; - } - - /* Same goes for number of towns, although no test is needed, just an increment */ - _settings_game.difficulty.number_towns++; - } - - if (CheckSavegameVersion(64)) { - /* copy the signal type/variant and move signal states bits */ - for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_RAILWAY) && HasSignals(t)) { - SetSignalStates(t, GB(_m[t].m2, 4, 4)); - SetSignalVariant(t, INVALID_TRACK, GetSignalVariant(t, TRACK_X)); - SetSignalType(t, INVALID_TRACK, GetSignalType(t, TRACK_X)); - ClrBit(_m[t].m2, 7); - } - } - } - - if (CheckSavegameVersion(69)) { - /* In some old savegames a bit was cleared when it should not be cleared */ - Vehicle *v; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_ROAD && (v->u.road.state == 250 || v->u.road.state == 251)) { - SetBit(v->u.road.state, RVS_IS_STOPPING); - } - } - } - - if (CheckSavegameVersion(70)) { - /* Added variables to support newindustries */ - Industry *i; - FOR_ALL_INDUSTRIES(i) i->founder = OWNER_NONE; - } - - /* From version 82, old style canals (above sealevel (0), WATER owner) are no longer supported. - Replace the owner for those by OWNER_NONE. */ - if (CheckSavegameVersion(82)) { - for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_WATER) && - GetWaterTileType(t) == WATER_TILE_CLEAR && - GetTileOwner(t) == OWNER_WATER && - TileHeight(t) != 0) { - SetTileOwner(t, OWNER_NONE); - } - } - } - - /* - * Add the 'previous' owner to the ship depots so we can reset it with - * the correct values when it gets destroyed. This prevents that - * someone can remove canals owned by somebody else and it prevents - * making floods using the removal of ship depots. - */ - if (CheckSavegameVersion(83)) { - for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_WATER) && IsShipDepot(t)) { - _m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; - } - } - } - - if (CheckSavegameVersion(74)) { - Station *st; - FOR_ALL_STATIONS(st) { - for (CargoID c = 0; c < NUM_CARGO; c++) { - st->goods[c].last_speed = 0; - if (st->goods[c].cargo.Count() != 0) SetBit(st->goods[c].acceptance_pickup, GoodsEntry::PICKUP); - } - } - } - - if (CheckSavegameVersion(78)) { - Industry *i; - uint j; - FOR_ALL_INDUSTRIES(i) { - const IndustrySpec *indsp = GetIndustrySpec(i->type); - for (j = 0; j < lengthof(i->produced_cargo); j++) { - i->produced_cargo[j] = indsp->produced_cargo[j]; - } - for (j = 0; j < lengthof(i->accepts_cargo); j++) { - i->accepts_cargo[j] = indsp->accepts_cargo[j]; - } - } - } - - /* Before version 81, the density of grass was always stored as zero, and - * grassy trees were always drawn fully grassy. Furthermore, trees on rough - * land used to have zero density, now they have full density. Therefore, - * make all grassy/rough land trees have a density of 3. */ - if (CheckSavegameVersion(81)) { - for (TileIndex t = 0; t < map_size; t++) { - if (GetTileType(t) == MP_TREES) { - TreeGround groundType = GetTreeGround(t); - if (groundType != TREE_GROUND_SNOW_DESERT) SetTreeGroundDensity(t, groundType, 3); - } - } - } - - - if (CheckSavegameVersion(93)) { - /* Rework of orders. */ - Order *order; - FOR_ALL_ORDERS(order) order->ConvertFromOldSavegame(); - - Vehicle *v; - FOR_ALL_VEHICLES(v) { - if (v->orders.list != NULL && v->orders.list->GetFirstOrder() != NULL && !v->orders.list->GetFirstOrder()->IsValid()) { - v->orders.list->FreeChain(); - v->orders.list = NULL; - } - - v->current_order.ConvertFromOldSavegame(); - if (v->type == VEH_ROAD && v->IsPrimaryVehicle() && v->FirstShared() == v) { - FOR_VEHICLE_ORDERS(v, order) order->SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS); - } - } - } else if (CheckSavegameVersion(94)) { - /* Unload and transfer are now mutual exclusive. */ - Order *order; - FOR_ALL_ORDERS(order) { - if ((order->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) { - order->SetUnloadType(OUFB_TRANSFER); - order->SetLoadType(OLFB_NO_LOAD); - } - } - - Vehicle *v; - FOR_ALL_VEHICLES(v) { - if ((v->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) { - v->current_order.SetUnloadType(OUFB_TRANSFER); - v->current_order.SetLoadType(OLFB_NO_LOAD); - } - } - } - - if (CheckSavegameVersion(84)) { - /* Update go to buoy orders because they are just waypoints */ - Order *order; - FOR_ALL_ORDERS(order) { - if (order->IsType(OT_GOTO_STATION) && GetStation(order->GetDestination())->IsBuoy()) { - order->SetLoadType(OLF_LOAD_IF_POSSIBLE); - order->SetUnloadType(OUF_UNLOAD_IF_POSSIBLE); - } - } - - /* Set all share owners to INVALID_COMPANY for - * 1) all inactive companies - * (when inactive companies were stored in the savegame - TTD, TTDP and some - * *really* old revisions of OTTD; else it is already set in InitializeCompanies()) - * 2) shares that are owned by inactive companies or self - * (caused by cheating clients in earlier revisions) */ - FOR_ALL_COMPANIES(c) { - for (uint i = 0; i < 4; i++) { - CompanyID company = c->share_owners[i]; - if (company == INVALID_COMPANY) continue; - if (!IsValidCompanyID(company) || company == c->index) c->share_owners[i] = INVALID_COMPANY; - } - } - } - - if (CheckSavegameVersion(86)) { - for (TileIndex t = 0; t < map_size; t++) { - /* Move river flag and update canals to use water class */ - if (IsTileType(t, MP_WATER)) { - if (GetWaterClass(t) != WATER_CLASS_RIVER) { - if (IsWater(t)) { - Owner o = GetTileOwner(t); - if (o == OWNER_WATER) { - MakeWater(t); - } else { - MakeCanal(t, o, Random()); - } - } else if (IsShipDepot(t)) { - Owner o = (Owner)_m[t].m4; // Original water owner - SetWaterClass(t, o == OWNER_WATER ? WATER_CLASS_SEA : WATER_CLASS_CANAL); - } - } - } - } - - /* Update locks, depots, docks and buoys to have a water class based - * on its neighbouring tiles. Done after river and canal updates to - * ensure neighbours are correct. */ - for (TileIndex t = 0; t < map_size; t++) { - if (GetTileSlope(t, NULL) != SLOPE_FLAT) continue; - - if (IsTileType(t, MP_WATER) && IsLock(t)) SetWaterClassDependingOnSurroundings(t, false); - if (IsTileType(t, MP_STATION) && (IsDock(t) || IsBuoy(t))) SetWaterClassDependingOnSurroundings(t, false); - } - } - - if (CheckSavegameVersion(87)) { - for (TileIndex t = 0; t < map_size; t++) { - /* skip oil rigs at borders! */ - if ((IsTileType(t, MP_WATER) || IsBuoyTile(t)) && - (TileX(t) == 0 || TileY(t) == 0 || TileX(t) == MapMaxX() - 1 || TileY(t) == MapMaxY() - 1)) { - /* Some version 86 savegames have wrong water class at map borders (under buoy, or after removing buoy). - * This conversion has to be done before buoys with invalid owner are removed. */ - SetWaterClass(t, WATER_CLASS_SEA); - } - - if (IsBuoyTile(t) || IsDriveThroughStopTile(t) || IsTileType(t, MP_WATER)) { - Owner o = GetTileOwner(t); - if (o < MAX_COMPANIES && !IsValidCompanyID(o)) { - _current_company = o; - ChangeTileOwner(t, o, INVALID_OWNER); - } - if (IsBuoyTile(t)) { - /* reset buoy owner to OWNER_NONE in the station struct - * (even if it is owned by active company) */ - GetStationByTile(t)->owner = OWNER_NONE; - } - } else if (IsTileType(t, MP_ROAD)) { - /* works for all RoadTileType */ - for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) { - /* update even non-existing road types to update tile owner too */ - Owner o = GetRoadOwner(t, rt); - if (o < MAX_COMPANIES && !IsValidCompanyID(o)) SetRoadOwner(t, rt, OWNER_NONE); - } - if (IsLevelCrossing(t)) { - Owner o = GetTileOwner(t); - if (!IsValidCompanyID(o)) { - /* remove leftover rail piece from crossing (from very old savegames) */ - _current_company = o; - DoCommand(t, 0, GetCrossingRailTrack(t), DC_EXEC | DC_BANKRUPT, CMD_REMOVE_SINGLE_RAIL); - } - } - } - } - - /* Convert old PF settings to new */ - if (_settings_game.pf.yapf.rail_use_yapf || CheckSavegameVersion(28)) { - _settings_game.pf.pathfinder_for_trains = VPF_YAPF; - } else { - _settings_game.pf.pathfinder_for_trains = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_NTP); - } - - if (_settings_game.pf.yapf.road_use_yapf || CheckSavegameVersion(28)) { - _settings_game.pf.pathfinder_for_roadvehs = VPF_YAPF; - } else { - _settings_game.pf.pathfinder_for_roadvehs = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_OPF); - } - - if (_settings_game.pf.yapf.ship_use_yapf) { - _settings_game.pf.pathfinder_for_ships = VPF_YAPF; - } else { - _settings_game.pf.pathfinder_for_ships = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_OPF); - } - } - - if (CheckSavegameVersion(88)) { - /* Profits are now with 8 bit fract */ - Vehicle *v; - FOR_ALL_VEHICLES(v) { - v->profit_this_year <<= 8; - v->profit_last_year <<= 8; - v->running_ticks = 0; - } - } - - if (CheckSavegameVersion(91)) { - /* Increase HouseAnimationFrame from 5 to 7 bits */ - for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_HOUSE) && GetHouseType(t) >= NEW_HOUSE_OFFSET) { - SetHouseAnimationFrame(t, GB(_m[t].m6, 3, 5)); - } - } - } - - if (CheckSavegameVersion(62)) { - /* Remove all trams from savegames without tram support. - * There would be trams without tram track under causing crashes sooner or later. */ - Vehicle *v; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_ROAD && v->First() == v && - HasBit(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM)) { - if (_switch_mode_errorstr == INVALID_STRING_ID || _switch_mode_errorstr == STR_NEWGRF_COMPATIBLE_LOAD_WARNING) { - _switch_mode_errorstr = STR_LOADGAME_REMOVED_TRAMS; - } - delete v; - } - } - } - - if (CheckSavegameVersion(99)) { - for (TileIndex t = 0; t < map_size; t++) { - /* Set newly introduced WaterClass of industry tiles */ - if (IsTileType(t, MP_STATION) && IsOilRig(t)) { - SetWaterClassDependingOnSurroundings(t, true); - } - if (IsTileType(t, MP_INDUSTRY)) { - if ((GetIndustrySpec(GetIndustryType(t))->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0) { - SetWaterClassDependingOnSurroundings(t, true); - } else { - SetWaterClass(t, WATER_CLASS_INVALID); - } - } - - /* Replace "house construction year" with "house age" */ - if (IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)) { - _m[t].m5 = Clamp(_cur_year - (_m[t].m5 + ORIGINAL_BASE_YEAR), 0, 0xFF); - } - } - } - - /* Move the signal variant back up one bit for PBS. We don't convert the old PBS - * format here, as an old layout wouldn't work properly anyway. To be safe, we - * clear any possible PBS reservations as well. */ - if (CheckSavegameVersion(100)) { - for (TileIndex t = 0; t < map_size; t++) { - switch (GetTileType(t)) { - case MP_RAILWAY: - if (HasSignals(t)) { - /* move the signal variant */ - SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); - SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); - ClrBit(_m[t].m2, 2); - ClrBit(_m[t].m2, 6); - } - - /* Clear PBS reservation on track */ - if (IsRailDepot(t) ||IsRailWaypoint(t)) { - SetDepotWaypointReservation(t, false); - } else { - SetTrackReservation(t, TRACK_BIT_NONE); - } - break; - - case MP_ROAD: /* Clear PBS reservation on crossing */ - if (IsLevelCrossing(t)) SetCrossingReservation(t, false); - break; - - case MP_STATION: /* Clear PBS reservation on station */ - if (IsRailwayStation(t)) SetRailwayStationReservation(t, false); - break; - - case MP_TUNNELBRIDGE: /* Clear PBS reservation on tunnels/birdges */ - if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) SetTunnelBridgeReservation(t, false); - break; - - default: break; - } - } - } - - /* Reserve all tracks trains are currently on. */ - if (CheckSavegameVersion(101)) { - Vehicle *v; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN) { - if ((v->u.rail.track & TRACK_BIT_WORMHOLE) == TRACK_BIT_WORMHOLE) { - TryReserveRailTrack(v->tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(v->tile))); - } else if ((v->u.rail.track & TRACK_BIT_MASK) != TRACK_BIT_NONE) { - TryReserveRailTrack(v->tile, TrackBitsToTrack(v->u.rail.track)); - } - } - } - - /* Give owners to waypoints, based on rail tracks it is sitting on. - * If none is available, specify OWNER_NONE */ - Waypoint *wp; - FOR_ALL_WAYPOINTS(wp) { - Owner owner = (IsRailWaypointTile(wp->xy) ? GetTileOwner(wp->xy) : OWNER_NONE); - wp->owner = IsValidCompanyID(owner) ? owner : OWNER_NONE; - } - } - - if (CheckSavegameVersion(102)) { - for (TileIndex t = 0; t < map_size; t++) { - /* Now all crossings should be in correct state */ - if (IsLevelCrossingTile(t)) UpdateLevelCrossing(t, false); - } - } - - if (CheckSavegameVersion(103)) { - /* Non-town-owned roads now store the closest town */ - UpdateNearestTownForRoadTiles(false); - - /* signs with invalid owner left from older savegames */ - Sign *si; - FOR_ALL_SIGNS(si) { - if (si->owner != OWNER_NONE && !IsValidCompanyID(si->owner)) si->owner = OWNER_NONE; - } - - /* Station can get named based on an industry type, but the current ones - * are not, so mark them as if they are not named by an industry. */ - Station *st; - FOR_ALL_STATIONS(st) { - st->indtype = IT_INVALID; - } - } - - if (CheckSavegameVersion(104)) { - Vehicle *v; - FOR_ALL_VEHICLES(v) { - /* Set engine_type of shadow and rotor */ - if (v->type == VEH_AIRCRAFT && !IsNormalAircraft(v)) { - v->engine_type = v->First()->engine_type; - } - } - - /* More companies ... */ - Company *c; - FOR_ALL_COMPANIES(c) { - if (c->bankrupt_asked == 0xFF) c->bankrupt_asked = 0xFFFF; - } - - Engine *e; - FOR_ALL_ENGINES(e) { - if (e->company_avail == 0xFF) e->company_avail = 0xFFFF; - } - - Town *t; - FOR_ALL_TOWNS(t) { - if (t->have_ratings == 0xFF) t->have_ratings = 0xFFFF; - for (uint i = 8; i != MAX_COMPANIES; i++) t->ratings[i] = RATING_INITIAL; - } - } - - GamelogPrintDebug(1); - - bool ret = InitializeWindowsAndCaches(); - /* Restore the signals */ - signal(SIGSEGV, prev_segfault); - signal(SIGABRT, prev_abort); - return ret; -} - -/** Reload all NewGRF files during a running game. This is a cut-down - * version of AfterLoadGame(). - * XXX - We need to reset the vehicle position hash because with a non-empty - * hash AfterLoadVehicles() will loop infinitely. We need AfterLoadVehicles() - * to recalculate vehicle data as some NewGRF vehicle sets could have been - * removed or added and changed statistics */ -void ReloadNewGRFData() -{ - /* reload grf data */ - GfxLoadSprites(); - LoadStringWidthTable(); - ResetEconomy(); - /* reload vehicles */ - ResetVehiclePosHash(); - AfterLoadVehicles(false); - StartupEngines(); - SetCachedEngineCounts(); - /* update station and waypoint graphics */ - AfterLoadWaypoints(); - AfterLoadStations(); - /* Check and update house and town values */ - UpdateHousesAndTowns(); - /* Update livery selection windows */ - for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) InvalidateWindowData(WC_COMPANY_COLOR, i, _loaded_newgrf_features.has_2CC); - /* redraw the whole screen */ - MarkWholeScreenDirty(); - CheckTrainsLengths(); -} diff --git a/src/order_base.h b/src/order_base.h --- a/src/order_base.h +++ b/src/order_base.h @@ -431,7 +431,4 @@ static inline bool IsValidOrderListID(ui #define FOR_ALL_ORDER_LISTS_FROM(ol, start) for (ol = GetOrderList(start); ol != NULL; ol = (ol->index + 1U < GetOrderListPoolSize()) ? GetOrderList(ol->index + 1U) : NULL) if (ol->IsValid()) #define FOR_ALL_ORDER_LISTS(ol) FOR_ALL_ORDER_LISTS_FROM(ol, 0) -/* (Un)pack routines */ -Order UnpackOldOrder(uint16 packed); - #endif /* ORDER_H */ diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -13,7 +13,6 @@ #include "command_func.h" #include "company_func.h" #include "news_func.h" -#include "saveload.h" #include "vehicle_gui.h" #include "cargotype.h" #include "aircraft.h" @@ -147,82 +146,6 @@ Order::Order(uint32 packed) this->travel_time = 0; } -void Order::ConvertFromOldSavegame() -{ - uint8 old_flags = this->flags; - this->flags = 0; - - /* First handle non-stop */ - if (_settings_client.gui.sg_new_nonstop) { - /* OFB_NON_STOP */ - this->SetNonStopType((old_flags & 8) ? ONSF_NO_STOP_AT_ANY_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS); - } else { - this->SetNonStopType((old_flags & 8) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE); - } - - switch (this->GetType()) { - /* Only a few types need the other savegame conversions. */ - case OT_GOTO_DEPOT: case OT_GOTO_STATION: case OT_LOADING: break; - default: return; - } - - if (this->GetType() != OT_GOTO_DEPOT) { - /* Then the load flags */ - if ((old_flags & 2) != 0) { // OFB_UNLOAD - this->SetLoadType(OLFB_NO_LOAD); - } else if ((old_flags & 4) == 0) { // !OFB_FULL_LOAD - this->SetLoadType(OLF_LOAD_IF_POSSIBLE); - } else { - this->SetLoadType(_settings_client.gui.sg_full_load_any ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD); - } - - /* Finally fix the unload flags */ - if ((old_flags & 1) != 0) { // OFB_TRANSFER - this->SetUnloadType(OUFB_TRANSFER); - } else if ((old_flags & 2) != 0) { // OFB_UNLOAD - this->SetUnloadType(OUFB_UNLOAD); - } else { - this->SetUnloadType(OUF_UNLOAD_IF_POSSIBLE); - } - } else { - /* Then the depot action flags */ - this->SetDepotActionType(((old_flags & 6) == 4) ? ODATFB_HALT : ODATF_SERVICE_ONLY); - - /* Finally fix the depot type flags */ - uint t = ((old_flags & 6) == 6) ? ODTFB_SERVICE : ODTF_MANUAL; - if ((old_flags & 2) != 0) t |= ODTFB_PART_OF_ORDERS; - this->SetDepotOrderType((OrderDepotTypeFlags)t); - } -} - -/** - * - * Unpacks a order from savegames with version 4 and lower - * - */ -static Order UnpackVersion4Order(uint16 packed) -{ - return Order(GB(packed, 8, 8) << 16 | GB(packed, 4, 4) << 8 | GB(packed, 0, 4)); -} - -/** - * - * Unpacks a order from savegames made with TTD(Patch) - * - */ -Order UnpackOldOrder(uint16 packed) -{ - Order order = UnpackVersion4Order(packed); - - /* - * Sanity check - * TTD stores invalid orders as OT_NOTHING with non-zero flags/station - */ - if (!order.IsValid() && packed != 0) order.MakeDummy(); - - return order; -} - /** * * Updates the widgets of a vehicle which contains the order-data @@ -1898,118 +1821,3 @@ void InitializeOrders() _backup_orders_tile = 0; } - -const SaveLoad *GetOrderDescription() { -static const SaveLoad _order_desc[] = { - SLE_VAR(Order, type, SLE_UINT8), - SLE_VAR(Order, flags, SLE_UINT8), - SLE_VAR(Order, dest, SLE_UINT16), - SLE_REF(Order, next, REF_ORDER), - SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, 36, SL_MAX_VERSION), - SLE_CONDVAR(Order, refit_subtype, SLE_UINT8, 36, SL_MAX_VERSION), - SLE_CONDVAR(Order, wait_time, SLE_UINT16, 67, SL_MAX_VERSION), - SLE_CONDVAR(Order, travel_time, SLE_UINT16, 67, SL_MAX_VERSION), - - /* Leftover from the minor savegame version stuff - * We will never use those free bytes, but we have to keep this line to allow loading of old savegames */ - SLE_CONDNULL(10, 5, 35), - SLE_END() -}; - return _order_desc; -} - -static void Save_ORDR() -{ - Order *order; - - FOR_ALL_ORDERS(order) { - SlSetArrayIndex(order->index); - SlObject(order, GetOrderDescription()); - } -} - -static void Load_ORDR() -{ - if (CheckSavegameVersionOldStyle(5, 2)) { - /* Version older than 5.2 did not have a ->next pointer. Convert them - (in the old days, the orderlist was 5000 items big) */ - size_t len = SlGetFieldLength(); - uint i; - - if (CheckSavegameVersion(5)) { - /* Pre-version 5 had an other layout for orders - (uint16 instead of uint32) */ - len /= sizeof(uint16); - uint16 *orders = MallocT(len + 1); - - SlArray(orders, len, SLE_UINT16); - - for (i = 0; i < len; ++i) { - Order *order = new (i) Order(); - order->AssignOrder(UnpackVersion4Order(orders[i])); - } - - free(orders); - } else if (CheckSavegameVersionOldStyle(5, 2)) { - len /= sizeof(uint16); - uint16 *orders = MallocT(len + 1); - - SlArray(orders, len, SLE_UINT32); - - for (i = 0; i < len; ++i) { - new (i) Order(orders[i]); - } - - free(orders); - } - - /* Update all the next pointer */ - for (i = 1; i < len; ++i) { - /* The orders were built like this: - * While the order is valid, set the previous will get it's next pointer set - * We start with index 1 because no order will have the first in it's next pointer */ - if (GetOrder(i)->IsValid()) - GetOrder(i - 1)->next = GetOrder(i); - } - } else { - int index; - - while ((index = SlIterateArray()) != -1) { - Order *order = new (index) Order(); - SlObject(order, GetOrderDescription()); - } - } -} - -const SaveLoad *GetOrderListDescription() { -static const SaveLoad _orderlist_desc[] = { - SLE_REF(OrderList, first, REF_ORDER), - SLE_END() -}; - return _orderlist_desc; -} - -static void Save_ORDL() -{ - OrderList *list; - - FOR_ALL_ORDER_LISTS(list) { - SlSetArrayIndex(list->index); - SlObject(list, GetOrderListDescription()); - } -} - -static void Load_ORDL() -{ - int index; - - while ((index = SlIterateArray()) != -1) { - OrderList *list = new (index) OrderList(); - SlObject(list, GetOrderListDescription()); - } -} - -extern const ChunkHandler _order_chunk_handlers[] = { - { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY}, - { 'ORDL', Save_ORDL, Load_ORDL, CH_ARRAY | CH_LAST}, -}; diff --git a/src/saveload.cpp b/src/saveload.cpp deleted file mode 100644 --- a/src/saveload.cpp +++ /dev/null @@ -1,1893 +0,0 @@ -/* $Id$ */ - -/** @file saveload.cpp - * All actions handling saving and loading goes on in this file. The general actions - * are as follows for saving a game (loading is analogous): - *
    - *
  1. initialize the writer by creating a temporary memory-buffer for it - *
  2. go through all to-be saved elements, each 'chunk' (ChunkHandler) prefixed by a label - *
  3. use their description array (SaveLoad) to know what elements to save and in what version - * of the game it was active (used when loading) - *
  4. write all data byte-by-byte to the temporary buffer so it is endian-safe - *
  5. when the buffer is full; flush it to the output (eg save to file) (_sl.buf, _sl.bufp, _sl.bufe) - *
  6. repeat this until everything is done, and flush any remaining output to file - *
- */ -#include "stdafx.h" -#include "openttd.h" -#include "debug.h" -#include "station_base.h" -#include "thread.h" -#include "town.h" -#include "saveload.h" -#include "network/network.h" -#include "variables.h" -#include "window_func.h" -#include "strings_func.h" -#include "gfx_func.h" -#include "core/alloc_func.hpp" -#include "functions.h" -#include "core/endian_func.hpp" -#include "vehicle_base.h" -#include "company_func.h" -#include "date_func.h" -#include "autoreplace_base.h" -#include "statusbar_gui.h" -#include "fileio_func.h" -#include -#include "gamelog.h" - -#include "table/strings.h" - -extern const uint16 SAVEGAME_VERSION = 105; - -SavegameType _savegame_type; ///< type of savegame we are loading - -uint32 _ttdp_version; ///< version of TTDP savegame (if applicable) -uint16 _sl_version; ///< the major savegame version identifier -byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! - -typedef void WriterProc(size_t len); -typedef size_t ReaderProc(); - -/** The saveload struct, containing reader-writer functions, bufffer, version, etc. */ -static struct { - bool save; ///< are we doing a save or a load atm. True when saving - byte need_length; ///< ??? - byte block_mode; ///< ??? - bool error; ///< did an error occur or not - - size_t obj_len; ///< the length of the current object we are busy with - int array_index, last_array_index; ///< in the case of an array, the current and last positions - - size_t offs_base; ///< the offset in number of bytes since we started writing data (eg uncompressed savegame size) - - WriterProc *write_bytes; ///< savegame writer function - ReaderProc *read_bytes; ///< savegame loader function - - const ChunkHandler* const *chs; ///< the chunk of data that is being processed atm (vehicles, signs, etc.) - - /* When saving/loading savegames, they are always saved to a temporary memory-place - * to be flushed to file (save) or to final place (load) when full. */ - byte *bufp, *bufe; ///< bufp(ointer) gives the current position in the buffer bufe(nd) gives the end of the buffer - - /* these 3 may be used by compressor/decompressors. */ - byte *buf; ///< pointer to temporary memory to read/write, initialized by SaveLoadFormat->initread/write - byte *buf_ori; ///< pointer to the original memory location of buf, used to free it afterwards - uint bufsize; ///< the size of the temporary memory *buf - FILE *fh; ///< the file from which is read or written to - - void (*excpt_uninit)(); ///< the function to execute on any encountered error - StringID error_str; ///< the translateable error message to show - char *extra_msg; ///< the error message -} _sl; - - -enum NeedLengthValues {NL_NONE = 0, NL_WANTLENGTH = 1, NL_CALCLENGTH = 2}; - -/** Error handler, calls longjmp to simulate an exception. - * @todo this was used to have a central place to handle errors, but it is - * pretty ugly, and seriously interferes with any multithreaded approaches */ -static void NORETURN SlError(StringID string, const char *extra_msg = NULL) -{ - _sl.error_str = string; - free(_sl.extra_msg); - _sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg); - throw std::exception(); -} - -typedef void (*AsyncSaveFinishProc)(); -static AsyncSaveFinishProc _async_save_finish = NULL; -static ThreadObject *_save_thread; - -/** - * Called by save thread to tell we finished saving. - */ -static void SetAsyncSaveFinish(AsyncSaveFinishProc proc) -{ - if (_exit_game) return; - while (_async_save_finish != NULL) CSleep(10); - - _async_save_finish = proc; -} - -/** - * Handle async save finishes. - */ -void ProcessAsyncSaveFinish() -{ - if (_async_save_finish == NULL) return; - - _async_save_finish(); - - _async_save_finish = NULL; - - if (_save_thread != NULL) { - _save_thread->Join(); - delete _save_thread; - _save_thread = NULL; - } -} - -/** - * Fill the input buffer by reading from the file with the given reader - */ -static void SlReadFill() -{ - size_t len = _sl.read_bytes(); - if (len == 0) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected end of chunk"); - - _sl.bufp = _sl.buf; - _sl.bufe = _sl.buf + len; - _sl.offs_base += len; -} - -static inline size_t SlGetOffs() {return _sl.offs_base - (_sl.bufe - _sl.bufp);} - -/** Return the size in bytes of a certain type of normal/atomic variable - * as it appears in memory. See VarTypes - * @param conv VarType type of variable that is used for calculating the size - * @return Return the size of this type in bytes */ -static inline byte SlCalcConvMemLen(VarType conv) -{ - static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0}; - byte length = GB(conv, 4, 4); - assert(length < lengthof(conv_mem_size)); - return conv_mem_size[length]; -} - -/** Return the size in bytes of a certain type of normal/atomic variable - * as it appears in a saved game. See VarTypes - * @param conv VarType type of variable that is used for calculating the size - * @return Return the size of this type in bytes */ -static inline byte SlCalcConvFileLen(VarType conv) -{ - static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2}; - byte length = GB(conv, 0, 4); - assert(length < lengthof(conv_file_size)); - return conv_file_size[length]; -} - -/** Return the size in bytes of a reference (pointer) */ -static inline size_t SlCalcRefLen() {return CheckSavegameVersion(69) ? 2 : 4;} - -/** Flush the output buffer by writing to disk with the given reader. - * If the buffer pointer has not yet been set up, set it up now. Usually - * only called when the buffer is full, or there is no more data to be processed - */ -static void SlWriteFill() -{ - /* flush the buffer to disk (the writer) */ - if (_sl.bufp != NULL) { - uint len = _sl.bufp - _sl.buf; - _sl.offs_base += len; - if (len) _sl.write_bytes(len); - } - - /* All the data from the buffer has been written away, rewind to the beginning - * to start reading in more data */ - _sl.bufp = _sl.buf; - _sl.bufe = _sl.buf + _sl.bufsize; -} - -/** Read in a single byte from file. If the temporary buffer is full, - * flush it to its final destination - * @return return the read byte from file - */ -static inline byte SlReadByteInternal() -{ - if (_sl.bufp == _sl.bufe) SlReadFill(); - return *_sl.bufp++; -} - -/** Wrapper for SlReadByteInternal */ -byte SlReadByte() {return SlReadByteInternal();} - -/** Write away a single byte from memory. If the temporary buffer is full, - * flush it to its destination (file) - * @param b the byte that is currently written - */ -static inline void SlWriteByteInternal(byte b) -{ - if (_sl.bufp == _sl.bufe) SlWriteFill(); - *_sl.bufp++ = b; -} - -/** Wrapper for SlWriteByteInternal */ -void SlWriteByte(byte b) {SlWriteByteInternal(b);} - -static inline int SlReadUint16() -{ - int x = SlReadByte() << 8; - return x | SlReadByte(); -} - -static inline uint32 SlReadUint32() -{ - uint32 x = SlReadUint16() << 16; - return x | SlReadUint16(); -} - -static inline uint64 SlReadUint64() -{ - uint32 x = SlReadUint32(); - uint32 y = SlReadUint32(); - return (uint64)x << 32 | y; -} - -static inline void SlWriteUint16(uint16 v) -{ - SlWriteByte(GB(v, 8, 8)); - SlWriteByte(GB(v, 0, 8)); -} - -static inline void SlWriteUint32(uint32 v) -{ - SlWriteUint16(GB(v, 16, 16)); - SlWriteUint16(GB(v, 0, 16)); -} - -static inline void SlWriteUint64(uint64 x) -{ - SlWriteUint32((uint32)(x >> 32)); - SlWriteUint32((uint32)x); -} - -/** - * Read in the header descriptor of an object or an array. - * If the highest bit is set (7), then the index is bigger than 127 - * elements, so use the next byte to read in the real value. - * The actual value is then both bytes added with the first shifted - * 8 bits to the left, and dropping the highest bit (which only indicated a big index). - * x = ((x & 0x7F) << 8) + SlReadByte(); - * @return Return the value of the index - */ -static uint SlReadSimpleGamma() -{ - uint i = SlReadByte(); - if (HasBit(i, 7)) { - i &= ~0x80; - if (HasBit(i, 6)) { - i &= ~0x40; - if (HasBit(i, 5)) { - i &= ~0x20; - if (HasBit(i, 4)) - SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unsupported gamma"); - i = (i << 8) | SlReadByte(); - } - i = (i << 8) | SlReadByte(); - } - i = (i << 8) | SlReadByte(); - } - return i; -} - -/** - * Write the header descriptor of an object or an array. - * If the element is bigger than 127, use 2 bytes for saving - * and use the highest byte of the first written one as a notice - * that the length consists of 2 bytes, etc.. like this: - * 0xxxxxxx - * 10xxxxxx xxxxxxxx - * 110xxxxx xxxxxxxx xxxxxxxx - * 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx - * @param i Index being written - */ - -static void SlWriteSimpleGamma(size_t i) -{ - if (i >= (1 << 7)) { - if (i >= (1 << 14)) { - if (i >= (1 << 21)) { - assert(i < (1 << 28)); - SlWriteByte((byte)(0xE0 | (i >> 24))); - SlWriteByte((byte)(i >> 16)); - } else { - SlWriteByte((byte)(0xC0 | (i >> 16))); - } - SlWriteByte((byte)(i >> 8)); - } else { - SlWriteByte((byte)(0x80 | (i >> 8))); - } - } - SlWriteByte((byte)i); -} - -/** Return how many bytes used to encode a gamma value */ -static inline uint SlGetGammaLength(size_t i) -{ - return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)); -} - -static inline uint SlReadSparseIndex() {return SlReadSimpleGamma();} -static inline void SlWriteSparseIndex(uint index) {SlWriteSimpleGamma(index);} - -static inline uint SlReadArrayLength() {return SlReadSimpleGamma();} -static inline void SlWriteArrayLength(size_t length) {SlWriteSimpleGamma(length);} -static inline uint SlGetArrayLength(size_t length) {return SlGetGammaLength(length);} - -void SlSetArrayIndex(uint index) -{ - _sl.need_length = NL_WANTLENGTH; - _sl.array_index = index; -} - -static size_t _next_offs; - -/** - * Iterate through the elements of an array and read the whole thing - * @return The index of the object, or -1 if we have reached the end of current block - */ -int SlIterateArray() -{ - int index; - - /* After reading in the whole array inside the loop - * we must have read in all the data, so we must be at end of current block. */ - if (_next_offs != 0 && SlGetOffs() != _next_offs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size"); - - while (true) { - uint length = SlReadArrayLength(); - if (length == 0) { - _next_offs = 0; - return -1; - } - - _sl.obj_len = --length; - _next_offs = SlGetOffs() + length; - - switch (_sl.block_mode) { - case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break; - case CH_ARRAY: index = _sl.array_index++; break; - default: - DEBUG(sl, 0, "SlIterateArray error"); - return -1; // error - } - - if (length != 0) return index; - } -} - -/** - * Sets the length of either a RIFF object or the number of items in an array. - * This lets us load an object or an array of arbitrary size - * @param length The length of the sought object/array - */ -void SlSetLength(size_t length) -{ - assert(_sl.save); - - switch (_sl.need_length) { - case NL_WANTLENGTH: - _sl.need_length = NL_NONE; - switch (_sl.block_mode) { - case CH_RIFF: - /* Ugly encoding of >16M RIFF chunks - * The lower 24 bits are normal - * The uppermost 4 bits are bits 24:27 */ - assert(length < (1 << 28)); - SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28))); - break; - case CH_ARRAY: - assert(_sl.last_array_index <= _sl.array_index); - while (++_sl.last_array_index <= _sl.array_index) - SlWriteArrayLength(1); - SlWriteArrayLength(length + 1); - break; - case CH_SPARSE_ARRAY: - SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index. - SlWriteSparseIndex(_sl.array_index); - break; - default: NOT_REACHED(); - } break; - case NL_CALCLENGTH: - _sl.obj_len += (int)length; - break; - } -} - -/** - * Save/Load bytes. These do not need to be converted to Little/Big Endian - * so directly write them or read them to/from file - * @param ptr The source or destination of the object being manipulated - * @param length number of bytes this fast CopyBytes lasts - */ -static void SlCopyBytes(void *ptr, size_t length) -{ - byte *p = (byte*)ptr; - - if (_sl.save) { - for (; length != 0; length--) {SlWriteByteInternal(*p++);} - } else { - for (; length != 0; length--) {*p++ = SlReadByteInternal();} - } -} - -/** Read in bytes from the file/data structure but don't do - * anything with them, discarding them in effect - * @param length The amount of bytes that is being treated this way - */ -static inline void SlSkipBytes(size_t length) -{ - for (; length != 0; length--) SlReadByte(); -} - -/* Get the length of the current object */ -size_t SlGetFieldLength() {return _sl.obj_len;} - -/** Return a signed-long version of the value of a setting - * @param ptr pointer to the variable - * @param conv type of variable, can be a non-clean - * type, eg one with other flags because it is parsed - * @return returns the value of the pointer-setting */ -int64 ReadValue(const void *ptr, VarType conv) -{ - switch (GetVarMemType(conv)) { - case SLE_VAR_BL: return (*(bool*)ptr != 0); - case SLE_VAR_I8: return *(int8* )ptr; - case SLE_VAR_U8: return *(byte* )ptr; - case SLE_VAR_I16: return *(int16* )ptr; - case SLE_VAR_U16: return *(uint16*)ptr; - case SLE_VAR_I32: return *(int32* )ptr; - case SLE_VAR_U32: return *(uint32*)ptr; - case SLE_VAR_I64: return *(int64* )ptr; - case SLE_VAR_U64: return *(uint64*)ptr; - case SLE_VAR_NULL:return 0; - default: NOT_REACHED(); - } - - /* useless, but avoids compiler warning this way */ - return 0; -} - -/** Write the value of a setting - * @param ptr pointer to the variable - * @param conv type of variable, can be a non-clean type, eg - * with other flags. It is parsed upon read - * @param val the new value being given to the variable */ -void WriteValue(void *ptr, VarType conv, int64 val) -{ - switch (GetVarMemType(conv)) { - case SLE_VAR_BL: *(bool *)ptr = (val != 0); break; - case SLE_VAR_I8: *(int8 *)ptr = val; break; - case SLE_VAR_U8: *(byte *)ptr = val; break; - case SLE_VAR_I16: *(int16 *)ptr = val; break; - case SLE_VAR_U16: *(uint16*)ptr = val; break; - case SLE_VAR_I32: *(int32 *)ptr = val; break; - case SLE_VAR_U32: *(uint32*)ptr = val; break; - case SLE_VAR_I64: *(int64 *)ptr = val; break; - case SLE_VAR_U64: *(uint64*)ptr = val; break; - case SLE_VAR_NAME: *(char**)ptr = CopyFromOldName(val); break; - case SLE_VAR_NULL: break; - default: NOT_REACHED(); - } -} - -/** - * Handle all conversion and typechecking of variables here. - * In the case of saving, read in the actual value from the struct - * and then write them to file, endian safely. Loading a value - * goes exactly the opposite way - * @param ptr The object being filled/read - * @param conv VarType type of the current element of the struct - */ -static void SlSaveLoadConv(void *ptr, VarType conv) -{ - int64 x = 0; - - if (_sl.save) { // SAVE values - /* Read a value from the struct. These ARE endian safe. */ - x = ReadValue(ptr, conv); - - /* Write the value to the file and check if its value is in the desired range */ - switch (GetVarFileType(conv)) { - case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break; - case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break; - case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break; - case SLE_FILE_STRINGID: - case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break; - case SLE_FILE_I32: - case SLE_FILE_U32: SlWriteUint32((uint32)x);break; - case SLE_FILE_I64: - case SLE_FILE_U64: SlWriteUint64(x);break; - default: NOT_REACHED(); - } - } else { // LOAD values - - /* Read a value from the file */ - switch (GetVarFileType(conv)) { - case SLE_FILE_I8: x = (int8 )SlReadByte(); break; - case SLE_FILE_U8: x = (byte )SlReadByte(); break; - case SLE_FILE_I16: x = (int16 )SlReadUint16(); break; - case SLE_FILE_U16: x = (uint16)SlReadUint16(); break; - case SLE_FILE_I32: x = (int32 )SlReadUint32(); break; - case SLE_FILE_U32: x = (uint32)SlReadUint32(); break; - case SLE_FILE_I64: x = (int64 )SlReadUint64(); break; - case SLE_FILE_U64: x = (uint64)SlReadUint64(); break; - case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break; - default: NOT_REACHED(); - } - - /* Write The value to the struct. These ARE endian safe. */ - WriteValue(ptr, conv, x); - } -} - -/** Calculate the net length of a string. This is in almost all cases - * just strlen(), but if the string is not properly terminated, we'll - * resort to the maximum length of the buffer. - * @param ptr pointer to the stringbuffer - * @param length maximum length of the string (buffer). If -1 we don't care - * about a maximum length, but take string length as it is. - * @return return the net length of the string */ -static inline size_t SlCalcNetStringLen(const char *ptr, size_t length) -{ - if (ptr == NULL) return 0; - return min(strlen(ptr), length - 1); -} - -/** Calculate the gross length of the string that it - * will occupy in the savegame. This includes the real length, returned - * by SlCalcNetStringLen and the length that the index will occupy. - * @param ptr pointer to the stringbuffer - * @param length maximum length of the string (buffer size, etc.) - * @param conv type of data been used - * @return return the gross length of the string */ -static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv) -{ - size_t len; - const char *str; - - switch (GetVarMemType(conv)) { - default: NOT_REACHED(); - case SLE_VAR_STR: - case SLE_VAR_STRQ: - str = *(const char**)ptr; - len = SIZE_MAX; - break; - case SLE_VAR_STRB: - case SLE_VAR_STRBQ: - str = (const char*)ptr; - len = length; - break; - } - - len = SlCalcNetStringLen(str, len); - return len + SlGetArrayLength(len); // also include the length of the index -} - -/** - * Save/Load a string. - * @param ptr the string being manipulated - * @param length of the string (full length) - * @param conv must be SLE_FILE_STRING */ -static void SlString(void *ptr, size_t length, VarType conv) -{ - size_t len; - - if (_sl.save) { // SAVE string - switch (GetVarMemType(conv)) { - default: NOT_REACHED(); - case SLE_VAR_STRB: - case SLE_VAR_STRBQ: - len = SlCalcNetStringLen((char*)ptr, length); - break; - case SLE_VAR_STR: - case SLE_VAR_STRQ: - ptr = *(char**)ptr; - len = SlCalcNetStringLen((char*)ptr, SIZE_MAX); - break; - } - - SlWriteArrayLength(len); - SlCopyBytes(ptr, len); - } else { // LOAD string - len = SlReadArrayLength(); - - switch (GetVarMemType(conv)) { - default: NOT_REACHED(); - case SLE_VAR_STRB: - case SLE_VAR_STRBQ: - if (len >= length) { - DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating"); - SlCopyBytes(ptr, length); - SlSkipBytes(len - length); - len = length - 1; - } else { - SlCopyBytes(ptr, len); - } - break; - case SLE_VAR_STR: - case SLE_VAR_STRQ: // Malloc'd string, free previous incarnation, and allocate - free(*(char**)ptr); - if (len == 0) { - *(char**)ptr = NULL; - } else { - *(char**)ptr = MallocT(len + 1); // terminating '\0' - ptr = *(char**)ptr; - SlCopyBytes(ptr, len); - } - break; - } - - ((char*)ptr)[len] = '\0'; // properly terminate the string - } -} - -/** - * Return the size in bytes of a certain type of atomic array - * @param length The length of the array counted in elements - * @param conv VarType type of the variable that is used in calculating the size - */ -static inline size_t SlCalcArrayLen(size_t length, VarType conv) -{ - return SlCalcConvFileLen(conv) * length; -} - -/** - * Save/Load an array. - * @param array The array being manipulated - * @param length The length of the array in elements - * @param conv VarType type of the atomic array (int, byte, uint64, etc.) - */ -void SlArray(void *array, size_t length, VarType conv) -{ - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcArrayLen(length, conv)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; - } - - /* NOTICE - handle some buggy stuff, in really old versions everything was saved - * as a byte-type. So detect this, and adjust array size accordingly */ - if (!_sl.save && _sl_version == 0) { - /* all arrays except difficulty settings */ - if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID || - conv == SLE_INT32 || conv == SLE_UINT32) { - SlCopyBytes(array, length * SlCalcConvFileLen(conv)); - return; - } - /* used for conversion of Money 32bit->64bit */ - if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) { - for (uint i = 0; i < length; i++) { - ((int64*)array)[i] = (int32)BSWAP32(SlReadUint32()); - } - return; - } - } - - /* If the size of elements is 1 byte both in file and memory, no special - * conversion is needed, use specialized copy-copy function to speed up things */ - if (conv == SLE_INT8 || conv == SLE_UINT8) { - SlCopyBytes(array, length); - } else { - byte *a = (byte*)array; - byte mem_size = SlCalcConvMemLen(conv); - - for (; length != 0; length --) { - SlSaveLoadConv(a, conv); - a += mem_size; // get size - } - } -} - - -static uint ReferenceToInt(const void* obj, SLRefType rt); -static void* IntToReference(uint index, SLRefType rt); - - -/** - * Return the size in bytes of a list - * @param list The std::list to find the size of - */ -static inline size_t SlCalcListLen(const void *list) -{ - std::list *l = (std::list *) list; - - int type_size = CheckSavegameVersion(69) ? 2 : 4; - /* Each entry is saved as type_size bytes, plus type_size bytes are used for the length - * of the list */ - return l->size() * type_size + type_size; -} - - -/** - * Save/Load a list. - * @param list The list being manipulated - * @param conv SLRefType type of the list (Vehicle *, Station *, etc) - */ -void SlList(void *list, SLRefType conv) -{ - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcListLen(list)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; - } - - std::list *l = (std::list *) list; - - if (_sl.save) { - SlWriteUint32((uint32)l->size()); - - std::list::iterator iter; - for (iter = l->begin(); iter != l->end(); ++iter) { - void *ptr = *iter; - SlWriteUint32(ReferenceToInt(ptr, conv)); - } - } else { - uint length = CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(); - - /* Load each reference and push to the end of the list */ - for (uint i = 0; i < length; i++) { - void *ptr = IntToReference(CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(), conv); - l->push_back(ptr); - } - } -} - - -/** Are we going to save this object or not? */ -static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld) -{ - if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false; - if (sld->conv & SLF_SAVE_NO) return false; - - return true; -} - -/** Are we going to load this variable when loading a savegame or not? - * @note If the variable is skipped it is skipped in the savegame - * bytestream itself as well, so there is no need to skip it somewhere else */ -static inline bool SlSkipVariableOnLoad(const SaveLoad *sld) -{ - if ((sld->conv & SLF_NETWORK_NO) && !_sl.save && _networking && !_network_server) { - SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length); - return true; - } - - return false; -} - -/** - * Calculate the size of an object. - * @param object to be measured - * @param sld The SaveLoad description of the object so we know how to manipulate it - * @return size of given objetc - */ -size_t SlCalcObjLength(const void *object, const SaveLoad *sld) -{ - size_t length = 0; - - /* Need to determine the length and write a length tag. */ - for (; sld->cmd != SL_END; sld++) { - length += SlCalcObjMemberLength(object, sld); - } - return length; -} - -size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) -{ - assert(_sl.save); - - switch (sld->cmd) { - case SL_VAR: - case SL_REF: - case SL_ARR: - case SL_STR: - case SL_LST: - /* CONDITIONAL saveload types depend on the savegame version */ - if (!SlIsObjectValidInSavegame(sld)) break; - - switch (sld->cmd) { - case SL_VAR: return SlCalcConvFileLen(sld->conv); - case SL_REF: return SlCalcRefLen(); - case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv); - case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv); - case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld)); - default: NOT_REACHED(); - } - break; - case SL_WRITEBYTE: return 1; // a byte is logically of size 1 - case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END)); - default: NOT_REACHED(); - } - return 0; -} - - -bool SlObjectMember(void *ptr, const SaveLoad *sld) -{ - VarType conv = GB(sld->conv, 0, 8); - switch (sld->cmd) { - case SL_VAR: - case SL_REF: - case SL_ARR: - case SL_STR: - case SL_LST: - /* CONDITIONAL saveload types depend on the savegame version */ - if (!SlIsObjectValidInSavegame(sld)) return false; - if (SlSkipVariableOnLoad(sld)) return false; - - switch (sld->cmd) { - case SL_VAR: SlSaveLoadConv(ptr, conv); break; - case SL_REF: // Reference variable, translate - if (_sl.save) { - SlWriteUint32(ReferenceToInt(*(void**)ptr, (SLRefType)conv)); - } else { - *(void**)ptr = IntToReference(CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(), (SLRefType)conv); - } - break; - case SL_ARR: SlArray(ptr, sld->length, conv); break; - case SL_STR: SlString(ptr, sld->length, conv); break; - case SL_LST: SlList(ptr, (SLRefType)conv); break; - default: NOT_REACHED(); - } - break; - - /* SL_WRITEBYTE translates a value of a variable to another one upon - * saving or loading. - * XXX - variable renaming abuse - * game_value: the value of the variable ingame is abused by sld->version_from - * file_value: the value of the variable in the savegame is abused by sld->version_to */ - case SL_WRITEBYTE: - if (_sl.save) { - SlWriteByte(sld->version_to); - } else { - *(byte*)ptr = sld->version_from; - } - break; - - /* SL_VEH_INCLUDE loads common code for vehicles */ - case SL_VEH_INCLUDE: - SlObject(ptr, GetVehicleDescription(VEH_END)); - break; - default: NOT_REACHED(); - } - return true; -} - -/** - * Main SaveLoad function. - * @param object The object that is being saved or loaded - * @param sld The SaveLoad description of the object so we know how to manipulate it - */ -void SlObject(void *object, const SaveLoad *sld) -{ - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcObjLength(object, sld)); - if (_sl.need_length == NL_CALCLENGTH) return; - } - - for (; sld->cmd != SL_END; sld++) { - void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld); - SlObjectMember(ptr, sld); - } -} - -/** - * Save or Load (a list of) global variables - * @param sldg The global variable that is being loaded or saved - */ -void SlGlobList(const SaveLoadGlobVarList *sldg) -{ - SlObject(NULL, (const SaveLoad*)sldg); -} - -/** - * Do something of which I have no idea what it is :P - * @param proc The callback procedure that is called - * @param arg The variable that will be used for the callback procedure - */ -void SlAutolength(AutolengthProc *proc, void *arg) -{ - size_t offs; - - assert(_sl.save); - - /* Tell it to calculate the length */ - _sl.need_length = NL_CALCLENGTH; - _sl.obj_len = 0; - proc(arg); - - /* Setup length */ - _sl.need_length = NL_WANTLENGTH; - SlSetLength(_sl.obj_len); - - offs = SlGetOffs() + _sl.obj_len; - - /* And write the stuff */ - proc(arg); - - if (offs != SlGetOffs()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size"); -} - -/** - * Load a chunk of data (eg vehicles, stations, etc.) - * @param ch The chunkhandler that will be used for the operation - */ -static void SlLoadChunk(const ChunkHandler *ch) -{ - byte m = SlReadByte(); - size_t len; - size_t endoffs; - - _sl.block_mode = m; - _sl.obj_len = 0; - - switch (m) { - case CH_ARRAY: - _sl.array_index = 0; - ch->load_proc(); - break; - case CH_SPARSE_ARRAY: - ch->load_proc(); - break; - default: - if ((m & 0xF) == CH_RIFF) { - /* Read length */ - len = (SlReadByte() << 16) | ((m >> 4) << 24); - len += SlReadUint16(); - _sl.obj_len = len; - endoffs = SlGetOffs() + len; - ch->load_proc(); - if (SlGetOffs() != endoffs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size"); - } else { - SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk type"); - } - break; - } -} - -/* Stub Chunk handlers to only calculate length and do nothing else */ -static ChunkSaveLoadProc *_tmp_proc_1; -static inline void SlStubSaveProc2(void *arg) {_tmp_proc_1();} -static void SlStubSaveProc() {SlAutolength(SlStubSaveProc2, NULL);} - -/** Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is - * prefixed by an ID identifying it, followed by data, and terminator where appropiate - * @param ch The chunkhandler that will be used for the operation - */ -static void SlSaveChunk(const ChunkHandler *ch) -{ - ChunkSaveLoadProc *proc = ch->save_proc; - - /* Don't save any chunk information if there is no save handler. */ - if (proc == NULL) return; - - SlWriteUint32(ch->id); - DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id); - - if (ch->flags & CH_AUTO_LENGTH) { - /* Need to calculate the length. Solve that by calling SlAutoLength in the save_proc. */ - _tmp_proc_1 = proc; - proc = SlStubSaveProc; - } - - _sl.block_mode = ch->flags & CH_TYPE_MASK; - switch (ch->flags & CH_TYPE_MASK) { - case CH_RIFF: - _sl.need_length = NL_WANTLENGTH; - proc(); - break; - case CH_ARRAY: - _sl.last_array_index = 0; - SlWriteByte(CH_ARRAY); - proc(); - SlWriteArrayLength(0); // Terminate arrays - break; - case CH_SPARSE_ARRAY: - SlWriteByte(CH_SPARSE_ARRAY); - proc(); - SlWriteArrayLength(0); // Terminate arrays - break; - default: NOT_REACHED(); - } -} - -/** Save all chunks */ -static void SlSaveChunks() -{ - const ChunkHandler *ch; - const ChunkHandler* const *chsc; - uint p; - - for (p = 0; p != CH_NUM_PRI_LEVELS; p++) { - for (chsc = _sl.chs; (ch = *chsc++) != NULL;) { - while (true) { - if (((ch->flags >> CH_PRI_SHL) & (CH_NUM_PRI_LEVELS - 1)) == p) - SlSaveChunk(ch); - if (ch->flags & CH_LAST) - break; - ch++; - } - } - } - - /* Terminator */ - SlWriteUint32(0); -} - -/** Find the ChunkHandler that will be used for processing the found - * chunk in the savegame or in memory - * @param id the chunk in question - * @return returns the appropiate chunkhandler - */ -static const ChunkHandler *SlFindChunkHandler(uint32 id) -{ - const ChunkHandler *ch; - const ChunkHandler *const *chsc; - for (chsc = _sl.chs; (ch = *chsc++) != NULL;) { - for (;;) { - if (ch->id == id) return ch; - if (ch->flags & CH_LAST) break; - ch++; - } - } - return NULL; -} - -/** Load all chunks */ -static void SlLoadChunks() -{ - uint32 id; - const ChunkHandler *ch; - - for (id = SlReadUint32(); id != 0; id = SlReadUint32()) { - DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); - - ch = SlFindChunkHandler(id); - if (ch == NULL) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unknown chunk type"); - SlLoadChunk(ch); - } -} - -/******************************************* - ********** START OF LZO CODE ************** - *******************************************/ -#define LZO_SIZE 8192 - -#include "minilzo.h" - -static size_t ReadLZO() -{ - byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; - uint32 tmp[2]; - uint32 size; - uint len; - - /* Read header*/ - if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed"); - - /* Check if size is bad */ - ((uint32*)out)[0] = size = tmp[1]; - - if (_sl_version != 0) { - tmp[0] = TO_BE32(tmp[0]); - size = TO_BE32(size); - } - - if (size >= sizeof(out)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Inconsistent size"); - - /* Read block */ - if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); - - /* Verify checksum */ - if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Bad checksum"); - - /* Decompress */ - lzo1x_decompress(out + sizeof(uint32)*1, size, _sl.buf, &len, NULL); - return len; -} - -/* p contains the pointer to the buffer, len contains the pointer to the length. - * len bytes will be written, p and l will be updated to reflect the next buffer. */ -static void WriteLZO(size_t size) -{ - byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; - byte wrkmem[sizeof(byte*)*4096]; - uint outlen; - - lzo1x_1_compress(_sl.buf, (lzo_uint)size, out + sizeof(uint32)*2, &outlen, wrkmem); - ((uint32*)out)[1] = TO_BE32(outlen); - ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32))); - if (fwrite(out, outlen + sizeof(uint32)*2, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); -} - -static bool InitLZO() -{ - _sl.bufsize = LZO_SIZE; - _sl.buf = _sl.buf_ori = MallocT(LZO_SIZE); - return true; -} - -static void UninitLZO() -{ - free(_sl.buf_ori); -} - -/********************************************* - ******** START OF NOCOMP CODE (uncompressed)* - *********************************************/ -static size_t ReadNoComp() -{ - return fread(_sl.buf, 1, LZO_SIZE, _sl.fh); -} - -static void WriteNoComp(size_t size) -{ - if (fwrite(_sl.buf, 1, size, _sl.fh) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); -} - -static bool InitNoComp() -{ - _sl.bufsize = LZO_SIZE; - _sl.buf = _sl.buf_ori = MallocT(LZO_SIZE); - return true; -} - -static void UninitNoComp() -{ - free(_sl.buf_ori); -} - -/******************************************** - ********** START OF MEMORY CODE (in ram)**** - ********************************************/ - -#include "table/sprites.h" -#include "gui.h" - -struct ThreadedSave { - uint count; - byte ff_state; - bool saveinprogress; - CursorID cursor; -}; - -/* A maximum size of of 128K * 500 = 64.000KB savegames */ -STATIC_OLD_POOL(Savegame, byte, 17, 500, NULL, NULL) -static ThreadedSave _ts; - -static bool InitMem() -{ - _ts.count = 0; - - _Savegame_pool.CleanPool(); - _Savegame_pool.AddBlockToPool(); - - /* A block from the pool is a contigious area of memory, so it is safe to write to it sequentially */ - _sl.bufsize = GetSavegamePoolSize(); - _sl.buf = GetSavegame(_ts.count); - return true; -} - -static void UnInitMem() -{ - _Savegame_pool.CleanPool(); -} - -static void WriteMem(size_t size) -{ - _ts.count += (uint)size; - /* Allocate new block and new buffer-pointer */ - _Savegame_pool.AddBlockIfNeeded(_ts.count); - _sl.buf = GetSavegame(_ts.count); -} - -/******************************************** - ********** START OF ZLIB CODE ************** - ********************************************/ - -#if defined(WITH_ZLIB) -#include - -static z_stream _z; - -static bool InitReadZlib() -{ - memset(&_z, 0, sizeof(_z)); - if (inflateInit(&_z) != Z_OK) return false; - - _sl.bufsize = 4096; - _sl.buf = _sl.buf_ori = MallocT(4096 + 4096); // also contains fread buffer - return true; -} - -static size_t ReadZlib() -{ - int r; - - _z.next_out = _sl.buf; - _z.avail_out = 4096; - - do { - /* read more bytes from the file? */ - if (_z.avail_in == 0) { - _z.avail_in = (uint)fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh); - } - - /* inflate the data */ - r = inflate(&_z, 0); - if (r == Z_STREAM_END) - break; - - if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed"); - } while (_z.avail_out); - - return 4096 - _z.avail_out; -} - -static void UninitReadZlib() -{ - inflateEnd(&_z); - free(_sl.buf_ori); -} - -static bool InitWriteZlib() -{ - memset(&_z, 0, sizeof(_z)); - if (deflateInit(&_z, 6) != Z_OK) return false; - - _sl.bufsize = 4096; - _sl.buf = _sl.buf_ori = MallocT(4096); // also contains fread buffer - return true; -} - -static void WriteZlibLoop(z_streamp z, byte *p, size_t len, int mode) -{ - byte buf[1024]; // output buffer - int r; - uint n; - z->next_in = p; - z->avail_in = (uInt)len; - do { - z->next_out = buf; - z->avail_out = sizeof(buf); - r = deflate(z, mode); - /* bytes were emitted? */ - if ((n = sizeof(buf) - z->avail_out) != 0) { - if (fwrite(buf, n, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); - } - if (r == Z_STREAM_END) - break; - if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code"); - } while (z->avail_in || !z->avail_out); -} - -static void WriteZlib(size_t len) -{ - WriteZlibLoop(&_z, _sl.buf, len, 0); -} - -static void UninitWriteZlib() -{ - /* flush any pending output. */ - if (_sl.fh) WriteZlibLoop(&_z, NULL, 0, Z_FINISH); - deflateEnd(&_z); - free(_sl.buf_ori); -} - -#endif /* WITH_ZLIB */ - -/******************************************* - ************* END OF CODE ***************** - *******************************************/ - -/* these define the chunks */ -extern const ChunkHandler _gamelog_chunk_handlers[]; -extern const ChunkHandler _misc_chunk_handlers[]; -extern const ChunkHandler _name_chunk_handlers[]; -extern const ChunkHandler _cheat_chunk_handlers[] ; -extern const ChunkHandler _setting_chunk_handlers[]; -extern const ChunkHandler _company_chunk_handlers[]; -extern const ChunkHandler _engine_chunk_handlers[]; -extern const ChunkHandler _veh_chunk_handlers[]; -extern const ChunkHandler _waypoint_chunk_handlers[]; -extern const ChunkHandler _depot_chunk_handlers[]; -extern const ChunkHandler _order_chunk_handlers[]; -extern const ChunkHandler _town_chunk_handlers[]; -extern const ChunkHandler _sign_chunk_handlers[]; -extern const ChunkHandler _station_chunk_handlers[]; -extern const ChunkHandler _industry_chunk_handlers[]; -extern const ChunkHandler _economy_chunk_handlers[]; -extern const ChunkHandler _animated_tile_chunk_handlers[]; -extern const ChunkHandler _newgrf_chunk_handlers[]; -extern const ChunkHandler _group_chunk_handlers[]; -extern const ChunkHandler _cargopacket_chunk_handlers[]; -extern const ChunkHandler _autoreplace_chunk_handlers[]; - -static const ChunkHandler * const _chunk_handlers[] = { - _gamelog_chunk_handlers, - _misc_chunk_handlers, - _name_chunk_handlers, - _cheat_chunk_handlers, - _setting_chunk_handlers, - _veh_chunk_handlers, - _waypoint_chunk_handlers, - _depot_chunk_handlers, - _order_chunk_handlers, - _industry_chunk_handlers, - _economy_chunk_handlers, - _engine_chunk_handlers, - _town_chunk_handlers, - _sign_chunk_handlers, - _station_chunk_handlers, - _company_chunk_handlers, - _animated_tile_chunk_handlers, - _newgrf_chunk_handlers, - _group_chunk_handlers, - _cargopacket_chunk_handlers, - _autoreplace_chunk_handlers, - NULL, -}; - -/** - * Pointers cannot be saved to a savegame, so this functions gets - * the index of the item, and if not available, it hussles with - * pointers (looks really bad :() - * Remember that a NULL item has value 0, and all - * indeces have +1, so vehicle 0 is saved as index 1. - * @param obj The object that we want to get the index of - * @param rt SLRefType type of the object the index is being sought of - * @return Return the pointer converted to an index of the type pointed to - */ -static uint ReferenceToInt(const void *obj, SLRefType rt) -{ - if (obj == NULL) return 0; - - switch (rt) { - case REF_VEHICLE_OLD: // Old vehicles we save as new onces - case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1; - case REF_STATION: return ((const Station*)obj)->index + 1; - case REF_TOWN: return ((const Town*)obj)->index + 1; - case REF_ORDER: return ((const Order*)obj)->index + 1; - case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1; - case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1; - case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1; - case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1; - default: NOT_REACHED(); - } - - return 0; // avoid compiler warning -} - -/** - * Pointers cannot be loaded from a savegame, so this function - * gets the index from the savegame and returns the appropiate - * pointer from the already loaded base. - * Remember that an index of 0 is a NULL pointer so all indeces - * are +1 so vehicle 0 is saved as 1. - * @param index The index that is being converted to a pointer - * @param rt SLRefType type of the object the pointer is sought of - * @return Return the index converted to a pointer of any type - */ -static void *IntToReference(uint index, SLRefType rt) -{ - /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE, - * and should be loaded like that */ - if (rt == REF_VEHICLE_OLD && !CheckSavegameVersionOldStyle(4, 4)) { - rt = REF_VEHICLE; - } - - /* No need to look up NULL pointers, just return immediately */ - if (rt != REF_VEHICLE_OLD && index == 0) { - return NULL; - } - - index--; // correct for the NULL index - - switch (rt) { - case REF_ORDERLIST: - if (_OrderList_pool.AddBlockIfNeeded(index)) return GetOrderList(index); - error("Orders: failed loading savegame: too many order lists"); - - case REF_ORDER: - if (_Order_pool.AddBlockIfNeeded(index)) return GetOrder(index); - error("Orders: failed loading savegame: too many orders"); - - case REF_VEHICLE: - if (_Vehicle_pool.AddBlockIfNeeded(index)) return GetVehicle(index); - error("Vehicles: failed loading savegame: too many vehicles"); - - case REF_STATION: - if (_Station_pool.AddBlockIfNeeded(index)) return GetStation(index); - error("Stations: failed loading savegame: too many stations"); - - case REF_TOWN: - if (_Town_pool.AddBlockIfNeeded(index)) return GetTown(index); - error("Towns: failed loading savegame: too many towns"); - - case REF_ROADSTOPS: - if (_RoadStop_pool.AddBlockIfNeeded(index)) return GetRoadStop(index); - error("RoadStops: failed loading savegame: too many RoadStops"); - - case REF_ENGINE_RENEWS: - if (_EngineRenew_pool.AddBlockIfNeeded(index)) return GetEngineRenew(index); - error("EngineRenews: failed loading savegame: too many EngineRenews"); - - case REF_CARGO_PACKET: - if (_CargoPacket_pool.AddBlockIfNeeded(index)) return GetCargoPacket(index); - error("CargoPackets: failed loading savegame: too many Cargo packets"); - - case REF_VEHICLE_OLD: - /* Old vehicles were saved differently: - * invalid vehicle was 0xFFFF, - * and the index was not - 1.. correct for this */ - index++; - if (index == INVALID_VEHICLE) return NULL; - - if (_Vehicle_pool.AddBlockIfNeeded(index)) return GetVehicle(index); - error("Vehicles: failed loading savegame: too many vehicles"); - - default: NOT_REACHED(); - } - - return NULL; -} - -/** The format for a reader/writer type of a savegame */ -struct SaveLoadFormat { - const char *name; ///< name of the compressor/decompressor (debug-only) - uint32 tag; ///< the 4-letter tag by which it is identified in the savegame - - bool (*init_read)(); ///< function executed upon initalization of the loader - ReaderProc *reader; ///< function that loads the data from the file - void (*uninit_read)(); ///< function executed when reading is finished - - bool (*init_write)(); ///< function executed upon intialization of the saver - WriterProc *writer; ///< function that saves the data to the file - void (*uninit_write)(); ///< function executed when writing is done -}; - -static const SaveLoadFormat _saveload_formats[] = { - {"memory", 0, NULL, NULL, NULL, InitMem, WriteMem, UnInitMem}, - {"lzo", TO_BE32X('OTTD'), InitLZO, ReadLZO, UninitLZO, InitLZO, WriteLZO, UninitLZO}, - {"none", TO_BE32X('OTTN'), InitNoComp, ReadNoComp, UninitNoComp, InitNoComp, WriteNoComp, UninitNoComp}, -#if defined(WITH_ZLIB) - {"zlib", TO_BE32X('OTTZ'), InitReadZlib, ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib}, -#else - {"zlib", TO_BE32X('OTTZ'), NULL, NULL, NULL, NULL, NULL, NULL}, -#endif -}; - -/** - * Return the savegameformat of the game. Whether it was create with ZLIB compression - * uncompressed, or another type - * @param s Name of the savegame format. If NULL it picks the first available one - * @return Pointer to SaveLoadFormat struct giving all characteristics of this type of savegame - */ -static const SaveLoadFormat *GetSavegameFormat(const char *s) -{ - const SaveLoadFormat *def = endof(_saveload_formats) - 1; - - /* find default savegame format, the highest one with which files can be written */ - while (!def->init_write) def--; - - if (s != NULL && s[0] != '\0') { - const SaveLoadFormat *slf; - for (slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) { - if (slf->init_write != NULL && strcmp(s, slf->name) == 0) - return slf; - } - - ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name); - } - return def; -} - -/* actual loader/saver function */ -void InitializeGame(uint size_x, uint size_y, bool reset_date); -extern bool AfterLoadGame(); -extern void SaveViewportBeforeSaveGame(); -extern bool LoadOldSaveGame(const char *file); - -/** Small helper function to close the to be loaded savegame an signal error */ -static inline SaveOrLoadResult AbortSaveLoad() -{ - if (_sl.fh != NULL) fclose(_sl.fh); - - _sl.fh = NULL; - return SL_ERROR; -} - -/** Update the gui accordingly when starting saving - * and set locks on saveload. Also turn off fast-forward cause with that - * saving takes Aaaaages */ -static void SaveFileStart() -{ - _ts.ff_state = _fast_forward; - _fast_forward = 0; - if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE); - - InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START); - _ts.saveinprogress = true; -} - -/** Update the gui accordingly when saving is done and release locks - * on saveload */ -static void SaveFileDone() -{ - if (_game_mode != GM_MENU) _fast_forward = _ts.ff_state; - if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE); - - InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH); - _ts.saveinprogress = false; -} - -/** Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friends) */ -void SetSaveLoadError(StringID str) -{ - _sl.error_str = str; -} - -/** Get the string representation of the error message */ -const char *GetSaveLoadErrorString() -{ - SetDParam(0, _sl.error_str); - SetDParamStr(1, _sl.extra_msg); - - static char err_str[512]; - GetString(err_str, _sl.save ? STR_4007_GAME_SAVE_FAILED : STR_4009_GAME_LOAD_FAILED, lastof(err_str)); - return err_str; -} - -/** Show a gui message when saving has failed */ -static void SaveFileError() -{ - SetDParamStr(0, GetSaveLoadErrorString()); - ShowErrorMessage(STR_JUST_RAW_STRING, STR_NULL, 0, 0); - SaveFileDone(); -} - -/** We have written the whole game into memory, _Savegame_pool, now find - * and appropiate compressor and start writing to file. - */ -static SaveOrLoadResult SaveFileToDisk(bool threaded) -{ - const SaveLoadFormat *fmt; - uint32 hdr[2]; - - _sl.excpt_uninit = NULL; - try { - fmt = GetSavegameFormat(_savegame_format); - - /* We have written our stuff to memory, now write it to file! */ - hdr[0] = fmt->tag; - hdr[1] = TO_BE32(SAVEGAME_VERSION << 16); - if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); - - if (!fmt->init_write()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); - - { - uint i; - uint count = 1 << Savegame_POOL_BLOCK_SIZE_BITS; - - if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk"); - for (i = 0; i != _Savegame_pool.GetBlockCount() - 1; i++) { - _sl.buf = _Savegame_pool.blocks[i]; - fmt->writer(count); - } - - /* The last block is (almost) always not fully filled, so only write away - * as much data as it is in there */ - _sl.buf = _Savegame_pool.blocks[i]; - fmt->writer(_ts.count - (i * count)); - } - - fmt->uninit_write(); - if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk"); - GetSavegameFormat("memory")->uninit_write(); // clean the memorypool - fclose(_sl.fh); - - if (threaded) SetAsyncSaveFinish(SaveFileDone); - - return SL_OK; - } - catch (...) { - AbortSaveLoad(); - if (_sl.excpt_uninit != NULL) _sl.excpt_uninit(); - - /* Skip the "color" character */ - DEBUG(sl, 0, GetSaveLoadErrorString() + 3); - - if (threaded) { - SetAsyncSaveFinish(SaveFileError); - } else { - SaveFileError(); - } - return SL_ERROR; - } -} - -static void SaveFileToDiskThread(void *arg) -{ - SaveFileToDisk(true); -} - -void WaitTillSaved() -{ - if (_save_thread == NULL) return; - - _save_thread->Join(); - delete _save_thread; - _save_thread = NULL; -} - -/** - * Main Save or Load function where the high-level saveload functions are - * handled. It opens the savegame, selects format and checks versions - * @param filename The name of the savegame being created/loaded - * @param mode Save or load. Load can also be a TTD(Patch) game. Use SL_LOAD, SL_OLD_LOAD or SL_SAVE - * @return Return the results of the action. SL_OK, SL_ERROR or SL_REINIT ("unload" the game) - */ -SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb) -{ - uint32 hdr[2]; - const SaveLoadFormat *fmt; - - /* An instance of saving is already active, so don't go saving again */ - if (_ts.saveinprogress && mode == SL_SAVE) { - /* if not an autosave, but a user action, show error message */ - if (!_do_autosave) ShowErrorMessage(INVALID_STRING_ID, STR_SAVE_STILL_IN_PROGRESS, 0, 0); - return SL_OK; - } - WaitTillSaved(); - - _next_offs = 0; - - /* Load a TTDLX or TTDPatch game */ - if (mode == SL_OLD_LOAD) { - InitializeGame(256, 256, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused - GamelogReset(); - if (!LoadOldSaveGame(filename)) return SL_REINIT; - _sl_version = 0; - _sl_minor_version = 0; - GamelogStartAction(GLAT_LOAD); - if (!AfterLoadGame()) { - GamelogStopAction(); - return SL_REINIT; - } - GamelogStopAction(); - return SL_OK; - } - - _sl.excpt_uninit = NULL; - try { - _sl.fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb); - - /* Make it a little easier to load savegames from the console */ - if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", SAVE_DIR); - if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", BASE_DIR); - - if (_sl.fh == NULL) { - SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); - } - - _sl.bufe = _sl.bufp = NULL; - _sl.offs_base = 0; - _sl.save = (mode != 0); - _sl.chs = _chunk_handlers; - - /* General tactic is to first save the game to memory, then use an available writer - * to write it to file, either in threaded mode if possible, or single-threaded */ - if (mode == SL_SAVE) { /* SAVE game */ - DEBUG(desync, 1, "save: %s\n", filename); - fmt = GetSavegameFormat("memory"); // write to memory - - _sl.write_bytes = fmt->writer; - _sl.excpt_uninit = fmt->uninit_write; - if (!fmt->init_write()) { - DEBUG(sl, 0, "Initializing writer '%s' failed.", fmt->name); - return AbortSaveLoad(); - } - - _sl_version = SAVEGAME_VERSION; - - SaveViewportBeforeSaveGame(); - SlSaveChunks(); - SlWriteFill(); // flush the save buffer - - SaveFileStart(); - if (_network_server || - (_save_thread = ThreadObject::New(&SaveFileToDiskThread, NULL)) == NULL) { - if (!_network_server) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode..."); - - SaveOrLoadResult result = SaveFileToDisk(false); - SaveFileDone(); - - return result; - } - } else { /* LOAD game */ - assert(mode == SL_LOAD); - DEBUG(desync, 1, "load: %s\n", filename); - - if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); - - /* see if we have any loader for this type. */ - for (fmt = _saveload_formats; ; fmt++) { - /* No loader found, treat as version 0 and use LZO format */ - if (fmt == endof(_saveload_formats)) { - DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format"); - #if defined(WINCE) - /* Of course some system had not to support rewind ;) */ - fseek(_sl.fh, 0L, SEEK_SET); - clearerr(_sl.fh); - #else - rewind(_sl.fh); - #endif - _sl_version = 0; - _sl_minor_version = 0; - fmt = _saveload_formats + 1; // LZO - break; - } - - if (fmt->tag == hdr[0]) { - /* check version number */ - _sl_version = TO_BE32(hdr[1]) >> 16; - /* Minor is not used anymore from version 18.0, but it is still needed - * in versions before that (4 cases) which can't be removed easy. - * Therefor it is loaded, but never saved (or, it saves a 0 in any scenario). - * So never EVER use this minor version again. -- TrueLight -- 22-11-2005 */ - _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF; - - DEBUG(sl, 1, "Loading savegame version %d", _sl_version); - - /* Is the version higher than the current? */ - if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME); - break; - } - } - - _sl.read_bytes = fmt->reader; - _sl.excpt_uninit = fmt->uninit_read; - - /* loader for this savegame type is not implemented? */ - if (fmt->init_read == NULL) { - char err_str[64]; - snprintf(err_str, lengthof(err_str), "Loader for '%s' is not available.", fmt->name); - SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str); - } - - if (!fmt->init_read()) { - char err_str[64]; - snprintf(err_str, lengthof(err_str), "Initializing loader '%s' failed", fmt->name); - SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str); - } - - /* Old maps were hardcoded to 256x256 and thus did not contain - * any mapsize information. Pre-initialize to 256x256 to not to - * confuse old games */ - InitializeGame(256, 256, true); - - GamelogReset(); - - SlLoadChunks(); - fmt->uninit_read(); - fclose(_sl.fh); - - GamelogStartAction(GLAT_LOAD); - - _savegame_type = SGT_OTTD; - - /* After loading fix up savegame for any internal changes that - * might've occured since then. If it fails, load back the old game */ - if (!AfterLoadGame()) { - GamelogStopAction(); - return SL_REINIT; - } - - GamelogStopAction(); - } - - return SL_OK; - } - catch (...) { - AbortSaveLoad(); - - /* deinitialize compressor. */ - if (_sl.excpt_uninit != NULL) _sl.excpt_uninit(); - - /* Skip the "color" character */ - DEBUG(sl, 0, GetSaveLoadErrorString() + 3); - - /* A saver/loader exception!! reinitialize all variables to prevent crash! */ - return (mode == SL_LOAD) ? SL_REINIT : SL_ERROR; - } -} - -/** Do a save when exiting the game (patch option) _settings_client.gui.autosave_on_exit */ -void DoExitSave() -{ - SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR); -} - -/** - * Fill the buffer with the default name for a savegame *or* screenshot. - * @param buf the buffer to write to. - * @param last the last element in the buffer. - */ -void GenerateDefaultSaveName(char *buf, const char *last) -{ - /* Check if we have a name for this map, which is the name of the first - * available company. When there's no company available we'll use - * 'Spectator' as "company" name. */ - CompanyID cid = _local_company; - if (!IsValidCompanyID(cid)) { - const Company *c; - FOR_ALL_COMPANIES(c) { - cid = c->index; - break; - } - } - - SetDParam(0, cid); - - /* Insert current date */ - switch (_settings_client.gui.date_format_in_default_names) { - case 0: SetDParam(1, STR_JUST_DATE_LONG); break; - case 1: SetDParam(1, STR_JUST_DATE_TINY); break; - case 2: SetDParam(1, STR_JUST_DATE_ISO); break; - default: NOT_REACHED(); - } - SetDParam(2, _date); - - /* Get the correct string (special string for when there's not company) */ - GetString(buf, !IsValidCompanyID(cid) ? STR_GAME_SAVELOAD_SPECTATOR_SAVEGAME : STR_4004, last); - SanitizeFilename(buf); -} - -#if 0 -/** - * Function to get the type of the savegame by looking at the file header. - * NOTICE: Not used right now, but could be used if extensions of savegames are garbled - * @param file Savegame to be checked - * @return SL_OLD_LOAD or SL_LOAD of the file - */ -int GetSavegameType(char *file) -{ - const SaveLoadFormat *fmt; - uint32 hdr; - FILE *f; - int mode = SL_OLD_LOAD; - - f = fopen(file, "rb"); - if (fread(&hdr, sizeof(hdr), 1, f) != 1) { - DEBUG(sl, 0, "Savegame is obsolete or invalid format"); - mode = SL_LOAD; // don't try to get filename, just show name as it is written - } else { - /* see if we have any loader for this type. */ - for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) { - if (fmt->tag == hdr) { - mode = SL_LOAD; // new type of savegame - break; - } - } - } - - fclose(f); - return mode; -} -#endif diff --git a/src/saveload.h b/src/saveload.h deleted file mode 100644 --- a/src/saveload.h +++ /dev/null @@ -1,332 +0,0 @@ -/* $Id$ */ - -/** @file saveload.h Functions/types related to saving and loading games. */ - -#ifndef SAVELOAD_H -#define SAVELOAD_H - -#include "fileio_type.h" - -#ifdef SIZE_MAX -#undef SIZE_MAX -#endif - -#define SIZE_MAX ((size_t)-1) - -enum SaveOrLoadResult { - SL_OK = 0, ///< completed successfully - SL_ERROR = 1, ///< error that was caught before internal structures were modified - SL_REINIT = 2, ///< error that was caught in the middle of updating game state, need to clear it. (can only happen during load) -}; - -enum SaveOrLoadMode { - SL_INVALID = -1, - SL_LOAD = 0, - SL_SAVE = 1, - SL_OLD_LOAD = 2, - SL_PNG = 3, - SL_BMP = 4, -}; - -enum SavegameType { - SGT_TTD, ///< TTD savegame (can be detected incorrectly) - SGT_TTDP1, ///< TTDP savegame ( -//- ) (data at NW border) - SGT_TTDP2, ///< TTDP savegame in new format (data at SE border) - SGT_OTTD ///< OTTD savegame -}; - -void GenerateDefaultSaveName(char *buf, const char *last); -void SetSaveLoadError(uint16 str); -const char *GetSaveLoadErrorString(); -SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb); -void WaitTillSaved(); -void DoExitSave(); - - -typedef void ChunkSaveLoadProc(); -typedef void AutolengthProc(void *arg); - -struct ChunkHandler { - uint32 id; - ChunkSaveLoadProc *save_proc; - ChunkSaveLoadProc *load_proc; - uint32 flags; -}; - -struct NullStruct { - byte null; -}; - -enum SLRefType { - REF_ORDER = 0, - REF_VEHICLE = 1, - REF_STATION = 2, - REF_TOWN = 3, - REF_VEHICLE_OLD = 4, - REF_ROADSTOPS = 5, - REF_ENGINE_RENEWS = 6, - REF_CARGO_PACKET = 7, - REF_ORDERLIST = 8, -}; - -#define SL_MAX_VERSION 255 - -enum { - INC_VEHICLE_COMMON = 0, -}; - -enum { - CH_RIFF = 0, - CH_ARRAY = 1, - CH_SPARSE_ARRAY = 2, - CH_TYPE_MASK = 3, - CH_LAST = 8, - CH_AUTO_LENGTH = 16, - - CH_PRI_0 = 0 << 4, - CH_PRI_1 = 1 << 4, - CH_PRI_2 = 2 << 4, - CH_PRI_3 = 3 << 4, - CH_PRI_SHL = 4, - CH_NUM_PRI_LEVELS = 4, -}; - -/** VarTypes is the general bitmasked magic type that tells us - * certain characteristics about the variable it refers to. For example - * SLE_FILE_* gives the size(type) as it would be in the savegame and - * SLE_VAR_* the size(type) as it is in memory during runtime. These are - * the first 8 bits (0-3 SLE_FILE, 4-7 SLE_VAR). - * Bits 8-15 are reserved for various flags as explained below */ -enum VarTypes { - /* 4 bits allocated a maximum of 16 types for NumberType */ - SLE_FILE_I8 = 0, - SLE_FILE_U8 = 1, - SLE_FILE_I16 = 2, - SLE_FILE_U16 = 3, - SLE_FILE_I32 = 4, - SLE_FILE_U32 = 5, - SLE_FILE_I64 = 6, - SLE_FILE_U64 = 7, - SLE_FILE_STRINGID = 8, ///< StringID offset into strings-array - SLE_FILE_STRING = 9, - /* 6 more possible file-primitives */ - - /* 4 bits allocated a maximum of 16 types for NumberType */ - SLE_VAR_BL = 0 << 4, - SLE_VAR_I8 = 1 << 4, - SLE_VAR_U8 = 2 << 4, - SLE_VAR_I16 = 3 << 4, - SLE_VAR_U16 = 4 << 4, - SLE_VAR_I32 = 5 << 4, - SLE_VAR_U32 = 6 << 4, - SLE_VAR_I64 = 7 << 4, - SLE_VAR_U64 = 8 << 4, - SLE_VAR_NULL = 9 << 4, ///< useful to write zeros in savegame. - SLE_VAR_STRB = 10 << 4, ///< string (with pre-allocated buffer) - SLE_VAR_STRBQ = 11 << 4, ///< string enclosed in quotes (with pre-allocated buffer) - SLE_VAR_STR = 12 << 4, ///< string pointer - SLE_VAR_STRQ = 13 << 4, ///< string pointer enclosed in quotes - SLE_VAR_NAME = 14 << 4, ///< old custom name to be converted to a char pointer - /* 1 more possible memory-primitives */ - - /* Shortcut values */ - SLE_VAR_CHAR = SLE_VAR_I8, - - /* Default combinations of variables. As savegames change, so can variables - * and thus it is possible that the saved value and internal size do not - * match and you need to specify custom combo. The defaults are listed here */ - SLE_BOOL = SLE_FILE_I8 | SLE_VAR_BL, - SLE_INT8 = SLE_FILE_I8 | SLE_VAR_I8, - SLE_UINT8 = SLE_FILE_U8 | SLE_VAR_U8, - SLE_INT16 = SLE_FILE_I16 | SLE_VAR_I16, - SLE_UINT16 = SLE_FILE_U16 | SLE_VAR_U16, - SLE_INT32 = SLE_FILE_I32 | SLE_VAR_I32, - SLE_UINT32 = SLE_FILE_U32 | SLE_VAR_U32, - SLE_INT64 = SLE_FILE_I64 | SLE_VAR_I64, - SLE_UINT64 = SLE_FILE_U64 | SLE_VAR_U64, - SLE_CHAR = SLE_FILE_I8 | SLE_VAR_CHAR, - SLE_STRINGID = SLE_FILE_STRINGID | SLE_VAR_U16, - SLE_STRINGBUF = SLE_FILE_STRING | SLE_VAR_STRB, - SLE_STRINGBQUOTE = SLE_FILE_STRING | SLE_VAR_STRBQ, - SLE_STRING = SLE_FILE_STRING | SLE_VAR_STR, - SLE_STRINGQUOTE = SLE_FILE_STRING | SLE_VAR_STRQ, - SLE_NAME = SLE_FILE_STRINGID | SLE_VAR_NAME, - - /* Shortcut values */ - SLE_UINT = SLE_UINT32, - SLE_INT = SLE_INT32, - SLE_STRB = SLE_STRINGBUF, - SLE_STRBQ = SLE_STRINGBQUOTE, - SLE_STR = SLE_STRING, - SLE_STRQ = SLE_STRINGQUOTE, - - /* 8 bits allocated for a maximum of 8 flags - * Flags directing saving/loading of a variable */ - SLF_SAVE_NO = 1 << 8, ///< do not save with savegame, basically client-based - SLF_CONFIG_NO = 1 << 9, ///< do not save to config file - SLF_NETWORK_NO = 1 << 10, ///< do not synchronize over network (but it is saved if SSF_SAVE_NO is not set) - /* 5 more possible flags */ -}; - -typedef uint32 VarType; - -enum SaveLoadTypes { - SL_VAR = 0, - SL_REF = 1, - SL_ARR = 2, - SL_STR = 3, - SL_LST = 4, - // non-normal save-load types - SL_WRITEBYTE = 8, - SL_VEH_INCLUDE = 9, - SL_END = 15 -}; - -typedef byte SaveLoadType; - -/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */ -struct SaveLoad { - bool global; ///< should we load a global variable or a non-global one - SaveLoadType cmd; ///< the action to take with the saved/loaded type, All types need different action - VarType conv; ///< type of the variable to be saved, int - uint16 length; ///< (conditional) length of the variable (eg. arrays) (max array size is 65536 elements) - uint16 version_from; ///< save/load the variable starting from this savegame version - uint16 version_to; ///< save/load the variable until this savegame version - /* NOTE: This element either denotes the address of the variable for a global - * variable, or the offset within a struct which is then bound to a variable - * during runtime. Decision on which one to use is controlled by the function - * that is called to save it. address: global=true, offset: global=false */ - void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536) -}; - -/* Same as SaveLoad but global variables are used (for better readability); */ -typedef SaveLoad SaveLoadGlobVarList; - -/* Simple variables, references (pointers) and arrays */ -#define SLE_GENERAL(cmd, base, variable, type, length, from, to) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable)} -#define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to) -#define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to) -#define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to) -#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to) -#define SLE_CONDLST(base, variable, type, from, to) SLE_GENERAL(SL_LST, base, variable, type, 0, from, to) - -#define SLE_VAR(base, variable, type) SLE_CONDVAR(base, variable, type, 0, SL_MAX_VERSION) -#define SLE_REF(base, variable, type) SLE_CONDREF(base, variable, type, 0, SL_MAX_VERSION) -#define SLE_ARR(base, variable, type, length) SLE_CONDARR(base, variable, type, length, 0, SL_MAX_VERSION) -#define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, 0, SL_MAX_VERSION) -#define SLE_LST(base, variable, type) SLE_CONDLST(base, variable, type, 0, SL_MAX_VERSION) - -#define SLE_CONDNULL(length, from, to) SLE_CONDARR(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to) - -/* Translate values ingame to different values in the savegame and vv */ -#define SLE_WRITEBYTE(base, variable, value) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, value, value) - -/* The same as the ones at the top, only the offset is given directly; used for unions */ -#define SLE_GENERALX(cmd, offset, type, length, param1, param2) {false, cmd, type, length, param1, param2, (void*)(offset)} -#define SLE_CONDVARX(offset, type, from, to) SLE_GENERALX(SL_VAR, offset, type, 0, from, to) -#define SLE_CONDARRX(offset, type, length, from, to) SLE_GENERALX(SL_ARR, offset, type, length, from, to) -#define SLE_CONDREFX(offset, type, from, to) SLE_GENERALX(SL_REF, offset, type, 0, from, to) - -#define SLE_VARX(offset, type) SLE_CONDVARX(offset, type, 0, SL_MAX_VERSION) -#define SLE_REFX(offset, type) SLE_CONDREFX(offset, type, 0, SL_MAX_VERSION) - -#define SLE_WRITEBYTEX(offset, something) SLE_GENERALX(SL_WRITEBYTE, offset, 0, 0, something, 0) -#define SLE_VEH_INCLUDEX() SLE_GENERALX(SL_VEH_INCLUDE, 0, 0, 0, 0, SL_MAX_VERSION) - -/* End marker */ -#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL} - -/* Simple variables, references (pointers) and arrays, but for global variables */ -#define SLEG_GENERAL(cmd, variable, type, length, from, to) {true, cmd, type, length, from, to, (void*)&variable} - -#define SLEG_CONDVAR(variable, type, from, to) SLEG_GENERAL(SL_VAR, variable, type, 0, from, to) -#define SLEG_CONDREF(variable, type, from, to) SLEG_GENERAL(SL_REF, variable, type, 0, from, to) -#define SLEG_CONDARR(variable, type, length, from, to) SLEG_GENERAL(SL_ARR, variable, type, length, from, to) -#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_GENERAL(SL_STR, variable, type, length, from, to) -#define SLEG_CONDLST(variable, type, from, to) SLEG_GENERAL(SL_LST, variable, type, 0, from, to) - -#define SLEG_VAR(variable, type) SLEG_CONDVAR(variable, type, 0, SL_MAX_VERSION) -#define SLEG_REF(variable, type) SLEG_CONDREF(variable, type, 0, SL_MAX_VERSION) -#define SLEG_ARR(variable, type) SLEG_CONDARR(variable, type, lengthof(variable), 0, SL_MAX_VERSION) -#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, lengthof(variable), 0, SL_MAX_VERSION) -#define SLEG_LST(variable, type) SLEG_CONDLST(variable, type, 0, SL_MAX_VERSION) - -#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to, (void*)NULL} - -#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL} - -/** Checks if the savegame is below major.minor. - */ -static inline bool CheckSavegameVersionOldStyle(uint16 major, byte minor) -{ - extern uint16 _sl_version; - extern byte _sl_minor_version; - return (_sl_version < major) || (_sl_version == major && _sl_minor_version < minor); -} - -/** Checks if the savegame is below version. - */ -static inline bool CheckSavegameVersion(uint16 version) -{ - extern uint16 _sl_version; - return _sl_version < version; -} - -/** Checks if some version from/to combination falls within the range of the - * active savegame version */ -static inline bool SlIsObjectCurrentlyValid(uint16 version_from, uint16 version_to) -{ - extern const uint16 SAVEGAME_VERSION; - if (SAVEGAME_VERSION < version_from || SAVEGAME_VERSION > version_to) return false; - - return true; -} - -/* Get the NumberType of a setting. This describes the integer type - * as it is represented in memory - * @param type VarType holding information about the variable-type - * @return return the SLE_VAR_* part of a variable-type description */ -static inline VarType GetVarMemType(VarType type) -{ - return type & 0xF0; // GB(type, 4, 4) << 4; -} - -/* Get the FileType of a setting. This describes the integer type - * as it is represented in a savegame/file - * @param type VarType holding information about the variable-type - * @param return the SLE_FILE_* part of a variable-type description */ -static inline VarType GetVarFileType(VarType type) -{ - return type & 0xF; // GB(type, 0, 4); -} - -/** Get the address of the variable. Which one to pick depends on the object - * pointer. If it is NULL we are dealing with global variables so the address - * is taken. If non-null only the offset is stored in the union and we need - * to add this to the address of the object */ -static inline void *GetVariableAddress(const void *object, const SaveLoad *sld) -{ - return (byte*)(sld->global ? NULL : object) + (ptrdiff_t)sld->address; -} - -int64 ReadValue(const void *ptr, VarType conv); -void WriteValue(void *ptr, VarType conv, int64 val); - -void SlSetArrayIndex(uint index); -int SlIterateArray(); - -void SlAutolength(AutolengthProc *proc, void *arg); -size_t SlGetFieldLength(); -void SlSetLength(size_t length); -size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld); -size_t SlCalcObjLength(const void *object, const SaveLoad *sld); - -byte SlReadByte(); -void SlWriteByte(byte b); - -void SlGlobList(const SaveLoadGlobVarList *sldg); -void SlArray(void *array, size_t length, VarType conv); -void SlObject(void *object, const SaveLoad *sld); -bool SlObjectMember(void *object, const SaveLoad *sld); - -#endif /* SAVELOAD_H */ diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/afterload.cpp @@ -0,0 +1,1704 @@ +/* $Id$ */ + +/** @file afterload.cpp Code updating data after game load */ + +#include "../stdafx.h" +#include "../strings_type.h" +#include "../tile_type.h" +#include "../tile_map.h" +#include "../map_type.h" +#include "../road_map.h" +#include "../town.h" +#include "../void_map.h" +#include "../signs_base.h" +#include "../window_func.h" +#include "../station_func.h" +#include "../company_base.h" +#include "../fios.h" +#include "../date_func.h" +#include "../engine_func.h" +#include "../train.h" +#include "../string_func.h" +#include "../newgrf_config.h" +#include "../gamelog.h" +#include "../waypoint.h" +#include "../station_map.h" +#include "../station_base.h" +#include "../tunnelbridge_map.h" +#include "../debug.h" +#include "../network/network.h" +#include "../openttd.h" +#include "../gfxinit.h" +#include "../gfx_func.h" +#include "../functions.h" +#include "../industry_map.h" +#include "../town_map.h" +#include "../clear_map.h" +#include "../engine_base.h" +#include "../landscape.h" +#include "../vehicle_func.h" +#include "../newgrf_station.h" +#include "../yapf/yapf.hpp" +#include "../elrail_func.h" +#include "../signs_func.h" +#include "../newgrf_house.h" +#include "../aircraft.h" +#include "../unmovable_map.h" +#include "../tree_map.h" +#include "../company_func.h" +#include "../command_func.h" +#include "../road_cmd.h" + +#include "table/strings.h" + +#include "saveload.h" +#include "saveload_internal.h" + +#include + +extern StringID _switch_mode_errorstr; +extern Company *DoStartupNewCompany(bool is_ai); +extern void InitializeRailGUI(); + +/** + * Makes a tile canal or water depending on the surroundings. + * + * Must only be used for converting old savegames. Use WaterClass now. + * + * This as for example docks and shipdepots do not store + * whether the tile used to be canal or 'normal' water. + * @param t the tile to change. + * @param o the owner of the new tile. + * @param include_invalid_water_class Also consider WATER_CLASS_INVALID, i.e. industry tiles on land + */ +void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_water_class) +{ + /* If the slope is not flat, we always assume 'land' (if allowed). Also for one-corner-raised-shores. + * Note: Wrt. autosloping under industry tiles this is the most fool-proof behaviour. */ + if (GetTileSlope(t, NULL) != SLOPE_FLAT) { + if (include_invalid_water_class) { + SetWaterClass(t, WATER_CLASS_INVALID); + return; + } else { + NOT_REACHED(); + } + } + + /* Mark tile dirty in all cases */ + MarkTileDirtyByTile(t); + + if (TileX(t) == 0 || TileY(t) == 0 || TileX(t) == MapMaxX() - 1 || TileY(t) == MapMaxY() - 1) { + /* tiles at map borders are always WATER_CLASS_SEA */ + SetWaterClass(t, WATER_CLASS_SEA); + return; + } + + bool has_water = false; + bool has_canal = false; + bool has_river = false; + + for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { + TileIndex neighbour = TileAddByDiagDir(t, dir); + switch (GetTileType(neighbour)) { + case MP_WATER: + /* clear water and shipdepots have already a WaterClass associated */ + if (IsCoast(neighbour)) { + has_water = true; + } else if (!IsLock(neighbour)) { + switch (GetWaterClass(neighbour)) { + case WATER_CLASS_SEA: has_water = true; break; + case WATER_CLASS_CANAL: has_canal = true; break; + case WATER_CLASS_RIVER: has_river = true; break; + default: NOT_REACHED(); + } + } + break; + + case MP_RAILWAY: + /* Shore or flooded halftile */ + has_water |= (GetRailGroundType(neighbour) == RAIL_GROUND_WATER); + break; + + case MP_TREES: + /* trees on shore */ + has_water |= (GetTreeGround(neighbour) == TREE_GROUND_SHORE); + break; + + default: break; + } + } + + if (!has_water && !has_canal && !has_river && include_invalid_water_class) { + SetWaterClass(t, WATER_CLASS_INVALID); + return; + } + + if (has_river && !has_canal) { + SetWaterClass(t, WATER_CLASS_RIVER); + } else if (has_canal || !has_water) { + SetWaterClass(t, WATER_CLASS_CANAL); + } else { + SetWaterClass(t, WATER_CLASS_SEA); + } +} + +static void ConvertTownOwner() +{ + for (TileIndex tile = 0; tile != MapSize(); tile++) { + switch (GetTileType(tile)) { + case MP_ROAD: + if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m3, 7)) { + _m[tile].m3 = OWNER_TOWN; + } + /* FALLTHROUGH */ + + case MP_TUNNELBRIDGE: + if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN); + break; + + default: break; + } + } +} + +/* since savegame version 4.1, exclusive transport rights are stored at towns */ +static void UpdateExclusiveRights() +{ + Town *t; + + FOR_ALL_TOWNS(t) { + t->exclusivity = INVALID_COMPANY; + } + + /* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete) + * could be implemented this way: + * 1.) Go through all stations + * Build an array town_blocked[ town_id ][ company_id ] + * that stores if at least one station in that town is blocked for a company + * 2.) Go through that array, if you find a town that is not blocked for + * one company, but for all others, then give him exclusivity. + */ +} + +static const byte convert_currency[] = { + 0, 1, 12, 8, 3, + 10, 14, 19, 4, 5, + 9, 11, 13, 6, 17, + 16, 22, 21, 7, 15, + 18, 2, 20, +}; + +/* since savegame version 4.2 the currencies are arranged differently */ +static void UpdateCurrencies() +{ + _settings_game.locale.currency = convert_currency[_settings_game.locale.currency]; +} + +/* Up to revision 1413 the invisible tiles at the southern border have not been + * MP_VOID, even though they should have. This is fixed by this function + */ +static void UpdateVoidTiles() +{ + uint i; + + for (i = 0; i < MapMaxY(); ++i) MakeVoid(i * MapSizeX() + MapMaxX()); + for (i = 0; i < MapSizeX(); ++i) MakeVoid(MapSizeX() * MapMaxY() + i); +} + +/* since savegame version 6.0 each sign has an "owner", signs without owner (from old games are set to 255) */ +static void UpdateSignOwner() +{ + Sign *si; + + FOR_ALL_SIGNS(si) si->owner = OWNER_NONE; +} + +static inline RailType UpdateRailType(RailType rt, RailType min) +{ + return rt >= min ? (RailType)(rt + 1): rt; +} + +/** + * Initialization of the windows and several kinds of caches. + * This is not done directly in AfterLoadGame because these + * functions require that all saveload conversions have been + * done. As people tend to add savegame conversion stuff after + * the intialization of the windows and caches quite some bugs + * had been made. + * Moving this out of there is both cleaner and less bug-prone. + * + * @return true if everything went according to plan, otherwise false. + */ +static bool InitializeWindowsAndCaches() +{ + /* Initialize windows */ + ResetWindowSystem(); + SetupColorsAndInitialWindow(); + + ResetViewportAfterLoadGame(); + + /* Update coordinates of the signs. */ + UpdateAllStationVirtCoord(); + UpdateAllSignVirtCoords(); + UpdateAllTownVirtCoords(); + UpdateAllWaypointSigns(); + + Company *c; + FOR_ALL_COMPANIES(c) { + /* For each company, verify (while loading a scenario) that the inauguration date is the current year and set it + * accordingly if it is not the case. No need to set it on companies that are not been used already, + * thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */ + if (_file_to_saveload.filetype == FT_SCENARIO && c->inaugurated_year != MIN_YEAR) { + c->inaugurated_year = _cur_year; + } + } + + SetCachedEngineCounts(); + + /* Towns have a noise controlled number of airports system + * So each airport's noise value must be added to the town->noise_reached value + * Reset each town's noise_reached value to '0' before. */ + UpdateAirportsNoise(); + + CheckTrainsLengths(); + + return true; +} + +/** + * Signal handler used to give a user a more useful report for crashes during + * the savegame loading process; especially when there's problems with the + * NewGRFs that are required by the savegame. + * @param unused well... unused + */ +void CDECL HandleSavegameLoadCrash(int unused) +{ + char buffer[8192]; + char *p = buffer; + p += seprintf(p, lastof(buffer), + "Loading your savegame caused OpenTTD to crash.\n" + "This is most likely caused by a missing NewGRF or a NewGRF that has been\n" + "loaded as replacement for a missing NewGRF. OpenTTD cannot easily\n" + "determine whether a replacement NewGRF is of a newer or older version.\n" + "It will load a NewGRF with the same GRF ID as the missing NewGRF. This\n" + "means that if the author makes incompatible NewGRFs with the same GRF ID\n" + "OpenTTD cannot magically do the right thing. In most cases OpenTTD will\n" + "load the savegame and not crash, but this is an exception.\n" + "Please load the savegame with the appropriate NewGRFs. When loading a\n" + "savegame still crashes when all NewGRFs are found you should file a\n" + "bug report. The missing NewGRFs are:\n"); + + for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) { + if (HasBit(c->flags, GCF_COMPATIBLE)) { + char buf[40]; + md5sumToString(buf, lastof(buf), c->md5sum); + p += seprintf(p, lastof(buffer), "NewGRF %08X (%s) not found; checksum %s. Tried another NewGRF with same GRF ID\n", BSWAP32(c->grfid), c->filename, buf); + } + if (c->status == GCS_NOT_FOUND) { + char buf[40]; + md5sumToString(buf, lastof(buf), c->md5sum); + p += seprintf(p, lastof(buffer), "NewGRF %08X (%s) not found; checksum %s\n", BSWAP32(c->grfid), c->filename, buf); + } + } + + ShowInfo(buffer); +} + + +bool AfterLoadGame() +{ + typedef void (CDECL *SignalHandlerPointer)(int); + SignalHandlerPointer prev_segfault = signal(SIGSEGV, HandleSavegameLoadCrash); + SignalHandlerPointer prev_abort = signal(SIGABRT, HandleSavegameLoadCrash); + + TileIndex map_size = MapSize(); + Company *c; + + if (CheckSavegameVersion(98)) GamelogOldver(); + + GamelogTestRevision(); + GamelogTestMode(); + + if (CheckSavegameVersion(98)) GamelogGRFAddList(_grfconfig); + + /* in very old versions, size of train stations was stored differently */ + if (CheckSavegameVersion(2)) { + Station *st; + FOR_ALL_STATIONS(st) { + if (st->train_tile != 0 && st->trainst_h == 0) { + extern SavegameType _savegame_type; + uint n = _savegame_type == SGT_OTTD ? 4 : 3; // OTTD uses 4 bits per dimensions, TTD 3 bits + uint w = GB(st->trainst_w, n, n); + uint h = GB(st->trainst_w, 0, n); + + if (GetRailStationAxis(st->train_tile) != AXIS_X) Swap(w, h); + + st->trainst_w = w; + st->trainst_h = h; + + assert(GetStationIndex(st->train_tile + TileDiffXY(w - 1, h - 1)) == st->index); + } + } + } + + /* in version 2.1 of the savegame, town owner was unified. */ + if (CheckSavegameVersionOldStyle(2, 1)) ConvertTownOwner(); + + /* from version 4.1 of the savegame, exclusive rights are stored at towns */ + if (CheckSavegameVersionOldStyle(4, 1)) UpdateExclusiveRights(); + + /* from version 4.2 of the savegame, currencies are in a different order */ + if (CheckSavegameVersionOldStyle(4, 2)) UpdateCurrencies(); + + /* from version 6.1 of the savegame, signs have an "owner" */ + if (CheckSavegameVersionOldStyle(6, 1)) UpdateSignOwner(); + + /* In old version there seems to be a problem that water is owned by + * OWNER_NONE, not OWNER_WATER.. I can't replicate it for the current + * (4.3) version, so I just check when versions are older, and then + * walk through the whole map.. */ + if (CheckSavegameVersionOldStyle(4, 3)) { + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_WATER) && GetTileOwner(t) >= MAX_COMPANIES) { + SetTileOwner(t, OWNER_WATER); + } + } + } + + if (CheckSavegameVersion(84)) { + FOR_ALL_COMPANIES(c) { + c->name = CopyFromOldName(c->name_1); + if (c->name != NULL) c->name_1 = STR_SV_UNNAMED; + c->president_name = CopyFromOldName(c->president_name_1); + if (c->president_name != NULL) c->president_name_1 = SPECSTR_PRESIDENT_NAME; + } + + Station *st; + FOR_ALL_STATIONS(st) { + st->name = CopyFromOldName(st->string_id); + /* generating new name would be too much work for little effect, use the station name fallback */ + if (st->name != NULL) st->string_id = STR_SV_STNAME_FALLBACK; + } + + Town *t; + FOR_ALL_TOWNS(t) { + t->name = CopyFromOldName(t->townnametype); + if (t->name != NULL) t->townnametype = SPECSTR_TOWNNAME_START + _settings_game.game_creation.town_name; + } + + Waypoint *wp; + FOR_ALL_WAYPOINTS(wp) { + wp->name = CopyFromOldName(wp->string); + wp->string = STR_EMPTY; + } + + for (uint i = 0; i < GetSignPoolSize(); i++) { + /* invalid signs are determined by si->ower == INVALID_COMPANY now */ + Sign *si = GetSign(i); + if (!si->IsValid() && si->name != NULL) { + si->owner = OWNER_NONE; + } + } + } + + /* From this point the old names array is cleared. */ + ResetOldNames(); + + if (CheckSavegameVersion(106)) { + /* no station is determined by 'tile == INVALID_TILE' now (instead of '0') */ + Station *st; + FOR_ALL_STATIONS(st) { + if (st->airport_tile == 0) st->airport_tile = INVALID_TILE; + if (st->dock_tile == 0) st->dock_tile = INVALID_TILE; + if (st->train_tile == 0) st->train_tile = INVALID_TILE; + } + + /* the same applies to Company::location_of_HQ */ + Company *c; + FOR_ALL_COMPANIES(c) { + if (c->location_of_HQ == 0 || (CheckSavegameVersion(4) && c->location_of_HQ == 0xFFFF)) { + c->location_of_HQ = INVALID_TILE; + } + } + } + + /* convert road side to my format. */ + if (_settings_game.vehicle.road_side) _settings_game.vehicle.road_side = 1; + + /* Check if all NewGRFs are present, we are very strict in MP mode */ + GRFListCompatibility gcf_res = IsGoodGRFConfigList(); + if (_networking && gcf_res != GLC_ALL_GOOD) { + SetSaveLoadError(STR_NETWORK_ERR_CLIENT_NEWGRF_MISMATCH); + /* Restore the signals */ + signal(SIGSEGV, prev_segfault); + signal(SIGABRT, prev_abort); + return false; + } + + switch (gcf_res) { + case GLC_COMPATIBLE: _switch_mode_errorstr = STR_NEWGRF_COMPATIBLE_LOAD_WARNING; break; + case GLC_NOT_FOUND: _switch_mode_errorstr = STR_NEWGRF_DISABLED_WARNING; _pause_game = -1; break; + default: break; + } + + /* Update current year + * must be done before loading sprites as some newgrfs check it */ + SetDate(_date); + + /* Force dynamic engines off when loading older savegames */ + if (CheckSavegameVersion(95)) _settings_game.vehicle.dynamic_engines = 0; + + /* Load the sprites */ + GfxLoadSprites(); + LoadStringWidthTable(); + + /* Copy temporary data to Engine pool */ + CopyTempEngineData(); + + /* Connect front and rear engines of multiheaded trains and converts + * subtype to the new format */ + if (CheckSavegameVersionOldStyle(17, 1)) ConvertOldMultiheadToNew(); + + /* Connect front and rear engines of multiheaded trains */ + ConnectMultiheadedTrains(); + + /* reinit the landscape variables (landscape might have changed) */ + InitializeLandscapeVariables(true); + + /* Update all vehicles */ + AfterLoadVehicles(true); + + /* Update all waypoints */ + if (CheckSavegameVersion(12)) FixOldWaypoints(); + + /* in version 2.2 of the savegame, we have new airports */ + if (CheckSavegameVersionOldStyle(2, 2)) UpdateOldAircraft(); + + AfterLoadTown(); + + /* make sure there is a town in the game */ + if (_game_mode == GM_NORMAL && !ClosestTownFromTile(0, UINT_MAX)) { + SetSaveLoadError(STR_NO_TOWN_IN_SCENARIO); + /* Restore the signals */ + signal(SIGSEGV, prev_segfault); + signal(SIGABRT, prev_abort); + return false; + } + + /* The void tiles on the southern border used to belong to a wrong class (pre 4.3). + * This problem appears in savegame version 21 too, see r3455. But after loading the + * savegame and saving again, the buggy map array could be converted to new savegame + * version. It didn't show up before r12070. */ + if (CheckSavegameVersion(87)) UpdateVoidTiles(); + + /* If Load Scenario / New (Scenario) Game is used, + * a company does not exist yet. So create one here. + * 1 exeption: network-games. Those can have 0 companies + * But this exeption is not true for non dedicated network_servers! */ + if (!IsValidCompanyID(COMPANY_FIRST) && (!_networking || (_networking && _network_server && !_network_dedicated))) + DoStartupNewCompany(false); + + if (CheckSavegameVersion(72)) { + /* Locks/shiplifts in very old savegames had OWNER_WATER as owner */ + for (TileIndex t = 0; t < MapSize(); t++) { + switch (GetTileType(t)) { + default: break; + + case MP_WATER: + if (GetWaterTileType(t) == WATER_TILE_LOCK && GetTileOwner(t) == OWNER_WATER) SetTileOwner(t, OWNER_NONE); + break; + + case MP_STATION: { + if (HasBit(_m[t].m6, 3)) SetBit(_m[t].m6, 2); + StationGfx gfx = GetStationGfx(t); + StationType st; + if ( IsInsideMM(gfx, 0, 8)) { // Railway station + st = STATION_RAIL; + SetStationGfx(t, gfx - 0); + } else if (IsInsideMM(gfx, 8, 67)) { // Airport + st = STATION_AIRPORT; + SetStationGfx(t, gfx - 8); + } else if (IsInsideMM(gfx, 67, 71)) { // Truck + st = STATION_TRUCK; + SetStationGfx(t, gfx - 67); + } else if (IsInsideMM(gfx, 71, 75)) { // Bus + st = STATION_BUS; + SetStationGfx(t, gfx - 71); + } else if (gfx == 75) { // Oil rig + st = STATION_OILRIG; + SetStationGfx(t, gfx - 75); + } else if (IsInsideMM(gfx, 76, 82)) { // Dock + st = STATION_DOCK; + SetStationGfx(t, gfx - 76); + } else if (gfx == 82) { // Buoy + st = STATION_BUOY; + SetStationGfx(t, gfx - 82); + } else if (IsInsideMM(gfx, 83, 168)) { // Extended airport + st = STATION_AIRPORT; + SetStationGfx(t, gfx - 83 + 67 - 8); + } else if (IsInsideMM(gfx, 168, 170)) { // Drive through truck + st = STATION_TRUCK; + SetStationGfx(t, gfx - 168 + GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET); + } else if (IsInsideMM(gfx, 170, 172)) { // Drive through bus + st = STATION_BUS; + SetStationGfx(t, gfx - 170 + GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET); + } else { + /* Restore the signals */ + signal(SIGSEGV, prev_segfault); + signal(SIGABRT, prev_abort); + return false; + } + SB(_m[t].m6, 3, 3, st); + } break; + } + } + } + + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_STATION: { + Station *st = GetStationByTile(t); + + /* Set up station spread; buoys do not have one */ + if (!IsBuoy(t)) st->rect.BeforeAddTile(t, StationRect::ADD_FORCE); + + switch (GetStationType(t)) { + case STATION_TRUCK: + case STATION_BUS: + if (CheckSavegameVersion(6)) { + /* From this version on there can be multiple road stops of the + * same type per station. Convert the existing stops to the new + * internal data structure. */ + RoadStop *rs = new RoadStop(t); + if (rs == NULL) error("Too many road stops in savegame"); + + RoadStop **head = + IsTruckStop(t) ? &st->truck_stops : &st->bus_stops; + *head = rs; + } + break; + + case STATION_OILRIG: { + /* Very old savegames sometimes have phantom oil rigs, i.e. + * an oil rig which got shut down, but not completly removed from + * the map + */ + TileIndex t1 = TILE_ADDXY(t, 0, 1); + if (IsTileType(t1, MP_INDUSTRY) && + GetIndustryGfx(t1) == GFX_OILRIG_1) { + /* The internal encoding of oil rigs was changed twice. + * It was 3 (till 2.2) and later 5 (till 5.1). + * Setting it unconditionally does not hurt. + */ + GetStationByTile(t)->airport_type = AT_OILRIG; + } else { + DeleteOilRig(t); + } + break; + } + + default: break; + } + break; + } + + default: break; + } + } + + /* In version 6.1 we put the town index in the map-array. To do this, we need + * to use m2 (16bit big), so we need to clean m2, and that is where this is + * all about ;) */ + if (CheckSavegameVersionOldStyle(6, 1)) { + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_HOUSE: + _m[t].m4 = _m[t].m2; + SetTownIndex(t, CalcClosestTownFromTile(t, UINT_MAX)->index); + break; + + case MP_ROAD: + _m[t].m4 |= (_m[t].m2 << 4); + if ((GB(_m[t].m5, 4, 2) == ROAD_TILE_CROSSING ? (Owner)_m[t].m3 : GetTileOwner(t)) == OWNER_TOWN) { + SetTownIndex(t, CalcClosestTownFromTile(t, UINT_MAX)->index); + } else { + SetTownIndex(t, 0); + } + break; + + default: break; + } + } + } + + /* From version 9.0, we update the max passengers of a town (was sometimes negative + * before that. */ + if (CheckSavegameVersion(9)) { + Town *t; + FOR_ALL_TOWNS(t) UpdateTownMaxPass(t); + } + + /* From version 16.0, we included autorenew on engines, which are now saved, but + * of course, we do need to initialize them for older savegames. */ + if (CheckSavegameVersion(16)) { + FOR_ALL_COMPANIES(c) { + c->engine_renew_list = NULL; + c->engine_renew = false; + c->engine_renew_months = -6; + c->engine_renew_money = 100000; + } + + /* When loading a game, _local_company is not yet set to the correct value. + * However, in a dedicated server we are a spectator, so nothing needs to + * happen. In case we are not a dedicated server, the local company always + * becomes company 0, unless we are in the scenario editor where all the + * companies are 'invalid'. + */ + if (!_network_dedicated && IsValidCompanyID(COMPANY_FIRST)) { + c = GetCompany(COMPANY_FIRST); + c->engine_renew = _settings_client.gui.autorenew; + c->engine_renew_months = _settings_client.gui.autorenew_months; + c->engine_renew_money = _settings_client.gui.autorenew_money; + } + } + + if (CheckSavegameVersion(48)) { + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_RAILWAY: + if (IsPlainRailTile(t)) { + /* Swap ground type and signal type for plain rail tiles, so the + * ground type uses the same bits as for depots and waypoints. */ + uint tmp = GB(_m[t].m4, 0, 4); + SB(_m[t].m4, 0, 4, GB(_m[t].m2, 0, 4)); + SB(_m[t].m2, 0, 4, tmp); + } else if (HasBit(_m[t].m5, 2)) { + /* Split waypoint and depot rail type and remove the subtype. */ + ClrBit(_m[t].m5, 2); + ClrBit(_m[t].m5, 6); + } + break; + + case MP_ROAD: + /* Swap m3 and m4, so the track type for rail crossings is the + * same as for normal rail. */ + Swap(_m[t].m3, _m[t].m4); + break; + + default: break; + } + } + } + + if (CheckSavegameVersion(61)) { + /* Added the RoadType */ + bool old_bridge = CheckSavegameVersion(42); + for (TileIndex t = 0; t < map_size; t++) { + switch(GetTileType(t)) { + case MP_ROAD: + SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2)); + switch (GetRoadTileType(t)) { + default: NOT_REACHED(); + case ROAD_TILE_NORMAL: + SB(_m[t].m4, 0, 4, GB(_m[t].m5, 0, 4)); + SB(_m[t].m4, 4, 4, 0); + SB(_m[t].m6, 2, 4, 0); + break; + case ROAD_TILE_CROSSING: + SB(_m[t].m4, 5, 2, GB(_m[t].m5, 2, 2)); + break; + case ROAD_TILE_DEPOT: break; + } + SetRoadTypes(t, ROADTYPES_ROAD); + break; + + case MP_STATION: + if (IsRoadStop(t)) SetRoadTypes(t, ROADTYPES_ROAD); + break; + + case MP_TUNNELBRIDGE: + /* Middle part of "old" bridges */ + if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break; + if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { + SetRoadTypes(t, ROADTYPES_ROAD); + } + break; + + default: break; + } + } + } + + if (CheckSavegameVersion(42)) { + Vehicle* v; + + for (TileIndex t = 0; t < map_size; t++) { + if (MayHaveBridgeAbove(t)) ClearBridgeMiddle(t); + if (IsBridgeTile(t)) { + if (HasBit(_m[t].m5, 6)) { // middle part + Axis axis = (Axis)GB(_m[t].m5, 0, 1); + + if (HasBit(_m[t].m5, 5)) { // transport route under bridge? + if (GB(_m[t].m5, 3, 2) == TRANSPORT_RAIL) { + MakeRailNormal( + t, + GetTileOwner(t), + axis == AXIS_X ? TRACK_BIT_Y : TRACK_BIT_X, + GetRailType(t) + ); + } else { + TownID town = IsTileOwner(t, OWNER_TOWN) ? ClosestTownFromTile(t, UINT_MAX)->index : 0; + + MakeRoadNormal( + t, + axis == AXIS_X ? ROAD_Y : ROAD_X, + ROADTYPES_ROAD, + town, + GetTileOwner(t), OWNER_NONE, OWNER_NONE + ); + } + } else { + if (GB(_m[t].m5, 3, 2) == 0) { + MakeClear(t, CLEAR_GRASS, 3); + } else { + if (GetTileSlope(t, NULL) != SLOPE_FLAT) { + MakeShore(t); + } else { + if (GetTileOwner(t) == OWNER_WATER) { + MakeWater(t); + } else { + MakeCanal(t, GetTileOwner(t), Random()); + } + } + } + } + SetBridgeMiddle(t, axis); + } else { // ramp + Axis axis = (Axis)GB(_m[t].m5, 0, 1); + uint north_south = GB(_m[t].m5, 5, 1); + DiagDirection dir = ReverseDiagDir(XYNSToDiagDir(axis, north_south)); + TransportType type = (TransportType)GB(_m[t].m5, 1, 2); + + _m[t].m5 = 1 << 7 | type << 2 | dir; + } + } + } + + FOR_ALL_VEHICLES(v) { + if (v->type != VEH_TRAIN && v->type != VEH_ROAD) continue; + if (IsBridgeTile(v->tile)) { + DiagDirection dir = GetTunnelBridgeDirection(v->tile); + + if (dir != DirToDiagDir(v->direction)) continue; + switch (dir) { + default: NOT_REACHED(); + case DIAGDIR_NE: if ((v->x_pos & 0xF) != 0) continue; break; + case DIAGDIR_SE: if ((v->y_pos & 0xF) != TILE_SIZE - 1) continue; break; + case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break; + case DIAGDIR_NW: if ((v->y_pos & 0xF) != 0) continue; break; + } + } else if (v->z_pos > GetSlopeZ(v->x_pos, v->y_pos)) { + v->tile = GetNorthernBridgeEnd(v->tile); + } else { + continue; + } + if (v->type == VEH_TRAIN) { + v->u.rail.track = TRACK_BIT_WORMHOLE; + } else { + v->u.road.state = RVSB_WORMHOLE; + } + } + } + + /* Elrails got added in rev 24 */ + if (CheckSavegameVersion(24)) { + Vehicle *v; + RailType min_rail = RAILTYPE_ELECTRIC; + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN) { + RailType rt = RailVehInfo(v->engine_type)->railtype; + + v->u.rail.railtype = rt; + if (rt == RAILTYPE_ELECTRIC) min_rail = RAILTYPE_RAIL; + } + } + + /* .. so we convert the entire map from normal to elrail (so maintain "fairness") */ + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_RAILWAY: + SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); + break; + + case MP_ROAD: + if (IsLevelCrossing(t)) { + SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); + } + break; + + case MP_STATION: + if (IsRailwayStation(t)) { + SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); + } + break; + + case MP_TUNNELBRIDGE: + if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) { + SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); + } + break; + + default: + break; + } + } + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) TrainConsistChanged(v, true); + } + + } + + /* In version 16.1 of the savegame a company can decide if trains, which get + * replaced, shall keep their old length. In all prior versions, just default + * to false */ + if (CheckSavegameVersionOldStyle(16, 1)) { + FOR_ALL_COMPANIES(c) c->renew_keep_length = false; + } + + /* In version 17, ground type is moved from m2 to m4 for depots and + * waypoints to make way for storing the index in m2. The custom graphics + * id which was stored in m4 is now saved as a grf/id reference in the + * waypoint struct. */ + if (CheckSavegameVersion(17)) { + Waypoint *wp; + + FOR_ALL_WAYPOINTS(wp) { + if (wp->deleted == 0) { + const StationSpec *statspec = NULL; + + if (HasBit(_m[wp->xy].m3, 4)) + statspec = GetCustomStationSpec(STAT_CLASS_WAYP, _m[wp->xy].m4 + 1); + + if (statspec != NULL) { + wp->stat_id = _m[wp->xy].m4 + 1; + wp->grfid = statspec->grffile->grfid; + wp->localidx = statspec->localidx; + } else { + /* No custom graphics set, so set to default. */ + wp->stat_id = 0; + wp->grfid = 0; + wp->localidx = 0; + } + + /* Move ground type bits from m2 to m4. */ + _m[wp->xy].m4 = GB(_m[wp->xy].m2, 0, 4); + /* Store waypoint index in the tile. */ + _m[wp->xy].m2 = wp->index; + } + } + } else { + /* As of version 17, we recalculate the custom graphic ID of waypoints + * from the GRF ID / station index. */ + AfterLoadWaypoints(); + } + + /* From version 15, we moved a semaphore bit from bit 2 to bit 3 in m4, making + * room for PBS. Now in version 21 move it back :P. */ + if (CheckSavegameVersion(21) && !CheckSavegameVersion(15)) { + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_RAILWAY: + if (HasSignals(t)) { + /* convert PBS signals to combo-signals */ + if (HasBit(_m[t].m2, 2)) SetSignalType(t, TRACK_X, SIGTYPE_COMBO); + + /* move the signal variant back */ + SetSignalVariant(t, TRACK_X, HasBit(_m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); + ClrBit(_m[t].m2, 3); + } + + /* Clear PBS reservation on track */ + if (!IsRailDepotTile(t)) { + SB(_m[t].m4, 4, 4, 0); + } else { + ClrBit(_m[t].m3, 6); + } + break; + + case MP_ROAD: /* Clear PBS reservation on crossing */ + if (IsLevelCrossing(t)) ClrBit(_m[t].m5, 0); + break; + + case MP_STATION: /* Clear PBS reservation on station */ + ClrBit(_m[t].m3, 6); + break; + + default: break; + } + } + } + + if (CheckSavegameVersion(25)) { + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_ROAD) { + v->vehstatus &= ~0x40; + v->u.road.slot = NULL; + v->u.road.slot_age = 0; + } + } + } else { + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_ROAD && v->u.road.slot != NULL) v->u.road.slot->num_vehicles++; + } + } + + if (CheckSavegameVersion(26)) { + Station *st; + FOR_ALL_STATIONS(st) { + st->last_vehicle_type = VEH_INVALID; + } + } + + YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK); + + if (CheckSavegameVersion(34)) FOR_ALL_COMPANIES(c) ResetCompanyLivery(c); + + FOR_ALL_COMPANIES(c) { + c->avail_railtypes = GetCompanyRailtypes(c->index); + c->avail_roadtypes = GetCompanyRoadtypes(c->index); + } + + if (!CheckSavegameVersion(27)) AfterLoadStations(); + + /* Time starts at 0 instead of 1920. + * Account for this in older games by adding an offset */ + if (CheckSavegameVersion(31)) { + Station *st; + Waypoint *wp; + Engine *e; + Industry *i; + Vehicle *v; + + _date += DAYS_TILL_ORIGINAL_BASE_YEAR; + _cur_year += ORIGINAL_BASE_YEAR; + + FOR_ALL_STATIONS(st) st->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR; + FOR_ALL_WAYPOINTS(wp) wp->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR; + FOR_ALL_ENGINES(e) e->intro_date += DAYS_TILL_ORIGINAL_BASE_YEAR; + FOR_ALL_COMPANIES(c) c->inaugurated_year += ORIGINAL_BASE_YEAR; + FOR_ALL_INDUSTRIES(i) i->last_prod_year += ORIGINAL_BASE_YEAR; + + FOR_ALL_VEHICLES(v) { + v->date_of_last_service += DAYS_TILL_ORIGINAL_BASE_YEAR; + v->build_year += ORIGINAL_BASE_YEAR; + } + } + + /* From 32 on we save the industry who made the farmland. + * To give this prettyness to old savegames, we remove all farmfields and + * plant new ones. */ + if (CheckSavegameVersion(32)) { + Industry *i; + + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) { + /* remove fields */ + MakeClear(t, CLEAR_GRASS, 3); + } else if (IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)) { + /* remove fences around fields */ + SetFenceSE(t, 0); + SetFenceSW(t, 0); + } + } + + FOR_ALL_INDUSTRIES(i) { + uint j; + + if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_ON_BUILT) { + for (j = 0; j != 50; j++) PlantRandomFarmField(i); + } + } + } + + /* Setting no refit flags to all orders in savegames from before refit in orders were added */ + if (CheckSavegameVersion(36)) { + Order *order; + Vehicle *v; + + FOR_ALL_ORDERS(order) { + order->SetRefit(CT_NO_REFIT); + } + + FOR_ALL_VEHICLES(v) { + v->current_order.SetRefit(CT_NO_REFIT); + } + } + + /* from version 38 we have optional elrails, since we cannot know the + * preference of a user, let elrails enabled; it can be disabled manually */ + if (CheckSavegameVersion(38)) _settings_game.vehicle.disable_elrails = false; + /* do the same as when elrails were enabled/disabled manually just now */ + SettingsDisableElrail(_settings_game.vehicle.disable_elrails); + InitializeRailGUI(); + + /* From version 53, the map array was changed for house tiles to allow + * space for newhouses grf features. A new byte, m7, was also added. */ + if (CheckSavegameVersion(53)) { + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_HOUSE)) { + if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { + /* Move the construction stage from m3[7..6] to m5[5..4]. + * The construction counter does not have to move. */ + SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2)); + SB(_m[t].m3, 6, 2, 0); + + /* The "house is completed" bit is now in m6[2]. */ + SetHouseCompleted(t, false); + } else { + /* The "lift has destination" bit has been moved from + * m5[7] to m7[0]. */ + SB(_me[t].m7, 0, 1, HasBit(_m[t].m5, 7)); + ClrBit(_m[t].m5, 7); + + /* The "lift is moving" bit has been removed, as it does + * the same job as the "lift has destination" bit. */ + ClrBit(_m[t].m1, 7); + + /* The position of the lift goes from m1[7..0] to m6[7..2], + * making m1 totally free, now. The lift position does not + * have to be a full byte since the maximum value is 36. */ + SetLiftPosition(t, GB(_m[t].m1, 0, 6 )); + + _m[t].m1 = 0; + _m[t].m3 = 0; + SetHouseCompleted(t, true); + } + } + } + } + + /* Check and update house and town values */ + UpdateHousesAndTowns(); + + if (CheckSavegameVersion(43)) { + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_INDUSTRY)) { + switch (GetIndustryGfx(t)) { + case GFX_POWERPLANT_SPARKS: + SetIndustryAnimationState(t, GB(_m[t].m1, 2, 5)); + break; + + case GFX_OILWELL_ANIMATED_1: + case GFX_OILWELL_ANIMATED_2: + case GFX_OILWELL_ANIMATED_3: + SetIndustryAnimationState(t, GB(_m[t].m1, 0, 2)); + break; + + case GFX_COAL_MINE_TOWER_ANIMATED: + case GFX_COPPER_MINE_TOWER_ANIMATED: + case GFX_GOLD_MINE_TOWER_ANIMATED: + SetIndustryAnimationState(t, _m[t].m1); + break; + + default: /* No animation states to change */ + break; + } + } + } + } + + if (CheckSavegameVersion(44)) { + Vehicle *v; + /* If we remove a station while cargo from it is still enroute, payment calculation will assume + * 0, 0 to be the source of the cargo, resulting in very high payments usually. v->source_xy + * stores the coordinates, preserving them even if the station is removed. However, if a game is loaded + * where this situation exists, the cargo-source information is lost. in this case, we set the source + * to the current tile of the vehicle to prevent excessive profits + */ + FOR_ALL_VEHICLES(v) { + const CargoList::List *packets = v->cargo.Packets(); + for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { + CargoPacket *cp = *it; + cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : v->tile; + cp->loaded_at_xy = cp->source_xy; + } + v->cargo.InvalidateCache(); + } + + /* Store position of the station where the goods come from, so there + * are no very high payments when stations get removed. However, if the + * station where the goods came from is already removed, the source + * information is lost. In that case we set it to the position of this + * station */ + Station *st; + FOR_ALL_STATIONS(st) { + for (CargoID c = 0; c < NUM_CARGO; c++) { + GoodsEntry *ge = &st->goods[c]; + + const CargoList::List *packets = ge->cargo.Packets(); + for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { + CargoPacket *cp = *it; + cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : st->xy; + cp->loaded_at_xy = cp->source_xy; + } + } + } + } + + if (CheckSavegameVersion(45)) { + Vehicle *v; + /* Originally just the fact that some cargo had been paid for was + * stored to stop people cheating and cashing in several times. This + * wasn't enough though as it was cleared when the vehicle started + * loading again, even if it didn't actually load anything, so now the + * amount of cargo that has been paid for is stored. */ + FOR_ALL_VEHICLES(v) { + const CargoList::List *packets = v->cargo.Packets(); + for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { + CargoPacket *cp = *it; + cp->paid_for = HasBit(v->vehicle_flags, 2); + } + ClrBit(v->vehicle_flags, 2); + v->cargo.InvalidateCache(); + } + } + + /* Buoys do now store the owner of the previous water tile, which can never + * be OWNER_NONE. So replace OWNER_NONE with OWNER_WATER. */ + if (CheckSavegameVersion(46)) { + Station *st; + FOR_ALL_STATIONS(st) { + if (st->IsBuoy() && IsTileOwner(st->xy, OWNER_NONE) && TileHeight(st->xy) == 0) SetTileOwner(st->xy, OWNER_WATER); + } + } + + if (CheckSavegameVersion(50)) { + Vehicle *v; + /* Aircraft units changed from 8 mph to 1 km/h */ + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_AIRCRAFT && v->subtype <= AIR_AIRCRAFT) { + const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type); + v->cur_speed *= 129; + v->cur_speed /= 10; + v->max_speed = avi->max_speed; + v->acceleration = avi->acceleration; + } + } + } + + if (CheckSavegameVersion(49)) FOR_ALL_COMPANIES(c) c->face = ConvertFromOldCompanyManagerFace(c->face); + + if (CheckSavegameVersion(52)) { + for (TileIndex t = 0; t < map_size; t++) { + if (IsStatueTile(t)) { + _m[t].m2 = CalcClosestTownFromTile(t, UINT_MAX)->index; + } + } + } + + /* A patch option containing the proportion of towns that grow twice as + * fast was added in version 54. From version 56 this is now saved in the + * town as cities can be built specifically in the scenario editor. */ + if (CheckSavegameVersion(56)) { + Town *t; + + FOR_ALL_TOWNS(t) { + if (_settings_game.economy.larger_towns != 0 && (t->index % _settings_game.economy.larger_towns) == 0) { + t->larger_town = true; + } + } + } + + if (CheckSavegameVersion(57)) { + Vehicle *v; + /* Added a FIFO queue of vehicles loading at stations */ + FOR_ALL_VEHICLES(v) { + if ((v->type != VEH_TRAIN || IsFrontEngine(v)) && // for all locs + !(v->vehstatus & (VS_STOPPED | VS_CRASHED)) && // not stopped or crashed + v->current_order.IsType(OT_LOADING)) { // loading + GetStation(v->last_station_visited)->loading_vehicles.push_back(v); + + /* The loading finished flag is *only* set when actually completely + * finished. Because the vehicle is loading, it is not finished. */ + ClrBit(v->vehicle_flags, VF_LOADING_FINISHED); + } + } + } else if (CheckSavegameVersion(59)) { + /* For some reason non-loading vehicles could be in the station's loading vehicle list */ + + Station *st; + FOR_ALL_STATIONS(st) { + std::list::iterator iter; + for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end();) { + Vehicle *v = *iter; + iter++; + if (!v->current_order.IsType(OT_LOADING)) st->loading_vehicles.remove(v); + } + } + } + + if (CheckSavegameVersion(58)) { + /* patch difficulty number_industries other than zero get bumped to +1 + * since a new option (very low at position1) has been added */ + if (_settings_game.difficulty.number_industries > 0) { + _settings_game.difficulty.number_industries++; + } + + /* Same goes for number of towns, although no test is needed, just an increment */ + _settings_game.difficulty.number_towns++; + } + + if (CheckSavegameVersion(64)) { + /* copy the signal type/variant and move signal states bits */ + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_RAILWAY) && HasSignals(t)) { + SetSignalStates(t, GB(_m[t].m2, 4, 4)); + SetSignalVariant(t, INVALID_TRACK, GetSignalVariant(t, TRACK_X)); + SetSignalType(t, INVALID_TRACK, GetSignalType(t, TRACK_X)); + ClrBit(_m[t].m2, 7); + } + } + } + + if (CheckSavegameVersion(69)) { + /* In some old savegames a bit was cleared when it should not be cleared */ + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_ROAD && (v->u.road.state == 250 || v->u.road.state == 251)) { + SetBit(v->u.road.state, RVS_IS_STOPPING); + } + } + } + + if (CheckSavegameVersion(70)) { + /* Added variables to support newindustries */ + Industry *i; + FOR_ALL_INDUSTRIES(i) i->founder = OWNER_NONE; + } + + /* From version 82, old style canals (above sealevel (0), WATER owner) are no longer supported. + Replace the owner for those by OWNER_NONE. */ + if (CheckSavegameVersion(82)) { + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_WATER) && + GetWaterTileType(t) == WATER_TILE_CLEAR && + GetTileOwner(t) == OWNER_WATER && + TileHeight(t) != 0) { + SetTileOwner(t, OWNER_NONE); + } + } + } + + /* + * Add the 'previous' owner to the ship depots so we can reset it with + * the correct values when it gets destroyed. This prevents that + * someone can remove canals owned by somebody else and it prevents + * making floods using the removal of ship depots. + */ + if (CheckSavegameVersion(83)) { + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_WATER) && IsShipDepot(t)) { + _m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; + } + } + } + + if (CheckSavegameVersion(74)) { + Station *st; + FOR_ALL_STATIONS(st) { + for (CargoID c = 0; c < NUM_CARGO; c++) { + st->goods[c].last_speed = 0; + if (st->goods[c].cargo.Count() != 0) SetBit(st->goods[c].acceptance_pickup, GoodsEntry::PICKUP); + } + } + } + + if (CheckSavegameVersion(78)) { + Industry *i; + uint j; + FOR_ALL_INDUSTRIES(i) { + const IndustrySpec *indsp = GetIndustrySpec(i->type); + for (j = 0; j < lengthof(i->produced_cargo); j++) { + i->produced_cargo[j] = indsp->produced_cargo[j]; + } + for (j = 0; j < lengthof(i->accepts_cargo); j++) { + i->accepts_cargo[j] = indsp->accepts_cargo[j]; + } + } + } + + /* Before version 81, the density of grass was always stored as zero, and + * grassy trees were always drawn fully grassy. Furthermore, trees on rough + * land used to have zero density, now they have full density. Therefore, + * make all grassy/rough land trees have a density of 3. */ + if (CheckSavegameVersion(81)) { + for (TileIndex t = 0; t < map_size; t++) { + if (GetTileType(t) == MP_TREES) { + TreeGround groundType = GetTreeGround(t); + if (groundType != TREE_GROUND_SNOW_DESERT) SetTreeGroundDensity(t, groundType, 3); + } + } + } + + + if (CheckSavegameVersion(93)) { + /* Rework of orders. */ + Order *order; + FOR_ALL_ORDERS(order) order->ConvertFromOldSavegame(); + + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->orders.list != NULL && v->orders.list->GetFirstOrder() != NULL && !v->orders.list->GetFirstOrder()->IsValid()) { + v->orders.list->FreeChain(); + v->orders.list = NULL; + } + + v->current_order.ConvertFromOldSavegame(); + if (v->type == VEH_ROAD && v->IsPrimaryVehicle() && v->FirstShared() == v) { + FOR_VEHICLE_ORDERS(v, order) order->SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS); + } + } + } else if (CheckSavegameVersion(94)) { + /* Unload and transfer are now mutual exclusive. */ + Order *order; + FOR_ALL_ORDERS(order) { + if ((order->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) { + order->SetUnloadType(OUFB_TRANSFER); + order->SetLoadType(OLFB_NO_LOAD); + } + } + + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if ((v->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) { + v->current_order.SetUnloadType(OUFB_TRANSFER); + v->current_order.SetLoadType(OLFB_NO_LOAD); + } + } + } + + if (CheckSavegameVersion(84)) { + /* Update go to buoy orders because they are just waypoints */ + Order *order; + FOR_ALL_ORDERS(order) { + if (order->IsType(OT_GOTO_STATION) && GetStation(order->GetDestination())->IsBuoy()) { + order->SetLoadType(OLF_LOAD_IF_POSSIBLE); + order->SetUnloadType(OUF_UNLOAD_IF_POSSIBLE); + } + } + + /* Set all share owners to INVALID_COMPANY for + * 1) all inactive companies + * (when inactive companies were stored in the savegame - TTD, TTDP and some + * *really* old revisions of OTTD; else it is already set in InitializeCompanies()) + * 2) shares that are owned by inactive companies or self + * (caused by cheating clients in earlier revisions) */ + FOR_ALL_COMPANIES(c) { + for (uint i = 0; i < 4; i++) { + CompanyID company = c->share_owners[i]; + if (company == INVALID_COMPANY) continue; + if (!IsValidCompanyID(company) || company == c->index) c->share_owners[i] = INVALID_COMPANY; + } + } + } + + if (CheckSavegameVersion(86)) { + for (TileIndex t = 0; t < map_size; t++) { + /* Move river flag and update canals to use water class */ + if (IsTileType(t, MP_WATER)) { + if (GetWaterClass(t) != WATER_CLASS_RIVER) { + if (IsWater(t)) { + Owner o = GetTileOwner(t); + if (o == OWNER_WATER) { + MakeWater(t); + } else { + MakeCanal(t, o, Random()); + } + } else if (IsShipDepot(t)) { + Owner o = (Owner)_m[t].m4; // Original water owner + SetWaterClass(t, o == OWNER_WATER ? WATER_CLASS_SEA : WATER_CLASS_CANAL); + } + } + } + } + + /* Update locks, depots, docks and buoys to have a water class based + * on its neighbouring tiles. Done after river and canal updates to + * ensure neighbours are correct. */ + for (TileIndex t = 0; t < map_size; t++) { + if (GetTileSlope(t, NULL) != SLOPE_FLAT) continue; + + if (IsTileType(t, MP_WATER) && IsLock(t)) SetWaterClassDependingOnSurroundings(t, false); + if (IsTileType(t, MP_STATION) && (IsDock(t) || IsBuoy(t))) SetWaterClassDependingOnSurroundings(t, false); + } + } + + if (CheckSavegameVersion(87)) { + for (TileIndex t = 0; t < map_size; t++) { + /* skip oil rigs at borders! */ + if ((IsTileType(t, MP_WATER) || IsBuoyTile(t)) && + (TileX(t) == 0 || TileY(t) == 0 || TileX(t) == MapMaxX() - 1 || TileY(t) == MapMaxY() - 1)) { + /* Some version 86 savegames have wrong water class at map borders (under buoy, or after removing buoy). + * This conversion has to be done before buoys with invalid owner are removed. */ + SetWaterClass(t, WATER_CLASS_SEA); + } + + if (IsBuoyTile(t) || IsDriveThroughStopTile(t) || IsTileType(t, MP_WATER)) { + Owner o = GetTileOwner(t); + if (o < MAX_COMPANIES && !IsValidCompanyID(o)) { + _current_company = o; + ChangeTileOwner(t, o, INVALID_OWNER); + } + if (IsBuoyTile(t)) { + /* reset buoy owner to OWNER_NONE in the station struct + * (even if it is owned by active company) */ + GetStationByTile(t)->owner = OWNER_NONE; + } + } else if (IsTileType(t, MP_ROAD)) { + /* works for all RoadTileType */ + for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) { + /* update even non-existing road types to update tile owner too */ + Owner o = GetRoadOwner(t, rt); + if (o < MAX_COMPANIES && !IsValidCompanyID(o)) SetRoadOwner(t, rt, OWNER_NONE); + } + if (IsLevelCrossing(t)) { + Owner o = GetTileOwner(t); + if (!IsValidCompanyID(o)) { + /* remove leftover rail piece from crossing (from very old savegames) */ + _current_company = o; + DoCommand(t, 0, GetCrossingRailTrack(t), DC_EXEC | DC_BANKRUPT, CMD_REMOVE_SINGLE_RAIL); + } + } + } + } + + /* Convert old PF settings to new */ + if (_settings_game.pf.yapf.rail_use_yapf || CheckSavegameVersion(28)) { + _settings_game.pf.pathfinder_for_trains = VPF_YAPF; + } else { + _settings_game.pf.pathfinder_for_trains = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_NTP); + } + + if (_settings_game.pf.yapf.road_use_yapf || CheckSavegameVersion(28)) { + _settings_game.pf.pathfinder_for_roadvehs = VPF_YAPF; + } else { + _settings_game.pf.pathfinder_for_roadvehs = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_OPF); + } + + if (_settings_game.pf.yapf.ship_use_yapf) { + _settings_game.pf.pathfinder_for_ships = VPF_YAPF; + } else { + _settings_game.pf.pathfinder_for_ships = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_OPF); + } + } + + if (CheckSavegameVersion(88)) { + /* Profits are now with 8 bit fract */ + Vehicle *v; + FOR_ALL_VEHICLES(v) { + v->profit_this_year <<= 8; + v->profit_last_year <<= 8; + v->running_ticks = 0; + } + } + + if (CheckSavegameVersion(91)) { + /* Increase HouseAnimationFrame from 5 to 7 bits */ + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_HOUSE) && GetHouseType(t) >= NEW_HOUSE_OFFSET) { + SetHouseAnimationFrame(t, GB(_m[t].m6, 3, 5)); + } + } + } + + if (CheckSavegameVersion(62)) { + /* Remove all trams from savegames without tram support. + * There would be trams without tram track under causing crashes sooner or later. */ + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_ROAD && v->First() == v && + HasBit(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM)) { + if (_switch_mode_errorstr == INVALID_STRING_ID || _switch_mode_errorstr == STR_NEWGRF_COMPATIBLE_LOAD_WARNING) { + _switch_mode_errorstr = STR_LOADGAME_REMOVED_TRAMS; + } + delete v; + } + } + } + + if (CheckSavegameVersion(99)) { + for (TileIndex t = 0; t < map_size; t++) { + /* Set newly introduced WaterClass of industry tiles */ + if (IsTileType(t, MP_STATION) && IsOilRig(t)) { + SetWaterClassDependingOnSurroundings(t, true); + } + if (IsTileType(t, MP_INDUSTRY)) { + if ((GetIndustrySpec(GetIndustryType(t))->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0) { + SetWaterClassDependingOnSurroundings(t, true); + } else { + SetWaterClass(t, WATER_CLASS_INVALID); + } + } + + /* Replace "house construction year" with "house age" */ + if (IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)) { + _m[t].m5 = Clamp(_cur_year - (_m[t].m5 + ORIGINAL_BASE_YEAR), 0, 0xFF); + } + } + } + + /* Move the signal variant back up one bit for PBS. We don't convert the old PBS + * format here, as an old layout wouldn't work properly anyway. To be safe, we + * clear any possible PBS reservations as well. */ + if (CheckSavegameVersion(100)) { + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_RAILWAY: + if (HasSignals(t)) { + /* move the signal variant */ + SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); + SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); + ClrBit(_m[t].m2, 2); + ClrBit(_m[t].m2, 6); + } + + /* Clear PBS reservation on track */ + if (IsRailDepot(t) ||IsRailWaypoint(t)) { + SetDepotWaypointReservation(t, false); + } else { + SetTrackReservation(t, TRACK_BIT_NONE); + } + break; + + case MP_ROAD: /* Clear PBS reservation on crossing */ + if (IsLevelCrossing(t)) SetCrossingReservation(t, false); + break; + + case MP_STATION: /* Clear PBS reservation on station */ + if (IsRailwayStation(t)) SetRailwayStationReservation(t, false); + break; + + case MP_TUNNELBRIDGE: /* Clear PBS reservation on tunnels/birdges */ + if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) SetTunnelBridgeReservation(t, false); + break; + + default: break; + } + } + } + + /* Reserve all tracks trains are currently on. */ + if (CheckSavegameVersion(101)) { + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN) { + if ((v->u.rail.track & TRACK_BIT_WORMHOLE) == TRACK_BIT_WORMHOLE) { + TryReserveRailTrack(v->tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(v->tile))); + } else if ((v->u.rail.track & TRACK_BIT_MASK) != TRACK_BIT_NONE) { + TryReserveRailTrack(v->tile, TrackBitsToTrack(v->u.rail.track)); + } + } + } + + /* Give owners to waypoints, based on rail tracks it is sitting on. + * If none is available, specify OWNER_NONE */ + Waypoint *wp; + FOR_ALL_WAYPOINTS(wp) { + Owner owner = (IsRailWaypointTile(wp->xy) ? GetTileOwner(wp->xy) : OWNER_NONE); + wp->owner = IsValidCompanyID(owner) ? owner : OWNER_NONE; + } + } + + if (CheckSavegameVersion(102)) { + for (TileIndex t = 0; t < map_size; t++) { + /* Now all crossings should be in correct state */ + if (IsLevelCrossingTile(t)) UpdateLevelCrossing(t, false); + } + } + + if (CheckSavegameVersion(103)) { + /* Non-town-owned roads now store the closest town */ + UpdateNearestTownForRoadTiles(false); + + /* signs with invalid owner left from older savegames */ + Sign *si; + FOR_ALL_SIGNS(si) { + if (si->owner != OWNER_NONE && !IsValidCompanyID(si->owner)) si->owner = OWNER_NONE; + } + + /* Station can get named based on an industry type, but the current ones + * are not, so mark them as if they are not named by an industry. */ + Station *st; + FOR_ALL_STATIONS(st) { + st->indtype = IT_INVALID; + } + } + + if (CheckSavegameVersion(104)) { + Vehicle *v; + FOR_ALL_VEHICLES(v) { + /* Set engine_type of shadow and rotor */ + if (v->type == VEH_AIRCRAFT && !IsNormalAircraft(v)) { + v->engine_type = v->First()->engine_type; + } + } + + /* More companies ... */ + Company *c; + FOR_ALL_COMPANIES(c) { + if (c->bankrupt_asked == 0xFF) c->bankrupt_asked = 0xFFFF; + } + + Engine *e; + FOR_ALL_ENGINES(e) { + if (e->company_avail == 0xFF) e->company_avail = 0xFFFF; + } + + Town *t; + FOR_ALL_TOWNS(t) { + if (t->have_ratings == 0xFF) t->have_ratings = 0xFFFF; + for (uint i = 8; i != MAX_COMPANIES; i++) t->ratings[i] = RATING_INITIAL; + } + } + + GamelogPrintDebug(1); + + bool ret = InitializeWindowsAndCaches(); + /* Restore the signals */ + signal(SIGSEGV, prev_segfault); + signal(SIGABRT, prev_abort); + return ret; +} + +/** Reload all NewGRF files during a running game. This is a cut-down + * version of AfterLoadGame(). + * XXX - We need to reset the vehicle position hash because with a non-empty + * hash AfterLoadVehicles() will loop infinitely. We need AfterLoadVehicles() + * to recalculate vehicle data as some NewGRF vehicle sets could have been + * removed or added and changed statistics */ +void ReloadNewGRFData() +{ + /* reload grf data */ + GfxLoadSprites(); + LoadStringWidthTable(); + ResetEconomy(); + /* reload vehicles */ + ResetVehiclePosHash(); + AfterLoadVehicles(false); + StartupEngines(); + SetCachedEngineCounts(); + /* update station and waypoint graphics */ + AfterLoadWaypoints(); + AfterLoadStations(); + /* Check and update house and town values */ + UpdateHousesAndTowns(); + /* Update livery selection windows */ + for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) InvalidateWindowData(WC_COMPANY_COLOR, i, _loaded_newgrf_features.has_2CC); + /* redraw the whole screen */ + MarkWholeScreenDirty(); + CheckTrainsLengths(); +} diff --git a/src/saveload/ai_sl.cpp b/src/saveload/ai_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/ai_sl.cpp @@ -0,0 +1,79 @@ +/* $Id$ */ + +/** @file ai_sl.cpp Code handling saving and loading of old AI + new AI initialisation after game load */ + +#include "../stdafx.h" +#include "../ai/ai.h" +#include "../ai/default/default.h" + +#include "saveload.h" + +static const SaveLoad _company_ai_desc[] = { + SLE_VAR(CompanyAI, state, SLE_UINT8), + SLE_VAR(CompanyAI, tick, SLE_UINT8), + SLE_CONDVAR(CompanyAI, state_counter, SLE_FILE_U16 | SLE_VAR_U32, 0, 12), + SLE_CONDVAR(CompanyAI, state_counter, SLE_UINT32, 13, SL_MAX_VERSION), + SLE_VAR(CompanyAI, timeout_counter, SLE_UINT16), + + SLE_VAR(CompanyAI, state_mode, SLE_UINT8), + SLE_VAR(CompanyAI, banned_tile_count, SLE_UINT8), + SLE_VAR(CompanyAI, railtype_to_use, SLE_UINT8), + + SLE_VAR(CompanyAI, cargo_type, SLE_UINT8), + SLE_VAR(CompanyAI, num_wagons, SLE_UINT8), + SLE_VAR(CompanyAI, build_kind, SLE_UINT8), + SLE_VAR(CompanyAI, num_build_rec, SLE_UINT8), + SLE_VAR(CompanyAI, num_loco_to_build, SLE_UINT8), + SLE_VAR(CompanyAI, num_want_fullload, SLE_UINT8), + + SLE_VAR(CompanyAI, route_type_mask, SLE_UINT8), + + SLE_CONDVAR(CompanyAI, start_tile_a, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(CompanyAI, start_tile_a, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(CompanyAI, cur_tile_a, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(CompanyAI, cur_tile_a, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_VAR(CompanyAI, start_dir_a, SLE_UINT8), + SLE_VAR(CompanyAI, cur_dir_a, SLE_UINT8), + + SLE_CONDVAR(CompanyAI, start_tile_b, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(CompanyAI, start_tile_b, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(CompanyAI, cur_tile_b, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(CompanyAI, cur_tile_b, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_VAR(CompanyAI, start_dir_b, SLE_UINT8), + SLE_VAR(CompanyAI, cur_dir_b, SLE_UINT8), + + SLE_REF(CompanyAI, cur_veh, REF_VEHICLE), + + SLE_ARR(CompanyAI, wagon_list, SLE_UINT16, 9), + SLE_ARR(CompanyAI, order_list_blocks, SLE_UINT8, 20), + SLE_ARR(CompanyAI, banned_tiles, SLE_UINT16, 16), + + SLE_CONDNULL(64, 2, SL_MAX_VERSION), + SLE_END() +}; + +static const SaveLoad _company_ai_build_rec_desc[] = { + SLE_CONDVAR(AiBuildRec, spec_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(AiBuildRec, spec_tile, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(AiBuildRec, use_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(AiBuildRec, use_tile, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_VAR(AiBuildRec, rand_rng, SLE_UINT8), + SLE_VAR(AiBuildRec, cur_building_rule, SLE_UINT8), + SLE_VAR(AiBuildRec, unk6, SLE_UINT8), + SLE_VAR(AiBuildRec, unk7, SLE_UINT8), + SLE_VAR(AiBuildRec, buildcmd_a, SLE_UINT8), + SLE_VAR(AiBuildRec, buildcmd_b, SLE_UINT8), + SLE_VAR(AiBuildRec, direction, SLE_UINT8), + SLE_VAR(AiBuildRec, cargo, SLE_UINT8), + SLE_END() +}; + + +void SaveLoad_AI(CompanyID company) +{ + CompanyAI *cai = &_companies_ai[company]; + SlObject(cai, _company_ai_desc); + for (int i = 0; i != cai->num_build_rec; i++) { + SlObject(&cai->src + i, _company_ai_build_rec_desc); + } +} diff --git a/src/saveload/animated_tile_sl.cpp b/src/saveload/animated_tile_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/animated_tile_sl.cpp @@ -0,0 +1,56 @@ +/* $Id$ */ + +/** @file animated_tile_sl.cpp Code handling saving and loading of animated tiles */ + +#include "../stdafx.h" +#include "../tile_type.h" +#include "../core/alloc_func.hpp" + +#include "saveload.h" + +extern TileIndex *_animated_tile_list; +extern uint _animated_tile_count; +extern uint _animated_tile_allocated; + +/** + * Save the ANIT chunk. + */ +static void Save_ANIT() +{ + SlSetLength(_animated_tile_count * sizeof(*_animated_tile_list)); + SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32); +} + +/** + * Load the ANIT chunk; the chunk containing the animated tiles. + */ +static void Load_ANIT() +{ + /* Before version 80 we did NOT have a variable length animated tile table */ + if (CheckSavegameVersion(80)) { + /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */ + SlArray(_animated_tile_list, 256, CheckSavegameVersion(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32); + + for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) { + if (_animated_tile_list[_animated_tile_count] == 0) break; + } + return; + } + + _animated_tile_count = (uint)SlGetFieldLength() / sizeof(*_animated_tile_list); + + /* Determine a nice rounded size for the amount of allocated tiles */ + _animated_tile_allocated = 256; + while (_animated_tile_allocated < _animated_tile_count) _animated_tile_allocated *= 2; + + _animated_tile_list = ReallocT(_animated_tile_list, _animated_tile_allocated); + SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32); +} + +/** + * "Definition" imported by the saveload code to be able to load and save + * the animated tile table. + */ +extern const ChunkHandler _animated_tile_chunk_handlers[] = { + { 'ANIT', Save_ANIT, Load_ANIT, CH_RIFF | CH_LAST}, +}; diff --git a/src/saveload/autoreplace_sl.cpp b/src/saveload/autoreplace_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/autoreplace_sl.cpp @@ -0,0 +1,51 @@ +/* $Id$ */ + +/** @file autoreplace_sl.cpp Code handling saving and loading of autoreplace rules */ + +#include "../stdafx.h" +#include "../autoreplace_type.h" +#include "../engine_type.h" +#include "../group_type.h" +#include "../autoreplace_base.h" + +#include "saveload.h" + +static const SaveLoad _engine_renew_desc[] = { + SLE_VAR(EngineRenew, from, SLE_UINT16), + SLE_VAR(EngineRenew, to, SLE_UINT16), + + SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS), + SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, 60, SL_MAX_VERSION), + SLE_END() +}; + +static void Save_ERNW() +{ + EngineRenew *er; + + FOR_ALL_ENGINE_RENEWS(er) { + SlSetArrayIndex(er->index); + SlObject(er, _engine_renew_desc); + } +} + +static void Load_ERNW() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + EngineRenew *er = new (index) EngineRenew(); + SlObject(er, _engine_renew_desc); + + /* Advanced vehicle lists, ungrouped vehicles got added */ + if (CheckSavegameVersion(60)) { + er->group_id = ALL_GROUP; + } else if (CheckSavegameVersion(71)) { + if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP; + } + } +} + +extern const ChunkHandler _autoreplace_chunk_handlers[] = { + { 'ERNW', Save_ERNW, Load_ERNW, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/cargopacket_sl.cpp b/src/saveload/cargopacket_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/cargopacket_sl.cpp @@ -0,0 +1,45 @@ +/* $Id$ */ + +/** @file cargopacket_sl.cpp Code handling saving and loading of cargo packets */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "../cargopacket.h" + +#include "saveload.h" + +static const SaveLoad _cargopacket_desc[] = { + SLE_VAR(CargoPacket, source, SLE_UINT16), + SLE_VAR(CargoPacket, source_xy, SLE_UINT32), + SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32), + SLE_VAR(CargoPacket, count, SLE_UINT16), + SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8), + SLE_VAR(CargoPacket, feeder_share, SLE_INT64), + SLE_VAR(CargoPacket, paid_for, SLE_BOOL), + + SLE_END() +}; + +static void Save_CAPA() +{ + CargoPacket *cp; + + FOR_ALL_CARGOPACKETS(cp) { + SlSetArrayIndex(cp->index); + SlObject(cp, _cargopacket_desc); + } +} + +static void Load_CAPA() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + CargoPacket *cp = new (index) CargoPacket(); + SlObject(cp, _cargopacket_desc); + } +} + +extern const ChunkHandler _cargopacket_chunk_handlers[] = { + { 'CAPA', Save_CAPA, Load_CAPA, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/cheat_sl.cpp b/src/saveload/cheat_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/cheat_sl.cpp @@ -0,0 +1,37 @@ +/* $Id$ */ + +/** @file cheat_sl.cpp Code handling saving and loading of cheats */ + +#include "../stdafx.h" +#include "../cheat_type.h" + +#include "saveload.h" + +static void Save_CHTS() +{ + /* Cannot use lengthof because _cheats is of type Cheats, not Cheat */ + byte count = sizeof(_cheats) / sizeof(Cheat); + Cheat *cht = (Cheat*) &_cheats; + Cheat *cht_last = &cht[count]; + + SlSetLength(count * 2); + for (; cht != cht_last; cht++) { + SlWriteByte(cht->been_used); + SlWriteByte(cht->value); + } +} + +static void Load_CHTS() +{ + Cheat *cht = (Cheat*)&_cheats; + size_t count = SlGetFieldLength() / 2; + + for (uint i = 0; i < count; i++) { + cht[i].been_used = (SlReadByte() != 0); + cht[i].value = (SlReadByte() != 0); + } +} + +extern const ChunkHandler _cheat_chunk_handlers[] = { + { 'CHTS', Save_CHTS, Load_CHTS, CH_RIFF | CH_LAST} +}; diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/company_sl.cpp @@ -0,0 +1,240 @@ +/* $Id$ */ + +/** @file company_sl.cpp Code handling saving and loading of company data */ + +#include "../stdafx.h" +#include "../company_base.h" +#include "../company_func.h" +#include "../network/network.h" +#include "../ai/ai.h" +#include "../ai/trolly/trolly.h" +#include "../company_manager_face.h" + +#include "saveload.h" + +/** + * Converts an old company manager's face format to the new company manager's face format + * + * Meaning of the bits in the old face (some bits are used in several times): + * - 4 and 5: chin + * - 6 to 9: eyebrows + * - 10 to 13: nose + * - 13 to 15: lips (also moustache for males) + * - 16 to 19: hair + * - 20 to 22: eye color + * - 20 to 27: tie, ear rings etc. + * - 28 to 30: glasses + * - 19, 26 and 27: race (bit 27 set and bit 19 equal to bit 26 = black, otherwise white) + * - 31: gender (0 = male, 1 = female) + * + * @param face the face in the old format + * @return the face in the new format + */ +CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face) +{ + CompanyManagerFace cmf = 0; + GenderEthnicity ge = GE_WM; + + if (HasBit(face, 31)) SetBit(ge, GENDER_FEMALE); + if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) SetBit(ge, ETHNICITY_BLACK); + + SetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, ge, ge); + SetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge, GB(face, 28, 3) <= 1); + SetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge, HasBit(ge, ETHNICITY_BLACK) ? 0 : ClampU(GB(face, 20, 3), 5, 7) - 5); + SetCompanyManagerFaceBits(cmf, CMFV_CHIN, ge, ScaleCompanyManagerFaceValue(CMFV_CHIN, ge, GB(face, 4, 2))); + SetCompanyManagerFaceBits(cmf, CMFV_EYEBROWS, ge, ScaleCompanyManagerFaceValue(CMFV_EYEBROWS, ge, GB(face, 6, 4))); + SetCompanyManagerFaceBits(cmf, CMFV_HAIR, ge, ScaleCompanyManagerFaceValue(CMFV_HAIR, ge, GB(face, 16, 4))); + SetCompanyManagerFaceBits(cmf, CMFV_JACKET, ge, ScaleCompanyManagerFaceValue(CMFV_JACKET, ge, GB(face, 20, 2))); + SetCompanyManagerFaceBits(cmf, CMFV_COLLAR, ge, ScaleCompanyManagerFaceValue(CMFV_COLLAR, ge, GB(face, 22, 2))); + SetCompanyManagerFaceBits(cmf, CMFV_GLASSES, ge, GB(face, 28, 1)); + + uint lips = GB(face, 10, 4); + if (!HasBit(ge, GENDER_FEMALE) && lips < 4) { + SetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge, true); + SetCompanyManagerFaceBits(cmf, CMFV_MOUSTACHE, ge, max(lips, 1U) - 1); + } else { + if (!HasBit(ge, GENDER_FEMALE)) { + lips = lips * 15 / 16; + lips -= 3; + if (HasBit(ge, ETHNICITY_BLACK) && lips > 8) lips = 0; + } else { + lips = ScaleCompanyManagerFaceValue(CMFV_LIPS, ge, lips); + } + SetCompanyManagerFaceBits(cmf, CMFV_LIPS, ge, lips); + + uint nose = GB(face, 13, 3); + if (ge == GE_WF) { + nose = (nose * 3 >> 3) * 3 >> 2; // There is 'hole' in the nose sprites for females + } else { + nose = ScaleCompanyManagerFaceValue(CMFV_NOSE, ge, nose); + } + SetCompanyManagerFaceBits(cmf, CMFV_NOSE, ge, nose); + } + + uint tie_earring = GB(face, 24, 4); + if (!HasBit(ge, GENDER_FEMALE) || tie_earring < 3) { // Not all females have an earring + if (HasBit(ge, GENDER_FEMALE)) SetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge, true); + SetCompanyManagerFaceBits(cmf, CMFV_TIE_EARRING, ge, HasBit(ge, GENDER_FEMALE) ? tie_earring : ScaleCompanyManagerFaceValue(CMFV_TIE_EARRING, ge, tie_earring / 2)); + } + + return cmf; +} + + + +/* Save/load of companies */ +static const SaveLoad _company_desc[] = { + SLE_VAR(Company, name_2, SLE_UINT32), + SLE_VAR(Company, name_1, SLE_STRINGID), + SLE_CONDSTR(Company, name, SLE_STR, 0, 84, SL_MAX_VERSION), + + SLE_VAR(Company, president_name_1, SLE_UINT16), + SLE_VAR(Company, president_name_2, SLE_UINT32), + SLE_CONDSTR(Company, president_name, SLE_STR, 0, 84, SL_MAX_VERSION), + + SLE_VAR(Company, face, SLE_UINT32), + + /* money was changed to a 64 bit field in savegame version 1. */ + SLE_CONDVAR(Company, money, SLE_VAR_I64 | SLE_FILE_I32, 0, 0), + SLE_CONDVAR(Company, money, SLE_INT64, 1, SL_MAX_VERSION), + + SLE_CONDVAR(Company, current_loan, SLE_VAR_I64 | SLE_FILE_I32, 0, 64), + SLE_CONDVAR(Company, current_loan, SLE_INT64, 65, SL_MAX_VERSION), + + SLE_VAR(Company, colour, SLE_UINT8), + SLE_VAR(Company, money_fraction, SLE_UINT8), + SLE_CONDVAR(Company, avail_railtypes, SLE_UINT8, 0, 57), + SLE_VAR(Company, block_preview, SLE_UINT8), + + SLE_CONDVAR(Company, cargo_types, SLE_FILE_U16 | SLE_VAR_U32, 0, 93), + SLE_CONDVAR(Company, cargo_types, SLE_UINT32, 94, SL_MAX_VERSION), + SLE_CONDVAR(Company, location_of_HQ, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Company, location_of_HQ, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Company, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Company, last_build_coordinate, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Company, inaugurated_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Company, inaugurated_year, SLE_INT32, 31, SL_MAX_VERSION), + + SLE_ARR(Company, share_owners, SLE_UINT8, 4), + + SLE_VAR(Company, num_valid_stat_ent, SLE_UINT8), + + SLE_VAR(Company, quarters_of_bankrupcy, SLE_UINT8), + SLE_CONDVAR(Company, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, 0, 103), + SLE_CONDVAR(Company, bankrupt_asked, SLE_UINT16, 104, SL_MAX_VERSION), + SLE_VAR(Company, bankrupt_timeout, SLE_INT16), + SLE_CONDVAR(Company, bankrupt_value, SLE_VAR_I64 | SLE_FILE_I32, 0, 64), + SLE_CONDVAR(Company, bankrupt_value, SLE_INT64, 65, SL_MAX_VERSION), + + /* yearly expenses was changed to 64-bit in savegame version 2. */ + SLE_CONDARR(Company, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, 0, 1), + SLE_CONDARR(Company, yearly_expenses, SLE_INT64, 3 * 13, 2, SL_MAX_VERSION), + + SLE_CONDVAR(Company, is_ai, SLE_BOOL, 2, SL_MAX_VERSION), + SLE_CONDNULL(1, 4, 99), + + /* Engine renewal settings */ + SLE_CONDNULL(512, 16, 18), + SLE_CONDREF(Company, engine_renew_list, REF_ENGINE_RENEWS, 19, SL_MAX_VERSION), + SLE_CONDVAR(Company, engine_renew, SLE_BOOL, 16, SL_MAX_VERSION), + SLE_CONDVAR(Company, engine_renew_months, SLE_INT16, 16, SL_MAX_VERSION), + SLE_CONDVAR(Company, engine_renew_money, SLE_UINT32, 16, SL_MAX_VERSION), + SLE_CONDVAR(Company, renew_keep_length, SLE_BOOL, 2, SL_MAX_VERSION), // added with 16.1, but was blank since 2 + + /* reserve extra space in savegame here. (currently 63 bytes) */ + SLE_CONDNULL(63, 2, SL_MAX_VERSION), + + SLE_END() +}; + +static const SaveLoad _company_economy_desc[] = { + /* these were changed to 64-bit in savegame format 2 */ + SLE_CONDVAR(CompanyEconomyEntry, income, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), + SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, 2, SL_MAX_VERSION), + SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), + SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, 2, SL_MAX_VERSION), + SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), + SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, 2, SL_MAX_VERSION), + + SLE_VAR(CompanyEconomyEntry, delivered_cargo, SLE_INT32), + SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), + + SLE_END() +}; + +static const SaveLoad _company_livery_desc[] = { + SLE_CONDVAR(Livery, in_use, SLE_BOOL, 34, SL_MAX_VERSION), + SLE_CONDVAR(Livery, colour1, SLE_UINT8, 34, SL_MAX_VERSION), + SLE_CONDVAR(Livery, colour2, SLE_UINT8, 34, SL_MAX_VERSION), + SLE_END() +}; + +static void SaveLoad_PLYR(Company *c) +{ + int i; + + SlObject(c, _company_desc); + + /* Write AI? */ + if (!IsHumanCompany(c->index)) { + extern void SaveLoad_AI(CompanyID company); + SaveLoad_AI(c->index); + } + + /* Write economy */ + SlObject(&c->cur_economy, _company_economy_desc); + + /* Write old economy entries. */ + for (i = 0; i < c->num_valid_stat_ent; i++) { + SlObject(&c->old_economy[i], _company_economy_desc); + } + + /* Write each livery entry. */ + int num_liveries = CheckSavegameVersion(63) ? LS_END - 4 : (CheckSavegameVersion(85) ? LS_END - 2: LS_END); + for (i = 0; i < num_liveries; i++) { + SlObject(&c->livery[i], _company_livery_desc); + } + + if (num_liveries < LS_END) { + /* We want to insert some liveries somewhere in between. This means some have to be moved. */ + memmove(&c->livery[LS_FREIGHT_WAGON], &c->livery[LS_PASSENGER_WAGON_MONORAIL], (LS_END - LS_FREIGHT_WAGON) * sizeof(c->livery[0])); + c->livery[LS_PASSENGER_WAGON_MONORAIL] = c->livery[LS_MONORAIL]; + c->livery[LS_PASSENGER_WAGON_MAGLEV] = c->livery[LS_MAGLEV]; + } + + if (num_liveries == LS_END - 4) { + /* Copy bus/truck liveries over to trams */ + c->livery[LS_PASSENGER_TRAM] = c->livery[LS_BUS]; + c->livery[LS_FREIGHT_TRAM] = c->livery[LS_TRUCK]; + } +} + +static void Save_PLYR() +{ + Company *c; + FOR_ALL_COMPANIES(c) { + SlSetArrayIndex(c->index); + SlAutolength((AutolengthProc*)SaveLoad_PLYR, c); + } +} + +static void Load_PLYR() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Company *c = new (index) Company(); + SaveLoad_PLYR(c); + _company_colours[index] = c->colour; + + /* This is needed so an AI is attached to a loaded AI */ + if (c->is_ai && (!_networking || _network_server) && _ai.enabled) { + /* Clear the memory of the new AI, otherwise we might be doing wrong things. */ + memset(&_companies_ainew[index], 0, sizeof(CompanyAiNew)); + AI_StartNewAI(c->index); + } + } +} + +extern const ChunkHandler _company_chunk_handlers[] = { + { 'PLYR', Save_PLYR, Load_PLYR, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/depot_sl.cpp b/src/saveload/depot_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/depot_sl.cpp @@ -0,0 +1,39 @@ +/* $Id$ */ + +/** @file depot_sl.cpp Code handling saving and loading of depots */ + +#include "../stdafx.h" +#include "../depot_base.h" + +#include "saveload.h" + +static const SaveLoad _depot_desc[] = { + SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Depot, xy, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_VAR(Depot, town_index, SLE_UINT16), + SLE_END() +}; + +static void Save_DEPT() +{ + Depot *depot; + + FOR_ALL_DEPOTS(depot) { + SlSetArrayIndex(depot->index); + SlObject(depot, _depot_desc); + } +} + +static void Load_DEPT() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + Depot *depot = new (index) Depot(); + SlObject(depot, _depot_desc); + } +} + +extern const ChunkHandler _depot_chunk_handlers[] = { + { 'DEPT', Save_DEPT, Load_DEPT, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/economy_sl.cpp b/src/saveload/economy_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/economy_sl.cpp @@ -0,0 +1,58 @@ +/* $Id$ */ + +/** @file economy_sl.cpp Code handling saving and loading of economy data */ + +#include "../stdafx.h" +#include "../economy_func.h" + +#include "saveload.h" + +/** Prices */ +static void SaveLoad_PRIC() +{ + int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64; + SlArray(&_price, NUM_PRICES, vt); + SlArray(&_price_frac, NUM_PRICES, SLE_UINT16); +} + +/** Cargo payment rates */ +static void SaveLoad_CAPR() +{ + uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO; + int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64; + SlArray(&_cargo_payment_rates, num_cargo, vt); + SlArray(&_cargo_payment_rates_frac, num_cargo, SLE_UINT16); +} + +static const SaveLoad _economy_desc[] = { + SLE_CONDVAR(Economy, max_loan, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), + SLE_CONDVAR(Economy, max_loan, SLE_INT64, 65, SL_MAX_VERSION), + SLE_CONDVAR(Economy, max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), + SLE_CONDVAR(Economy, max_loan_unround, SLE_INT64, 65, SL_MAX_VERSION), + SLE_CONDVAR(Economy, max_loan_unround_fract, SLE_UINT16, 70, SL_MAX_VERSION), + SLE_VAR(Economy, fluct, SLE_INT16), + SLE_VAR(Economy, interest_rate, SLE_UINT8), + SLE_VAR(Economy, infl_amount, SLE_UINT8), + SLE_VAR(Economy, infl_amount_pr, SLE_UINT8), + SLE_CONDVAR(Economy, industry_daily_change_counter, SLE_UINT32, 102, SL_MAX_VERSION), + SLE_END() +}; + +/** Economy variables */ +static void Save_ECMY() +{ + SlObject(&_economy, _economy_desc); +} + +/** Economy variables */ +static void Load_ECMY() +{ + SlObject(&_economy, _economy_desc); + StartupIndustryDailyChanges(CheckSavegameVersion(102)); // old savegames will need to be initialized +} + +extern const ChunkHandler _economy_chunk_handlers[] = { + { 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH}, + { 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH}, + { 'ECMY', Save_ECMY, Load_ECMY, CH_RIFF | CH_LAST}, +}; diff --git a/src/saveload/engine_sl.cpp b/src/saveload/engine_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/engine_sl.cpp @@ -0,0 +1,118 @@ +/* $Id$ */ + +/** @file engine_sl.cpp Code handling saving and loading of engines */ + +#include "../stdafx.h" +#include "saveload.h" +#include "saveload_internal.h" +#include "../engine_base.h" +#include + +static const SaveLoad _engine_desc[] = { + SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Engine, intro_date, SLE_INT32, 31, SL_MAX_VERSION), + SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Engine, age, SLE_INT32, 31, SL_MAX_VERSION), + SLE_VAR(Engine, reliability, SLE_UINT16), + SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16), + SLE_VAR(Engine, reliability_start, SLE_UINT16), + SLE_VAR(Engine, reliability_max, SLE_UINT16), + SLE_VAR(Engine, reliability_final, SLE_UINT16), + SLE_VAR(Engine, duration_phase_1, SLE_UINT16), + SLE_VAR(Engine, duration_phase_2, SLE_UINT16), + SLE_VAR(Engine, duration_phase_3, SLE_UINT16), + + SLE_VAR(Engine, lifelength, SLE_UINT8), + SLE_VAR(Engine, flags, SLE_UINT8), + SLE_VAR(Engine, preview_company_rank,SLE_UINT8), + SLE_VAR(Engine, preview_wait, SLE_UINT8), + SLE_CONDNULL(1, 0, 44), + SLE_CONDVAR(Engine, company_avail, SLE_FILE_U8 | SLE_VAR_U16, 0, 103), + SLE_CONDVAR(Engine, company_avail, SLE_UINT16, 104, SL_MAX_VERSION), + SLE_CONDSTR(Engine, name, SLE_STR, 0, 84, SL_MAX_VERSION), + + /* reserve extra space in savegame here. (currently 16 bytes) */ + SLE_CONDNULL(16, 2, SL_MAX_VERSION), + + SLE_END() +}; + +static std::map _temp_engine; + +Engine *GetTempDataEngine(EngineID index) +{ + return &_temp_engine[index]; +} + +static void Save_ENGN() +{ + Engine *e; + FOR_ALL_ENGINES(e) { + SlSetArrayIndex(e->index); + SlObject(e, _engine_desc); + } +} + +static void Load_ENGN() +{ + /* As engine data is loaded before engines are initialized we need to load + * this information into a temporary array. This is then copied into the + * engine pool after processing NewGRFs by CopyTempEngineData(). */ + int index; + while ((index = SlIterateArray()) != -1) { + Engine *e = GetTempDataEngine(index); + SlObject(e, _engine_desc); + } +} + +/** + * Copy data from temporary engine array into the real engine pool. + */ +void CopyTempEngineData() +{ + Engine *e; + FOR_ALL_ENGINES(e) { + if (e->index >= _temp_engine.size()) break; + + const Engine *se = GetTempDataEngine(e->index); + e->intro_date = se->intro_date; + e->age = se->age; + e->reliability = se->reliability; + e->reliability_spd_dec = se->reliability_spd_dec; + e->reliability_start = se->reliability_start; + e->reliability_max = se->reliability_max; + e->reliability_final = se->reliability_final; + e->duration_phase_1 = se->duration_phase_1; + e->duration_phase_2 = se->duration_phase_2; + e->duration_phase_3 = se->duration_phase_3; + e->lifelength = se->lifelength; + e->flags = se->flags; + e->preview_company_rank= se->preview_company_rank; + e->preview_wait = se->preview_wait; + e->company_avail = se->company_avail; + if (se->name != NULL) e->name = strdup(se->name); + } + + /* Get rid of temporary data */ + _temp_engine.clear(); +} + +static void Load_ENGS() +{ + /* Load old separate String ID list into a temporary array. This + * was always 256 entries. */ + StringID names[256]; + + SlArray(names, lengthof(names), SLE_STRINGID); + + /* Copy each string into the temporary engine array. */ + for (EngineID engine = 0; engine < lengthof(names); engine++) { + Engine *e = GetTempDataEngine(engine); + e->name = CopyFromOldName(names[engine]); + } +} + +extern const ChunkHandler _engine_chunk_handlers[] = { + { 'ENGN', Save_ENGN, Load_ENGN, CH_ARRAY }, + { 'ENGS', NULL, Load_ENGS, CH_RIFF | CH_LAST }, +}; diff --git a/src/saveload/gamelog_sl.cpp b/src/saveload/gamelog_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/gamelog_sl.cpp @@ -0,0 +1,161 @@ +/* $Id$ */ + +/** @file gamelog_sl.cpp Code handling saving and loading of gamelog data */ + +#include "../stdafx.h" +#include "../gamelog.h" +#include "../gamelog_internal.h" +#include "../core/alloc_func.hpp" + +#include "saveload.h" + +static const SaveLoad _glog_action_desc[] = { + SLE_VAR(LoggedAction, tick, SLE_UINT16), + SLE_END() +}; + +static const SaveLoad _glog_mode_desc[] = { + SLE_VAR(LoggedChange, mode.mode, SLE_UINT8), + SLE_VAR(LoggedChange, mode.landscape, SLE_UINT8), + SLE_END() +}; + +static const SaveLoad _glog_revision_desc[] = { + SLE_ARR(LoggedChange, revision.text, SLE_UINT8, NETWORK_REVISION_LENGTH), + SLE_VAR(LoggedChange, revision.newgrf, SLE_UINT32), + SLE_VAR(LoggedChange, revision.slver, SLE_UINT16), + SLE_VAR(LoggedChange, revision.modified, SLE_UINT8), + SLE_END() +}; + +static const SaveLoad _glog_oldver_desc[] = { + SLE_VAR(LoggedChange, oldver.type, SLE_UINT32), + SLE_VAR(LoggedChange, oldver.version, SLE_UINT32), + SLE_END() +}; + +static const SaveLoad _glog_patch_desc[] = { + SLE_STR(LoggedChange, patch.name, SLE_STR, 128), + SLE_VAR(LoggedChange, patch.oldval, SLE_INT32), + SLE_VAR(LoggedChange, patch.newval, SLE_INT32), + SLE_END() +}; + +static const SaveLoad _glog_grfadd_desc[] = { + SLE_VAR(LoggedChange, grfadd.grfid, SLE_UINT32 ), + SLE_ARR(LoggedChange, grfadd.md5sum, SLE_UINT8, 16), + SLE_END() +}; + +static const SaveLoad _glog_grfrem_desc[] = { + SLE_VAR(LoggedChange, grfrem.grfid, SLE_UINT32), + SLE_END() +}; + +static const SaveLoad _glog_grfcompat_desc[] = { + SLE_VAR(LoggedChange, grfcompat.grfid, SLE_UINT32 ), + SLE_ARR(LoggedChange, grfcompat.md5sum, SLE_UINT8, 16), + SLE_END() +}; + +static const SaveLoad _glog_grfparam_desc[] = { + SLE_VAR(LoggedChange, grfparam.grfid, SLE_UINT32), + SLE_END() +}; + +static const SaveLoad _glog_grfmove_desc[] = { + SLE_VAR(LoggedChange, grfmove.grfid, SLE_UINT32), + SLE_VAR(LoggedChange, grfmove.offset, SLE_INT32), + SLE_END() +}; + +static const SaveLoad _glog_grfbug_desc[] = { + SLE_VAR(LoggedChange, grfbug.data, SLE_UINT64), + SLE_VAR(LoggedChange, grfbug.grfid, SLE_UINT32), + SLE_VAR(LoggedChange, grfbug.bug, SLE_UINT8), + SLE_END() +}; + +static const SaveLoad *_glog_desc[] = { + _glog_mode_desc, + _glog_revision_desc, + _glog_oldver_desc, + _glog_patch_desc, + _glog_grfadd_desc, + _glog_grfrem_desc, + _glog_grfcompat_desc, + _glog_grfparam_desc, + _glog_grfmove_desc, + _glog_grfbug_desc, +}; + +assert_compile(lengthof(_glog_desc) == GLCT_END); + +static void Load_GLOG() +{ + assert(_gamelog_action == NULL); + assert(_gamelog_actions == 0); + + GamelogActionType at; + while ((at = (GamelogActionType)SlReadByte()) != GLAT_NONE) { + _gamelog_action = ReallocT(_gamelog_action, _gamelog_actions + 1); + LoggedAction *la = &_gamelog_action[_gamelog_actions++]; + + la->at = at; + + SlObject(la, _glog_action_desc); // has to be saved after 'DATE'! + la->change = NULL; + la->changes = 0; + + GamelogChangeType ct; + while ((ct = (GamelogChangeType)SlReadByte()) != GLCT_NONE) { + la->change = ReallocT(la->change, la->changes + 1); + + LoggedChange *lc = &la->change[la->changes++]; + /* for SLE_STR, pointer has to be valid! so make it NULL */ + memset(lc, 0, sizeof(*lc)); + lc->ct = ct; + + assert((uint)ct < GLCT_END); + + SlObject(lc, _glog_desc[ct]); + } + } +} + +static void Save_GLOG() +{ + const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; + size_t length = 0; + + for (const LoggedAction *la = _gamelog_action; la != laend; la++) { + const LoggedChange *lcend = &la->change[la->changes]; + for (LoggedChange *lc = la->change; lc != lcend; lc++) { + assert((uint)lc->ct < lengthof(_glog_desc)); + length += SlCalcObjLength(lc, _glog_desc[lc->ct]) + 1; + } + length += 4; + } + length++; + + SlSetLength(length); + + for (LoggedAction *la = _gamelog_action; la != laend; la++) { + SlWriteByte(la->at); + SlObject(la, _glog_action_desc); + + const LoggedChange *lcend = &la->change[la->changes]; + for (LoggedChange *lc = la->change; lc != lcend; lc++) { + SlWriteByte(lc->ct); + assert((uint)lc->ct < GLCT_END); + SlObject(lc, _glog_desc[lc->ct]); + } + SlWriteByte(GLCT_NONE); + } + SlWriteByte(GLAT_NONE); +} + + +extern const ChunkHandler _gamelog_chunk_handlers[] = { + { 'GLOG', Save_GLOG, Load_GLOG, CH_RIFF | CH_LAST } +}; diff --git a/src/saveload/group_sl.cpp b/src/saveload/group_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/group_sl.cpp @@ -0,0 +1,43 @@ +/* $Id$ */ + +/** @file group_sl.cpp Code handling saving and loading of economy data */ + +#include "../stdafx.h" +#include "../group.h" + +#include "saveload.h" + +static const SaveLoad _group_desc[] = { + SLE_CONDVAR(Group, name, SLE_NAME, 0, 83), + SLE_CONDSTR(Group, name, SLE_STR, 0, 84, SL_MAX_VERSION), + SLE_VAR(Group, num_vehicle, SLE_UINT16), + SLE_VAR(Group, owner, SLE_UINT8), + SLE_VAR(Group, vehicle_type, SLE_UINT8), + SLE_VAR(Group, replace_protection, SLE_BOOL), + SLE_END() +}; + +static void Save_GRPS(void) +{ + Group *g; + + FOR_ALL_GROUPS(g) { + SlSetArrayIndex(g->index); + SlObject(g, _group_desc); + } +} + + +static void Load_GRPS(void) +{ + int index; + + while ((index = SlIterateArray()) != -1) { + Group *g = new (index) Group(); + SlObject(g, _group_desc); + } +} + +extern const ChunkHandler _group_chunk_handlers[] = { + { 'GRPS', Save_GRPS, Load_GRPS, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/industry_sl.cpp b/src/saveload/industry_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/industry_sl.cpp @@ -0,0 +1,155 @@ +/* $Id$ */ + +/** @file industry_sl.cpp Code handling saving and loading of industries */ + +#include "../stdafx.h" +#include "../tile_type.h" +#include "../strings_type.h" +#include "../company_type.h" +#include "../industry.h" +#include "../newgrf_commons.h" + +#include "saveload.h" + +static const SaveLoad _industry_desc[] = { + SLE_CONDVAR(Industry, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Industry, xy, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_VAR(Industry, width, SLE_UINT8), + SLE_VAR(Industry, height, SLE_UINT8), + SLE_REF(Industry, town, REF_TOWN), + SLE_CONDNULL( 2, 0, 60), ///< used to be industry's produced_cargo + SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, 78, SL_MAX_VERSION), + SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, 70, SL_MAX_VERSION), + SLE_ARR(Industry, produced_cargo_waiting, SLE_UINT16, 2), + SLE_ARR(Industry, production_rate, SLE_UINT8, 2), + SLE_CONDNULL( 3, 0, 60), ///< used to be industry's accepts_cargo + SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, 78, SL_MAX_VERSION), + SLE_VAR(Industry, prod_level, SLE_UINT8), + SLE_ARR(Industry, this_month_production, SLE_UINT16, 2), + SLE_ARR(Industry, this_month_transported, SLE_UINT16, 2), + SLE_ARR(Industry, last_month_pct_transported, SLE_UINT8, 2), + SLE_ARR(Industry, last_month_production, SLE_UINT16, 2), + SLE_ARR(Industry, last_month_transported, SLE_UINT16, 2), + + SLE_VAR(Industry, counter, SLE_UINT16), + + SLE_VAR(Industry, type, SLE_UINT8), + SLE_VAR(Industry, owner, SLE_UINT8), + SLE_VAR(Industry, random_color, SLE_UINT8), + SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, 31, SL_MAX_VERSION), + SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8), + + SLE_CONDVAR(Industry, founder, SLE_UINT8, 70, SL_MAX_VERSION), + SLE_CONDVAR(Industry, construction_date, SLE_INT32, 70, SL_MAX_VERSION), + SLE_CONDVAR(Industry, construction_type, SLE_UINT8, 70, SL_MAX_VERSION), + SLE_CONDVAR(Industry, last_cargo_accepted_at, SLE_INT32, 70, SL_MAX_VERSION), + SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, 73, SL_MAX_VERSION), + + SLE_CONDARRX(cpp_offsetof(Industry, psa) + cpp_offsetof(Industry::PersistentStorage, storage), SLE_UINT32, 16, 76, SL_MAX_VERSION), + + SLE_CONDVAR(Industry, random_triggers, SLE_UINT8, 82, SL_MAX_VERSION), + SLE_CONDVAR(Industry, random, SLE_UINT16, 82, SL_MAX_VERSION), + + /* reserve extra space in savegame here. (currently 32 bytes) */ + SLE_CONDNULL(32, 2, SL_MAX_VERSION), + + SLE_END() +}; + +static void Save_INDY() +{ + Industry *ind; + + /* Write the industries */ + FOR_ALL_INDUSTRIES(ind) { + SlSetArrayIndex(ind->index); + SlObject(ind, _industry_desc); + } +} + +/* Save and load the mapping between the industry/tile id on the map, and the grf file + * it came from. */ +static const SaveLoad _industries_id_mapping_desc[] = { + SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), + SLE_VAR(EntityIDMapping, entity_id, SLE_UINT8), + SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT8), + SLE_END() +}; + +static void Save_IIDS() +{ + uint i; + uint j = _industry_mngr.GetMaxMapping(); + + for (i = 0; i < j; i++) { + SlSetArrayIndex(i); + SlObject(&_industry_mngr.mapping_ID[i], _industries_id_mapping_desc); + } +} + +static void Save_TIDS() +{ + uint i; + uint j = _industile_mngr.GetMaxMapping(); + + for (i = 0; i < j; i++) { + SlSetArrayIndex(i); + SlObject(&_industile_mngr.mapping_ID[i], _industries_id_mapping_desc); + } +} + +static void Load_INDY() +{ + int index; + + ResetIndustryCounts(); + + while ((index = SlIterateArray()) != -1) { + Industry *i = new (index) Industry(); + SlObject(i, _industry_desc); + IncIndustryTypeCount(i->type); + } +} + +static void Load_IIDS() +{ + int index; + uint max_id; + + /* clear the current mapping stored. + * This will create the manager if ever it is not yet done */ + _industry_mngr.ResetMapping(); + + /* get boundary for the temporary map loader NUM_INDUSTRYTYPES? */ + max_id = _industry_mngr.GetMaxMapping(); + + while ((index = SlIterateArray()) != -1) { + if ((uint)index >= max_id) break; + SlObject(&_industry_mngr.mapping_ID[index], _industries_id_mapping_desc); + } +} + +static void Load_TIDS() +{ + int index; + uint max_id; + + /* clear the current mapping stored. + * This will create the manager if ever it is not yet done */ + _industile_mngr.ResetMapping(); + + /* get boundary for the temporary map loader NUM_INDUSTILES? */ + max_id = _industile_mngr.GetMaxMapping(); + + while ((index = SlIterateArray()) != -1) { + if ((uint)index >= max_id) break; + SlObject(&_industile_mngr.mapping_ID[index], _industries_id_mapping_desc); + } +} + +extern const ChunkHandler _industry_chunk_handlers[] = { + { 'INDY', Save_INDY, Load_INDY, CH_ARRAY}, + { 'IIDS', Save_IIDS, Load_IIDS, CH_ARRAY}, + { 'TIDS', Save_TIDS, Load_TIDS, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/map_sl.cpp @@ -0,0 +1,249 @@ +/* $Id$ */ + +/** @file map_sl.cpp Code handling saving and loading of map */ + +#include "../stdafx.h" +#include "../tile_type.h" +#include "../map_func.h" +#include "../core/alloc_type.hpp" +#include "../core/bitmath_func.hpp" + +#include "saveload.h" + +static uint32 _map_dim_x; +static uint32 _map_dim_y; + +static const SaveLoadGlobVarList _map_dimensions[] = { + SLEG_CONDVAR(_map_dim_x, SLE_UINT32, 6, SL_MAX_VERSION), + SLEG_CONDVAR(_map_dim_y, SLE_UINT32, 6, SL_MAX_VERSION), + SLEG_END() +}; + +static void Save_MAPS() +{ + _map_dim_x = MapSizeX(); + _map_dim_y = MapSizeY(); + SlGlobList(_map_dimensions); +} + +static void Load_MAPS() +{ + SlGlobList(_map_dimensions); + AllocateMap(_map_dim_x, _map_dim_y); +} + +enum { + MAP_SL_BUF_SIZE = 4096 +}; + +static void Load_MAPT() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type_height = buf[j]; + } +} + +static void Save_MAPT() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].type_height; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } +} + +static void Load_MAP1() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; + } +} + +static void Save_MAP1() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m1; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } +} + +static void Load_MAP2() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, + /* In those versions the m2 was 8 bits */ + CheckSavegameVersion(5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16 + ); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j]; + } +} + +static void Save_MAP2() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + SlSetLength(size * sizeof(uint16)); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m2; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT16); + } +} + +static void Load_MAP3() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; + } +} + +static void Save_MAP3() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m3; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } +} + +static void Load_MAP4() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; + } +} + +static void Save_MAP4() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m4; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } +} + +static void Load_MAP5() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; + } +} + +static void Save_MAP5() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m5; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } +} + +static void Load_MAP6() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + if (CheckSavegameVersion(42)) { + for (TileIndex i = 0; i != size;) { + /* 1024, otherwise we overflow on 64x64 maps! */ + SlArray(buf, 1024, SLE_UINT8); + for (uint j = 0; j != 1024; j++) { + _m[i++].m6 = GB(buf[j], 0, 2); + _m[i++].m6 = GB(buf[j], 2, 2); + _m[i++].m6 = GB(buf[j], 4, 2); + _m[i++].m6 = GB(buf[j], 6, 2); + } + } + } else { + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m6 = buf[j]; + } + } +} + +static void Save_MAP6() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m6; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } +} + +static void Load_MAP7() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; + } +} + +static void Save_MAP7() +{ + SmallStackSafeStackAlloc buf; + TileIndex size = MapSize(); + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m7; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } +} + +extern const ChunkHandler _map_chunk_handlers[] = { + { 'MAPS', Save_MAPS, Load_MAPS, CH_RIFF }, + { 'MAPT', Save_MAPT, Load_MAPT, CH_RIFF }, + { 'MAPO', Save_MAP1, Load_MAP1, CH_RIFF }, + { 'MAP2', Save_MAP2, Load_MAP2, CH_RIFF }, + { 'M3LO', Save_MAP3, Load_MAP3, CH_RIFF }, + { 'M3HI', Save_MAP4, Load_MAP4, CH_RIFF }, + { 'MAP5', Save_MAP5, Load_MAP5, CH_RIFF }, + { 'MAPE', Save_MAP6, Load_MAP6, CH_RIFF }, + { 'MAP7', Save_MAP7, Load_MAP7, CH_RIFF }, +}; diff --git a/src/saveload/misc_sl.cpp b/src/saveload/misc_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/misc_sl.cpp @@ -0,0 +1,105 @@ +/* $Id$ */ + +/** @file misc_sl.cpp Saving and loading of things that didn't fit anywhere else */ + +#include "../stdafx.h" +#include "../date_func.h" +#include "../variables.h" +#include "../core/random_func.hpp" +#include "../openttd.h" +#include "../tile_type.h" +#include "../zoom_func.h" +#include "../vehicle_func.h" +#include "../window_gui.h" +#include "../window_func.h" +#include "../viewport_func.h" +#include "../gfx_func.h" + +#include "saveload.h" + +extern TileIndex _cur_tileloop_tile; + +/* Keep track of current game position */ +int _saved_scrollpos_x; +int _saved_scrollpos_y; + +void SaveViewportBeforeSaveGame() +{ + const Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + + if (w != NULL) { + _saved_scrollpos_x = w->viewport->scrollpos_x; + _saved_scrollpos_y = w->viewport->scrollpos_y; + _saved_scrollpos_zoom = w->viewport->zoom; + } +} + +void ResetViewportAfterLoadGame() +{ + Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + + w->viewport->scrollpos_x = _saved_scrollpos_x; + w->viewport->scrollpos_y = _saved_scrollpos_y; + w->viewport->dest_scrollpos_x = _saved_scrollpos_x; + w->viewport->dest_scrollpos_y = _saved_scrollpos_y; + + ViewPort *vp = w->viewport; + vp->zoom = min(_saved_scrollpos_zoom, ZOOM_LVL_MAX); + vp->virtual_width = ScaleByZoom(vp->width, vp->zoom); + vp->virtual_height = ScaleByZoom(vp->height, vp->zoom); + + DoZoomInOutWindow(ZOOM_NONE, w); // update button status + MarkWholeScreenDirty(); +} + + +static const SaveLoadGlobVarList _date_desc[] = { + SLEG_CONDVAR(_date, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), + SLEG_CONDVAR(_date, SLE_INT32, 31, SL_MAX_VERSION), + SLEG_VAR(_date_fract, SLE_UINT16), + SLEG_VAR(_tick_counter, SLE_UINT16), + SLEG_VAR(_vehicle_id_ctr_day, SLE_UINT16), + SLEG_VAR(_age_cargo_skip_counter, SLE_UINT8), + SLE_CONDNULL(1, 0, 45), + SLEG_CONDVAR(_cur_tileloop_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLEG_CONDVAR(_cur_tileloop_tile, SLE_UINT32, 6, SL_MAX_VERSION), + SLEG_VAR(_disaster_delay, SLE_UINT16), + SLEG_VAR(_station_tick_ctr, SLE_UINT16), + SLEG_VAR(_random.state[0], SLE_UINT32), + SLEG_VAR(_random.state[1], SLE_UINT32), + SLEG_CONDVAR(_cur_town_ctr, SLE_FILE_U8 | SLE_VAR_U32, 0, 9), + SLEG_CONDVAR(_cur_town_ctr, SLE_UINT32, 10, SL_MAX_VERSION), + SLEG_VAR(_cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32), + SLEG_VAR(_next_competitor_start, SLE_FILE_U16 | SLE_VAR_U32), + SLEG_VAR(_trees_tick_ctr, SLE_UINT8), + SLEG_CONDVAR(_pause_game, SLE_UINT8, 4, SL_MAX_VERSION), + SLEG_CONDVAR(_cur_town_iter, SLE_UINT32, 11, SL_MAX_VERSION), + SLEG_END() +}; + +/* Save load date related variables as well as persistent tick counters + * XXX: currently some unrelated stuff is just put here */ +static void SaveLoad_DATE() +{ + SlGlobList(_date_desc); +} + + +static const SaveLoadGlobVarList _view_desc[] = { + SLEG_CONDVAR(_saved_scrollpos_x, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), + SLEG_CONDVAR(_saved_scrollpos_x, SLE_INT32, 6, SL_MAX_VERSION), + SLEG_CONDVAR(_saved_scrollpos_y, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), + SLEG_CONDVAR(_saved_scrollpos_y, SLE_INT32, 6, SL_MAX_VERSION), + SLEG_VAR(_saved_scrollpos_zoom, SLE_UINT8), + SLEG_END() +}; + +static void SaveLoad_VIEW() +{ + SlGlobList(_view_desc); +} + +extern const ChunkHandler _misc_chunk_handlers[] = { + { 'DATE', SaveLoad_DATE, SaveLoad_DATE, CH_RIFF}, + { 'VIEW', SaveLoad_VIEW, SaveLoad_VIEW, CH_RIFF | CH_LAST}, +}; diff --git a/src/saveload/newgrf_sl.cpp b/src/saveload/newgrf_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/newgrf_sl.cpp @@ -0,0 +1,52 @@ +/* $Id$ */ + +/** @file newgrf_sl.cpp Code handling saving and loading of newgrf config */ + +#include "../stdafx.h" +#include "../newgrf_config.h" +#include "../core/bitmath_func.hpp" +#include "../core/alloc_func.hpp" +#include "../gfx_func.h" + +#include "saveload.h" + +static const SaveLoad _grfconfig_desc[] = { + SLE_STR(GRFConfig, filename, SLE_STR, 0x40), + SLE_VAR(GRFConfig, grfid, SLE_UINT32), + SLE_ARR(GRFConfig, md5sum, SLE_UINT8, 16), + SLE_ARR(GRFConfig, param, SLE_UINT32, 0x80), + SLE_VAR(GRFConfig, num_params, SLE_UINT8), + SLE_CONDVAR(GRFConfig, windows_paletted, SLE_BOOL, 101, SL_MAX_VERSION), + SLE_END() +}; + + +static void Save_NGRF() +{ + int index = 0; + + for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) { + if (HasBit(c->flags, GCF_STATIC)) continue; + SlSetArrayIndex(index++); + SlObject(c, _grfconfig_desc); + } +} + + +static void Load_NGRF() +{ + ClearGRFConfigList(&_grfconfig); + while (SlIterateArray() != -1) { + GRFConfig *c = CallocT(1); + SlObject(c, _grfconfig_desc); + if (CheckSavegameVersion(101)) c->windows_paletted = (_use_palette == PAL_WINDOWS); + AppendToGRFConfigList(&_grfconfig, c); + } + + /* Append static NewGRF configuration */ + AppendStaticGRFConfigs(&_grfconfig); +} + +extern const ChunkHandler _newgrf_chunk_handlers[] = { + { 'NGRF', Save_NGRF, Load_NGRF, CH_ARRAY | CH_LAST } +}; diff --git a/src/saveload/oldloader.cpp b/src/saveload/oldloader.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/oldloader.cpp @@ -0,0 +1,1736 @@ +/* $Id$ */ + +/** @file oldloader.cpp Loading of old TTD(patch) savegames. */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "../station_map.h" +#include "../town.h" +#include "../industry.h" +#include "../company_func.h" +#include "../company_base.h" +#include "../aircraft.h" +#include "../roadveh.h" +#include "../ship.h" +#include "../train.h" +#include "../signs_base.h" +#include "../debug.h" +#include "../depot_base.h" +#include "../newgrf_config.h" +#include "../ai/ai.h" +#include "../ai/default/default.h" +#include "../zoom_func.h" +#include "../functions.h" +#include "../date_func.h" +#include "../vehicle_func.h" +#include "../variables.h" +#include "../strings_func.h" +#include "../effectvehicle_base.h" + +#include "table/strings.h" + +#include "saveload.h" +#include "saveload_internal.h" + +enum { + HEADER_SIZE = 49, + BUFFER_SIZE = 4096, + + OLD_MAP_SIZE = 256 * 256 +}; + +struct LoadgameState { + FILE *file; + + uint chunk_size; + + bool decoding; + byte decode_char; + + uint buffer_count; + uint buffer_cur; + byte buffer[BUFFER_SIZE]; + + uint total_read; + bool failed; +}; + +/* OldChunk-Type */ +enum OldChunkType { + OC_SIMPLE = 0, + OC_NULL = 1, + OC_CHUNK = 2, + OC_ASSERT = 3, + /* 8 bits allocated (256 max) */ + + OC_VAR_I8 = 1 << 8, + OC_VAR_U8 = 2 << 8, + OC_VAR_I16 = 3 << 8, + OC_VAR_U16 = 4 << 8, + OC_VAR_I32 = 5 << 8, + OC_VAR_U32 = 6 << 8, + OC_VAR_I64 = 7 << 8, + /* 8 bits allocated (256 max) */ + + OC_FILE_I8 = 1 << 16, + OC_FILE_U8 = 2 << 16, + OC_FILE_I16 = 3 << 16, + OC_FILE_U16 = 4 << 16, + OC_FILE_I32 = 5 << 16, + OC_FILE_U32 = 6 << 16, + /* 8 bits allocated (256 max) */ + + OC_INT8 = OC_VAR_I8 | OC_FILE_I8, + OC_UINT8 = OC_VAR_U8 | OC_FILE_U8, + OC_INT16 = OC_VAR_I16 | OC_FILE_I16, + OC_UINT16 = OC_VAR_U16 | OC_FILE_U16, + OC_INT32 = OC_VAR_I32 | OC_FILE_I32, + OC_UINT32 = OC_VAR_U32 | OC_FILE_U32, + + OC_TILE = OC_VAR_U32 | OC_FILE_U16, + + /** + * Dereference the pointer once before writing to it, + * so we do not have to use big static arrays. + */ + OC_DEREFERENCE_POINTER = 1 << 31, + + OC_END = 0 ///< End of the whole chunk, all 32 bits set to zero +}; + +DECLARE_ENUM_AS_BIT_SET(OldChunkType); + +typedef bool OldChunkProc(LoadgameState *ls, int num); + +struct OldChunks { + OldChunkType type; ///< Type of field + uint32 amount; ///< Amount of fields + + void *ptr; ///< Pointer where to save the data (may only be set if offset is 0) + uint offset; ///< Offset from basepointer (may only be set if ptr is NULL) + OldChunkProc *proc; ///< Pointer to function that is called with OC_CHUNK +}; + +/* If it fails, check lines above.. */ +assert_compile(sizeof(TileIndex) == 4); + +extern SavegameType _savegame_type; +extern uint32 _ttdp_version; + +static uint32 _bump_assert_value; +static bool _read_ttdpatch_flags; + +static OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 8);} +static OldChunkType GetOldChunkVarType(OldChunkType type) {return (OldChunkType)(GB(type, 8, 8) << 8);} +static OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);} + +static inline byte CalcOldVarLen(OldChunkType type) +{ + static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8}; + byte length = GB(type, 8, 8); + assert(length != 0 && length < lengthof(type_mem_size)); + return type_mem_size[length]; +} + +/** + * + * Reads a byte from a file (do not call yourself, use ReadByte()) + * + */ +static byte ReadByteFromFile(LoadgameState *ls) +{ + /* To avoid slow reads, we read BUFFER_SIZE of bytes per time + and just return a byte per time */ + if (ls->buffer_cur >= ls->buffer_count) { + /* Read some new bytes from the file */ + int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file); + + /* We tried to read, but there is nothing in the file anymore.. */ + if (count == 0) { + DEBUG(oldloader, 0, "Read past end of file, loading failed"); + ls->failed = true; + } + + ls->buffer_count = count; + ls->buffer_cur = 0; + } + + return ls->buffer[ls->buffer_cur++]; +} + +/** + * + * Reads a byte from the buffer and decompress if needed + * + */ +static byte ReadByte(LoadgameState *ls) +{ + /* Old savegames have a nice compression algorithm (RLE) + which means that we have a chunk, which starts with a length + byte. If that byte is negative, we have to repeat the next byte + that many times ( + 1). Else, we need to read that amount of bytes. + Works pretty good if you have many zero's behind eachother */ + + if (ls->chunk_size == 0) { + /* Read new chunk */ + int8 new_byte = ReadByteFromFile(ls); + + if (new_byte < 0) { + /* Repeat next char for new_byte times */ + ls->decoding = true; + ls->decode_char = ReadByteFromFile(ls); + ls->chunk_size = -new_byte + 1; + } else { + ls->decoding = false; + ls->chunk_size = new_byte + 1; + } + } + + ls->total_read++; + ls->chunk_size--; + + return ls->decoding ? ls->decode_char : ReadByteFromFile(ls); +} + +static inline uint16 ReadUint16(LoadgameState *ls) +{ + byte x = ReadByte(ls); + return x | ReadByte(ls) << 8; +} + +static inline uint32 ReadUint32(LoadgameState *ls) +{ + uint16 x = ReadUint16(ls); + return x | ReadUint16(ls) << 16; +} + +/** + * + * Loads a chunk from the old savegame + * + */ +static bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks) +{ + const OldChunks *chunk = chunks; + byte *base_ptr = (byte*)base; + + while (chunk->type != OC_END) { + byte *ptr = (byte*)chunk->ptr; + if ((chunk->type & OC_DEREFERENCE_POINTER) != 0) ptr = *(byte**)ptr; + + for (uint i = 0; i < chunk->amount; i++) { + if (ls->failed) return false; + + /* Handle simple types */ + if (GetOldChunkType(chunk->type) != 0) { + switch (GetOldChunkType(chunk->type)) { + /* Just read the byte and forget about it */ + case OC_NULL: ReadByte(ls); break; + + case OC_CHUNK: + /* Call function, with 'i' as parameter to tell which item we + * are going to read */ + if (!chunk->proc(ls, i)) return false; + break; + + case OC_ASSERT: + DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value); + if (ls->total_read != chunk->offset + _bump_assert_value) ls->failed = true; + default: break; + } + } else { + uint64 res = 0; + + /* Reading from the file: bits 16 to 23 have the FILE type */ + switch (GetOldChunkFileType(chunk->type)) { + case OC_FILE_I8: res = (int8)ReadByte(ls); break; + case OC_FILE_U8: res = ReadByte(ls); break; + case OC_FILE_I16: res = (int16)ReadUint16(ls); break; + case OC_FILE_U16: res = ReadUint16(ls); break; + case OC_FILE_I32: res = (int32)ReadUint32(ls); break; + case OC_FILE_U32: res = ReadUint32(ls); break; + default: NOT_REACHED(); + } + + /* Sanity check */ + assert(base_ptr != NULL || chunk->ptr != NULL); + + /* Writing to the var: bits 8 to 15 have the VAR type */ + if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset; + + /* Write the data */ + switch (GetOldChunkVarType(chunk->type)) { + case OC_VAR_I8: *(int8 *)ptr = GB(res, 0, 8); break; + case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break; + case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break; + case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break; + case OC_VAR_I32:*(int32 *)ptr = res; break; + case OC_VAR_U32:*(uint32*)ptr = res; break; + case OC_VAR_I64:*(int64 *)ptr = res; break; + default: NOT_REACHED(); + } + + /* Increase pointer base for arrays when looping */ + if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type); + } + } + + chunk++; + } + + return true; +} + +/** + * + * Initialize some data before reading + * + */ +static void InitLoading(LoadgameState *ls) +{ + ls->chunk_size = 0; + ls->total_read = 0; + ls->failed = false; + + ls->decoding = false; + ls->decode_char = 0; + + ls->buffer_cur = 0; + ls->buffer_count = 0; + memset(ls->buffer, 0, BUFFER_SIZE); + + _bump_assert_value = 0; + + _savegame_type = SGT_TTD; + _ttdp_version = 0; + + _read_ttdpatch_flags = false; +} + + +/* + * Begin -- Stuff to fix the savegames to be OpenTTD compatible + */ + +extern uint32 GetOldTownName(uint32 townnameparts, byte old_town_name_type); + +static void FixOldTowns() +{ + Town *town; + + /* Convert town-names if needed */ + FOR_ALL_TOWNS(town) { + if (IsInsideMM(town->townnametype, 0x20C1, 0x20C3)) { + town->townnametype = SPECSTR_TOWNNAME_ENGLISH + _settings_game.game_creation.town_name; + town->townnameparts = GetOldTownName(town->townnameparts, _settings_game.game_creation.town_name); + } + } +} + +static StringID *_old_vehicle_names = NULL; + +static void FixOldVehicles() +{ + Vehicle* v; + + FOR_ALL_VEHICLES(v) { + v->name = CopyFromOldName(_old_vehicle_names[v->index]); + + /* We haven't used this bit for stations for ages */ + if (v->type == VEH_ROAD && + v->u.road.state != RVSB_IN_DEPOT && + v->u.road.state != RVSB_WORMHOLE) { + ClrBit(v->u.road.state, RVS_IS_STOPPING); + } + + /* The subtype should be 0, but it sometimes isn't :( */ + if (v->type == VEH_ROAD) v->subtype = 0; + + /* Sometimes primary vehicles would have a nothing (invalid) order + * or vehicles that could not have an order would still have a + * (loading) order which causes assertions and the like later on. + */ + if (!IsCompanyBuildableVehicleType(v) || + (v->IsPrimaryVehicle() && v->current_order.IsType(OT_NOTHING))) { + v->current_order.MakeDummy(); + } + + /* Shared orders are fixed in AfterLoadVehicles now */ + } +} + +/* + * End -- Stuff to fix the savegames to be OpenTTD compatible + */ + + +/* Help: + * - OCL_SVAR: load 'type' to offset 'offset' in a struct of type 'base', which must also + * be given via base in LoadChunk() as real pointer + * - OCL_VAR: load 'type' to a global var + * - OCL_END: every struct must end with this + * - OCL_NULL: read 'amount' of bytes and send them to /dev/null or something + * - OCL_CHUNK: load an other proc to load a part of the savegame, 'amount' times + * - OCL_ASSERT: to check if we are really at the place we expect to be.. because old savegames are too binary to be sure ;) + */ +#define OCL_SVAR(type, base, offset) { type, 1, NULL, (uint)cpp_offsetof(base, offset), NULL } +#define OCL_VAR(type, amount, pointer) { type, amount, pointer, 0, NULL } +#define OCL_END() { OC_END, 0, NULL, 0, NULL } +#define OCL_NULL(amount) { OC_NULL, amount, NULL, 0, NULL } +#define OCL_CHUNK(amount, proc) { OC_CHUNK, amount, NULL, 0, proc } +#define OCL_ASSERT(size) { OC_ASSERT, 1, NULL, size, NULL } + +/* The savegames has some hard-coded pointers, because it always enters the same + piece of memory.. we don't.. so we need to remap ;) + Old Towns are 94 bytes big + Old Orders are 2 bytes big */ +#define REMAP_TOWN_IDX(x) ((x) - (0x0459154 - 0x0458EF0)) / 94 +#define REMAP_ORDER_IDX(x) ((x) - (0x045AB08 - 0x0458EF0)) / 2 + +extern TileIndex *_animated_tile_list; +extern uint _animated_tile_count; +extern char *_old_name_array; + +static byte _old_vehicle_multiplier; +static uint8 *_old_map3; +static uint32 _old_town_index; +static uint16 _old_string_id; +static uint16 _old_string_id_2; +static uint16 _old_extra_chunk_nums; + +static void ReadTTDPatchFlags() +{ + if (_read_ttdpatch_flags) return; + + _read_ttdpatch_flags = true; + + /* TTDPatch misuses _old_map3 for flags.. read them! */ + _old_vehicle_multiplier = _old_map3[0]; + /* Somehow.... there was an error in some savegames, so 0 becomes 1 + and 1 becomes 2. The rest of the values are okay */ + if (_old_vehicle_multiplier < 2) _old_vehicle_multiplier++; + + _old_vehicle_names = MallocT(_old_vehicle_multiplier * 850); + + /* TTDPatch increases the Vehicle-part in the middle of the game, + so if the multipler is anything else but 1, the assert fails.. + bump the assert value so it doesn't! + (1 multipler == 850 vehicles + 1 vehicle == 128 bytes */ + _bump_assert_value = (_old_vehicle_multiplier - 1) * 850 * 128; + + for (uint i = 0; i < 17; i++) { // check tile 0, too + if (_old_map3[i] != 0) _savegame_type = SGT_TTDP1; + } + + /* Check if we have a modern TTDPatch savegame (has extra data all around) */ + if (memcmp(&_old_map3[0x1FFFA], "TTDp", 4) == 0) _savegame_type = SGT_TTDP2; + + _old_extra_chunk_nums = _old_map3[_savegame_type == SGT_TTDP2 ? 0x1FFFE : 0x2]; + + /* Clean the misused places */ + for (uint i = 0; i < 17; i++) _old_map3[i] = 0; + for (uint i = 0x1FE00; i < 0x20000; i++) _old_map3[i] = 0; + + if (_savegame_type == SGT_TTDP2) DEBUG(oldloader, 2, "Found TTDPatch game"); + + DEBUG(oldloader, 3, "Vehicle-multiplier is set to %d (%d vehicles)", _old_vehicle_multiplier, _old_vehicle_multiplier * 850); +} + +static const OldChunks town_chunk[] = { + OCL_SVAR( OC_TILE, Town, xy ), + OCL_NULL( 2 ), ///< population, no longer in use + OCL_SVAR( OC_UINT16, Town, townnametype ), + OCL_SVAR( OC_UINT32, Town, townnameparts ), + OCL_SVAR( OC_UINT8, Town, grow_counter ), + OCL_NULL( 1 ), ///< sort_index, no longer in use + OCL_NULL( 4 ), ///< sign-coordinates, no longer in use + OCL_NULL( 2 ), ///< namewidth, no longer in use + OCL_SVAR( OC_UINT16, Town, flags12 ), + OCL_NULL( 10 ), ///< radius, no longer in use + + OCL_SVAR( OC_UINT16, Town, ratings[0] ), + OCL_SVAR( OC_UINT16, Town, ratings[1] ), + OCL_SVAR( OC_UINT16, Town, ratings[2] ), + OCL_SVAR( OC_UINT16, Town, ratings[3] ), + OCL_SVAR( OC_UINT16, Town, ratings[4] ), + OCL_SVAR( OC_UINT16, Town, ratings[5] ), + OCL_SVAR( OC_UINT16, Town, ratings[6] ), + OCL_SVAR( OC_UINT16, Town, ratings[7] ), + + /* XXX - This is pretty odd.. we read 32bit, but only write 16bit.. sure there is + nothing changed ? ? */ + OCL_SVAR( OC_FILE_U32 | OC_VAR_U16, Town, have_ratings ), + OCL_SVAR( OC_FILE_U32 | OC_VAR_U16, Town, statues ), + OCL_NULL( 2 ), ///< num_houses, no longer in use + OCL_SVAR( OC_UINT8, Town, time_until_rebuild ), + OCL_SVAR( OC_UINT8, Town, growth_rate ), + + OCL_SVAR( OC_UINT16, Town, new_max_pass ), + OCL_SVAR( OC_UINT16, Town, new_max_mail ), + OCL_SVAR( OC_UINT16, Town, new_act_pass ), + OCL_SVAR( OC_UINT16, Town, new_act_mail ), + OCL_SVAR( OC_UINT16, Town, max_pass ), + OCL_SVAR( OC_UINT16, Town, max_mail ), + OCL_SVAR( OC_UINT16, Town, act_pass ), + OCL_SVAR( OC_UINT16, Town, act_mail ), + + OCL_SVAR( OC_UINT8, Town, pct_pass_transported ), + OCL_SVAR( OC_UINT8, Town, pct_mail_transported ), + + OCL_SVAR( OC_UINT16, Town, new_act_food ), + OCL_SVAR( OC_UINT16, Town, new_act_water ), + OCL_SVAR( OC_UINT16, Town, act_food ), + OCL_SVAR( OC_UINT16, Town, act_water ), + + OCL_SVAR( OC_UINT8, Town, road_build_months ), + OCL_SVAR( OC_UINT8, Town, fund_buildings_months ), + + OCL_NULL( 8 ), ///< some junk at the end of the record + + OCL_END() +}; +static bool LoadOldTown(LoadgameState *ls, int num) +{ + Town *t = new (num) Town(); + if (!LoadChunk(ls, t, town_chunk)) return false; + + if (t->xy == 0) t->xy = INVALID_TILE; + + return true; +} + +static uint16 _old_order; +static const OldChunks order_chunk[] = { + OCL_VAR ( OC_UINT16, 1, &_old_order ), + OCL_END() +}; + +static bool LoadOldOrder(LoadgameState *ls, int num) +{ + if (!LoadChunk(ls, NULL, order_chunk)) return false; + + new (num) Order(UnpackOldOrder(_old_order)); + + /* Relink the orders to eachother (in TTD(Patch) the orders for one + vehicle are behind eachother, with an invalid order (OT_NOTHING) as indication that + it is the last order */ + if (num > 0 && GetOrder(num)->IsValid()) + GetOrder(num - 1)->next = GetOrder(num); + + return true; +} + +static bool LoadOldAnimTileList(LoadgameState *ls, int num) +{ + /* This is sligthly hackish - we must load a chunk into an array whose + * address isn't static, but instead pointed to by _animated_tile_list. + * To achieve that, create an OldChunks list on the stack on the fly. + * The list cannot be static because the value of _animated_tile_list + * can change between calls. */ + + const OldChunks anim_chunk[] = { + OCL_VAR ( OC_TILE, 256, _animated_tile_list ), + OCL_END () + }; + + if (!LoadChunk(ls, NULL, anim_chunk)) return false; + + /* Update the animated tile counter by counting till the first zero in the array */ + for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) { + if (_animated_tile_list[_animated_tile_count] == 0) break; + } + + return true; +} + +static const OldChunks depot_chunk[] = { + OCL_SVAR( OC_TILE, Depot, xy ), + OCL_VAR ( OC_UINT32, 1, &_old_town_index ), + OCL_END() +}; + +static bool LoadOldDepot(LoadgameState *ls, int num) +{ + Depot *d = new (num) Depot(); + if (!LoadChunk(ls, d, depot_chunk)) return false; + + if (d->xy != 0) { + GetDepot(num)->town_index = REMAP_TOWN_IDX(_old_town_index); + } else { + d->xy = INVALID_TILE; + } + + return true; +} + +static int32 _old_price; +static uint16 _old_price_frac; +static const OldChunks price_chunk[] = { + OCL_VAR ( OC_INT32, 1, &_old_price ), + OCL_VAR ( OC_UINT16, 1, &_old_price_frac ), + OCL_END() +}; + +static bool LoadOldPrice(LoadgameState *ls, int num) +{ + if (!LoadChunk(ls, NULL, price_chunk)) return false; + + /* We use a struct to store the prices, but they are ints in a row.. + so just access the struct as an array of int32's */ + ((Money*)&_price)[num] = _old_price; + _price_frac[num] = _old_price_frac; + + return true; +} + +static const OldChunks cargo_payment_rate_chunk[] = { + OCL_VAR ( OC_INT32, 1, &_old_price ), + OCL_VAR ( OC_UINT16, 1, &_old_price_frac ), + + OCL_NULL( 2 ), ///< Junk + OCL_END() +}; + +static bool LoadOldCargoPaymentRate(LoadgameState *ls, int num) +{ + if (!LoadChunk(ls, NULL, cargo_payment_rate_chunk)) return false; + + _cargo_payment_rates[num] = -_old_price; + _cargo_payment_rates_frac[num] = _old_price_frac; + + return true; +} + +static uint _current_station_id; +static uint16 _waiting_acceptance; +static uint8 _cargo_source; +static uint8 _cargo_days; + +static const OldChunks goods_chunk[] = { + OCL_VAR ( OC_UINT16, 1, &_waiting_acceptance ), + OCL_SVAR( OC_UINT8, GoodsEntry, days_since_pickup ), + OCL_SVAR( OC_UINT8, GoodsEntry, rating ), + OCL_VAR ( OC_UINT8, 1, &_cargo_source ), + OCL_VAR ( OC_UINT8, 1, &_cargo_days ), + OCL_SVAR( OC_UINT8, GoodsEntry, last_speed ), + OCL_SVAR( OC_UINT8, GoodsEntry, last_age ), + + OCL_END() +}; + +static bool LoadOldGood(LoadgameState *ls, int num) +{ + Station *st = GetStation(_current_station_id); + GoodsEntry *ge = &st->goods[num]; + bool ret = LoadChunk(ls, ge, goods_chunk); + if (!ret) return false; + + SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); + SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, _cargo_source != 0xFF); + if (GB(_waiting_acceptance, 0, 12) != 0) { + CargoPacket *cp = new CargoPacket(); + cp->source = (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source; + cp->count = GB(_waiting_acceptance, 0, 12); + cp->days_in_transit = _cargo_days; + ge->cargo.Append(cp); + } + return ret; +} + +static const OldChunks station_chunk[] = { + OCL_SVAR( OC_TILE, Station, xy ), + OCL_VAR ( OC_UINT32, 1, &_old_town_index ), + + OCL_NULL( 4 ), ///< bus/lorry tile + OCL_SVAR( OC_TILE, Station, train_tile ), + OCL_SVAR( OC_TILE, Station, airport_tile ), + OCL_SVAR( OC_TILE, Station, dock_tile ), + OCL_SVAR( OC_UINT8, Station, trainst_w ), + + OCL_NULL( 1 ), ///< sort-index, no longer in use + OCL_NULL( 2 ), ///< sign-width, no longer in use + + OCL_VAR ( OC_UINT16, 1, &_old_string_id ), + + OCL_NULL( 4 ), ///< sign left/top, no longer in use + + OCL_SVAR( OC_UINT16, Station, had_vehicle_of_type ), + + OCL_CHUNK( 12, LoadOldGood ), + + OCL_SVAR( OC_UINT8, Station, time_since_load ), + OCL_SVAR( OC_UINT8, Station, time_since_unload ), + OCL_SVAR( OC_UINT8, Station, delete_ctr ), + OCL_SVAR( OC_UINT8, Station, owner ), + OCL_SVAR( OC_UINT8, Station, facilities ), + OCL_SVAR( OC_UINT8, Station, airport_type ), + /* Bus/truck status, no longer in use + * Blocked months + * Unknown + */ + OCL_NULL( 4 ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Station, airport_flags ), + OCL_NULL( 2 ), ///< last_vehicle. now last_vehicle_type + + OCL_NULL( 4 ), ///< Junk at end of chunk + + OCL_END() +}; +static bool LoadOldStation(LoadgameState *ls, int num) +{ + Station *st = new (num) Station(); + _current_station_id = num; + + if (!LoadChunk(ls, st, station_chunk)) + return false; + + if (st->xy != 0) { + st->town = GetTown(REMAP_TOWN_IDX(_old_town_index)); + st->string_id = RemapOldStringID(_old_string_id); + } else { + st->xy = INVALID_TILE; + } + + return true; +} + +static const OldChunks industry_chunk[] = { + OCL_SVAR( OC_TILE, Industry, xy ), + OCL_VAR ( OC_UINT32, 1, &_old_town_index ), + OCL_SVAR( OC_UINT8, Industry, width ), + OCL_SVAR( OC_UINT8, Industry, height ), + OCL_NULL( 2 ), ///< used to be industry's produced_cargo + + OCL_SVAR( OC_UINT16, Industry, produced_cargo_waiting[0] ), + OCL_SVAR( OC_UINT16, Industry, produced_cargo_waiting[1] ), + + OCL_SVAR( OC_UINT8, Industry, production_rate[0] ), + OCL_SVAR( OC_UINT8, Industry, production_rate[1] ), + + OCL_NULL( 3 ), ///< used to be industry's accepts_cargo + + OCL_SVAR( OC_UINT8, Industry, prod_level ), + + OCL_SVAR( OC_UINT16, Industry, this_month_production[0] ), + OCL_SVAR( OC_UINT16, Industry, this_month_production[1] ), + OCL_SVAR( OC_UINT16, Industry, this_month_transported[0] ), + OCL_SVAR( OC_UINT16, Industry, this_month_transported[1] ), + + OCL_SVAR( OC_UINT8, Industry, last_month_pct_transported[0] ), + OCL_SVAR( OC_UINT8, Industry, last_month_pct_transported[1] ), + + OCL_SVAR( OC_UINT16, Industry, last_month_production[0] ), + OCL_SVAR( OC_UINT16, Industry, last_month_production[1] ), + OCL_SVAR( OC_UINT16, Industry, last_month_transported[0] ), + OCL_SVAR( OC_UINT16, Industry, last_month_transported[1] ), + + OCL_SVAR( OC_UINT8, Industry, type ), + OCL_SVAR( OC_UINT8, Industry, owner ), + OCL_SVAR( OC_UINT8, Industry, random_color ), + OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Industry, last_prod_year ), + OCL_SVAR( OC_UINT16, Industry, counter ), + OCL_SVAR( OC_UINT8, Industry, was_cargo_delivered ), + + OCL_NULL( 9 ), ///< Random junk at the end of this chunk + + OCL_END() +}; + +static bool LoadOldIndustry(LoadgameState *ls, int num) +{ + Industry *i = new (num) Industry(); + if (!LoadChunk(ls, i, industry_chunk)) return false; + + if (i->xy != 0) { + i->town = GetTown(REMAP_TOWN_IDX(_old_town_index)); + IncIndustryTypeCount(i->type); + } else { + i->xy = INVALID_TILE; + } + + return true; +} + +static CompanyID _current_company_id; +static int32 _old_yearly; + +static const OldChunks _company_yearly_chunk[] = { + OCL_VAR( OC_INT32, 1, &_old_yearly ), + OCL_END() +}; + +static bool OldCompanyYearly(LoadgameState *ls, int num) +{ + int i; + Company *c = GetCompany(_current_company_id); + + for (i = 0; i < 13; i++) { + if (!LoadChunk(ls, NULL, _company_yearly_chunk)) return false; + + c->yearly_expenses[num][i] = _old_yearly; + } + + return true; +} + +static const OldChunks _company_economy_chunk[] = { + OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, income ), + OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, expenses ), + OCL_SVAR( OC_INT32, CompanyEconomyEntry, delivered_cargo ), + OCL_SVAR( OC_INT32, CompanyEconomyEntry, performance_history ), + OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, company_value ), + + OCL_END() +}; + +static bool OldCompanyEconomy(LoadgameState *ls, int num) +{ + int i; + Company *c = GetCompany(_current_company_id); + + if (!LoadChunk(ls, &c->cur_economy, _company_economy_chunk)) return false; + + /* Don't ask, but the number in TTD(Patch) are inversed to OpenTTD */ + c->cur_economy.income = -c->cur_economy.income; + c->cur_economy.expenses = -c->cur_economy.expenses; + + for (i = 0; i < 24; i++) { + if (!LoadChunk(ls, &c->old_economy[i], _company_economy_chunk)) return false; + + c->old_economy[i].income = -c->old_economy[i].income; + c->old_economy[i].expenses = -c->old_economy[i].expenses; + } + + return true; +} + +static const OldChunks _company_ai_build_rec_chunk[] = { + OCL_SVAR( OC_TILE, AiBuildRec, spec_tile ), + OCL_SVAR( OC_TILE, AiBuildRec, use_tile ), + OCL_SVAR( OC_UINT8, AiBuildRec, rand_rng ), + OCL_SVAR( OC_UINT8, AiBuildRec, cur_building_rule ), + OCL_SVAR( OC_UINT8, AiBuildRec, unk6 ), + OCL_SVAR( OC_UINT8, AiBuildRec, unk7 ), + OCL_SVAR( OC_UINT8, AiBuildRec, buildcmd_a ), + OCL_SVAR( OC_UINT8, AiBuildRec, buildcmd_b ), + OCL_SVAR( OC_UINT8, AiBuildRec, direction ), + OCL_SVAR( OC_UINT8, AiBuildRec, cargo ), + + OCL_NULL( 8 ), ///< Junk... + + OCL_END() +}; + +static bool OldLoadAIBuildRec(LoadgameState *ls, int num) +{ + Company *c = GetCompany(_current_company_id); + + switch (num) { + case 0: return LoadChunk(ls, &_companies_ai[c->index].src, _company_ai_build_rec_chunk); + case 1: return LoadChunk(ls, &_companies_ai[c->index].dst, _company_ai_build_rec_chunk); + case 2: return LoadChunk(ls, &_companies_ai[c->index].mid1, _company_ai_build_rec_chunk); + case 3: return LoadChunk(ls, &_companies_ai[c->index].mid2, _company_ai_build_rec_chunk); + } + + return false; +} +static const OldChunks _company_ai_chunk[] = { + OCL_SVAR( OC_UINT8, CompanyAI, state ), + OCL_NULL( 1 ), ///< Junk + OCL_SVAR( OC_UINT8, CompanyAI, state_mode ), + OCL_SVAR( OC_UINT16, CompanyAI, state_counter ), + OCL_SVAR( OC_UINT16, CompanyAI, timeout_counter ), + + OCL_CHUNK( 4, OldLoadAIBuildRec ), + + OCL_NULL( 20 ), ///< More junk + + OCL_SVAR( OC_UINT8, CompanyAI, cargo_type ), + OCL_SVAR( OC_UINT8, CompanyAI, num_wagons ), + OCL_SVAR( OC_UINT8, CompanyAI, build_kind ), + OCL_SVAR( OC_UINT8, CompanyAI, num_build_rec ), + OCL_SVAR( OC_UINT8, CompanyAI, num_loco_to_build ), + OCL_SVAR( OC_UINT8, CompanyAI, num_want_fullload ), + + OCL_NULL( 14 ), ///< Oh no more junk :| + + OCL_NULL( 2 ), ///< Loco-id, not used + + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[0] ), + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[1] ), + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[2] ), + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[3] ), + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[4] ), + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[5] ), + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[6] ), + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[7] ), + OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[8] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[0] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[1] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[2] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[3] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[4] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[5] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[6] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[7] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[8] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[9] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[10] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[11] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[12] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[13] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[14] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[15] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[16] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[17] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[18] ), + OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[19] ), + + OCL_SVAR( OC_UINT16, CompanyAI, start_tile_a ), + OCL_SVAR( OC_UINT16, CompanyAI, start_tile_b ), + OCL_SVAR( OC_UINT16, CompanyAI, cur_tile_a ), + OCL_SVAR( OC_UINT16, CompanyAI, cur_tile_b ), + + OCL_SVAR( OC_UINT8, CompanyAI, start_dir_a ), + OCL_SVAR( OC_UINT8, CompanyAI, start_dir_b ), + OCL_SVAR( OC_UINT8, CompanyAI, cur_dir_a ), + OCL_SVAR( OC_UINT8, CompanyAI, cur_dir_b ), + + OCL_SVAR( OC_UINT8, CompanyAI, banned_tile_count ), + + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[0] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[0] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[1] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[1] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[2] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[2] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[3] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[3] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[4] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[4] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[5] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[5] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[6] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[6] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[7] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[7] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[8] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[8] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[9] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[9] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[10] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[10] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[11] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[11] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[12] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[12] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[13] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[13] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[14] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[14] ), + OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[15] ), + OCL_SVAR( OC_UINT8, CompanyAI, banned_val[15] ), + + OCL_SVAR( OC_UINT8, CompanyAI, railtype_to_use ), + OCL_SVAR( OC_UINT8, CompanyAI, route_type_mask ), + + OCL_END() +}; + +static bool OldCompanyAI(LoadgameState *ls, int num) +{ + return LoadChunk(ls, &_companies_ai[_current_company_id], _company_ai_chunk); +} + +uint8 ai_tick; +static const OldChunks _company_chunk[] = { + OCL_VAR ( OC_UINT16, 1, &_old_string_id ), + OCL_SVAR( OC_UINT32, Company, name_2 ), + OCL_SVAR( OC_UINT32, Company, face ), + OCL_VAR ( OC_UINT16, 1, &_old_string_id_2 ), + OCL_SVAR( OC_UINT32, Company, president_name_2 ), + + OCL_SVAR( OC_INT32, Company, money ), + OCL_SVAR( OC_INT32, Company, current_loan ), + + OCL_SVAR( OC_UINT8, Company, colour ), + OCL_SVAR( OC_UINT8, Company, money_fraction ), + OCL_SVAR( OC_UINT8, Company, quarters_of_bankrupcy ), + OCL_SVAR( OC_UINT8, Company, bankrupt_asked ), + OCL_SVAR( OC_FILE_U32 | OC_VAR_I64, Company, bankrupt_value ), + OCL_SVAR( OC_UINT16, Company, bankrupt_timeout ), + + OCL_SVAR( OC_UINT32, Company, cargo_types ), + + OCL_CHUNK( 3, OldCompanyYearly ), + OCL_CHUNK( 1, OldCompanyEconomy ), + + OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Company, inaugurated_year), + OCL_SVAR( OC_TILE, Company, last_build_coordinate ), + OCL_SVAR( OC_UINT8, Company, num_valid_stat_ent ), + + OCL_CHUNK( 1, OldCompanyAI ), + + OCL_SVAR( OC_UINT8, Company, block_preview ), + OCL_VAR( OC_UINT8, 1, &ai_tick ), + OCL_SVAR( OC_UINT8, Company, avail_railtypes ), + OCL_SVAR( OC_TILE, Company, location_of_HQ ), + OCL_SVAR( OC_UINT8, Company, share_owners[0] ), + OCL_SVAR( OC_UINT8, Company, share_owners[1] ), + OCL_SVAR( OC_UINT8, Company, share_owners[2] ), + OCL_SVAR( OC_UINT8, Company, share_owners[3] ), + + OCL_NULL( 8 ), ///< junk at end of chunk + + OCL_END() +}; + +static bool LoadOldCompany(LoadgameState *ls, int num) +{ + Company *c = new (num) Company(); + + _current_company_id = (CompanyID)num; + + if (!LoadChunk(ls, c, _company_chunk)) return false; + + if (_old_string_id == 0) { + delete c; + return true; + } + + c->name_1 = RemapOldStringID(_old_string_id); + c->president_name_1 = RemapOldStringID(_old_string_id_2); + _companies_ai[_current_company_id].tick = ai_tick; + + if (num == 0) { + /* If the first company has no name, make sure we call it UNNAMED */ + if (c->name_1 == 0) + c->name_1 = STR_SV_UNNAMED; + } else { + /* Beside some multiplayer maps (1 on 1), which we don't official support, + * all other companys are an AI.. mark them as such */ + c->is_ai = true; + } + + /* Sometimes it is better to not ask.. in old scenarios, the money + * was always 893288 pounds. In the newer versions this is correct, + * but correct for those oldies + * Ps: this also means that if you had exact 893288 pounds, you will go back + * to 100000.. this is a very VERY small chance ;) */ + if (c->money == 893288) c->money = c->current_loan = 100000; + + _company_colours[num] = c->colour; + c->inaugurated_year -= ORIGINAL_BASE_YEAR; + + /* State 20 for AI companies is sell vehicle. Since the AI struct is not + * really figured out as of now, _companies_ai[c->index].cur_veh; needed for 'sell vehicle' + * is NULL and the function will crash. To fix this, just change the state + * to some harmless state, like 'loop vehicle'; 1 */ + if (!IsHumanCompany((CompanyID)num) && _companies_ai[c->index].state == 20) _companies_ai[c->index].state = 1; + + if (c->is_ai && (!_networking || _network_server) && _ai.enabled) + AI_StartNewAI(c->index); + + return true; +} + +static uint32 _old_order_ptr; +static uint16 _old_next_ptr; +static uint32 _current_vehicle_id; + +static const OldChunks vehicle_train_chunk[] = { + OCL_SVAR( OC_UINT8, VehicleRail, track ), + OCL_SVAR( OC_UINT8, VehicleRail, force_proceed ), + OCL_SVAR( OC_UINT16, VehicleRail, crash_anim_pos ), + OCL_SVAR( OC_UINT8, VehicleRail, railtype ), + + OCL_NULL( 5 ), ///< Junk + + OCL_END() +}; + +static const OldChunks vehicle_road_chunk[] = { + OCL_SVAR( OC_UINT8, VehicleRoad, state ), + OCL_SVAR( OC_UINT8, VehicleRoad, frame ), + OCL_SVAR( OC_UINT16, VehicleRoad, blocked_ctr ), + OCL_SVAR( OC_UINT8, VehicleRoad, overtaking ), + OCL_SVAR( OC_UINT8, VehicleRoad, overtaking_ctr ), + OCL_SVAR( OC_UINT16, VehicleRoad, crashed_ctr ), + OCL_SVAR( OC_UINT8, VehicleRoad, reverse_ctr ), + + OCL_NULL( 1 ), ///< Junk + + OCL_END() +}; + +static const OldChunks vehicle_ship_chunk[] = { + OCL_SVAR( OC_UINT8, VehicleShip, state ), + + OCL_NULL( 9 ), ///< Junk + + OCL_END() +}; + +static const OldChunks vehicle_air_chunk[] = { + OCL_SVAR( OC_UINT8, VehicleAir, pos ), + OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, VehicleAir, targetairport ), + OCL_SVAR( OC_UINT16, VehicleAir, crashed_counter ), + OCL_SVAR( OC_UINT8, VehicleAir, state ), + + OCL_NULL( 5 ), ///< Junk + + OCL_END() +}; + +static const OldChunks vehicle_effect_chunk[] = { + OCL_SVAR( OC_UINT16, VehicleEffect, animation_state ), + OCL_SVAR( OC_UINT8, VehicleEffect, animation_substate ), + + OCL_NULL( 7 ), // Junk + + OCL_END() +}; + +static const OldChunks vehicle_disaster_chunk[] = { + OCL_SVAR( OC_UINT16, VehicleDisaster, image_override ), + OCL_SVAR( OC_UINT16, VehicleDisaster, big_ufo_destroyer_target ), + + OCL_NULL( 6 ), ///< Junk + + OCL_END() +}; + +static const OldChunks vehicle_empty_chunk[] = { + OCL_NULL( 10 ), ///< Junk + + OCL_END() +}; + +static bool LoadOldVehicleUnion(LoadgameState *ls, int num) +{ + Vehicle *v = GetVehicle(_current_vehicle_id); + uint temp = ls->total_read; + bool res; + + switch (v->type) { + default: NOT_REACHED(); + case VEH_INVALID : res = LoadChunk(ls, NULL, vehicle_empty_chunk); break; + case VEH_TRAIN : res = LoadChunk(ls, &v->u.rail, vehicle_train_chunk); break; + case VEH_ROAD : res = LoadChunk(ls, &v->u.road, vehicle_road_chunk); break; + case VEH_SHIP : res = LoadChunk(ls, &v->u.ship, vehicle_ship_chunk); break; + case VEH_AIRCRAFT: res = LoadChunk(ls, &v->u.air, vehicle_air_chunk); break; + case VEH_EFFECT : res = LoadChunk(ls, &v->u.effect, vehicle_effect_chunk); break; + case VEH_DISASTER: res = LoadChunk(ls, &v->u.disaster, vehicle_disaster_chunk); break; + } + + /* This chunk size should always be 10 bytes */ + if (ls->total_read - temp != 10) { + DEBUG(oldloader, 0, "Assert failed in VehicleUnion: invalid chunk size"); + return false; + } + + return res; +} + +static uint16 _cargo_count; + +static const OldChunks vehicle_chunk[] = { + OCL_SVAR( OC_UINT8, Vehicle, subtype ), + + OCL_NULL( 2 ), ///< Hash, calculated automatically + OCL_NULL( 2 ), ///< Index, calculated automatically + + OCL_VAR ( OC_UINT32, 1, &_old_order_ptr ), + OCL_VAR ( OC_UINT16, 1, &_old_order ), + + OCL_NULL ( 1 ), ///< num_orders, now calculated + OCL_SVAR( OC_UINT8, Vehicle, cur_order_index ), + OCL_SVAR( OC_TILE, Vehicle, dest_tile ), + OCL_SVAR( OC_UINT16, Vehicle, load_unload_time_rem ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, date_of_last_service ), + OCL_SVAR( OC_UINT16, Vehicle, service_interval ), + OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, last_station_visited ), + OCL_SVAR( OC_UINT8, Vehicle, tick_counter ), + OCL_SVAR( OC_UINT16, Vehicle, max_speed ), + + OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, x_pos ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, y_pos ), + OCL_SVAR( OC_UINT8, Vehicle, z_pos ), + OCL_SVAR( OC_UINT8, Vehicle, direction ), + OCL_NULL( 2 ), ///< x_offs and y_offs, calculated automatically + OCL_NULL( 2 ), ///< x_extent and y_extent, calculated automatically + OCL_NULL( 1 ), ///< z_extent, calculated automatically + + OCL_SVAR( OC_UINT8, Vehicle, owner ), + OCL_SVAR( OC_TILE, Vehicle, tile ), + OCL_SVAR( OC_UINT16, Vehicle, cur_image ), + + OCL_NULL( 8 ), ///< Vehicle sprite box, calculated automatically + + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, Vehicle, vehstatus ), + OCL_SVAR( OC_UINT16, Vehicle, cur_speed ), + OCL_SVAR( OC_UINT8, Vehicle, subspeed ), + OCL_SVAR( OC_UINT8, Vehicle, acceleration ), + OCL_SVAR( OC_UINT8, Vehicle, progress ), + + OCL_SVAR( OC_UINT8, Vehicle, cargo_type ), + OCL_SVAR( OC_UINT16, Vehicle, cargo_cap ), + OCL_VAR ( OC_UINT16, 1, &_cargo_count ), + OCL_VAR ( OC_UINT8, 1, &_cargo_source ), + OCL_VAR ( OC_UINT8, 1, &_cargo_days ), + + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, age ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, max_age ), + OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Vehicle, build_year ), + OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, unitnumber ), + + OCL_SVAR( OC_UINT16, Vehicle, engine_type ), + + OCL_SVAR( OC_UINT8, Vehicle, spritenum ), + OCL_SVAR( OC_UINT8, Vehicle, day_counter ), + + OCL_SVAR( OC_UINT8, Vehicle, breakdowns_since_last_service ), + OCL_SVAR( OC_UINT8, Vehicle, breakdown_ctr ), + OCL_SVAR( OC_UINT8, Vehicle, breakdown_delay ), + OCL_SVAR( OC_UINT8, Vehicle, breakdown_chance ), + + OCL_SVAR( OC_UINT16, Vehicle, reliability ), + OCL_SVAR( OC_UINT16, Vehicle, reliability_spd_dec ), + + OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, Vehicle, profit_this_year ), + OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, Vehicle, profit_last_year ), + + OCL_VAR ( OC_UINT16, 1, &_old_next_ptr ), + + OCL_SVAR( OC_UINT32, Vehicle, value ), + + OCL_VAR ( OC_UINT16, 1, &_old_string_id ), + + OCL_CHUNK( 1, LoadOldVehicleUnion ), + + OCL_NULL( 20 ), ///< Junk at end of struct (TTDPatch has some data in it) + + OCL_END() +}; + +bool LoadOldVehicle(LoadgameState *ls, int num) +{ + uint i; + + /* Read the TTDPatch flags, because we need some info from it */ + ReadTTDPatchFlags(); + + for (i = 0; i < _old_vehicle_multiplier; i++) { + _current_vehicle_id = num * _old_vehicle_multiplier + i; + + /* Read the vehicle type and allocate the right vehicle */ + Vehicle *v; + switch (ReadByte(ls)) { + default: NOT_REACHED(); + case 0x00 /*VEH_INVALID */: v = new (_current_vehicle_id) InvalidVehicle(); break; + case 0x10 /*VEH_TRAIN */: v = new (_current_vehicle_id) Train(); break; + case 0x11 /*VEH_ROAD */: v = new (_current_vehicle_id) RoadVehicle(); break; + case 0x12 /*VEH_SHIP */: v = new (_current_vehicle_id) Ship(); break; + case 0x13 /*VEH_AIRCRAFT*/: v = new (_current_vehicle_id) Aircraft(); break; + case 0x14 /*VEH_EFFECT */: v = new (_current_vehicle_id) EffectVehicle(); break; + case 0x15 /*VEH_DISASTER*/: v = new (_current_vehicle_id) DisasterVehicle(); break; + } + if (!LoadChunk(ls, v, vehicle_chunk)) return false; + + /* This should be consistent, else we have a big problem... */ + if (v->index != _current_vehicle_id) { + DEBUG(oldloader, 0, "Loading failed - vehicle-array is invalid"); + return false; + } + + if (_old_order_ptr != 0 && _old_order_ptr != 0xFFFFFFFF) { + uint old_id = REMAP_ORDER_IDX(_old_order_ptr); + /* There is a maximum of 5000 orders in old savegames, so *if* + * we go over that limit something is very wrong. In that case + * we just assume there are no orders for the vehicle. + */ + if (old_id < 5000) v->orders.old = GetOrder(old_id); + } + v->current_order.AssignOrder(UnpackOldOrder(_old_order)); + + /* For some reason we need to correct for this */ + switch (v->spritenum) { + case 0xfd: break; + case 0xff: v->spritenum = 0xfe; break; + default: v->spritenum >>= 1; break; + } + + if (_old_next_ptr != 0xFFFF) v->next = GetVehiclePoolSize() <= _old_next_ptr ? new (_old_next_ptr) InvalidVehicle() : GetVehicle(_old_next_ptr); + + _old_vehicle_names[_current_vehicle_id] = RemapOldStringID(_old_string_id); + v->name = NULL; + + /* Vehicle-subtype is different in TTD(Patch) */ + if (v->type == VEH_EFFECT) v->subtype = v->subtype >> 1; + + if (_cargo_count != 0) { + CargoPacket *cp = new CargoPacket((_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, _cargo_count); + cp->days_in_transit = _cargo_days; + v->cargo.Append(cp); + } + } + + return true; +} + +static const OldChunks sign_chunk[] = { + OCL_VAR ( OC_UINT16, 1, &_old_string_id ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Sign, x ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Sign, y ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_I8, Sign, z ), + + OCL_NULL( 6 ), ///< Width of sign, no longer in use + + OCL_END() +}; + +static bool LoadOldSign(LoadgameState *ls, int num) +{ + Sign *si = new (num) Sign(); + if (!LoadChunk(ls, si, sign_chunk)) return false; + + _old_string_id = RemapOldStringID(_old_string_id); + si->name = CopyFromOldName(_old_string_id); + + return true; +} + +static const OldChunks engine_chunk[] = { + OCL_SVAR( OC_UINT16, Engine, company_avail ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Engine, intro_date ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Engine, age ), + OCL_SVAR( OC_UINT16, Engine, reliability ), + OCL_SVAR( OC_UINT16, Engine, reliability_spd_dec ), + OCL_SVAR( OC_UINT16, Engine, reliability_start ), + OCL_SVAR( OC_UINT16, Engine, reliability_max ), + OCL_SVAR( OC_UINT16, Engine, reliability_final ), + OCL_SVAR( OC_UINT16, Engine, duration_phase_1 ), + OCL_SVAR( OC_UINT16, Engine, duration_phase_2 ), + OCL_SVAR( OC_UINT16, Engine, duration_phase_3 ), + + OCL_SVAR( OC_UINT8, Engine, lifelength ), + OCL_SVAR( OC_UINT8, Engine, flags ), + OCL_SVAR( OC_UINT8, Engine, preview_company_rank ), + OCL_SVAR( OC_UINT8, Engine, preview_wait ), + + OCL_NULL( 2 ), ///< Junk + + OCL_END() +}; + +static bool LoadOldEngine(LoadgameState *ls, int num) +{ + Engine *e = GetTempDataEngine(num); + return LoadChunk(ls, e, engine_chunk); +} + +static bool LoadOldEngineName(LoadgameState *ls, int num) +{ + Engine *e = GetTempDataEngine(num); + e->name = CopyFromOldName(RemapOldStringID(ReadUint16(ls))); + return true; +} + +static const OldChunks subsidy_chunk[] = { + OCL_SVAR( OC_UINT8, Subsidy, cargo_type ), + OCL_SVAR( OC_UINT8, Subsidy, age ), + OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Subsidy, from ), + OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Subsidy, to ), + + OCL_END() +}; + +static inline bool LoadOldSubsidy(LoadgameState *ls, int num) +{ + return LoadChunk(ls, &_subsidies[num], subsidy_chunk); +} + +static const OldChunks game_difficulty_chunk[] = { + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, max_no_competitors ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_start_time ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, number_towns ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, number_industries ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, DifficultySettings, max_loan ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, initial_interest ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, vehicle_costs ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_speed ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_intelligence ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, vehicle_breakdowns ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, subsidy_multiplier ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, construction_cost ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, terrain_type ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, quantity_sea_lakes ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, economy ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, line_reverse_mode ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, disasters ), + OCL_END() +}; + +static inline bool LoadOldGameDifficulty(LoadgameState *ls, int num) +{ + bool ret = LoadChunk(ls, &_settings_game.difficulty, game_difficulty_chunk); + _settings_game.difficulty.max_loan *= 1000; + return ret; +} + + +static bool LoadOldMapPart1(LoadgameState *ls, int num) +{ + uint i; + + for (i = 0; i < OLD_MAP_SIZE; i++) { + _m[i].m1 = ReadByte(ls); + } + for (i = 0; i < OLD_MAP_SIZE; i++) { + _m[i].m2 = ReadByte(ls); + } + for (i = 0; i < OLD_MAP_SIZE; i++) { + _old_map3[i * 2] = ReadByte(ls); + _old_map3[i * 2 + 1] = ReadByte(ls); + } + for (i = 0; i < OLD_MAP_SIZE / 4; i++) { + byte b = ReadByte(ls); + _m[i * 4 + 0].m6 = GB(b, 0, 2); + _m[i * 4 + 1].m6 = GB(b, 2, 2); + _m[i * 4 + 2].m6 = GB(b, 4, 2); + _m[i * 4 + 3].m6 = GB(b, 6, 2); + } + + return !ls->failed; +} + +static bool LoadOldMapPart2(LoadgameState *ls, int num) +{ + uint i; + + for (i = 0; i < OLD_MAP_SIZE; i++) { + _m[i].type_height = ReadByte(ls); + } + for (i = 0; i < OLD_MAP_SIZE; i++) { + _m[i].m5 = ReadByte(ls); + } + + return !ls->failed; +} + +static bool LoadTTDPatchExtraChunks(LoadgameState *ls, int num) +{ + ReadTTDPatchFlags(); + + DEBUG(oldloader, 2, "Found %d extra chunk(s)", _old_extra_chunk_nums); + + for (int i = 0; i != _old_extra_chunk_nums; i++) { + uint16 id = ReadUint16(ls); + uint32 len = ReadUint32(ls); + + switch (id) { + /* List of GRFIDs, used in the savegame. 0x8004 is the new ID + * They are saved in a 'GRFID:4 active:1' format, 5 bytes for each entry */ + case 0x2: + case 0x8004: { + /* Skip the first element: TTDP hack for the Action D special variables (FFFF0000 01) */ + ReadUint32(ls); ReadByte(ls); len -= 5; + + ClearGRFConfigList(&_grfconfig); + while (len != 0) { + uint32 grfid = ReadUint32(ls); + + if (ReadByte(ls) == 1) { + GRFConfig *c = CallocT(1); + c->grfid = grfid; + c->filename = strdup("TTDP game, no information"); + + AppendToGRFConfigList(&_grfconfig, c); + DEBUG(oldloader, 3, "TTDPatch game using GRF file with GRFID %0X", BSWAP32(c->grfid)); + } + len -= 5; + }; + + /* Append static NewGRF configuration */ + AppendStaticGRFConfigs(&_grfconfig); + } break; + + /* TTDPatch version and configuration */ + case 0x3: + _ttdp_version = ReadUint32(ls); + DEBUG(oldloader, 3, "Game saved with TTDPatch version %d.%d.%d r%d", + GB(_ttdp_version, 24, 8), GB(_ttdp_version, 20, 4), GB(_ttdp_version, 16, 4), GB(_ttdp_version, 0, 16)); + len -= 4; + while (len-- != 0) ReadByte(ls); // skip the configuration + break; + + default: + DEBUG(oldloader, 4, "Skipping unknown extra chunk %X", id); + while (len-- != 0) ReadByte(ls); + break; + } + } + + return !ls->failed; +} + +extern TileIndex _cur_tileloop_tile; +static uint32 _old_cur_town_ctr; +static const OldChunks main_chunk[] = { + OCL_ASSERT( 0 ), + OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_date ), + OCL_VAR ( OC_UINT16, 1, &_date_fract ), + OCL_NULL( 600 ), ///< TextEffects + OCL_VAR ( OC_UINT32, 2, &_random.state ), + + OCL_ASSERT( 0x264 ), + OCL_CHUNK( 70, LoadOldTown ), + OCL_ASSERT( 0x1C18 ), + OCL_CHUNK(5000, LoadOldOrder ), + OCL_ASSERT( 0x4328 ), + + OCL_CHUNK( 1, LoadOldAnimTileList ), + OCL_NULL( 4 ), ///< old end-of-order-list-pointer, no longer in use + + OCL_CHUNK( 255, LoadOldDepot ), + OCL_ASSERT( 0x4B26 ), + + OCL_VAR ( OC_UINT32, 1, &_old_cur_town_ctr ), + OCL_NULL( 2 ), ///< timer_counter, no longer in use + OCL_NULL( 2 ), ///< land_code, no longer in use + + OCL_VAR ( OC_FILE_U16 | OC_VAR_U8, 1, &_age_cargo_skip_counter ), + OCL_VAR ( OC_UINT16, 1, &_tick_counter ), + OCL_VAR ( OC_TILE, 1, &_cur_tileloop_tile ), + + OCL_CHUNK( 49, LoadOldPrice ), + OCL_CHUNK( 12, LoadOldCargoPaymentRate ), + + OCL_ASSERT( 0x4CBA ), + + OCL_CHUNK( 1, LoadOldMapPart1 ), + + OCL_ASSERT( 0x48CBA ), + + OCL_CHUNK(250, LoadOldStation ), + OCL_CHUNK( 90, LoadOldIndustry ), + OCL_CHUNK( 8, LoadOldCompany ), + + OCL_ASSERT( 0x547F2 ), + + OCL_CHUNK( 850, LoadOldVehicle ), + + OCL_ASSERT( 0x6F0F2 ), + + OCL_VAR ( OC_UINT8 | OC_DEREFERENCE_POINTER, 32 * 500, &_old_name_array ), + + OCL_NULL( 0x2000 ), ///< Old hash-table, no longer in use + + OCL_CHUNK( 40, LoadOldSign ), + OCL_CHUNK(256, LoadOldEngine ), + + OCL_VAR ( OC_UINT16, 1, &_vehicle_id_ctr_day ), + + OCL_CHUNK( 8, LoadOldSubsidy ), + + OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_next_competitor_start ), + OCL_VAR ( OC_FILE_I16 | OC_VAR_I32, 1, &_saved_scrollpos_x ), + OCL_VAR ( OC_FILE_I16 | OC_VAR_I32, 1, &_saved_scrollpos_y ), + OCL_VAR ( OC_FILE_U16 | OC_VAR_U8, 1, &_saved_scrollpos_zoom ), + + OCL_VAR ( OC_FILE_U32 | OC_VAR_I64, 1, &_economy.max_loan ), + OCL_VAR ( OC_FILE_U32 | OC_VAR_I64, 1, &_economy.max_loan_unround ), + OCL_VAR ( OC_INT16, 1, &_economy.fluct ), + + OCL_VAR ( OC_UINT16, 1, &_disaster_delay ), + + OCL_NULL( 144 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables + + OCL_CHUNK(256, LoadOldEngineName ), + + OCL_NULL( 144 ), ///< AI cargo-stuff, calculated in InitializeLandscapeVariables + OCL_NULL( 2 ), ///< Company indexes of companies, no longer in use + + OCL_VAR ( OC_FILE_U8 | OC_VAR_U16, 1, &_station_tick_ctr ), + + OCL_VAR ( OC_UINT8, 1, &_settings_game.locale.currency ), + OCL_VAR ( OC_UINT8, 1, &_settings_game.locale.units ), + OCL_VAR ( OC_FILE_U8 | OC_VAR_U32, 1, &_cur_company_tick_index ), + + OCL_NULL( 2 ), ///< Date stuff, calculated automatically + OCL_NULL( 8 ), ///< Company colors, calculated automatically + + OCL_VAR ( OC_UINT8, 1, &_economy.infl_amount ), + OCL_VAR ( OC_UINT8, 1, &_economy.infl_amount_pr ), + OCL_VAR ( OC_UINT8, 1, &_economy.interest_rate ), + OCL_NULL( 1 ), // available airports + OCL_VAR ( OC_UINT8, 1, &_settings_game.vehicle.road_side ), + OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.town_name ), + + OCL_CHUNK( 1, LoadOldGameDifficulty ), + + OCL_ASSERT( 0x77130 ), + + OCL_VAR ( OC_UINT8, 1, &_settings_game.difficulty.diff_level ), + OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.landscape ), + OCL_VAR ( OC_UINT8, 1, &_trees_tick_ctr ), + + OCL_NULL( 1 ), ///< Custom vehicle types yes/no, no longer used + OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.snow_line ), + + OCL_NULL( 32 ), ///< new_industry_randtable, no longer used (because of new design) + OCL_NULL( 36 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables + + OCL_ASSERT( 0x77179 ), + + OCL_CHUNK( 1, LoadOldMapPart2 ), + + OCL_ASSERT( 0x97179 ), + + /* Below any (if available) extra chunks from TTDPatch can follow */ + OCL_CHUNK(1, LoadTTDPatchExtraChunks), + + OCL_END() +}; + +static bool LoadOldMain(LoadgameState *ls) +{ + int i; + + /* The first 49 is the name of the game + checksum, skip it */ + fseek(ls->file, HEADER_SIZE, SEEK_SET); + + DEBUG(oldloader, 3, "Reading main chunk..."); + /* Load the biggest chunk */ + _old_map3 = MallocT(OLD_MAP_SIZE * 2); + _old_vehicle_names = NULL; + if (!LoadChunk(ls, NULL, main_chunk)) { + DEBUG(oldloader, 0, "Loading failed"); + free(_old_map3); + free(_old_vehicle_names); + return false; + } + DEBUG(oldloader, 3, "Done, converting game data..."); + + /* Fix some general stuff */ + _settings_game.game_creation.landscape = _settings_game.game_creation.landscape & 0xF; + + /* Remap some pointers */ + _cur_town_ctr = REMAP_TOWN_IDX(_old_cur_town_ctr); + + /* _old_map3 is changed in _map3_lo and _map3_hi */ + for (i = 0; i < OLD_MAP_SIZE; i++) { + _m[i].m3 = _old_map3[i * 2]; + _m[i].m4 = _old_map3[i * 2 + 1]; + } + + for (i = 0; i < OLD_MAP_SIZE; i ++) { + switch (GetTileType(i)) { + case MP_STATION: + _m[i].m4 = 0; // We do not understand this TTDP station mapping (yet) + switch (_m[i].m5) { + /* We have drive through stops at a totally different place */ + case 0x53: case 0x54: _m[i].m5 += 170 - 0x53; break; // Bus drive through + case 0x57: case 0x58: _m[i].m5 += 168 - 0x57; break; // Truck drive through + case 0x55: case 0x56: _m[i].m5 += 170 - 0x55; break; // Bus tram stop + case 0x59: case 0x5A: _m[i].m5 += 168 - 0x59; break; // Truck tram stop + default: break; + } + break; + + case MP_RAILWAY: + /* We save presignals different from TTDPatch, convert them */ + if (GetRailTileType(i) == RAIL_TILE_SIGNALS) { + /* This byte is always zero in TTD for this type of tile */ + if (_m[i].m4) /* Convert the presignals to our own format */ + _m[i].m4 = (_m[i].m4 >> 1) & 7; + } + /* TTDPatch stores PBS things in L6 and all elsewhere; so we'll just + * clear it for ourselves and let OTTD's rebuild PBS itself */ + _m[i].m4 &= 0xF; /* Only keep the lower four bits; upper four is PBS */ + break; + + case MP_WATER: + if (GetWaterClass(i) == 3) MakeRiver(i, Random()); + break; + + default: + break; + } + } + + /* Make sure the available engines are really available, otherwise + * we will get a "new vehicle"-spree. */ + Engine *e; + FOR_ALL_ENGINES(e) { + if (_date >= (e->intro_date + 365)) { + e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE; + e->company_avail = (CompanyMask)-1; + } + } + + /* Fix the game to be compatible with OpenTTD */ + FixOldTowns(); + FixOldVehicles(); + + /* We have a new difficulty setting */ + _settings_game.difficulty.town_council_tolerance = Clamp(_settings_game.difficulty.diff_level, 0, 2); + + DEBUG(oldloader, 3, "Finished converting game data"); + DEBUG(oldloader, 1, "TTD(Patch) savegame successfully converted"); + + free(_old_map3); + free(_old_vehicle_names); + + return true; +} + +bool LoadOldSaveGame(const char *file) +{ + LoadgameState ls; + + DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame"); + + InitLoading(&ls); + + /* Open file */ + ls.file = fopen(file, "rb"); + + if (ls.file == NULL) { + DEBUG(oldloader, 0, "Cannot open file '%s'", file); + return false; + } + + /* Load the main chunk */ + if (!LoadOldMain(&ls)) return false; + + fclose(ls.file); + + /* Some old TTD(Patch) savegames could have buoys at tile 0 + * (without assigned station struct) + * MakeWater() can be used as long as sea has the same + * format as old savegames (eg. everything is zeroed) */ + MakeWater(0); + + _pause_game = 2; + + return true; +} + +void GetOldSaveGameName(char *title, const char *path, const char *file) +{ + char filename[MAX_PATH]; + FILE *f; + + snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, file); + f = fopen(filename, "rb"); + title[0] = '\0'; + title[48] = '\0'; + + if (f == NULL) return; + + if (fread(title, 1, 48, f) != 48) snprintf(title, 48, "Corrupt file"); + + fclose(f); +} diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/order_sl.cpp @@ -0,0 +1,203 @@ +/* $Id$ */ + +/** @file order_sl.cpp Code handling saving and loading of orders */ + +#include "../stdafx.h" +#include "../order_base.h" +#include "../core/alloc_func.hpp" +#include "../settings_type.h" + +#include "saveload.h" + +void Order::ConvertFromOldSavegame() +{ + uint8 old_flags = this->flags; + this->flags = 0; + + /* First handle non-stop */ + if (_settings_client.gui.sg_new_nonstop) { + /* OFB_NON_STOP */ + this->SetNonStopType((old_flags & 8) ? ONSF_NO_STOP_AT_ANY_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS); + } else { + this->SetNonStopType((old_flags & 8) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE); + } + + switch (this->GetType()) { + /* Only a few types need the other savegame conversions. */ + case OT_GOTO_DEPOT: case OT_GOTO_STATION: case OT_LOADING: break; + default: return; + } + + if (this->GetType() != OT_GOTO_DEPOT) { + /* Then the load flags */ + if ((old_flags & 2) != 0) { // OFB_UNLOAD + this->SetLoadType(OLFB_NO_LOAD); + } else if ((old_flags & 4) == 0) { // !OFB_FULL_LOAD + this->SetLoadType(OLF_LOAD_IF_POSSIBLE); + } else { + this->SetLoadType(_settings_client.gui.sg_full_load_any ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD); + } + + /* Finally fix the unload flags */ + if ((old_flags & 1) != 0) { // OFB_TRANSFER + this->SetUnloadType(OUFB_TRANSFER); + } else if ((old_flags & 2) != 0) { // OFB_UNLOAD + this->SetUnloadType(OUFB_UNLOAD); + } else { + this->SetUnloadType(OUF_UNLOAD_IF_POSSIBLE); + } + } else { + /* Then the depot action flags */ + this->SetDepotActionType(((old_flags & 6) == 4) ? ODATFB_HALT : ODATF_SERVICE_ONLY); + + /* Finally fix the depot type flags */ + uint t = ((old_flags & 6) == 6) ? ODTFB_SERVICE : ODTF_MANUAL; + if ((old_flags & 2) != 0) t |= ODTFB_PART_OF_ORDERS; + this->SetDepotOrderType((OrderDepotTypeFlags)t); + } +} + +/** Unpacks a order from savegames with version 4 and lower + * @param packed packed order + * @return unpacked order + */ +static Order UnpackVersion4Order(uint16 packed) +{ + return Order(GB(packed, 8, 8) << 16 | GB(packed, 4, 4) << 8 | GB(packed, 0, 4)); +} + +/** Unpacks a order from savegames made with TTD(Patch) + * @param packed packed order + * @return unpacked order + */ +Order UnpackOldOrder(uint16 packed) +{ + Order order = UnpackVersion4Order(packed); + + /* + * Sanity check + * TTD stores invalid orders as OT_NOTHING with non-zero flags/station + */ + if (!order.IsValid() && packed != 0) order.MakeDummy(); + + return order; +} + +const SaveLoad *GetOrderDescription() +{ + static const SaveLoad _order_desc[] = { + SLE_VAR(Order, type, SLE_UINT8), + SLE_VAR(Order, flags, SLE_UINT8), + SLE_VAR(Order, dest, SLE_UINT16), + SLE_REF(Order, next, REF_ORDER), + SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, 36, SL_MAX_VERSION), + SLE_CONDVAR(Order, refit_subtype, SLE_UINT8, 36, SL_MAX_VERSION), + SLE_CONDVAR(Order, wait_time, SLE_UINT16, 67, SL_MAX_VERSION), + SLE_CONDVAR(Order, travel_time, SLE_UINT16, 67, SL_MAX_VERSION), + + /* Leftover from the minor savegame version stuff + * We will never use those free bytes, but we have to keep this line to allow loading of old savegames */ + SLE_CONDNULL(10, 5, 35), + SLE_END() + }; + + return _order_desc; +} + +static void Save_ORDR() +{ + Order *order; + + FOR_ALL_ORDERS(order) { + SlSetArrayIndex(order->index); + SlObject(order, GetOrderDescription()); + } +} + +static void Load_ORDR() +{ + if (CheckSavegameVersionOldStyle(5, 2)) { + /* Version older than 5.2 did not have a ->next pointer. Convert them + (in the old days, the orderlist was 5000 items big) */ + size_t len = SlGetFieldLength(); + uint i; + + if (CheckSavegameVersion(5)) { + /* Pre-version 5 had an other layout for orders + (uint16 instead of uint32) */ + len /= sizeof(uint16); + uint16 *orders = MallocT(len + 1); + + SlArray(orders, len, SLE_UINT16); + + for (i = 0; i < len; ++i) { + Order *order = new (i) Order(); + order->AssignOrder(UnpackVersion4Order(orders[i])); + } + + free(orders); + } else if (CheckSavegameVersionOldStyle(5, 2)) { + len /= sizeof(uint16); + uint16 *orders = MallocT(len + 1); + + SlArray(orders, len, SLE_UINT32); + + for (i = 0; i < len; ++i) { + new (i) Order(orders[i]); + } + + free(orders); + } + + /* Update all the next pointer */ + for (i = 1; i < len; ++i) { + /* The orders were built like this: + * While the order is valid, set the previous will get it's next pointer set + * We start with index 1 because no order will have the first in it's next pointer */ + if (GetOrder(i)->IsValid()) + GetOrder(i - 1)->next = GetOrder(i); + } + } else { + int index; + + while ((index = SlIterateArray()) != -1) { + Order *order = new (index) Order(); + SlObject(order, GetOrderDescription()); + } + } +} + +const SaveLoad *GetOrderListDescription() +{ + static const SaveLoad _orderlist_desc[] = { + SLE_REF(OrderList, first, REF_ORDER), + SLE_END() + }; + + return _orderlist_desc; +} + +static void Save_ORDL() +{ + OrderList *list; + + FOR_ALL_ORDER_LISTS(list) { + SlSetArrayIndex(list->index); + SlObject(list, GetOrderListDescription()); + } +} + +static void Load_ORDL() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + OrderList *list = new (index) OrderList(); + SlObject(list, GetOrderListDescription()); + } +} + +extern const ChunkHandler _order_chunk_handlers[] = { + { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY}, + { 'ORDL', Save_ORDL, Load_ORDL, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/saveload.cpp @@ -0,0 +1,1899 @@ +/* $Id$ */ + +/** @file saveload.cpp + * All actions handling saving and loading goes on in this file. The general actions + * are as follows for saving a game (loading is analogous): + *
    + *
  1. initialize the writer by creating a temporary memory-buffer for it + *
  2. go through all to-be saved elements, each 'chunk' (ChunkHandler) prefixed by a label + *
  3. use their description array (SaveLoad) to know what elements to save and in what version + * of the game it was active (used when loading) + *
  4. write all data byte-by-byte to the temporary buffer so it is endian-safe + *
  5. when the buffer is full; flush it to the output (eg save to file) (_sl.buf, _sl.bufp, _sl.bufe) + *
  6. repeat this until everything is done, and flush any remaining output to file + *
+ */ +#include "../stdafx.h" +#include "../openttd.h" +#include "../debug.h" +#include "../station_base.h" +#include "../thread.h" +#include "../town.h" +#include "../network/network.h" +#include "../variables.h" +#include "../window_func.h" +#include "../strings_func.h" +#include "../gfx_func.h" +#include "../core/alloc_func.hpp" +#include "../functions.h" +#include "../core/endian_func.hpp" +#include "../vehicle_base.h" +#include "../company_func.h" +#include "../date_func.h" +#include "../autoreplace_base.h" +#include "../statusbar_gui.h" +#include "../fileio_func.h" +#include "../gamelog.h" + +#include "table/strings.h" + +#include "saveload.h" +#include "saveload_internal.h" + +#include + +extern const uint16 SAVEGAME_VERSION = 105; + +SavegameType _savegame_type; ///< type of savegame we are loading + +uint32 _ttdp_version; ///< version of TTDP savegame (if applicable) +uint16 _sl_version; ///< the major savegame version identifier +byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! + +typedef void WriterProc(size_t len); +typedef size_t ReaderProc(); + +/** The saveload struct, containing reader-writer functions, bufffer, version, etc. */ +static struct { + bool save; ///< are we doing a save or a load atm. True when saving + byte need_length; ///< ??? + byte block_mode; ///< ??? + bool error; ///< did an error occur or not + + size_t obj_len; ///< the length of the current object we are busy with + int array_index, last_array_index; ///< in the case of an array, the current and last positions + + size_t offs_base; ///< the offset in number of bytes since we started writing data (eg uncompressed savegame size) + + WriterProc *write_bytes; ///< savegame writer function + ReaderProc *read_bytes; ///< savegame loader function + + const ChunkHandler* const *chs; ///< the chunk of data that is being processed atm (vehicles, signs, etc.) + + /* When saving/loading savegames, they are always saved to a temporary memory-place + * to be flushed to file (save) or to final place (load) when full. */ + byte *bufp, *bufe; ///< bufp(ointer) gives the current position in the buffer bufe(nd) gives the end of the buffer + + /* these 3 may be used by compressor/decompressors. */ + byte *buf; ///< pointer to temporary memory to read/write, initialized by SaveLoadFormat->initread/write + byte *buf_ori; ///< pointer to the original memory location of buf, used to free it afterwards + uint bufsize; ///< the size of the temporary memory *buf + FILE *fh; ///< the file from which is read or written to + + void (*excpt_uninit)(); ///< the function to execute on any encountered error + StringID error_str; ///< the translateable error message to show + char *extra_msg; ///< the error message +} _sl; + + +enum NeedLengthValues {NL_NONE = 0, NL_WANTLENGTH = 1, NL_CALCLENGTH = 2}; + +/** Error handler, calls longjmp to simulate an exception. + * @todo this was used to have a central place to handle errors, but it is + * pretty ugly, and seriously interferes with any multithreaded approaches */ +static void NORETURN SlError(StringID string, const char *extra_msg = NULL) +{ + _sl.error_str = string; + free(_sl.extra_msg); + _sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg); + throw std::exception(); +} + +typedef void (*AsyncSaveFinishProc)(); +static AsyncSaveFinishProc _async_save_finish = NULL; +static ThreadObject *_save_thread; + +/** + * Called by save thread to tell we finished saving. + */ +static void SetAsyncSaveFinish(AsyncSaveFinishProc proc) +{ + if (_exit_game) return; + while (_async_save_finish != NULL) CSleep(10); + + _async_save_finish = proc; +} + +/** + * Handle async save finishes. + */ +void ProcessAsyncSaveFinish() +{ + if (_async_save_finish == NULL) return; + + _async_save_finish(); + + _async_save_finish = NULL; + + if (_save_thread != NULL) { + _save_thread->Join(); + delete _save_thread; + _save_thread = NULL; + } +} + +/** + * Fill the input buffer by reading from the file with the given reader + */ +static void SlReadFill() +{ + size_t len = _sl.read_bytes(); + if (len == 0) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected end of chunk"); + + _sl.bufp = _sl.buf; + _sl.bufe = _sl.buf + len; + _sl.offs_base += len; +} + +static inline size_t SlGetOffs() {return _sl.offs_base - (_sl.bufe - _sl.bufp);} + +/** Return the size in bytes of a certain type of normal/atomic variable + * as it appears in memory. See VarTypes + * @param conv VarType type of variable that is used for calculating the size + * @return Return the size of this type in bytes */ +static inline byte SlCalcConvMemLen(VarType conv) +{ + static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0}; + byte length = GB(conv, 4, 4); + assert(length < lengthof(conv_mem_size)); + return conv_mem_size[length]; +} + +/** Return the size in bytes of a certain type of normal/atomic variable + * as it appears in a saved game. See VarTypes + * @param conv VarType type of variable that is used for calculating the size + * @return Return the size of this type in bytes */ +static inline byte SlCalcConvFileLen(VarType conv) +{ + static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2}; + byte length = GB(conv, 0, 4); + assert(length < lengthof(conv_file_size)); + return conv_file_size[length]; +} + +/** Return the size in bytes of a reference (pointer) */ +static inline size_t SlCalcRefLen() {return CheckSavegameVersion(69) ? 2 : 4;} + +/** Flush the output buffer by writing to disk with the given reader. + * If the buffer pointer has not yet been set up, set it up now. Usually + * only called when the buffer is full, or there is no more data to be processed + */ +static void SlWriteFill() +{ + /* flush the buffer to disk (the writer) */ + if (_sl.bufp != NULL) { + uint len = _sl.bufp - _sl.buf; + _sl.offs_base += len; + if (len) _sl.write_bytes(len); + } + + /* All the data from the buffer has been written away, rewind to the beginning + * to start reading in more data */ + _sl.bufp = _sl.buf; + _sl.bufe = _sl.buf + _sl.bufsize; +} + +/** Read in a single byte from file. If the temporary buffer is full, + * flush it to its final destination + * @return return the read byte from file + */ +static inline byte SlReadByteInternal() +{ + if (_sl.bufp == _sl.bufe) SlReadFill(); + return *_sl.bufp++; +} + +/** Wrapper for SlReadByteInternal */ +byte SlReadByte() {return SlReadByteInternal();} + +/** Write away a single byte from memory. If the temporary buffer is full, + * flush it to its destination (file) + * @param b the byte that is currently written + */ +static inline void SlWriteByteInternal(byte b) +{ + if (_sl.bufp == _sl.bufe) SlWriteFill(); + *_sl.bufp++ = b; +} + +/** Wrapper for SlWriteByteInternal */ +void SlWriteByte(byte b) {SlWriteByteInternal(b);} + +static inline int SlReadUint16() +{ + int x = SlReadByte() << 8; + return x | SlReadByte(); +} + +static inline uint32 SlReadUint32() +{ + uint32 x = SlReadUint16() << 16; + return x | SlReadUint16(); +} + +static inline uint64 SlReadUint64() +{ + uint32 x = SlReadUint32(); + uint32 y = SlReadUint32(); + return (uint64)x << 32 | y; +} + +static inline void SlWriteUint16(uint16 v) +{ + SlWriteByte(GB(v, 8, 8)); + SlWriteByte(GB(v, 0, 8)); +} + +static inline void SlWriteUint32(uint32 v) +{ + SlWriteUint16(GB(v, 16, 16)); + SlWriteUint16(GB(v, 0, 16)); +} + +static inline void SlWriteUint64(uint64 x) +{ + SlWriteUint32((uint32)(x >> 32)); + SlWriteUint32((uint32)x); +} + +/** + * Read in the header descriptor of an object or an array. + * If the highest bit is set (7), then the index is bigger than 127 + * elements, so use the next byte to read in the real value. + * The actual value is then both bytes added with the first shifted + * 8 bits to the left, and dropping the highest bit (which only indicated a big index). + * x = ((x & 0x7F) << 8) + SlReadByte(); + * @return Return the value of the index + */ +static uint SlReadSimpleGamma() +{ + uint i = SlReadByte(); + if (HasBit(i, 7)) { + i &= ~0x80; + if (HasBit(i, 6)) { + i &= ~0x40; + if (HasBit(i, 5)) { + i &= ~0x20; + if (HasBit(i, 4)) + SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unsupported gamma"); + i = (i << 8) | SlReadByte(); + } + i = (i << 8) | SlReadByte(); + } + i = (i << 8) | SlReadByte(); + } + return i; +} + +/** + * Write the header descriptor of an object or an array. + * If the element is bigger than 127, use 2 bytes for saving + * and use the highest byte of the first written one as a notice + * that the length consists of 2 bytes, etc.. like this: + * 0xxxxxxx + * 10xxxxxx xxxxxxxx + * 110xxxxx xxxxxxxx xxxxxxxx + * 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx + * @param i Index being written + */ + +static void SlWriteSimpleGamma(size_t i) +{ + if (i >= (1 << 7)) { + if (i >= (1 << 14)) { + if (i >= (1 << 21)) { + assert(i < (1 << 28)); + SlWriteByte((byte)(0xE0 | (i >> 24))); + SlWriteByte((byte)(i >> 16)); + } else { + SlWriteByte((byte)(0xC0 | (i >> 16))); + } + SlWriteByte((byte)(i >> 8)); + } else { + SlWriteByte((byte)(0x80 | (i >> 8))); + } + } + SlWriteByte((byte)i); +} + +/** Return how many bytes used to encode a gamma value */ +static inline uint SlGetGammaLength(size_t i) +{ + return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)); +} + +static inline uint SlReadSparseIndex() {return SlReadSimpleGamma();} +static inline void SlWriteSparseIndex(uint index) {SlWriteSimpleGamma(index);} + +static inline uint SlReadArrayLength() {return SlReadSimpleGamma();} +static inline void SlWriteArrayLength(size_t length) {SlWriteSimpleGamma(length);} +static inline uint SlGetArrayLength(size_t length) {return SlGetGammaLength(length);} + +void SlSetArrayIndex(uint index) +{ + _sl.need_length = NL_WANTLENGTH; + _sl.array_index = index; +} + +static size_t _next_offs; + +/** + * Iterate through the elements of an array and read the whole thing + * @return The index of the object, or -1 if we have reached the end of current block + */ +int SlIterateArray() +{ + int index; + + /* After reading in the whole array inside the loop + * we must have read in all the data, so we must be at end of current block. */ + if (_next_offs != 0 && SlGetOffs() != _next_offs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size"); + + while (true) { + uint length = SlReadArrayLength(); + if (length == 0) { + _next_offs = 0; + return -1; + } + + _sl.obj_len = --length; + _next_offs = SlGetOffs() + length; + + switch (_sl.block_mode) { + case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break; + case CH_ARRAY: index = _sl.array_index++; break; + default: + DEBUG(sl, 0, "SlIterateArray error"); + return -1; // error + } + + if (length != 0) return index; + } +} + +/** + * Sets the length of either a RIFF object or the number of items in an array. + * This lets us load an object or an array of arbitrary size + * @param length The length of the sought object/array + */ +void SlSetLength(size_t length) +{ + assert(_sl.save); + + switch (_sl.need_length) { + case NL_WANTLENGTH: + _sl.need_length = NL_NONE; + switch (_sl.block_mode) { + case CH_RIFF: + /* Ugly encoding of >16M RIFF chunks + * The lower 24 bits are normal + * The uppermost 4 bits are bits 24:27 */ + assert(length < (1 << 28)); + SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28))); + break; + case CH_ARRAY: + assert(_sl.last_array_index <= _sl.array_index); + while (++_sl.last_array_index <= _sl.array_index) + SlWriteArrayLength(1); + SlWriteArrayLength(length + 1); + break; + case CH_SPARSE_ARRAY: + SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index. + SlWriteSparseIndex(_sl.array_index); + break; + default: NOT_REACHED(); + } break; + case NL_CALCLENGTH: + _sl.obj_len += (int)length; + break; + } +} + +/** + * Save/Load bytes. These do not need to be converted to Little/Big Endian + * so directly write them or read them to/from file + * @param ptr The source or destination of the object being manipulated + * @param length number of bytes this fast CopyBytes lasts + */ +static void SlCopyBytes(void *ptr, size_t length) +{ + byte *p = (byte*)ptr; + + if (_sl.save) { + for (; length != 0; length--) {SlWriteByteInternal(*p++);} + } else { + for (; length != 0; length--) {*p++ = SlReadByteInternal();} + } +} + +/** Read in bytes from the file/data structure but don't do + * anything with them, discarding them in effect + * @param length The amount of bytes that is being treated this way + */ +static inline void SlSkipBytes(size_t length) +{ + for (; length != 0; length--) SlReadByte(); +} + +/* Get the length of the current object */ +size_t SlGetFieldLength() {return _sl.obj_len;} + +/** Return a signed-long version of the value of a setting + * @param ptr pointer to the variable + * @param conv type of variable, can be a non-clean + * type, eg one with other flags because it is parsed + * @return returns the value of the pointer-setting */ +int64 ReadValue(const void *ptr, VarType conv) +{ + switch (GetVarMemType(conv)) { + case SLE_VAR_BL: return (*(bool*)ptr != 0); + case SLE_VAR_I8: return *(int8* )ptr; + case SLE_VAR_U8: return *(byte* )ptr; + case SLE_VAR_I16: return *(int16* )ptr; + case SLE_VAR_U16: return *(uint16*)ptr; + case SLE_VAR_I32: return *(int32* )ptr; + case SLE_VAR_U32: return *(uint32*)ptr; + case SLE_VAR_I64: return *(int64* )ptr; + case SLE_VAR_U64: return *(uint64*)ptr; + case SLE_VAR_NULL:return 0; + default: NOT_REACHED(); + } + + /* useless, but avoids compiler warning this way */ + return 0; +} + +/** Write the value of a setting + * @param ptr pointer to the variable + * @param conv type of variable, can be a non-clean type, eg + * with other flags. It is parsed upon read + * @param val the new value being given to the variable */ +void WriteValue(void *ptr, VarType conv, int64 val) +{ + switch (GetVarMemType(conv)) { + case SLE_VAR_BL: *(bool *)ptr = (val != 0); break; + case SLE_VAR_I8: *(int8 *)ptr = val; break; + case SLE_VAR_U8: *(byte *)ptr = val; break; + case SLE_VAR_I16: *(int16 *)ptr = val; break; + case SLE_VAR_U16: *(uint16*)ptr = val; break; + case SLE_VAR_I32: *(int32 *)ptr = val; break; + case SLE_VAR_U32: *(uint32*)ptr = val; break; + case SLE_VAR_I64: *(int64 *)ptr = val; break; + case SLE_VAR_U64: *(uint64*)ptr = val; break; + case SLE_VAR_NAME: *(char**)ptr = CopyFromOldName(val); break; + case SLE_VAR_NULL: break; + default: NOT_REACHED(); + } +} + +/** + * Handle all conversion and typechecking of variables here. + * In the case of saving, read in the actual value from the struct + * and then write them to file, endian safely. Loading a value + * goes exactly the opposite way + * @param ptr The object being filled/read + * @param conv VarType type of the current element of the struct + */ +static void SlSaveLoadConv(void *ptr, VarType conv) +{ + int64 x = 0; + + if (_sl.save) { // SAVE values + /* Read a value from the struct. These ARE endian safe. */ + x = ReadValue(ptr, conv); + + /* Write the value to the file and check if its value is in the desired range */ + switch (GetVarFileType(conv)) { + case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break; + case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break; + case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break; + case SLE_FILE_STRINGID: + case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break; + case SLE_FILE_I32: + case SLE_FILE_U32: SlWriteUint32((uint32)x);break; + case SLE_FILE_I64: + case SLE_FILE_U64: SlWriteUint64(x);break; + default: NOT_REACHED(); + } + } else { // LOAD values + + /* Read a value from the file */ + switch (GetVarFileType(conv)) { + case SLE_FILE_I8: x = (int8 )SlReadByte(); break; + case SLE_FILE_U8: x = (byte )SlReadByte(); break; + case SLE_FILE_I16: x = (int16 )SlReadUint16(); break; + case SLE_FILE_U16: x = (uint16)SlReadUint16(); break; + case SLE_FILE_I32: x = (int32 )SlReadUint32(); break; + case SLE_FILE_U32: x = (uint32)SlReadUint32(); break; + case SLE_FILE_I64: x = (int64 )SlReadUint64(); break; + case SLE_FILE_U64: x = (uint64)SlReadUint64(); break; + case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break; + default: NOT_REACHED(); + } + + /* Write The value to the struct. These ARE endian safe. */ + WriteValue(ptr, conv, x); + } +} + +/** Calculate the net length of a string. This is in almost all cases + * just strlen(), but if the string is not properly terminated, we'll + * resort to the maximum length of the buffer. + * @param ptr pointer to the stringbuffer + * @param length maximum length of the string (buffer). If -1 we don't care + * about a maximum length, but take string length as it is. + * @return return the net length of the string */ +static inline size_t SlCalcNetStringLen(const char *ptr, size_t length) +{ + if (ptr == NULL) return 0; + return min(strlen(ptr), length - 1); +} + +/** Calculate the gross length of the string that it + * will occupy in the savegame. This includes the real length, returned + * by SlCalcNetStringLen and the length that the index will occupy. + * @param ptr pointer to the stringbuffer + * @param length maximum length of the string (buffer size, etc.) + * @param conv type of data been used + * @return return the gross length of the string */ +static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv) +{ + size_t len; + const char *str; + + switch (GetVarMemType(conv)) { + default: NOT_REACHED(); + case SLE_VAR_STR: + case SLE_VAR_STRQ: + str = *(const char**)ptr; + len = SIZE_MAX; + break; + case SLE_VAR_STRB: + case SLE_VAR_STRBQ: + str = (const char*)ptr; + len = length; + break; + } + + len = SlCalcNetStringLen(str, len); + return len + SlGetArrayLength(len); // also include the length of the index +} + +/** + * Save/Load a string. + * @param ptr the string being manipulated + * @param length of the string (full length) + * @param conv must be SLE_FILE_STRING */ +static void SlString(void *ptr, size_t length, VarType conv) +{ + size_t len; + + if (_sl.save) { // SAVE string + switch (GetVarMemType(conv)) { + default: NOT_REACHED(); + case SLE_VAR_STRB: + case SLE_VAR_STRBQ: + len = SlCalcNetStringLen((char*)ptr, length); + break; + case SLE_VAR_STR: + case SLE_VAR_STRQ: + ptr = *(char**)ptr; + len = SlCalcNetStringLen((char*)ptr, SIZE_MAX); + break; + } + + SlWriteArrayLength(len); + SlCopyBytes(ptr, len); + } else { // LOAD string + len = SlReadArrayLength(); + + switch (GetVarMemType(conv)) { + default: NOT_REACHED(); + case SLE_VAR_STRB: + case SLE_VAR_STRBQ: + if (len >= length) { + DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating"); + SlCopyBytes(ptr, length); + SlSkipBytes(len - length); + len = length - 1; + } else { + SlCopyBytes(ptr, len); + } + break; + case SLE_VAR_STR: + case SLE_VAR_STRQ: // Malloc'd string, free previous incarnation, and allocate + free(*(char**)ptr); + if (len == 0) { + *(char**)ptr = NULL; + } else { + *(char**)ptr = MallocT(len + 1); // terminating '\0' + ptr = *(char**)ptr; + SlCopyBytes(ptr, len); + } + break; + } + + ((char*)ptr)[len] = '\0'; // properly terminate the string + } +} + +/** + * Return the size in bytes of a certain type of atomic array + * @param length The length of the array counted in elements + * @param conv VarType type of the variable that is used in calculating the size + */ +static inline size_t SlCalcArrayLen(size_t length, VarType conv) +{ + return SlCalcConvFileLen(conv) * length; +} + +/** + * Save/Load an array. + * @param array The array being manipulated + * @param length The length of the array in elements + * @param conv VarType type of the atomic array (int, byte, uint64, etc.) + */ +void SlArray(void *array, size_t length, VarType conv) +{ + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcArrayLen(length, conv)); + /* Determine length only? */ + if (_sl.need_length == NL_CALCLENGTH) return; + } + + /* NOTICE - handle some buggy stuff, in really old versions everything was saved + * as a byte-type. So detect this, and adjust array size accordingly */ + if (!_sl.save && _sl_version == 0) { + /* all arrays except difficulty settings */ + if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID || + conv == SLE_INT32 || conv == SLE_UINT32) { + SlCopyBytes(array, length * SlCalcConvFileLen(conv)); + return; + } + /* used for conversion of Money 32bit->64bit */ + if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) { + for (uint i = 0; i < length; i++) { + ((int64*)array)[i] = (int32)BSWAP32(SlReadUint32()); + } + return; + } + } + + /* If the size of elements is 1 byte both in file and memory, no special + * conversion is needed, use specialized copy-copy function to speed up things */ + if (conv == SLE_INT8 || conv == SLE_UINT8) { + SlCopyBytes(array, length); + } else { + byte *a = (byte*)array; + byte mem_size = SlCalcConvMemLen(conv); + + for (; length != 0; length --) { + SlSaveLoadConv(a, conv); + a += mem_size; // get size + } + } +} + + +static uint ReferenceToInt(const void* obj, SLRefType rt); +static void* IntToReference(uint index, SLRefType rt); + + +/** + * Return the size in bytes of a list + * @param list The std::list to find the size of + */ +static inline size_t SlCalcListLen(const void *list) +{ + std::list *l = (std::list *) list; + + int type_size = CheckSavegameVersion(69) ? 2 : 4; + /* Each entry is saved as type_size bytes, plus type_size bytes are used for the length + * of the list */ + return l->size() * type_size + type_size; +} + + +/** + * Save/Load a list. + * @param list The list being manipulated + * @param conv SLRefType type of the list (Vehicle *, Station *, etc) + */ +void SlList(void *list, SLRefType conv) +{ + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcListLen(list)); + /* Determine length only? */ + if (_sl.need_length == NL_CALCLENGTH) return; + } + + std::list *l = (std::list *) list; + + if (_sl.save) { + SlWriteUint32((uint32)l->size()); + + std::list::iterator iter; + for (iter = l->begin(); iter != l->end(); ++iter) { + void *ptr = *iter; + SlWriteUint32(ReferenceToInt(ptr, conv)); + } + } else { + uint length = CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(); + + /* Load each reference and push to the end of the list */ + for (uint i = 0; i < length; i++) { + void *ptr = IntToReference(CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(), conv); + l->push_back(ptr); + } + } +} + + +/** Are we going to save this object or not? */ +static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld) +{ + if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false; + if (sld->conv & SLF_SAVE_NO) return false; + + return true; +} + +/** Are we going to load this variable when loading a savegame or not? + * @note If the variable is skipped it is skipped in the savegame + * bytestream itself as well, so there is no need to skip it somewhere else */ +static inline bool SlSkipVariableOnLoad(const SaveLoad *sld) +{ + if ((sld->conv & SLF_NETWORK_NO) && !_sl.save && _networking && !_network_server) { + SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length); + return true; + } + + return false; +} + +/** + * Calculate the size of an object. + * @param object to be measured + * @param sld The SaveLoad description of the object so we know how to manipulate it + * @return size of given objetc + */ +size_t SlCalcObjLength(const void *object, const SaveLoad *sld) +{ + size_t length = 0; + + /* Need to determine the length and write a length tag. */ + for (; sld->cmd != SL_END; sld++) { + length += SlCalcObjMemberLength(object, sld); + } + return length; +} + +size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) +{ + assert(_sl.save); + + switch (sld->cmd) { + case SL_VAR: + case SL_REF: + case SL_ARR: + case SL_STR: + case SL_LST: + /* CONDITIONAL saveload types depend on the savegame version */ + if (!SlIsObjectValidInSavegame(sld)) break; + + switch (sld->cmd) { + case SL_VAR: return SlCalcConvFileLen(sld->conv); + case SL_REF: return SlCalcRefLen(); + case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv); + case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv); + case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld)); + default: NOT_REACHED(); + } + break; + case SL_WRITEBYTE: return 1; // a byte is logically of size 1 + case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END)); + default: NOT_REACHED(); + } + return 0; +} + + +bool SlObjectMember(void *ptr, const SaveLoad *sld) +{ + VarType conv = GB(sld->conv, 0, 8); + switch (sld->cmd) { + case SL_VAR: + case SL_REF: + case SL_ARR: + case SL_STR: + case SL_LST: + /* CONDITIONAL saveload types depend on the savegame version */ + if (!SlIsObjectValidInSavegame(sld)) return false; + if (SlSkipVariableOnLoad(sld)) return false; + + switch (sld->cmd) { + case SL_VAR: SlSaveLoadConv(ptr, conv); break; + case SL_REF: // Reference variable, translate + if (_sl.save) { + SlWriteUint32(ReferenceToInt(*(void**)ptr, (SLRefType)conv)); + } else { + *(void**)ptr = IntToReference(CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(), (SLRefType)conv); + } + break; + case SL_ARR: SlArray(ptr, sld->length, conv); break; + case SL_STR: SlString(ptr, sld->length, conv); break; + case SL_LST: SlList(ptr, (SLRefType)conv); break; + default: NOT_REACHED(); + } + break; + + /* SL_WRITEBYTE translates a value of a variable to another one upon + * saving or loading. + * XXX - variable renaming abuse + * game_value: the value of the variable ingame is abused by sld->version_from + * file_value: the value of the variable in the savegame is abused by sld->version_to */ + case SL_WRITEBYTE: + if (_sl.save) { + SlWriteByte(sld->version_to); + } else { + *(byte*)ptr = sld->version_from; + } + break; + + /* SL_VEH_INCLUDE loads common code for vehicles */ + case SL_VEH_INCLUDE: + SlObject(ptr, GetVehicleDescription(VEH_END)); + break; + default: NOT_REACHED(); + } + return true; +} + +/** + * Main SaveLoad function. + * @param object The object that is being saved or loaded + * @param sld The SaveLoad description of the object so we know how to manipulate it + */ +void SlObject(void *object, const SaveLoad *sld) +{ + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcObjLength(object, sld)); + if (_sl.need_length == NL_CALCLENGTH) return; + } + + for (; sld->cmd != SL_END; sld++) { + void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld); + SlObjectMember(ptr, sld); + } +} + +/** + * Save or Load (a list of) global variables + * @param sldg The global variable that is being loaded or saved + */ +void SlGlobList(const SaveLoadGlobVarList *sldg) +{ + SlObject(NULL, (const SaveLoad*)sldg); +} + +/** + * Do something of which I have no idea what it is :P + * @param proc The callback procedure that is called + * @param arg The variable that will be used for the callback procedure + */ +void SlAutolength(AutolengthProc *proc, void *arg) +{ + size_t offs; + + assert(_sl.save); + + /* Tell it to calculate the length */ + _sl.need_length = NL_CALCLENGTH; + _sl.obj_len = 0; + proc(arg); + + /* Setup length */ + _sl.need_length = NL_WANTLENGTH; + SlSetLength(_sl.obj_len); + + offs = SlGetOffs() + _sl.obj_len; + + /* And write the stuff */ + proc(arg); + + if (offs != SlGetOffs()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size"); +} + +/** + * Load a chunk of data (eg vehicles, stations, etc.) + * @param ch The chunkhandler that will be used for the operation + */ +static void SlLoadChunk(const ChunkHandler *ch) +{ + byte m = SlReadByte(); + size_t len; + size_t endoffs; + + _sl.block_mode = m; + _sl.obj_len = 0; + + switch (m) { + case CH_ARRAY: + _sl.array_index = 0; + ch->load_proc(); + break; + case CH_SPARSE_ARRAY: + ch->load_proc(); + break; + default: + if ((m & 0xF) == CH_RIFF) { + /* Read length */ + len = (SlReadByte() << 16) | ((m >> 4) << 24); + len += SlReadUint16(); + _sl.obj_len = len; + endoffs = SlGetOffs() + len; + ch->load_proc(); + if (SlGetOffs() != endoffs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size"); + } else { + SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk type"); + } + break; + } +} + +/* Stub Chunk handlers to only calculate length and do nothing else */ +static ChunkSaveLoadProc *_tmp_proc_1; +static inline void SlStubSaveProc2(void *arg) {_tmp_proc_1();} +static void SlStubSaveProc() {SlAutolength(SlStubSaveProc2, NULL);} + +/** Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is + * prefixed by an ID identifying it, followed by data, and terminator where appropiate + * @param ch The chunkhandler that will be used for the operation + */ +static void SlSaveChunk(const ChunkHandler *ch) +{ + ChunkSaveLoadProc *proc = ch->save_proc; + + /* Don't save any chunk information if there is no save handler. */ + if (proc == NULL) return; + + SlWriteUint32(ch->id); + DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id); + + if (ch->flags & CH_AUTO_LENGTH) { + /* Need to calculate the length. Solve that by calling SlAutoLength in the save_proc. */ + _tmp_proc_1 = proc; + proc = SlStubSaveProc; + } + + _sl.block_mode = ch->flags & CH_TYPE_MASK; + switch (ch->flags & CH_TYPE_MASK) { + case CH_RIFF: + _sl.need_length = NL_WANTLENGTH; + proc(); + break; + case CH_ARRAY: + _sl.last_array_index = 0; + SlWriteByte(CH_ARRAY); + proc(); + SlWriteArrayLength(0); // Terminate arrays + break; + case CH_SPARSE_ARRAY: + SlWriteByte(CH_SPARSE_ARRAY); + proc(); + SlWriteArrayLength(0); // Terminate arrays + break; + default: NOT_REACHED(); + } +} + +/** Save all chunks */ +static void SlSaveChunks() +{ + const ChunkHandler *ch; + const ChunkHandler* const *chsc; + uint p; + + for (p = 0; p != CH_NUM_PRI_LEVELS; p++) { + for (chsc = _sl.chs; (ch = *chsc++) != NULL;) { + while (true) { + if (((ch->flags >> CH_PRI_SHL) & (CH_NUM_PRI_LEVELS - 1)) == p) + SlSaveChunk(ch); + if (ch->flags & CH_LAST) + break; + ch++; + } + } + } + + /* Terminator */ + SlWriteUint32(0); +} + +/** Find the ChunkHandler that will be used for processing the found + * chunk in the savegame or in memory + * @param id the chunk in question + * @return returns the appropiate chunkhandler + */ +static const ChunkHandler *SlFindChunkHandler(uint32 id) +{ + const ChunkHandler *ch; + const ChunkHandler *const *chsc; + for (chsc = _sl.chs; (ch = *chsc++) != NULL;) { + for (;;) { + if (ch->id == id) return ch; + if (ch->flags & CH_LAST) break; + ch++; + } + } + return NULL; +} + +/** Load all chunks */ +static void SlLoadChunks() +{ + uint32 id; + const ChunkHandler *ch; + + for (id = SlReadUint32(); id != 0; id = SlReadUint32()) { + DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); + + ch = SlFindChunkHandler(id); + if (ch == NULL) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unknown chunk type"); + SlLoadChunk(ch); + } +} + +/******************************************* + ********** START OF LZO CODE ************** + *******************************************/ +#define LZO_SIZE 8192 + +#include "../minilzo.h" + +static size_t ReadLZO() +{ + byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; + uint32 tmp[2]; + uint32 size; + uint len; + + /* Read header*/ + if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed"); + + /* Check if size is bad */ + ((uint32*)out)[0] = size = tmp[1]; + + if (_sl_version != 0) { + tmp[0] = TO_BE32(tmp[0]); + size = TO_BE32(size); + } + + if (size >= sizeof(out)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Inconsistent size"); + + /* Read block */ + if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); + + /* Verify checksum */ + if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Bad checksum"); + + /* Decompress */ + lzo1x_decompress(out + sizeof(uint32)*1, size, _sl.buf, &len, NULL); + return len; +} + +/* p contains the pointer to the buffer, len contains the pointer to the length. + * len bytes will be written, p and l will be updated to reflect the next buffer. */ +static void WriteLZO(size_t size) +{ + byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; + byte wrkmem[sizeof(byte*)*4096]; + uint outlen; + + lzo1x_1_compress(_sl.buf, (lzo_uint)size, out + sizeof(uint32)*2, &outlen, wrkmem); + ((uint32*)out)[1] = TO_BE32(outlen); + ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32))); + if (fwrite(out, outlen + sizeof(uint32)*2, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); +} + +static bool InitLZO() +{ + _sl.bufsize = LZO_SIZE; + _sl.buf = _sl.buf_ori = MallocT(LZO_SIZE); + return true; +} + +static void UninitLZO() +{ + free(_sl.buf_ori); +} + +/********************************************* + ******** START OF NOCOMP CODE (uncompressed)* + *********************************************/ +static size_t ReadNoComp() +{ + return fread(_sl.buf, 1, LZO_SIZE, _sl.fh); +} + +static void WriteNoComp(size_t size) +{ + if (fwrite(_sl.buf, 1, size, _sl.fh) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); +} + +static bool InitNoComp() +{ + _sl.bufsize = LZO_SIZE; + _sl.buf = _sl.buf_ori = MallocT(LZO_SIZE); + return true; +} + +static void UninitNoComp() +{ + free(_sl.buf_ori); +} + +/******************************************** + ********** START OF MEMORY CODE (in ram)**** + ********************************************/ + +#include "../table/sprites.h" +#include "../gui.h" + +struct ThreadedSave { + uint count; + byte ff_state; + bool saveinprogress; + CursorID cursor; +}; + +/* A maximum size of of 128K * 500 = 64.000KB savegames */ +STATIC_OLD_POOL(Savegame, byte, 17, 500, NULL, NULL) +static ThreadedSave _ts; + +static bool InitMem() +{ + _ts.count = 0; + + _Savegame_pool.CleanPool(); + _Savegame_pool.AddBlockToPool(); + + /* A block from the pool is a contigious area of memory, so it is safe to write to it sequentially */ + _sl.bufsize = GetSavegamePoolSize(); + _sl.buf = GetSavegame(_ts.count); + return true; +} + +static void UnInitMem() +{ + _Savegame_pool.CleanPool(); +} + +static void WriteMem(size_t size) +{ + _ts.count += (uint)size; + /* Allocate new block and new buffer-pointer */ + _Savegame_pool.AddBlockIfNeeded(_ts.count); + _sl.buf = GetSavegame(_ts.count); +} + +/******************************************** + ********** START OF ZLIB CODE ************** + ********************************************/ + +#if defined(WITH_ZLIB) +#include + +static z_stream _z; + +static bool InitReadZlib() +{ + memset(&_z, 0, sizeof(_z)); + if (inflateInit(&_z) != Z_OK) return false; + + _sl.bufsize = 4096; + _sl.buf = _sl.buf_ori = MallocT(4096 + 4096); // also contains fread buffer + return true; +} + +static size_t ReadZlib() +{ + int r; + + _z.next_out = _sl.buf; + _z.avail_out = 4096; + + do { + /* read more bytes from the file? */ + if (_z.avail_in == 0) { + _z.avail_in = (uint)fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh); + } + + /* inflate the data */ + r = inflate(&_z, 0); + if (r == Z_STREAM_END) + break; + + if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed"); + } while (_z.avail_out); + + return 4096 - _z.avail_out; +} + +static void UninitReadZlib() +{ + inflateEnd(&_z); + free(_sl.buf_ori); +} + +static bool InitWriteZlib() +{ + memset(&_z, 0, sizeof(_z)); + if (deflateInit(&_z, 6) != Z_OK) return false; + + _sl.bufsize = 4096; + _sl.buf = _sl.buf_ori = MallocT(4096); // also contains fread buffer + return true; +} + +static void WriteZlibLoop(z_streamp z, byte *p, size_t len, int mode) +{ + byte buf[1024]; // output buffer + int r; + uint n; + z->next_in = p; + z->avail_in = (uInt)len; + do { + z->next_out = buf; + z->avail_out = sizeof(buf); + r = deflate(z, mode); + /* bytes were emitted? */ + if ((n = sizeof(buf) - z->avail_out) != 0) { + if (fwrite(buf, n, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); + } + if (r == Z_STREAM_END) + break; + if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code"); + } while (z->avail_in || !z->avail_out); +} + +static void WriteZlib(size_t len) +{ + WriteZlibLoop(&_z, _sl.buf, len, 0); +} + +static void UninitWriteZlib() +{ + /* flush any pending output. */ + if (_sl.fh) WriteZlibLoop(&_z, NULL, 0, Z_FINISH); + deflateEnd(&_z); + free(_sl.buf_ori); +} + +#endif /* WITH_ZLIB */ + +/******************************************* + ************* END OF CODE ***************** + *******************************************/ + +/* these define the chunks */ +extern const ChunkHandler _gamelog_chunk_handlers[]; +extern const ChunkHandler _map_chunk_handlers[]; +extern const ChunkHandler _misc_chunk_handlers[]; +extern const ChunkHandler _name_chunk_handlers[]; +extern const ChunkHandler _cheat_chunk_handlers[] ; +extern const ChunkHandler _setting_chunk_handlers[]; +extern const ChunkHandler _company_chunk_handlers[]; +extern const ChunkHandler _engine_chunk_handlers[]; +extern const ChunkHandler _veh_chunk_handlers[]; +extern const ChunkHandler _waypoint_chunk_handlers[]; +extern const ChunkHandler _depot_chunk_handlers[]; +extern const ChunkHandler _order_chunk_handlers[]; +extern const ChunkHandler _town_chunk_handlers[]; +extern const ChunkHandler _sign_chunk_handlers[]; +extern const ChunkHandler _station_chunk_handlers[]; +extern const ChunkHandler _industry_chunk_handlers[]; +extern const ChunkHandler _economy_chunk_handlers[]; +extern const ChunkHandler _subsidy_chunk_handlers[]; +extern const ChunkHandler _animated_tile_chunk_handlers[]; +extern const ChunkHandler _newgrf_chunk_handlers[]; +extern const ChunkHandler _group_chunk_handlers[]; +extern const ChunkHandler _cargopacket_chunk_handlers[]; +extern const ChunkHandler _autoreplace_chunk_handlers[]; + +static const ChunkHandler * const _chunk_handlers[] = { + _gamelog_chunk_handlers, + _map_chunk_handlers, + _misc_chunk_handlers, + _name_chunk_handlers, + _cheat_chunk_handlers, + _setting_chunk_handlers, + _veh_chunk_handlers, + _waypoint_chunk_handlers, + _depot_chunk_handlers, + _order_chunk_handlers, + _industry_chunk_handlers, + _economy_chunk_handlers, + _subsidy_chunk_handlers, + _engine_chunk_handlers, + _town_chunk_handlers, + _sign_chunk_handlers, + _station_chunk_handlers, + _company_chunk_handlers, + _animated_tile_chunk_handlers, + _newgrf_chunk_handlers, + _group_chunk_handlers, + _cargopacket_chunk_handlers, + _autoreplace_chunk_handlers, + NULL, +}; + +/** + * Pointers cannot be saved to a savegame, so this functions gets + * the index of the item, and if not available, it hussles with + * pointers (looks really bad :() + * Remember that a NULL item has value 0, and all + * indeces have +1, so vehicle 0 is saved as index 1. + * @param obj The object that we want to get the index of + * @param rt SLRefType type of the object the index is being sought of + * @return Return the pointer converted to an index of the type pointed to + */ +static uint ReferenceToInt(const void *obj, SLRefType rt) +{ + if (obj == NULL) return 0; + + switch (rt) { + case REF_VEHICLE_OLD: // Old vehicles we save as new onces + case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1; + case REF_STATION: return ((const Station*)obj)->index + 1; + case REF_TOWN: return ((const Town*)obj)->index + 1; + case REF_ORDER: return ((const Order*)obj)->index + 1; + case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1; + case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1; + case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1; + case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1; + default: NOT_REACHED(); + } + + return 0; // avoid compiler warning +} + +/** + * Pointers cannot be loaded from a savegame, so this function + * gets the index from the savegame and returns the appropiate + * pointer from the already loaded base. + * Remember that an index of 0 is a NULL pointer so all indeces + * are +1 so vehicle 0 is saved as 1. + * @param index The index that is being converted to a pointer + * @param rt SLRefType type of the object the pointer is sought of + * @return Return the index converted to a pointer of any type + */ +static void *IntToReference(uint index, SLRefType rt) +{ + /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE, + * and should be loaded like that */ + if (rt == REF_VEHICLE_OLD && !CheckSavegameVersionOldStyle(4, 4)) { + rt = REF_VEHICLE; + } + + /* No need to look up NULL pointers, just return immediately */ + if (rt != REF_VEHICLE_OLD && index == 0) { + return NULL; + } + + index--; // correct for the NULL index + + switch (rt) { + case REF_ORDERLIST: + if (_OrderList_pool.AddBlockIfNeeded(index)) return GetOrderList(index); + error("Orders: failed loading savegame: too many order lists"); + + case REF_ORDER: + if (_Order_pool.AddBlockIfNeeded(index)) return GetOrder(index); + error("Orders: failed loading savegame: too many orders"); + + case REF_VEHICLE: + if (_Vehicle_pool.AddBlockIfNeeded(index)) return GetVehicle(index); + error("Vehicles: failed loading savegame: too many vehicles"); + + case REF_STATION: + if (_Station_pool.AddBlockIfNeeded(index)) return GetStation(index); + error("Stations: failed loading savegame: too many stations"); + + case REF_TOWN: + if (_Town_pool.AddBlockIfNeeded(index)) return GetTown(index); + error("Towns: failed loading savegame: too many towns"); + + case REF_ROADSTOPS: + if (_RoadStop_pool.AddBlockIfNeeded(index)) return GetRoadStop(index); + error("RoadStops: failed loading savegame: too many RoadStops"); + + case REF_ENGINE_RENEWS: + if (_EngineRenew_pool.AddBlockIfNeeded(index)) return GetEngineRenew(index); + error("EngineRenews: failed loading savegame: too many EngineRenews"); + + case REF_CARGO_PACKET: + if (_CargoPacket_pool.AddBlockIfNeeded(index)) return GetCargoPacket(index); + error("CargoPackets: failed loading savegame: too many Cargo packets"); + + case REF_VEHICLE_OLD: + /* Old vehicles were saved differently: + * invalid vehicle was 0xFFFF, + * and the index was not - 1.. correct for this */ + index++; + if (index == INVALID_VEHICLE) return NULL; + + if (_Vehicle_pool.AddBlockIfNeeded(index)) return GetVehicle(index); + error("Vehicles: failed loading savegame: too many vehicles"); + + default: NOT_REACHED(); + } + + return NULL; +} + +/** The format for a reader/writer type of a savegame */ +struct SaveLoadFormat { + const char *name; ///< name of the compressor/decompressor (debug-only) + uint32 tag; ///< the 4-letter tag by which it is identified in the savegame + + bool (*init_read)(); ///< function executed upon initalization of the loader + ReaderProc *reader; ///< function that loads the data from the file + void (*uninit_read)(); ///< function executed when reading is finished + + bool (*init_write)(); ///< function executed upon intialization of the saver + WriterProc *writer; ///< function that saves the data to the file + void (*uninit_write)(); ///< function executed when writing is done +}; + +static const SaveLoadFormat _saveload_formats[] = { + {"memory", 0, NULL, NULL, NULL, InitMem, WriteMem, UnInitMem}, + {"lzo", TO_BE32X('OTTD'), InitLZO, ReadLZO, UninitLZO, InitLZO, WriteLZO, UninitLZO}, + {"none", TO_BE32X('OTTN'), InitNoComp, ReadNoComp, UninitNoComp, InitNoComp, WriteNoComp, UninitNoComp}, +#if defined(WITH_ZLIB) + {"zlib", TO_BE32X('OTTZ'), InitReadZlib, ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib}, +#else + {"zlib", TO_BE32X('OTTZ'), NULL, NULL, NULL, NULL, NULL, NULL}, +#endif +}; + +/** + * Return the savegameformat of the game. Whether it was create with ZLIB compression + * uncompressed, or another type + * @param s Name of the savegame format. If NULL it picks the first available one + * @return Pointer to SaveLoadFormat struct giving all characteristics of this type of savegame + */ +static const SaveLoadFormat *GetSavegameFormat(const char *s) +{ + const SaveLoadFormat *def = endof(_saveload_formats) - 1; + + /* find default savegame format, the highest one with which files can be written */ + while (!def->init_write) def--; + + if (s != NULL && s[0] != '\0') { + const SaveLoadFormat *slf; + for (slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) { + if (slf->init_write != NULL && strcmp(s, slf->name) == 0) + return slf; + } + + ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name); + } + return def; +} + +/* actual loader/saver function */ +void InitializeGame(uint size_x, uint size_y, bool reset_date); +extern bool AfterLoadGame(); +extern bool LoadOldSaveGame(const char *file); + +/** Small helper function to close the to be loaded savegame an signal error */ +static inline SaveOrLoadResult AbortSaveLoad() +{ + if (_sl.fh != NULL) fclose(_sl.fh); + + _sl.fh = NULL; + return SL_ERROR; +} + +/** Update the gui accordingly when starting saving + * and set locks on saveload. Also turn off fast-forward cause with that + * saving takes Aaaaages */ +static void SaveFileStart() +{ + _ts.ff_state = _fast_forward; + _fast_forward = 0; + if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE); + + InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START); + _ts.saveinprogress = true; +} + +/** Update the gui accordingly when saving is done and release locks + * on saveload */ +static void SaveFileDone() +{ + if (_game_mode != GM_MENU) _fast_forward = _ts.ff_state; + if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE); + + InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH); + _ts.saveinprogress = false; +} + +/** Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friends) */ +void SetSaveLoadError(StringID str) +{ + _sl.error_str = str; +} + +/** Get the string representation of the error message */ +const char *GetSaveLoadErrorString() +{ + SetDParam(0, _sl.error_str); + SetDParamStr(1, _sl.extra_msg); + + static char err_str[512]; + GetString(err_str, _sl.save ? STR_4007_GAME_SAVE_FAILED : STR_4009_GAME_LOAD_FAILED, lastof(err_str)); + return err_str; +} + +/** Show a gui message when saving has failed */ +static void SaveFileError() +{ + SetDParamStr(0, GetSaveLoadErrorString()); + ShowErrorMessage(STR_JUST_RAW_STRING, STR_NULL, 0, 0); + SaveFileDone(); +} + +/** We have written the whole game into memory, _Savegame_pool, now find + * and appropiate compressor and start writing to file. + */ +static SaveOrLoadResult SaveFileToDisk(bool threaded) +{ + const SaveLoadFormat *fmt; + uint32 hdr[2]; + + _sl.excpt_uninit = NULL; + try { + fmt = GetSavegameFormat(_savegame_format); + + /* We have written our stuff to memory, now write it to file! */ + hdr[0] = fmt->tag; + hdr[1] = TO_BE32(SAVEGAME_VERSION << 16); + if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); + + if (!fmt->init_write()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); + + { + uint i; + uint count = 1 << Savegame_POOL_BLOCK_SIZE_BITS; + + if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk"); + for (i = 0; i != _Savegame_pool.GetBlockCount() - 1; i++) { + _sl.buf = _Savegame_pool.blocks[i]; + fmt->writer(count); + } + + /* The last block is (almost) always not fully filled, so only write away + * as much data as it is in there */ + _sl.buf = _Savegame_pool.blocks[i]; + fmt->writer(_ts.count - (i * count)); + } + + fmt->uninit_write(); + if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk"); + GetSavegameFormat("memory")->uninit_write(); // clean the memorypool + fclose(_sl.fh); + + if (threaded) SetAsyncSaveFinish(SaveFileDone); + + return SL_OK; + } + catch (...) { + AbortSaveLoad(); + if (_sl.excpt_uninit != NULL) _sl.excpt_uninit(); + + /* Skip the "color" character */ + DEBUG(sl, 0, GetSaveLoadErrorString() + 3); + + if (threaded) { + SetAsyncSaveFinish(SaveFileError); + } else { + SaveFileError(); + } + return SL_ERROR; + } +} + +static void SaveFileToDiskThread(void *arg) +{ + SaveFileToDisk(true); +} + +void WaitTillSaved() +{ + if (_save_thread == NULL) return; + + _save_thread->Join(); + delete _save_thread; + _save_thread = NULL; +} + +/** + * Main Save or Load function where the high-level saveload functions are + * handled. It opens the savegame, selects format and checks versions + * @param filename The name of the savegame being created/loaded + * @param mode Save or load. Load can also be a TTD(Patch) game. Use SL_LOAD, SL_OLD_LOAD or SL_SAVE + * @return Return the results of the action. SL_OK, SL_ERROR or SL_REINIT ("unload" the game) + */ +SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb) +{ + uint32 hdr[2]; + const SaveLoadFormat *fmt; + + /* An instance of saving is already active, so don't go saving again */ + if (_ts.saveinprogress && mode == SL_SAVE) { + /* if not an autosave, but a user action, show error message */ + if (!_do_autosave) ShowErrorMessage(INVALID_STRING_ID, STR_SAVE_STILL_IN_PROGRESS, 0, 0); + return SL_OK; + } + WaitTillSaved(); + + _next_offs = 0; + + /* Load a TTDLX or TTDPatch game */ + if (mode == SL_OLD_LOAD) { + InitializeGame(256, 256, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused + GamelogReset(); + if (!LoadOldSaveGame(filename)) return SL_REINIT; + _sl_version = 0; + _sl_minor_version = 0; + GamelogStartAction(GLAT_LOAD); + if (!AfterLoadGame()) { + GamelogStopAction(); + return SL_REINIT; + } + GamelogStopAction(); + return SL_OK; + } + + _sl.excpt_uninit = NULL; + try { + _sl.fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb); + + /* Make it a little easier to load savegames from the console */ + if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", SAVE_DIR); + if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", BASE_DIR); + + if (_sl.fh == NULL) { + SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); + } + + _sl.bufe = _sl.bufp = NULL; + _sl.offs_base = 0; + _sl.save = (mode != 0); + _sl.chs = _chunk_handlers; + + /* General tactic is to first save the game to memory, then use an available writer + * to write it to file, either in threaded mode if possible, or single-threaded */ + if (mode == SL_SAVE) { /* SAVE game */ + DEBUG(desync, 1, "save: %s\n", filename); + fmt = GetSavegameFormat("memory"); // write to memory + + _sl.write_bytes = fmt->writer; + _sl.excpt_uninit = fmt->uninit_write; + if (!fmt->init_write()) { + DEBUG(sl, 0, "Initializing writer '%s' failed.", fmt->name); + return AbortSaveLoad(); + } + + _sl_version = SAVEGAME_VERSION; + + SaveViewportBeforeSaveGame(); + SlSaveChunks(); + SlWriteFill(); // flush the save buffer + + SaveFileStart(); + if (_network_server || + (_save_thread = ThreadObject::New(&SaveFileToDiskThread, NULL)) == NULL) { + if (!_network_server) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode..."); + + SaveOrLoadResult result = SaveFileToDisk(false); + SaveFileDone(); + + return result; + } + } else { /* LOAD game */ + assert(mode == SL_LOAD); + DEBUG(desync, 1, "load: %s\n", filename); + + if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); + + /* see if we have any loader for this type. */ + for (fmt = _saveload_formats; ; fmt++) { + /* No loader found, treat as version 0 and use LZO format */ + if (fmt == endof(_saveload_formats)) { + DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format"); + #if defined(WINCE) + /* Of course some system had not to support rewind ;) */ + fseek(_sl.fh, 0L, SEEK_SET); + clearerr(_sl.fh); + #else + rewind(_sl.fh); + #endif + _sl_version = 0; + _sl_minor_version = 0; + fmt = _saveload_formats + 1; // LZO + break; + } + + if (fmt->tag == hdr[0]) { + /* check version number */ + _sl_version = TO_BE32(hdr[1]) >> 16; + /* Minor is not used anymore from version 18.0, but it is still needed + * in versions before that (4 cases) which can't be removed easy. + * Therefor it is loaded, but never saved (or, it saves a 0 in any scenario). + * So never EVER use this minor version again. -- TrueLight -- 22-11-2005 */ + _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF; + + DEBUG(sl, 1, "Loading savegame version %d", _sl_version); + + /* Is the version higher than the current? */ + if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME); + break; + } + } + + _sl.read_bytes = fmt->reader; + _sl.excpt_uninit = fmt->uninit_read; + + /* loader for this savegame type is not implemented? */ + if (fmt->init_read == NULL) { + char err_str[64]; + snprintf(err_str, lengthof(err_str), "Loader for '%s' is not available.", fmt->name); + SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str); + } + + if (!fmt->init_read()) { + char err_str[64]; + snprintf(err_str, lengthof(err_str), "Initializing loader '%s' failed", fmt->name); + SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str); + } + + /* Old maps were hardcoded to 256x256 and thus did not contain + * any mapsize information. Pre-initialize to 256x256 to not to + * confuse old games */ + InitializeGame(256, 256, true); + + GamelogReset(); + + SlLoadChunks(); + fmt->uninit_read(); + fclose(_sl.fh); + + GamelogStartAction(GLAT_LOAD); + + _savegame_type = SGT_OTTD; + + /* After loading fix up savegame for any internal changes that + * might've occured since then. If it fails, load back the old game */ + if (!AfterLoadGame()) { + GamelogStopAction(); + return SL_REINIT; + } + + GamelogStopAction(); + } + + return SL_OK; + } + catch (...) { + AbortSaveLoad(); + + /* deinitialize compressor. */ + if (_sl.excpt_uninit != NULL) _sl.excpt_uninit(); + + /* Skip the "color" character */ + DEBUG(sl, 0, GetSaveLoadErrorString() + 3); + + /* A saver/loader exception!! reinitialize all variables to prevent crash! */ + return (mode == SL_LOAD) ? SL_REINIT : SL_ERROR; + } +} + +/** Do a save when exiting the game (patch option) _settings_client.gui.autosave_on_exit */ +void DoExitSave() +{ + SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR); +} + +/** + * Fill the buffer with the default name for a savegame *or* screenshot. + * @param buf the buffer to write to. + * @param last the last element in the buffer. + */ +void GenerateDefaultSaveName(char *buf, const char *last) +{ + /* Check if we have a name for this map, which is the name of the first + * available company. When there's no company available we'll use + * 'Spectator' as "company" name. */ + CompanyID cid = _local_company; + if (!IsValidCompanyID(cid)) { + const Company *c; + FOR_ALL_COMPANIES(c) { + cid = c->index; + break; + } + } + + SetDParam(0, cid); + + /* Insert current date */ + switch (_settings_client.gui.date_format_in_default_names) { + case 0: SetDParam(1, STR_JUST_DATE_LONG); break; + case 1: SetDParam(1, STR_JUST_DATE_TINY); break; + case 2: SetDParam(1, STR_JUST_DATE_ISO); break; + default: NOT_REACHED(); + } + SetDParam(2, _date); + + /* Get the correct string (special string for when there's not company) */ + GetString(buf, !IsValidCompanyID(cid) ? STR_GAME_SAVELOAD_SPECTATOR_SAVEGAME : STR_4004, last); + SanitizeFilename(buf); +} + +#if 0 +/** + * Function to get the type of the savegame by looking at the file header. + * NOTICE: Not used right now, but could be used if extensions of savegames are garbled + * @param file Savegame to be checked + * @return SL_OLD_LOAD or SL_LOAD of the file + */ +int GetSavegameType(char *file) +{ + const SaveLoadFormat *fmt; + uint32 hdr; + FILE *f; + int mode = SL_OLD_LOAD; + + f = fopen(file, "rb"); + if (fread(&hdr, sizeof(hdr), 1, f) != 1) { + DEBUG(sl, 0, "Savegame is obsolete or invalid format"); + mode = SL_LOAD; // don't try to get filename, just show name as it is written + } else { + /* see if we have any loader for this type. */ + for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) { + if (fmt->tag == hdr) { + mode = SL_LOAD; // new type of savegame + break; + } + } + } + + fclose(f); + return mode; +} +#endif diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h new file mode 100644 --- /dev/null +++ b/src/saveload/saveload.h @@ -0,0 +1,332 @@ +/* $Id$ */ + +/** @file saveload.h Functions/types related to saving and loading games. */ + +#ifndef SAVELOAD_H +#define SAVELOAD_H + +#include "../fileio_type.h" + +#ifdef SIZE_MAX +#undef SIZE_MAX +#endif + +#define SIZE_MAX ((size_t)-1) + +enum SaveOrLoadResult { + SL_OK = 0, ///< completed successfully + SL_ERROR = 1, ///< error that was caught before internal structures were modified + SL_REINIT = 2, ///< error that was caught in the middle of updating game state, need to clear it. (can only happen during load) +}; + +enum SaveOrLoadMode { + SL_INVALID = -1, + SL_LOAD = 0, + SL_SAVE = 1, + SL_OLD_LOAD = 2, + SL_PNG = 3, + SL_BMP = 4, +}; + +enum SavegameType { + SGT_TTD, ///< TTD savegame (can be detected incorrectly) + SGT_TTDP1, ///< TTDP savegame ( -//- ) (data at NW border) + SGT_TTDP2, ///< TTDP savegame in new format (data at SE border) + SGT_OTTD ///< OTTD savegame +}; + +void GenerateDefaultSaveName(char *buf, const char *last); +void SetSaveLoadError(uint16 str); +const char *GetSaveLoadErrorString(); +SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb); +void WaitTillSaved(); +void DoExitSave(); + + +typedef void ChunkSaveLoadProc(); +typedef void AutolengthProc(void *arg); + +struct ChunkHandler { + uint32 id; + ChunkSaveLoadProc *save_proc; + ChunkSaveLoadProc *load_proc; + uint32 flags; +}; + +struct NullStruct { + byte null; +}; + +enum SLRefType { + REF_ORDER = 0, + REF_VEHICLE = 1, + REF_STATION = 2, + REF_TOWN = 3, + REF_VEHICLE_OLD = 4, + REF_ROADSTOPS = 5, + REF_ENGINE_RENEWS = 6, + REF_CARGO_PACKET = 7, + REF_ORDERLIST = 8, +}; + +#define SL_MAX_VERSION 255 + +enum { + INC_VEHICLE_COMMON = 0, +}; + +enum { + CH_RIFF = 0, + CH_ARRAY = 1, + CH_SPARSE_ARRAY = 2, + CH_TYPE_MASK = 3, + CH_LAST = 8, + CH_AUTO_LENGTH = 16, + + CH_PRI_0 = 0 << 4, + CH_PRI_1 = 1 << 4, + CH_PRI_2 = 2 << 4, + CH_PRI_3 = 3 << 4, + CH_PRI_SHL = 4, + CH_NUM_PRI_LEVELS = 4, +}; + +/** VarTypes is the general bitmasked magic type that tells us + * certain characteristics about the variable it refers to. For example + * SLE_FILE_* gives the size(type) as it would be in the savegame and + * SLE_VAR_* the size(type) as it is in memory during runtime. These are + * the first 8 bits (0-3 SLE_FILE, 4-7 SLE_VAR). + * Bits 8-15 are reserved for various flags as explained below */ +enum VarTypes { + /* 4 bits allocated a maximum of 16 types for NumberType */ + SLE_FILE_I8 = 0, + SLE_FILE_U8 = 1, + SLE_FILE_I16 = 2, + SLE_FILE_U16 = 3, + SLE_FILE_I32 = 4, + SLE_FILE_U32 = 5, + SLE_FILE_I64 = 6, + SLE_FILE_U64 = 7, + SLE_FILE_STRINGID = 8, ///< StringID offset into strings-array + SLE_FILE_STRING = 9, + /* 6 more possible file-primitives */ + + /* 4 bits allocated a maximum of 16 types for NumberType */ + SLE_VAR_BL = 0 << 4, + SLE_VAR_I8 = 1 << 4, + SLE_VAR_U8 = 2 << 4, + SLE_VAR_I16 = 3 << 4, + SLE_VAR_U16 = 4 << 4, + SLE_VAR_I32 = 5 << 4, + SLE_VAR_U32 = 6 << 4, + SLE_VAR_I64 = 7 << 4, + SLE_VAR_U64 = 8 << 4, + SLE_VAR_NULL = 9 << 4, ///< useful to write zeros in savegame. + SLE_VAR_STRB = 10 << 4, ///< string (with pre-allocated buffer) + SLE_VAR_STRBQ = 11 << 4, ///< string enclosed in quotes (with pre-allocated buffer) + SLE_VAR_STR = 12 << 4, ///< string pointer + SLE_VAR_STRQ = 13 << 4, ///< string pointer enclosed in quotes + SLE_VAR_NAME = 14 << 4, ///< old custom name to be converted to a char pointer + /* 1 more possible memory-primitives */ + + /* Shortcut values */ + SLE_VAR_CHAR = SLE_VAR_I8, + + /* Default combinations of variables. As savegames change, so can variables + * and thus it is possible that the saved value and internal size do not + * match and you need to specify custom combo. The defaults are listed here */ + SLE_BOOL = SLE_FILE_I8 | SLE_VAR_BL, + SLE_INT8 = SLE_FILE_I8 | SLE_VAR_I8, + SLE_UINT8 = SLE_FILE_U8 | SLE_VAR_U8, + SLE_INT16 = SLE_FILE_I16 | SLE_VAR_I16, + SLE_UINT16 = SLE_FILE_U16 | SLE_VAR_U16, + SLE_INT32 = SLE_FILE_I32 | SLE_VAR_I32, + SLE_UINT32 = SLE_FILE_U32 | SLE_VAR_U32, + SLE_INT64 = SLE_FILE_I64 | SLE_VAR_I64, + SLE_UINT64 = SLE_FILE_U64 | SLE_VAR_U64, + SLE_CHAR = SLE_FILE_I8 | SLE_VAR_CHAR, + SLE_STRINGID = SLE_FILE_STRINGID | SLE_VAR_U16, + SLE_STRINGBUF = SLE_FILE_STRING | SLE_VAR_STRB, + SLE_STRINGBQUOTE = SLE_FILE_STRING | SLE_VAR_STRBQ, + SLE_STRING = SLE_FILE_STRING | SLE_VAR_STR, + SLE_STRINGQUOTE = SLE_FILE_STRING | SLE_VAR_STRQ, + SLE_NAME = SLE_FILE_STRINGID | SLE_VAR_NAME, + + /* Shortcut values */ + SLE_UINT = SLE_UINT32, + SLE_INT = SLE_INT32, + SLE_STRB = SLE_STRINGBUF, + SLE_STRBQ = SLE_STRINGBQUOTE, + SLE_STR = SLE_STRING, + SLE_STRQ = SLE_STRINGQUOTE, + + /* 8 bits allocated for a maximum of 8 flags + * Flags directing saving/loading of a variable */ + SLF_SAVE_NO = 1 << 8, ///< do not save with savegame, basically client-based + SLF_CONFIG_NO = 1 << 9, ///< do not save to config file + SLF_NETWORK_NO = 1 << 10, ///< do not synchronize over network (but it is saved if SSF_SAVE_NO is not set) + /* 5 more possible flags */ +}; + +typedef uint32 VarType; + +enum SaveLoadTypes { + SL_VAR = 0, + SL_REF = 1, + SL_ARR = 2, + SL_STR = 3, + SL_LST = 4, + // non-normal save-load types + SL_WRITEBYTE = 8, + SL_VEH_INCLUDE = 9, + SL_END = 15 +}; + +typedef byte SaveLoadType; + +/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */ +struct SaveLoad { + bool global; ///< should we load a global variable or a non-global one + SaveLoadType cmd; ///< the action to take with the saved/loaded type, All types need different action + VarType conv; ///< type of the variable to be saved, int + uint16 length; ///< (conditional) length of the variable (eg. arrays) (max array size is 65536 elements) + uint16 version_from; ///< save/load the variable starting from this savegame version + uint16 version_to; ///< save/load the variable until this savegame version + /* NOTE: This element either denotes the address of the variable for a global + * variable, or the offset within a struct which is then bound to a variable + * during runtime. Decision on which one to use is controlled by the function + * that is called to save it. address: global=true, offset: global=false */ + void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536) +}; + +/* Same as SaveLoad but global variables are used (for better readability); */ +typedef SaveLoad SaveLoadGlobVarList; + +/* Simple variables, references (pointers) and arrays */ +#define SLE_GENERAL(cmd, base, variable, type, length, from, to) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable)} +#define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to) +#define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to) +#define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to) +#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to) +#define SLE_CONDLST(base, variable, type, from, to) SLE_GENERAL(SL_LST, base, variable, type, 0, from, to) + +#define SLE_VAR(base, variable, type) SLE_CONDVAR(base, variable, type, 0, SL_MAX_VERSION) +#define SLE_REF(base, variable, type) SLE_CONDREF(base, variable, type, 0, SL_MAX_VERSION) +#define SLE_ARR(base, variable, type, length) SLE_CONDARR(base, variable, type, length, 0, SL_MAX_VERSION) +#define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, 0, SL_MAX_VERSION) +#define SLE_LST(base, variable, type) SLE_CONDLST(base, variable, type, 0, SL_MAX_VERSION) + +#define SLE_CONDNULL(length, from, to) SLE_CONDARR(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to) + +/* Translate values ingame to different values in the savegame and vv */ +#define SLE_WRITEBYTE(base, variable, value) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, value, value) + +/* The same as the ones at the top, only the offset is given directly; used for unions */ +#define SLE_GENERALX(cmd, offset, type, length, param1, param2) {false, cmd, type, length, param1, param2, (void*)(offset)} +#define SLE_CONDVARX(offset, type, from, to) SLE_GENERALX(SL_VAR, offset, type, 0, from, to) +#define SLE_CONDARRX(offset, type, length, from, to) SLE_GENERALX(SL_ARR, offset, type, length, from, to) +#define SLE_CONDREFX(offset, type, from, to) SLE_GENERALX(SL_REF, offset, type, 0, from, to) + +#define SLE_VARX(offset, type) SLE_CONDVARX(offset, type, 0, SL_MAX_VERSION) +#define SLE_REFX(offset, type) SLE_CONDREFX(offset, type, 0, SL_MAX_VERSION) + +#define SLE_WRITEBYTEX(offset, something) SLE_GENERALX(SL_WRITEBYTE, offset, 0, 0, something, 0) +#define SLE_VEH_INCLUDEX() SLE_GENERALX(SL_VEH_INCLUDE, 0, 0, 0, 0, SL_MAX_VERSION) + +/* End marker */ +#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL} + +/* Simple variables, references (pointers) and arrays, but for global variables */ +#define SLEG_GENERAL(cmd, variable, type, length, from, to) {true, cmd, type, length, from, to, (void*)&variable} + +#define SLEG_CONDVAR(variable, type, from, to) SLEG_GENERAL(SL_VAR, variable, type, 0, from, to) +#define SLEG_CONDREF(variable, type, from, to) SLEG_GENERAL(SL_REF, variable, type, 0, from, to) +#define SLEG_CONDARR(variable, type, length, from, to) SLEG_GENERAL(SL_ARR, variable, type, length, from, to) +#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_GENERAL(SL_STR, variable, type, length, from, to) +#define SLEG_CONDLST(variable, type, from, to) SLEG_GENERAL(SL_LST, variable, type, 0, from, to) + +#define SLEG_VAR(variable, type) SLEG_CONDVAR(variable, type, 0, SL_MAX_VERSION) +#define SLEG_REF(variable, type) SLEG_CONDREF(variable, type, 0, SL_MAX_VERSION) +#define SLEG_ARR(variable, type) SLEG_CONDARR(variable, type, lengthof(variable), 0, SL_MAX_VERSION) +#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, lengthof(variable), 0, SL_MAX_VERSION) +#define SLEG_LST(variable, type) SLEG_CONDLST(variable, type, 0, SL_MAX_VERSION) + +#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to, (void*)NULL} + +#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL} + +/** Checks if the savegame is below major.minor. + */ +static inline bool CheckSavegameVersionOldStyle(uint16 major, byte minor) +{ + extern uint16 _sl_version; + extern byte _sl_minor_version; + return (_sl_version < major) || (_sl_version == major && _sl_minor_version < minor); +} + +/** Checks if the savegame is below version. + */ +static inline bool CheckSavegameVersion(uint16 version) +{ + extern uint16 _sl_version; + return _sl_version < version; +} + +/** Checks if some version from/to combination falls within the range of the + * active savegame version */ +static inline bool SlIsObjectCurrentlyValid(uint16 version_from, uint16 version_to) +{ + extern const uint16 SAVEGAME_VERSION; + if (SAVEGAME_VERSION < version_from || SAVEGAME_VERSION > version_to) return false; + + return true; +} + +/* Get the NumberType of a setting. This describes the integer type + * as it is represented in memory + * @param type VarType holding information about the variable-type + * @return return the SLE_VAR_* part of a variable-type description */ +static inline VarType GetVarMemType(VarType type) +{ + return type & 0xF0; // GB(type, 4, 4) << 4; +} + +/* Get the FileType of a setting. This describes the integer type + * as it is represented in a savegame/file + * @param type VarType holding information about the variable-type + * @param return the SLE_FILE_* part of a variable-type description */ +static inline VarType GetVarFileType(VarType type) +{ + return type & 0xF; // GB(type, 0, 4); +} + +/** Get the address of the variable. Which one to pick depends on the object + * pointer. If it is NULL we are dealing with global variables so the address + * is taken. If non-null only the offset is stored in the union and we need + * to add this to the address of the object */ +static inline void *GetVariableAddress(const void *object, const SaveLoad *sld) +{ + return (byte*)(sld->global ? NULL : object) + (ptrdiff_t)sld->address; +} + +int64 ReadValue(const void *ptr, VarType conv); +void WriteValue(void *ptr, VarType conv, int64 val); + +void SlSetArrayIndex(uint index); +int SlIterateArray(); + +void SlAutolength(AutolengthProc *proc, void *arg); +size_t SlGetFieldLength(); +void SlSetLength(size_t length); +size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld); +size_t SlCalcObjLength(const void *object, const SaveLoad *sld); + +byte SlReadByte(); +void SlWriteByte(byte b); + +void SlGlobList(const SaveLoadGlobVarList *sldg); +void SlArray(void *array, size_t length, VarType conv); +void SlObject(void *object, const SaveLoad *sld); +bool SlObjectMember(void *object, const SaveLoad *sld); + +#endif /* SAVELOAD_H */ diff --git a/src/saveload/saveload_internal.h b/src/saveload/saveload_internal.h new file mode 100644 --- /dev/null +++ b/src/saveload/saveload_internal.h @@ -0,0 +1,40 @@ +/* $Id$ */ + +/** @file saveload_internal.h Declaration of functions used in more save/load files */ + +#ifndef SAVELOAD_INTERNAL_H +#define SAVELOAD_INTERNAL_H + +#include "../strings_type.h" +#include "../company_manager_face.h" +#include "../order_base.h" + +void InitializeOldNames(); +StringID RemapOldStringID(StringID s); +char *CopyFromOldName(StringID id); +void ResetOldNames(); + +void FixOldWaypoints(); + +void AfterLoadWaypoints(); +void AfterLoadVehicles(bool part_of_load); +void AfterLoadStations(); +void AfterLoadTown(); +void UpdateHousesAndTowns(); + +void UpdateOldAircraft(); + +void SaveViewportBeforeSaveGame(); +void ResetViewportAfterLoadGame(); + +void ConvertOldMultiheadToNew(); +void ConnectMultiheadedTrains(); + +extern int32 _saved_scrollpos_x; +extern int32 _saved_scrollpos_y; + +CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face); + +Order UnpackOldOrder(uint16 packed); + +#endif /* SAVELOAD_INTERNAL_H */ diff --git a/src/saveload/signs_sl.cpp b/src/saveload/signs_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/signs_sl.cpp @@ -0,0 +1,49 @@ +/* $Id$ */ + +/** @file signs_sl.cpp Code handling saving and loading of economy data */ + +#include "../stdafx.h" +#include "../strings_func.h" +#include "../company_func.h" +#include "../signs_base.h" +#include "../signs_func.h" + +#include "saveload_internal.h" +#include "saveload.h" + +static const SaveLoad _sign_desc[] = { + SLE_CONDVAR(Sign, name, SLE_NAME, 0, 83), + SLE_CONDSTR(Sign, name, SLE_STR, 0, 84, SL_MAX_VERSION), + SLE_CONDVAR(Sign, x, SLE_FILE_I16 | SLE_VAR_I32, 0, 4), + SLE_CONDVAR(Sign, y, SLE_FILE_I16 | SLE_VAR_I32, 0, 4), + SLE_CONDVAR(Sign, x, SLE_INT32, 5, SL_MAX_VERSION), + SLE_CONDVAR(Sign, y, SLE_INT32, 5, SL_MAX_VERSION), + SLE_CONDVAR(Sign, owner, SLE_UINT8, 6, SL_MAX_VERSION), + SLE_VAR(Sign, z, SLE_UINT8), + SLE_END() +}; + +/** Save all signs */ +static void Save_SIGN() +{ + Sign *si; + + FOR_ALL_SIGNS(si) { + SlSetArrayIndex(si->index); + SlObject(si, _sign_desc); + } +} + +/** Load all signs */ +static void Load_SIGN() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Sign *si = new (index) Sign(); + SlObject(si, _sign_desc); + } +} + +extern const ChunkHandler _sign_chunk_handlers[] = { + { 'SIGN', Save_SIGN, Load_SIGN, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/station_sl.cpp @@ -0,0 +1,227 @@ +/* $Id$ */ + +/** @file station_sl.cpp Code handling saving and loading of economy data */ + +#include "../stdafx.h" +#include "../station_base.h" +#include "../core/bitmath_func.hpp" +#include "../core/alloc_func.hpp" +#include "../variables.h" +#include "../newgrf_station.h" + +#include "saveload.h" + + +void AfterLoadStations() +{ + /* Update the speclists of all stations to point to the currently loaded custom stations. */ + Station *st; + FOR_ALL_STATIONS(st) { + for (uint i = 0; i < st->num_specs; i++) { + if (st->speclist[i].grfid == 0) continue; + + st->speclist[i].spec = GetCustomStationSpecByGrf(st->speclist[i].grfid, st->speclist[i].localidx); + } + + for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache(); + + StationUpdateAnimTriggers(st); + } +} + +static const SaveLoad _roadstop_desc[] = { + SLE_VAR(RoadStop, xy, SLE_UINT32), + SLE_CONDNULL(1, 0, 44), + SLE_VAR(RoadStop, status, SLE_UINT8), + /* Index was saved in some versions, but this is not needed */ + SLE_CONDNULL(4, 0, 8), + SLE_CONDNULL(2, 0, 44), + SLE_CONDNULL(1, 0, 25), + + SLE_REF(RoadStop, next, REF_ROADSTOPS), + SLE_CONDNULL(2, 0, 44), + + SLE_CONDNULL(4, 0, 24), + SLE_CONDNULL(1, 25, 25), + + SLE_END() +}; + +static const SaveLoad _station_desc[] = { + SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Station, xy, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDNULL(4, 0, 5), ///< bus/lorry tile + SLE_CONDVAR(Station, train_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Station, train_tile, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Station, airport_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Station, airport_tile, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Station, dock_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Station, dock_tile, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_REF(Station, town, REF_TOWN), + SLE_VAR(Station, trainst_w, SLE_UINT8), + SLE_CONDVAR(Station, trainst_h, SLE_UINT8, 2, SL_MAX_VERSION), + + SLE_CONDNULL(1, 0, 3), ///< alpha_order + + SLE_VAR(Station, string_id, SLE_STRINGID), + SLE_CONDSTR(Station, name, SLE_STR, 0, 84, SL_MAX_VERSION), + SLE_CONDVAR(Station, indtype, SLE_UINT8, 103, SL_MAX_VERSION), + SLE_VAR(Station, had_vehicle_of_type, SLE_UINT16), + + SLE_VAR(Station, time_since_load, SLE_UINT8), + SLE_VAR(Station, time_since_unload, SLE_UINT8), + SLE_VAR(Station, delete_ctr, SLE_UINT8), + SLE_VAR(Station, owner, SLE_UINT8), + SLE_VAR(Station, facilities, SLE_UINT8), + SLE_VAR(Station, airport_type, SLE_UINT8), + + SLE_CONDNULL(2, 0, 5), ///< Truck/bus stop status + SLE_CONDNULL(1, 0, 4), ///< Blocked months + + SLE_CONDVAR(Station, airport_flags, SLE_VAR_U64 | SLE_FILE_U16, 0, 2), + SLE_CONDVAR(Station, airport_flags, SLE_VAR_U64 | SLE_FILE_U32, 3, 45), + SLE_CONDVAR(Station, airport_flags, SLE_UINT64, 46, SL_MAX_VERSION), + + SLE_CONDNULL(2, 0, 25), ///< last-vehicle + SLE_CONDVAR(Station, last_vehicle_type, SLE_UINT8, 26, SL_MAX_VERSION), + + SLE_CONDNULL(2, 3, 25), ///< custom station class and id + SLE_CONDVAR(Station, build_date, SLE_FILE_U16 | SLE_VAR_I32, 3, 30), + SLE_CONDVAR(Station, build_date, SLE_INT32, 31, SL_MAX_VERSION), + + SLE_CONDREF(Station, bus_stops, REF_ROADSTOPS, 6, SL_MAX_VERSION), + SLE_CONDREF(Station, truck_stops, REF_ROADSTOPS, 6, SL_MAX_VERSION), + + /* Used by newstations for graphic variations */ + SLE_CONDVAR(Station, random_bits, SLE_UINT16, 27, SL_MAX_VERSION), + SLE_CONDVAR(Station, waiting_triggers, SLE_UINT8, 27, SL_MAX_VERSION), + SLE_CONDVAR(Station, num_specs, SLE_UINT8, 27, SL_MAX_VERSION), + + SLE_CONDLST(Station, loading_vehicles, REF_VEHICLE, 57, SL_MAX_VERSION), + + /* reserve extra space in savegame here. (currently 32 bytes) */ + SLE_CONDNULL(32, 2, SL_MAX_VERSION), + + SLE_END() +}; + +static uint16 _waiting_acceptance; +static uint16 _cargo_source; +static uint32 _cargo_source_xy; +static uint16 _cargo_days; +static Money _cargo_feeder_share; + +static const SaveLoad _station_speclist_desc[] = { + SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, 27, SL_MAX_VERSION), + SLE_CONDVAR(StationSpecList, localidx, SLE_UINT8, 27, SL_MAX_VERSION), + + SLE_END() +}; + + +void SaveLoad_STNS(Station *st) +{ + static const SaveLoad _goods_desc[] = { + SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, 0, 67), + SLE_CONDVAR(GoodsEntry, acceptance_pickup, SLE_UINT8, 68, SL_MAX_VERSION), + SLE_CONDNULL(2, 51, 67), + SLE_VAR(GoodsEntry, days_since_pickup, SLE_UINT8), + SLE_VAR(GoodsEntry, rating, SLE_UINT8), + SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), + SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67), + SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67), + SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67), + SLE_VAR(GoodsEntry, last_speed, SLE_UINT8), + SLE_VAR(GoodsEntry, last_age, SLE_UINT8), + SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, 14, 64), + SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67), + SLE_CONDLST(GoodsEntry, cargo.packets, REF_CARGO_PACKET, 68, SL_MAX_VERSION), + + SLE_END() + }; + + + SlObject(st, _station_desc); + + _waiting_acceptance = 0; + + uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO; + for (CargoID i = 0; i < num_cargo; i++) { + GoodsEntry *ge = &st->goods[i]; + SlObject(ge, _goods_desc); + if (CheckSavegameVersion(68)) { + SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); + if (GB(_waiting_acceptance, 0, 12) != 0) { + /* Don't construct the packet with station here, because that'll fail with old savegames */ + CargoPacket *cp = new CargoPacket(); + /* In old versions, enroute_from used 0xFF as INVALID_STATION */ + cp->source = (CheckSavegameVersion(7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source; + cp->count = GB(_waiting_acceptance, 0, 12); + cp->days_in_transit = _cargo_days; + cp->feeder_share = _cargo_feeder_share; + cp->source_xy = _cargo_source_xy; + cp->days_in_transit = _cargo_days; + cp->feeder_share = _cargo_feeder_share; + SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, 1); + ge->cargo.Append(cp); + } + } + } + + if (st->num_specs != 0) { + /* Allocate speclist memory when loading a game */ + if (st->speclist == NULL) st->speclist = CallocT(st->num_specs); + for (uint i = 0; i < st->num_specs; i++) { + SlObject(&st->speclist[i], _station_speclist_desc); + } + } +} + +static void Save_STNS() +{ + Station *st; + /* Write the stations */ + FOR_ALL_STATIONS(st) { + SlSetArrayIndex(st->index); + SlAutolength((AutolengthProc*)SaveLoad_STNS, st); + } +} + +static void Load_STNS() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Station *st = new (index) Station(); + + SaveLoad_STNS(st); + } + + /* This is to ensure all pointers are within the limits of _stations_size */ + if (_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0; +} + +static void Save_ROADSTOP() +{ + RoadStop *rs; + + FOR_ALL_ROADSTOPS(rs) { + SlSetArrayIndex(rs->index); + SlObject(rs, _roadstop_desc); + } +} + +static void Load_ROADSTOP() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + RoadStop *rs = new (index) RoadStop(INVALID_TILE); + + SlObject(rs, _roadstop_desc); + } +} + +extern const ChunkHandler _station_chunk_handlers[] = { + { 'STNS', Save_STNS, Load_STNS, CH_ARRAY }, + { 'ROAD', Save_ROADSTOP, Load_ROADSTOP, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/strings_sl.cpp b/src/saveload/strings_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/strings_sl.cpp @@ -0,0 +1,126 @@ +/* $Id$ */ + +/** @file strings_sl.cpp Code handling saving and loading of strings */ + +#include "../stdafx.h" +#include "../strings_type.h" +#include "../core/math_func.hpp" +#include "../core/bitmath_func.hpp" +#include "../core/alloc_func.hpp" +#include "../string_func.h" + +#include "table/strings.h" + +#include "saveload.h" + +/** + * Remap a string ID from the old format to the new format + * @param s StringID that requires remapping + * @return translated ID + */ +StringID RemapOldStringID(StringID s) +{ + switch (s) { + case 0x0006: return STR_SV_EMPTY; + case 0x7000: return STR_SV_UNNAMED; + case 0x70E4: return SPECSTR_PLAYERNAME_ENGLISH; + case 0x70E9: return SPECSTR_PLAYERNAME_ENGLISH; + case 0x8864: return STR_SV_TRAIN_NAME; + case 0x902B: return STR_SV_ROADVEH_NAME; + case 0x9830: return STR_SV_SHIP_NAME; + case 0xA02F: return STR_SV_AIRCRAFT_NAME; + + default: + if (IsInsideMM(s, 0x300F, 0x3030)) { + return s - 0x300F + STR_SV_STNAME; + } else { + return s; + } + } +} + +/** Location to load the old names to. */ +char *_old_name_array = NULL; + +/** + * Copy and convert old custom names to UTF-8. + * They were all stored in a 512 by 32 long string array and are + * now stored with stations, waypoints and other places with names. + * @param id the StringID of the custom name to clone. + * @return the clones custom name. + */ +char *CopyFromOldName(StringID id) +{ + /* Is this name an (old) custom name? */ + if (GB(id, 11, 5) != 15) return NULL; + + if (CheckSavegameVersion(37)) { + /* Old names were 32 characters long, so 128 characters should be + * plenty to allow for expansion when converted to UTF-8. */ + char tmp[128]; + const char *strfrom = &_old_name_array[32 * GB(id, 0, 9)]; + char *strto = tmp; + + for (; *strfrom != '\0'; strfrom++) { + WChar c = (byte)*strfrom; + + /* Map from non-ISO8859-15 characters to UTF-8. */ + switch (c) { + case 0xA4: c = 0x20AC; break; // Euro + case 0xA6: c = 0x0160; break; // S with caron + case 0xA8: c = 0x0161; break; // s with caron + case 0xB4: c = 0x017D; break; // Z with caron + case 0xB8: c = 0x017E; break; // z with caron + case 0xBC: c = 0x0152; break; // OE ligature + case 0xBD: c = 0x0153; break; // oe ligature + case 0xBE: c = 0x0178; break; // Y with diaresis + default: break; + } + + /* Check character will fit into our buffer. */ + if (strto + Utf8CharLen(c) > lastof(tmp)) break; + + strto += Utf8Encode(strto, c); + } + + /* Terminate the new string and copy it back to the name array */ + *strto = '\0'; + + return strdup(tmp); + } else { + /* Name will already be in UTF-8. */ + return strdup(&_old_name_array[32 * GB(id, 0, 9)]); + } +} + +/** + * Free the memory of the old names array. + * Should be called once the old names have all been converted. + */ +void ResetOldNames() +{ + free(_old_name_array); + _old_name_array = NULL; +} + +/** + * Initialize the old names table memory. + */ +void InitializeOldNames() +{ + free(_old_name_array); + _old_name_array = CallocT(512 * 32); +} + +static void Load_NAME() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + SlArray(&_old_name_array[32 * index], SlGetFieldLength(), SLE_UINT8); + } +} + +extern const ChunkHandler _name_chunk_handlers[] = { + { 'NAME', NULL, Load_NAME, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/subsidy_sl.cpp b/src/saveload/subsidy_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/subsidy_sl.cpp @@ -0,0 +1,43 @@ +/* $Id$ */ + +/** @file subsidy_sl.cpp Code handling saving and loading of subsidies */ + +#include "../stdafx.h" +#include "../economy_func.h" + +#include "saveload.h" + +static const SaveLoad _subsidies_desc[] = { + SLE_VAR(Subsidy, cargo_type, SLE_UINT8), + SLE_VAR(Subsidy, age, SLE_UINT8), + SLE_CONDVAR(Subsidy, from, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), + SLE_CONDVAR(Subsidy, from, SLE_UINT16, 5, SL_MAX_VERSION), + SLE_CONDVAR(Subsidy, to, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), + SLE_CONDVAR(Subsidy, to, SLE_UINT16, 5, SL_MAX_VERSION), + SLE_END() +}; + +void Save_SUBS() +{ + int i; + Subsidy *s; + + for (i = 0; i != lengthof(_subsidies); i++) { + s = &_subsidies[i]; + if (s->cargo_type != CT_INVALID) { + SlSetArrayIndex(i); + SlObject(s, _subsidies_desc); + } + } +} + +void Load_SUBS() +{ + int index; + while ((index = SlIterateArray()) != -1) + SlObject(&_subsidies[index], _subsidies_desc); +} + +extern const ChunkHandler _subsidy_chunk_handlers[] = { + { 'SUBS', Save_SUBS, Load_SUBS, CH_ARRAY}, +}; diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/town_sl.cpp @@ -0,0 +1,210 @@ +/* $Id$ */ + +/** @file town_sl.cpp Code handling saving and loading of towns and houses */ + +#include "../stdafx.h" +#include "../town.h" +#include "../newgrf_house.h" +#include "../newgrf_commons.h" +#include "../variables.h" +#include "../tile_map.h" +#include "../town_map.h" + +#include "saveload.h" + +extern uint _total_towns; + +/** + * Check and update town and house values. + * + * Checked are the HouseIDs. Updated are the + * town population the number of houses per + * town, the town radius and the max passengers + * of the town. + */ +void UpdateHousesAndTowns() +{ + Town *town; + InitializeBuildingCounts(); + + /* Reset town population and num_houses */ + FOR_ALL_TOWNS(town) { + town->population = 0; + town->num_houses = 0; + } + + for (TileIndex t = 0; t < MapSize(); t++) { + HouseID house_id; + + if (!IsTileType(t, MP_HOUSE)) continue; + + house_id = GetHouseType(t); + if (!GetHouseSpecs(house_id)->enabled && house_id >= NEW_HOUSE_OFFSET) { + /* The specs for this type of house are not available any more, so + * replace it with the substitute original house type. */ + house_id = _house_mngr.GetSubstituteID(house_id); + SetHouseType(t, house_id); + } + + town = GetTownByTile(t); + IncreaseBuildingCount(town, house_id); + if (IsHouseCompleted(t)) town->population += GetHouseSpecs(house_id)->population; + + /* Increase the number of houses for every house, but only once. */ + if (GetHouseNorthPart(house_id) == 0) town->num_houses++; + } + + /* Update the population and num_house dependant values */ + FOR_ALL_TOWNS(town) { + UpdateTownRadius(town); + UpdateTownMaxPass(town); + } +} + +/** Save and load of towns. */ +static const SaveLoad _town_desc[] = { + SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Town, xy, SLE_UINT32, 6, SL_MAX_VERSION), + + SLE_CONDNULL(2, 0, 2), ///< population, no longer in use + SLE_CONDNULL(4, 3, 84), ///< population, no longer in use + SLE_CONDNULL(2, 0, 91), ///< num_houses, no longer in use + + SLE_CONDVAR(Town, townnamegrfid, SLE_UINT32, 66, SL_MAX_VERSION), + SLE_VAR(Town, townnametype, SLE_UINT16), + SLE_VAR(Town, townnameparts, SLE_UINT32), + SLE_CONDSTR(Town, name, SLE_STR, 0, 84, SL_MAX_VERSION), + + SLE_VAR(Town, flags12, SLE_UINT8), + SLE_CONDVAR(Town, statues, SLE_FILE_U8 | SLE_VAR_U16, 0, 103), + SLE_CONDVAR(Town, statues, SLE_UINT16, 104, SL_MAX_VERSION), + + SLE_CONDNULL(1, 0, 1), ///< sort_index, no longer in use + + SLE_CONDVAR(Town, have_ratings, SLE_FILE_U8 | SLE_VAR_U16, 0, 103), + SLE_CONDVAR(Town, have_ratings, SLE_UINT16, 104, SL_MAX_VERSION), + SLE_CONDARR(Town, ratings, SLE_INT16, 8, 0, 103), + SLE_CONDARR(Town, ratings, SLE_INT16, MAX_COMPANIES, 104, SL_MAX_VERSION), + /* failed bribe attempts are stored since savegame format 4 */ + SLE_CONDARR(Town, unwanted, SLE_INT8, 8, 4, 103), + SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, 104, SL_MAX_VERSION), + + SLE_CONDVAR(Town, max_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), + SLE_CONDVAR(Town, max_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), + SLE_CONDVAR(Town, new_max_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), + SLE_CONDVAR(Town, new_max_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), + SLE_CONDVAR(Town, act_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), + SLE_CONDVAR(Town, act_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), + SLE_CONDVAR(Town, new_act_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), + SLE_CONDVAR(Town, new_act_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), + + SLE_CONDVAR(Town, max_pass, SLE_UINT32, 9, SL_MAX_VERSION), + SLE_CONDVAR(Town, max_mail, SLE_UINT32, 9, SL_MAX_VERSION), + SLE_CONDVAR(Town, new_max_pass, SLE_UINT32, 9, SL_MAX_VERSION), + SLE_CONDVAR(Town, new_max_mail, SLE_UINT32, 9, SL_MAX_VERSION), + SLE_CONDVAR(Town, act_pass, SLE_UINT32, 9, SL_MAX_VERSION), + SLE_CONDVAR(Town, act_mail, SLE_UINT32, 9, SL_MAX_VERSION), + SLE_CONDVAR(Town, new_act_pass, SLE_UINT32, 9, SL_MAX_VERSION), + SLE_CONDVAR(Town, new_act_mail, SLE_UINT32, 9, SL_MAX_VERSION), + + SLE_VAR(Town, pct_pass_transported, SLE_UINT8), + SLE_VAR(Town, pct_mail_transported, SLE_UINT8), + + SLE_VAR(Town, act_food, SLE_UINT16), + SLE_VAR(Town, act_water, SLE_UINT16), + SLE_VAR(Town, new_act_food, SLE_UINT16), + SLE_VAR(Town, new_act_water, SLE_UINT16), + + SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT8, 0, 53), + SLE_CONDVAR(Town, grow_counter, SLE_UINT8, 0, 53), + SLE_CONDVAR(Town, growth_rate, SLE_UINT8, 0, 53), + + SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT16, 54, SL_MAX_VERSION), + SLE_CONDVAR(Town, grow_counter, SLE_UINT16, 54, SL_MAX_VERSION), + SLE_CONDVAR(Town, growth_rate, SLE_INT16, 54, SL_MAX_VERSION), + + SLE_VAR(Town, fund_buildings_months, SLE_UINT8), + SLE_VAR(Town, road_build_months, SLE_UINT8), + + SLE_CONDVAR(Town, exclusivity, SLE_UINT8, 2, SL_MAX_VERSION), + SLE_CONDVAR(Town, exclusive_counter, SLE_UINT8, 2, SL_MAX_VERSION), + + SLE_CONDVAR(Town, larger_town, SLE_BOOL, 56, SL_MAX_VERSION), + + /* reserve extra space in savegame here. (currently 30 bytes) */ + SLE_CONDNULL(30, 2, SL_MAX_VERSION), + + SLE_END() +}; + +/* Save and load the mapping between the house id on the map, and the grf file + * it came from. */ +static const SaveLoad _house_id_mapping_desc[] = { + SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), + SLE_VAR(EntityIDMapping, entity_id, SLE_UINT8), + SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT8), + SLE_END() +}; + +static void Save_HOUSEIDS() +{ + uint j = _house_mngr.GetMaxMapping(); + + for (uint i = 0; i < j; i++) { + SlSetArrayIndex(i); + SlObject(&_house_mngr.mapping_ID[i], _house_id_mapping_desc); + } +} + +static void Load_HOUSEIDS() +{ + int index; + + _house_mngr.ResetMapping(); + uint max_id = _house_mngr.GetMaxMapping(); + + while ((index = SlIterateArray()) != -1) { + if ((uint)index >= max_id) break; + SlObject(&_house_mngr.mapping_ID[index], _house_id_mapping_desc); + } +} + +static void Save_TOWN() +{ + Town *t; + + FOR_ALL_TOWNS(t) { + SlSetArrayIndex(t->index); + SlObject(t, _town_desc); + } +} + +static void Load_TOWN() +{ + int index; + + _total_towns = 0; + + while ((index = SlIterateArray()) != -1) { + Town *t = new (index) Town(); + SlObject(t, _town_desc); + + _total_towns++; + } + + /* This is to ensure all pointers are within the limits of + * the size of the TownPool */ + if (_cur_town_ctr > GetMaxTownIndex()) + _cur_town_ctr = 0; +} + +void AfterLoadTown() +{ + Town *t; + FOR_ALL_TOWNS(t) t->InitializeLayout(); +} + +extern const ChunkHandler _town_chunk_handlers[] = { + { 'HIDS', Save_HOUSEIDS, Load_HOUSEIDS, CH_ARRAY }, + { 'CITY', Save_TOWN, Load_TOWN, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/vehicle_sl.cpp @@ -0,0 +1,670 @@ +/* $Id$ */ + +/** @file vehicle_sl.cpp Code handling saving and loading of vehicles */ + +#include "../stdafx.h" +#include "../vehicle_base.h" +#include "../vehicle_func.h" +#include "../train.h" +#include "../roadveh.h" +#include "../ship.h" +#include "../aircraft.h" +#include "../effectvehicle_base.h" + +#include "saveload.h" + +#include + +/* + * Link front and rear multiheaded engines to each other + * This is done when loading a savegame + */ +void ConnectMultiheadedTrains() +{ + Vehicle *v; + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN) { + v->u.rail.other_multiheaded_part = NULL; + } + } + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) { + /* Two ways to associate multiheaded parts to each other: + * sequential-matching: Trains shall be arranged to look like <..>..<..>..<..>.. + * bracket-matching: Free vehicle chains shall be arranged to look like ..<..<..>..<..>..>.. + * + * Note: Old savegames might contain chains which do not comply with these rules, e.g. + * - the front and read parts have invalid orders + * - different engine types might be combined + * - there might be different amounts of front and rear parts. + * + * Note: The multiheaded parts need to be matched exactly like they are matched on the server, else desyncs will occur. + * This is why two matching strategies are needed. + */ + + bool sequential_matching = IsFrontEngine(v); + + for (Vehicle *u = v; u != NULL; u = GetNextVehicle(u)) { + if (u->u.rail.other_multiheaded_part != NULL) continue; // we already linked this one + + if (IsMultiheaded(u)) { + if (!IsTrainEngine(u)) { + /* we got a rear car without a front car. We will convert it to a front one */ + SetTrainEngine(u); + u->spritenum--; + } + + /* Find a matching back part */ + EngineID eid = u->engine_type; + Vehicle *w; + if (sequential_matching) { + for (w = GetNextVehicle(u); w != NULL; w = GetNextVehicle(w)) { + if (w->engine_type != eid || w->u.rail.other_multiheaded_part != NULL || !IsMultiheaded(w)) continue; + + /* we found a car to partner with this engine. Now we will make sure it face the right way */ + if (IsTrainEngine(w)) { + ClearTrainEngine(w); + w->spritenum++; + } + break; + } + } else { + uint stack_pos = 0; + for (w = GetNextVehicle(u); w != NULL; w = GetNextVehicle(w)) { + if (w->engine_type != eid || w->u.rail.other_multiheaded_part != NULL || !IsMultiheaded(w)) continue; + + if (IsTrainEngine(w)) { + stack_pos++; + } else { + if (stack_pos == 0) break; + stack_pos--; + } + } + } + + if (w != NULL) { + w->u.rail.other_multiheaded_part = u; + u->u.rail.other_multiheaded_part = w; + } else { + /* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */ + ClearMultiheaded(u); + } + } + } + } + } +} + +/** + * Converts all trains to the new subtype format introduced in savegame 16.2 + * It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found + */ +void ConvertOldMultiheadToNew() +{ + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN) { + SetBit(v->subtype, 7); // indicates that it's the old format and needs to be converted in the next loop + } + } + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN) { + if (HasBit(v->subtype, 7) && ((v->subtype & ~0x80) == 0 || (v->subtype & ~0x80) == 4)) { + for (Vehicle *u = v; u != NULL; u = u->Next()) { + const RailVehicleInfo *rvi = RailVehInfo(u->engine_type); + + ClrBit(u->subtype, 7); + switch (u->subtype) { + case 0: /* TS_Front_Engine */ + if (rvi->railveh_type == RAILVEH_MULTIHEAD) SetMultiheaded(u); + SetFrontEngine(u); + SetTrainEngine(u); + break; + + case 1: /* TS_Artic_Part */ + u->subtype = 0; + SetArticulatedPart(u); + break; + + case 2: /* TS_Not_First */ + u->subtype = 0; + if (rvi->railveh_type == RAILVEH_WAGON) { + // normal wagon + SetTrainWagon(u); + break; + } + if (rvi->railveh_type == RAILVEH_MULTIHEAD && rvi->image_index == u->spritenum - 1) { + // rear end of a multiheaded engine + SetMultiheaded(u); + break; + } + if (rvi->railveh_type == RAILVEH_MULTIHEAD) SetMultiheaded(u); + SetTrainEngine(u); + break; + + case 4: /* TS_Free_Car */ + u->subtype = 0; + SetTrainWagon(u); + SetFreeWagon(u); + break; + default: NOT_REACHED(); break; + } + } + } + } + } +} + + +/** need to be called to load aircraft from old version */ +void UpdateOldAircraft() +{ + /* set airport_flags to 0 for all airports just to be sure */ + Station *st; + FOR_ALL_STATIONS(st) { + st->airport_flags = 0; // reset airport + } + + Vehicle *v_oldstyle; + FOR_ALL_VEHICLES(v_oldstyle) { + /* airplane has another vehicle with subtype 4 (shadow), helicopter also has 3 (rotor) + * skip those */ + if (v_oldstyle->type == VEH_AIRCRAFT && IsNormalAircraft(v_oldstyle)) { + /* airplane in terminal stopped doesn't hurt anyone, so goto next */ + if (v_oldstyle->vehstatus & VS_STOPPED && v_oldstyle->u.air.state == 0) { + v_oldstyle->u.air.state = HANGAR; + continue; + } + + AircraftLeaveHangar(v_oldstyle); // make airplane visible if it was in a depot for example + v_oldstyle->vehstatus &= ~VS_STOPPED; // make airplane moving + v_oldstyle->u.air.state = FLYING; + AircraftNextAirportPos_and_Order(v_oldstyle); // move it to the entry point of the airport + GetNewVehiclePosResult gp = GetNewVehiclePos(v_oldstyle); + v_oldstyle->tile = 0; // aircraft in air is tile=0 + + /* correct speed of helicopter-rotors */ + if (v_oldstyle->subtype == AIR_HELICOPTER) v_oldstyle->Next()->Next()->cur_speed = 32; + + /* set new position x,y,z */ + SetAircraftPosition(v_oldstyle, gp.x, gp.y, GetAircraftFlyingAltitude(v_oldstyle)); + } + } +} + +/** Called after load to update coordinates */ +void AfterLoadVehicles(bool part_of_load) +{ + Vehicle *v; + + FOR_ALL_VEHICLES(v) { + /* Reinstate the previous pointer */ + if (v->Next() != NULL) v->Next()->previous = v; + if (v->NextShared() != NULL) v->NextShared()->previous_shared = v; + + v->UpdateDeltaXY(v->direction); + + if (part_of_load) v->fill_percent_te_id = INVALID_TE_ID; + v->first = NULL; + if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE; + if (v->type == VEH_ROAD) v->u.road.first_engine = INVALID_ENGINE; + + v->cargo.InvalidateCache(); + } + + /* AfterLoadVehicles may also be called in case of NewGRF reload, in this + * case we may not convert orders again. */ + if (part_of_load) { + /* Create shared vehicle chain for very old games (pre 5,2) and create + * OrderList from shared vehicle chains. For this to work correctly, the + * following conditions must be fulfilled: + * a) both next_shared and previous_shared are not set for pre 5,2 games + * b) both next_shared and previous_shared are set for later games + */ + std::map mapping; + + FOR_ALL_VEHICLES(v) { + if (v->orders.old != NULL) { + if (CheckSavegameVersion(105)) { // Pre-105 didn't save an OrderList + if (mapping[v->orders.old] == NULL) { + /* This adds the whole shared vehicle chain for case b */ + v->orders.list = mapping[v->orders.old] = new OrderList(v->orders.old, v); + } else { + v->orders.list = mapping[v->orders.old]; + /* For old games (case a) we must create the shared vehicle chain */ + if (CheckSavegameVersionOldStyle(5, 2)) { + v->AddToShared(v->orders.list->GetFirstSharedVehicle()); + } + } + } else { // OrderList was saved as such, only recalculate not saved values + if (v->PreviousShared() == NULL) { + new (v->orders.list) OrderList(v->orders.list->GetFirstOrder(), v); + } + } + } + } + } + + FOR_ALL_VEHICLES(v) { + /* Fill the first pointers */ + if (v->Previous() == NULL) { + for (Vehicle *u = v; u != NULL; u = u->Next()) { + u->first = v; + } + } + } + + FOR_ALL_VEHICLES(v) { + assert(v->first != NULL); + + if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) { + if (IsFrontEngine(v)) v->u.rail.last_speed = v->cur_speed; // update displayed train speed + TrainConsistChanged(v, false); + } else if (v->type == VEH_ROAD && IsRoadVehFront(v)) { + RoadVehUpdateCache(v); + } + } + + /* Stop non-front engines */ + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN && IsTrainEngine(v) && !IsFrontEngine(v)) v->vehstatus |= VS_STOPPED; + } + + FOR_ALL_VEHICLES(v) { + switch (v->type) { + case VEH_ROAD: + v->u.road.roadtype = HasBit(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; + v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype); + /* FALL THROUGH */ + case VEH_TRAIN: + case VEH_SHIP: + v->cur_image = v->GetImage(v->direction); + break; + + case VEH_AIRCRAFT: + if (IsNormalAircraft(v)) { + v->cur_image = v->GetImage(v->direction); + + /* The plane's shadow will have the same image as the plane */ + Vehicle *shadow = v->Next(); + shadow->cur_image = v->cur_image; + + /* In the case of a helicopter we will update the rotor sprites */ + if (v->subtype == AIR_HELICOPTER) { + Vehicle *rotor = shadow->Next(); + rotor->cur_image = GetRotorImage(v); + } + + UpdateAircraftCache(v); + } + break; + default: break; + } + + v->left_coord = INVALID_COORD; + VehiclePositionChanged(v); + } +} + +static uint8 _cargo_days; +static uint16 _cargo_source; +static uint32 _cargo_source_xy; +static uint16 _cargo_count; +static uint16 _cargo_paid_for; +static Money _cargo_feeder_share; +static uint32 _cargo_loaded_at_xy; + +/** + * Make it possible to make the saveload tables "friends" of other classes. + * @param vt the vehicle type. Can be VEH_END for the common vehicle description data + * @return the saveload description + */ +const SaveLoad *GetVehicleDescription(VehicleType vt) +{ + /** Save and load of vehicles */ + static const SaveLoad _common_veh_desc[] = { + SLE_VAR(Vehicle, subtype, SLE_UINT8), + + SLE_REF(Vehicle, next, REF_VEHICLE_OLD), + SLE_CONDVAR(Vehicle, name, SLE_NAME, 0, 83), + SLE_CONDSTR(Vehicle, name, SLE_STR, 0, 84, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, 0, 7), + SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, 8, SL_MAX_VERSION), + SLE_VAR(Vehicle, owner, SLE_UINT8), + SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_VAR(Vehicle, z_pos, SLE_UINT8), + SLE_VAR(Vehicle, direction, SLE_UINT8), + + SLE_CONDNULL(2, 0, 57), + SLE_VAR(Vehicle, spritenum, SLE_UINT8), + SLE_CONDNULL(5, 0, 57), + SLE_VAR(Vehicle, engine_type, SLE_UINT16), + + SLE_VAR(Vehicle, max_speed, SLE_UINT16), + SLE_VAR(Vehicle, cur_speed, SLE_UINT16), + SLE_VAR(Vehicle, subspeed, SLE_UINT8), + SLE_VAR(Vehicle, acceleration, SLE_UINT8), + SLE_VAR(Vehicle, progress, SLE_UINT8), + + SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), + SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, 5, SL_MAX_VERSION), + + SLE_VAR(Vehicle, cargo_type, SLE_UINT8), + SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, 35, SL_MAX_VERSION), + SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67), + SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), + SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67), + SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67), + SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), + SLEG_CONDVAR( _cargo_count, SLE_UINT16, 0, 67), + SLE_CONDLST(Vehicle, cargo, REF_CARGO_PACKET, 68, SL_MAX_VERSION), + + SLE_VAR(Vehicle, day_counter, SLE_UINT8), + SLE_VAR(Vehicle, tick_counter, SLE_UINT8), + SLE_CONDVAR(Vehicle, running_ticks, SLE_UINT8, 88, SL_MAX_VERSION), + + SLE_VAR(Vehicle, cur_order_index, SLE_UINT8), + /* num_orders is now part of OrderList and is not saved but counted */ + SLE_CONDNULL(1, 0, 104), + + /* This next line is for version 4 and prior compatibility.. it temporarily reads + type and flags (which were both 4 bits) into type. Later on this is + converted correctly */ + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8, 0, 4), + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), + + /* Orders for version 5 and on */ + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8, 5, SL_MAX_VERSION), + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, flags), SLE_UINT8, 5, SL_MAX_VERSION), + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION), + + /* Refit in current order */ + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_cargo), SLE_UINT8, 36, SL_MAX_VERSION), + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_subtype), SLE_UINT8, 36, SL_MAX_VERSION), + + /* Timetable in current order */ + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, wait_time), SLE_UINT16, 67, SL_MAX_VERSION), + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, travel_time), SLE_UINT16, 67, SL_MAX_VERSION), + + SLE_CONDREF(Vehicle, orders, REF_ORDER, 0, 104), + SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, 105, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Vehicle, max_age, SLE_INT32, 31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, 31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Vehicle, service_interval, SLE_INT32, 31, SL_MAX_VERSION), + SLE_VAR(Vehicle, reliability, SLE_UINT16), + SLE_VAR(Vehicle, reliability_spd_dec, SLE_UINT16), + SLE_VAR(Vehicle, breakdown_ctr, SLE_UINT8), + SLE_VAR(Vehicle, breakdown_delay, SLE_UINT8), + SLE_VAR(Vehicle, breakdowns_since_last_service, SLE_UINT8), + SLE_VAR(Vehicle, breakdown_chance, SLE_UINT8), + SLE_CONDVAR(Vehicle, build_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Vehicle, build_year, SLE_INT32, 31, SL_MAX_VERSION), + + SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16), + SLEG_CONDVAR( _cargo_paid_for, SLE_UINT16, 45, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, vehicle_flags, SLE_UINT8, 40, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), + SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, 65, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), + SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, 65, SL_MAX_VERSION), + SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64, 51, 64), + SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67), + SLEG_CONDVAR( _cargo_loaded_at_xy, SLE_UINT32, 51, 67), + SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), + SLE_CONDVAR(Vehicle, value, SLE_INT64, 65, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, random_bits, SLE_UINT8, 2, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, 2, SL_MAX_VERSION), + + SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, 2, SL_MAX_VERSION), + SLE_CONDNULL(2, 2, 68), + SLE_CONDNULL(4, 69, 100), + + SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, 60, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, current_order_time, SLE_UINT32, 67, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, lateness_counter, SLE_INT32, 67, SL_MAX_VERSION), + + /* reserve extra space in savegame here. (currently 10 bytes) */ + SLE_CONDNULL(10, 2, SL_MAX_VERSION), + + SLE_END() + }; + + + static const SaveLoad _train_desc[] = { + SLE_WRITEBYTE(Vehicle, type, VEH_TRAIN), + SLE_VEH_INCLUDEX(), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, crash_anim_pos), SLE_UINT16), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, force_proceed), SLE_UINT8), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, railtype), SLE_UINT8), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, track), SLE_UINT8), + + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_FILE_U8 | SLE_VAR_U16, 2, 99), + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_UINT16, 100, SL_MAX_VERSION), + SLE_CONDNULL(2, 2, 59), + + SLE_CONDNULL(2, 2, 19), + /* reserve extra space in savegame here. (currently 11 bytes) */ + SLE_CONDNULL(11, 2, SL_MAX_VERSION), + + SLE_END() + }; + + static const SaveLoad _roadveh_desc[] = { + SLE_WRITEBYTE(Vehicle, type, VEH_ROAD), + SLE_VEH_INCLUDEX(), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, state), SLE_UINT8), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, frame), SLE_UINT8), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, blocked_ctr), SLE_UINT16), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking), SLE_UINT8), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking_ctr), SLE_UINT8), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, crashed_ctr), SLE_UINT16), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, reverse_ctr), SLE_UINT8), + + SLE_CONDREFX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot), REF_ROADSTOPS, 6, SL_MAX_VERSION), + SLE_CONDNULL(1, 6, SL_MAX_VERSION), + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot_age), SLE_UINT8, 6, SL_MAX_VERSION), + /* reserve extra space in savegame here. (currently 16 bytes) */ + SLE_CONDNULL(16, 2, SL_MAX_VERSION), + + SLE_END() + }; + + static const SaveLoad _ship_desc[] = { + SLE_WRITEBYTE(Vehicle, type, VEH_SHIP), + SLE_VEH_INCLUDEX(), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleShip, state), SLE_UINT8), + + /* reserve extra space in savegame here. (currently 16 bytes) */ + SLE_CONDNULL(16, 2, SL_MAX_VERSION), + + SLE_END() + }; + + static const SaveLoad _aircraft_desc[] = { + SLE_WRITEBYTE(Vehicle, type, VEH_AIRCRAFT), + SLE_VEH_INCLUDEX(), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, crashed_counter), SLE_UINT16), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, pos), SLE_UINT8), + + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport), SLE_UINT16, 5, SL_MAX_VERSION), + + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, state), SLE_UINT8), + + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, previous_pos), SLE_UINT8, 2, SL_MAX_VERSION), + + /* reserve extra space in savegame here. (currently 15 bytes) */ + SLE_CONDNULL(15, 2, SL_MAX_VERSION), + + SLE_END() + }; + + static const SaveLoad _special_desc[] = { + SLE_WRITEBYTE(Vehicle, type, VEH_EFFECT), + + SLE_VAR(Vehicle, subtype, SLE_UINT8), + + SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), + SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), + SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION), + SLE_VAR(Vehicle, z_pos, SLE_UINT8), + + SLE_VAR(Vehicle, cur_image, SLE_UINT16), + SLE_CONDNULL(5, 0, 57), + SLE_VAR(Vehicle, progress, SLE_UINT8), + SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleEffect, animation_state), SLE_UINT16), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleEffect, animation_substate), SLE_UINT8), + + SLE_CONDVAR(Vehicle, spritenum, SLE_UINT8, 2, SL_MAX_VERSION), + + /* reserve extra space in savegame here. (currently 15 bytes) */ + SLE_CONDNULL(15, 2, SL_MAX_VERSION), + + SLE_END() + }; + + static const SaveLoad _disaster_desc[] = { + SLE_WRITEBYTE(Vehicle, type, VEH_DISASTER), + + SLE_REF(Vehicle, next, REF_VEHICLE_OLD), + + SLE_VAR(Vehicle, subtype, SLE_UINT8), + SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), + SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), + SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION), + SLE_VAR(Vehicle, z_pos, SLE_UINT8), + SLE_VAR(Vehicle, direction, SLE_UINT8), + + SLE_CONDNULL(5, 0, 57), + SLE_VAR(Vehicle, owner, SLE_UINT8), + SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), + SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION), + + SLE_VAR(Vehicle, cur_image, SLE_UINT16), + SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), + SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION), + SLE_VAR(Vehicle, tick_counter, SLE_UINT8), + + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, image_override), SLE_UINT16), + SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, big_ufo_destroyer_target), SLE_UINT16), + + /* reserve extra space in savegame here. (currently 16 bytes) */ + SLE_CONDNULL(16, 2, SL_MAX_VERSION), + + SLE_END() + }; + + + static const SaveLoad *_veh_descs[] = { + _train_desc, + _roadveh_desc, + _ship_desc, + _aircraft_desc, + _special_desc, + _disaster_desc, + _common_veh_desc, + }; + + return _veh_descs[vt]; +} + +/** Will be called when the vehicles need to be saved. */ +static void Save_VEHS() +{ + Vehicle *v; + /* Write the vehicles */ + FOR_ALL_VEHICLES(v) { + SlSetArrayIndex(v->index); + SlObject(v, GetVehicleDescription(v->type)); + } +} + +/** Will be called when vehicles need to be loaded. */ +void Load_VEHS() +{ + int index; + + _cargo_count = 0; + + while ((index = SlIterateArray()) != -1) { + Vehicle *v; + VehicleType vtype = (VehicleType)SlReadByte(); + + switch (vtype) { + case VEH_TRAIN: v = new (index) Train(); break; + case VEH_ROAD: v = new (index) RoadVehicle(); break; + case VEH_SHIP: v = new (index) Ship(); break; + case VEH_AIRCRAFT: v = new (index) Aircraft(); break; + case VEH_EFFECT: v = new (index) EffectVehicle(); break; + case VEH_DISASTER: v = new (index) DisasterVehicle(); break; + case VEH_INVALID: v = new (index) InvalidVehicle(); break; + default: NOT_REACHED(); + } + + SlObject(v, GetVehicleDescription(vtype)); + + if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v)) { + /* Don't construct the packet with station here, because that'll fail with old savegames */ + CargoPacket *cp = new CargoPacket(); + cp->source = _cargo_source; + cp->source_xy = _cargo_source_xy; + cp->count = _cargo_count; + cp->days_in_transit = _cargo_days; + cp->feeder_share = _cargo_feeder_share; + cp->loaded_at_xy = _cargo_loaded_at_xy; + v->cargo.Append(cp); + } + + /* Old savegames used 'last_station_visited = 0xFF' */ + if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF) + v->last_station_visited = INVALID_STATION; + + if (CheckSavegameVersion(5)) { + /* Convert the current_order.type (which is a mix of type and flags, because + * in those versions, they both were 4 bits big) to type and flags */ + v->current_order.flags = GB(v->current_order.type, 4, 4); + v->current_order.type &= 0x0F; + } + + /* Advanced vehicle lists got added */ + if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP; + } +} + +extern const ChunkHandler _veh_chunk_handlers[] = { + { 'VEHS', Save_VEHS, Load_VEHS, CH_SPARSE_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/waypoint_sl.cpp b/src/saveload/waypoint_sl.cpp new file mode 100644 --- /dev/null +++ b/src/saveload/waypoint_sl.cpp @@ -0,0 +1,96 @@ +/* $Id$ */ + +/** @file waypoint_sl.cpp Code handling saving and loading of waypoints */ + +#include "../stdafx.h" +#include "../waypoint.h" +#include "../newgrf_station.h" +#include "../town.h" + +#include "table/strings.h" + +#include "saveload.h" + +/** + * Update waypoint graphics id against saved GRFID/localidx. + * This is to ensure the chosen graphics are correct if GRF files are changed. + */ +void AfterLoadWaypoints() +{ + Waypoint *wp; + + FOR_ALL_WAYPOINTS(wp) { + uint i; + + if (wp->grfid == 0) continue; + + for (i = 0; i < GetNumCustomStations(STAT_CLASS_WAYP); i++) { + const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, i); + if (statspec != NULL && statspec->grffile->grfid == wp->grfid && statspec->localidx == wp->localidx) { + wp->stat_id = i; + break; + } + } + } +} + +/** + * Fix savegames which stored waypoints in their old format + */ +void FixOldWaypoints() +{ + Waypoint *wp; + + /* Convert the old 'town_or_string', to 'string' / 'town' / 'town_cn' */ + FOR_ALL_WAYPOINTS(wp) { + wp->town_index = ClosestTownFromTile(wp->xy, UINT_MAX)->index; + wp->town_cn = 0; + if (wp->string & 0xC000) { + wp->town_cn = wp->string & 0x3F; + wp->string = STR_NULL; + } + } +} + +static const SaveLoad _waypoint_desc[] = { + SLE_CONDVAR(Waypoint, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Waypoint, xy, SLE_UINT32, 6, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, town_index, SLE_UINT16, 12, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, town_cn, SLE_FILE_U8 | SLE_VAR_U16, 12, 88), + SLE_CONDVAR(Waypoint, town_cn, SLE_UINT16, 89, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, string, SLE_STRINGID, 0, 83), + SLE_CONDSTR(Waypoint, name, SLE_STR, 0, 84, SL_MAX_VERSION), + SLE_VAR(Waypoint, deleted, SLE_UINT8), + + SLE_CONDVAR(Waypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32, 3, 30), + SLE_CONDVAR(Waypoint, build_date, SLE_INT32, 31, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, localidx, SLE_UINT8, 3, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, grfid, SLE_UINT32, 17, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, owner, SLE_UINT8, 101, SL_MAX_VERSION), + + SLE_END() +}; + +static void Save_WAYP() +{ + Waypoint *wp; + + FOR_ALL_WAYPOINTS(wp) { + SlSetArrayIndex(wp->index); + SlObject(wp, _waypoint_desc); + } +} + +static void Load_WAYP() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + Waypoint *wp = new (index) Waypoint(); + SlObject(wp, _waypoint_desc); + } +} + +extern const ChunkHandler _waypoint_chunk_handlers[] = { + { 'CHKP', Save_WAYP, Load_WAYP, CH_ARRAY | CH_LAST}, +}; diff --git a/src/screenshot.cpp b/src/screenshot.cpp --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -17,7 +17,7 @@ #include "core/alloc_func.hpp" #include "core/endian_func.hpp" #include "map_func.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "company_func.h" #include "table/strings.h" diff --git a/src/settings.cpp b/src/settings.cpp --- a/src/settings.cpp +++ b/src/settings.cpp @@ -29,7 +29,6 @@ #include "settings_internal.h" #include "command_func.h" #include "console_func.h" -#include "saveload.h" #include "npf.h" #include "yapf/yapf.h" #include "newgrf.h" diff --git a/src/settings_internal.h b/src/settings_internal.h --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -5,7 +5,7 @@ #ifndef SETTINGS_INTERNAL_H #define SETTINGS_INTERNAL_H -#include "saveload.h" +#include "saveload/saveload.h" #include "settings_type.h" /** Convention/Type of settings. This is then further specified if necessary diff --git a/src/signs.cpp b/src/signs.cpp --- a/src/signs.cpp +++ b/src/signs.cpp @@ -8,7 +8,6 @@ #include "company_func.h" #include "signs_base.h" #include "signs_func.h" -#include "saveload.h" #include "command_func.h" #include "variables.h" #include "strings_func.h" @@ -56,17 +55,12 @@ static void UpdateSignVirtCoords(Sign *s UpdateViewportSignPos(&si->sign, pt.x, pt.y - 6, STR_2806); } -/** - * - * Update the coordinates of all signs - * - */ +/** Update the coordinates of all signs */ void UpdateAllSignVirtCoords() { Sign *si; FOR_ALL_SIGNS(si) UpdateSignVirtCoords(si); - } /** @@ -203,48 +197,3 @@ void InitializeSigns() _Sign_pool.CleanPool(); _Sign_pool.AddBlockToPool(); } - -static const SaveLoad _sign_desc[] = { - SLE_CONDVAR(Sign, name, SLE_NAME, 0, 83), - SLE_CONDSTR(Sign, name, SLE_STR, 0, 84, SL_MAX_VERSION), - SLE_CONDVAR(Sign, x, SLE_FILE_I16 | SLE_VAR_I32, 0, 4), - SLE_CONDVAR(Sign, y, SLE_FILE_I16 | SLE_VAR_I32, 0, 4), - SLE_CONDVAR(Sign, x, SLE_INT32, 5, SL_MAX_VERSION), - SLE_CONDVAR(Sign, y, SLE_INT32, 5, SL_MAX_VERSION), - SLE_CONDVAR(Sign, owner, SLE_UINT8, 6, SL_MAX_VERSION), - SLE_VAR(Sign, z, SLE_UINT8), - SLE_END() -}; - -/** - * - * Save all signs - * - */ -static void Save_SIGN() -{ - Sign *si; - - FOR_ALL_SIGNS(si) { - SlSetArrayIndex(si->index); - SlObject(si, _sign_desc); - } -} - -/** - * - * Load all signs - * - */ -static void Load_SIGN() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Sign *si = new (index) Sign(); - SlObject(si, _sign_desc); - } -} - -extern const ChunkHandler _sign_chunk_handlers[] = { - { 'SIGN', Save_SIGN, Load_SIGN, CH_ARRAY | CH_LAST}, -}; diff --git a/src/signs_base.h b/src/signs_base.h --- a/src/signs_base.h +++ b/src/signs_base.h @@ -7,6 +7,7 @@ #include "signs_type.h" #include "viewport_type.h" +#include "tile_type.h" #include "oldpool.h" DECLARE_OLD_POOL(Sign, Sign, 2, 16000) diff --git a/src/station.cpp b/src/station.cpp --- a/src/station.cpp +++ b/src/station.cpp @@ -9,7 +9,6 @@ #include "station_map.h" #include "station_base.h" #include "town.h" -#include "saveload.h" #include "company_func.h" #include "airport.h" #include "sprite.h" diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -15,7 +15,6 @@ #include "command_func.h" #include "town.h" #include "news_func.h" -#include "saveload.h" #include "airport.h" #include "sprite.h" #include "train.h" @@ -3185,24 +3184,6 @@ void InitializeStations() _station_tick_ctr = 0; } - -void AfterLoadStations() -{ - /* Update the speclists of all stations to point to the currently loaded custom stations. */ - Station *st; - FOR_ALL_STATIONS(st) { - for (uint i = 0; i < st->num_specs; i++) { - if (st->speclist[i].grfid == 0) continue; - - st->speclist[i].spec = GetCustomStationSpecByGrf(st->speclist[i].grfid, st->speclist[i].localidx); - } - - for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache(); - - StationUpdateAnimTriggers(st); - } -} - static CommandCost TerraformTile_Station(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new) { if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) { @@ -3255,200 +3236,3 @@ extern const TileTypeProcs _tile_type_st GetFoundation_Station, /* get_foundation_proc */ TerraformTile_Station, /* terraform_tile_proc */ }; - -static const SaveLoad _roadstop_desc[] = { - SLE_VAR(RoadStop, xy, SLE_UINT32), - SLE_CONDNULL(1, 0, 44), - SLE_VAR(RoadStop, status, SLE_UINT8), - /* Index was saved in some versions, but this is not needed */ - SLE_CONDNULL(4, 0, 8), - SLE_CONDNULL(2, 0, 44), - SLE_CONDNULL(1, 0, 25), - - SLE_REF(RoadStop, next, REF_ROADSTOPS), - SLE_CONDNULL(2, 0, 44), - - SLE_CONDNULL(4, 0, 24), - SLE_CONDNULL(1, 25, 25), - - SLE_END() -}; - -static const SaveLoad _station_desc[] = { - SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Station, xy, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDNULL(4, 0, 5), ///< bus/lorry tile - SLE_CONDVAR(Station, train_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Station, train_tile, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Station, airport_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Station, airport_tile, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Station, dock_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Station, dock_tile, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_REF(Station, town, REF_TOWN), - SLE_VAR(Station, trainst_w, SLE_UINT8), - SLE_CONDVAR(Station, trainst_h, SLE_UINT8, 2, SL_MAX_VERSION), - - SLE_CONDNULL(1, 0, 3), ///< alpha_order - - SLE_VAR(Station, string_id, SLE_STRINGID), - SLE_CONDSTR(Station, name, SLE_STR, 0, 84, SL_MAX_VERSION), - SLE_CONDVAR(Station, indtype, SLE_UINT8, 103, SL_MAX_VERSION), - SLE_VAR(Station, had_vehicle_of_type, SLE_UINT16), - - SLE_VAR(Station, time_since_load, SLE_UINT8), - SLE_VAR(Station, time_since_unload, SLE_UINT8), - SLE_VAR(Station, delete_ctr, SLE_UINT8), - SLE_VAR(Station, owner, SLE_UINT8), - SLE_VAR(Station, facilities, SLE_UINT8), - SLE_VAR(Station, airport_type, SLE_UINT8), - - SLE_CONDNULL(2, 0, 5), ///< Truck/bus stop status - SLE_CONDNULL(1, 0, 4), ///< Blocked months - - SLE_CONDVAR(Station, airport_flags, SLE_VAR_U64 | SLE_FILE_U16, 0, 2), - SLE_CONDVAR(Station, airport_flags, SLE_VAR_U64 | SLE_FILE_U32, 3, 45), - SLE_CONDVAR(Station, airport_flags, SLE_UINT64, 46, SL_MAX_VERSION), - - SLE_CONDNULL(2, 0, 25), ///< last-vehicle - SLE_CONDVAR(Station, last_vehicle_type, SLE_UINT8, 26, SL_MAX_VERSION), - - SLE_CONDNULL(2, 3, 25), ///< custom station class and id - SLE_CONDVAR(Station, build_date, SLE_FILE_U16 | SLE_VAR_I32, 3, 30), - SLE_CONDVAR(Station, build_date, SLE_INT32, 31, SL_MAX_VERSION), - - SLE_CONDREF(Station, bus_stops, REF_ROADSTOPS, 6, SL_MAX_VERSION), - SLE_CONDREF(Station, truck_stops, REF_ROADSTOPS, 6, SL_MAX_VERSION), - - /* Used by newstations for graphic variations */ - SLE_CONDVAR(Station, random_bits, SLE_UINT16, 27, SL_MAX_VERSION), - SLE_CONDVAR(Station, waiting_triggers, SLE_UINT8, 27, SL_MAX_VERSION), - SLE_CONDVAR(Station, num_specs, SLE_UINT8, 27, SL_MAX_VERSION), - - SLE_CONDLST(Station, loading_vehicles, REF_VEHICLE, 57, SL_MAX_VERSION), - - /* reserve extra space in savegame here. (currently 32 bytes) */ - SLE_CONDNULL(32, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static uint16 _waiting_acceptance; -static uint16 _cargo_source; -static uint32 _cargo_source_xy; -static uint16 _cargo_days; -static Money _cargo_feeder_share; - -static const SaveLoad _station_speclist_desc[] = { - SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, 27, SL_MAX_VERSION), - SLE_CONDVAR(StationSpecList, localidx, SLE_UINT8, 27, SL_MAX_VERSION), - - SLE_END() -}; - - -void SaveLoad_STNS(Station *st) -{ - static const SaveLoad _goods_desc[] = { - SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, 0, 67), - SLE_CONDVAR(GoodsEntry, acceptance_pickup, SLE_UINT8, 68, SL_MAX_VERSION), - SLE_CONDNULL(2, 51, 67), - SLE_VAR(GoodsEntry, days_since_pickup, SLE_UINT8), - SLE_VAR(GoodsEntry, rating, SLE_UINT8), - SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), - SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67), - SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67), - SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67), - SLE_VAR(GoodsEntry, last_speed, SLE_UINT8), - SLE_VAR(GoodsEntry, last_age, SLE_UINT8), - SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, 14, 64), - SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67), - SLE_CONDLST(GoodsEntry, cargo.packets, REF_CARGO_PACKET, 68, SL_MAX_VERSION), - - SLE_END() -}; - - - SlObject(st, _station_desc); - - _waiting_acceptance = 0; - - uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO; - for (CargoID i = 0; i < num_cargo; i++) { - GoodsEntry *ge = &st->goods[i]; - SlObject(ge, _goods_desc); - if (CheckSavegameVersion(68)) { - SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); - if (GB(_waiting_acceptance, 0, 12) != 0) { - /* Don't construct the packet with station here, because that'll fail with old savegames */ - CargoPacket *cp = new CargoPacket(); - /* In old versions, enroute_from used 0xFF as INVALID_STATION */ - cp->source = (CheckSavegameVersion(7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source; - cp->count = GB(_waiting_acceptance, 0, 12); - cp->days_in_transit = _cargo_days; - cp->feeder_share = _cargo_feeder_share; - cp->source_xy = _cargo_source_xy; - cp->days_in_transit = _cargo_days; - cp->feeder_share = _cargo_feeder_share; - SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, 1); - ge->cargo.Append(cp); - } - } - } - - if (st->num_specs != 0) { - /* Allocate speclist memory when loading a game */ - if (st->speclist == NULL) st->speclist = CallocT(st->num_specs); - for (uint i = 0; i < st->num_specs; i++) { - SlObject(&st->speclist[i], _station_speclist_desc); - } - } -} - -static void Save_STNS() -{ - Station *st; - /* Write the stations */ - FOR_ALL_STATIONS(st) { - SlSetArrayIndex(st->index); - SlAutolength((AutolengthProc*)SaveLoad_STNS, st); - } -} - -static void Load_STNS() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Station *st = new (index) Station(); - - SaveLoad_STNS(st); - } - - /* This is to ensure all pointers are within the limits of _stations_size */ - if (_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0; -} - -static void Save_ROADSTOP() -{ - RoadStop *rs; - - FOR_ALL_ROADSTOPS(rs) { - SlSetArrayIndex(rs->index); - SlObject(rs, _roadstop_desc); - } -} - -static void Load_ROADSTOP() -{ - int index; - - while ((index = SlIterateArray()) != -1) { - RoadStop *rs = new (index) RoadStop(INVALID_TILE); - - SlObject(rs, _roadstop_desc); - } -} - -extern const ChunkHandler _station_chunk_handlers[] = { - { 'STNS', Save_STNS, Load_STNS, CH_ARRAY }, - { 'ROAD', Save_ROADSTOP, Load_ROADSTOP, CH_ARRAY | CH_LAST}, -}; diff --git a/src/station_func.h b/src/station_func.h --- a/src/station_func.h +++ b/src/station_func.h @@ -25,7 +25,6 @@ StationSet FindStationsAroundTiles(TileI void ShowStationViewWindow(StationID station); void UpdateAllStationVirtCoord(); -void AfterLoadStations(); void GetProductionAroundTiles(AcceptedCargo produced, TileIndex tile, int w, int h, int rad); void GetAcceptanceAroundTiles(AcceptedCargo accepts, TileIndex tile, int w, int h, int rad); diff --git a/src/strings.cpp b/src/strings.cpp --- a/src/strings.cpp +++ b/src/strings.cpp @@ -39,7 +39,6 @@ #include "video/video_driver.hpp" #include "engine_func.h" #include "engine_base.h" -#include "saveload.h" #include "strgen/strgen.h" #include "table/strings.h" @@ -1593,114 +1592,3 @@ void CheckForMissingGlyphsInLoadedLangua /* --- Handling of saving/loading string IDs from old savegames --- */ -/** - * Remap a string ID from the old format to the new format - * @param s StringID that requires remapping - * @return translated ID - */ -StringID RemapOldStringID(StringID s) -{ - switch (s) { - case 0x0006: return STR_SV_EMPTY; - case 0x7000: return STR_SV_UNNAMED; - case 0x70E4: return SPECSTR_PLAYERNAME_ENGLISH; - case 0x70E9: return SPECSTR_PLAYERNAME_ENGLISH; - case 0x8864: return STR_SV_TRAIN_NAME; - case 0x902B: return STR_SV_ROADVEH_NAME; - case 0x9830: return STR_SV_SHIP_NAME; - case 0xA02F: return STR_SV_AIRCRAFT_NAME; - - default: - if (IsInsideMM(s, 0x300F, 0x3030)) { - return s - 0x300F + STR_SV_STNAME; - } else { - return s; - } - } -} - -/** Location to load the old names to. */ -char *_old_name_array = NULL; - -/** - * Copy and convert old custom names to UTF-8. - * They were all stored in a 512 by 32 long string array and are - * now stored with stations, waypoints and other places with names. - * @param id the StringID of the custom name to clone. - * @return the clones custom name. - */ -char *CopyFromOldName(StringID id) -{ - /* Is this name an (old) custom name? */ - if (GB(id, 11, 5) != 15) return NULL; - - if (CheckSavegameVersion(37)) { - /* Old names were 32 characters long, so 128 characters should be - * plenty to allow for expansion when converted to UTF-8. */ - char tmp[128]; - const char *strfrom = &_old_name_array[32 * GB(id, 0, 9)]; - char *strto = tmp; - - for (; *strfrom != '\0'; strfrom++) { - WChar c = (byte)*strfrom; - - /* Map from non-ISO8859-15 characters to UTF-8. */ - switch (c) { - case 0xA4: c = 0x20AC; break; // Euro - case 0xA6: c = 0x0160; break; // S with caron - case 0xA8: c = 0x0161; break; // s with caron - case 0xB4: c = 0x017D; break; // Z with caron - case 0xB8: c = 0x017E; break; // z with caron - case 0xBC: c = 0x0152; break; // OE ligature - case 0xBD: c = 0x0153; break; // oe ligature - case 0xBE: c = 0x0178; break; // Y with diaresis - default: break; - } - - /* Check character will fit into our buffer. */ - if (strto + Utf8CharLen(c) > lastof(tmp)) break; - - strto += Utf8Encode(strto, c); - } - - /* Terminate the new string and copy it back to the name array */ - *strto = '\0'; - - return strdup(tmp); - } else { - /* Name will already be in UTF-8. */ - return strdup(&_old_name_array[32 * GB(id, 0, 9)]); - } -} - -/** - * Free the memory of the old names array. - * Should be called once the old names have all been converted. - */ -void ResetOldNames() -{ - free(_old_name_array); - _old_name_array = NULL; -} - -/** - * Initialize the old names table memory. - */ -void InitializeOldNames() -{ - free(_old_name_array); - _old_name_array = CallocT(512 * 32); -} - -static void Load_NAME() -{ - int index; - - while ((index = SlIterateArray()) != -1) { - SlArray(&_old_name_array[32 * index], SlGetFieldLength(), SLE_UINT8); - } -} - -extern const ChunkHandler _name_chunk_handlers[] = { - { 'NAME', NULL, Load_NAME, CH_ARRAY | CH_LAST}, -}; diff --git a/src/strings_func.h b/src/strings_func.h --- a/src/strings_func.h +++ b/src/strings_func.h @@ -68,7 +68,4 @@ struct StringIDCompare void CheckForMissingGlyphsInLoadedLanguagePack(); -StringID RemapOldStringID(StringID s); -char *CopyFromOldName(StringID id); - #endif /* STRINGS_TYPE_H */ diff --git a/src/town.h b/src/town.h --- a/src/town.h +++ b/src/town.h @@ -362,7 +362,6 @@ extern int _cleared_town_rating; void ResetHouses(); void ClearTownHouse(Town *t, TileIndex tile); -void AfterLoadTown(); void UpdateTownMaxPass(Town *t); void UpdateTownRadius(Town *t); bool CheckIfAuthorityAllows(TileIndex tile); diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -19,7 +19,6 @@ #include "station_base.h" #include "company_base.h" #include "news_func.h" -#include "saveload.h" #include "gui.h" #include "unmovable_map.h" #include "water_map.h" @@ -2676,155 +2675,6 @@ extern const TileTypeProcs _tile_type_to TerraformTile_Town, // terraform_tile_proc }; - -/** Save and load of towns. */ -static const SaveLoad _town_desc[] = { - SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Town, xy, SLE_UINT32, 6, SL_MAX_VERSION), - - SLE_CONDNULL(2, 0, 2), ///< population, no longer in use - SLE_CONDNULL(4, 3, 84), ///< population, no longer in use - SLE_CONDNULL(2, 0, 91), ///< num_houses, no longer in use - - SLE_CONDVAR(Town, townnamegrfid, SLE_UINT32, 66, SL_MAX_VERSION), - SLE_VAR(Town, townnametype, SLE_UINT16), - SLE_VAR(Town, townnameparts, SLE_UINT32), - SLE_CONDSTR(Town, name, SLE_STR, 0, 84, SL_MAX_VERSION), - - SLE_VAR(Town, flags12, SLE_UINT8), - SLE_CONDVAR(Town, statues, SLE_FILE_U8 | SLE_VAR_U16, 0, 103), - SLE_CONDVAR(Town, statues, SLE_UINT16, 104, SL_MAX_VERSION), - - SLE_CONDNULL(1, 0, 1), ///< sort_index, no longer in use - - SLE_CONDVAR(Town, have_ratings, SLE_FILE_U8 | SLE_VAR_U16, 0, 103), - SLE_CONDVAR(Town, have_ratings, SLE_UINT16, 104, SL_MAX_VERSION), - SLE_CONDARR(Town, ratings, SLE_INT16, 8, 0, 103), - SLE_CONDARR(Town, ratings, SLE_INT16, MAX_COMPANIES, 104, SL_MAX_VERSION), - /* failed bribe attempts are stored since savegame format 4 */ - SLE_CONDARR(Town, unwanted, SLE_INT8, 8, 4, 103), - SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, 104, SL_MAX_VERSION), - - SLE_CONDVAR(Town, max_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), - SLE_CONDVAR(Town, max_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), - SLE_CONDVAR(Town, new_max_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), - SLE_CONDVAR(Town, new_max_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), - SLE_CONDVAR(Town, act_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), - SLE_CONDVAR(Town, act_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), - SLE_CONDVAR(Town, new_act_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), - SLE_CONDVAR(Town, new_act_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8), - - SLE_CONDVAR(Town, max_pass, SLE_UINT32, 9, SL_MAX_VERSION), - SLE_CONDVAR(Town, max_mail, SLE_UINT32, 9, SL_MAX_VERSION), - SLE_CONDVAR(Town, new_max_pass, SLE_UINT32, 9, SL_MAX_VERSION), - SLE_CONDVAR(Town, new_max_mail, SLE_UINT32, 9, SL_MAX_VERSION), - SLE_CONDVAR(Town, act_pass, SLE_UINT32, 9, SL_MAX_VERSION), - SLE_CONDVAR(Town, act_mail, SLE_UINT32, 9, SL_MAX_VERSION), - SLE_CONDVAR(Town, new_act_pass, SLE_UINT32, 9, SL_MAX_VERSION), - SLE_CONDVAR(Town, new_act_mail, SLE_UINT32, 9, SL_MAX_VERSION), - - SLE_VAR(Town, pct_pass_transported, SLE_UINT8), - SLE_VAR(Town, pct_mail_transported, SLE_UINT8), - - SLE_VAR(Town, act_food, SLE_UINT16), - SLE_VAR(Town, act_water, SLE_UINT16), - SLE_VAR(Town, new_act_food, SLE_UINT16), - SLE_VAR(Town, new_act_water, SLE_UINT16), - - SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT8, 0, 53), - SLE_CONDVAR(Town, grow_counter, SLE_UINT8, 0, 53), - SLE_CONDVAR(Town, growth_rate, SLE_UINT8, 0, 53), - - SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT16, 54, SL_MAX_VERSION), - SLE_CONDVAR(Town, grow_counter, SLE_UINT16, 54, SL_MAX_VERSION), - SLE_CONDVAR(Town, growth_rate, SLE_INT16, 54, SL_MAX_VERSION), - - SLE_VAR(Town, fund_buildings_months, SLE_UINT8), - SLE_VAR(Town, road_build_months, SLE_UINT8), - - SLE_CONDVAR(Town, exclusivity, SLE_UINT8, 2, SL_MAX_VERSION), - SLE_CONDVAR(Town, exclusive_counter, SLE_UINT8, 2, SL_MAX_VERSION), - - SLE_CONDVAR(Town, larger_town, SLE_BOOL, 56, SL_MAX_VERSION), - - /* reserve extra space in savegame here. (currently 30 bytes) */ - SLE_CONDNULL(30, 2, SL_MAX_VERSION), - - SLE_END() -}; - -/* Save and load the mapping between the house id on the map, and the grf file - * it came from. */ -static const SaveLoad _house_id_mapping_desc[] = { - SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), - SLE_VAR(EntityIDMapping, entity_id, SLE_UINT8), - SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT8), - SLE_END() -}; - -static void Save_HOUSEIDS() -{ - uint j = _house_mngr.GetMaxMapping(); - - for (uint i = 0; i < j; i++) { - SlSetArrayIndex(i); - SlObject(&_house_mngr.mapping_ID[i], _house_id_mapping_desc); - } -} - -static void Load_HOUSEIDS() -{ - int index; - - _house_mngr.ResetMapping(); - uint max_id = _house_mngr.GetMaxMapping(); - - while ((index = SlIterateArray()) != -1) { - if ((uint)index >= max_id) break; - SlObject(&_house_mngr.mapping_ID[index], _house_id_mapping_desc); - } -} - -static void Save_TOWN() -{ - Town *t; - - FOR_ALL_TOWNS(t) { - SlSetArrayIndex(t->index); - SlObject(t, _town_desc); - } -} - -static void Load_TOWN() -{ - int index; - - _total_towns = 0; - - while ((index = SlIterateArray()) != -1) { - Town *t = new (index) Town(); - SlObject(t, _town_desc); - - _total_towns++; - } - - /* This is to ensure all pointers are within the limits of - * the size of the TownPool */ - if (_cur_town_ctr > GetMaxTownIndex()) - _cur_town_ctr = 0; -} - -void AfterLoadTown() -{ - Town *t; - FOR_ALL_TOWNS(t) t->InitializeLayout(); -} - -extern const ChunkHandler _town_chunk_handlers[] = { - { 'HIDS', Save_HOUSEIDS, Load_HOUSEIDS, CH_ARRAY }, - { 'CITY', Save_TOWN, Load_TOWN, CH_ARRAY | CH_LAST}, -}; - void ResetHouses() { memset(&_house_specs, 0, sizeof(_house_specs)); diff --git a/src/train.h b/src/train.h --- a/src/train.h +++ b/src/train.h @@ -287,9 +287,6 @@ static inline Vehicle *GetPrevUnit(const return w; } -void ConvertOldMultiheadToNew(); -void ConnectMultiheadedTrains(); - void CcBuildLoco(bool success, TileIndex tile, uint32 p1, uint32 p2); void CcBuildWagon(bool success, TileIndex tile, uint32 p1, uint32 p2); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -4500,146 +4500,3 @@ void InitializeTrains() { _age_cargo_skip_counter = 1; } - -/* - * Link front and rear multiheaded engines to each other - * This is done when loading a savegame - */ -void ConnectMultiheadedTrains() -{ - Vehicle *v; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN) { - v->u.rail.other_multiheaded_part = NULL; - } - } - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) { - /* Two ways to associate multiheaded parts to each other: - * sequential-matching: Trains shall be arranged to look like <..>..<..>..<..>.. - * bracket-matching: Free vehicle chains shall be arranged to look like ..<..<..>..<..>..>.. - * - * Note: Old savegames might contain chains which do not comply with these rules, e.g. - * - the front and read parts have invalid orders - * - different engine types might be combined - * - there might be different amounts of front and rear parts. - * - * Note: The multiheaded parts need to be matched exactly like they are matched on the server, else desyncs will occur. - * This is why two matching strategies are needed. - */ - - bool sequential_matching = IsFrontEngine(v); - - for (Vehicle *u = v; u != NULL; u = GetNextVehicle(u)) { - if (u->u.rail.other_multiheaded_part != NULL) continue; // we already linked this one - - if (IsMultiheaded(u)) { - if (!IsTrainEngine(u)) { - /* we got a rear car without a front car. We will convert it to a front one */ - SetTrainEngine(u); - u->spritenum--; - } - - /* Find a matching back part */ - EngineID eid = u->engine_type; - Vehicle *w; - if (sequential_matching) { - for (w = GetNextVehicle(u); w != NULL; w = GetNextVehicle(w)) { - if (w->engine_type != eid || w->u.rail.other_multiheaded_part != NULL || !IsMultiheaded(w)) continue; - - /* we found a car to partner with this engine. Now we will make sure it face the right way */ - if (IsTrainEngine(w)) { - ClearTrainEngine(w); - w->spritenum++; - } - break; - } - } else { - uint stack_pos = 0; - for (w = GetNextVehicle(u); w != NULL; w = GetNextVehicle(w)) { - if (w->engine_type != eid || w->u.rail.other_multiheaded_part != NULL || !IsMultiheaded(w)) continue; - - if (IsTrainEngine(w)) { - stack_pos++; - } else { - if (stack_pos == 0) break; - stack_pos--; - } - } - } - - if (w != NULL) { - w->u.rail.other_multiheaded_part = u; - u->u.rail.other_multiheaded_part = w; - } else { - /* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */ - ClearMultiheaded(u); - } - } - } - } - } -} - -/** - * Converts all trains to the new subtype format introduced in savegame 16.2 - * It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found - */ -void ConvertOldMultiheadToNew() -{ - Vehicle *v; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN) { - SetBit(v->subtype, 7); // indicates that it's the old format and needs to be converted in the next loop - } - } - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN) { - if (HasBit(v->subtype, 7) && ((v->subtype & ~0x80) == 0 || (v->subtype & ~0x80) == 4)) { - for (Vehicle *u = v; u != NULL; u = u->Next()) { - const RailVehicleInfo *rvi = RailVehInfo(u->engine_type); - - ClrBit(u->subtype, 7); - switch (u->subtype) { - case 0: /* TS_Front_Engine */ - if (rvi->railveh_type == RAILVEH_MULTIHEAD) SetMultiheaded(u); - SetFrontEngine(u); - SetTrainEngine(u); - break; - - case 1: /* TS_Artic_Part */ - u->subtype = 0; - SetArticulatedPart(u); - break; - - case 2: /* TS_Not_First */ - u->subtype = 0; - if (rvi->railveh_type == RAILVEH_WAGON) { - // normal wagon - SetTrainWagon(u); - break; - } - if (rvi->railveh_type == RAILVEH_MULTIHEAD && rvi->image_index == u->spritenum - 1) { - // rear end of a multiheaded engine - SetMultiheaded(u); - break; - } - if (rvi->railveh_type == RAILVEH_MULTIHEAD) SetMultiheaded(u); - SetTrainEngine(u); - break; - - case 4: /* TS_Free_Car */ - u->subtype = 0; - SetTrainWagon(u); - SetFreeWagon(u); - break; - default: NOT_REACHED(); break; - } - } - } - } - } -} diff --git a/src/variables.h b/src/variables.h --- a/src/variables.h +++ b/src/variables.h @@ -33,10 +33,6 @@ VARDEF uint _next_competitor_start; /* Determines how often to run the tree loop */ VARDEF byte _trees_tick_ctr; -/* Keep track of current game position */ -VARDEF int _saved_scrollpos_x; -VARDEF int _saved_scrollpos_y; - /* NOSAVE: Used in palette animations only, not really important. */ VARDEF int _palette_animation_counter; diff --git a/src/vehicle.cpp b/src/vehicle.cpp --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -15,7 +15,7 @@ #include "gfx_func.h" #include "news_func.h" #include "command_func.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "company_func.h" #include "debug.h" #include "vehicle_gui.h" @@ -59,9 +59,6 @@ #include "table/sprites.h" #include "table/strings.h" -#include - -#define INVALID_COORD (0x7fffffff) #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6)) VehicleID _vehicle_id_ctr_day; @@ -222,120 +219,6 @@ void VehiclePositionChanged(Vehicle *v) v->bottom_coord = pt.y + spr->height + 2; } -/** Called after load to update coordinates */ -void AfterLoadVehicles(bool part_of_load) -{ - Vehicle *v; - - FOR_ALL_VEHICLES(v) { - /* Reinstate the previous pointer */ - if (v->Next() != NULL) v->Next()->previous = v; - if (v->NextShared() != NULL) v->NextShared()->previous_shared = v; - - v->UpdateDeltaXY(v->direction); - - if (part_of_load) v->fill_percent_te_id = INVALID_TE_ID; - v->first = NULL; - if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE; - if (v->type == VEH_ROAD) v->u.road.first_engine = INVALID_ENGINE; - - v->cargo.InvalidateCache(); - } - - /* AfterLoadVehicles may also be called in case of NewGRF reload, in this - * case we may not convert orders again. */ - if (part_of_load) { - /* Create shared vehicle chain for very old games (pre 5,2) and create - * OrderList from shared vehicle chains. For this to work correctly, the - * following conditions must be fulfilled: - * a) both next_shared and previous_shared are not set for pre 5,2 games - * b) both next_shared and previous_shared are set for later games - */ - std::map mapping; - - FOR_ALL_VEHICLES(v) { - if (v->orders.old != NULL) { - if (CheckSavegameVersion(105)) { // Pre-105 didn't save an OrderList - if (mapping[v->orders.old] == NULL) { - /* This adds the whole shared vehicle chain for case b */ - v->orders.list = mapping[v->orders.old] = new OrderList(v->orders.old, v); - } else { - v->orders.list = mapping[v->orders.old]; - /* For old games (case a) we must create the shared vehicle chain */ - if (CheckSavegameVersionOldStyle(5, 2)) { - v->AddToShared(v->orders.list->GetFirstSharedVehicle()); - } - } - } else { // OrderList was saved as such, only recalculate not saved values - if (v->PreviousShared() == NULL) { - new (v->orders.list) OrderList(v->orders.list->GetFirstOrder(), v); - } - } - } - } - } - - FOR_ALL_VEHICLES(v) { - /* Fill the first pointers */ - if (v->Previous() == NULL) { - for (Vehicle *u = v; u != NULL; u = u->Next()) { - u->first = v; - } - } - } - - FOR_ALL_VEHICLES(v) { - assert(v->first != NULL); - - if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) { - if (IsFrontEngine(v)) v->u.rail.last_speed = v->cur_speed; // update displayed train speed - TrainConsistChanged(v, false); - } else if (v->type == VEH_ROAD && IsRoadVehFront(v)) { - RoadVehUpdateCache(v); - } - } - - /* Stop non-front engines */ - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN && IsTrainEngine(v) && !IsFrontEngine(v)) v->vehstatus |= VS_STOPPED; - } - - FOR_ALL_VEHICLES(v) { - switch (v->type) { - case VEH_ROAD: - v->u.road.roadtype = HasBit(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; - v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype); - /* FALL THROUGH */ - case VEH_TRAIN: - case VEH_SHIP: - v->cur_image = v->GetImage(v->direction); - break; - - case VEH_AIRCRAFT: - if (IsNormalAircraft(v)) { - v->cur_image = v->GetImage(v->direction); - - /* The plane's shadow will have the same image as the plane */ - Vehicle *shadow = v->Next(); - shadow->cur_image = v->cur_image; - - /* In the case of a helicopter we will update the rotor sprites */ - if (v->subtype == AIR_HELICOPTER) { - Vehicle *rotor = shadow->Next(); - rotor->cur_image = GetRotorImage(v); - } - - UpdateAircraftCache(v); - } - break; - default: break; - } - - v->left_coord = INVALID_COORD; - VehiclePositionChanged(v); - } -} - Vehicle::Vehicle() { this->type = VEH_INVALID; @@ -622,11 +505,19 @@ void ResetVehicleColorMap() FOR_ALL_VEHICLES(v) { v->colormap = PAL_NONE; } } +/** + * List of vehicles that should check for autoreplace this tick. + * Mapping of vehicle -> leave depot immediatelly after autoreplace. + */ +typedef SmallMap AutoreplaceMap; +static AutoreplaceMap _vehicles_to_autoreplace; + void InitializeVehicles() { _Vehicle_pool.CleanPool(); _Vehicle_pool.AddBlockToPool(); + _vehicles_to_autoreplace.Reset(); ResetVehiclePosHash(); } @@ -739,13 +630,6 @@ Vehicle::~Vehicle() new (this) InvalidVehicle(); } -/** - * List of vehicles that should check for autoreplace this tick. - * Mapping of vehicle -> leave depot immediatelly after autoreplace. - */ -typedef SmallMap AutoreplaceMap; -static AutoreplaceMap _vehicles_to_autoreplace; - /** Adds a vehicle to the list of vehicles, that visited a depot this tick * @param *v vehicle to add */ @@ -2128,367 +2012,6 @@ SpriteID GetVehiclePalette(const Vehicle return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v); } -static uint8 _cargo_days; -static uint16 _cargo_source; -static uint32 _cargo_source_xy; -static uint16 _cargo_count; -static uint16 _cargo_paid_for; -static Money _cargo_feeder_share; -static uint32 _cargo_loaded_at_xy; - -/** - * Make it possible to make the saveload tables "friends" of other classes. - * @param vt the vehicle type. Can be VEH_END for the common vehicle description data - * @return the saveload description - */ -const SaveLoad *GetVehicleDescription(VehicleType vt) -{ -/** Save and load of vehicles */ -static const SaveLoad _common_veh_desc[] = { - SLE_VAR(Vehicle, subtype, SLE_UINT8), - - SLE_REF(Vehicle, next, REF_VEHICLE_OLD), - SLE_CONDVAR(Vehicle, name, SLE_NAME, 0, 83), - SLE_CONDSTR(Vehicle, name, SLE_STR, 0, 84, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, 0, 7), - SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, 8, SL_MAX_VERSION), - SLE_VAR(Vehicle, owner, SLE_UINT8), - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_VAR(Vehicle, z_pos, SLE_UINT8), - SLE_VAR(Vehicle, direction, SLE_UINT8), - - SLE_CONDNULL(2, 0, 57), - SLE_VAR(Vehicle, spritenum, SLE_UINT8), - SLE_CONDNULL(5, 0, 57), - SLE_VAR(Vehicle, engine_type, SLE_UINT16), - - SLE_VAR(Vehicle, max_speed, SLE_UINT16), - SLE_VAR(Vehicle, cur_speed, SLE_UINT16), - SLE_VAR(Vehicle, subspeed, SLE_UINT8), - SLE_VAR(Vehicle, acceleration, SLE_UINT8), - SLE_VAR(Vehicle, progress, SLE_UINT8), - - SLE_VAR(Vehicle, vehstatus, SLE_UINT8), - SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), - SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, 5, SL_MAX_VERSION), - - SLE_VAR(Vehicle, cargo_type, SLE_UINT8), - SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, 35, SL_MAX_VERSION), - SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67), - SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), - SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67), - SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67), - SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), - SLEG_CONDVAR( _cargo_count, SLE_UINT16, 0, 67), - SLE_CONDLST(Vehicle, cargo, REF_CARGO_PACKET, 68, SL_MAX_VERSION), - - SLE_VAR(Vehicle, day_counter, SLE_UINT8), - SLE_VAR(Vehicle, tick_counter, SLE_UINT8), - SLE_CONDVAR(Vehicle, running_ticks, SLE_UINT8, 88, SL_MAX_VERSION), - - SLE_VAR(Vehicle, cur_order_index, SLE_UINT8), - /* num_orders is now part of OrderList and is not saved but counted */ - SLE_CONDNULL(1, 0, 104), - - /* This next line is for version 4 and prior compatibility.. it temporarily reads - type and flags (which were both 4 bits) into type. Later on this is - converted correctly */ - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8, 0, 4), - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), - - /* Orders for version 5 and on */ - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8, 5, SL_MAX_VERSION), - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, flags), SLE_UINT8, 5, SL_MAX_VERSION), - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION), - - /* Refit in current order */ - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_cargo), SLE_UINT8, 36, SL_MAX_VERSION), - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_subtype), SLE_UINT8, 36, SL_MAX_VERSION), - - /* Timetable in current order */ - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, wait_time), SLE_UINT16, 67, SL_MAX_VERSION), - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, travel_time), SLE_UINT16, 67, SL_MAX_VERSION), - - SLE_CONDREF(Vehicle, orders, REF_ORDER, 0, 104), - SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, 105, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Vehicle, max_age, SLE_INT32, 31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, 31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Vehicle, service_interval, SLE_INT32, 31, SL_MAX_VERSION), - SLE_VAR(Vehicle, reliability, SLE_UINT16), - SLE_VAR(Vehicle, reliability_spd_dec, SLE_UINT16), - SLE_VAR(Vehicle, breakdown_ctr, SLE_UINT8), - SLE_VAR(Vehicle, breakdown_delay, SLE_UINT8), - SLE_VAR(Vehicle, breakdowns_since_last_service, SLE_UINT8), - SLE_VAR(Vehicle, breakdown_chance, SLE_UINT8), - SLE_CONDVAR(Vehicle, build_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Vehicle, build_year, SLE_INT32, 31, SL_MAX_VERSION), - - SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16), - SLEG_CONDVAR( _cargo_paid_for, SLE_UINT16, 45, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, vehicle_flags, SLE_UINT8, 40, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), - SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, 65, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), - SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, 65, SL_MAX_VERSION), - SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64,51, 64), - SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67), - SLEG_CONDVAR( _cargo_loaded_at_xy, SLE_UINT32, 51, 67), - SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), - SLE_CONDVAR(Vehicle, value, SLE_INT64, 65, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, random_bits, SLE_UINT8, 2, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, 2, SL_MAX_VERSION), - - SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, 2, SL_MAX_VERSION), - SLE_CONDNULL(2, 2, 68), - SLE_CONDNULL(4, 69, 100), - - SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, 60, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, current_order_time, SLE_UINT32, 67, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, lateness_counter, SLE_INT32, 67, SL_MAX_VERSION), - - /* reserve extra space in savegame here. (currently 10 bytes) */ - SLE_CONDNULL(10, 2, SL_MAX_VERSION), - - SLE_END() -}; - - -static const SaveLoad _train_desc[] = { - SLE_WRITEBYTE(Vehicle, type, VEH_TRAIN), - SLE_VEH_INCLUDEX(), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, crash_anim_pos), SLE_UINT16), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, force_proceed), SLE_UINT8), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, railtype), SLE_UINT8), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, track), SLE_UINT8), - - SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_FILE_U8 | SLE_VAR_U16, 2, 99), - SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_UINT16,100, SL_MAX_VERSION), - SLE_CONDNULL(2, 2, 59), - - SLE_CONDNULL(2, 2, 19), - /* reserve extra space in savegame here. (currently 11 bytes) */ - SLE_CONDNULL(11, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static const SaveLoad _roadveh_desc[] = { - SLE_WRITEBYTE(Vehicle, type, VEH_ROAD), - SLE_VEH_INCLUDEX(), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, state), SLE_UINT8), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, frame), SLE_UINT8), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, blocked_ctr), SLE_UINT16), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking), SLE_UINT8), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking_ctr), SLE_UINT8), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, crashed_ctr), SLE_UINT16), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, reverse_ctr), SLE_UINT8), - - SLE_CONDREFX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot), REF_ROADSTOPS, 6, SL_MAX_VERSION), - SLE_CONDNULL(1, 6, SL_MAX_VERSION), - SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot_age), SLE_UINT8, 6, SL_MAX_VERSION), - /* reserve extra space in savegame here. (currently 16 bytes) */ - SLE_CONDNULL(16, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static const SaveLoad _ship_desc[] = { - SLE_WRITEBYTE(Vehicle, type, VEH_SHIP), - SLE_VEH_INCLUDEX(), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleShip, state), SLE_UINT8), - - /* reserve extra space in savegame here. (currently 16 bytes) */ - SLE_CONDNULL(16, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static const SaveLoad _aircraft_desc[] = { - SLE_WRITEBYTE(Vehicle, type, VEH_AIRCRAFT), - SLE_VEH_INCLUDEX(), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, crashed_counter), SLE_UINT16), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, pos), SLE_UINT8), - - SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), - SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport), SLE_UINT16, 5, SL_MAX_VERSION), - - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, state), SLE_UINT8), - - SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, previous_pos), SLE_UINT8, 2, SL_MAX_VERSION), - - /* reserve extra space in savegame here. (currently 15 bytes) */ - SLE_CONDNULL(15, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static const SaveLoad _special_desc[] = { - SLE_WRITEBYTE(Vehicle, type, VEH_EFFECT), - - SLE_VAR(Vehicle, subtype, SLE_UINT8), - - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), - SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), - SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION), - SLE_VAR(Vehicle, z_pos, SLE_UINT8), - - SLE_VAR(Vehicle, cur_image, SLE_UINT16), - SLE_CONDNULL(5, 0, 57), - SLE_VAR(Vehicle, progress, SLE_UINT8), - SLE_VAR(Vehicle, vehstatus, SLE_UINT8), - - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleEffect, animation_state), SLE_UINT16), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleEffect, animation_substate), SLE_UINT8), - - SLE_CONDVAR(Vehicle, spritenum, SLE_UINT8, 2, SL_MAX_VERSION), - - /* reserve extra space in savegame here. (currently 15 bytes) */ - SLE_CONDNULL(15, 2, SL_MAX_VERSION), - - SLE_END() -}; - -static const SaveLoad _disaster_desc[] = { - SLE_WRITEBYTE(Vehicle, type, VEH_DISASTER), - - SLE_REF(Vehicle, next, REF_VEHICLE_OLD), - - SLE_VAR(Vehicle, subtype, SLE_UINT8), - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), - SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), - SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION), - SLE_VAR(Vehicle, z_pos, SLE_UINT8), - SLE_VAR(Vehicle, direction, SLE_UINT8), - - SLE_CONDNULL(5, 0, 57), - SLE_VAR(Vehicle, owner, SLE_UINT8), - SLE_VAR(Vehicle, vehstatus, SLE_UINT8), - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), - SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION), - - SLE_VAR(Vehicle, cur_image, SLE_UINT16), - SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), - SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION), - SLE_VAR(Vehicle, tick_counter, SLE_UINT8), - - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, image_override), SLE_UINT16), - SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, big_ufo_destroyer_target), SLE_UINT16), - - /* reserve extra space in savegame here. (currently 16 bytes) */ - SLE_CONDNULL(16, 2, SL_MAX_VERSION), - - SLE_END() -}; - - -static const SaveLoad *_veh_descs[] = { - _train_desc, - _roadveh_desc, - _ship_desc, - _aircraft_desc, - _special_desc, - _disaster_desc, - _common_veh_desc, -}; - - return _veh_descs[vt]; -} - -/** Will be called when the vehicles need to be saved. */ -static void Save_VEHS() -{ - Vehicle *v; - /* Write the vehicles */ - FOR_ALL_VEHICLES(v) { - SlSetArrayIndex(v->index); - SlObject(v, GetVehicleDescription(v->type)); - } -} - -/** Will be called when vehicles need to be loaded. */ -void Load_VEHS() -{ - _vehicles_to_autoreplace.Reset(); - - int index; - - _cargo_count = 0; - - while ((index = SlIterateArray()) != -1) { - Vehicle *v; - VehicleType vtype = (VehicleType)SlReadByte(); - - switch (vtype) { - case VEH_TRAIN: v = new (index) Train(); break; - case VEH_ROAD: v = new (index) RoadVehicle(); break; - case VEH_SHIP: v = new (index) Ship(); break; - case VEH_AIRCRAFT: v = new (index) Aircraft(); break; - case VEH_EFFECT: v = new (index) EffectVehicle(); break; - case VEH_DISASTER: v = new (index) DisasterVehicle(); break; - case VEH_INVALID: v = new (index) InvalidVehicle(); break; - default: NOT_REACHED(); - } - - SlObject(v, GetVehicleDescription(vtype)); - - if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v)) { - /* Don't construct the packet with station here, because that'll fail with old savegames */ - CargoPacket *cp = new CargoPacket(); - cp->source = _cargo_source; - cp->source_xy = _cargo_source_xy; - cp->count = _cargo_count; - cp->days_in_transit = _cargo_days; - cp->feeder_share = _cargo_feeder_share; - cp->loaded_at_xy = _cargo_loaded_at_xy; - v->cargo.Append(cp); - } - - /* Old savegames used 'last_station_visited = 0xFF' */ - if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF) - v->last_station_visited = INVALID_STATION; - - if (CheckSavegameVersion(5)) { - /* Convert the current_order.type (which is a mix of type and flags, because - * in those versions, they both were 4 bits big) to type and flags */ - v->current_order.flags = GB(v->current_order.type, 4, 4); - v->current_order.type &= 0x0F; - } - - /* Advanced vehicle lists got added */ - if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP; - } -} - -extern const ChunkHandler _veh_chunk_handlers[] = { - { 'VEHS', Save_VEHS, Load_VEHS, CH_SPARSE_ARRAY | CH_LAST}, -}; void Vehicle::BeginLoading() { diff --git a/src/vehicle_base.h b/src/vehicle_base.h --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -194,7 +194,6 @@ DECLARE_OLD_POOL(Vehicle, Vehicle, 9, 12 /* Some declarations of functions, so we can make them friendly */ struct SaveLoad; extern const SaveLoad *GetVehicleDescription(VehicleType vt); -extern void AfterLoadVehicles(bool part_of_load); struct LoadgameState; extern bool LoadOldVehicle(LoadgameState *ls, int num); @@ -712,4 +711,6 @@ Trackdir GetVehicleTrackdir(const Vehicl void CheckVehicle32Day(Vehicle *v); +static const int32 INVALID_COORD = 0x7fffffff; + #endif /* VEHICLE_BASE_H */ diff --git a/src/viewport.cpp b/src/viewport.cpp --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -2785,33 +2785,3 @@ void ResetObjectToPlace() { SetObjectToPlace(SPR_CURSOR_MOUSE, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0); } - - -void SaveViewportBeforeSaveGame() -{ - const Window *w = FindWindowById(WC_MAIN_WINDOW, 0); - - if (w != NULL) { - _saved_scrollpos_x = w->viewport->scrollpos_x; - _saved_scrollpos_y = w->viewport->scrollpos_y; - _saved_scrollpos_zoom = w->viewport->zoom; - } -} - -void ResetViewportAfterLoadGame() -{ - Window *w = FindWindowById(WC_MAIN_WINDOW, 0); - - w->viewport->scrollpos_x = _saved_scrollpos_x; - w->viewport->scrollpos_y = _saved_scrollpos_y; - w->viewport->dest_scrollpos_x = _saved_scrollpos_x; - w->viewport->dest_scrollpos_y = _saved_scrollpos_y; - - ViewPort *vp = w->viewport; - vp->zoom = min(_saved_scrollpos_zoom, ZOOM_LVL_MAX); - vp->virtual_width = ScaleByZoom(vp->width, vp->zoom); - vp->virtual_height = ScaleByZoom(vp->height, vp->zoom); - - DoZoomInOutWindow(ZOOM_NONE, w); // update button status - MarkWholeScreenDirty(); -} diff --git a/src/water.h b/src/water.h --- a/src/water.h +++ b/src/water.h @@ -15,6 +15,5 @@ void DrawWaterClassGround(const struct T void DrawShoreTile(Slope tileh); void MakeWaterKeepingClass(TileIndex tile, Owner o); -void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_water_class); #endif /* WATER_H */ diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -101,88 +101,6 @@ static void MarkCanalsAndRiversAroundDir } } -/** - * Makes a tile canal or water depending on the surroundings. - * - * Must only be used for converting old savegames. Use WaterClass now. - * - * This as for example docks and shipdepots do not store - * whether the tile used to be canal or 'normal' water. - * @param t the tile to change. - * @param o the owner of the new tile. - * @param include_invalid_water_class Also consider WATER_CLASS_INVALID, i.e. industry tiles on land - */ -void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_water_class) -{ - /* If the slope is not flat, we always assume 'land' (if allowed). Also for one-corner-raised-shores. - * Note: Wrt. autosloping under industry tiles this is the most fool-proof behaviour. */ - if (GetTileSlope(t, NULL) != SLOPE_FLAT) { - if (include_invalid_water_class) { - SetWaterClass(t, WATER_CLASS_INVALID); - return; - } else { - NOT_REACHED(); - } - } - - /* Mark tile dirty in all cases */ - MarkTileDirtyByTile(t); - - if (TileX(t) == 0 || TileY(t) == 0 || TileX(t) == MapMaxX() - 1 || TileY(t) == MapMaxY() - 1) { - /* tiles at map borders are always WATER_CLASS_SEA */ - SetWaterClass(t, WATER_CLASS_SEA); - return; - } - - bool has_water = false; - bool has_canal = false; - bool has_river = false; - - for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { - TileIndex neighbour = TileAddByDiagDir(t, dir); - switch (GetTileType(neighbour)) { - case MP_WATER: - /* clear water and shipdepots have already a WaterClass associated */ - if (IsCoast(neighbour)) { - has_water = true; - } else if (!IsLock(neighbour)) { - switch (GetWaterClass(neighbour)) { - case WATER_CLASS_SEA: has_water = true; break; - case WATER_CLASS_CANAL: has_canal = true; break; - case WATER_CLASS_RIVER: has_river = true; break; - default: NOT_REACHED(); - } - } - break; - - case MP_RAILWAY: - /* Shore or flooded halftile */ - has_water |= (GetRailGroundType(neighbour) == RAIL_GROUND_WATER); - break; - - case MP_TREES: - /* trees on shore */ - has_water |= (GetTreeGround(neighbour) == TREE_GROUND_SHORE); - break; - - default: break; - } - } - - if (!has_water && !has_canal && !has_river && include_invalid_water_class) { - SetWaterClass(t, WATER_CLASS_INVALID); - return; - } - - if (has_river && !has_canal) { - SetWaterClass(t, WATER_CLASS_RIVER); - } else if (has_canal || !has_water) { - SetWaterClass(t, WATER_CLASS_CANAL); - } else { - SetWaterClass(t, WATER_CLASS_SEA); - } -} - /** Build a ship depot. * @param tile tile where ship depot is built diff --git a/src/waypoint.cpp b/src/waypoint.cpp --- a/src/waypoint.cpp +++ b/src/waypoint.cpp @@ -11,7 +11,6 @@ #include "rail_map.h" #include "rail.h" #include "bridge_map.h" -#include "saveload.h" #include "station_base.h" #include "town.h" #include "waypoint.h" @@ -157,29 +156,6 @@ static Waypoint *FindDeletedWaypointClos return best; } -/** - * Update waypoint graphics id against saved GRFID/localidx. - * This is to ensure the chosen graphics are correct if GRF files are changed. - */ -void AfterLoadWaypoints() -{ - Waypoint *wp; - - FOR_ALL_WAYPOINTS(wp) { - uint i; - - if (wp->grfid == 0) continue; - - for (i = 0; i < GetNumCustomStations(STAT_CLASS_WAYP); i++) { - const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, i); - if (statspec != NULL && statspec->grffile->grfid == wp->grfid && statspec->localidx == wp->localidx) { - wp->stat_id = i; - break; - } - } - } -} - /** Convert existing rail to waypoint. Eg build a waypoint station over * piece of rail * @param tile tile where waypoint will be built @@ -463,69 +439,8 @@ Waypoint::~Waypoint() this->xy = INVALID_TILE; } -/** - * Fix savegames which stored waypoints in their old format - */ -void FixOldWaypoints() -{ - Waypoint *wp; - - /* Convert the old 'town_or_string', to 'string' / 'town' / 'town_cn' */ - FOR_ALL_WAYPOINTS(wp) { - wp->town_index = ClosestTownFromTile(wp->xy, UINT_MAX)->index; - wp->town_cn = 0; - if (wp->string & 0xC000) { - wp->town_cn = wp->string & 0x3F; - wp->string = STR_NULL; - } - } -} - void InitializeWaypoints() { _Waypoint_pool.CleanPool(); _Waypoint_pool.AddBlockToPool(); } - -static const SaveLoad _waypoint_desc[] = { - SLE_CONDVAR(Waypoint, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Waypoint, xy, SLE_UINT32, 6, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, town_index, SLE_UINT16, 12, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, town_cn, SLE_FILE_U8 | SLE_VAR_U16, 12, 88), - SLE_CONDVAR(Waypoint, town_cn, SLE_UINT16, 89, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, string, SLE_STRINGID, 0, 83), - SLE_CONDSTR(Waypoint, name, SLE_STR, 0, 84, SL_MAX_VERSION), - SLE_VAR(Waypoint, deleted, SLE_UINT8), - - SLE_CONDVAR(Waypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32, 3, 30), - SLE_CONDVAR(Waypoint, build_date, SLE_INT32, 31, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, localidx, SLE_UINT8, 3, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, grfid, SLE_UINT32, 17, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, owner, SLE_UINT8, 101, SL_MAX_VERSION), - - SLE_END() -}; - -static void Save_WAYP() -{ - Waypoint *wp; - - FOR_ALL_WAYPOINTS(wp) { - SlSetArrayIndex(wp->index); - SlObject(wp, _waypoint_desc); - } -} - -static void Load_WAYP() -{ - int index; - - while ((index = SlIterateArray()) != -1) { - Waypoint *wp = new (index) Waypoint(); - SlObject(wp, _waypoint_desc); - } -} - -extern const ChunkHandler _waypoint_chunk_handlers[] = { - { 'CHKP', Save_WAYP, Load_WAYP, CH_ARRAY | CH_LAST}, -}; diff --git a/src/waypoint.h b/src/waypoint.h --- a/src/waypoint.h +++ b/src/waypoint.h @@ -12,6 +12,7 @@ #include "station_type.h" #include "town_type.h" #include "viewport_type.h" +#include "date_type.h" DECLARE_OLD_POOL(Waypoint, Waypoint, 3, 8000) @@ -63,8 +64,6 @@ CommandCost RemoveTrainWaypoint(TileInde Station *ComposeWaypointStation(TileIndex tile); void ShowWaypointWindow(const Waypoint *wp); void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype); -void FixOldWaypoints(); void UpdateAllWaypointSigns(); -void AfterLoadWaypoints(); #endif /* WAYPOINT_H */ diff --git a/src/win32.cpp b/src/win32.cpp --- a/src/win32.cpp +++ b/src/win32.cpp @@ -5,7 +5,7 @@ #include "stdafx.h" #include "openttd.h" #include "debug.h" -#include "saveload.h" +#include "saveload/saveload.h" #include "gfx_func.h" #include "textbuf_gui.h" #include "fileio_func.h"