diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 34daa354ac..8048cdaeda 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(network) add_subdirectory(os) add_subdirectory(pathfinder) add_subdirectory(saveload) +add_subdirectory(sl) add_subdirectory(sound) add_subdirectory(spriteloader) add_subdirectory(table) diff --git a/src/cargopacket.h b/src/cargopacket.h index cb1805de52..276991de6d 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -18,7 +18,7 @@ #include "vehicle_type.h" #include "company_type.h" #include "core/multimap.hpp" -#include "saveload/saveload_common.h" +#include "sl/saveload_common.h" #include /** Unique identifier for a single cargo packet. */ diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp index 95cd91af25..5105697b0b 100644 --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -13,7 +13,7 @@ #include "company_base.h" #include "company_func.h" #include "date_func.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "textbuf_gui.h" #include "window_gui.h" #include "string_func.h" diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 29a17acdda..ea0089ef07 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -12,7 +12,7 @@ #include "debug.h" #include "engine_func.h" #include "landscape.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "network/core/game_info.h" #include "network/network.h" #include "network/network_func.h" diff --git a/src/crashlog.cpp b/src/crashlog.cpp index c01d0f0330..1821bb9d1a 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -20,7 +20,7 @@ #include "music/music_driver.hpp" #include "sound/sound_driver.hpp" #include "video/video_driver.hpp" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "screenshot.h" #include "gfx_func.h" #include "network/network.h" diff --git a/src/date.cpp b/src/date.cpp index a12ca1feab..79c3f46e6f 100644 --- a/src/date.cpp +++ b/src/date.cpp @@ -17,7 +17,7 @@ #include "vehicle_base.h" #include "rail_gui.h" #include "linkgraph/linkgraph.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "newgrf_profiling.h" #include "console_func.h" #include "debug.h" diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index ec87faffe7..b9ede19dac 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -8,7 +8,7 @@ /** @file fios_gui.cpp GUIs for loading/saving games, scenarios, heightmaps, ... */ #include "stdafx.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "error.h" #include "gui.h" #include "gfx_func.h" diff --git a/src/gamelog.cpp b/src/gamelog.cpp index 8a153b7d13..1ce896e2d4 100644 --- a/src/gamelog.cpp +++ b/src/gamelog.cpp @@ -8,7 +8,7 @@ /** @file gamelog.cpp Definition of functions used for logging of important changes in the game */ #include "stdafx.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "string_func.h" #include "settings_type.h" #include "gamelog_internal.h" diff --git a/src/genworld.cpp b/src/genworld.cpp index 51c7f06f92..8a1a7f5910 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -21,7 +21,7 @@ #include "water.h" #include "video/video_driver.hpp" #include "tilehighlight_func.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "void_map.h" #include "town.h" #include "newgrf.h" diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index f276a2fb7a..478818c200 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -25,7 +25,7 @@ #include "town.h" #include "core/geometry_func.hpp" #include "core/random_func.hpp" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "progress.h" #include "error.h" #include "newgrf_townname.h" diff --git a/src/heightmap.cpp b/src/heightmap.cpp index a665d630ac..f8c7369954 100644 --- a/src/heightmap.cpp +++ b/src/heightmap.cpp @@ -12,7 +12,7 @@ #include "clear_map.h" #include "void_map.h" #include "error.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "bmp.h" #include "gfx_func.h" #include "fios.h" diff --git a/src/landscape.cpp b/src/landscape.cpp index 5cf8ce6c80..592ac130a3 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -30,7 +30,7 @@ #include "company_func.h" #include "tunnelbridge_map.h" #include "pathfinder/npf/aystar.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "framerate_type.h" #include "town.h" #include "3rdparty/cpp-btree/btree_set.h" diff --git a/src/linkgraph/linkgraph.h b/src/linkgraph/linkgraph.h index 23bf6fb4e6..40bab9dd8b 100644 --- a/src/linkgraph/linkgraph.h +++ b/src/linkgraph/linkgraph.h @@ -16,7 +16,7 @@ #include "../station_base.h" #include "../cargotype.h" #include "../date_func.h" -#include "../saveload/saveload_common.h" +#include "../sl/saveload_common.h" #include "linkgraph_type.h" #include "../3rdparty/cpp-btree/btree_map.h" #include diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 3f46407e23..373d1a7f96 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -34,7 +34,7 @@ #include "error.h" #include "news_gui.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "widgets/main_widget.h" diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index e68e278adb..976f9f6837 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -9,8 +9,8 @@ #include "../stdafx.h" #include "network_gui.h" -#include "../saveload/saveload.h" -#include "../saveload/saveload_filter.h" +#include "../sl/saveload.h" +#include "../sl/saveload_filter.h" #include "../command_func.h" #include "../console_func.h" #include "../strings_func.h" diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index dc0ad2cc46..21a3ae8dc3 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -18,8 +18,8 @@ #include "../console_func.h" #include "../company_base.h" #include "../command_func.h" -#include "../saveload/saveload.h" -#include "../saveload/saveload_filter.h" +#include "../sl/saveload.h" +#include "../sl/saveload_filter.h" #include "../station_base.h" #include "../genworld.h" #include "../company_func.h" diff --git a/src/openttd.cpp b/src/openttd.cpp index 3487dede2b..0c8a6049a2 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -20,7 +20,7 @@ #include "gui.h" #include "base_media_base.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "company_func.h" #include "command_func.h" #include "news_func.h" diff --git a/src/order_backup.h b/src/order_backup.h index 7a1cab0c33..33d7f1be62 100644 --- a/src/order_backup.h +++ b/src/order_backup.h @@ -16,7 +16,7 @@ #include "vehicle_type.h" #include "base_consist.h" #include "order_base.h" -#include "saveload/saveload_common.h" +#include "sl/saveload_common.h" /** Unique identifier for an order backup. */ typedef uint8 OrderBackupID; diff --git a/src/order_base.h b/src/order_base.h index d6d219d333..38023669f1 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -20,7 +20,7 @@ #include "date_type.h" #include "schdispatch.h" #include "gfx_type.h" -#include "saveload/saveload_common.h" +#include "sl/saveload_common.h" #include #include diff --git a/src/os/macosx/crashlog_osx.cpp b/src/os/macosx/crashlog_osx.cpp index 2db57e9cc4..fc05d284f9 100644 --- a/src/os/macosx/crashlog_osx.cpp +++ b/src/os/macosx/crashlog_osx.cpp @@ -11,7 +11,7 @@ #include "../../crashlog.h" #include "../../string_func.h" #include "../../gamelog.h" -#include "../../saveload/saveload.h" +#include "../../sl/saveload.h" #include "../../thread.h" #include "../../screenshot.h" #include "../../debug.h" diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index 7f804cd5eb..41d3682f3c 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -12,7 +12,7 @@ #include "../../crashlog_bfd.h" #include "../../string_func.h" #include "../../gamelog.h" -#include "../../saveload/saveload.h" +#include "../../sl/saveload.h" #include #include diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index 0a58ac7379..b0767c42ab 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -17,7 +17,7 @@ #include "../../fileio_func.h" #include "../../strings_func.h" #include "../../gamelog.h" -#include "../../saveload/saveload.h" +#include "../../sl/saveload.h" #include "../../video/video_driver.hpp" #include "../../screenshot.h" #include "../../debug.h" diff --git a/src/saveload/CMakeLists.txt b/src/saveload/CMakeLists.txt index ce8b04f212..3961647f9a 100644 --- a/src/saveload/CMakeLists.txt +++ b/src/saveload/CMakeLists.txt @@ -1,4 +1,4 @@ -add_subdirectory(upstream) +add_subdirectory(compat) add_files( afterload.cpp @@ -6,17 +6,13 @@ add_files( airport_sl.cpp animated_tile_sl.cpp autoreplace_sl.cpp - bridge_signal_sl.cpp cargomonitor_sl.cpp cargopacket_sl.cpp cheat_sl.cpp company_sl.cpp - debug_sl.cpp depot_sl.cpp economy_sl.cpp engine_sl.cpp - extended_ver_sl.cpp - extended_ver_sl.h game_sl.cpp gamelog_sl.cpp goal_sl.cpp @@ -29,33 +25,16 @@ add_files( misc_sl.cpp newgrf_sl.cpp newgrf_sl.h - newsignals_sl.cpp object_sl.cpp - oldloader.cpp - oldloader.h - oldloader_sl.cpp order_sl.cpp - plans_sl.cpp saveload.cpp saveload.h - saveload_buffer.h - saveload_common.h - saveload_filter.h - saveload_internal.h - saveload_types.h - signal_sl.cpp + settings_sl.cpp signs_sl.cpp station_sl.cpp storage_sl.cpp - strings_sl.cpp story_sl.cpp subsidy_sl.cpp - tbtr_template_replacement_sl.cpp - tbtr_template_veh_sl.cpp town_sl.cpp - tracerestrict_sl.cpp - train_speed_adaptation.cpp - tunnel_sl.cpp vehicle_sl.cpp - waypoint_sl.cpp ) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index a759333c0d..6963266f32 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -75,7 +75,7 @@ #include "../timer/timer_game_tick.h" -#include "saveload_internal.h" +#include "../sl/saveload_internal.h" #include #include diff --git a/src/saveload/ai_sl.cpp b/src/saveload/ai_sl.cpp index 0b5b9f6e54..97175e0057 100644 --- a/src/saveload/ai_sl.cpp +++ b/src/saveload/ai_sl.cpp @@ -8,9 +8,12 @@ /** @file ai_sl.cpp Handles the saveload part of the AIs */ #include "../stdafx.h" -#include "../company_base.h" #include "../debug.h" + #include "saveload.h" +#include "compat/ai_sl_compat.h" + +#include "../company_base.h" #include "../string_func.h" #include "../ai/ai.hpp" @@ -20,16 +23,18 @@ #include "../safeguards.h" +namespace upstream_sl { + static std::string _ai_saveload_name; static int _ai_saveload_version; static std::string _ai_saveload_settings; static bool _ai_saveload_is_random; -static const SaveLoad _ai_company[] = { - SLEG_SSTR(_ai_saveload_name, SLE_STR), - SLEG_SSTR(_ai_saveload_settings, SLE_STR), - SLEG_CONDVAR(_ai_saveload_version, SLE_UINT32, SLV_108, SL_MAX_VERSION), - SLEG_CONDVAR(_ai_saveload_is_random, SLE_BOOL, SLV_136, SL_MAX_VERSION), +static const SaveLoad _ai_company_desc[] = { + SLEG_SSTR("name", _ai_saveload_name, SLE_STR), + SLEG_SSTR("settings", _ai_saveload_settings, SLE_STR), + SLEG_CONDVAR("version", _ai_saveload_version, SLE_UINT32, SLV_108, SL_MAX_VERSION), + SLEG_CONDVAR("is_random", _ai_saveload_is_random, SLE_BOOL, SLV_136, SL_MAX_VERSION), }; static void SaveReal_AIPL(int *index_ptr) @@ -49,76 +54,87 @@ static void SaveReal_AIPL(int *index_ptr) _ai_saveload_is_random = config->IsRandom(); _ai_saveload_settings = config->SettingsToString(); - SlObject(nullptr, _ai_company); + SlObject(nullptr, _ai_company_desc); /* If the AI was active, store its data too */ if (Company::IsValidAiID(index)) AI::Save(index); } -static void Load_AIPL() -{ - /* Free all current data */ - for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { - AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(nullptr); - } +struct AIPLChunkHandler : ChunkHandler { + AIPLChunkHandler() : ChunkHandler('AIPL', CH_TABLE) {} - CompanyID index; - while ((index = (CompanyID)SlIterateArray()) != (CompanyID)-1) { - if (index >= MAX_COMPANIES) SlErrorCorrupt("Too many AI configs"); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_ai_company_desc, _ai_company_sl_compat); - _ai_saveload_is_random = false; - _ai_saveload_version = -1; - SlObject(nullptr, _ai_company); - - if (_game_mode == GM_MENU || (_networking && !_network_server)) { - if (Company::IsValidAiID(index)) AIInstance::LoadEmpty(); - continue; + /* Free all current data */ + for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { + AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(nullptr); } - AIConfig *config = AIConfig::GetConfig(index, AIConfig::SSS_FORCE_GAME); - if (_ai_saveload_name.empty()) { - /* A random AI. */ - config->Change(nullptr, -1, false, true); - } else { - config->Change(_ai_saveload_name.c_str(), _ai_saveload_version, false, _ai_saveload_is_random); - if (!config->HasScript()) { - /* No version of the AI available that can load the data. Try to load the - * latest version of the AI instead. */ - config->Change(_ai_saveload_name.c_str(), -1, false, _ai_saveload_is_random); - if (!config->HasScript()) { - if (_ai_saveload_name.compare("%_dummy") != 0) { - DEBUG(script, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version); - DEBUG(script, 0, "A random other AI will be loaded in its place."); - } else { - DEBUG(script, 0, "The savegame had no AIs available at the time of saving."); - DEBUG(script, 0, "A random available AI will be loaded now."); - } - } else { - DEBUG(script, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version); - DEBUG(script, 0, "The latest version of that AI has been loaded instead, but it'll not get the savegame data as it's incompatible."); - } - /* Make sure the AI doesn't get the saveload data, as it was not the - * writer of the saveload data in the first place */ - _ai_saveload_version = -1; + CompanyID index; + while ((index = (CompanyID)SlIterateArray()) != (CompanyID)-1) { + if (index >= MAX_COMPANIES) SlErrorCorrupt("Too many AI configs"); + + _ai_saveload_is_random = false; + _ai_saveload_version = -1; + SlObject(nullptr, slt); + + if (_game_mode == GM_MENU || (_networking && !_network_server)) { + if (Company::IsValidAiID(index)) AIInstance::LoadEmpty(); + continue; } + + AIConfig *config = AIConfig::GetConfig(index, AIConfig::SSS_FORCE_GAME); + if (_ai_saveload_name.empty()) { + /* A random AI. */ + config->Change(nullptr, -1, false, true); + } else { + config->Change(_ai_saveload_name.c_str(), _ai_saveload_version, false, _ai_saveload_is_random); + if (!config->HasScript()) { + /* No version of the AI available that can load the data. Try to load the + * latest version of the AI instead. */ + config->Change(_ai_saveload_name.c_str(), -1, false, _ai_saveload_is_random); + if (!config->HasScript()) { + if (_ai_saveload_name.compare("%_dummy") != 0) { + DEBUG(script, 0, "The savegame has an AI by the name '%s', version %u which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version); + DEBUG(script, 0, "A random other AI will be loaded in its place."); + } else { + DEBUG(script, 0, "The savegame had no AIs available at the time of saving."); + DEBUG(script, 0, "A random available AI will be loaded now."); + } + } else { + DEBUG(script, 0, "The savegame has an AI by the name '%s', version %u which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version); + DEBUG(script, 0, "The latest version of that AI has been loaded instead, but it'll not get the savegame data as it's incompatible."); + } + /* Make sure the AI doesn't get the saveload data, as it was not the + * writer of the saveload data in the first place */ + _ai_saveload_version = -1; + } + } + + config->StringToSettings(_ai_saveload_settings); + + /* Load the AI saved data */ + if (Company::IsValidAiID(index)) config->SetToLoadData(AIInstance::Load(_ai_saveload_version)); } - - config->StringToSettings(_ai_saveload_settings); - - /* Load the AI saved data */ - if (Company::IsValidAiID(index)) config->SetToLoadData(AIInstance::Load(_ai_saveload_version)); } -} -static void Save_AIPL() -{ - for (int i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { - SlSetArrayIndex(i); - SlAutolength((AutolengthProc *)SaveReal_AIPL, &i); + void Save() const override + { + SlTableHeader(_ai_company_desc); + + for (int i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + SlSetArrayIndex(i); + SlAutolength((AutolengthProc *)SaveReal_AIPL, &i); + } } -} +}; -static const ChunkHandler ai_chunk_handlers[] = { - { 'AIPL', Save_AIPL, Load_AIPL, nullptr, nullptr, CH_ARRAY }, +static const AIPLChunkHandler AIPL; +static const ChunkHandlerRef ai_chunk_handlers[] = { + AIPL, }; extern const ChunkHandlerTable _ai_chunk_handlers(ai_chunk_handlers); + +} diff --git a/src/saveload/airport_sl.cpp b/src/saveload/airport_sl.cpp index 114e5672ab..9fbcf51816 100644 --- a/src/saveload/airport_sl.cpp +++ b/src/saveload/airport_sl.cpp @@ -14,29 +14,23 @@ #include "../safeguards.h" -static void Save_APID() -{ - Save_NewGRFMapping(_airport_mngr); -} +namespace upstream_sl { -static void Load_APID() -{ - Load_NewGRFMapping(_airport_mngr); -} +struct APIDChunkHandler : NewGRFMappingChunkHandler { + APIDChunkHandler() : NewGRFMappingChunkHandler('APID', _airport_mngr) {} +}; -static void Save_ATID() -{ - Save_NewGRFMapping(_airporttile_mngr); -} +struct ATIDChunkHandler : NewGRFMappingChunkHandler { + ATIDChunkHandler() : NewGRFMappingChunkHandler('ATID', _airporttile_mngr) {} +}; -static void Load_ATID() -{ - Load_NewGRFMapping(_airporttile_mngr); -} - -static const ChunkHandler airport_chunk_handlers[] = { - { 'ATID', Save_ATID, Load_ATID, nullptr, nullptr, CH_ARRAY }, - { 'APID', Save_APID, Load_APID, nullptr, nullptr, CH_ARRAY }, +static const ATIDChunkHandler ATID; +static const APIDChunkHandler APID; +static const ChunkHandlerRef airport_chunk_handlers[] = { + ATID, + APID, }; extern const ChunkHandlerTable _airport_chunk_handlers(airport_chunk_handlers); + +} diff --git a/src/saveload/animated_tile_sl.cpp b/src/saveload/animated_tile_sl.cpp index b9ccc44092..b437305e3f 100644 --- a/src/saveload/animated_tile_sl.cpp +++ b/src/saveload/animated_tile_sl.cpp @@ -8,73 +8,77 @@ /** @file animated_tile_sl.cpp Code handling saving and loading of animated tiles */ #include "../stdafx.h" -#include "../animated_tile.h" + +#include "saveload.h" +#include "compat/animated_tile_sl_compat.h" + #include "../tile_type.h" +#include "../animated_tile.h" #include "../core/alloc_func.hpp" #include "../core/smallvec_type.hpp" -#include "saveload.h" - #include "../safeguards.h" -/** - * Save the ANIT chunk. - */ -static void Save_ANIT() -{ - uint count = 0; - for (const auto &it : _animated_tiles) { - if (!it.second.pending_deletion) count++; - } - SlSetLength(count * 5); - for (const auto &it : _animated_tiles) { - if (it.second.pending_deletion) continue; - SlWriteUint32(it.first); - SlWriteByte(it.second.speed); - } -} +namespace upstream_sl { -/** - * 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 (IsSavegameVersionBefore(SLV_80)) { - /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */ - TileIndex anim_list[256]; - SlArray(anim_list, 256, IsSavegameVersionBefore(SLV_6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32); +static std::vector _tmp_animated_tiles; - for (int i = 0; i < 256; i++) { - if (anim_list[i] == 0) break; - _animated_tiles[anim_list[i]] = {}; +static const SaveLoad _animated_tile_desc[] = { + SLEG_VECTOR("tiles", _tmp_animated_tiles, SLE_UINT32), +}; + +struct ANITChunkHandler : ChunkHandler { + ANITChunkHandler() : ChunkHandler('ANIT', CH_TABLE) {} + + void Save() const override + { + // removed + NOT_REACHED(); + } + + void Load() const override + { + /* Before version 80 we did NOT have a variable length animated tile table */ + if (IsSavegameVersionBefore(SLV_80)) { + /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */ + TileIndex anim_list[256]; + SlCopy(anim_list, 256, IsSavegameVersionBefore(SLV_6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32); + + for (int i = 0; i < 256; i++) { + if (anim_list[i] == 0) break; + _animated_tiles[anim_list[i]] = {}; + } + return; } - return; - } - _animated_tiles.clear(); - if (SlXvIsFeaturePresent(XSLFI_ANIMATED_TILE_EXTRA)) { - uint count = (uint)SlGetFieldLength() / 5; - for (uint i = 0; i < count; i++) { - TileIndex tile = SlReadUint32(); - AnimatedTileInfo info = {}; - info.speed = SlReadByte(); - _animated_tiles[tile] = info; + if (IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY)) { + size_t count = SlGetFieldLength() / sizeof(uint32); + _animated_tiles.clear(); + for (uint i = 0; i < count; i++) { + _animated_tiles[SlReadUint32()] = {}; + } + return; } - } else { - uint count = (uint)SlGetFieldLength() / 4; - for (uint i = 0; i < count; i++) { - _animated_tiles[SlReadUint32()] = {}; - } - } -} -/** - * "Definition" imported by the saveload code to be able to load and save - * the animated tile table. - */ -static const ChunkHandler animated_tile_chunk_handlers[] = { - { 'ANIT', Save_ANIT, Load_ANIT, nullptr, nullptr, CH_RIFF }, + const std::vector slt = SlCompatTableHeader(_animated_tile_desc, _animated_tile_sl_compat); + + if (SlIterateArray() == -1) return; + SlGlobList(slt); + if (SlIterateArray() != -1) SlErrorCorrupt("Too many ANIT entries"); + + for (TileIndex t : _tmp_animated_tiles) { + _animated_tiles[t] = {}; + } + _tmp_animated_tiles.clear(); + } +}; + + +static const ANITChunkHandler ANIT; +static const ChunkHandlerRef animated_tile_chunk_handlers[] = { + ANIT, }; extern const ChunkHandlerTable _animated_tile_chunk_handlers(animated_tile_chunk_handlers); + +} diff --git a/src/saveload/autoreplace_sl.cpp b/src/saveload/autoreplace_sl.cpp index f54866c107..72f24556e9 100644 --- a/src/saveload/autoreplace_sl.cpp +++ b/src/saveload/autoreplace_sl.cpp @@ -8,12 +8,16 @@ /** @file autoreplace_sl.cpp Code handling saving and loading of autoreplace rules */ #include "../stdafx.h" -#include "../autoreplace_base.h" #include "saveload.h" +#include "compat/autoreplace_sl_compat.h" + +#include "../autoreplace_base.h" #include "../safeguards.h" +namespace upstream_sl { + static const SaveLoad _engine_renew_desc[] = { SLE_VAR(EngineRenew, from, SLE_UINT16), SLE_VAR(EngineRenew, to, SLE_UINT16), @@ -23,40 +27,51 @@ static const SaveLoad _engine_renew_desc[] = { SLE_CONDVAR(EngineRenew, replace_when_old, SLE_BOOL, SLV_175, SL_MAX_VERSION), }; -static void Save_ERNW() -{ - for (EngineRenew *er : EngineRenew::Iterate()) { - SlSetArrayIndex(er->index); - SlObject(er, _engine_renew_desc); - } -} +struct ERNWChunkHandler : ChunkHandler { + ERNWChunkHandler() : ChunkHandler('ERNW', CH_TABLE) {} -static void Load_ERNW() -{ - int index; + void Save() const override + { + SlTableHeader(_engine_renew_desc); - while ((index = SlIterateArray()) != -1) { - EngineRenew *er = new (index) EngineRenew(); - SlObject(er, _engine_renew_desc); - - /* Advanced vehicle lists, ungrouped vehicles got added */ - if (IsSavegameVersionBefore(SLV_60)) { - er->group_id = ALL_GROUP; - } else if (IsSavegameVersionBefore(SLV_71)) { - if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP; + for (EngineRenew *er : EngineRenew::Iterate()) { + SlSetArrayIndex(er->index); + SlObject(er, _engine_renew_desc); } } -} -static void Ptrs_ERNW() -{ - for (EngineRenew *er : EngineRenew::Iterate()) { - SlObject(er, _engine_renew_desc); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_engine_renew_desc, _engine_renew_sl_compat); + + int index; + + while ((index = SlIterateArray()) != -1) { + EngineRenew *er = new (index) EngineRenew(); + SlObject(er, slt); + + /* Advanced vehicle lists, ungrouped vehicles got added */ + if (IsSavegameVersionBefore(SLV_60)) { + er->group_id = ALL_GROUP; + } else if (IsSavegameVersionBefore(SLV_71)) { + if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP; + } + } } -} -static const ChunkHandler autoreplace_chunk_handlers[] = { - { 'ERNW', Save_ERNW, Load_ERNW, Ptrs_ERNW, nullptr, CH_ARRAY }, + void FixPointers() const override + { + for (EngineRenew *er : EngineRenew::Iterate()) { + SlObject(er, _engine_renew_desc); + } + } +}; + +static const ERNWChunkHandler ERNW; +static const ChunkHandlerRef autoreplace_chunk_handlers[] = { + ERNW, }; extern const ChunkHandlerTable _autoreplace_chunk_handlers(autoreplace_chunk_handlers); + +} diff --git a/src/saveload/cargomonitor_sl.cpp b/src/saveload/cargomonitor_sl.cpp index abb0d59b99..ba0b598fab 100644 --- a/src/saveload/cargomonitor_sl.cpp +++ b/src/saveload/cargomonitor_sl.cpp @@ -8,12 +8,16 @@ /** @file cargomonitor_sl.cpp Code handling saving and loading of Cargo monitoring. */ #include "../stdafx.h" -#include "../cargomonitor.h" #include "saveload.h" +#include "compat/cargomonitor_sl_compat.h" + +#include "../cargomonitor.h" #include "../safeguards.h" +namespace upstream_sl { + /** Temporary storage of cargo monitoring data for loading or saving it. */ struct TempStorage { CargoMonitorID number; @@ -41,85 +45,102 @@ static CargoMonitorID FixupCargoMonitor(CargoMonitorID number) return number; } -/** Save the #_cargo_deliveries monitoring map. */ -static void SaveDelivery() -{ - TempStorage storage; +/** #_cargo_deliveries monitoring map. */ +struct CMDLChunkHandler : ChunkHandler { + CMDLChunkHandler() : ChunkHandler('CMDL', CH_TABLE) {} - int i = 0; - CargoMonitorMap::const_iterator iter = _cargo_deliveries.begin(); - while (iter != _cargo_deliveries.end()) { - storage.number = iter->first; - storage.amount = iter->second; + void Save() const override + { + SlTableHeader(_cargomonitor_pair_desc); - SlSetArrayIndex(i); - SlObject(&storage, _cargomonitor_pair_desc); + TempStorage storage; - i++; - iter++; + int i = 0; + CargoMonitorMap::const_iterator iter = _cargo_deliveries.begin(); + while (iter != _cargo_deliveries.end()) { + storage.number = iter->first; + storage.amount = iter->second; + + SlSetArrayIndex(i); + SlObject(&storage, _cargomonitor_pair_desc); + + i++; + iter++; + } } -} -/** Load the #_cargo_deliveries monitoring map. */ -static void LoadDelivery() -{ - TempStorage storage; - bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_cargomonitor_pair_desc, _cargomonitor_pair_sl_compat); - ClearCargoDeliveryMonitoring(); - for (;;) { - if (SlIterateArray() < 0) break; - SlObject(&storage, _cargomonitor_pair_desc); + TempStorage storage; + bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR); - if (fix) storage.number = FixupCargoMonitor(storage.number); + ClearCargoDeliveryMonitoring(); + for (;;) { + if (SlIterateArray() < 0) break; + SlObject(&storage, slt); - std::pair p(storage.number, storage.amount); - _cargo_deliveries.insert(p); + if (fix) storage.number = FixupCargoMonitor(storage.number); + + std::pair p(storage.number, storage.amount); + _cargo_deliveries.insert(p); + } } -} +}; +/** #_cargo_pickups monitoring map. */ +struct CMPUChunkHandler : ChunkHandler { + CMPUChunkHandler() : ChunkHandler('CMPU', CH_TABLE) {} -/** Save the #_cargo_pickups monitoring map. */ -static void SavePickup() -{ - TempStorage storage; + void Save() const override + { + SlTableHeader(_cargomonitor_pair_desc); - int i = 0; - CargoMonitorMap::const_iterator iter = _cargo_pickups.begin(); - while (iter != _cargo_pickups.end()) { - storage.number = iter->first; - storage.amount = iter->second; + TempStorage storage; - SlSetArrayIndex(i); - SlObject(&storage, _cargomonitor_pair_desc); + int i = 0; + CargoMonitorMap::const_iterator iter = _cargo_pickups.begin(); + while (iter != _cargo_pickups.end()) { + storage.number = iter->first; + storage.amount = iter->second; - i++; - iter++; + SlSetArrayIndex(i); + SlObject(&storage, _cargomonitor_pair_desc); + + i++; + iter++; + } } -} -/** Load the #_cargo_pickups monitoring map. */ -static void LoadPickup() -{ - TempStorage storage; - bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_cargomonitor_pair_desc, _cargomonitor_pair_sl_compat); - ClearCargoPickupMonitoring(); - for (;;) { - if (SlIterateArray() < 0) break; - SlObject(&storage, _cargomonitor_pair_desc); + TempStorage storage; + bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR); - if (fix) storage.number = FixupCargoMonitor(storage.number); + ClearCargoPickupMonitoring(); + for (;;) { + if (SlIterateArray() < 0) break; + SlObject(&storage, slt); - std::pair p(storage.number, storage.amount); - _cargo_pickups.insert(p); + if (fix) storage.number = FixupCargoMonitor(storage.number); + + std::pair p(storage.number, storage.amount); + _cargo_pickups.insert(p); + } } -} +}; /** Chunk definition of the cargomonitoring maps. */ -static const ChunkHandler cargomonitor_chunk_handlers[] = { - { 'CMDL', SaveDelivery, LoadDelivery, nullptr, nullptr, CH_ARRAY }, - { 'CMPU', SavePickup, LoadPickup, nullptr, nullptr, CH_ARRAY }, +static const CMDLChunkHandler CMDL; +static const CMPUChunkHandler CMPU; +static const ChunkHandlerRef cargomonitor_chunk_handlers[] = { + CMDL, + CMPU, }; extern const ChunkHandlerTable _cargomonitor_chunk_handlers(cargomonitor_chunk_handlers); + +} diff --git a/src/saveload/cargopacket_sl.cpp b/src/saveload/cargopacket_sl.cpp index 69ad66fec4..65ae77e254 100644 --- a/src/saveload/cargopacket_sl.cpp +++ b/src/saveload/cargopacket_sl.cpp @@ -8,100 +8,16 @@ /** @file cargopacket_sl.cpp Code handling saving and loading of cargo packets */ #include "../stdafx.h" -#include "../vehicle_base.h" -#include "../station_base.h" -#include "../scope_info.h" -#include "../3rdparty/cpp-btree/btree_map.h" #include "saveload.h" +#include "compat/cargopacket_sl_compat.h" + +#include "../vehicle_base.h" +#include "../station_base.h" #include "../safeguards.h" -extern btree::btree_map _cargo_packet_deferred_payments; - -/** - * Savegame conversion for cargopackets. - */ -/* static */ void CargoPacket::AfterLoad() -{ - if (IsSavegameVersionBefore(SLV_44)) { - /* If we remove a station while cargo from it is still en route, 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 (const Vehicle *v : Vehicle::Iterate()) { - const CargoPacketList *packets = v->cargo.Packets(); - for (VehicleCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) { - CargoPacket *cp = *it; - cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : v->tile; - cp->loaded_at_xy = cp->source_xy; - } - } - - /* 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 */ - for (Station *st : Station::Iterate()) { - for (CargoID c = 0; c < NUM_CARGO; c++) { - GoodsEntry *ge = &st->goods[c]; - - const StationCargoPacketMap *packets = ge->cargo.Packets(); - for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) { - CargoPacket *cp = *it; - cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : st->xy; - cp->loaded_at_xy = cp->source_xy; - } - } - } - } - - if (IsSavegameVersionBefore(SLV_120)) { - /* CargoPacket's source should be either INVALID_STATION or a valid station */ - for (CargoPacket *cp : CargoPacket::Iterate()) { - if (!Station::IsValidID(cp->source)) cp->source = INVALID_STATION; - } - } - - if (!IsSavegameVersionBefore(SLV_68)) { - /* Only since version 68 we have cargo packets. Savegames from before used - * 'new CargoPacket' + cargolist.Append so their caches are already - * correct and do not need rebuilding. */ - for (Vehicle *v : Vehicle::Iterate()) v->cargo.InvalidateCache(); - - for (Station *st : Station::Iterate()) { - for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache(); - } - } - - if (IsSavegameVersionBefore(SLV_181)) { - for (Vehicle *v : Vehicle::Iterate()) v->cargo.KeepAll(); - } -} - -/** - * Savegame conversion for cargopackets. - */ -/* static */ void CargoPacket::PostVehiclesAfterLoad() -{ - if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { - extern std::map _veh_cpp_packets; - for (auto &iter : _veh_cpp_packets) { - if (iter.second.empty()) continue; - Vehicle *v = Vehicle::Get(iter.first); - Station *st = Station::Get(v->First()->last_station_visited); - assert_msg(st != nullptr, "%s", scope_dumper().VehicleInfo(v)); - for (CargoPacket *cp : iter.second) { - st->goods[v->cargo_type].cargo.AfterLoadIncreaseReservationCount(cp->count); - v->cargo.Append(cp, VehicleCargoList::MTA_LOAD); - } - } - _veh_cpp_packets.clear(); - } -} +namespace upstream_sl { /** * Wrapper function to get the CargoPacket's internal structure while @@ -111,85 +27,50 @@ extern btree::btree_map _cargo_packet_deferred_payments; SaveLoadTable GetCargoPacketDesc() { 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_CONDVAR_X(CargoPacket, days_in_transit, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_CARGO_AGE, 0, 0)), - SLE_CONDVAR_X(CargoPacket, days_in_transit, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_CARGO_AGE)), - SLE_VAR(CargoPacket, feeder_share, SLE_INT64), - SLE_CONDVAR(CargoPacket, source_type, SLE_UINT8, SLV_125, SL_MAX_VERSION), - SLE_CONDVAR(CargoPacket, source_id, SLE_UINT16, SLV_125, SL_MAX_VERSION), - - /* Used to be paid_for, but that got changed. */ - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_121), + 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_CONDVAR(CargoPacket, days_in_transit, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_MORE_CARGO_AGE), + SLE_CONDVAR(CargoPacket, days_in_transit, SLE_UINT16, SLV_MORE_CARGO_AGE, SL_MAX_VERSION), + SLE_VAR(CargoPacket, feeder_share, SLE_INT64), + SLE_CONDVAR(CargoPacket, source_type, SLE_UINT8, SLV_125, SL_MAX_VERSION), + SLE_CONDVAR(CargoPacket, source_id, SLE_UINT16, SLV_125, SL_MAX_VERSION), }; return _cargopacket_desc; } -/** - * Save the cargo packets. - */ -static void Save_CAPA() -{ - std::vector filtered_packet_desc = SlFilterObject(GetCargoPacketDesc()); - for (CargoPacket *cp : CargoPacket::Iterate()) { - SlSetArrayIndex(cp->index); - SlObjectSaveFiltered(cp, filtered_packet_desc); - } -} +struct CAPAChunkHandler : ChunkHandler { + CAPAChunkHandler() : ChunkHandler('CAPA', CH_TABLE) {} -/** - * Load the cargo packets. - */ -static void Load_CAPA() -{ - std::vector filtered_packet_desc = SlFilterObject(GetCargoPacketDesc()); - int index; - while ((index = SlIterateArray()) != -1) { - CargoPacket *cp = new (index) CargoPacket(); - SlObjectLoadFiltered(cp, filtered_packet_desc); - } -} + void Save() const override + { + SlTableHeader(GetCargoPacketDesc()); -/** - * Save cargo packet deferred payments. - */ -void Save_CPDP() -{ - SlSetLength(16 * _cargo_packet_deferred_payments.size()); - - for (auto &it : _cargo_packet_deferred_payments) { - SlWriteUint64(it.first); - SlWriteUint64(it.second); - } -} - -/** - * Load cargo packet deferred payments. - */ -void Load_CPDP() -{ - uint count = static_cast(SlGetFieldLength() / 16); - uint last_cargo_packet_id = std::numeric_limits::max(); - - for (uint i = 0; i < count; i++) { - uint64 k = SlReadUint64(); - uint64 v = SlReadUint64(); - _cargo_packet_deferred_payments[k] = v; - if (k >> 32 != last_cargo_packet_id) { - last_cargo_packet_id = k >> 32; - CargoPacket::Get(last_cargo_packet_id)->flags |= CargoPacket::CPF_HAS_DEFERRED_PAYMENT; + for (CargoPacket *cp : CargoPacket::Iterate()) { + SlSetArrayIndex(cp->index); + SlObject(cp, GetCargoPacketDesc()); } } -} + void Load() const override + { + const std::vector slt = SlCompatTableHeader(GetCargoPacketDesc(), _cargopacket_sl_compat); + int index; -/** Chunk handlers related to cargo packets. */ -static const ChunkHandler cargopacket_chunk_handlers[] = { - { 'CAPA', Save_CAPA, Load_CAPA, nullptr, nullptr, CH_ARRAY }, - { 'CPDP', Save_CPDP, Load_CPDP, nullptr, nullptr, CH_RIFF }, + while ((index = SlIterateArray()) != -1) { + CargoPacket *cp = new (index) CargoPacket(); + SlObject(cp, slt); + } + } +}; + +static const CAPAChunkHandler CAPA; +static const ChunkHandlerRef cargopacket_chunk_handlers[] = { + CAPA, }; extern const ChunkHandlerTable _cargopacket_chunk_handlers(cargopacket_chunk_handlers); + +} diff --git a/src/saveload/cheat_sl.cpp b/src/saveload/cheat_sl.cpp index d92f9945f3..abf67c3621 100644 --- a/src/saveload/cheat_sl.cpp +++ b/src/saveload/cheat_sl.cpp @@ -8,29 +8,15 @@ /** @file cheat_sl.cpp Code handling saving and loading of cheats */ #include "../stdafx.h" -#include "../cheat_type.h" -#include "../debug.h" #include "saveload.h" +#include "compat/cheat_sl_compat.h" -#include -#include +#include "../cheat_type.h" #include "../safeguards.h" -extern std::map _unknown_cheats; - -struct ExtraCheatNameDesc { - const char *name; - Cheat *cht; -}; - -static ExtraCheatNameDesc _extra_cheat_descs[] = { - { "inflation_cost", &_extra_cheats.inflation_cost }, - { "inflation_income", &_extra_cheats.inflation_income }, - { "station_rating", &_extra_cheats.station_rating }, - { "town_rating", &_extra_cheats.town_rating }, -}; +namespace upstream_sl { static const SaveLoad _cheats_desc[] = { SLE_VAR(Cheats, magic_bulldozer.been_used, SLE_BOOL), @@ -41,146 +27,60 @@ static const SaveLoad _cheats_desc[] = { SLE_VAR(Cheats, money.value, SLE_BOOL), SLE_VAR(Cheats, crossing_tunnels.been_used, SLE_BOOL), SLE_VAR(Cheats, crossing_tunnels.value, SLE_BOOL), - SLE_NULL(1), - SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS(). SLE_VAR(Cheats, no_jetcrash.been_used, SLE_BOOL), SLE_VAR(Cheats, no_jetcrash.value, SLE_BOOL), - SLE_NULL(1), - SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS(). SLE_VAR(Cheats, change_date.been_used, SLE_BOOL), SLE_VAR(Cheats, change_date.value, SLE_BOOL), SLE_VAR(Cheats, setup_prod.been_used, SLE_BOOL), SLE_VAR(Cheats, setup_prod.value, SLE_BOOL), - SLE_NULL(1), - SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS(). SLE_VAR(Cheats, edit_max_hl.been_used, SLE_BOOL), SLE_VAR(Cheats, edit_max_hl.value, SLE_BOOL), }; -/** - * Save the cheat values. - */ -static void Save_CHTS() -{ - SlSetLength(std::size(_cheats_desc)); - SlObject(&_cheats, _cheats_desc); -} -/** - * Load the cheat values. - */ -static void Load_CHTS() -{ - size_t count = SlGetFieldLength(); - std::vector slt; +struct CHTSChunkHandler : ChunkHandler { + CHTSChunkHandler() : ChunkHandler('CHTS', CH_TABLE) {} - /* Cheats were added over the years without a savegame bump. They are - * stored as 2 SLE_BOOLs per entry. "count" indicates how many SLE_BOOLs - * are stored for this savegame. So read only "count" SLE_BOOLs (and in - * result "count / 2" cheats). */ - for (auto &sld : _cheats_desc) { - count--; - slt.push_back(sld); + void Save() const override + { + SlTableHeader(_cheats_desc); - if (count == 0) break; + SlSetArrayIndex(0); + SlObject(&_cheats, _cheats_desc); } - SlObject(&_cheats, slt); -} + void Load() const override + { + std::vector slt = SlCompatTableHeader(_cheats_desc, _cheats_sl_compat); -/** - * Load the extra cheat values. - */ -static void Load_CHTX() -{ - struct CheatsExtLoad { - char name[256]; - Cheat cht; - }; + if (IsSavegameVersionBefore(SLV_TABLE_CHUNKS)) { + size_t count = SlGetFieldLength(); + std::vector oslt; - static const SaveLoad _cheats_ext_load_desc[] = { - SLE_STR(CheatsExtLoad, name, SLE_STRB, 256), - SLE_VAR(CheatsExtLoad, cht.been_used, SLE_BOOL), - SLE_VAR(CheatsExtLoad, cht.value, SLE_BOOL), - }; + /* Cheats were added over the years without a savegame bump. They are + * stored as 2 SLE_BOOLs per entry. "count" indicates how many SLE_BOOLs + * are stored for this savegame. So read only "count" SLE_BOOLs (and in + * result "count / 2" cheats). */ + for (auto &sld : slt) { + count--; + oslt.push_back(sld); - CheatsExtLoad current_cheat; - - uint32 chunk_flags = SlReadUint32(); - // flags are not in use yet, reserve for future expansion - if (chunk_flags != 0) SlErrorCorruptFmt("CHTX chunk: unknown chunk header flags: 0x%X", chunk_flags); - - uint32 cheat_count = SlReadUint32(); - for (uint32 i = 0; i < cheat_count; i++) { - SlObject(¤t_cheat, _cheats_ext_load_desc); - - bool found = false; - for (uint j = 0; j < lengthof(_extra_cheat_descs); j++) { - const ExtraCheatNameDesc &desc = _extra_cheat_descs[j]; - if (strcmp(desc.name, current_cheat.name) == 0) { - *(desc.cht) = current_cheat.cht; - found = true; - break; + if (count == 0) break; } + slt = oslt; } - if (!found) { - DEBUG(sl, 1, "CHTX chunk: Could not find cheat: '%s'", current_cheat.name); - _unknown_cheats[current_cheat.name] = current_cheat.cht; - } + + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; + SlObject(&_cheats, slt); + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many CHTS entries"); } -} - -/** - * Save the extra cheat values. - */ -static void Save_CHTX() -{ - struct CheatsExtSave { - const char *name; - Cheat cht; - }; - - static const SaveLoad _cheats_ext_save_desc[] = { - SLE_STR(CheatsExtSave, name, SLE_STR, 0), - SLE_VAR(CheatsExtSave, cht.been_used, SLE_BOOL), - SLE_VAR(CheatsExtSave, cht.value, SLE_BOOL), - }; - - SlAutolength([](void *) { - SlWriteUint32(0); // flags - SlWriteUint32((uint32)(lengthof(_extra_cheat_descs) + _unknown_cheats.size())); // cheat count - - for (uint j = 0; j < lengthof(_extra_cheat_descs); j++) { - CheatsExtSave save = { _extra_cheat_descs[j].name, *(_extra_cheat_descs[j].cht) }; - SlObject(&save, _cheats_ext_save_desc); - } - for (const auto &iter : _unknown_cheats) { - CheatsExtSave save = { iter.first.c_str(), iter.second }; - SlObject(&save, _cheats_ext_save_desc); - } - }, nullptr); -} - -/** - * Internal structure used in SaveSettingsPatx() and SaveSettingsPlyx() - */ -struct SettingsExtSave { - uint32 flags; - const char *name; - uint32 setting_length; }; -static const SaveLoad _settings_ext_save_desc[] = { - SLE_VAR(SettingsExtSave, flags, SLE_UINT32), - SLE_STR(SettingsExtSave, name, SLE_STR, 0), - SLE_VAR(SettingsExtSave, setting_length, SLE_UINT32), -}; - - -/** Chunk handlers related to cheats. */ -static const ChunkHandler cheat_chunk_handlers[] = { - { 'CHTS', Save_CHTS, Load_CHTS, nullptr, nullptr, CH_RIFF }, - { 'CHTX', Save_CHTX, Load_CHTX, nullptr, nullptr, CH_RIFF }, +static const CHTSChunkHandler CHTS; +static const ChunkHandlerRef cheat_chunk_handlers[] = { + CHTS, }; extern const ChunkHandlerTable _cheat_chunk_handlers(cheat_chunk_handlers); + +} diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 9f3118470c..3506356832 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -8,241 +8,227 @@ /** @file company_sl.cpp Code handling saving and loading of company data */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/company_sl_compat.h" + #include "../company_func.h" #include "../company_manager_face.h" #include "../fios.h" #include "../tunnelbridge_map.h" #include "../tunnelbridge.h" #include "../station_base.h" -#include "../settings_func.h" #include "../strings_func.h" -#include "../network/network.h" -#include "../network/network_func.h" -#include "../network/network_server.h" -#include "../3rdparty/randombytes/randombytes.h" -#include "../3rdparty/monocypher/monocypher.h" - -#include "saveload.h" -#include "saveload_buffer.h" #include "table/strings.h" #include "../safeguards.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 colour - * - 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; +void SetDefaultCompanySettings(CompanyID cid); - if (HasBit(face, 31)) SetBit(ge, GENDER_FEMALE); - if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) SetBit(ge, ETHNICITY_BLACK); +namespace upstream_sl { - 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)); +/* We do need to read this single value, as the bigger it gets, the more data is stored */ +struct CompanyOldAI { + uint8 num_build_rec; +}; - 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, std::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); +class SlCompanyOldAIBuildRec : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = {{}}; // Needed to keep DefaultSaveLoadHandler happy. + inline const static SaveLoadCompatTable compat_description = _company_old_ai_buildrec_compat; - 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); - } + SaveLoadTable GetDescription() const override { return {}; } - 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; -} - -/** Rebuilding of company statistics after loading a savegame. */ -void AfterLoadCompanyStats() -{ - /* Reset infrastructure statistics to zero. */ - for (Company *c : Company::Iterate()) MemSetT(&c->infrastructure, 0); - - /* Collect airport count. */ - for (const Station *st : Station::Iterate()) { - if ((st->facilities & FACIL_AIRPORT) && Company::IsValidID(st->owner)) { - Company::Get(st->owner)->infrastructure.airport++; + void Load(CompanyOldAI *old_ai) const override + { + for (int i = 0; i != old_ai->num_build_rec; i++) { + SlObject(nullptr, this->GetLoadDescription()); } } - Company *c; - for (TileIndex tile = 0; tile < MapSize(); tile++) { - switch (GetTileType(tile)) { - case MP_RAILWAY: - c = Company::GetIfValid(GetTileOwner(tile)); - if (c != nullptr) { - uint pieces = 1; - if (IsPlainRail(tile)) { - TrackBits bits = GetTrackBits(tile); - if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) { - c->infrastructure.rail[GetSecondaryRailType(tile)]++; - } else { - pieces = CountBits(bits); - if (TracksOverlap(bits)) pieces *= pieces; - } - } - c->infrastructure.rail[GetRailType(tile)] += pieces; + void LoadCheck(CompanyOldAI *old_ai) const override { this->Load(old_ai); } +}; - if (HasSignals(tile)) c->infrastructure.signal += CountBits(GetPresentSignals(tile)); - } - break; +class SlCompanyOldAI : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_CONDVAR(CompanyOldAI, num_build_rec, SLE_UINT8, SL_MIN_VERSION, SLV_107), + SLEG_STRUCTLIST("buildrec", SlCompanyOldAIBuildRec), + }; + inline const static SaveLoadCompatTable compat_description = _company_old_ai_compat; - case MP_ROAD: { - if (IsLevelCrossing(tile)) { - c = Company::GetIfValid(GetTileOwner(tile)); - if (c != nullptr) c->infrastructure.rail[GetRailType(tile)] += LEVELCROSSING_TRACKBIT_FACTOR; - } + void Load(CompanyProperties *c) const override + { + if (!c->is_ai) return; - /* Iterate all present road types as each can have a different owner. */ - for (RoadTramType rtt : _roadtramtypes) { - RoadType rt = GetRoadType(tile, rtt); - if (rt == INVALID_ROADTYPE) continue; - c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rtt)); - /* A level crossings and depots have two road bits. */ - if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rtt)) : 2; + CompanyOldAI old_ai; + SlObject(&old_ai, this->GetLoadDescription()); + } + + void LoadCheck(CompanyProperties *c) const override { this->Load(c); } +}; + +class SlCompanySettings : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + /* Engine renewal settings */ + SLE_CONDREF(CompanyProperties, engine_renew_list, REF_ENGINE_RENEWS, SLV_19, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, settings.engine_renew, SLE_BOOL, SLV_16, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, settings.engine_renew_months, SLE_INT16, SLV_16, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, settings.engine_renew_money, SLE_UINT32, SLV_16, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, settings.renew_keep_length, SLE_BOOL, SLV_2, SL_MAX_VERSION), + + /* Default vehicle settings */ + SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_ispercent, SLE_BOOL, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_trains, SLE_UINT16, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_roadveh, SLE_UINT16, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_aircraft, SLE_UINT16, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_ships, SLE_UINT16, SLV_120, SL_MAX_VERSION), + }; + inline const static SaveLoadCompatTable compat_description = _company_settings_compat; + + void Save(CompanyProperties *c) const override + { + SlObject(c, this->GetDescription()); + } + + void Load(CompanyProperties *c) const override + { + SlObject(c, this->GetLoadDescription()); + } + + void FixPointers(CompanyProperties *c) const override + { + SlObject(c, this->GetDescription()); + } + + void LoadCheck(CompanyProperties *c) const override { this->Load(c); } +}; + +class SlCompanyEconomy : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_CONDVAR(CompanyEconomyEntry, income, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), + SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, SLV_2, SL_MAX_VERSION), + SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), + SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, SLV_2, SL_MAX_VERSION), + SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), + SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, SLV_2, SL_MAX_VERSION), + + SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, SL_MIN_VERSION, SLV_170), + SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, SLV_170, SLV_EXTEND_CARGOTYPES), + SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), + SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), + }; + inline const static SaveLoadCompatTable compat_description = _company_economy_compat; + + void Save(CompanyProperties *c) const override + { + SlObject(&c->cur_economy, this->GetDescription()); + } + + void Load(CompanyProperties *c) const override + { + SlObject(&c->cur_economy, this->GetLoadDescription()); + } + + void FixPointers(CompanyProperties *c) const override + { + SlObject(&c->cur_economy, this->GetDescription()); + } + + void LoadCheck(CompanyProperties *c) const override { this->Load(c); } +}; + +class SlCompanyOldEconomy : public SlCompanyEconomy { +public: + void Save(CompanyProperties *c) const override + { + SlSetStructListLength(c->num_valid_stat_ent); + for (int i = 0; i < c->num_valid_stat_ent; i++) { + SlObject(&c->old_economy[i], this->GetDescription()); + } + } + + void Load(CompanyProperties *c) const override + { + if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { + c->num_valid_stat_ent = (uint8)SlGetStructListLength(UINT8_MAX); + } + if (c->num_valid_stat_ent > lengthof(c->old_economy)) SlErrorCorrupt("Too many old economy entries"); + + for (int i = 0; i < c->num_valid_stat_ent; i++) { + SlObject(&c->old_economy[i], this->GetLoadDescription()); + } + } + + void LoadCheck(CompanyProperties *c) const override { this->Load(c); } +}; + +class SlCompanyLiveries : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_CONDVAR(Livery, in_use, SLE_UINT8, SLV_34, SL_MAX_VERSION), + SLE_CONDVAR(Livery, colour1, SLE_UINT8, SLV_34, SL_MAX_VERSION), + SLE_CONDVAR(Livery, colour2, SLE_UINT8, SLV_34, SL_MAX_VERSION), + }; + inline const static SaveLoadCompatTable compat_description = _company_liveries_compat; + + /** + * Get the number of liveries used by this savegame version. + * @return The number of liveries used by this savegame version. + */ + size_t GetNumLiveries() const + { + if (IsSavegameVersionBefore(SLV_63)) return LS_END - 4; + if (IsSavegameVersionBefore(SLV_85)) return LS_END - 2; + if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return LS_END; + /* Read from the savegame how long the list is. */ + return SlGetStructListLength(LS_END); + } + + void Save(CompanyProperties *c) const override + { + SlSetStructListLength(LS_END); + for (int i = 0; i < LS_END; i++) { + SlObject(&c->livery[i], this->GetDescription()); + } + } + + void Load(CompanyProperties *c) const override + { + size_t num_liveries = this->GetNumLiveries(); + bool update_in_use = IsSavegameVersionBefore(SLV_GROUP_LIVERIES); + + for (size_t i = 0; i < num_liveries; i++) { + SlObject(&c->livery[i], this->GetLoadDescription()); + if (update_in_use && i != LS_DEFAULT) { + if (c->livery[i].in_use == 0) { + c->livery[i].colour1 = c->livery[LS_DEFAULT].colour1; + c->livery[i].colour2 = c->livery[LS_DEFAULT].colour2; + } else { + c->livery[i].in_use = 3; } - break; } + } - case MP_STATION: - c = Company::GetIfValid(GetTileOwner(tile)); - if (c != nullptr && GetStationType(tile) != STATION_AIRPORT && !IsBuoy(tile)) c->infrastructure.station++; + if (IsSavegameVersionBefore(SLV_85)) { + /* 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]; + } - switch (GetStationType(tile)) { - case STATION_RAIL: - case STATION_WAYPOINT: - if (c != nullptr && !IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]++; - break; - - case STATION_BUS: - case STATION_TRUCK: - case STATION_ROADWAYPOINT: { - /* Iterate all present road types as each can have a different owner. */ - for (RoadTramType rtt : _roadtramtypes) { - RoadType rt = GetRoadType(tile, rtt); - if (rt == INVALID_ROADTYPE) continue; - c = Company::GetIfValid(GetRoadOwner(tile, rtt)); - if (c != nullptr) c->infrastructure.road[rt] += 2; // A road stop has two road bits. - } - break; - } - - case STATION_DOCK: - case STATION_BUOY: - if (GetWaterClass(tile) == WATER_CLASS_CANAL) { - if (c != nullptr) c->infrastructure.water++; - } - break; - - default: - break; - } - break; - - case MP_WATER: - if (IsShipDepot(tile) || IsLock(tile)) { - c = Company::GetIfValid(GetTileOwner(tile)); - if (c != nullptr) { - if (IsShipDepot(tile)) c->infrastructure.water += LOCK_DEPOT_TILE_FACTOR; - if (IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE) { - /* The middle tile specifies the owner of the lock. */ - c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // the middle tile specifies the owner of the - break; // do not count the middle tile as canal - } - } - } - FALLTHROUGH; - - case MP_OBJECT: - if (GetWaterClass(tile) == WATER_CLASS_CANAL) { - c = Company::GetIfValid(GetTileOwner(tile)); - if (c != nullptr) c->infrastructure.water++; - } - break; - - case MP_TUNNELBRIDGE: { - /* Only count the tunnel/bridge if we're on the western end tile. */ - if (GetTunnelBridgeDirection(tile) < DIAGDIR_SW) { - TileIndex other_end = GetOtherTunnelBridgeEnd(tile); - - /* Count each tunnel/bridge TUNNELBRIDGE_TRACKBIT_FACTOR times to simulate - * the higher structural maintenance needs, and don't forget the end tiles. */ - const uint middle_len = GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR; - - switch (GetTunnelBridgeTransportType(tile)) { - case TRANSPORT_RAIL: - AddRailTunnelBridgeInfrastructure(tile, other_end); - break; - - case TRANSPORT_ROAD: { - AddRoadTunnelBridgeInfrastructure(tile, other_end); - break; - } - - case TRANSPORT_WATER: - c = Company::GetIfValid(GetTileOwner(tile)); - if (c != nullptr) c->infrastructure.water += middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR); - break; - - default: - break; - } - } - break; - } - - default: - break; + if (IsSavegameVersionBefore(SLV_63)) { + /* 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]; } } -} - + void LoadCheck(CompanyProperties *c) const override { this->Load(c); } +}; /* Save/load of companies */ static const SaveLoad _company_desc[] = { @@ -265,11 +251,8 @@ static const SaveLoad _company_desc[] = { SLE_VAR(CompanyProperties, colour, SLE_UINT8), SLE_VAR(CompanyProperties, money_fraction, SLE_UINT8), - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_58), ///< avail_railtypes SLE_VAR(CompanyProperties, block_preview, SLE_UINT8), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_94), ///< cargo_types - SLE_CONDNULL(4, SLV_94, SLV_170), ///< cargo_types SLE_CONDVAR(CompanyProperties, location_of_HQ, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), SLE_CONDVAR(CompanyProperties, location_of_HQ, SLE_UINT32, SLV_6, SL_MAX_VERSION), SLE_CONDVAR(CompanyProperties, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), @@ -279,11 +262,9 @@ static const SaveLoad _company_desc[] = { SLE_ARR(CompanyProperties, share_owners, SLE_UINT8, 4), - SLE_VAR(CompanyProperties, num_valid_stat_ent, SLE_UINT8), + SLE_CONDVAR(CompanyProperties, num_valid_stat_ent, SLE_UINT8, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), SLE_VAR(CompanyProperties, months_of_bankruptcy, SLE_UINT8), - SLE_CONDVAR_X(CompanyProperties, bankrupt_last_asked, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BANKRUPTCY_EXTRA)), - SLE_CONDVAR_X(CompanyProperties, bankrupt_flags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BANKRUPTCY_EXTRA, 2)), SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_UINT16, SLV_104, SL_MAX_VERSION), SLE_VAR(CompanyProperties, bankrupt_timeout, SLE_INT16), @@ -292,412 +273,91 @@ static const SaveLoad _company_desc[] = { /* yearly expenses was changed to 64-bit in savegame version 2. */ SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, SL_MIN_VERSION, SLV_2), - SLE_CONDARR_X(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 13, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING, 0, 0)), - SLE_CONDARR_X(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 15, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)), + SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 13, SLV_2, SL_MAX_VERSION), SLE_CONDVAR(CompanyProperties, is_ai, SLE_BOOL, SLV_2, SL_MAX_VERSION), - SLE_CONDNULL(1, SLV_107, SLV_112), ///< is_noai - SLE_CONDNULL(1, SLV_4, SLV_100), SLE_CONDVAR(CompanyProperties, terraform_limit, SLE_UINT32, SLV_156, SL_MAX_VERSION), SLE_CONDVAR(CompanyProperties, clear_limit, SLE_UINT32, SLV_156, SL_MAX_VERSION), SLE_CONDVAR(CompanyProperties, tree_limit, SLE_UINT32, SLV_175, SL_MAX_VERSION), - SLE_CONDVAR_X(CompanyProperties, purchase_land_limit, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BUY_LAND_RATE_LIMIT)), - SLE_CONDVAR_X(CompanyProperties, build_object_limit, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BUILD_OBJECT_RATE_LIMIT)), + SLEG_STRUCT("settings", SlCompanySettings), + SLEG_CONDSTRUCT("old_ai", SlCompanyOldAI, SL_MIN_VERSION, SLV_107), + SLEG_STRUCT("cur_economy", SlCompanyEconomy), + SLEG_STRUCTLIST("old_economy", SlCompanyOldEconomy), + SLEG_CONDSTRUCTLIST("liveries", SlCompanyLiveries, SLV_34, SL_MAX_VERSION), }; -static const SaveLoad _company_settings_desc[] = { - /* Engine renewal settings */ - SLE_CONDNULL(512, SLV_16, SLV_19), - SLE_CONDREF(Company, engine_renew_list, REF_ENGINE_RENEWS, SLV_19, SL_MAX_VERSION), - SLE_CONDVAR(Company, settings.engine_renew, SLE_BOOL, SLV_16, SL_MAX_VERSION), - SLE_CONDVAR(Company, settings.engine_renew_months, SLE_INT16, SLV_16, SL_MAX_VERSION), - SLE_CONDVAR(Company, settings.engine_renew_money, SLE_UINT32, SLV_16, SL_MAX_VERSION), - SLE_CONDVAR(Company, settings.renew_keep_length, SLE_BOOL, SLV_2, SL_MAX_VERSION), +struct PLYRChunkHandler : ChunkHandler { + PLYRChunkHandler() : ChunkHandler('PLYR', CH_TABLE) {} - /* Default vehicle settings */ - SLE_CONDVAR(Company, settings.vehicle.servint_ispercent, SLE_BOOL, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR(Company, settings.vehicle.servint_trains, SLE_UINT16, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR(Company, settings.vehicle.servint_roadveh, SLE_UINT16, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR(Company, settings.vehicle.servint_aircraft, SLE_UINT16, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR(Company, settings.vehicle.servint_ships, SLE_UINT16, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR_X(Company, settings.vehicle.auto_timetable_by_default, SLE_BOOL, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 2, 2)), + void Save() const override + { + SlTableHeader(_company_desc); - SLE_CONDNULL(63, SLV_2, SLV_144), // old reserved space -}; - -static const SaveLoad _company_settings_skip_desc[] = { - /* Engine renewal settings */ - SLE_CONDNULL(512, SLV_16, SLV_19), - SLE_CONDNULL(2, SLV_19, SLV_69), // engine_renew_list - SLE_CONDNULL(4, SLV_69, SL_MAX_VERSION), // engine_renew_list - SLE_CONDNULL(1, SLV_16, SL_MAX_VERSION), // settings.engine_renew - SLE_CONDNULL(2, SLV_16, SL_MAX_VERSION), // settings.engine_renew_months - SLE_CONDNULL(4, SLV_16, SL_MAX_VERSION), // settings.engine_renew_money - SLE_CONDNULL(1, SLV_2, SL_MAX_VERSION), // settings.renew_keep_length - - /* Default vehicle settings */ - SLE_CONDNULL(1, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_ispercent - SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_trains - SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_roadveh - SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_aircraft - SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_ships - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 2, 2)), // settings.vehicle.auto_timetable_by_default - - SLE_CONDNULL(63, SLV_2, SLV_144), // old reserved space -}; - -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, SL_MIN_VERSION, SLV_2), - SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, SLV_2, SL_MAX_VERSION), - SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), - SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, SLV_2, SL_MAX_VERSION), - SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), - SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, SLV_2, SL_MAX_VERSION), - - SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, SL_MIN_VERSION, SLV_170), - SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, SLV_170, SLV_EXTEND_CARGOTYPES), - SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), - SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), -}; - -/* We do need to read this single value, as the bigger it gets, the more data is stored */ -struct CompanyOldAI { - uint8 num_build_rec; -}; - -static const SaveLoad _company_ai_desc[] = { - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_13), - SLE_CONDNULL(4, SLV_13, SLV_107), - SLE_CONDNULL(8, SL_MIN_VERSION, SLV_107), - SLE_CONDVAR(CompanyOldAI, num_build_rec, SLE_UINT8, SL_MIN_VERSION, SLV_107), - SLE_CONDNULL(3, SL_MIN_VERSION, SLV_107), - - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), - SLE_CONDNULL(4, SLV_6, SLV_107), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), - SLE_CONDNULL(4, SLV_6, SLV_107), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107), - - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), - SLE_CONDNULL(4, SLV_6, SLV_107), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), - SLE_CONDNULL(4, SLV_6, SLV_107), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107), - - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_69), - SLE_CONDNULL(4, SLV_69, SLV_107), - - SLE_CONDNULL(18, SL_MIN_VERSION, SLV_107), - SLE_CONDNULL(20, SL_MIN_VERSION, SLV_107), - SLE_CONDNULL(32, SL_MIN_VERSION, SLV_107), - - SLE_CONDNULL(64, SLV_2, SLV_107), -}; - -static const SaveLoad _company_ai_build_rec_desc[] = { - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), - SLE_CONDNULL(4, SLV_6, SLV_107), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), - SLE_CONDNULL(4, SLV_6, SLV_107), - SLE_CONDNULL(8, SL_MIN_VERSION, SLV_107), -}; - -static const SaveLoad _company_livery_desc[] = { - SLE_CONDVAR(Livery, in_use, SLE_UINT8, SLV_34, SL_MAX_VERSION), - SLE_CONDVAR(Livery, colour1, SLE_UINT8, SLV_34, SL_MAX_VERSION), - SLE_CONDVAR(Livery, colour2, SLE_UINT8, SLV_34, SL_MAX_VERSION), -}; - -static void SaveLoad_PLYR_common(Company *c, CompanyProperties *cprops) -{ - int i; - - SlObject(cprops, _company_desc); - if (c != nullptr) { - SlObject(c, _company_settings_desc); - } else { - char nothing; - SlObject(¬hing, _company_settings_skip_desc); - } - - /* Keep backwards compatible for savegames, so load the old AI block */ - if (IsSavegameVersionBefore(SLV_107) && cprops->is_ai) { - CompanyOldAI old_ai; - char nothing; - - SlObject(&old_ai, _company_ai_desc); - for (i = 0; i != old_ai.num_build_rec; i++) { - SlObject(¬hing, _company_ai_build_rec_desc); + for (Company *c : Company::Iterate()) { + SlSetArrayIndex(c->index); + SlObject(c, _company_desc); } } - /* Write economy */ - SlObject(&cprops->cur_economy, _company_economy_desc); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_company_desc, _company_sl_compat); - /* Write old economy entries. */ - if (cprops->num_valid_stat_ent > lengthof(cprops->old_economy)) SlErrorCorrupt("Too many old economy entries"); - for (i = 0; i < cprops->num_valid_stat_ent; i++) { - SlObject(&cprops->old_economy[i], _company_economy_desc); + int index; + while ((index = SlIterateArray()) != -1) { + Company *c = new (index) Company(); + SetDefaultCompanySettings(c->index); + SlObject((CompanyProperties *)c, slt); + _company_colours[index] = (Colours)c->colour; + } } - /* Write each livery entry. */ - int num_liveries = IsSavegameVersionBefore(SLV_63) ? LS_END - 4 : (IsSavegameVersionBefore(SLV_85) ? LS_END - 2: LS_END); - bool update_in_use = IsSavegameVersionBefore(SLV_GROUP_LIVERIES); - if (c != nullptr) { - for (i = 0; i < num_liveries; i++) { - SlObject(&c->livery[i], _company_livery_desc); - if (update_in_use && i != LS_DEFAULT) { - if (c->livery[i].in_use == 0) { - c->livery[i].colour1 = c->livery[LS_DEFAULT].colour1; - c->livery[i].colour2 = c->livery[LS_DEFAULT].colour2; - } else { - c->livery[i].in_use = 3; + + void LoadCheck(size_t) const override + { + const std::vector slt = SlCompatTableHeader(_company_desc, _company_sl_compat); + + int index; + while ((index = SlIterateArray()) != -1) { + CompanyProperties *cprops = new CompanyProperties(); + SlObject(cprops, slt); + + /* We do not load old custom names */ + if (IsSavegameVersionBefore(SLV_84)) { + if (GetStringTab(cprops->name_1) == TEXT_TAB_OLD_CUSTOM) { + cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; + } + + if (GetStringTab(cprops->president_name_1) == TEXT_TAB_OLD_CUSTOM) { + cprops->president_name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; } } - } - 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]; - } - } else { - /* Skip liveries */ - Livery dummy_livery; - for (i = 0; i < num_liveries; i++) { - SlObject(&dummy_livery, _company_livery_desc); - } - } -} - -static void SaveLoad_PLYR(Company *c) -{ - SaveLoad_PLYR_common(c, c); -} - -static void Save_PLYR() -{ - for (Company *c : Company::Iterate()) { - SlSetArrayIndex(c->index); - SlAutolength((AutolengthProc*)SaveLoad_PLYR, c); - } -} - -static void Load_PLYR() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Company *c = new (index) Company(); - SetDefaultCompanySettings(c->index); - SaveLoad_PLYR(c); - _company_colours[index] = (Colours)c->colour; - - // settings moved from game settings to company settings - if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 2)) { - c->settings.auto_timetable_separation_rate = _settings_game.order.old_timetable_separation_rate; - } - if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 3)) { - c->settings.vehicle.auto_separation_by_default = _settings_game.order.old_timetable_separation; - } - } -} - -static void Check_PLYR() -{ - int index; - while ((index = SlIterateArray()) != -1) { - CompanyProperties *cprops = new CompanyProperties(); - SaveLoad_PLYR_common(nullptr, cprops); - - /* We do not load old custom names */ - if (IsSavegameVersionBefore(SLV_84)) { - if (GetStringTab(cprops->name_1) == TEXT_TAB_OLD_CUSTOM) { - cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; - } - - if (GetStringTab(cprops->president_name_1) == TEXT_TAB_OLD_CUSTOM) { - cprops->president_name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; - } - } - - if (cprops->name.empty() && !IsInsideMM(cprops->name_1, SPECSTR_COMPANY_NAME_START, SPECSTR_COMPANY_NAME_LAST + 1) && + if (cprops->name.empty() && !IsInsideMM(cprops->name_1, SPECSTR_COMPANY_NAME_START, SPECSTR_COMPANY_NAME_LAST + 1) && cprops->name_1 != STR_GAME_SAVELOAD_NOT_AVAILABLE && cprops->name_1 != STR_SV_UNNAMED && cprops->name_1 != SPECSTR_ANDCO_NAME && cprops->name_1 != SPECSTR_PRESIDENT_NAME && cprops->name_1 != SPECSTR_SILLY_NAME) { - cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; - } - - if (!_load_check_data.companies.Insert(index, cprops)) delete cprops; - } -} - -static void Ptrs_PLYR() -{ - for (Company *c : Company::Iterate()) { - SlObject(c, _company_settings_desc); - } -} - -extern void LoadSettingsPlyx(bool skip); -extern void SaveSettingsPlyx(); - -static void Load_PLYX() -{ - LoadSettingsPlyx(false); -} - -static void Check_PLYX() -{ - LoadSettingsPlyx(true); -} - -static void Save_PLYX() -{ - SaveSettingsPlyx(); -} - -static void Load_PLYP() -{ - size_t size = SlGetFieldLength(); - CompanyMask invalid_mask = 0; - if (SlXvIsFeaturePresent(XSLFI_COMPANY_PW, 2)) { - if (size <= 2) return; - invalid_mask = SlReadUint16(); - size -= 2; - } - if (size <= 16 + 24 + 16 || (_networking && !_network_server)) { - SlSkipBytes(size); - return; - } - if (!_network_server) { - extern CompanyMask _saved_PLYP_invalid_mask; - extern std::vector _saved_PLYP_data; - - _saved_PLYP_invalid_mask = invalid_mask; - _saved_PLYP_data.resize(size); - ReadBuffer::GetCurrent()->CopyBytes(_saved_PLYP_data.data(), _saved_PLYP_data.size()); - return; - } - - uint8 token[16]; - ReadBuffer::GetCurrent()->CopyBytes(token, 16); - if (memcmp(token, _network_company_password_storage_token, 16) != 0) { - DEBUG(sl, 2, "Skipping encrypted company passwords"); - SlSkipBytes(size - 16); - return; - } - - uint8 nonce[24]; - uint8 mac[16]; - ReadBuffer::GetCurrent()->CopyBytes(nonce, 24); - ReadBuffer::GetCurrent()->CopyBytes(mac, 16); - - std::vector buffer(size - 16 - 24 - 16); - ReadBuffer::GetCurrent()->CopyBytes(buffer.data(), buffer.size()); - - if (crypto_unlock(buffer.data(), _network_company_password_storage_key, nonce, mac, buffer.data(), buffer.size()) == 0) { - SlLoadFromBuffer(buffer.data(), buffer.size(), [invalid_mask]() { - _network_company_server_id.resize(SlReadUint32()); - ReadBuffer::GetCurrent()->CopyBytes((uint8 *)_network_company_server_id.data(), _network_company_server_id.size()); - - while (true) { - uint16 cid = SlReadUint16(); - if (cid >= MAX_COMPANIES) break; - std::string password; - password.resize(SlReadUint32()); - ReadBuffer::GetCurrent()->CopyBytes((uint8 *)password.data(), password.size()); - if (!HasBit(invalid_mask, cid)) { - NetworkServerSetCompanyPassword((CompanyID)cid, password, true); - } + cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; } - ReadBuffer::GetCurrent()->SkipBytes(SlReadByte()); // Skip padding - }); - DEBUG(sl, 2, "Decrypted company passwords"); - } else { - DEBUG(sl, 2, "Failed to decrypt company passwords"); - } -} - -static void Save_PLYP() -{ - if ((_networking && !_network_server) || IsNetworkServerSave()) { - SlSetLength(0); - return; - } - if (!_network_server) { - extern CompanyMask _saved_PLYP_invalid_mask; - extern std::vector _saved_PLYP_data; - - if (_saved_PLYP_data.empty()) { - SlSetLength(0); - } else { - SlSetLength(2 + _saved_PLYP_data.size()); - SlWriteUint16(_saved_PLYP_invalid_mask); - MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)_saved_PLYP_data.data(), _saved_PLYP_data.size()); + if (!_load_check_data.companies.Insert(index, cprops)) delete cprops; } - return; } - uint8 nonce[24]; /* Use only once per key: random */ - if (randombytes(nonce, 24) < 0) { - /* Can't get a random nonce, just give up */ - SlSetLength(0); - return; + void FixPointers() const override + { + for (Company *c : Company::Iterate()) { + SlObject((CompanyProperties *)c, _company_desc); + } } +}; - std::vector buffer = SlSaveToVector([](void *) { - SlWriteUint32((uint32)_network_company_server_id.size()); - MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)_network_company_server_id.data(), _network_company_server_id.size()); - - for (const Company *c : Company::Iterate()) { - SlWriteUint16(c->index); - - const std::string &password = _network_company_states[c->index].password; - SlWriteUint32((uint32)password.size()); - MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)password.data(), password.size()); - } - - SlWriteUint16(0xFFFF); - - /* Add some random length padding to not make it too obvious from the length whether passwords are set or not */ - uint8 padding[256]; - if (randombytes(padding, 256) >= 0) { - SlWriteByte(padding[0]); - MemoryDumper::GetCurrent()->CopyBytes(padding + 1, padding[0]); - } else { - SlWriteByte(0); - } - }, nullptr); - - - uint8 mac[16]; /* Message authentication code */ - - /* Encrypt in place */ - crypto_lock(mac, buffer.data(), _network_company_password_storage_key, nonce, buffer.data(), buffer.size()); - - SlSetLength(2 + 16 + 24 + 16 + buffer.size()); - SlWriteUint16(0); // Invalid mask - MemoryDumper::GetCurrent()->CopyBytes(_network_company_password_storage_token, 16); - MemoryDumper::GetCurrent()->CopyBytes(nonce, 24); - MemoryDumper::GetCurrent()->CopyBytes(mac, 16); - MemoryDumper::GetCurrent()->CopyBytes(buffer.data(), buffer.size()); -} - -static const ChunkHandler company_chunk_handlers[] = { - { 'PLYR', Save_PLYR, Load_PLYR, Ptrs_PLYR, Check_PLYR, CH_ARRAY }, - { 'PLYX', Save_PLYX, Load_PLYX, nullptr, Check_PLYX, CH_RIFF }, - { 'PLYP', Save_PLYP, Load_PLYP, nullptr, nullptr, CH_RIFF }, +static const PLYRChunkHandler PLYR; +static const ChunkHandlerRef company_chunk_handlers[] = { + PLYR, }; extern const ChunkHandlerTable _company_chunk_handlers(company_chunk_handlers); + +} diff --git a/src/saveload/upstream/compat/CMakeLists.txt b/src/saveload/compat/CMakeLists.txt similarity index 100% rename from src/saveload/upstream/compat/CMakeLists.txt rename to src/saveload/compat/CMakeLists.txt diff --git a/src/saveload/upstream/compat/ai_sl_compat.h b/src/saveload/compat/ai_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/ai_sl_compat.h rename to src/saveload/compat/ai_sl_compat.h diff --git a/src/saveload/upstream/compat/animated_tile_sl_compat.h b/src/saveload/compat/animated_tile_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/animated_tile_sl_compat.h rename to src/saveload/compat/animated_tile_sl_compat.h diff --git a/src/saveload/upstream/compat/autoreplace_sl_compat.h b/src/saveload/compat/autoreplace_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/autoreplace_sl_compat.h rename to src/saveload/compat/autoreplace_sl_compat.h diff --git a/src/saveload/upstream/compat/cargomonitor_sl_compat.h b/src/saveload/compat/cargomonitor_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/cargomonitor_sl_compat.h rename to src/saveload/compat/cargomonitor_sl_compat.h diff --git a/src/saveload/upstream/compat/cargopacket_sl_compat.h b/src/saveload/compat/cargopacket_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/cargopacket_sl_compat.h rename to src/saveload/compat/cargopacket_sl_compat.h diff --git a/src/saveload/upstream/compat/cheat_sl_compat.h b/src/saveload/compat/cheat_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/cheat_sl_compat.h rename to src/saveload/compat/cheat_sl_compat.h diff --git a/src/saveload/upstream/compat/company_sl_compat.h b/src/saveload/compat/company_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/company_sl_compat.h rename to src/saveload/compat/company_sl_compat.h diff --git a/src/saveload/upstream/compat/depot_sl_compat.h b/src/saveload/compat/depot_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/depot_sl_compat.h rename to src/saveload/compat/depot_sl_compat.h diff --git a/src/saveload/upstream/compat/economy_sl_compat.h b/src/saveload/compat/economy_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/economy_sl_compat.h rename to src/saveload/compat/economy_sl_compat.h diff --git a/src/saveload/upstream/compat/engine_sl_compat.h b/src/saveload/compat/engine_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/engine_sl_compat.h rename to src/saveload/compat/engine_sl_compat.h diff --git a/src/saveload/upstream/compat/game_sl_compat.h b/src/saveload/compat/game_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/game_sl_compat.h rename to src/saveload/compat/game_sl_compat.h diff --git a/src/saveload/upstream/compat/gamelog_sl_compat.h b/src/saveload/compat/gamelog_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/gamelog_sl_compat.h rename to src/saveload/compat/gamelog_sl_compat.h diff --git a/src/saveload/upstream/compat/goal_sl_compat.h b/src/saveload/compat/goal_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/goal_sl_compat.h rename to src/saveload/compat/goal_sl_compat.h diff --git a/src/saveload/upstream/compat/group_sl_compat.h b/src/saveload/compat/group_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/group_sl_compat.h rename to src/saveload/compat/group_sl_compat.h diff --git a/src/saveload/upstream/compat/industry_sl_compat.h b/src/saveload/compat/industry_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/industry_sl_compat.h rename to src/saveload/compat/industry_sl_compat.h diff --git a/src/saveload/upstream/compat/labelmaps_sl_compat.h b/src/saveload/compat/labelmaps_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/labelmaps_sl_compat.h rename to src/saveload/compat/labelmaps_sl_compat.h diff --git a/src/saveload/upstream/compat/linkgraph_sl_compat.h b/src/saveload/compat/linkgraph_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/linkgraph_sl_compat.h rename to src/saveload/compat/linkgraph_sl_compat.h diff --git a/src/saveload/upstream/compat/map_sl_compat.h b/src/saveload/compat/map_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/map_sl_compat.h rename to src/saveload/compat/map_sl_compat.h diff --git a/src/saveload/upstream/compat/misc_sl_compat.h b/src/saveload/compat/misc_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/misc_sl_compat.h rename to src/saveload/compat/misc_sl_compat.h diff --git a/src/saveload/upstream/compat/newgrf_sl_compat.h b/src/saveload/compat/newgrf_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/newgrf_sl_compat.h rename to src/saveload/compat/newgrf_sl_compat.h diff --git a/src/saveload/upstream/compat/object_sl_compat.h b/src/saveload/compat/object_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/object_sl_compat.h rename to src/saveload/compat/object_sl_compat.h diff --git a/src/saveload/upstream/compat/order_sl_compat.h b/src/saveload/compat/order_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/order_sl_compat.h rename to src/saveload/compat/order_sl_compat.h diff --git a/src/saveload/upstream/compat/settings_sl_compat.h b/src/saveload/compat/settings_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/settings_sl_compat.h rename to src/saveload/compat/settings_sl_compat.h diff --git a/src/saveload/upstream/compat/signs_sl_compat.h b/src/saveload/compat/signs_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/signs_sl_compat.h rename to src/saveload/compat/signs_sl_compat.h diff --git a/src/saveload/upstream/compat/station_sl_compat.h b/src/saveload/compat/station_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/station_sl_compat.h rename to src/saveload/compat/station_sl_compat.h diff --git a/src/saveload/upstream/compat/storage_sl_compat.h b/src/saveload/compat/storage_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/storage_sl_compat.h rename to src/saveload/compat/storage_sl_compat.h diff --git a/src/saveload/upstream/compat/story_sl_compat.h b/src/saveload/compat/story_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/story_sl_compat.h rename to src/saveload/compat/story_sl_compat.h diff --git a/src/saveload/upstream/compat/subsidy_sl_compat.h b/src/saveload/compat/subsidy_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/subsidy_sl_compat.h rename to src/saveload/compat/subsidy_sl_compat.h diff --git a/src/saveload/upstream/compat/town_sl_compat.h b/src/saveload/compat/town_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/town_sl_compat.h rename to src/saveload/compat/town_sl_compat.h diff --git a/src/saveload/upstream/compat/vehicle_sl_compat.h b/src/saveload/compat/vehicle_sl_compat.h similarity index 100% rename from src/saveload/upstream/compat/vehicle_sl_compat.h rename to src/saveload/compat/vehicle_sl_compat.h diff --git a/src/saveload/depot_sl.cpp b/src/saveload/depot_sl.cpp index 73cf0cd4cf..b838db0ff3 100644 --- a/src/saveload/depot_sl.cpp +++ b/src/saveload/depot_sl.cpp @@ -8,57 +8,71 @@ /** @file depot_sl.cpp Code handling saving and loading of depots */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/depot_sl_compat.h" + #include "../depot_base.h" #include "../town.h" -#include "saveload.h" - #include "../safeguards.h" +namespace upstream_sl { + static TownID _town_index; static const SaveLoad _depot_desc[] = { SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), SLE_CONDVAR(Depot, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLEG_CONDVAR(_town_index, SLE_UINT16, SL_MIN_VERSION, SLV_141), + SLEG_CONDVAR("town_index", _town_index, SLE_UINT16, SL_MIN_VERSION, SLV_141), SLE_CONDREF(Depot, town, REF_TOWN, SLV_141, SL_MAX_VERSION), SLE_CONDVAR(Depot, town_cn, SLE_UINT16, SLV_141, SL_MAX_VERSION), SLE_CONDSTR(Depot, name, SLE_STR, 0, SLV_141, SL_MAX_VERSION), SLE_CONDVAR(Depot, build_date, SLE_INT32, SLV_142, SL_MAX_VERSION), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 5)), }; -static void Save_DEPT() -{ - for (Depot *depot : Depot::Iterate()) { - SlSetArrayIndex(depot->index); - SlObject(depot, _depot_desc); +struct DEPTChunkHandler : ChunkHandler { + DEPTChunkHandler() : ChunkHandler('DEPT', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(_depot_desc); + + for (Depot *depot : Depot::Iterate()) { + SlSetArrayIndex(depot->index); + SlObject(depot, _depot_desc); + } } -} -static void Load_DEPT() -{ - int index; + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_depot_desc, _depot_sl_compat); - while ((index = SlIterateArray()) != -1) { - Depot *depot = new (index) Depot(); - SlObject(depot, _depot_desc); + int index; - /* Set the town 'pointer' so we can restore it later. */ - if (IsSavegameVersionBefore(SLV_141)) depot->town = (Town *)(size_t)_town_index; + while ((index = SlIterateArray()) != -1) { + Depot *depot = new (index) Depot(); + SlObject(depot, slt); + + /* Set the town 'pointer' so we can restore it later. */ + if (IsSavegameVersionBefore(SLV_141)) depot->town = (Town *)(size_t)_town_index; + } } -} -static void Ptrs_DEPT() -{ - for (Depot *depot : Depot::Iterate()) { - SlObject(depot, _depot_desc); - if (IsSavegameVersionBefore(SLV_141)) depot->town = Town::Get((size_t)depot->town); + void FixPointers() const override + { + for (Depot *depot : Depot::Iterate()) { + SlObject(depot, _depot_desc); + if (IsSavegameVersionBefore(SLV_141)) depot->town = Town::Get((size_t)depot->town); + } } -} +}; -static const ChunkHandler depot_chunk_handlers[] = { - { 'DEPT', Save_DEPT, Load_DEPT, Ptrs_DEPT, nullptr, CH_ARRAY }, +static const DEPTChunkHandler DEPT; +static const ChunkHandlerRef depot_chunk_handlers[] = { + DEPT, }; extern const ChunkHandlerTable _depot_chunk_handlers(depot_chunk_handlers); + +} diff --git a/src/saveload/economy_sl.cpp b/src/saveload/economy_sl.cpp index 6cc2a8629b..2ddf04264a 100644 --- a/src/saveload/economy_sl.cpp +++ b/src/saveload/economy_sl.cpp @@ -8,34 +8,18 @@ /** @file economy_sl.cpp Code handling saving and loading of economy data */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/economy_sl_compat.h" + #include "../economy_func.h" #include "../economy_base.h" -#include "saveload.h" - #include "../safeguards.h" -/** Prices in pre 126 savegames */ -static void Load_PRIC() -{ - /* Old games store 49 base prices, very old games store them as int32 */ - int vt = IsSavegameVersionBefore(SLV_65) ? SLE_FILE_I32 : SLE_FILE_I64; - SlArray(nullptr, 49, vt | SLE_VAR_NULL); - SlArray(nullptr, 49, SLE_FILE_U16 | SLE_VAR_NULL); -} - -/** Cargo payment rates in pre 126 savegames */ -static void Load_CAPR() -{ - uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; - int vt = IsSavegameVersionBefore(SLV_65) ? SLE_FILE_I32 : SLE_FILE_I64; - SlArray(nullptr, num_cargo, vt | SLE_VAR_NULL); - SlArray(nullptr, num_cargo, SLE_FILE_U16 | SLE_VAR_NULL); -} +namespace upstream_sl { static const SaveLoad _economy_desc[] = { - SLE_CONDNULL(4, SL_MIN_VERSION, SLV_65), // max_loan - SLE_CONDNULL(8, SLV_65, SLV_144), // max_loan SLE_CONDVAR(Economy, old_max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), SLE_CONDVAR(Economy, old_max_loan_unround, SLE_INT64, SLV_65, SLV_126), SLE_CONDVAR(Economy, old_max_loan_unround_fract, SLE_UINT16, SLV_70, SLV_126), @@ -46,60 +30,80 @@ static const SaveLoad _economy_desc[] = { SLE_VAR(Economy, infl_amount, SLE_UINT8), SLE_VAR(Economy, infl_amount_pr, SLE_UINT8), SLE_CONDVAR(Economy, industry_daily_change_counter, SLE_UINT32, SLV_102, SL_MAX_VERSION), - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), }; /** Economy variables */ -static void Save_ECMY() -{ - SlObject(&_economy, _economy_desc); -} +struct ECMYChunkHandler : ChunkHandler { + ECMYChunkHandler() : ChunkHandler('ECMY', CH_TABLE) {} -/** Economy variables */ -static void Load_ECMY() -{ - SlObject(&_economy, _economy_desc); - StartupIndustryDailyChanges(IsSavegameVersionBefore(SLV_102)); // old savegames will need to be initialized -} + void Save() const override + { + SlTableHeader(_economy_desc); + + SlSetArrayIndex(0); + SlObject(&_economy, _economy_desc); + } + + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_economy_desc, _economy_sl_compat); + + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; + SlObject(&_economy, slt); + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many ECMY entries"); + + StartupIndustryDailyChanges(IsSavegameVersionBefore(SLV_102)); // old savegames will need to be initialized + } +}; static const SaveLoad _cargopayment_desc[] = { SLE_REF(CargoPayment, front, REF_VEHICLE), SLE_VAR(CargoPayment, route_profit, SLE_INT64), SLE_VAR(CargoPayment, visual_profit, SLE_INT64), - SLE_CONDVAR_X(CargoPayment, visual_transfer, SLE_INT64, SLV_181, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP)), + SLE_CONDVAR(CargoPayment, visual_transfer, SLE_INT64, SLV_181, SL_MAX_VERSION), }; -static void Save_CAPY() -{ - for (CargoPayment *cp : CargoPayment::Iterate()) { - SlSetArrayIndex(cp->index); - SlObject(cp, _cargopayment_desc); +struct CAPYChunkHandler : ChunkHandler { + CAPYChunkHandler() : ChunkHandler('CAPY', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(_cargopayment_desc); + + for (CargoPayment *cp : CargoPayment::Iterate()) { + SlSetArrayIndex(cp->index); + SlObject(cp, _cargopayment_desc); + } } -} -static void Load_CAPY() -{ - int index; + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_cargopayment_desc, _cargopayment_sl_compat); - while ((index = SlIterateArray()) != -1) { - CargoPayment *cp = new (index) CargoPayment(); - SlObject(cp, _cargopayment_desc); + int index; + + while ((index = SlIterateArray()) != -1) { + CargoPayment *cp = new (index) CargoPayment(); + SlObject(cp, slt); + } } -} -static void Ptrs_CAPY() -{ - for (CargoPayment *cp : CargoPayment::Iterate()) { - SlObject(cp, _cargopayment_desc); + void FixPointers() const override + { + for (CargoPayment *cp : CargoPayment::Iterate()) { + SlObject(cp, _cargopayment_desc); + } } -} +}; - -static const ChunkHandler economy_chunk_handlers[] = { - { 'CAPY', Save_CAPY, Load_CAPY, Ptrs_CAPY, nullptr, CH_ARRAY }, - { 'PRIC', nullptr, Load_PRIC, nullptr, nullptr, CH_RIFF }, - { 'CAPR', nullptr, Load_CAPR, nullptr, nullptr, CH_RIFF }, - { 'ECMY', Save_ECMY, Load_ECMY, nullptr, nullptr, CH_RIFF }, +static const CAPYChunkHandler CAPY; +static const ECMYChunkHandler ECMY; +static const ChunkHandlerRef economy_chunk_handlers[] = { + CAPY, + ECMY, }; extern const ChunkHandlerTable _economy_chunk_handlers(economy_chunk_handlers); + +} diff --git a/src/saveload/engine_sl.cpp b/src/saveload/engine_sl.cpp index 9f1eebefe7..e5ab5e7239 100644 --- a/src/saveload/engine_sl.cpp +++ b/src/saveload/engine_sl.cpp @@ -8,14 +8,20 @@ /** @file engine_sl.cpp Code handling saving and loading of engines */ #include "../stdafx.h" -#include "saveload_internal.h" + +#include "saveload.h" +#include "compat/engine_sl_compat.h" + #include "../engine_base.h" -#include "../engine_func.h" #include "../string_func.h" #include #include "../safeguards.h" +Engine *GetTempDataEngine(EngineID index); + +namespace upstream_sl { + static const SaveLoad _engine_desc[] = { SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), SLE_CONDVAR(Engine, intro_date, SLE_INT32, SLV_31, SL_MAX_VERSION), @@ -29,142 +35,51 @@ static const SaveLoad _engine_desc[] = { 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_CONDNULL(1, SL_MIN_VERSION, SLV_121), SLE_VAR(Engine, flags, SLE_UINT8), - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_179), // old preview_company_rank SLE_CONDVAR(Engine, preview_asked, SLE_UINT16, SLV_179, SL_MAX_VERSION), SLE_CONDVAR(Engine, preview_company, SLE_UINT8, SLV_179, SL_MAX_VERSION), SLE_VAR(Engine, preview_wait, SLE_UINT8), - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_45), SLE_CONDVAR(Engine, company_avail, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), SLE_CONDVAR(Engine, company_avail, SLE_UINT16, SLV_104, SL_MAX_VERSION), SLE_CONDVAR(Engine, company_hidden, SLE_UINT16, SLV_193, SL_MAX_VERSION), - SLE_CONDSTR(Engine, name, SLE_STR, 0, SLV_84, SL_MAX_VERSION), - - SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space + SLE_CONDSTR(Engine, name, SLE_STR, 0, SLV_84, SL_MAX_VERSION), }; -static std::vector _temp_engine; +struct ENGNChunkHandler : ChunkHandler { + ENGNChunkHandler() : ChunkHandler('ENGN', CH_TABLE) {} -/** - * Allocate an Engine structure, but not using the pools. - * The allocated Engine must be freed using FreeEngine; - * @return Allocated engine. - */ -static Engine* CallocEngine() -{ - uint8 *zero = CallocT(sizeof(Engine)); - Engine *engine = new (zero) Engine(); - return engine; -} + void Save() const override + { + SlTableHeader(_engine_desc); -/** - * Deallocate an Engine constructed by CallocEngine. - * @param e Engine to free. - */ -static void FreeEngine(Engine *e) -{ - if (e != nullptr) { - e->~Engine(); - free(e); - } -} - -Engine *GetTempDataEngine(EngineID index) -{ - if (index < _temp_engine.size()) { - return _temp_engine[index]; - } else if (index == _temp_engine.size()) { - _temp_engine.push_back(CallocEngine()); - return _temp_engine[index]; - } else { - NOT_REACHED(); - } -} - -static void Save_ENGN() -{ - for (Engine *e : Engine::Iterate()) { - 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); - - if (IsSavegameVersionBefore(SLV_179)) { - /* preview_company_rank was replaced with preview_company and preview_asked. - * Just cancel any previews. */ - e->flags &= ~4; // ENGINE_OFFER_WINDOW_OPEN - e->preview_company = INVALID_COMPANY; - e->preview_asked = MAX_UVALUE(CompanyMask); + for (Engine *e : Engine::Iterate()) { + SlSetArrayIndex(e->index); + SlObject(e, _engine_desc); } } -} -/** - * Copy data from temporary engine array into the real engine pool. - */ -void CopyTempEngineData() -{ - for (Engine *e : Engine::Iterate()) { - if (e->index >= _temp_engine.size()) break; + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_engine_desc, _engine_sl_compat); - 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->flags = se->flags; - e->preview_asked = se->preview_asked; - e->preview_company = se->preview_company; - e->preview_wait = se->preview_wait; - e->company_avail = se->company_avail; - e->company_hidden = se->company_hidden; - e->name = se->name; + /* 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, slt); + + if (IsSavegameVersionBefore(SLV_179)) { + /* preview_company_rank was replaced with preview_company and preview_asked. + * Just cancel any previews. */ + e->flags &= ~4; // ENGINE_OFFER_WINDOW_OPEN + e->preview_company = INVALID_COMPANY; + e->preview_asked = MAX_UVALUE(CompanyMask); + } + } } - - ResetTempEngineData(); -} - -void ResetTempEngineData() -{ - /* Get rid of temporary data */ - for (std::vector::iterator it = _temp_engine.begin(); it != _temp_engine.end(); ++it) { - FreeEngine(*it); - } - _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]); - } -} +}; /** Save and load the mapping between the engine id in the pool, and the grf file it came from. */ static const SaveLoad _engine_id_mapping_desc[] = { @@ -174,35 +89,41 @@ static const SaveLoad _engine_id_mapping_desc[] = { SLE_VAR(EngineIDMapping, substitute_id, SLE_UINT8), }; -static void Save_EIDS() -{ - uint index = 0; - for (EngineIDMapping &eid : _engine_mngr) { - SlSetArrayIndex(index); - SlObject(&eid, _engine_id_mapping_desc); - index++; +struct EIDSChunkHandler : ChunkHandler { + EIDSChunkHandler() : ChunkHandler('EIDS', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(_engine_id_mapping_desc); + + uint index = 0; + for (EngineIDMapping &eid : _engine_mngr) { + SlSetArrayIndex(index); + SlObject(&eid, _engine_id_mapping_desc); + index++; + } } -} -static void Load_EIDS() -{ - _engine_mngr.clear(); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_engine_id_mapping_desc, _engine_id_mapping_sl_compat); - while (SlIterateArray() != -1) { - EngineIDMapping *eid = &_engine_mngr.emplace_back(); - SlObject(eid, _engine_id_mapping_desc); + _engine_mngr.clear(); + + while (SlIterateArray() != -1) { + EngineIDMapping *eid = &_engine_mngr.emplace_back(); + SlObject(eid, slt); + } } -} +}; -void AfterLoadEngines() -{ - AnalyseEngineCallbacks(); -} - -static const ChunkHandler engine_chunk_handlers[] = { - { 'EIDS', Save_EIDS, Load_EIDS, nullptr, nullptr, CH_ARRAY }, - { 'ENGN', Save_ENGN, Load_ENGN, nullptr, nullptr, CH_ARRAY }, - { 'ENGS', nullptr, Load_ENGS, nullptr, nullptr, CH_RIFF }, +static const EIDSChunkHandler EIDS; +static const ENGNChunkHandler ENGN; +static const ChunkHandlerRef engine_chunk_handlers[] = { + EIDS, + ENGN, }; extern const ChunkHandlerTable _engine_chunk_handlers(engine_chunk_handlers); + +} diff --git a/src/saveload/game_sl.cpp b/src/saveload/game_sl.cpp index 7da7da71ed..6be21bb2d1 100644 --- a/src/saveload/game_sl.cpp +++ b/src/saveload/game_sl.cpp @@ -9,9 +9,11 @@ #include "../stdafx.h" #include "../debug.h" -#include "saveload.h" -#include "../string_func.h" +#include "saveload.h" +#include "compat/game_sl_compat.h" + +#include "../string_func.h" #include "../game/game.hpp" #include "../game/game_config.hpp" #include "../network/network.h" @@ -20,16 +22,20 @@ #include "../safeguards.h" +extern GameStrings *_current_data; + +namespace upstream_sl { + static std::string _game_saveload_name; static int _game_saveload_version; static std::string _game_saveload_settings; static bool _game_saveload_is_random; -static const SaveLoad _game_script[] = { - SLEG_SSTR(_game_saveload_name, SLE_STR), - SLEG_SSTR(_game_saveload_settings, SLE_STR), - SLEG_VAR(_game_saveload_version, SLE_UINT32), - SLEG_VAR(_game_saveload_is_random, SLE_BOOL), +static const SaveLoad _game_script_desc[] = { + SLEG_SSTR("name", _game_saveload_name, SLE_STR), + SLEG_SSTR("settings", _game_saveload_settings, SLE_STR), + SLEG_VAR("version", _game_saveload_version, SLE_UINT32), + SLEG_VAR("is_random", _game_saveload_is_random, SLE_BOOL), }; static void SaveReal_GSDT(int *index_ptr) @@ -48,133 +54,156 @@ static void SaveReal_GSDT(int *index_ptr) _game_saveload_is_random = config->IsRandom(); _game_saveload_settings = config->SettingsToString(); - SlObject(nullptr, _game_script); + SlObject(nullptr, _game_script_desc); Game::Save(); } -static void Load_GSDT() -{ - /* Free all current data */ - GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME)->Change(nullptr); +struct GSDTChunkHandler : ChunkHandler { + GSDTChunkHandler() : ChunkHandler('GSDT', CH_TABLE) {} - if ((CompanyID)SlIterateArray() == (CompanyID)-1) return; + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_game_script_desc, _game_script_sl_compat); - _game_saveload_version = -1; - SlObject(nullptr, _game_script); + /* Free all current data */ + GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME)->Change(nullptr); - if (_game_mode == GM_MENU || (_networking && !_network_server)) { - GameInstance::LoadEmpty(); - if ((CompanyID)SlIterateArray() != (CompanyID)-1) SlErrorCorrupt("Too many GameScript configs"); - return; - } + if (SlIterateArray() == -1) return; - GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME); - if (!_game_saveload_name.empty()) { - config->Change(_game_saveload_name.c_str(), _game_saveload_version, false, _game_saveload_is_random); - if (!config->HasScript()) { - /* No version of the GameScript available that can load the data. Try to load the - * latest version of the GameScript instead. */ - config->Change(_game_saveload_name.c_str(), -1, false, _game_saveload_is_random); - if (!config->HasScript()) { - if (_game_saveload_name.compare("%_dummy") != 0) { - DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %d which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version); - DEBUG(script, 0, "This game will continue to run without GameScript."); - } else { - DEBUG(script, 0, "The savegame had no GameScript available at the time of saving."); - DEBUG(script, 0, "This game will continue to run without GameScript."); - } - } else { - DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %d which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version); - DEBUG(script, 0, "The latest version of that GameScript has been loaded instead, but it'll not get the savegame data as it's incompatible."); - } - /* Make sure the GameScript doesn't get the saveload data, as it was not the - * writer of the saveload data in the first place */ - _game_saveload_version = -1; + _game_saveload_version = -1; + SlObject(nullptr, slt); + + if (_game_mode == GM_MENU || (_networking && !_network_server)) { + GameInstance::LoadEmpty(); + if (SlIterateArray() != -1) SlErrorCorrupt("Too many GameScript configs"); + return; } + + GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME); + if (!_game_saveload_name.empty()) { + config->Change(_game_saveload_name.c_str(), _game_saveload_version, false, _game_saveload_is_random); + if (!config->HasScript()) { + /* No version of the GameScript available that can load the data. Try to load the + * latest version of the GameScript instead. */ + config->Change(_game_saveload_name.c_str(), -1, false, _game_saveload_is_random); + if (!config->HasScript()) { + if (_game_saveload_name.compare("%_dummy") != 0) { + DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %u which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version); + DEBUG(script, 0, "This game will continue to run without GameScript."); + } else { + DEBUG(script, 0, "The savegame had no GameScript available at the time of saving."); + DEBUG(script, 0, "This game will continue to run without GameScript."); + } + } else { + DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %u which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version); + DEBUG(script, 0, "The latest version of that GameScript has been loaded instead, but it'll not get the savegame data as it's incompatible."); + } + /* Make sure the GameScript doesn't get the saveload data, as it was not the + * writer of the saveload data in the first place */ + _game_saveload_version = -1; + } + } + + config->StringToSettings(_game_saveload_settings); + + /* Load the GameScript saved data */ + config->SetToLoadData(GameInstance::Load(_game_saveload_version)); + + if (SlIterateArray() != -1) SlErrorCorrupt("Too many GameScript configs"); } - config->StringToSettings(_game_saveload_settings); - - /* Load the GameScript saved data */ - config->SetToLoadData(GameInstance::Load(_game_saveload_version)); - - if ((CompanyID)SlIterateArray() != (CompanyID)-1) SlErrorCorrupt("Too many GameScript configs"); -} - -static void Save_GSDT() -{ - SlSetArrayIndex(0); - SlAutolength((AutolengthProc *)SaveReal_GSDT, nullptr); -} - -extern GameStrings *_current_data; + void Save() const override + { + SlTableHeader(_game_script_desc); + SlSetArrayIndex(0); + SlAutolength((AutolengthProc *)SaveReal_GSDT, nullptr); + } +}; static std::string _game_saveload_string; -static uint _game_saveload_strings; +static uint32 _game_saveload_strings; -static const SaveLoad _game_language_header[] = { - SLEG_SSTR(_game_saveload_string, SLE_STR), - SLEG_VAR(_game_saveload_strings, SLE_UINT32), -}; +class SlGameLanguageString : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLEG_SSTR("string", _game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL), + }; + inline const static SaveLoadCompatTable compat_description = _game_language_string_sl_compat; -static const SaveLoad _game_language_string[] = { - SLEG_SSTR(_game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL), -}; + void Save(LanguageStrings *ls) const override + { + SlSetStructListLength(ls->lines.size()); -static void SaveReal_GSTR(const LanguageStrings *ls) -{ - _game_saveload_string = ls->language.c_str(); - _game_saveload_strings = (uint)ls->lines.size(); - - SlObject(nullptr, _game_language_header); - for (const auto &i : ls->lines) { - _game_saveload_string = i.c_str(); - SlObject(nullptr, _game_language_string); + for (const auto &string : ls->lines) { + _game_saveload_string = string; + SlObject(nullptr, this->GetDescription()); + } } -} -static void Load_GSTR() -{ - delete _current_data; - _current_data = new GameStrings(); + void Load(LanguageStrings *ls) const override + { + uint32 length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _game_saveload_strings : (uint32)SlGetStructListLength(UINT32_MAX); - while (SlIterateArray() != -1) { - _game_saveload_string.clear(); - SlObject(nullptr, _game_language_header); + for (uint32 i = 0; i < length; i++) { + SlObject(nullptr, this->GetLoadDescription()); + ls->lines.emplace_back(_game_saveload_string); + } + } +}; - LanguageStrings ls(_game_saveload_string); - for (uint i = 0; i < _game_saveload_strings; i++) { - SlObject(nullptr, _game_language_string); - ls.lines.emplace_back(_game_saveload_string); +static const SaveLoad _game_language_desc[] = { + SLE_SSTR(LanguageStrings, language, SLE_STR), + SLEG_CONDVAR("count", _game_saveload_strings, SLE_UINT32, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), + SLEG_STRUCTLIST("strings", SlGameLanguageString), +}; + +struct GSTRChunkHandler : ChunkHandler { + GSTRChunkHandler() : ChunkHandler('GSTR', CH_TABLE) {} + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_game_language_desc, _game_language_sl_compat); + + delete _current_data; + _current_data = new GameStrings(); + + while (SlIterateArray() != -1) { + LanguageStrings ls; + SlObject(&ls, slt); + _current_data->raw_strings.push_back(std::move(ls)); } - _current_data->raw_strings.push_back(std::move(ls)); + /* If there were no strings in the savegame, set GameStrings to nullptr */ + if (_current_data->raw_strings.size() == 0) { + delete _current_data; + _current_data = nullptr; + return; + } + + _current_data->Compile(); + ReconsiderGameScriptLanguage(); } - /* If there were no strings in the savegame, set GameStrings to nullptr */ - if (_current_data->raw_strings.size() == 0) { - delete _current_data; - _current_data = nullptr; - return; + void Save() const override + { + SlTableHeader(_game_language_desc); + + if (_current_data == nullptr) return; + + for (uint i = 0; i < _current_data->raw_strings.size(); i++) { + SlSetArrayIndex(i); + SlObject(&_current_data->raw_strings[i], _game_language_desc); + } } +}; - _current_data->Compile(); - ReconsiderGameScriptLanguage(); -} - -static void Save_GSTR() -{ - if (_current_data == nullptr) return; - - for (uint i = 0; i < _current_data->raw_strings.size(); i++) { - SlSetArrayIndex(i); - SlAutolength((AutolengthProc *)SaveReal_GSTR, &_current_data->raw_strings[i]); - } -} - -static const ChunkHandler game_chunk_handlers[] = { - { 'GSTR', Save_GSTR, Load_GSTR, nullptr, nullptr, CH_ARRAY }, - { 'GSDT', Save_GSDT, Load_GSDT, nullptr, nullptr, CH_ARRAY }, +static const GSTRChunkHandler GSTR; +static const GSDTChunkHandler GSDT; +static const ChunkHandlerRef game_chunk_handlers[] = { + GSTR, + GSDT, }; extern const ChunkHandlerTable _game_chunk_handlers(game_chunk_handlers); + +} diff --git a/src/saveload/gamelog_sl.cpp b/src/saveload/gamelog_sl.cpp index 8270190c53..dae47c919b 100644 --- a/src/saveload/gamelog_sl.cpp +++ b/src/saveload/gamelog_sl.cpp @@ -8,176 +8,421 @@ /** @file gamelog_sl.cpp Code handling saving and loading of gamelog data */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/gamelog_sl_compat.h" + #include "../gamelog_internal.h" #include "../fios.h" #include "../string_func.h" -#include "saveload.h" - #include "../safeguards.h" -static const SaveLoad _glog_action_desc[] = { - SLE_CONDVAR_X(LoggedAction, tick, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)), - SLE_CONDVAR_X(LoggedAction, tick, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)), -}; +namespace upstream_sl { -static const SaveLoad _glog_mode_desc[] = { - SLE_VAR(LoggedChange, mode.mode, SLE_UINT8), - SLE_VAR(LoggedChange, mode.landscape, SLE_UINT8), +class SlGamelogMode : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(LoggedChange, mode.mode, SLE_UINT8), + SLE_VAR(LoggedChange, mode.landscape, SLE_UINT8), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_mode_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_MODE) return; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_MODE) return; + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } }; static char old_revision_text[GAMELOG_REVISION_LENGTH]; +static std::string revision_test; -static const SaveLoad _glog_revision_desc[] = { - SLEG_CONDARR_X(old_revision_text, SLE_UINT8, GAMELOG_REVISION_LENGTH, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTENDED_GAMELOG, 0, 0)), - SLE_CONDSTR_X(LoggedChange, revision.text, SLE_STR, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTENDED_GAMELOG)), - SLE_VAR(LoggedChange, revision.newgrf, SLE_UINT32), - SLE_VAR(LoggedChange, revision.slver, SLE_UINT16), - SLE_VAR(LoggedChange, revision.modified, SLE_UINT8), -}; +class SlGamelogRevision : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLEG_CONDARR("revision.text", old_revision_text, SLE_UINT8, GAMELOG_REVISION_LENGTH, SL_MIN_VERSION, SLV_STRING_GAMELOG), + SLEG_CONDSSTR("revision.text", revision_test, SLE_STR, SLV_STRING_GAMELOG, SL_MAX_VERSION), + SLE_VAR(LoggedChange, revision.newgrf, SLE_UINT32), + SLE_VAR(LoggedChange, revision.slver, SLE_UINT16), + SLE_VAR(LoggedChange, revision.modified, SLE_UINT8), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_revision_sl_compat; -static const SaveLoad _glog_oldver_desc[] = { - SLE_VAR(LoggedChange, oldver.type, SLE_UINT32), - SLE_VAR(LoggedChange, oldver.version, SLE_UINT32), -}; + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_REVISION) return; + SlObject(lc, this->GetDescription()); + } -static const SaveLoad _glog_setting_desc[] = { - SLE_STR(LoggedChange, setting.name, SLE_STR, 128), - SLE_VAR(LoggedChange, setting.oldval, SLE_INT32), - SLE_VAR(LoggedChange, setting.newval, SLE_INT32), -}; - -static const SaveLoad _glog_grfadd_desc[] = { - SLE_VAR(LoggedChange, grfadd.grfid, SLE_UINT32 ), - SLE_ARR(LoggedChange, grfadd.md5sum, SLE_UINT8, 16), -}; - -static const SaveLoad _glog_grfrem_desc[] = { - SLE_VAR(LoggedChange, grfrem.grfid, SLE_UINT32), -}; - -static const SaveLoad _glog_grfcompat_desc[] = { - SLE_VAR(LoggedChange, grfcompat.grfid, SLE_UINT32 ), - SLE_ARR(LoggedChange, grfcompat.md5sum, SLE_UINT8, 16), -}; - -static const SaveLoad _glog_grfparam_desc[] = { - SLE_VAR(LoggedChange, grfparam.grfid, SLE_UINT32), -}; - -static const SaveLoad _glog_grfmove_desc[] = { - SLE_VAR(LoggedChange, grfmove.grfid, SLE_UINT32), - SLE_VAR(LoggedChange, grfmove.offset, SLE_INT32), -}; - -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), -}; - -static const SaveLoad _glog_emergency_desc[] = { - SLE_CONDNULL(0, SL_MIN_VERSION, SL_MIN_VERSION), // Just an empty list, to keep the rest of the code easier. -}; - -static const SaveLoadTable _glog_desc[] = { - _glog_mode_desc, - _glog_revision_desc, - _glog_oldver_desc, - _glog_setting_desc, - _glog_grfadd_desc, - _glog_grfrem_desc, - _glog_grfcompat_desc, - _glog_grfparam_desc, - _glog_grfmove_desc, - _glog_grfbug_desc, - _glog_emergency_desc, -}; - -static_assert(lengthof(_glog_desc) == GLCT_END); - -static void Load_GLOG_common(LoggedAction *&gamelog_action, uint &gamelog_actions) -{ - assert(gamelog_action == nullptr); - assert(gamelog_actions == 0); - - byte type; - while ((type = SlReadByte()) != GLAT_NONE) { - if (type >= GLAT_END) SlErrorCorrupt("Invalid gamelog action type"); - GamelogActionType at = (GamelogActionType)type; - - 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 = nullptr; - la->changes = 0; - - while ((type = SlReadByte()) != GLCT_NONE) { - if (type >= GLCT_END) SlErrorCorrupt("Invalid gamelog change type"); - GamelogChangeType ct = (GamelogChangeType)type; - - 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 nullptr */ - memset(lc, 0, sizeof(*lc)); - lc->ct = ct; - - SlObject(lc, _glog_desc[ct]); - if (ct == GLCT_REVISION && SlXvIsFeatureMissing(XSLFI_EXTENDED_GAMELOG)) { - lc->revision.text = stredup(old_revision_text, lastof(old_revision_text)); - } + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_REVISION) return; + SlObject(lc, this->GetLoadDescription()); + if (IsSavegameVersionBefore(SLV_STRING_GAMELOG)) { + lc->revision.text = stredup(old_revision_text, lastof(old_revision_text)); + } else { + lc->revision.text = stredup(revision_test.c_str()); } } -} -static void Save_GLOG() -{ - const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; - size_t length = 0; + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; - 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 += 10; +class SlGamelogOldver : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(LoggedChange, oldver.type, SLE_UINT32), + SLE_VAR(LoggedChange, oldver.version, SLE_UINT32), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_oldver_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_OLDVER) return; + SlObject(lc, this->GetDescription()); } - length++; - SlSetLength(length); + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_OLDVER) return; + SlObject(lc, this->GetLoadDescription()); + } - for (LoggedAction *la = _gamelog_action; la != laend; la++) { - SlWriteByte(la->at); - SlObject(la, _glog_action_desc); + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +class SlGamelogSetting : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_STR(LoggedChange, setting.name, SLE_STR, 128), + SLE_VAR(LoggedChange, setting.oldval, SLE_INT32), + SLE_VAR(LoggedChange, setting.newval, SLE_INT32), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_setting_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_SETTING) return; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_SETTING) return; + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +class SlGamelogGrfadd : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(LoggedChange, grfadd.grfid, SLE_UINT32 ), + SLE_ARR(LoggedChange, grfadd.md5sum, SLE_UINT8, 16), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_grfadd_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFADD) return; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFADD) return; + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +class SlGamelogGrfrem : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(LoggedChange, grfrem.grfid, SLE_UINT32), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_grfrem_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFREM) return; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFREM) return; + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +class SlGamelogGrfcompat : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(LoggedChange, grfcompat.grfid, SLE_UINT32 ), + SLE_ARR(LoggedChange, grfcompat.md5sum, SLE_UINT8, 16), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_grfcompat_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFCOMPAT) return; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFCOMPAT) return; + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +class SlGamelogGrfparam : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(LoggedChange, grfparam.grfid, SLE_UINT32), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_grfparam_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFPARAM) return; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFPARAM) return; + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +class SlGamelogGrfmove : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(LoggedChange, grfmove.grfid, SLE_UINT32), + SLE_VAR(LoggedChange, grfmove.offset, SLE_INT32), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_grfmove_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFMOVE) return; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFMOVE) return; + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +class SlGamelogGrfbug : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(LoggedChange, grfbug.data, SLE_UINT64), + SLE_VAR(LoggedChange, grfbug.grfid, SLE_UINT32), + SLE_VAR(LoggedChange, grfbug.bug, SLE_UINT8), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_grfbug_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFBUG) return; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_GRFBUG) return; + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +static bool _is_emergency_save = true; + +class SlGamelogEmergency : public DefaultSaveLoadHandler { +public: + /* We need to store something, so store a "true" value. */ + inline static const SaveLoad description[] = { + SLEG_CONDVAR("is_emergency_save", _is_emergency_save, SLE_BOOL, SLV_RIFF_TO_ARRAY, SL_MAX_VERSION), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_emergency_sl_compat; + + void Save(LoggedChange *lc) const override + { + if (lc->ct != GLCT_EMERGENCY) return; + + _is_emergency_save = true; + SlObject(lc, this->GetDescription()); + } + + void Load(LoggedChange *lc) const override + { + if (lc->ct != GLCT_EMERGENCY) return; + + SlObject(lc, this->GetLoadDescription()); + } + + void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } +}; + +class SlGamelogAction : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_SAVEBYTE(LoggedChange, ct), + SLEG_STRUCT("mode", SlGamelogMode), + SLEG_STRUCT("revision", SlGamelogRevision), + SLEG_STRUCT("oldver", SlGamelogOldver), + SLEG_STRUCT("setting", SlGamelogSetting), + SLEG_STRUCT("grfadd", SlGamelogGrfadd), + SLEG_STRUCT("grfrem", SlGamelogGrfrem), + SLEG_STRUCT("grfcompat", SlGamelogGrfcompat), + SLEG_STRUCT("grfparam", SlGamelogGrfparam), + SLEG_STRUCT("grfmove", SlGamelogGrfmove), + SLEG_STRUCT("grfbug", SlGamelogGrfbug), + SLEG_STRUCT("emergency", SlGamelogEmergency), + }; + inline const static SaveLoadCompatTable compat_description = _gamelog_action_sl_compat; + + void Save(LoggedAction *la) const override + { + SlSetStructListLength(la->changes); 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]); + SlObject(lc, this->GetDescription()); } - SlWriteByte(GLCT_NONE); } - SlWriteByte(GLAT_NONE); -} -static void Load_GLOG() -{ - Load_GLOG_common(_gamelog_action, _gamelog_actions); -} + void Load(LoggedAction *la) const override + { + if (IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY)) { + byte type; + while ((type = SlReadByte()) != GLCT_NONE) { + if (type >= GLCT_END) SlErrorCorrupt("Invalid gamelog change type"); + GamelogChangeType ct = (GamelogChangeType)type; -static void Check_GLOG() -{ - Load_GLOG_common(_load_check_data.gamelog_action, _load_check_data.gamelog_actions); -} + la->change = ReallocT(la->change, la->changes + 1); -static const ChunkHandler gamelog_chunk_handlers[] = { - { 'GLOG', Save_GLOG, Load_GLOG, nullptr, Check_GLOG, CH_RIFF } + LoggedChange *lc = &la->change[la->changes++]; + memset(lc, 0, sizeof(*lc)); + lc->ct = ct; + + SlObject(lc, this->GetLoadDescription()); + } + return; + } + + size_t length = SlGetStructListLength(UINT32_MAX); + la->change = ReallocT(la->change, length); + la->changes = (uint32)length; + + for (size_t i = 0; i < length; i++) { + LoggedChange *lc = &la->change[i]; + memset(lc, 0, sizeof(*lc)); + + lc->ct = (GamelogChangeType)SlReadByte(); + SlObject(lc, this->GetLoadDescription()); + } + } + + void LoadCheck(LoggedAction *la) const override { this->Load(la); } +}; + +static const SaveLoad _gamelog_desc[] = { + SLE_CONDVAR(LoggedAction, at, SLE_UINT8, SLV_RIFF_TO_ARRAY, SL_MAX_VERSION), + SLE_CONDVAR(LoggedAction, tick, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SLV_U64_TICK_COUNTER), + SLE_CONDVAR(LoggedAction, tick, SLE_UINT64, SLV_U64_TICK_COUNTER, SL_MAX_VERSION), + SLEG_STRUCTLIST("action", SlGamelogAction), +}; + +struct GLOGChunkHandler : ChunkHandler { + GLOGChunkHandler() : ChunkHandler('GLOG', CH_TABLE) {} + + void LoadCommon(LoggedAction *&gamelog_action, uint &gamelog_actions) const + { + assert(gamelog_action == nullptr); + assert(gamelog_actions == 0); + + const std::vector slt = SlCompatTableHeader(_gamelog_desc, _gamelog_sl_compat); + + if (IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY)) { + byte type; + while ((type = SlReadByte()) != GLAT_NONE) { + if (type >= GLAT_END) SlErrorCorrupt("Invalid gamelog action type"); + + gamelog_action = ReallocT(gamelog_action, gamelog_actions + 1); + LoggedAction *la = &gamelog_action[gamelog_actions++]; + memset(la, 0, sizeof(*la)); + + la->at = (GamelogActionType)type; + SlObject(la, slt); + } + return; + } + + while (SlIterateArray() != -1) { + gamelog_action = ReallocT(gamelog_action, gamelog_actions + 1); + LoggedAction *la = &gamelog_action[gamelog_actions++]; + memset(la, 0, sizeof(*la)); + + SlObject(la, slt); + } + } + + void Save() const override + { + SlTableHeader(_gamelog_desc); + + const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; + + uint i = 0; + for (LoggedAction *la = _gamelog_action; la != laend; la++, i++) { + SlSetArrayIndex(i); + SlObject(la, _gamelog_desc); + } + } + + void Load() const override + { + this->LoadCommon(_gamelog_action, _gamelog_actions); + } + + void LoadCheck(size_t) const override + { + this->LoadCommon(_load_check_data.gamelog_action, _load_check_data.gamelog_actions); + } +}; + +static const GLOGChunkHandler GLOG; +static const ChunkHandlerRef gamelog_chunk_handlers[] = { + GLOG, }; extern const ChunkHandlerTable _gamelog_chunk_handlers(gamelog_chunk_handlers); + +} diff --git a/src/saveload/goal_sl.cpp b/src/saveload/goal_sl.cpp index c062107e46..265e192e35 100644 --- a/src/saveload/goal_sl.cpp +++ b/src/saveload/goal_sl.cpp @@ -8,12 +8,16 @@ /** @file goal_sl.cpp Code handling saving and loading of goals */ #include "../stdafx.h" -#include "../goal_base.h" #include "saveload.h" +#include "compat/goal_sl_compat.h" + +#include "../goal_base.h" #include "../safeguards.h" +namespace upstream_sl { + static const SaveLoad _goals_desc[] = { SLE_VAR(Goal, company, SLE_FILE_U16 | SLE_VAR_U8), SLE_VAR(Goal, type, SLE_FILE_U16 | SLE_VAR_U8), @@ -23,25 +27,36 @@ static const SaveLoad _goals_desc[] = { SLE_CONDVAR(Goal, completed, SLE_BOOL, SLV_182, SL_MAX_VERSION), }; -static void Save_GOAL() -{ - for (Goal *s : Goal::Iterate()) { - SlSetArrayIndex(s->index); - SlObject(s, _goals_desc); - } -} +struct GOALChunkHandler : ChunkHandler { + GOALChunkHandler() : ChunkHandler('GOAL', CH_TABLE) {} -static void Load_GOAL() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Goal *s = new (index) Goal(); - SlObject(s, _goals_desc); - } -} + void Save() const override + { + SlTableHeader(_goals_desc); -static const ChunkHandler goal_chunk_handlers[] = { - { 'GOAL', Save_GOAL, Load_GOAL, nullptr, nullptr, CH_ARRAY }, + for (Goal *s : Goal::Iterate()) { + SlSetArrayIndex(s->index); + SlObject(s, _goals_desc); + } + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_goals_desc, _goals_sl_compat); + + int index; + while ((index = SlIterateArray()) != -1) { + Goal *s = new (index) Goal(); + SlObject(s, slt); + } + } +}; + +static const GOALChunkHandler GOAL; +static const ChunkHandlerRef goal_chunk_handlers[] = { + GOAL, }; extern const ChunkHandlerTable _goal_chunk_handlers(goal_chunk_handlers); + +} diff --git a/src/saveload/group_sl.cpp b/src/saveload/group_sl.cpp index f41091611c..90a2ad21b7 100644 --- a/src/saveload/group_sl.cpp +++ b/src/saveload/group_sl.cpp @@ -12,13 +12,15 @@ #include "../company_base.h" #include "saveload.h" +#include "compat/group_sl_compat.h" #include "../safeguards.h" +namespace upstream_sl { + static const SaveLoad _group_desc[] = { SLE_CONDVAR(Group, name, SLE_NAME, SL_MIN_VERSION, SLV_84), SLE_CONDSSTR(Group, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), // num_vehicle SLE_VAR(Group, owner, SLE_UINT8), SLE_VAR(Group, vehicle_type, SLE_UINT8), SLE_VAR(Group, flags, SLE_UINT8), @@ -28,35 +30,46 @@ static const SaveLoad _group_desc[] = { SLE_CONDVAR(Group, parent, SLE_UINT16, SLV_189, SL_MAX_VERSION), }; -static void Save_GRPS() -{ - for (Group *g : Group::Iterate()) { - SlSetArrayIndex(g->index); - SlObject(g, _group_desc); - } -} +struct GRPSChunkHandler : ChunkHandler { + GRPSChunkHandler() : ChunkHandler('GRPS', CH_TABLE) {} + void Save() const override + { + SlTableHeader(_group_desc); -static void Load_GRPS() -{ - int index; - - while ((index = SlIterateArray()) != -1) { - Group *g = new (index) Group(); - SlObject(g, _group_desc); - - if (IsSavegameVersionBefore(SLV_189)) g->parent = INVALID_GROUP; - - if (IsSavegameVersionBefore(SLV_GROUP_LIVERIES)) { - const Company *c = Company::Get(g->owner); - g->livery.colour1 = c->livery[LS_DEFAULT].colour1; - g->livery.colour2 = c->livery[LS_DEFAULT].colour2; + for (Group *g : Group::Iterate()) { + SlSetArrayIndex(g->index); + SlObject(g, _group_desc); } } -} -static const ChunkHandler group_chunk_handlers[] = { - { 'GRPS', Save_GRPS, Load_GRPS, nullptr, nullptr, CH_ARRAY }, + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_group_desc, _group_sl_compat); + + int index; + + while ((index = SlIterateArray()) != -1) { + Group *g = new (index) Group(); + SlObject(g, slt); + + if (IsSavegameVersionBefore(SLV_189)) g->parent = INVALID_GROUP; + + if (IsSavegameVersionBefore(SLV_GROUP_LIVERIES)) { + const Company *c = Company::Get(g->owner); + g->livery.colour1 = c->livery[LS_DEFAULT].colour1; + g->livery.colour2 = c->livery[LS_DEFAULT].colour2; + } + } + } +}; + +static const GRPSChunkHandler GRPS; +static const ChunkHandlerRef group_chunk_handlers[] = { + GRPS, }; extern const ChunkHandlerTable _group_chunk_handlers(group_chunk_handlers); + +} diff --git a/src/saveload/industry_sl.cpp b/src/saveload/industry_sl.cpp index c75674069e..c626a4eeb9 100644 --- a/src/saveload/industry_sl.cpp +++ b/src/saveload/industry_sl.cpp @@ -8,13 +8,134 @@ /** @file industry_sl.cpp Code handling saving and loading of industries */ #include "../stdafx.h" -#include "../industry.h" #include "saveload.h" +#include "compat/industry_sl_compat.h" + +#include "../industry.h" #include "newgrf_sl.h" #include "../safeguards.h" +namespace upstream_sl { + +static const int THIS_MONTH = 0; +static const int LAST_MONTH = 1; + +struct ProducedHistory { + uint16_t production; ///< Total produced + uint16_t transported; ///< Total transported + + uint8_t PctTransported() const + { + if (this->production == 0) return 0; + return ClampTo(this->transported * 256 / this->production); + } +}; + +struct ProducedCargo { + CargoID cargo; ///< Cargo type + uint16_t waiting; ///< Amount of cargo produced + uint8_t rate; ///< Production rate + std::array history; ///< History of cargo produced and transported +}; + +struct AcceptedCargo { + CargoID cargo; ///< Cargo type + uint16_t waiting; ///< Amount of cargo waiting to processed + Date last_accepted; ///< Last day cargo was accepted by this industry +}; + +class SlIndustryAccepted : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(AcceptedCargo, cargo, SLE_UINT8), + SLE_VAR(AcceptedCargo, waiting, SLE_UINT16), + SLE_VAR(AcceptedCargo, last_accepted, SLE_INT32), + }; + inline const static SaveLoadCompatTable compat_description = {}; + + void Save(Industry *i) const override + { + NOT_REACHED(); + } + + void Load(Industry *i) const override + { + size_t len = SlGetStructListLength(INDUSTRY_NUM_INPUTS); + + for (size_t j = 0; j < len; j++) { + AcceptedCargo a = {}; + SlObject(&a, this->GetDescription()); + if (j < INDUSTRY_NUM_INPUTS) { + i->accepts_cargo[j] = a.cargo; + i->incoming_cargo_waiting[j] = a.waiting; + i->last_cargo_accepted_at[j] = a.last_accepted; + } + } + } +}; + +class SlIndustryProducedHistory : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(ProducedHistory, production, SLE_UINT16), + SLE_VAR(ProducedHistory, transported, SLE_UINT16), + }; + inline const static SaveLoadCompatTable compat_description = {}; + + void Save(ProducedCargo *p) const override + { + NOT_REACHED(); + } + + void Load(ProducedCargo *p) const override + { + size_t len = SlGetStructListLength(p->history.size()); + + for (auto &h : p->history) { + if (--len > p->history.size()) break; // unsigned so wraps after hitting zero. + SlObject(&h, this->GetDescription()); + } + } +}; + +class SlIndustryProduced : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(ProducedCargo, cargo, SLE_UINT8), + SLE_VAR(ProducedCargo, waiting, SLE_UINT16), + SLE_VAR(ProducedCargo, rate, SLE_UINT8), + SLEG_STRUCTLIST("history", SlIndustryProducedHistory), + }; + inline const static SaveLoadCompatTable compat_description = {}; + + void Save(Industry *i) const override + { + NOT_REACHED(); + } + + void Load(Industry *i) const override + { + size_t len = SlGetStructListLength(INDUSTRY_NUM_OUTPUTS); + + for (size_t j = 0; j < len; j++) { + ProducedCargo p = {}; + SlObject(&p, this->GetDescription()); + if (j < INDUSTRY_NUM_OUTPUTS) { + i->produced_cargo[j] = p.cargo; + i->produced_cargo_waiting[j] = p.waiting; + i->production_rate[j] = p.rate; + i->this_month_production[j] = p.history[THIS_MONTH].production; + i->this_month_transported[j] = p.history[THIS_MONTH].transported; + i->last_month_production[j] = p.history[LAST_MONTH].production; + i->last_month_transported[j] = p.history[LAST_MONTH].transported; + i->last_month_pct_transported[j] = p.history[LAST_MONTH].PctTransported(); + } + } + } +}; + static OldPersistentStorage _old_ind_persistent_storage; static const SaveLoad _industry_desc[] = { @@ -24,29 +145,27 @@ static const SaveLoad _industry_desc[] = { SLE_VAR(Industry, location.h, SLE_FILE_U8 | SLE_VAR_U16), SLE_REF(Industry, town, REF_TOWN), SLE_CONDREF(Industry, neutral_station, REF_STATION, SLV_SERVE_NEUTRAL_INDUSTRIES, SL_MAX_VERSION), - SLE_CONDNULL( 2, SL_MIN_VERSION, SLV_61), ///< used to be industry's produced_cargo SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDARR(Industry, production_rate, SLE_UINT8, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, production_rate, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), - SLE_CONDNULL( 3, SL_MIN_VERSION, SLV_61), ///< used to be industry's accepts_cargo + SLE_CONDARR(Industry, production_rate, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_VAR(Industry, prod_level, SLE_UINT8), SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_VAR(Industry, counter, SLE_UINT16), @@ -62,88 +181,100 @@ static const SaveLoad _industry_desc[] = { SLE_CONDVAR(Industry, construction_date, SLE_INT32, SLV_70, SL_MAX_VERSION), SLE_CONDVAR(Industry, construction_type, SLE_UINT8, SLV_70, SL_MAX_VERSION), SLE_CONDVAR(Industry, last_cargo_accepted_at[0], SLE_INT32, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, last_cargo_accepted_at, SLE_INT32, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, last_cargo_accepted_at, SLE_INT32, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, SLV_73, SL_MAX_VERSION), SLE_CONDVAR(Industry, exclusive_supplier, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION), SLE_CONDVAR(Industry, exclusive_consumer, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION), - SLEG_CONDARR(_old_ind_persistent_storage.storage, SLE_UINT32, 16, SLV_76, SLV_161), + SLEG_CONDARR("storage", _old_ind_persistent_storage.storage, SLE_UINT32, 16, SLV_76, SLV_161), SLE_CONDREF(Industry, psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), - SLE_CONDNULL(1, SLV_82, SLV_197), // random_triggers SLE_CONDVAR(Industry, random, SLE_UINT16, SLV_82, SL_MAX_VERSION), SLE_CONDSSTR(Industry, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_INDUSTRY_TEXT, SL_MAX_VERSION), - SLE_CONDNULL(32, SLV_2, SLV_144), // old reserved space + SLEG_CONDSTRUCTLIST("accepted", SlIndustryAccepted, SLV_INDUSTRY_CARGO_REORGANISE, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("produced", SlIndustryProduced, SLV_INDUSTRY_CARGO_REORGANISE, SL_MAX_VERSION), }; -static void Save_INDY() -{ - /* Write the industries */ - for (Industry *ind : Industry::Iterate()) { - SlSetArrayIndex(ind->index); - SlObject(ind, _industry_desc); - } -} +struct INDYChunkHandler : ChunkHandler { + INDYChunkHandler() : ChunkHandler('INDY', CH_TABLE) {} -static void Save_IIDS() -{ - Save_NewGRFMapping(_industry_mngr); -} + void Save() const override + { + SlTableHeader(_industry_desc); -static void Save_TIDS() -{ - Save_NewGRFMapping(_industile_mngr); -} - -static void Load_INDY() -{ - int index; - - Industry::ResetIndustryCounts(); - - while ((index = SlIterateArray()) != -1) { - Industry *i = new (index) Industry(); - SlObject(i, _industry_desc); - - /* Before savegame version 161, persistent storages were not stored in a pool. */ - if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_76)) { - /* Store the old persistent storage. The GRFID will be added later. */ - assert(PersistentStorage::CanAllocateItem()); - i->psa = new PersistentStorage(0, 0, 0); - memcpy(i->psa->storage, _old_ind_persistent_storage.storage, sizeof(_old_ind_persistent_storage.storage)); + /* Write the industries */ + for (Industry *ind : Industry::Iterate()) { + SlSetArrayIndex(ind->index); + SlObject(ind, _industry_desc); } - Industry::IncIndustryTypeCount(i->type); } -} -static void Load_IIDS() -{ - Load_NewGRFMapping(_industry_mngr); -} + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_industry_desc, _industry_sl_compat); -static void Load_TIDS() -{ - Load_NewGRFMapping(_industile_mngr); -} + int index; -static void Ptrs_INDY() -{ - for (Industry *i : Industry::Iterate()) { - SlObject(i, _industry_desc); + Industry::ResetIndustryCounts(); + + while ((index = SlIterateArray()) != -1) { + Industry *i = new (index) Industry(); + SlObject(i, slt); + + /* Before savegame version 161, persistent storages were not stored in a pool. */ + if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_76)) { + /* Store the old persistent storage. The GRFID will be added later. */ + assert(PersistentStorage::CanAllocateItem()); + i->psa = new PersistentStorage(0, 0, 0); + memcpy(i->psa->storage, _old_ind_persistent_storage.storage, sizeof(_old_ind_persistent_storage.storage)); + } + Industry::IncIndustryTypeCount(i->type); + } } -} + + void FixPointers() const override + { + for (Industry *i : Industry::Iterate()) { + SlObject(i, _industry_desc); + } + } +}; + +struct IIDSChunkHandler : NewGRFMappingChunkHandler { + IIDSChunkHandler() : NewGRFMappingChunkHandler('IIDS', _industry_mngr) {} +}; + +struct TIDSChunkHandler : NewGRFMappingChunkHandler { + TIDSChunkHandler() : NewGRFMappingChunkHandler('TIDS', _industile_mngr) {} +}; /** Description of the data to save and load in #IndustryBuildData. */ static const SaveLoad _industry_builder_desc[] = { - SLEG_VAR(_industry_builder.wanted_inds, SLE_UINT32), + SLEG_VAR("wanted_inds", _industry_builder.wanted_inds, SLE_UINT32), }; -/** Load/save industry builder. */ -static void LoadSave_IBLD() -{ - SlGlobList(_industry_builder_desc); -} +/** Industry builder. */ +struct IBLDChunkHandler : ChunkHandler { + IBLDChunkHandler() : ChunkHandler('IBLD', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(_industry_builder_desc); + + SlSetArrayIndex(0); + SlGlobList(_industry_builder_desc); + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_industry_builder_desc, _industry_builder_sl_compat); + + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; + SlGlobList(slt); + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many IBLD entries"); + } +}; /** Description of the data to save and load in #IndustryTypeBuildData. */ static const SaveLoad _industrytype_builder_desc[] = { @@ -154,34 +285,48 @@ static const SaveLoad _industrytype_builder_desc[] = { SLE_VAR(IndustryTypeBuildData, wait_count, SLE_UINT16), }; -/** Save industry-type build data. */ -static void Save_ITBL() -{ - for (int i = 0; i < NUM_INDUSTRYTYPES; i++) { - SlSetArrayIndex(i); - SlObject(_industry_builder.builddata + i, _industrytype_builder_desc); - } -} +/** Industry-type build data. */ +struct ITBLChunkHandler : ChunkHandler { + ITBLChunkHandler() : ChunkHandler('ITBL', CH_TABLE) {} -/** Load industry-type build data. */ -static void Load_ITBL() -{ - for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) { - _industry_builder.builddata[it].Reset(); - } - int index; - while ((index = SlIterateArray()) != -1) { - if ((uint)index >= NUM_INDUSTRYTYPES) SlErrorCorrupt("Too many industry builder datas"); - SlObject(_industry_builder.builddata + index, _industrytype_builder_desc); - } -} + void Save() const override + { + SlTableHeader(_industrytype_builder_desc); -static const ChunkHandler industry_chunk_handlers[] = { - { 'INDY', Save_INDY, Load_INDY, Ptrs_INDY, nullptr, CH_ARRAY }, - { 'IIDS', Save_IIDS, Load_IIDS, nullptr, nullptr, CH_ARRAY }, - { 'TIDS', Save_TIDS, Load_TIDS, nullptr, nullptr, CH_ARRAY }, - { 'IBLD', LoadSave_IBLD, LoadSave_IBLD, nullptr, nullptr, CH_RIFF }, - { 'ITBL', Save_ITBL, Load_ITBL, nullptr, nullptr, CH_ARRAY }, + for (int i = 0; i < NUM_INDUSTRYTYPES; i++) { + SlSetArrayIndex(i); + SlObject(_industry_builder.builddata + i, _industrytype_builder_desc); + } + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_industrytype_builder_desc, _industrytype_builder_sl_compat); + + for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) { + _industry_builder.builddata[it].Reset(); + } + int index; + while ((index = SlIterateArray()) != -1) { + if ((uint)index >= NUM_INDUSTRYTYPES) SlErrorCorrupt("Too many industry builder datas"); + SlObject(_industry_builder.builddata + index, slt); + } + } +}; + +static const INDYChunkHandler INDY; +static const IIDSChunkHandler IIDS; +static const TIDSChunkHandler TIDS; +static const IBLDChunkHandler IBLD; +static const ITBLChunkHandler ITBL; +static const ChunkHandlerRef industry_chunk_handlers[] = { + INDY, + IIDS, + TIDS, + IBLD, + ITBL, }; extern const ChunkHandlerTable _industry_chunk_handlers(industry_chunk_handlers); + +} diff --git a/src/saveload/labelmaps_sl.cpp b/src/saveload/labelmaps_sl.cpp index 9d8c084b94..f218ce0ebf 100644 --- a/src/saveload/labelmaps_sl.cpp +++ b/src/saveload/labelmaps_sl.cpp @@ -8,94 +8,20 @@ /** @file labelmaps_sl.cpp Code handling saving and loading of rail type label mappings */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/labelmaps_sl_compat.h" + #include "../station_map.h" #include "../tunnelbridge_map.h" -#include "saveload.h" -#include "saveload_internal.h" - #include "../safeguards.h" -std::vector _railtype_list; +void ResetLabelMaps(); -/** - * Test if any saved rail type labels are different to the currently loaded - * rail types, which therefore requires conversion. - * @return true if (and only if) conversion due to rail type changes is needed. - */ -static bool NeedRailTypeConversion() -{ - for (uint i = 0; i < _railtype_list.size(); i++) { - if ((RailType)i < RAILTYPE_END) { - const RailtypeInfo *rti = GetRailTypeInfo((RailType)i); - if (rti->label != _railtype_list[i]) return true; - } else { - if (_railtype_list[i] != 0) return true; - } - } +extern std::vector _railtype_list; - /* No rail type conversion is necessary */ - return false; -} - -void AfterLoadLabelMaps() -{ - if (NeedRailTypeConversion()) { - RailType railtype_conversion_map[RAILTYPE_END]; - - for (uint i = 0; i < _railtype_list.size(); i++) { - RailType r = GetRailTypeByLabel(_railtype_list[i]); - if (r == INVALID_RAILTYPE) r = RAILTYPE_BEGIN; - - railtype_conversion_map[i] = r; - } - for (uint i = (uint)_railtype_list.size(); i < RAILTYPE_END; i++) { - railtype_conversion_map[i] = RAILTYPE_RAIL; - } - - auto convert = [&](TileIndex t) { - SetRailType(t, railtype_conversion_map[GetRailType(t)]); - RailType secondary = GetTileSecondaryRailTypeIfValid(t); - if (secondary != INVALID_RAILTYPE) SetSecondaryRailType(t, railtype_conversion_map[secondary]); - }; - - for (TileIndex t = 0; t < MapSize(); t++) { - switch (GetTileType(t)) { - case MP_RAILWAY: - convert(t); - break; - - case MP_ROAD: - if (IsLevelCrossing(t)) { - convert(t); - } - break; - - case MP_STATION: - if (HasStationRail(t)) { - convert(t); - } - break; - - case MP_TUNNELBRIDGE: - if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) { - convert(t); - } - break; - - default: - break; - } - } - } - - ResetLabelMaps(); -} - -void ResetLabelMaps() -{ - _railtype_list.clear(); -} +namespace upstream_sl { /** Container for a label for SaveLoad system */ struct LabelObject { @@ -106,33 +32,43 @@ static const SaveLoad _label_object_desc[] = { SLE_VAR(LabelObject, label, SLE_UINT32), }; -static void Save_RAIL() -{ - LabelObject lo; +struct RAILChunkHandler : ChunkHandler { + RAILChunkHandler() : ChunkHandler('RAIL', CH_TABLE) {} - for (RailType r = RAILTYPE_BEGIN; r != RAILTYPE_END; r++) { - lo.label = GetRailTypeInfo(r)->label; + void Save() const override + { + SlTableHeader(_label_object_desc); - SlSetArrayIndex(r); - SlObject(&lo, _label_object_desc); + LabelObject lo; + + for (RailType r = RAILTYPE_BEGIN; r != RAILTYPE_END; r++) { + lo.label = GetRailTypeInfo(r)->label; + + SlSetArrayIndex(r); + SlObject(&lo, _label_object_desc); + } } -} -static void Load_RAIL() -{ - ResetLabelMaps(); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_label_object_desc, _label_object_sl_compat); - LabelObject lo; + ResetLabelMaps(); - while (SlIterateArray() != -1) { - SlObject(&lo, _label_object_desc); - _railtype_list.push_back((RailTypeLabel)lo.label); + LabelObject lo; + + while (SlIterateArray() != -1) { + SlObject(&lo, slt); + _railtype_list.push_back((RailTypeLabel)lo.label); + } } -} +}; -static const ChunkHandler labelmaps_chunk_handlers[] = { - { 'RAIL', Save_RAIL, Load_RAIL, nullptr, nullptr, CH_ARRAY }, +static const RAILChunkHandler RAIL; +static const ChunkHandlerRef labelmaps_chunk_handlers[] = { + RAIL, }; extern const ChunkHandlerTable _labelmaps_chunk_handlers(labelmaps_chunk_handlers); +} diff --git a/src/saveload/league_sl.cpp b/src/saveload/league_sl.cpp index 4f70d34208..9d2cab9e71 100644 --- a/src/saveload/league_sl.cpp +++ b/src/saveload/league_sl.cpp @@ -6,23 +6,91 @@ */ /** @file league_sl.cpp Code handling saving and loading of league tables */ + #include "../stdafx.h" #include "saveload.h" -extern SaveLoadVersion _sl_xv_upstream_version; +#include "../league_base.h" -struct GetLeagueChunkLoadInfo -{ - static SaveLoadVersion GetLoadVersion() +#include "../safeguards.h" + +namespace upstream_sl { + +static const SaveLoad _league_table_elements_desc[] = { + SLE_VAR(LeagueTableElement, table, SLE_UINT8), + SLE_CONDVAR(LeagueTableElement, rating, SLE_FILE_U64 | SLE_VAR_I64, SL_MIN_VERSION, SLV_LINKGRAPH_EDGES), + SLE_CONDVAR(LeagueTableElement, rating, SLE_INT64, SLV_LINKGRAPH_EDGES, SL_MAX_VERSION), + SLE_VAR(LeagueTableElement, company, SLE_UINT8), + SLE_SSTR(LeagueTableElement, text, SLE_STR | SLF_ALLOW_CONTROL), + SLE_SSTR(LeagueTableElement, score, SLE_STR | SLF_ALLOW_CONTROL), + SLE_VAR(LeagueTableElement, link.type, SLE_UINT8), + SLE_VAR(LeagueTableElement, link.target, SLE_UINT32), +}; + +struct LEAEChunkHandler : ChunkHandler { + LEAEChunkHandler() : ChunkHandler('LEAE', CH_TABLE) {} + + void Save() const override { - return _sl_xv_upstream_version != SL_MIN_VERSION ? _sl_xv_upstream_version : SLV_MULTITRACK_LEVEL_CROSSINGS; + SlTableHeader(_league_table_elements_desc); + + for (LeagueTableElement *lte : LeagueTableElement::Iterate()) { + SlSetArrayIndex(lte->index); + SlObject(lte, _league_table_elements_desc); + } + } + + void Load() const override + { + const std::vector slt = SlTableHeader(_league_table_elements_desc); + + int index; + while ((index = SlIterateArray()) != -1) { + LeagueTableElement *lte = new (index) LeagueTableElement(); + SlObject(lte, slt); + } } }; -static const ChunkHandler league_chunk_handlers[] = { - MakeUpstreamChunkHandler<'LEAE', GetLeagueChunkLoadInfo>(), - MakeUpstreamChunkHandler<'LEAT', GetLeagueChunkLoadInfo>(), +static const SaveLoad _league_tables_desc[] = { + SLE_SSTR(LeagueTable, title, SLE_STR | SLF_ALLOW_CONTROL), + SLE_SSTR(LeagueTable, header, SLE_STR | SLF_ALLOW_CONTROL), + SLE_SSTR(LeagueTable, footer, SLE_STR | SLF_ALLOW_CONTROL), +}; + +struct LEATChunkHandler : ChunkHandler { + LEATChunkHandler() : ChunkHandler('LEAT', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(_league_tables_desc); + + for (LeagueTable *lt : LeagueTable::Iterate()) { + SlSetArrayIndex(lt->index); + SlObject(lt, _league_tables_desc); + } + } + + void Load() const override + { + const std::vector slt = SlTableHeader(_league_tables_desc); + + int index; + while ((index = SlIterateArray()) != -1) { + LeagueTable *lt = new (index) LeagueTable(); + SlObject(lt, slt); + } + } +}; + +static const LEAEChunkHandler LEAE; +static const LEATChunkHandler LEAT; +static const ChunkHandlerRef league_chunk_handlers[] = { + LEAE, + LEAT, }; extern const ChunkHandlerTable _league_chunk_handlers(league_chunk_handlers); + +} diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp index 77594c625c..820035cb98 100644 --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -8,21 +8,115 @@ /** @file linkgraph_sl.cpp Code handling saving and loading of link graphs */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/linkgraph_sl_compat.h" + #include "../linkgraph/linkgraph.h" #include "../linkgraph/linkgraphjob.h" #include "../linkgraph/linkgraphschedule.h" #include "../network/network.h" -#include "../settings_internal.h" -#include "saveload.h" #include "../safeguards.h" +namespace upstream_sl { + typedef LinkGraph::BaseNode Node; typedef LinkGraph::BaseEdge Edge; -const SettingDesc *GetSettingDescription(uint index); - static uint16 _num_nodes; +static LinkGraph *_linkgraph; ///< Contains the current linkgraph being saved/loaded. +static NodeID _linkgraph_from; ///< Contains the current "from" node being saved/loaded. +static NodeID _edge_dest_node; +static NodeID _edge_next_edge; + +class SlLinkgraphEdge : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(Edge, capacity, SLE_UINT32), + SLE_VAR(Edge, usage, SLE_UINT32), + SLE_CONDVAR(Edge, travel_time_sum, SLE_UINT64, SLV_LINKGRAPH_TRAVEL_TIME, SL_MAX_VERSION), + SLE_VAR(Edge, last_unrestricted_update, SLE_INT32), + SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION), + SLEG_VAR("dest_node", _edge_dest_node, SLE_UINT16), + SLEG_CONDVAR("next_edge", _edge_next_edge, SLE_UINT16, SL_MIN_VERSION, SLV_LINKGRAPH_EDGES), + }; + inline const static SaveLoadCompatTable compat_description = _linkgraph_edge_sl_compat; + + void Save(Node *bn) const override + { + NOT_REACHED(); + } + + void Load(Node *bn) const override + { + uint16 max_size = _linkgraph->Size(); + + if (IsSavegameVersionBefore(SLV_191)) { + NOT_REACHED(); + } + + if (IsSavegameVersionBefore(SLV_LINKGRAPH_EDGES)) { + size_t used_size = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? max_size : SlGetStructListLength(UINT16_MAX); + + /* ... but as that wasted a lot of space we save a sparse matrix now. */ + for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _edge_next_edge) { + if (used_size == 0) SlErrorCorrupt("Link graph structure overflow"); + used_size--; + + if (to >= max_size) SlErrorCorrupt("Link graph structure overflow"); + SlObject(&_linkgraph->edges[std::make_pair(_linkgraph_from, to)], this->GetLoadDescription()); + } + + if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) && used_size > 0) SlErrorCorrupt("Corrupted link graph"); + } else { + /* Edge data is now a simple vector and not any kind of matrix. */ + size_t size = SlGetStructListLength(UINT16_MAX); + for (size_t i = 0; i < size; i++) { + Edge edge; + SlObject(&edge, this->GetLoadDescription()); + if (_edge_dest_node >= max_size) SlErrorCorrupt("Link graph structure overflow"); + _linkgraph->edges[std::make_pair(_linkgraph_from, _edge_dest_node)] = edge; + } + } + } +}; + +class SlLinkgraphNode : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_CONDVAR(Node, xy, SLE_UINT32, SLV_191, SL_MAX_VERSION), + SLE_VAR(Node, supply, SLE_UINT32), + SLE_VAR(Node, demand, SLE_UINT32), + SLE_VAR(Node, station, SLE_UINT16), + SLE_VAR(Node, last_update, SLE_INT32), + SLEG_STRUCTLIST("edges", SlLinkgraphEdge), + }; + inline const static SaveLoadCompatTable compat_description = _linkgraph_node_sl_compat; + + void Save(LinkGraph *lg) const override + { + _linkgraph = lg; + + SlSetStructListLength(lg->Size()); + for (NodeID from = 0; from < lg->Size(); ++from) { + _linkgraph_from = from; + SlObject(&lg->nodes[from], this->GetDescription()); + } + } + + void Load(LinkGraph *lg) const override + { + _linkgraph = lg; + + uint16 length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _num_nodes : (uint16)SlGetStructListLength(UINT16_MAX); + lg->Init(length); + for (NodeID from = 0; from < length; ++from) { + _linkgraph_from = from; + SlObject(&lg->nodes[from], this->GetLoadDescription()); + } + } +}; /** * Get a SaveLoad array for a link graph. @@ -32,18 +126,36 @@ SaveLoadTable GetLinkGraphDesc() { static const SaveLoad link_graph_desc[] = { SLE_VAR(LinkGraph, last_compression, SLE_INT32), - SLEG_VAR(_num_nodes, SLE_UINT16), + SLEG_CONDVAR("num_nodes", _num_nodes, SLE_UINT16, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), SLE_VAR(LinkGraph, cargo, SLE_UINT8), + SLEG_STRUCTLIST("nodes", SlLinkgraphNode), }; return link_graph_desc; } -void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj) -{ - lgj->join_date_ticks *= DAY_TICKS; - lgj->join_date_ticks += LinkGraphSchedule::SPAWN_JOIN_TICK; - lgj->start_date_ticks = lgj->join_date_ticks - (lgj->Settings().recalc_time * DAY_TICKS); -} +/** + * Proxy to reuse LinkGraph to save/load a LinkGraphJob. + * One of the members of a LinkGraphJob is a LinkGraph, but SLEG_STRUCT() + * doesn't allow us to select a member. So instead, we add a bit of glue to + * accept a LinkGraphJob, get the LinkGraph, and use that to call the + * save/load routines for a regular LinkGraph. + */ +class SlLinkgraphJobProxy : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = {{}}; // Needed to keep DefaultSaveLoadHandler happy. + SaveLoadTable GetDescription() const override { return GetLinkGraphDesc(); } + inline const static SaveLoadCompatTable compat_description = _linkgraph_sl_compat; + + void Save(LinkGraphJob *lgj) const override + { + SlObject(const_cast(&lgj->Graph()), this->GetDescription()); + } + + void Load(LinkGraphJob *lgj) const override + { + SlObject(const_cast(&lgj->Graph()), this->GetLoadDescription()); + } +}; /** * Get a SaveLoad array for a link graph job. The settings struct is derived from @@ -56,43 +168,24 @@ void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj) */ SaveLoadTable GetLinkGraphJobDesc() { - static std::vector saveloads; - static const char *prefix = "linkgraph."; + static const SaveLoad job_desc[] = { + SLE_VAR2(LinkGraphJob, "linkgraph.recalc_interval", settings.recalc_interval, SLE_UINT16), + SLE_VAR2(LinkGraphJob, "linkgraph.recalc_time", settings.recalc_time, SLE_UINT16), + SLE_VAR2(LinkGraphJob, "linkgraph.distribution_pax", settings.distribution_pax, SLE_UINT8), + SLE_VAR2(LinkGraphJob, "linkgraph.distribution_mail", settings.distribution_mail, SLE_UINT8), + SLE_VAR2(LinkGraphJob, "linkgraph.distribution_armoured", settings.distribution_armoured, SLE_UINT8), + SLE_VAR2(LinkGraphJob, "linkgraph.distribution_default", settings.distribution_default, SLE_UINT8), + SLE_VAR2(LinkGraphJob, "linkgraph.accuracy", settings.accuracy, SLE_UINT8), + SLE_VAR2(LinkGraphJob, "linkgraph.demand_distance", settings.demand_distance, SLE_UINT8), + SLE_VAR2(LinkGraphJob, "linkgraph.demand_size", settings.demand_size, SLE_UINT8), + SLE_VAR2(LinkGraphJob, "linkgraph.short_path_saturation", settings.short_path_saturation, SLE_UINT8), - /* Build the SaveLoad array on first call and don't touch it later on */ - if (saveloads.size() == 0) { - size_t offset_gamesettings = cpp_offsetof(GameSettings, linkgraph); - size_t offset_component = cpp_offsetof(LinkGraphJob, settings); + SLE_VAR2(LinkGraphJob, "join_date", join_date_ticks, SLE_INT32), + SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16), + SLEG_STRUCT("linkgraph", SlLinkgraphJobProxy), + }; - size_t prefixlen = strlen(prefix); - - int setting = 0; - const SettingDesc *desc = GetSettingDescription(setting); - while (desc != nullptr) { - if (desc->name != nullptr && strncmp(desc->name, prefix, prefixlen) == 0) { - SaveLoad sl = desc->save; - if (GetVarMemType(sl.conv) != SLE_VAR_NULL) { - char *&address = reinterpret_cast(sl.address); - address -= offset_gamesettings; - address += offset_component; - } - saveloads.push_back(sl); - } - desc = GetSettingDescription(++setting); - } - - const SaveLoad job_desc[] = { - SLE_VAR(LinkGraphJob, join_date_ticks, SLE_INT32), - SLE_CONDVAR_X(LinkGraphJob, start_date_ticks, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_DAY_SCALE)), - SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16), - }; - - for (auto &sld : job_desc) { - saveloads.push_back(sld); - } - } - - return saveloads; + return job_desc; } /** @@ -108,190 +201,6 @@ SaveLoadTable GetLinkGraphScheduleDesc() return schedule_desc; } -/* Edges and nodes are saved in the correct order, so we don't need to save their IDs. */ - -/** - * SaveLoad desc for a link graph node. - */ -static const SaveLoad _node_desc[] = { - SLE_CONDVAR(Node, xy, SLE_UINT32, SLV_191, SL_MAX_VERSION), - SLE_VAR(Node, supply, SLE_UINT32), - SLE_VAR(Node, demand, SLE_UINT32), - SLE_VAR(Node, station, SLE_UINT16), - SLE_VAR(Node, last_update, SLE_INT32), -}; - -/** - * SaveLoad desc for a link graph edge. - */ -static const SaveLoad _edge_desc[] = { - SLE_CONDNULL(4, SL_MIN_VERSION, SLV_191), // distance - SLE_VAR(Edge, capacity, SLE_UINT32), - SLE_VAR(Edge, usage, SLE_UINT32), - SLE_CONDVAR_X(Edge, travel_time_sum, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_TRAVEL_TIME)), - SLE_VAR(Edge, last_unrestricted_update, SLE_INT32), - SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION), - SLE_CONDVAR_X(Edge, last_aircraft_update, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_AIRCRAFT)), -// SLE_VAR(Edge, next_edge, SLE_UINT16), // Removed since XSLFI_LINKGRAPH_SPARSE_EDGES -}; - -std::vector _filtered_node_desc; -std::vector _filtered_edge_desc; -std::vector _filtered_job_desc; - -static void FilterDescs() -{ - _filtered_node_desc = SlFilterObject(_node_desc); - _filtered_edge_desc = SlFilterObject(_edge_desc); - _filtered_job_desc = SlFilterObject(GetLinkGraphJobDesc()); -} - -/** - * Save a link graph. - * @param lg Link graph to be saved or loaded. - */ -void Save_LinkGraph(LinkGraph &lg) -{ - uint16 size = lg.Size(); - auto edge_iter = lg.edges.begin(); - auto edge_end = lg.edges.end(); - for (NodeID from = 0; from < size; ++from) { - Node *node = &lg.nodes[from]; - SlObjectSaveFiltered(node, _filtered_node_desc); - - while (edge_iter != edge_end && edge_iter->first.first == from) { - SlWriteUint16(edge_iter->first.second); - Edge *edge = &edge_iter->second; - SlObjectSaveFiltered(edge, _filtered_edge_desc); - ++edge_iter; - } - SlWriteUint16(INVALID_NODE); - } -} - -/** - * Load a link graph. - * @param lg Link graph to be saved or loaded. - */ -void Load_LinkGraph(LinkGraph &lg) -{ - uint size = lg.Size(); - if (SlXvIsFeaturePresent(XSLFI_LINKGRAPH_SPARSE_EDGES)) { - for (NodeID from = 0; from < size; ++from) { - Node *node = &lg.nodes[from]; - SlObjectLoadFiltered(node, _filtered_node_desc); - while (true) { - NodeID to = SlReadUint16(); - if (to == INVALID_NODE) break; - SlObjectLoadFiltered(&lg.edges[std::make_pair(from, to)], _filtered_edge_desc); - } - } - } else if (IsSavegameVersionBefore(SLV_191)) { - std::vector temp_edges; - std::vector temp_next_edges; - temp_edges.resize(size); - temp_next_edges.resize(size); - for (NodeID from = 0; from < size; ++from) { - Node *node = &lg.nodes[from]; - SlObjectLoadFiltered(node, _filtered_node_desc); - /* We used to save the full matrix ... */ - for (NodeID to = 0; to < size; ++to) { - SlObjectLoadFiltered(&temp_edges[to], _filtered_edge_desc); - temp_next_edges[to] = SlReadUint16(); - } - for (NodeID to = from; to != INVALID_NODE; to = temp_next_edges[to]) { - lg.edges[std::make_pair(from, to)] = temp_edges[to]; - } - } - } else { - for (NodeID from = 0; from < size; ++from) { - Node *node = &lg.nodes[from]; - SlObjectLoadFiltered(node, _filtered_node_desc); - /* ... but as that wasted a lot of space we save a sparse matrix now. */ - for (NodeID to = from; to != INVALID_NODE;) { - if (to >= size) SlErrorCorrupt("Link graph structure overflow"); - SlObjectLoadFiltered(&lg.edges[std::make_pair(from, to)], _filtered_edge_desc); - to = SlReadUint16(); - } - } - } -} - -/** - * Save a link graph job. - * @param lgj LinkGraphJob to be saved. - */ -static void DoSave_LGRJ(LinkGraphJob *lgj) -{ - SlObjectSaveFiltered(lgj, _filtered_job_desc); - _num_nodes = lgj->Size(); - SlObjectSaveFiltered(const_cast(&lgj->Graph()), GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals - Save_LinkGraph(const_cast(lgj->Graph())); -} - -/** - * Save a link graph. - * @param lg LinkGraph to be saved. - */ -static void DoSave_LGRP(LinkGraph *lg) -{ - _num_nodes = lg->Size(); - SlObjectSaveFiltered(lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals - Save_LinkGraph(*lg); -} - -/** - * Load all link graphs. - */ -static void Load_LGRP() -{ - FilterDescs(); - int index; - while ((index = SlIterateArray()) != -1) { - if (!LinkGraph::CanAllocateItem()) { - /* Impossible as they have been present in previous game. */ - NOT_REACHED(); - } - LinkGraph *lg = new (index) LinkGraph(); - SlObjectLoadFiltered(lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals - lg->Init(_num_nodes); - Load_LinkGraph(*lg); - } -} - -/** - * Load all link graph jobs. - */ -static void Load_LGRJ() -{ - FilterDescs(); - int index; - while ((index = SlIterateArray()) != -1) { - if (!LinkGraphJob::CanAllocateItem()) { - /* Impossible as they have been present in previous game. */ - NOT_REACHED(); - } - LinkGraphJob *lgj = new (index) LinkGraphJob(); - SlObjectLoadFiltered(lgj, _filtered_job_desc); - if (SlXvIsFeatureMissing(XSLFI_LINKGRAPH_DAY_SCALE)) { - extern void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj); - GetLinkGraphJobDayLengthScaleAfterLoad(lgj); - } - LinkGraph &lg = const_cast(lgj->Graph()); - SlObjectLoadFiltered(&lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals - lg.Init(_num_nodes); - Load_LinkGraph(lg); - } -} - -/** - * Load the link graph schedule. - */ -static void Load_LGRS() -{ - SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); -} - /** * Spawn the threads for running link graph calculations. * Has to be done after loading as the cargo classes might have changed. @@ -314,6 +223,9 @@ void AfterLoadLinkGraphs() } } } + for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { + GetLinkGraphJobDayLengthScaleAfterLoad(lgj); + } LinkGraphSchedule::instance.SpawnAll(); @@ -323,49 +235,99 @@ void AfterLoadLinkGraphs() } /** - * Save all link graphs. + * All link graphs. */ -static void Save_LGRP() -{ - FilterDescs(); - for (LinkGraph *lg : LinkGraph::Iterate()) { - SlSetArrayIndex(lg->index); - SlAutolength((AutolengthProc*)DoSave_LGRP, lg); +struct LGRPChunkHandler : ChunkHandler { + LGRPChunkHandler() : ChunkHandler('LGRP', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(GetLinkGraphDesc()); + + for (LinkGraph *lg : LinkGraph::Iterate()) { + SlSetArrayIndex(lg->index); + SlObject(lg, GetLinkGraphDesc()); + } } -} -/** - * Save all link graph jobs. - */ -static void Save_LGRJ() -{ - FilterDescs(); - for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { - SlSetArrayIndex(lgj->index); - SlAutolength((AutolengthProc*)DoSave_LGRJ, lgj); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(GetLinkGraphDesc(), _linkgraph_sl_compat); + + int index; + while ((index = SlIterateArray()) != -1) { + LinkGraph *lg = new (index) LinkGraph(); + SlObject(lg, slt); + } } -} +}; /** - * Save the link graph schedule. + * All link graph jobs. */ -static void Save_LGRS() -{ - SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); -} +struct LGRJChunkHandler : ChunkHandler { + LGRJChunkHandler() : ChunkHandler('LGRJ', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(GetLinkGraphJobDesc()); + + for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { + SlSetArrayIndex(lgj->index); + SlObject(lgj, GetLinkGraphJobDesc()); + } + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(GetLinkGraphJobDesc(), _linkgraph_job_sl_compat); + + int index; + while ((index = SlIterateArray()) != -1) { + LinkGraphJob *lgj = new (index) LinkGraphJob(); + SlObject(lgj, slt); + } + } +}; /** - * Substitute pointers in link graph schedule. + * Link graph schedule. */ -static void Ptrs_LGRS() -{ - SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); -} +struct LGRSChunkHandler : ChunkHandler { + LGRSChunkHandler() : ChunkHandler('LGRS', CH_TABLE) {} -static const ChunkHandler linkgraph_chunk_handlers[] = { - { 'LGRP', Save_LGRP, Load_LGRP, nullptr, nullptr, CH_ARRAY }, - { 'LGRJ', Save_LGRJ, Load_LGRJ, nullptr, nullptr, CH_ARRAY }, - { 'LGRS', Save_LGRS, Load_LGRS, Ptrs_LGRS, nullptr, CH_RIFF } + void Save() const override + { + SlTableHeader(GetLinkGraphScheduleDesc()); + + SlSetArrayIndex(0); + SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(GetLinkGraphScheduleDesc(), _linkgraph_schedule_sl_compat); + + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; + SlObject(&LinkGraphSchedule::instance, slt); + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many LGRS entries"); + } + + void FixPointers() const override + { + SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); + } +}; + +static const LGRPChunkHandler LGRP; +static const LGRJChunkHandler LGRJ; +static const LGRSChunkHandler LGRS; +static const ChunkHandlerRef linkgraph_chunk_handlers[] = { + LGRP, + LGRJ, + LGRS, }; extern const ChunkHandlerTable _linkgraph_chunk_handlers(linkgraph_chunk_handlers); + +} diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp index 5017b7557e..d974da8322 100644 --- a/src/saveload/map_sl.cpp +++ b/src/saveload/map_sl.cpp @@ -8,413 +8,378 @@ /** @file map_sl.cpp Code handling saving and loading of map */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/map_sl_compat.h" + #include "../map_func.h" #include "../core/bitmath_func.hpp" -#include "../core/endian_func.hpp" -#include "../core/endian_type.hpp" #include "../fios.h" #include -#include "saveload.h" -#include "saveload_buffer.h" - #include "../safeguards.h" +namespace upstream_sl { + static uint32 _map_dim_x; static uint32 _map_dim_y; -extern bool _sl_maybe_chillpp; - -static const SaveLoad _map_dimensions[] = { - SLEG_CONDVAR(_map_dim_x, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLEG_CONDVAR(_map_dim_y, SLE_UINT32, SLV_6, SL_MAX_VERSION), +static const SaveLoad _map_desc[] = { + SLEG_CONDVAR("dim_x", _map_dim_x, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLEG_CONDVAR("dim_y", _map_dim_y, SLE_UINT32, SLV_6, SL_MAX_VERSION), }; -static void Save_MAPS() -{ - _map_dim_x = MapSizeX(); - _map_dim_y = MapSizeY(); - SlGlobList(_map_dimensions); -} +struct MAPSChunkHandler : ChunkHandler { + MAPSChunkHandler() : ChunkHandler('MAPS', CH_TABLE) {} -static void Load_MAPS() -{ - SlGlobList(_map_dimensions); - if (!ValidateMapSize(_map_dim_x, _map_dim_y)) { - SlErrorCorruptFmt("Invalid map size: %u x %u", _map_dim_x, _map_dim_y); + void Save() const override + { + SlTableHeader(_map_desc); + + _map_dim_x = MapSizeX(); + _map_dim_y = MapSizeY(); + + SlSetArrayIndex(0); + SlGlobList(_map_desc); } - AllocateMap(_map_dim_x, _map_dim_y); -} -static void Check_MAPS() -{ - SlGlobList(_map_dimensions); - _load_check_data.map_size_x = _map_dim_x; - _load_check_data.map_size_y = _map_dim_y; -} + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_map_desc, _map_sl_compat); + + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; + SlGlobList(slt); + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many MAPS entries"); + + AllocateMap(_map_dim_x, _map_dim_y); + } + + void LoadCheck(size_t) const override + { + const std::vector slt = SlCompatTableHeader(_map_desc, _map_sl_compat); + + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; + SlGlobList(slt); + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many MAPS entries"); + + _load_check_data.map_size_x = _map_dim_x; + _load_check_data.map_size_y = _map_dim_y; + } +}; static const uint MAP_SL_BUF_SIZE = 4096; -static void Load_MAPT() -{ - std::array buf; - TileIndex size = MapSize(); +struct MAPTChunkHandler : ChunkHandler { + MAPTChunkHandler() : ChunkHandler('MAPT', CH_RIFF) {} - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type = buf[j]; + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type = buf[j]; + } } -} -static void Check_MAPH_common() -{ - if (_sl_maybe_chillpp && (SlGetFieldLength() == 0 || SlGetFieldLength() == (size_t)_map_dim_x * (size_t)_map_dim_y * 2)) { - _sl_maybe_chillpp = false; - extern void SlXvChillPPSpecialSavegameVersions(); - SlXvChillPPSpecialSavegameVersions(); + void Save() const override + { + std::array 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; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + } } -} +}; -static void Check_MAPH() -{ - Check_MAPH_common(); - SlSkipBytes(SlGetFieldLength()); -} +struct MAPHChunkHandler : ChunkHandler { + MAPHChunkHandler() : ChunkHandler('MAPH', CH_RIFF) {} -static void Load_MAPH() -{ - Check_MAPH_common(); - if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { - if (SlGetFieldLength() != 0) { - _sl_xv_feature_versions[XSLFI_HEIGHT_8_BIT] = 2; - std::array buf; - TileIndex size = MapSize(); + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; + } + } + + void Save() const override + { + std::array 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++].height; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +}; + +struct MAPOChunkHandler : ChunkHandler { + MAPOChunkHandler() : ChunkHandler('MAPO', CH_RIFF) {} + + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; + } + } + + void Save() const override + { + std::array 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; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +}; + +struct MAP2ChunkHandler : ChunkHandler { + MAP2ChunkHandler() : ChunkHandler('MAP2', CH_RIFF) {} + + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, + /* In those versions the m2 was 8 bits */ + IsSavegameVersionBefore(SLV_5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16 + ); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j]; + } + } + + void Save() const override + { + std::array 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; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); + } + } +}; + +struct M3LOChunkHandler : ChunkHandler { + M3LOChunkHandler() : ChunkHandler('M3LO', CH_RIFF) {} + + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; + } + } + + void Save() const override + { + std::array 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; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +}; + +struct M3HIChunkHandler : ChunkHandler { + M3HIChunkHandler() : ChunkHandler('M3HI', CH_RIFF) {} + + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; + } + } + + void Save() const override + { + std::array 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; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +}; + +struct MAP5ChunkHandler : ChunkHandler { + MAP5ChunkHandler() : ChunkHandler('MAP5', CH_RIFF) {} + + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; + } + } + + void Save() const override + { + std::array 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; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +}; + +struct MAPEChunkHandler : ChunkHandler { + MAPEChunkHandler() : ChunkHandler('MAPE', CH_RIFF) {} + + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + + if (IsSavegameVersionBefore(SLV_42)) { for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; + /* 1024, otherwise we overflow on 64x64 maps! */ + SlCopy(buf.data(), 1024, SLE_UINT8); + for (uint j = 0; j != 1024; j++) { + _me[i++].m6 = GB(buf[j], 0, 2); + _me[i++].m6 = GB(buf[j], 2, 2); + _me[i++].m6 = GB(buf[j], 4, 2); + _me[i++].m6 = GB(buf[j], 6, 2); + } + } + } else { + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m6 = buf[j]; } } - return; } - std::array buf; - TileIndex size = MapSize(); + void Save() const override + { + std::array buf; + TileIndex size = MapSize(); - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; - } -} - -static void Load_MAP1() -{ - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; - } -} - -static void Load_MAP2() -{ - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, - /* In those versions the m2 was 8 bits */ - IsSavegameVersionBefore(SLV_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 Load_MAP3() -{ - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; - } -} - -static void Load_MAP4() -{ - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; - } -} - -static void Load_MAP5() -{ - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; - } -} - -static void Load_MAP6() -{ - std::array buf; - TileIndex size = MapSize(); - - if (IsSavegameVersionBefore(SLV_42)) { + SlSetLength(size); for (TileIndex i = 0; i != size;) { - /* 1024, otherwise we overflow on 64x64 maps! */ - SlArray(buf.data(), 1024, SLE_UINT8); - for (uint j = 0; j != 1024; j++) { - _me[i++].m6 = GB(buf[j], 0, 2); - _me[i++].m6 = GB(buf[j], 2, 2); - _me[i++].m6 = GB(buf[j], 4, 2); - _me[i++].m6 = GB(buf[j], 6, 2); - } + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m6; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } - } else { + } +}; + +struct MAP7ChunkHandler : ChunkHandler { + MAP7ChunkHandler() : ChunkHandler('MAP7', CH_RIFF) {} + + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m6 = buf[j]; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; } } -} -static void Load_MAP7() -{ - std::array buf; - TileIndex size = MapSize(); + void Save() const override + { + std::array buf; + TileIndex size = MapSize(); - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; - } -} - -static void Load_MAP8() -{ - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m8 = buf[j]; - } -} - -static void Load_WMAP() -{ - static_assert(sizeof(Tile) == 8); - static_assert(sizeof(TileExtended) == 4); - assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 1 || _sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2); - - ReadBuffer *reader = ReadBuffer::GetCurrent(); - const TileIndex size = MapSize(); - -#if TTD_ENDIAN == TTD_LITTLE_ENDIAN - reader->CopyBytes((byte *) _m, size * 8); -#else - for (TileIndex i = 0; i != size; i++) { - reader->CheckBytes(8); - _m[i].type = reader->RawReadByte(); - _m[i].height = reader->RawReadByte(); - uint16 m2 = reader->RawReadByte(); - m2 |= ((uint16) reader->RawReadByte()) << 8; - _m[i].m2 = m2; - _m[i].m1 = reader->RawReadByte(); - _m[i].m3 = reader->RawReadByte(); - _m[i].m4 = reader->RawReadByte(); - _m[i].m5 = reader->RawReadByte(); - } -#endif - - if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 1) { - for (TileIndex i = 0; i != size; i++) { - reader->CheckBytes(2); - _me[i].m6 = reader->RawReadByte(); - _me[i].m7 = reader->RawReadByte(); + SlSetLength(size); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m7; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } - } else if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2) { -#if TTD_ENDIAN == TTD_LITTLE_ENDIAN - reader->CopyBytes((byte *) _me, size * 4); -#else - for (TileIndex i = 0; i != size; i++) { - reader->CheckBytes(4); - _me[i].m6 = reader->RawReadByte(); - _me[i].m7 = reader->RawReadByte(); - uint16 m8 = reader->RawReadByte(); - m8 |= ((uint16) reader->RawReadByte()) << 8; - _me[i].m8 = m8; + } +}; + +struct MAP8ChunkHandler : ChunkHandler { + MAP8ChunkHandler() : ChunkHandler('MAP8', CH_RIFF) {} + + void Load() const override + { + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m8 = buf[j]; } -#endif - } else { - NOT_REACHED(); } -} -static void Save_WMAP() -{ - static_assert(sizeof(Tile) == 8); - static_assert(sizeof(TileExtended) == 4); - assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2); + void Save() const override + { + std::array buf; + TileIndex size = MapSize(); - MemoryDumper *dumper = MemoryDumper::GetCurrent(); - const TileIndex size = MapSize(); - SlSetLength(size * 12); - -#if TTD_ENDIAN == TTD_LITTLE_ENDIAN - dumper->CopyBytes((byte *) _m, size * 8); - dumper->CopyBytes((byte *) _me, size * 4); -#else - for (TileIndex i = 0; i != size; i++) { - dumper->CheckBytes(8); - dumper->RawWriteByte(_m[i].type); - dumper->RawWriteByte(_m[i].height); - dumper->RawWriteByte(GB(_m[i].m2, 0, 8)); - dumper->RawWriteByte(GB(_m[i].m2, 8, 8)); - dumper->RawWriteByte(_m[i].m1); - dumper->RawWriteByte(_m[i].m3); - dumper->RawWriteByte(_m[i].m4); - dumper->RawWriteByte(_m[i].m5); + SlSetLength(size * sizeof(uint16)); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m8; + SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); + } } - for (TileIndex i = 0; i != size; i++) { - dumper->CheckBytes(4); - dumper->RawWriteByte(_me[i].m6); - dumper->RawWriteByte(_me[i].m7); - dumper->RawWriteByte(GB(_me[i].m8, 0, 8)); - dumper->RawWriteByte(GB(_me[i].m8, 8, 8)); - } -#endif -} - -struct MAPT { - typedef uint8 FieldT; - static const FieldT &GetField(TileIndex t) { return _m[t].type; } }; -struct MAPH { - typedef uint8 FieldT; - static const FieldT &GetField(TileIndex t) { return _m[t].height; } -}; - -struct MAP1 { - typedef uint8 FieldT; - static const FieldT &GetField(TileIndex t) { return _m[t].m1; } -}; - -struct MAP2 { - typedef uint16 FieldT; - static const FieldT &GetField(TileIndex t) { return _m[t].m2; } -}; - -struct MAP3 { - typedef uint8 FieldT; - static const FieldT &GetField(TileIndex t) { return _m[t].m3; } -}; - -struct MAP4 { - typedef uint8 FieldT; - static const FieldT &GetField(TileIndex t) { return _m[t].m4; } -}; - -struct MAP5 { - typedef uint8 FieldT; - static const FieldT &GetField(TileIndex t) { return _m[t].m5; } -}; - -struct MAP6 { - typedef uint8 FieldT; - static const FieldT &GetField(TileIndex t) { return _me[t].m6; } -}; - -struct MAP7 { - typedef uint8 FieldT; - static const FieldT &GetField(TileIndex t) { return _me[t].m7; } -}; - -struct MAP8 { - typedef uint16 FieldT; - static const FieldT &GetField(TileIndex t) { return _me[t].m8; } -}; - -template -struct MAP_VarType {}; - -template <> -struct MAP_VarType -{ - static const VarType var_type = SLE_UINT8; -}; - -template <> -struct MAP_VarType -{ - static const VarType var_type = SLE_UINT16; -}; - -template -static void Save_MAP() -{ - assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 0); - - std::array buf; - TileIndex size = MapSize(); - - SlSetLength(size * sizeof(typename T::FieldT)); - for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = T::GetField(i++); - SlArray(buf.data(), MAP_SL_BUF_SIZE, MAP_VarType::var_type); - } -} - -static ChunkSaveLoadSpecialOpResult Special_WMAP(uint32 chunk_id, ChunkSaveLoadSpecialOp op) -{ - switch (op) { - case CSLSO_SHOULD_SAVE_CHUNK: - if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 0) return CSLSOR_DONT_SAVE_CHUNK; - break; - - default: - break; - } - return CSLSOR_NONE; -} - -static ChunkSaveLoadSpecialOpResult Special_MAP_Chunks(uint32 chunk_id, ChunkSaveLoadSpecialOp op) -{ - switch (op) { - case CSLSO_SHOULD_SAVE_CHUNK: - if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] != 0) return CSLSOR_DONT_SAVE_CHUNK; - break; - - default: - break; - } - return CSLSOR_NONE; -} - -static const ChunkHandler map_chunk_handlers[] = { - { 'MAPS', Save_MAPS, Load_MAPS, nullptr, Check_MAPS, CH_RIFF }, - { 'MAPT', Save_MAP, Load_MAPT, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'MAPH', Save_MAP, Load_MAPH, nullptr, Check_MAPH, CH_RIFF, Special_MAP_Chunks }, - { 'MAPO', Save_MAP, Load_MAP1, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'MAP2', Save_MAP, Load_MAP2, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'M3LO', Save_MAP, Load_MAP3, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'M3HI', Save_MAP, Load_MAP4, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'MAP5', Save_MAP, Load_MAP5, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'MAPE', Save_MAP, Load_MAP6, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'MAP7', Save_MAP, Load_MAP7, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'MAP8', Save_MAP, Load_MAP8, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, - { 'WMAP', Save_WMAP, Load_WMAP, nullptr, nullptr, CH_RIFF, Special_WMAP }, +static const MAPSChunkHandler MAPS; +static const MAPTChunkHandler MAPT; +static const MAPHChunkHandler MAPH; +static const MAPOChunkHandler MAPO; +static const MAP2ChunkHandler MAP2; +static const M3LOChunkHandler M3LO; +static const M3HIChunkHandler M3HI; +static const MAP5ChunkHandler MAP5; +static const MAPEChunkHandler MAPE; +static const MAP7ChunkHandler MAP7; +static const MAP8ChunkHandler MAP8; +static const ChunkHandlerRef map_chunk_handlers[] = { + MAPS, + MAPT, + MAPH, + MAPO, + MAP2, + M3LO, + M3HI, + MAP5, + MAPE, + MAP7, + MAP8, }; extern const ChunkHandlerTable _map_chunk_handlers(map_chunk_handlers); + +} diff --git a/src/saveload/misc_sl.cpp b/src/saveload/misc_sl.cpp index 26071eef7a..6e563b6c2f 100644 --- a/src/saveload/misc_sl.cpp +++ b/src/saveload/misc_sl.cpp @@ -8,6 +8,10 @@ /** @file misc_sl.cpp Saving and loading of things that didn't fit anywhere else */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/misc_sl_compat.h" + #include "../date_func.h" #include "../zoom_func.h" #include "../window_gui.h" @@ -16,171 +20,128 @@ #include "../gfx_func.h" #include "../core/random_func.hpp" #include "../fios.h" -#include "../road_type.h" -#include "../core/checksum_func.hpp" -#include "../event_logs.h" #include "../timer/timer.h" #include "../timer/timer_game_tick.h" -#include "saveload.h" - #include "../safeguards.h" extern TileIndex _cur_tileloop_tile; extern TileIndex _aux_tileloop_tile; extern uint16 _disaster_delay; extern byte _trees_tick_ctr; -extern uint64 _aspect_cfg_hash; /* Keep track of current game position */ -int _saved_scrollpos_x; -int _saved_scrollpos_y; -ZoomLevel _saved_scrollpos_zoom; +extern int _saved_scrollpos_x; +extern int _saved_scrollpos_y; +extern ZoomLevel _saved_scrollpos_zoom; -void SaveViewportBeforeSaveGame() -{ - const Window *w = FindWindowById(WC_MAIN_WINDOW, 0); - - if (w != nullptr) { - _saved_scrollpos_x = w->viewport->scrollpos_x; - _saved_scrollpos_y = w->viewport->scrollpos_y; - _saved_scrollpos_zoom = w->viewport->zoom; - } -} - -void ResetViewportAfterLoadGame() -{ - Window *w = GetMainWindow(); - - 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 = std::min(_saved_scrollpos_zoom, ZOOM_LVL_MAX); - vp->virtual_width = ScaleByZoom(vp->width, vp->zoom); - vp->virtual_height = ScaleByZoom(vp->height, vp->zoom); - - /* If zoom_max is ZOOM_LVL_MIN then the setting has not been loaded yet, therefore all levels are allowed. */ - if (_settings_client.gui.zoom_max != ZOOM_LVL_MIN) { - /* Ensure zoom level is allowed */ - while (vp->zoom < _settings_client.gui.zoom_min) DoZoomInOutWindow(ZOOM_OUT, w); - while (vp->zoom > _settings_client.gui.zoom_max) DoZoomInOutWindow(ZOOM_IN, w); - } - - DoZoomInOutWindow(ZOOM_NONE, w); // update button status - MarkWholeScreenDirty(); -} - -byte _age_cargo_skip_counter; ///< Skip aging of cargo? Used before savegame version 162. +extern byte _age_cargo_skip_counter; ///< Skip aging of cargo? Used before savegame version 162. extern TimeoutTimer _new_competitor_timeout; +namespace upstream_sl { + static const SaveLoad _date_desc[] = { - SLEG_CONDVAR(_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLEG_CONDVAR(_date, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLEG_VAR(_date_fract, SLE_UINT16), - SLEG_CONDVAR_X(_tick_counter, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)), - SLEG_CONDVAR_X(_tick_counter, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)), - SLEG_CONDVAR_X(_tick_skip_counter, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH)), - SLEG_CONDVAR_X(_scaled_tick_counter, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), - SLEG_CONDVAR_X(_scaled_date_ticks_offset, SLE_INT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_157), // _vehicle_id_ctr_day - SLEG_CONDVAR(_age_cargo_skip_counter, SLE_UINT8, SL_MIN_VERSION, SLV_162), - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_46), - SLEG_CONDVAR(_cur_tileloop_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLEG_CONDVAR(_cur_tileloop_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLEG_VAR(_disaster_delay, SLE_UINT16), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120), - SLEG_VAR(_random.state[0], SLE_UINT32), - SLEG_VAR(_random.state[1], SLE_UINT32), - SLEG_CONDVAR_X(_state_checksum.state, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATE_CHECKSUM)), - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_10), - SLE_CONDNULL(4, SLV_10, SLV_120), - SLEG_VAR(_cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32), - SLEG_CONDVAR(_new_competitor_timeout.period, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109), - SLEG_CONDVAR_X(_new_competitor_timeout.period, SLE_UINT32, SLV_109, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE, 0, 0)), - SLEG_VAR(_trees_tick_ctr, SLE_UINT8), - SLEG_CONDVAR(_pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION), - SLEG_CONDVAR_X(_game_events_overall, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)), - SLEG_CONDVAR_X(_road_layout_change_counter, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 4, 6)), // _extra_aspects - SLEG_CONDVAR_X(_aspect_cfg_hash, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 7)), - SLEG_CONDVAR_X(_aux_tileloop_tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUX_TILE_LOOP)), - SLE_CONDNULL(4, SLV_11, SLV_120), - SLEG_CONDVAR_X(_new_competitor_timeout.period, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), - SLEG_CONDVAR_X(_new_competitor_timeout.storage.elapsed, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), - SLEG_CONDVAR_X(_new_competitor_timeout.fired, SLE_BOOL, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), + SLEG_CONDVAR("date", _date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLEG_CONDVAR("date", _date, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLEG_VAR("date_fract", _date_fract, SLE_UINT16), + SLEG_CONDVAR("tick_counter", _tick_counter, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SLV_U64_TICK_COUNTER), + SLEG_CONDVAR("tick_counter", _tick_counter, SLE_UINT64, SLV_U64_TICK_COUNTER, SL_MAX_VERSION), + SLEG_CONDVAR("age_cargo_skip_counter", _age_cargo_skip_counter, SLE_UINT8, SL_MIN_VERSION, SLV_162), + SLEG_CONDVAR("cur_tileloop_tile", _cur_tileloop_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLEG_CONDVAR("cur_tileloop_tile", _cur_tileloop_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLEG_VAR("next_disaster_start", _disaster_delay, SLE_UINT16), + SLEG_VAR("random_state[0]", _random.state[0], SLE_UINT32), + SLEG_VAR("random_state[1]", _random.state[1], SLE_UINT32), + SLEG_VAR("company_tick_counter", _cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32), + SLEG_VAR("trees_tick_counter", _trees_tick_ctr, SLE_UINT8), + SLEG_CONDVAR("pause_mode", _pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION), + /* For older savegames, we load the current value as the "period"; afterload will set the "fired" and "elapsed". */ + SLEG_CONDVAR("next_competitor_start", _new_competitor_timeout.period, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109), + SLEG_CONDVAR("next_competitor_start", _new_competitor_timeout.period, SLE_UINT32, SLV_109, SLV_AI_START_DATE), + SLEG_CONDVAR("competitors_interval", _new_competitor_timeout.period, SLE_UINT32, SLV_AI_START_DATE, SL_MAX_VERSION), + SLEG_CONDVAR("competitors_interval_elapsed", _new_competitor_timeout.storage.elapsed, SLE_UINT32, SLV_AI_START_DATE, SL_MAX_VERSION), + SLEG_CONDVAR("competitors_interval_fired", _new_competitor_timeout.fired, SLE_BOOL, SLV_AI_START_DATE, SL_MAX_VERSION), }; static const SaveLoad _date_check_desc[] = { - SLEG_CONDVAR(_load_check_data.current_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLEG_CONDVAR(_load_check_data.current_date, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_NULL(2), // _date_fract - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)), // _tick_counter - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)), // _tick_counter - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH)), // _tick_skip_counter - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), // _scaled_tick_counter - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), // _scaled_date_ticks_offset - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_157), // _vehicle_id_ctr_day - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_162), // _age_cargo_skip_counter - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_46), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), // _cur_tileloop_tile - SLE_CONDNULL(4, SLV_6, SL_MAX_VERSION), // _cur_tileloop_tile - SLE_NULL(2), // _disaster_delay - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120), - SLE_NULL(4), // _random.state[0] - SLE_NULL(4), // _random.state[1] - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATE_CHECKSUM)), // _state_checksum.state - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_10), - SLE_CONDNULL(4, SLV_10, SLV_120), - SLE_NULL(1), // _cur_company_tick_index - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_109), // _new_competitor_timeout.period - SLE_CONDNULL_X(4, SLV_109, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE, 0, 0)), // _new_competitor_timeout.period - SLE_NULL(1), // _trees_tick_ctr - SLE_CONDNULL(1, SLV_4, SL_MAX_VERSION), // _pause_mode - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)), // _game_events_overall - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), // _road_layout_change_counter - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 4, 6)), // _extra_aspects - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 7)), // _aspect_cfg_hash - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUX_TILE_LOOP)), // _aux_tileloop_tile - SLE_CONDNULL(4, SLV_11, SLV_120), - SLE_CONDNULL_X(9, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), // _new_competitor_timeout + SLEG_CONDVAR("date", _load_check_data.current_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLEG_CONDVAR("date", _load_check_data.current_date, SLE_INT32, SLV_31, SL_MAX_VERSION), }; /* 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); - SetScaledTickVariables(); -} +struct DATEChunkHandler : ChunkHandler { + DATEChunkHandler() : ChunkHandler('DATE', CH_TABLE) {} -static void Check_DATE() -{ - SlGlobList(_date_check_desc); - if (IsSavegameVersionBefore(SLV_31)) { - _load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR; + void Save() const override + { + SlTableHeader(_date_desc); + + SlSetArrayIndex(0); + SlGlobList(_date_desc); + } + + void LoadCommon(const SaveLoadTable &slt, const SaveLoadCompatTable &slct) const + { + const std::vector oslt = SlCompatTableHeader(slt, slct); + + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; + SlGlobList(oslt); + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many DATE entries"); + } + + void Load() const override + { + this->LoadCommon(_date_desc, _date_sl_compat); } -} -static const SaveLoad _view_desc[] = { - SLEG_CONDVAR(_saved_scrollpos_x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLEG_CONDVAR(_saved_scrollpos_x, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLEG_CONDVAR(_saved_scrollpos_y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLEG_CONDVAR(_saved_scrollpos_y, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLEG_VAR(_saved_scrollpos_zoom, SLE_UINT8), + void LoadCheck(size_t) const override + { + this->LoadCommon(_date_check_desc, _date_check_sl_compat); + + if (IsSavegameVersionBefore(SLV_31)) { + _load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR; + } + } }; -static void SaveLoad_VIEW() -{ - SlGlobList(_view_desc); -} +static const SaveLoad _view_desc[] = { + SLEG_CONDVAR("x", _saved_scrollpos_x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLEG_CONDVAR("x", _saved_scrollpos_x, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLEG_CONDVAR("y", _saved_scrollpos_y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLEG_CONDVAR("y", _saved_scrollpos_y, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLEG_VAR("zoom", _saved_scrollpos_zoom, SLE_UINT8), +}; -static const ChunkHandler misc_chunk_handlers[] = { - { 'DATE', SaveLoad_DATE, SaveLoad_DATE, nullptr, Check_DATE, CH_RIFF }, - { 'VIEW', SaveLoad_VIEW, SaveLoad_VIEW, nullptr, nullptr, CH_RIFF }, +struct VIEWChunkHandler : ChunkHandler { + VIEWChunkHandler() : ChunkHandler('VIEW', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(_view_desc); + + SlSetArrayIndex(0); + SlGlobList(_view_desc); + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_view_desc, _view_sl_compat); + + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; + SlGlobList(slt); + if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many DATE entries"); + } +}; + +static const DATEChunkHandler DATE; +static const VIEWChunkHandler VIEW; +static const ChunkHandlerRef misc_chunk_handlers[] = { + DATE, + VIEW, }; extern const ChunkHandlerTable _misc_chunk_handlers(misc_chunk_handlers); + +} diff --git a/src/saveload/newgrf_sl.cpp b/src/saveload/newgrf_sl.cpp index dd5932a750..ae36fe7756 100644 --- a/src/saveload/newgrf_sl.cpp +++ b/src/saveload/newgrf_sl.cpp @@ -8,63 +8,55 @@ /** @file newgrf_sl.cpp Code handling saving and loading of newgrf config */ #include "../stdafx.h" -#include "../fios.h" -#include "../string_func.h" #include "saveload.h" +#include "compat/newgrf_sl_compat.h" + #include "newgrf_sl.h" +#include "../fios.h" #include "../safeguards.h" +namespace upstream_sl { + /** Save and load the mapping between a spec and the NewGRF it came from. */ -static const SaveLoad _newgrf_mapping_desc_old[] = { +static const SaveLoad _newgrf_mapping_desc[] = { SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), - SLE_VAR(EntityIDMapping, entity_id, SLE_FILE_U8 | SLE_VAR_U16), - SLE_VAR(EntityIDMapping, substitute_id, SLE_FILE_U8 | SLE_VAR_U16), -}; -static const SaveLoad _newgrf_mapping_desc_new[] = { - SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), - SLE_VAR(EntityIDMapping, entity_id, SLE_UINT16), - SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT16), + SLE_CONDVAR(EntityIDMapping, entity_id, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_EXTEND_ENTITY_MAPPING), + SLE_CONDVAR(EntityIDMapping, entity_id, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION), + SLE_CONDVAR(EntityIDMapping, substitute_id, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_EXTEND_ENTITY_MAPPING), + SLE_CONDVAR(EntityIDMapping, substitute_id, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION), }; /** * Save a GRF ID + local id -> OpenTTD's id mapping. - * @param mapping The mapping to save. */ -void Save_NewGRFMapping(const OverrideManagerBase &mapping) +void NewGRFMappingChunkHandler::Save() const { - for (uint i = 0; i < mapping.GetMaxMapping(); i++) { - if (mapping.mappings[i].grfid == 0 && - mapping.mappings[i].entity_id == 0) continue; - SlSetArrayIndex(i); - SlSetLength(4 + 2 + 2); - SlObjectSaveFiltered(const_cast(&mapping.mappings[i]), _newgrf_mapping_desc_new); // _newgrf_mapping_desc_new has no conditionals - } + // removed + NOT_REACHED(); } /** * Load a GRF ID + local id -> OpenTTD's id mapping. - * @param mapping The mapping to load. */ -void Load_NewGRFMapping(OverrideManagerBase &mapping) +void NewGRFMappingChunkHandler::Load() const { + const std::vector slt = SlCompatTableHeader(_newgrf_mapping_desc, _newgrf_mapping_sl_compat); + /* Clear the current mapping stored. * This will create the manager if ever it is not yet done */ - mapping.ResetMapping(); + this->mapping.ResetMapping(); - uint max_id = mapping.GetMaxMapping(); - - SaveLoadTable slt = SlXvIsFeaturePresent(XSLFI_NEWGRF_ENTITY_EXTRA) ? SaveLoadTable(_newgrf_mapping_desc_new) : SaveLoadTable(_newgrf_mapping_desc_old); + uint max_id = this->mapping.GetMaxMapping(); int index; while ((index = SlIterateArray()) != -1) { - if (unlikely((uint)index >= max_id)) SlErrorCorrupt("Too many NewGRF entity mappings"); - SlObjectLoadFiltered(&mapping.mappings[index], slt); // _newgrf_mapping_desc_old/_newgrf_mapping_desc_new has no conditionals + if ((uint)index >= max_id) SlErrorCorrupt("Too many NewGRF entity mappings"); + SlObject(&this->mapping.mappings[index], slt); } } -static std::string _grf_name; static const SaveLoad _grfconfig_desc[] = { SLE_SSTR(GRFConfig, filename, SLE_STR), @@ -74,60 +66,66 @@ static const SaveLoad _grfconfig_desc[] = { SLE_ARR(GRFConfig, param, SLE_UINT32, 0x80), SLE_VAR(GRFConfig, num_params, SLE_UINT8), SLE_CONDVAR(GRFConfig, palette, SLE_UINT8, SLV_101, SL_MAX_VERSION), - SLEG_CONDSSTR_X(_grf_name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_INFO_EXTRA)), }; -static void Save_NGRF() -{ - int index = 0; +struct NGRFChunkHandler : ChunkHandler { + NGRFChunkHandler() : ChunkHandler('NGRF', CH_TABLE) {} - for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) { - if (HasBit(c->flags, GCF_STATIC) || HasBit(c->flags, GCF_INIT_ONLY)) continue; - SlSetArrayIndex(index++); - _grf_name = str_strip_all_scc(GetDefaultLangGRFStringFromGRFText(c->name)); - SlObject(c, _grfconfig_desc); - } -} + void Save() const override + { + SlTableHeader(_grfconfig_desc); + int index = 0; -static void Load_NGRF_common(GRFConfig *&grfconfig) -{ - ClearGRFConfigList(&grfconfig); - while (SlIterateArray() != -1) { - GRFConfig *c = new GRFConfig(); - SlObject(c, _grfconfig_desc); - if (SlXvIsFeaturePresent(XSLFI_NEWGRF_INFO_EXTRA)) { - AddGRFTextToList(c->name, 0x7F, c->ident.grfid, false, _grf_name.c_str()); + for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) { + if (HasBit(c->flags, GCF_STATIC) || HasBit(c->flags, GCF_INIT_ONLY)) continue; + SlSetArrayIndex(index++); + SlObject(c, _grfconfig_desc); } - if (IsSavegameVersionBefore(SLV_101)) c->SetSuitablePalette(); - AppendToGRFConfigList(&grfconfig, c); } -} -static void Load_NGRF() -{ - Load_NGRF_common(_grfconfig); - if (_game_mode == GM_MENU) { - /* Intro game must not have NewGRF. */ - if (_grfconfig != nullptr) SlErrorCorrupt("The intro game must not use NewGRF"); + void LoadCommon(GRFConfig *&grfconfig) const + { + const std::vector slt = SlCompatTableHeader(_grfconfig_desc, _grfconfig_sl_compat); - /* Activate intro NewGRFs (townnames) */ - ResetGRFConfig(false); - } else { - /* Append static NewGRF configuration */ - AppendStaticGRFConfigs(&_grfconfig); + ClearGRFConfigList(&grfconfig); + while (SlIterateArray() != -1) { + GRFConfig *c = new GRFConfig(); + SlObject(c, slt); + if (IsSavegameVersionBefore(SLV_101)) c->SetSuitablePalette(); + AppendToGRFConfigList(&grfconfig, c); + } } -} -static void Check_NGRF() -{ - Load_NGRF_common(_load_check_data.grfconfig); -} + void Load() const override + { + this->LoadCommon(_grfconfig); -static const ChunkHandler newgrf_chunk_handlers[] = { - { 'NGRF', Save_NGRF, Load_NGRF, nullptr, Check_NGRF, CH_ARRAY } + if (_game_mode == GM_MENU) { + /* Intro game must not have NewGRF. */ + if (_grfconfig != nullptr) SlErrorCorrupt("The intro game must not use NewGRF"); + + /* Activate intro NewGRFs (townnames) */ + ResetGRFConfig(false); + } else { + /* Append static NewGRF configuration */ + AppendStaticGRFConfigs(&_grfconfig); + } + } + + void LoadCheck(size_t) const override + { + this->LoadCommon(_load_check_data.grfconfig); + } +}; + +static const NGRFChunkHandler NGRF; +static const ChunkHandlerRef newgrf_chunk_handlers[] = { + NGRF, }; extern const ChunkHandlerTable _newgrf_chunk_handlers(newgrf_chunk_handlers); + +} diff --git a/src/saveload/newgrf_sl.h b/src/saveload/newgrf_sl.h index 191046de25..91e90fb77a 100644 --- a/src/saveload/newgrf_sl.h +++ b/src/saveload/newgrf_sl.h @@ -7,12 +7,21 @@ /** @file newgrf_sl.h Code handling saving and loading of NewGRF mappings. */ -#ifndef SAVELOAD_NEWGRF_SL_H -#define SAVELOAD_NEWGRF_SL_H +#ifndef SAVELOAD_UPSTREAM_NEWGRF_SL_H +#define SAVELOAD_UPSTREAM_NEWGRF_SL_H #include "../newgrf_commons.h" -void Save_NewGRFMapping(const OverrideManagerBase &mapping); -void Load_NewGRFMapping(OverrideManagerBase &mapping); +namespace upstream_sl { -#endif /* SAVELOAD_NEWGRF_SL_H */ +struct NewGRFMappingChunkHandler : ChunkHandler { + OverrideManagerBase &mapping; + + NewGRFMappingChunkHandler(uint32 id, OverrideManagerBase &mapping) : ChunkHandler(id, CH_TABLE), mapping(mapping) {} + void Save() const override; + void Load() const override; +}; + +} + +#endif /* SAVELOAD_UPSTREAM_NEWGRF_SL_H */ diff --git a/src/saveload/object_sl.cpp b/src/saveload/object_sl.cpp index dcf5fd65cb..9bbd74e59b 100644 --- a/src/saveload/object_sl.cpp +++ b/src/saveload/object_sl.cpp @@ -8,14 +8,18 @@ /** @file object_sl.cpp Code handling saving and loading of objects */ #include "../stdafx.h" -#include "../object_base.h" -#include "../object_map.h" #include "saveload.h" +#include "compat/object_sl_compat.h" + +#include "../object_base.h" +#include "../object_map.h" #include "newgrf_sl.h" #include "../safeguards.h" +namespace upstream_sl { + static const SaveLoad _object_desc[] = { SLE_VAR(Object, location.tile, SLE_UINT32), SLE_VAR(Object, location.w, SLE_FILE_U8 | SLE_VAR_U16), @@ -27,48 +31,54 @@ static const SaveLoad _object_desc[] = { SLE_CONDVAR(Object, type, SLE_UINT16, SLV_186, SL_MAX_VERSION), }; -static void Save_OBJS() -{ - /* Write the objects */ - for (Object *o : Object::Iterate()) { - SlSetArrayIndex(o->index); - SlObject(o, _object_desc); - } -} +struct OBJSChunkHandler : ChunkHandler { + OBJSChunkHandler() : ChunkHandler('OBJS', CH_TABLE) {} -static void Load_OBJS() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Object *o = new (index) Object(); - SlObject(o, _object_desc); - } -} + void Save() const override + { + SlTableHeader(_object_desc); -static void Ptrs_OBJS() -{ - for (Object *o : Object::Iterate()) { - SlObject(o, _object_desc); - if (IsSavegameVersionBefore(SLV_148) && !IsTileType(o->location.tile, MP_OBJECT)) { - /* Due to a small bug stale objects could remain. */ - delete o; + /* Write the objects */ + for (Object *o : Object::Iterate()) { + SlSetArrayIndex(o->index); + SlObject(o, _object_desc); } } -} -static void Save_OBID() -{ - Save_NewGRFMapping(_object_mngr); -} + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_object_desc, _object_sl_compat); -static void Load_OBID() -{ - Load_NewGRFMapping(_object_mngr); -} + int index; + while ((index = SlIterateArray()) != -1) { + Object *o = new (index) Object(); + SlObject(o, slt); + } + } -static const ChunkHandler object_chunk_handlers[] = { - { 'OBID', Save_OBID, Load_OBID, nullptr, nullptr, CH_ARRAY }, - { 'OBJS', Save_OBJS, Load_OBJS, Ptrs_OBJS, nullptr, CH_ARRAY }, + void FixPointers() const override + { + for (Object *o : Object::Iterate()) { + SlObject(o, _object_desc); + if (IsSavegameVersionBefore(SLV_148) && !IsTileType(o->location.tile, MP_OBJECT)) { + /* Due to a small bug stale objects could remain. */ + delete o; + } + } + } +}; + +struct OBIDChunkHandler : NewGRFMappingChunkHandler { + OBIDChunkHandler() : NewGRFMappingChunkHandler('OBID', _object_mngr) {} +}; + +static const OBIDChunkHandler OBID; +static const OBJSChunkHandler OBJS; +static const ChunkHandlerRef object_chunk_handlers[] = { + OBID, + OBJS, }; extern const ChunkHandlerTable _object_chunk_handlers(object_chunk_handlers); + +} diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index 721a5a0d30..13bf9f752a 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -8,72 +8,18 @@ /** @file order_sl.cpp Code handling saving and loading of orders */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/order_sl_compat.h" + #include "../order_backup.h" +#include "../order_base.h" #include "../settings_type.h" #include "../network/network.h" -#include "saveload_internal.h" - #include "../safeguards.h" -static uint32 _jokerpp_separation_mode; -std::vector _jokerpp_auto_separation; -std::vector _jokerpp_non_auto_separation; - -/** - * Converts this order from an old savegame's version; - * it moves all bits to the new location. - */ -void Order::ConvertFromOldSavegame() -{ - uint8 old_flags = this->flags; - this->flags = 0; - - /* First handle non-stop - use value from savegame if possible, else use value from config file */ - if (_settings_client.gui.sg_new_nonstop || (IsSavegameVersionBefore(SLV_22) && _savegame_type != SGT_TTO && _savegame_type != SGT_TTD && (_settings_client.gui.new_nonstop || _settings_game.order.nonstop_only))) { - /* 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 { - /* old OTTD versions stored full_load_any in config file - assume it was enabled when loading */ - this->SetLoadType(_settings_client.gui.sg_full_load_any || IsSavegameVersionBefore(SLV_22) ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD); - } - - if (this->IsType(OT_GOTO_STATION)) this->SetStopLocation(OSL_PLATFORM_FAR_END); - - /* 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); - } -} +namespace upstream_sl { /** * Unpacks a order from savegames with version 4 and lower @@ -82,17 +28,7 @@ void Order::ConvertFromOldSavegame() */ static Order UnpackVersion4Order(uint16 packed) { - return Order(((uint64) GB(packed, 8, 8)) << 24 | ((uint64) GB(packed, 4, 4)) << 8 | ((uint64) GB(packed, 0, 4))); -} - -/** - * Unpacks a order from savegames with version 5.1 and lower - * @param packed packed order - * @return unpacked order - */ -static Order UnpackVersion5Order(uint32 packed) -{ - return Order(((uint64) GB(packed, 16, 16)) << 24 | ((uint64) GB(packed, 8, 8)) << 8 | ((uint64) GB(packed, 0, 8))); + return Order(GB(packed, 8, 8) << 16 | GB(packed, 4, 4) << 8 | GB(packed, 0, 4)); } /** @@ -117,221 +53,146 @@ SaveLoadTable GetOrderDescription() { static const SaveLoad _order_desc[] = { SLE_VAR(Order, type, SLE_UINT8), - SLE_CONDVAR_X(Order, flags, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 0, 0)), - SLE_CONDVAR_X(Order, flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 1)), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_VAR(Order, flags, SLE_FILE_U8 | SLE_VAR_U16), SLE_VAR(Order, dest, SLE_UINT16), SLE_REF(Order, next, REF_ORDER), SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION), - SLE_CONDNULL(1, SLV_36, SLV_182), // refit_subtype - SLE_CONDVAR_X(Order, occupancy, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_OCCUPANCY)), - SLE_CONDVAR_X(Order, wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), - SLE_CONDVAR_X(Order, wait_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), - SLE_CONDVAR_X(Order, travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), - SLE_CONDVAR_X(Order, travel_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), + SLE_CONDVAR(Order, wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), + SLE_CONDVAR(Order, travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), SLE_CONDVAR(Order, max_speed, SLE_UINT16, SLV_172, SL_MAX_VERSION), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_COND_ORDERS, 1, 6)), // jump_counter - - /* 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, SLV_5, SLV_36), }; return _order_desc; } -static std::vector _filtered_desc; +struct ORDRChunkHandler : ChunkHandler { + ORDRChunkHandler() : ChunkHandler('ORDR', CH_TABLE) {} -static void Save_ORDR() -{ - _filtered_desc = SlFilterObject(GetOrderDescription()); - for (Order *order : Order::Iterate()) { - SlSetArrayIndex(order->index); - SlObjectSaveFiltered(order, _filtered_desc); - } -} + void Save() const override + { + const SaveLoadTable slt = GetOrderDescription(); + SlTableHeader(slt); -static void Load_ORDR() -{ - if (IsSavegameVersionBefore(SLV_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(); - - if (IsSavegameVersionBefore(SLV_5)) { - /* Pre-version 5 had another layout for orders - * (uint16 instead of uint32) */ - len /= sizeof(uint16); - uint16 *orders = MallocT(len + 1); - - SlArray(orders, len, SLE_UINT16); - - for (size_t i = 0; i < len; ++i) { - Order *o = new (i) Order(); - o->AssignOrder(UnpackVersion4Order(orders[i])); - } - - free(orders); - } else if (IsSavegameVersionBefore(SLV_5, 2)) { - len /= sizeof(uint32); - uint32 *orders = MallocT(len + 1); - - SlArray(orders, len, SLE_UINT32); - - for (size_t i = 0; i < len; ++i) { - Order *o = new (i) Order(); - o->AssignOrder(UnpackVersion5Order(orders[i])); - } - - free(orders); - } - - /* Update all the next pointer */ - for (Order *o : Order::Iterate()) { - size_t order_index = o->index; - /* Delete invalid orders */ - if (o->IsType(OT_NOTHING)) { - delete o; - continue; - } - /* The orders were built like this: - * While the order is valid, set the previous will get its next pointer set */ - Order *prev = Order::GetIfValid(order_index - 1); - if (prev != nullptr) prev->next = o; - } - } else { - _filtered_desc = SlFilterObject(GetOrderDescription()); - int index; - - while ((index = SlIterateArray()) != -1) { - Order *order = new (index) Order(); - SlObjectLoadFiltered(order, _filtered_desc); - } - } -} - -const SaveLoadTable GetOrderExtraInfoDescription() -{ - static const SaveLoad _order_extra_info_desc[] = { - SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, 32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 1, 2)), - SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, NUM_CARGO, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 3)), - SLE_CONDVAR_X(OrderExtraInfo, xflags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)), - SLE_CONDVAR_X(OrderExtraInfo, xdata, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA)), - SLE_CONDVAR_X(OrderExtraInfo, dispatch_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 3)), - SLE_CONDVAR_X(OrderExtraInfo, colour, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA, 2)), - }; - - return _order_extra_info_desc; -} - -void Save_ORDX() -{ - _filtered_desc = SlFilterObject(GetOrderExtraInfoDescription()); - for (Order *order : Order::Iterate()) { - if (order->extra) { + for (Order *order : Order::Iterate()) { SlSetArrayIndex(order->index); - SlObjectSaveFiltered(order->extra.get(), _filtered_desc); + SlObject(order, slt); } } -} -void Load_ORDX() -{ - _filtered_desc = SlFilterObject(GetOrderExtraInfoDescription()); - int index; - while ((index = SlIterateArray()) != -1) { - Order *order = Order::GetIfValid(index); - assert(order != nullptr); - order->AllocExtraInfo(); - SlObjectLoadFiltered(order->extra.get(), _filtered_desc); + void Load() const override + { + if (IsSavegameVersionBefore(SLV_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(); + + if (IsSavegameVersionBefore(SLV_5)) { + /* Pre-version 5 had another layout for orders + * (uint16 instead of uint32) */ + len /= sizeof(uint16); + uint16 *orders = MallocT(len + 1); + + SlCopy(orders, len, SLE_UINT16); + + for (size_t i = 0; i < len; ++i) { + Order *o = new (i) Order(); + o->AssignOrder(UnpackVersion4Order(orders[i])); + } + + free(orders); + } else if (IsSavegameVersionBefore(SLV_5, 2)) { + len /= sizeof(uint32); + uint32 *orders = MallocT(len + 1); + + SlCopy(orders, len, SLE_UINT32); + + for (size_t i = 0; i < len; ++i) { + new (i) Order(orders[i]); + } + + free(orders); + } + + /* Update all the next pointer */ + for (Order *o : Order::Iterate()) { + size_t order_index = o->index; + /* Delete invalid orders */ + if (o->IsType(OT_NOTHING)) { + delete o; + continue; + } + /* The orders were built like this: + * While the order is valid, set the previous will get its next pointer set */ + Order *prev = Order::GetIfValid(order_index - 1); + if (prev != nullptr) prev->next = o; + } + } else { + const std::vector slt = SlCompatTableHeader(GetOrderDescription(), _order_sl_compat); + + int index; + + while ((index = SlIterateArray()) != -1) { + Order *order = new (index) Order(); + SlObject(order, slt); + } + } } -} -static void Ptrs_ORDR() -{ - /* Orders from old savegames have pointers corrected in Load_ORDR */ - if (IsSavegameVersionBefore(SLV_5, 2)) return; + void FixPointers() const override + { + /* Orders from old savegames have pointers corrected in Load_ORDR */ + if (IsSavegameVersionBefore(SLV_5, 2)) return; - for (Order *o : Order::Iterate()) { - SlObject(o, GetOrderDescription()); + for (Order *o : Order::Iterate()) { + SlObject(o, GetOrderDescription()); + } } -} - -SaveLoadTable GetDispatchScheduleDescription() -{ - static const SaveLoad _order_extra_info_desc[] = { - SLE_VARVEC(DispatchSchedule, scheduled_dispatch, SLE_UINT32), - SLE_VAR(DispatchSchedule, scheduled_dispatch_duration, SLE_UINT32), - SLE_VAR(DispatchSchedule, scheduled_dispatch_start_date, SLE_INT32), - SLE_VAR(DispatchSchedule, scheduled_dispatch_start_full_date_fract, SLE_UINT16), - SLE_VAR(DispatchSchedule, scheduled_dispatch_last_dispatch, SLE_INT32), - SLE_VAR(DispatchSchedule, scheduled_dispatch_max_delay, SLE_INT32), - SLE_CONDSSTR_X(DispatchSchedule, name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 4)), - }; - - return _order_extra_info_desc; -} +}; SaveLoadTable GetOrderListDescription() { static const SaveLoad _orderlist_desc[] = { - SLE_REF(OrderList, first, REF_ORDER), - SLEG_CONDVAR_X(_jokerpp_separation_mode, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), - SLE_CONDNULL_X(21, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + SLE_REF(OrderList, first, REF_ORDER), }; return _orderlist_desc; } -static void Save_ORDL() -{ - for (OrderList *list : OrderList::Iterate()) { - SlSetArrayIndex(list->index); - SlAutolength([](void *data) { - OrderList *list = static_cast(data); +struct ORDLChunkHandler : ChunkHandler { + ORDLChunkHandler() : ChunkHandler('ORDL', CH_TABLE) {} + + void Save() const override + { + const SaveLoadTable slt = GetOrderListDescription(); + SlTableHeader(slt); + + for (OrderList *list : OrderList::Iterate()) { + SlSetArrayIndex(list->index); + SlObject(list, slt); + } + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(GetOrderListDescription(), _orderlist_sl_compat); + + int index; + + while ((index = SlIterateArray()) != -1) { + /* set num_orders to 0 so it's a valid OrderList */ + OrderList *list = new (index) OrderList(0); + SlObject(list, slt); + } + + } + + void FixPointers() const override + { + for (OrderList *list : OrderList::Iterate()) { SlObject(list, GetOrderListDescription()); - SlWriteUint32(list->GetScheduledDispatchScheduleCount()); - for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) { - SlObject(&ds, GetDispatchScheduleDescription()); - } - }, list); - } -} - -static void Load_ORDL() -{ - _jokerpp_auto_separation.clear(); - _jokerpp_non_auto_separation.clear(); - int index; - - while ((index = SlIterateArray()) != -1) { - /* set num_orders to 0 so it's a valid OrderList */ - OrderList *list = new (index) OrderList(0); - SlObject(list, GetOrderListDescription()); - if (SlXvIsFeaturePresent(XSLFI_JOKERPP)) { - if (_jokerpp_separation_mode == 0) { - _jokerpp_auto_separation.push_back(list); - } else { - _jokerpp_non_auto_separation.push_back(list); - } - } - if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH)) { - uint count = SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3) ? SlReadUint32() : 1; - list->GetScheduledDispatchScheduleSet().resize(count); - for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) { - SlObject(&ds, GetDispatchScheduleDescription()); - } } } -} - -void Ptrs_ORDL() -{ - for (OrderList *list : OrderList::Iterate()) { - SlObject(list, GetOrderListDescription()); - list->ReindexOrderList(); - } -} +}; SaveLoadTable GetOrderBackupDescription() { @@ -341,76 +202,72 @@ SaveLoadTable GetOrderBackupDescription() SLE_VAR(OrderBackup, group, SLE_UINT16), SLE_CONDVAR(OrderBackup, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SL_MIN_VERSION, SLV_192), SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION), - SLE_STR(OrderBackup, name, SLE_STR, 0), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_192), // clone (2 bytes of pointer, i.e. garbage) + SLE_SSTR(OrderBackup, name, SLE_STR), SLE_CONDREF(OrderBackup, clone, REF_VEHICLE, SLV_192, SL_MAX_VERSION), - SLE_VAR(OrderBackup, cur_real_order_index, SLE_VEHORDERID), - SLE_CONDVAR(OrderBackup, cur_implicit_order_index, SLE_VEHORDERID, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR_X(OrderBackup, cur_timetable_order_index, SLE_VEHORDERID, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)), + SLE_VAR(OrderBackup, cur_real_order_index, SLE_FILE_U8 | SLE_VAR_U16), + SLE_CONDVAR(OrderBackup, cur_implicit_order_index, SLE_FILE_U8 | SLE_VAR_U16, SLV_176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, SLV_176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, lateness_counter, SLE_INT32, SLV_176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, timetable_start, SLE_INT32, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR_X(OrderBackup,timetable_start_subticks, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)), - SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_176, SLV_180), - SLE_CONDVAR_X(OrderBackup, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 0, 0)), - SLE_CONDVAR_X(OrderBackup, vehicle_flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 1)), + SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_176, SLV_180), + SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION), SLE_REF(OrderBackup, orders, REF_ORDER), - SLE_CONDNULL_X(18, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2, 2)), }; return _order_backup_desc; } -void Save_BKOR() -{ - /* We only save this when we're a network server - * as we want this information on our clients. For - * normal games this information isn't needed. */ - if (!_networking || !_network_server) return; +struct BKORChunkHandler : ChunkHandler { + BKORChunkHandler() : ChunkHandler('BKOR', CH_TABLE) {} - for (OrderBackup *ob : OrderBackup::Iterate()) { - SlSetArrayIndex(ob->index); - SlAutolength([](void *data) { - OrderBackup *ob = static_cast(data); - SlObject(ob, GetOrderBackupDescription()); - SlWriteUint32((uint)ob->dispatch_schedules.size()); - for (DispatchSchedule &ds : ob->dispatch_schedules) { - SlObject(&ds, GetDispatchScheduleDescription()); - } - }, ob); - } -} + void Save() const override + { + const SaveLoadTable slt = GetOrderBackupDescription(); + SlTableHeader(slt); -void Load_BKOR() -{ - int index; + /* We only save this when we're a network server + * as we want this information on our clients. For + * normal games this information isn't needed. */ + if (!_networking || !_network_server) return; - while ((index = SlIterateArray()) != -1) { - /* set num_orders to 0 so it's a valid OrderList */ - OrderBackup *ob = new (index) OrderBackup(); - SlObject(ob, GetOrderBackupDescription()); - if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3)) { - uint count = SlReadUint32(); - ob->dispatch_schedules.resize(count); - for (DispatchSchedule &ds : ob->dispatch_schedules) { - SlObject(&ds, GetDispatchScheduleDescription()); - } + for (OrderBackup *ob : OrderBackup::Iterate()) { + SlSetArrayIndex(ob->index); + SlObject(ob, slt); } } -} -static void Ptrs_BKOR() -{ - for (OrderBackup *ob : OrderBackup::Iterate()) { - SlObject(ob, GetOrderBackupDescription()); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(GetOrderBackupDescription(), _order_backup_sl_compat); + + int index; + + while ((index = SlIterateArray()) != -1) { + /* set num_orders to 0 so it's a valid OrderList */ + OrderBackup *ob = new (index) OrderBackup(); + SlObject(ob, slt); + if (ob->cur_real_order_index == 0xFF) ob->cur_real_order_index = INVALID_VEH_ORDER_ID; + if (ob->cur_implicit_order_index == 0xFF) ob->cur_implicit_order_index = INVALID_VEH_ORDER_ID; + } } -} -static const ChunkHandler order_chunk_handlers[] = { - { 'BKOR', Save_BKOR, Load_BKOR, Ptrs_BKOR, nullptr, CH_ARRAY }, - { 'ORDR', Save_ORDR, Load_ORDR, Ptrs_ORDR, nullptr, CH_ARRAY }, - { 'ORDL', Save_ORDL, Load_ORDL, Ptrs_ORDL, nullptr, CH_ARRAY }, - { 'ORDX', Save_ORDX, Load_ORDX, nullptr, nullptr, CH_SPARSE_ARRAY }, + void FixPointers() const override + { + for (OrderBackup *ob : OrderBackup::Iterate()) { + SlObject(ob, GetOrderBackupDescription()); + } + } +}; + +static const BKORChunkHandler BKOR; +static const ORDRChunkHandler ORDR; +static const ORDLChunkHandler ORDL; +static const ChunkHandlerRef order_chunk_handlers[] = { + BKOR, + ORDR, + ORDL, }; extern const ChunkHandlerTable _order_chunk_handlers(order_chunk_handlers); + +} diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index fc4bf9f1fd..a9404ef8a0 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -21,88 +21,33 @@ */ #include "../stdafx.h" + +#include "saveload.h" #include "../debug.h" -#include "../station_base.h" -#include "../thread.h" -#include "../town.h" -#include "../network/network.h" -#include "../window_func.h" +#include "../string_func.h" #include "../strings_func.h" -#include "../core/endian_func.hpp" +#include "../core/bitmath_func.hpp" #include "../vehicle_base.h" -#include "../company_func.h" -#include "../date_func.h" -#include "../autoreplace_base.h" -#include "../roadstop_base.h" +#include "../station_base.h" #include "../linkgraph/linkgraph.h" #include "../linkgraph/linkgraphjob.h" -#include "../statusbar_gui.h" -#include "../fileio_func.h" -#include "../gamelog.h" -#include "../string_func.h" -#include "../string_func_extra.h" -#include "../fios.h" -#include "../error.h" -#include "../scope.h" +#include "../town.h" +#include "../roadstop_base.h" +#include "../autoreplace_base.h" + #include -#include -#include -#ifdef __EMSCRIPTEN__ -# include -#endif - -#include "../tbtr_template_vehicle.h" - -#include "table/strings.h" - -#include "saveload_internal.h" -#include "saveload_filter.h" -#include "saveload_buffer.h" -#include "extended_ver_sl.h" - #include #include - -#include "../thread.h" -#include -#include -#if defined(__MINGW32__) -#include "../3rdparty/mingw-std-threads/mingw.mutex.h" -#include "../3rdparty/mingw-std-threads/mingw.condition_variable.h" -#endif +#include +#include +#include #include "../safeguards.h" -extern const SaveLoadVersion SAVEGAME_VERSION = SLV_CUSTOM_SUBSIDY_DURATION; ///< Current savegame version of OpenTTD. -extern const SaveLoadVersion MAX_LOAD_SAVEGAME_VERSION = (SaveLoadVersion)(SL_MAX_VERSION - 1); ///< Max loadable savegame version of OpenTTD. - -const SaveLoadVersion SAVEGAME_VERSION_EXT = (SaveLoadVersion)(0x8000); ///< Savegame extension indicator mask - -SavegameType _savegame_type; ///< type of savegame we are loading -FileToSaveLoad _file_to_saveload; ///< File to save or load in the openttd loop. - -uint32 _ttdp_version; ///< version of TTDP savegame (if applicable) -SaveLoadVersion _sl_version; ///< the major savegame version identifier -byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! -std::string _savegame_format; ///< how to compress savegames -bool _do_autosave; ///< are we doing an autosave at the moment? - -extern bool _sl_is_ext_version; -extern bool _sl_maybe_springpp; -extern bool _sl_maybe_chillpp; -extern bool _sl_upstream_mode; +StringID RemapOldStringID(StringID s); +std::string CopyFromOldName(StringID id); namespace upstream_sl { - void SlNullPointers(); - void SlLoadChunks(); - void SlLoadChunkByID(uint32 id); - void SlLoadCheckChunks(); - void SlLoadCheckChunkByID(uint32 id); - void SlFixPointers(); - void SlFixPointerChunkByID(uint32 id); - void SlSaveChunkChunkByID(uint32 id); - void SlResetLoadState(); -} /** What are we currently doing? */ enum SaveLoadAction { @@ -116,163 +61,35 @@ enum SaveLoadAction { enum NeedLength { NL_NONE = 0, ///< not working in NeedLength mode NL_WANTLENGTH = 1, ///< writing length and data + NL_CALCLENGTH = 2, ///< need to calculate the length }; -void ReadBuffer::SkipBytesSlowPath(size_t bytes) -{ - bytes -= (this->bufe - this->bufp); - while (true) { - size_t len = this->reader->Read(this->buf, lengthof(this->buf)); - if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); - this->read += len; - if (len >= bytes) { - this->bufp = this->buf + bytes; - this->bufe = this->buf + len; - return; - } else { - bytes -= len; - } - } -} - -void ReadBuffer::AcquireBytes() -{ - size_t remainder = this->bufe - this->bufp; - if (remainder) { - memmove(this->buf, this->bufp, remainder); - } - size_t len = this->reader->Read(this->buf + remainder, lengthof(this->buf) - remainder); - if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); - - this->read += len; - this->bufp = this->buf; - this->bufe = this->buf + remainder + len; -} - -void MemoryDumper::FinaliseBlock() -{ - assert(this->saved_buf == nullptr); - if (!this->blocks.empty()) { - size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf); - this->blocks.back().size = s; - this->completed_block_bytes += s; - } - this->buf = this->bufe = nullptr; -} - -void MemoryDumper::AllocateBuffer() -{ - if (this->saved_buf) { - const size_t offset = this->buf - this->autolen_buf; - const size_t size = (this->autolen_buf_end - this->autolen_buf) * 2; - this->autolen_buf = ReallocT(this->autolen_buf, size); - this->autolen_buf_end = this->autolen_buf + size; - this->buf = this->autolen_buf + offset; - this->bufe = this->autolen_buf_end; - return; - } - this->FinaliseBlock(); - this->buf = MallocT(MEMORY_CHUNK_SIZE); - this->blocks.emplace_back(this->buf); - this->bufe = this->buf + MEMORY_CHUNK_SIZE; -} - -/** - * Flush this dumper into a writer. - * @param writer The filter we want to use. - */ -void MemoryDumper::Flush(SaveFilter *writer) -{ - this->FinaliseBlock(); - - size_t block_count = this->blocks.size(); - for (size_t i = 0; i < block_count; i++) { - writer->Write(this->blocks[i].data, this->blocks[i].size); - } - - writer->Finish(); -} - -void MemoryDumper::StartAutoLength() -{ - assert(this->saved_buf == nullptr); - - this->saved_buf = this->buf; - this->saved_bufe = this->bufe; - this->buf = this->autolen_buf; - this->bufe = this->autolen_buf_end; -} - -std::pair MemoryDumper::StopAutoLength() -{ - assert(this->saved_buf != nullptr); - auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf); - - this->buf = this->saved_buf; - this->bufe = this->saved_bufe; - this->saved_buf = this->saved_bufe = nullptr; - return res; -} - -/** - * Get the size of the memory dump made so far. - * @return The size. - */ -size_t MemoryDumper::GetSize() const -{ - assert(this->saved_buf == nullptr); - return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0); -} - /** The saveload struct, containing reader-writer functions, buffer, version, etc. */ struct SaveLoadParams { SaveLoadAction action; ///< are we doing a save or a load atm. NeedLength need_length; ///< working in NeedLength (Autolength) mode? 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 - - MemoryDumper *dumper; ///< Memory dumper to write the savegame to. - SaveFilter *sf; ///< Filter to write the savegame to. - - ReadBuffer *reader; ///< Savegame reading buffer. - LoadFilter *lf; ///< Filter to read the savegame from. - - StringID error_str; ///< the translatable error message to show - std::string extra_msg; ///< the error message - - bool saveinprogress; ///< Whether there is currently a save in progress. - SaveModeFlags save_flags; ///< Save mode flags + bool expect_table_header; ///< In the case of a table, if the header is saved/loaded. }; static SaveLoadParams _sl; ///< Parameters used for/at saveload. -ReadBuffer *ReadBuffer::GetCurrent() -{ - return _sl.reader; -} - -MemoryDumper *MemoryDumper::GetCurrent() -{ - return _sl.dumper; -} - -static const std::vector &ChunkHandlers() +static const std::vector &ChunkHandlers() { /* These define the chunks */ - extern const ChunkHandlerTable _version_ext_chunk_handlers; extern const ChunkHandlerTable _gamelog_chunk_handlers; extern const ChunkHandlerTable _map_chunk_handlers; extern const ChunkHandlerTable _misc_chunk_handlers; - extern const ChunkHandlerTable _name_chunk_handlers; + //extern const ChunkHandlerTable _name_chunk_handlers; extern const ChunkHandlerTable _cheat_chunk_handlers; extern const ChunkHandlerTable _setting_chunk_handlers; extern const ChunkHandlerTable _company_chunk_handlers; extern const ChunkHandlerTable _engine_chunk_handlers; extern const ChunkHandlerTable _veh_chunk_handlers; - extern const ChunkHandlerTable _waypoint_chunk_handlers; + //extern const ChunkHandlerTable _waypoint_chunk_handlers; extern const ChunkHandlerTable _depot_chunk_handlers; extern const ChunkHandlerTable _order_chunk_handlers; extern const ChunkHandlerTable _town_chunk_handlers; @@ -297,28 +114,17 @@ static const std::vector &ChunkHandlers() extern const ChunkHandlerTable _airport_chunk_handlers; extern const ChunkHandlerTable _object_chunk_handlers; extern const ChunkHandlerTable _persistent_storage_chunk_handlers; - extern const ChunkHandlerTable _trace_restrict_chunk_handlers; - extern const ChunkHandlerTable _signal_chunk_handlers; - extern const ChunkHandlerTable _plan_chunk_handlers; - extern const ChunkHandlerTable _template_replacement_chunk_handlers; - extern const ChunkHandlerTable _template_vehicle_chunk_handlers; - extern const ChunkHandlerTable _bridge_signal_chunk_handlers; - extern const ChunkHandlerTable _tunnel_chunk_handlers; - extern const ChunkHandlerTable _train_speed_adaptation_chunk_handlers; - extern const ChunkHandlerTable _new_signal_chunk_handlers; - extern const ChunkHandlerTable _debug_chunk_handlers; /** List of all chunks in a savegame. */ static const ChunkHandlerTable _chunk_handler_tables[] = { - _version_ext_chunk_handlers, _gamelog_chunk_handlers, _map_chunk_handlers, _misc_chunk_handlers, - _name_chunk_handlers, + //_name_chunk_handlers, _cheat_chunk_handlers, _setting_chunk_handlers, _veh_chunk_handlers, - _waypoint_chunk_handlers, + //_waypoint_chunk_handlers, _depot_chunk_handlers, _order_chunk_handlers, _industry_chunk_handlers, @@ -345,19 +151,9 @@ static const std::vector &ChunkHandlers() _airport_chunk_handlers, _object_chunk_handlers, _persistent_storage_chunk_handlers, - _trace_restrict_chunk_handlers, - _signal_chunk_handlers, - _plan_chunk_handlers, - _template_replacement_chunk_handlers, - _template_vehicle_chunk_handlers, - _bridge_signal_chunk_handlers, - _tunnel_chunk_handlers, - _train_speed_adaptation_chunk_handlers, - _new_signal_chunk_handlers, - _debug_chunk_handlers, }; - static std::vector _chunk_handlers; + static std::vector _chunk_handlers; if (_chunk_handlers.empty()) { for (auto &chunk_handler_table : _chunk_handler_tables) { @@ -371,226 +167,23 @@ static const std::vector &ChunkHandlers() } /** Null all pointers (convert index -> nullptr) */ -static void SlNullPointers() +void SlNullPointers() { - if (_sl_upstream_mode) { - upstream_sl::SlNullPointers(); - return; - } - _sl.action = SLA_NULL; /* We don't want any savegame conversion code to run * during NULLing; especially those that try to get * pointers from other pools. */ _sl_version = SAVEGAME_VERSION; - SlXvSetCurrentState(); - for (auto &ch : ChunkHandlers()) { - if (ch.ptrs_proc != nullptr) { - DEBUG(sl, 3, "Nulling pointers for %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); - ch.ptrs_proc(); - } + for (const ChunkHandler &ch : ChunkHandlers()) { + DEBUG(sl, 3, "Nulling pointers for %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); + ch.FixPointers(); } assert(_sl.action == SLA_NULL); } -struct ThreadSlErrorException { - StringID string; - std::string extra_msg; -}; - -/** - * Error handler. Sets everything up to show an error message and to clean - * up the mess of a partial savegame load. - * @param string The translatable error message to show. - * @param extra_msg An extra error message coming from one of the APIs. - * @note This function does never return as it throws an exception to - * break out of all the saveload code. - */ -void NORETURN SlError(StringID string, std::string extra_msg) -{ - if (IsNonMainThread() && IsNonGameThread() && _sl.action != SLA_SAVE) { - throw ThreadSlErrorException{ string, std::move(extra_msg) }; - } - - /* Distinguish between loading into _load_check_data vs. normal save/load. */ - if (_sl.action == SLA_LOAD_CHECK) { - _load_check_data.error = string; - _load_check_data.error_msg = std::move(extra_msg); - } else { - _sl.error_str = string; - _sl.extra_msg = std::move(extra_msg); - } - - /* We have to nullptr all pointers here; we might be in a state where - * the pointers are actually filled with indices, which means that - * when we access them during cleaning the pool dereferences of - * those indices will be made with segmentation faults as result. */ - if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers(); - - /* Logging could be active. */ - GamelogStopAnyAction(); - - throw std::exception(); -} - -/** - * As SlError, except that it takes a format string and additional parameters - */ -void NORETURN CDECL SlErrorFmt(StringID string, const char *msg, ...) -{ - va_list va; - va_start(va, msg); - std::string str = stdstr_vfmt(msg, va); - va_end(va); - SlError(string, std::move(str)); -} - -/** - * Error handler for corrupt savegames. Sets everything up to show the - * error message and to clean up the mess of a partial savegame load. - * @param msg Location the corruption has been spotted. - * @note This function does never return as it throws an exception to - * break out of all the saveload code. - */ -void NORETURN SlErrorCorrupt(std::string msg) -{ - SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, std::move(msg)); -} - -/** - * Issue an SlErrorCorrupt with a format string. - * @param format format string - * @param ... arguments to format string - * @note This function does never return as it throws an exception to - * break out of all the saveload code. - */ -void NORETURN CDECL SlErrorCorruptFmt(const char *format, ...) -{ - va_list va; - va_start(va, format); - std::string str = stdstr_vfmt(format, va); - va_end(va); - SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, std::move(str)); -} - -typedef void (*AsyncSaveFinishProc)(); ///< Callback for when the savegame loading is finished. -static std::atomic _async_save_finish; ///< Callback to call when the savegame loading is finished. -static std::thread _save_thread; ///< The thread we're using to compress and write a savegame - -/** - * Called by save thread to tell we finished saving. - * @param proc The callback to call when saving is done. - */ -static void SetAsyncSaveFinish(AsyncSaveFinishProc proc) -{ - if (_exit_game) return; - while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10); - - _async_save_finish.store(proc, std::memory_order_release); -} - -/** - * Handle async save finishes. - */ -void ProcessAsyncSaveFinish() -{ - AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel); - if (proc == nullptr) return; - - proc(); - - if (_save_thread.joinable()) { - _save_thread.join(); - } -} - -/** - * Wrapper for reading a byte from the buffer. - * @return The read byte. - */ -byte SlReadByte() -{ - return _sl.reader->ReadByte(); -} - -/** - * 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 - */ -void SlSkipBytes(size_t length) -{ - return _sl.reader->SkipBytes(length); -} - -int SlReadUint16() -{ - _sl.reader->CheckBytes(2); - return _sl.reader->RawReadUint16(); -} - -uint32 SlReadUint32() -{ - _sl.reader->CheckBytes(4); - return _sl.reader->RawReadUint32(); -} - -uint64 SlReadUint64() -{ - _sl.reader->CheckBytes(8); - return _sl.reader->RawReadUint64(); -} - -/** - * Wrapper for writing a byte to the dumper. - * @param b The byte to write. - */ -void SlWriteByte(byte b) -{ - _sl.dumper->WriteByte(b); -} - -void SlWriteUint16(uint16 v) -{ - _sl.dumper->CheckBytes(2); - _sl.dumper->RawWriteUint16(v); -} - -void SlWriteUint32(uint32 v) -{ - _sl.dumper->CheckBytes(4); - _sl.dumper->RawWriteUint32(v); -} - -void SlWriteUint64(uint64 v) -{ - _sl.dumper->CheckBytes(8); - _sl.dumper->RawWriteUint64(v); -} - -/** - * Returns number of bytes read so far - * May only be called during a load/load check action - */ -size_t SlGetBytesRead() -{ - assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK); - return _sl.reader->GetSize(); -} - -/** - * Returns number of bytes written so far - * May only be called during a save action - */ -size_t SlGetBytesWritten() -{ - assert(_sl.action == SLA_SAVE); - return _sl.dumper->GetSize(); -} - /** * 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 @@ -697,6 +290,41 @@ static inline uint SlGetArrayLength(size_t length) return SlGetGammaLength(length); } +/** + * Return the type as saved/loaded inside the savegame. + */ +static uint8 GetSavegameFileType(const SaveLoad &sld) +{ + switch (sld.cmd) { + case SL_VAR: + return GetVarFileType(sld.conv); break; + + case SL_STR: + case SL_STDSTR: + case SL_ARR: + case SL_VECTOR: + case SL_DEQUE: + return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break; + + case SL_REF: + return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32; + + case SL_REFLIST: + case SL_REFDEQUE: + case SL_REFVEC: + return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD; + + case SL_SAVEBYTE: + return SLE_FILE_U8; + + case SL_STRUCT: + case SL_STRUCTLIST: + return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD; + + default: NOT_REACHED(); + } +} + /** * Return the size in bytes of a certain type of normal/atomic variable * as it appears in memory. See VarTypes @@ -728,9 +356,9 @@ static inline uint SlCalcConvMemLen(VarType conv) */ static inline byte SlCalcConvFileLen(VarType conv) { + static const byte conv_file_size[] = {0, 1, 1, 2, 2, 4, 4, 8, 8, 2}; + uint8 type = GetVarFileType(conv); - if (type == SLE_FILE_VEHORDERID) return SlXvIsFeaturePresent(XSLFI_MORE_VEHICLE_ORDERS) ? 2 : 1; - static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2}; assert(type < lengthof(conv_file_size)); return conv_file_size[type]; } @@ -759,23 +387,28 @@ int SlIterateArray() /* 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 && _sl.reader->GetSize() != _next_offs) { - DEBUG(sl, 1, "Invalid chunk size: " PRINTF_SIZE " != " PRINTF_SIZE, _sl.reader->GetSize(), _next_offs); - SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position " PRINTF_SIZE ", actually at " PRINTF_SIZE, _next_offs, _sl.reader->GetSize()); - } + if (_next_offs != 0 && SlGetBytesRead() != _next_offs) SlErrorCorrupt("Invalid chunk size"); for (;;) { uint length = SlReadArrayLength(); if (length == 0) { + assert(!_sl.expect_table_header); _next_offs = 0; return -1; } _sl.obj_len = --length; - _next_offs = _sl.reader->GetSize() + length; + _next_offs = SlGetBytesRead() + length; + + if (_sl.expect_table_header) { + _sl.expect_table_header = false; + return INT32_MAX; + } switch (_sl.block_mode) { + case CH_SPARSE_TABLE: case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break; + case CH_TABLE: case CH_ARRAY: index = _sl.array_index++; break; default: DEBUG(sl, 0, "SlIterateArray error"); @@ -792,7 +425,7 @@ int SlIterateArray() void SlSkipArray() { while (SlIterateArray() != -1) { - SlSkipBytes(_next_offs - _sl.reader->GetSize()); + SlSkipBytes(_next_offs - SlGetBytesRead()); } } @@ -808,27 +441,21 @@ void SlSetLength(size_t length) switch (_sl.need_length) { case NL_WANTLENGTH: _sl.need_length = NL_NONE; + if ((_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) && _sl.expect_table_header) { + _sl.expect_table_header = false; + SlWriteArrayLength(length + 1); + break; + } + 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 - * - * If we have more than 28 bits, use an extra uint32 and - * signal this using the extended chunk header */ -#ifdef POINTER_IS_64BIT - assert(length < (1LL << 32)); -#endif - if (length >= (1 << 28)) { - /* write out extended chunk header */ - SlWriteByte(CH_EXT_HDR); - SlWriteUint32(static_cast(SLCEHF_BIG_RIFF)); - } - SlWriteUint32(static_cast((length & 0xFFFFFF) | ((length >> 24) << 28))); - if (length >= (1 << 28)) { - SlWriteUint32(static_cast(length >> 28)); - } + * The uppermost 4 bits are bits 24:27 */ + assert(length < (1 << 28)); + SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28))); break; + case CH_TABLE: case CH_ARRAY: assert(_sl.last_array_index <= _sl.array_index); while (++_sl.last_array_index <= _sl.array_index) { @@ -836,6 +463,7 @@ void SlSetLength(size_t length) } SlWriteArrayLength(length + 1); break; + case CH_SPARSE_TABLE: case CH_SPARSE_ARRAY: SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index. SlWriteSparseIndex(_sl.array_index); @@ -844,6 +472,10 @@ void SlSetLength(size_t length) } break; + case NL_CALCLENGTH: + _sl.obj_len += (int)length; + break; + default: NOT_REACHED(); } } @@ -861,10 +493,10 @@ static void SlCopyBytes(void *ptr, size_t length) switch (_sl.action) { case SLA_LOAD_CHECK: case SLA_LOAD: - _sl.reader->CopyBytes(p, length); + for (; length != 0; length--) *p++ = SlReadByte(); break; case SLA_SAVE: - _sl.dumper->CopyBytes(p, length); + for (; length != 0; length--) SlWriteByte(*p++); break; default: NOT_REACHED(); } @@ -920,7 +552,6 @@ void WriteValue(void *ptr, VarType conv, int64 val) case SLE_VAR_I64: *(int64 *)ptr = val; break; case SLE_VAR_U64: *(uint64*)ptr = val; break; case SLE_VAR_NAME: *reinterpret_cast(ptr) = CopyFromOldName(val); break; - case SLE_VAR_CNAME: *(TinyString*)ptr = CopyFromOldName(val); break; case SLE_VAR_NULL: break; default: NOT_REACHED(); } @@ -934,10 +565,9 @@ void WriteValue(void *ptr, VarType conv, int64 val) * @param ptr The object being filled/read * @param conv VarType type of the current element of the struct */ -template -static void SlSaveLoadConvGeneric(void *ptr, VarType conv) +static void SlSaveLoadConv(void *ptr, VarType conv) { - switch (action) { + switch (_sl.action) { case SLA_SAVE: { int64 x = ReadValue(ptr, conv); @@ -947,7 +577,6 @@ static void SlSaveLoadConvGeneric(void *ptr, VarType conv) 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_VEHORDERID: case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break; case SLE_FILE_I32: case SLE_FILE_U32: SlWriteUint32((uint32)x);break; @@ -971,14 +600,6 @@ static void SlSaveLoadConvGeneric(void *ptr, VarType conv) 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; - case SLE_FILE_VEHORDERID: - if (SlXvIsFeaturePresent(XSLFI_MORE_VEHICLE_ORDERS)) { - x = (uint16)SlReadUint16(); - } else { - VehicleOrderID id = (byte)SlReadByte(); - x = (id == 0xFF) ? INVALID_VEH_ORDER_ID : id; - } - break; default: NOT_REACHED(); } @@ -992,23 +613,6 @@ static void SlSaveLoadConvGeneric(void *ptr, VarType conv) } } -void SlSaveLoadConv(void *ptr, VarType conv) -{ - switch (_sl.action) { - case SLA_SAVE: - SlSaveLoadConvGeneric(ptr, conv); - return; - case SLA_LOAD_CHECK: - case SLA_LOAD: - SlSaveLoadConvGeneric(ptr, conv); - return; - case SLA_PTRS: - case SLA_NULL: - return; - default: NOT_REACHED(); - } -} - /** * 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 @@ -1024,18 +628,6 @@ static inline size_t SlCalcNetStringLen(const char *ptr, size_t length) return std::min(strlen(ptr), length - 1); } -/** - * Calculate the gross length of the std::string that it - * will occupy in the savegame. This includes the real length, - * and the length that the index will occupy. - * @param str reference to the std::string - * @return return the gross length of the string - */ -static inline size_t SlCalcStdStrLen(const std::string &str) -{ - return str.size() + SlGetArrayLength(str.size()); // also include the length of the index -} - /** * Calculate the gross length of the string that it * will occupy in the savegame. This includes the real length, returned @@ -1067,6 +659,21 @@ static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType con return len + SlGetArrayLength(len); // also include the length of the index } +/** + * 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 \c std::string. + * @return The gross length of the string. + */ +static inline size_t SlCalcStdStringLen(const void *ptr) +{ + const std::string *str = reinterpret_cast(ptr); + + size_t len = str->length(); + return len + SlGetArrayLength(len); // also include the length of the index +} + /** * Save/Load a string. * @param ptr the string being manipulated @@ -1152,14 +759,18 @@ static void SlString(void *ptr, size_t length, VarType conv) * @param ptr the string being manipulated * @param conv must be SLE_FILE_STRING */ -static void SlStdString(std::string &str, VarType conv) +static void SlStdString(void *ptr, VarType conv) { + std::string *str = reinterpret_cast(ptr); + switch (_sl.action) { case SLA_SAVE: { - SlWriteArrayLength(str.size()); - SlCopyBytes(str.data(), str.size()); + size_t len = str->length(); + SlWriteArrayLength(len); + SlCopyBytes(const_cast(static_cast(str->c_str())), len); break; } + case SLA_LOAD_CHECK: case SLA_LOAD: { size_t len = SlReadArrayLength(); @@ -1168,23 +779,27 @@ static void SlStdString(std::string &str, VarType conv) return; } - str.resize(len); - SlCopyBytes(str.data(), len); + char *buf = AllocaM(char, len + 1); + SlCopyBytes(buf, len); + buf[len] = '\0'; // properly terminate the string StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK; if ((conv & SLF_ALLOW_CONTROL) != 0) { settings = settings | SVS_ALLOW_CONTROL_CODE; if (IsSavegameVersionBefore(SLV_169)) { - char *buf = str.data(); - str.resize(str_fix_scc_encoded(buf, buf + str.size()) - buf); + str_fix_scc_encoded(buf, buf + len); } } if ((conv & SLF_ALLOW_NEWLINE) != 0) { settings = settings | SVS_ALLOW_NEWLINE; } - StrMakeValidInPlace(str, settings); - break; + + StrMakeValidInPlace(buf, buf + len, settings); + + // Store sanitized string. + str->assign(buf); } + case SLA_PTRS: break; case SLA_NULL: break; default: NOT_REACHED(); @@ -1192,43 +807,34 @@ static void SlStdString(std::string &str, VarType conv) } /** - * 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 + * Internal function to save/Load a list of SL_VARs. + * SlCopy() and SlArray() are very similar, with the exception of the header. + * This function represents the common part. + * @param object The object being manipulated. + * @param length The length of the object in elements + * @param conv VarType type of the items. */ -static inline size_t SlCalcArrayLen(size_t length, VarType conv) +static void SlCopyInternal(void *object, 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) -{ - if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return; - - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcArrayLen(length, conv)); + if (GetVarMemType(conv) == SLE_VAR_NULL) { + assert(_sl.action != SLA_SAVE); // Use SL_NULL if you want to write null-bytes + SlSkipBytes(length * SlCalcConvFileLen(conv)); + 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 */ + * as a byte-type. So detect this, and adjust object size accordingly */ if (_sl.action != SLA_SAVE && _sl_version == 0) { - /* all arrays except difficulty settings */ + /* all objects except difficulty settings */ if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID || conv == SLE_INT32 || conv == SLE_UINT32) { - SlCopyBytes(array, length * SlCalcConvFileLen(conv)); + SlCopyBytes(object, 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()); + ((int64*)object)[i] = (int32)BSWAP32(SlReadUint32()); } return; } @@ -1237,9 +843,9 @@ void SlArray(void *array, size_t length, VarType conv) /* 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); + SlCopyBytes(object, length); } else { - byte *a = (byte*)array; + byte *a = (byte*)object; byte mem_size = SlCalcConvMemLen(conv); for (; length != 0; length --) { @@ -1249,6 +855,79 @@ void SlArray(void *array, size_t length, VarType conv) } } +/** + * Copy a list of SL_VARs to/from a savegame. + * These entries are copied as-is, and you as caller have to make sure things + * like length-fields are calculated correctly. + * @param object The object being manipulated. + * @param length The length of the object in elements + * @param conv VarType type of the items. + */ +void SlCopy(void *object, size_t length, VarType conv) +{ + if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return; + + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(length * SlCalcConvFileLen(conv)); + /* Determine length only? */ + if (_sl.need_length == NL_CALCLENGTH) return; + } + + SlCopyInternal(object, length, conv); +} + +/** + * 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 + SlGetArrayLength(length); +} + +/** + * Save/Load the length of the array followed by the array of SL_VAR elements. + * @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.) + */ +static void SlArray(void *array, size_t length, VarType conv) +{ + switch (_sl.action) { + case SLA_SAVE: + SlWriteArrayLength(length); + SlCopyInternal(array, length, conv); + return; + + case SLA_LOAD_CHECK: + case SLA_LOAD: { + if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { + size_t sv_length = SlReadArrayLength(); + if (GetVarMemType(conv) == SLE_VAR_NULL) { + /* We don't know this field, so we assume the length in the savegame is correct. */ + length = sv_length; + } else if (sv_length != length) { + /* If the SLE_ARR changes size, a savegame bump is required + * and the developer should have written conversion lines. + * Error out to make this more visible. */ + SlErrorCorrupt("Fixed-length array is of wrong length"); + } + } + + SlCopyInternal(array, length, conv); + return; + } + + case SLA_PTRS: + case SLA_NULL: + return; + + default: + NOT_REACHED(); + } +} /** * Pointers cannot be saved to a savegame, so this functions gets @@ -1269,7 +948,6 @@ static size_t ReferenceToInt(const void *obj, SLRefType rt) switch (rt) { case REF_VEHICLE_OLD: // Old vehicles we save as new ones case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1; - case REF_TEMPLATE_VEHICLE: return ((const TemplateVehicle*)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; @@ -1329,10 +1007,6 @@ static void *IntToReference(size_t index, SLRefType rt) if (Vehicle::IsValidID(index)) return Vehicle::Get(index); SlErrorCorrupt("Referencing invalid Vehicle"); - case REF_TEMPLATE_VEHICLE: - if (TemplateVehicle::IsValidID(index)) return TemplateVehicle::Get(index); - SlErrorCorrupt("Referencing invalid TemplateVehicle"); - case REF_STATION: if (Station::IsValidID(index)) return Station::Get(index); SlErrorCorrupt("Referencing invalid Station"); @@ -1413,7 +1087,7 @@ public: const SlStorageT *list = static_cast(storage); - int type_size = SlCalcConvFileLen(SLE_FILE_U32); // Size of the length of the list. + int type_size = SlGetArrayLength(list->size()); int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : (VarType)SLE_FILE_U32); return list->size() * item_size + type_size; } @@ -1442,7 +1116,7 @@ public: switch (_sl.action) { case SLA_SAVE: - SlWriteUint32((uint32)list->size()); + SlWriteArrayLength(list->size()); for (auto &item : *list) { SlSaveLoadMember(cmd, &item, conv); @@ -1453,8 +1127,8 @@ public: case SLA_LOAD: { size_t length; switch (cmd) { - case SL_VAR: length = SlReadUint32(); break; - case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); break; + case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break; + case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break; default: NOT_REACHED(); } @@ -1486,28 +1160,29 @@ public: * @param list The std::list to find the size of. * @param conv VarType type of variable that is used for calculating the size. */ - template -static inline size_t SlCalcRefListLen(const void *list) +static inline size_t SlCalcRefListLen(const void *list, VarType conv) { - const PtrList *l = (const PtrList *) list; - - int type_size = IsSavegameVersionBefore(SLV_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; + return SlStorageHelper::SlCalcLen(list, conv, SL_REF); } /** - * Return the size in bytes of a list - * @param list The std::list to find the size of + * Return the size in bytes of a deque. + * @param list The std::list to find the size of. + * @param conv VarType type of variable that is used for calculating the size. */ - template -static inline size_t SlCalcVarListLen(const void *list, size_t item_size) +static inline size_t SlCalcRefDequeLen(const void *list, VarType conv) { - const PtrList *l = (const PtrList *) list; - /* Each entry is saved as item_size bytes, plus 4 bytes are used for the length - * of the list */ - return l->size() * item_size + 4; + return SlStorageHelper::SlCalcLen(list, conv, SL_REF); +} + +/** + * Return the size in bytes of a vector. + * @param list The std::list to find the size of. + * @param conv VarType type of variable that is used for calculating the size. + */ +static inline size_t SlCalcRefVectorLen(const void *list, VarType conv) +{ + return SlStorageHelper::SlCalcLen(list, conv, SL_REF); } /** @@ -1515,102 +1190,50 @@ static inline size_t SlCalcVarListLen(const void *list, size_t item_size) * @param list The list being manipulated. * @param conv VarType type of variable that is used for calculating the size. */ -template -static void SlRefList(void *list, SLRefType conv) +static void SlRefList(void *list, VarType conv) { /* Automatically calculate the length? */ if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcRefListLen(list)); + SlSetLength(SlCalcRefListLen(list, conv)); + /* Determine length only? */ + if (_sl.need_length == NL_CALCLENGTH) return; } - PtrList *l = (PtrList *)list; - - switch (_sl.action) { - case SLA_SAVE: { - SlWriteUint32((uint32)l->size()); - - typename PtrList::iterator iter; - for (iter = l->begin(); iter != l->end(); ++iter) { - void *ptr = *iter; - SlWriteUint32((uint32)ReferenceToInt(ptr, conv)); - } - break; - } - case SLA_LOAD_CHECK: - case SLA_LOAD: { - size_t length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); - - /* Load each reference and push to the end of the list */ - for (size_t i = 0; i < length; i++) { - size_t data = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); - l->push_back((void *)data); - } - break; - } - case SLA_PTRS: { - PtrList temp = *l; - - l->clear(); - typename PtrList::iterator iter; - for (iter = temp.begin(); iter != temp.end(); ++iter) { - void *ptr = IntToReference((size_t)*iter, conv); - l->push_back(ptr); - } - break; - } - case SLA_NULL: - l->clear(); - break; - default: NOT_REACHED(); - } + SlStorageHelper::SlSaveLoad(list, conv, SL_REF); } /** - * Save/Load a list. - * @param list The list being manipulated - * @param conv VarType type of the list + * Save/Load a deque. + * @param list The list being manipulated. + * @param conv VarType type of variable that is used for calculating the size. */ -template -static void SlVarList(void *list, VarType conv) +static void SlRefDeque(void *list, VarType conv) { - const size_t size_len = SlCalcConvMemLen(conv); /* Automatically calculate the length? */ if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcVarListLen(list, size_len)); + SlSetLength(SlCalcRefDequeLen(list, conv)); + /* Determine length only? */ + if (_sl.need_length == NL_CALCLENGTH) return; } - PtrList *l = (PtrList *)list; + SlStorageHelper::SlSaveLoad(list, conv, SL_REF); +} - switch (_sl.action) { - case SLA_SAVE: { - SlWriteUint32((uint32)l->size()); - - typename PtrList::iterator iter; - for (iter = l->begin(); iter != l->end(); ++iter) { - SlSaveLoadConv(&(*iter), conv); - } - break; - } - case SLA_LOAD_CHECK: - case SLA_LOAD: { - size_t length = SlReadUint32(); - l->resize(length); - - typename PtrList::iterator iter; - iter = l->begin(); - - for (size_t i = 0; i < length; i++) { - SlSaveLoadConv(&(*iter), conv); - ++iter; - } - break; - } - case SLA_PTRS: break; - case SLA_NULL: - l->clear(); - break; - default: NOT_REACHED(); +/** + * Save/Load a deque. + * @param list The list being manipulated. + * @param conv VarType type of variable that is used for calculating the size. + */ +static void SlRefVector(void *list, VarType conv) +{ + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcRefVectorLen(list, conv)); + /* Determine length only? */ + if (_sl.need_length == NL_CALCLENGTH) return; } + + SlStorageHelper::SlSaveLoad(list, conv, SL_REF); } /** @@ -1655,10 +1278,80 @@ static void SlDeque(void *deque, VarType conv) } } +/** + * Return the size in bytes of a std::vector. + * @param vector The std::vector to find the size of + * @param conv VarType type of variable that is used for calculating the size + */ +static inline size_t SlCalcVectorLen(const void *vector, VarType conv) +{ + switch (GetVarMemType(conv)) { + case SLE_VAR_BL: NOT_REACHED(); // Not supported + case SLE_VAR_I8: return SlStorageHelper::SlCalcLen(vector, conv); + case SLE_VAR_U8: return SlStorageHelper::SlCalcLen(vector, conv); + case SLE_VAR_I16: return SlStorageHelper::SlCalcLen(vector, conv); + case SLE_VAR_U16: return SlStorageHelper::SlCalcLen(vector, conv); + case SLE_VAR_I32: return SlStorageHelper::SlCalcLen(vector, conv); + case SLE_VAR_U32: return SlStorageHelper::SlCalcLen(vector, conv); + case SLE_VAR_I64: return SlStorageHelper::SlCalcLen(vector, conv); + case SLE_VAR_U64: return SlStorageHelper::SlCalcLen(vector, conv); + default: NOT_REACHED(); + } +} + +/** + * Save/load a std::vector. + * @param vector The std::vector being manipulated + * @param conv VarType type of variable that is used for calculating the size + */ +static void SlVector(void *vector, VarType conv) +{ + switch (GetVarMemType(conv)) { + case SLE_VAR_BL: NOT_REACHED(); // Not supported + case SLE_VAR_I8: SlStorageHelper::SlSaveLoad(vector, conv); break; + case SLE_VAR_U8: SlStorageHelper::SlSaveLoad(vector, conv); break; + case SLE_VAR_I16: SlStorageHelper::SlSaveLoad(vector, conv); break; + case SLE_VAR_U16: SlStorageHelper::SlSaveLoad(vector, conv); break; + case SLE_VAR_I32: SlStorageHelper::SlSaveLoad(vector, conv); break; + case SLE_VAR_U32: SlStorageHelper::SlSaveLoad(vector, conv); break; + case SLE_VAR_I64: SlStorageHelper::SlSaveLoad(vector, conv); break; + case SLE_VAR_U64: SlStorageHelper::SlSaveLoad(vector, conv); break; + default: NOT_REACHED(); + } +} + /** Are we going to save this object or not? */ static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld) { - return sld.ext_feature_test.IsFeaturePresent(_sl_version, sld.version_from, sld.version_to); + return (_sl_version >= sld.version_from && _sl_version < sld.version_to); +} + +/** + * Calculate the size of the table header. + * @param slt The SaveLoad table with objects to save/load. + * @return size of given object. + */ +static size_t SlCalcTableHeader(const SaveLoadTable &slt) +{ + size_t length = 0; + + for (auto &sld : slt) { + if (!SlIsObjectValidInSavegame(sld)) continue; + + length += SlCalcConvFileLen(SLE_UINT8); + length += SlCalcStdStringLen(&sld.name); + } + + length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry. + + for (auto &sld : slt) { + if (!SlIsObjectValidInSavegame(sld)) continue; + if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) { + length += SlCalcTableHeader(sld.handler->GetDescription()); + } + } + + return length; } /** @@ -1682,46 +1375,46 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld) { assert(_sl.action == SLA_SAVE); - switch (sld.cmd) { - case SL_VAR: - case SL_REF: - case SL_ARR: - case SL_STR: - case SL_REFLIST: - case SL_PTRDEQ: - case SL_VEC: - case SL_DEQUE: - case SL_STDSTR: - case SL_VARVEC: - /* CONDITIONAL saveload types depend on the savegame version */ - if (!SlIsObjectValidInSavegame(sld)) break; + if (!SlIsObjectValidInSavegame(sld)) return 0; - 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_REFLIST: return SlCalcRefListLen>(GetVariableAddress(object, sld)); - case SL_PTRDEQ: return SlCalcRefListLen>(GetVariableAddress(object, sld)); - case SL_VEC: return SlCalcRefListLen>(GetVariableAddress(object, sld)); - case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv); - case SL_VARVEC: { - const size_t size_len = SlCalcConvMemLen(sld.conv); - switch (size_len) { - case 1: return SlCalcVarListLen>(GetVariableAddress(object, sld), 1); - case 2: return SlCalcVarListLen>(GetVariableAddress(object, sld), 2); - case 4: return SlCalcVarListLen>(GetVariableAddress(object, sld), 4); - case 8: return SlCalcVarListLen>(GetVariableAddress(object, sld), 8); - default: NOT_REACHED(); - } - } - case SL_STDSTR: return SlCalcStdStrLen(*static_cast(GetVariableAddress(object, sld))); - default: NOT_REACHED(); + 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_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv); + case SL_REFDEQUE: return SlCalcRefDequeLen(GetVariableAddress(object, sld), sld.conv); + case SL_REFVEC: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv); + case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv); + case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv); + case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld)); + case SL_SAVEBYTE: return 1; // a byte is logically of size 1 + case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length; + + case SL_STRUCT: + case SL_STRUCTLIST: { + NeedLength old_need_length = _sl.need_length; + size_t old_obj_len = _sl.obj_len; + + _sl.need_length = NL_CALCLENGTH; + _sl.obj_len = 0; + + /* Pretend that we are saving to collect the object size. Other + * means are difficult, as we don't know the length of the list we + * are about to store. */ + sld.handler->Save(const_cast(object)); + size_t length = _sl.obj_len; + + _sl.obj_len = old_obj_len; + _sl.need_length = old_need_length; + + if (sld.cmd == SL_STRUCT) { + length += SlGetArrayLength(1); } - break; - case SL_WRITEBYTE: return 1; // a byte is logically of size 1 - case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END)); - case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription()); + + return length; + } + default: NOT_REACHED(); } return 0; @@ -1775,91 +1468,11 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld) } } -void SlFilterObject(const SaveLoadTable &slt, std::vector &save); - -static void SlFilterObjectMember(const SaveLoad &sld, std::vector &save) +static bool SlObjectMember(void *object, const SaveLoad &sld) { - assert(IsVariableSizeRight(sld)); + assert_msg(IsVariableSizeRight(sld), "%s, size: %u, length: %u, cmd: %u, conv: 0x%02X", sld.name.c_str(), (uint) sld.size, sld.length, sld.cmd, sld.conv); - switch (sld.cmd) { - case SL_VAR: - case SL_REF: - case SL_ARR: - case SL_STR: - case SL_REFLIST: - case SL_PTRDEQ: - case SL_VEC: - case SL_DEQUE: - case SL_STDSTR: - case SL_VARVEC: - /* CONDITIONAL saveload types depend on the savegame version */ - if (!SlIsObjectValidInSavegame(sld)) return; - - switch (_sl.action) { - case SLA_SAVE: - case SLA_LOAD_CHECK: - case SLA_LOAD: - break; - case SLA_PTRS: - case SLA_NULL: - switch (sld.cmd) { - case SL_REF: - case SL_REFLIST: - case SL_PTRDEQ: - case SL_VEC: - break; - - /* non-ptr types do not require SLA_PTRS or SLA_NULL actions */ - default: - return; - } - break; - default: NOT_REACHED(); - } - - save.push_back(sld); - break; - - /* SL_WRITEBYTE writes a value to the savegame to identify the type of an object. - * When loading, the value is read explictly with SlReadByte() to determine which - * object description to use. */ - case SL_WRITEBYTE: - if (_sl.action == SLA_SAVE) save.push_back(sld); - break; - - /* SL_VEH_INCLUDE loads common code for vehicles */ - case SL_VEH_INCLUDE: - SlFilterObject(GetVehicleDescription(VEH_END), save); - break; - - case SL_ST_INCLUDE: - SlFilterObject(GetBaseStationDescription(), save); - break; - - default: NOT_REACHED(); - } -} - -void SlFilterObject(const SaveLoadTable &slt, std::vector &save) -{ - for (auto &sld : slt) { - SlFilterObjectMember(sld, save); - } -} - -std::vector SlFilterObject(const SaveLoadTable &slt) -{ - std::vector save; - SlFilterObject(slt, save); - return save; -} - -template -bool SlObjectMemberGeneric(void *object, const SaveLoad &sld) -{ - void *ptr = GetVariableAddress(object, sld); - - if (check_version) assert(IsVariableSizeRight(sld)); + if (!SlIsObjectValidInSavegame(sld)) return false; VarType conv = GB(sld.conv, 0, 8); switch (sld.cmd) { @@ -1868,63 +1481,36 @@ bool SlObjectMemberGeneric(void *object, const SaveLoad &sld) case SL_ARR: case SL_STR: case SL_REFLIST: - case SL_PTRDEQ: - case SL_VEC: + case SL_REFDEQUE: + case SL_REFVEC: case SL_DEQUE: - case SL_STDSTR: - case SL_VARVEC: - /* CONDITIONAL saveload types depend on the savegame version */ - if (check_version) { - if (!SlIsObjectValidInSavegame(sld)) return false; - } + case SL_VECTOR: + case SL_STDSTR: { + void *ptr = GetVariableAddress(object, sld); switch (sld.cmd) { - case SL_VAR: SlSaveLoadConvGeneric(ptr, conv); break; - case SL_REF: // Reference variable, translate - switch (action) { - case SLA_SAVE: - SlWriteUint32((uint32)ReferenceToInt(*(void **)ptr, (SLRefType)conv)); - break; - case SLA_LOAD_CHECK: - case SLA_LOAD: - *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); - break; - case SLA_PTRS: - *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv); - break; - case SLA_NULL: - *(void **)ptr = nullptr; - break; - default: NOT_REACHED(); - } - break; + case SL_VAR: SlSaveLoadConv(ptr, conv); break; + case SL_REF: SlSaveLoadRef(ptr, conv); break; case SL_ARR: SlArray(ptr, sld.length, conv); break; case SL_STR: SlString(ptr, sld.length, sld.conv); break; - case SL_REFLIST: SlRefList>(ptr, (SLRefType)conv); break; - case SL_PTRDEQ: SlRefList>(ptr, (SLRefType)conv); break; - case SL_VEC: SlRefList>(ptr, (SLRefType)conv); break; + case SL_REFLIST: SlRefList(ptr, conv); break; + case SL_REFDEQUE: SlRefDeque(ptr, conv); break; + case SL_REFVEC: SlRefVector(ptr, conv); break; case SL_DEQUE: SlDeque(ptr, conv); break; - case SL_VARVEC: { - const size_t size_len = SlCalcConvMemLen(sld.conv); - switch (size_len) { - case 1: SlVarList>(ptr, conv); break; - case 2: SlVarList>(ptr, conv); break; - case 4: SlVarList>(ptr, conv); break; - case 8: SlVarList>(ptr, conv); break; - default: NOT_REACHED(); - } - break; - } - case SL_STDSTR: SlStdString(*static_cast(ptr), sld.conv); break; + case SL_VECTOR: SlVector(ptr, conv); break; + case SL_STDSTR: SlStdString(ptr, sld.conv); break; default: NOT_REACHED(); } break; + } - /* SL_WRITEBYTE writes a value to the savegame to identify the type of an object. + /* SL_SAVEBYTE writes a value to the savegame to identify the type of an object. * When loading, the value is read explicitly with SlReadByte() to determine which * object description to use. */ - case SL_WRITEBYTE: - switch (action) { + case SL_SAVEBYTE: { + void *ptr = GetVariableAddress(object, sld); + + switch (_sl.action) { case SLA_SAVE: SlWriteByte(*(uint8 *)ptr); break; case SLA_LOAD_CHECK: case SLA_LOAD: @@ -1933,14 +1519,57 @@ bool SlObjectMemberGeneric(void *object, const SaveLoad &sld) default: NOT_REACHED(); } break; + } - /* SL_VEH_INCLUDE loads common code for vehicles */ - case SL_VEH_INCLUDE: - SlObject(ptr, GetVehicleDescription(VEH_END)); + case SL_NULL: { + assert(GetVarMemType(sld.conv) == SLE_VAR_NULL); + + switch (_sl.action) { + case SLA_LOAD_CHECK: + case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break; + case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break; + case SLA_PTRS: + case SLA_NULL: break; + default: NOT_REACHED(); + } break; + } - case SL_ST_INCLUDE: - SlObject(ptr, GetBaseStationDescription()); + case SL_STRUCT: + case SL_STRUCTLIST: + switch (_sl.action) { + case SLA_SAVE: { + if (sld.cmd == SL_STRUCT) { + /* Store in the savegame if this struct was written or not. */ + SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0); + } + sld.handler->Save(object); + break; + } + + case SLA_LOAD_CHECK: { + if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { + SlGetStructListLength(1); + } + sld.handler->LoadCheck(object); + break; + } + + case SLA_LOAD: { + if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { + SlGetStructListLength(1); + } + sld.handler->Load(object); + break; + } + + case SLA_PTRS: + sld.handler->FixPointers(object); + break; + + case SLA_NULL: break; + default: NOT_REACHED(); + } break; default: NOT_REACHED(); @@ -1948,20 +1577,32 @@ bool SlObjectMemberGeneric(void *object, const SaveLoad &sld) return true; } -bool SlObjectMember(void *object, const SaveLoad &sld) +/** + * Set the length of this list. + * @param The length of the list. + */ +void SlSetStructListLength(size_t length) { - switch (_sl.action) { - case SLA_SAVE: - return SlObjectMemberGeneric(object, sld); - case SLA_LOAD_CHECK: - case SLA_LOAD: - return SlObjectMemberGeneric(object, sld); - case SLA_PTRS: - return SlObjectMemberGeneric(object, sld); - case SLA_NULL: - return SlObjectMemberGeneric(object, sld); - default: NOT_REACHED(); + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlGetArrayLength(length)); + if (_sl.need_length == NL_CALCLENGTH) return; } + + SlWriteArrayLength(length); +} + +/** + * Get the length of this list; if it exceeds the limit, error out. + * @param limit The maximum size the list can be. + * @return The length of the list. + */ +size_t SlGetStructListLength(size_t limit) +{ + size_t length = SlReadArrayLength(); + if (length > limit) SlErrorCorrupt("List exceeds storage size"); + + return length; } /** @@ -1974,6 +1615,7 @@ void SlObject(void *object, const SaveLoadTable &slt) /* Automatically calculate the length? */ if (_sl.need_length != NL_NONE) { SlSetLength(SlCalcObjLength(object, slt)); + if (_sl.need_length == NL_CALCLENGTH) return; } for (auto &sld : slt) { @@ -1981,45 +1623,232 @@ void SlObject(void *object, const SaveLoadTable &slt) } } -template -void SlObjectIterateBase(void *object, const SaveLoadTable &slt) -{ - for (auto &sld : slt) { - SlObjectMemberGeneric(object, sld); +/** + * Handler that is assigned when there is a struct read in the savegame which + * is not known to the code. This means we are going to skip it. + */ +class SlSkipHandler : public SaveLoadHandler { + void Save(void *object) const override + { + NOT_REACHED(); } -} -void SlObjectSaveFiltered(void *object, const SaveLoadTable &slt) -{ - if (_sl.need_length != NL_NONE) { - _sl.need_length = NL_NONE; - _sl.dumper->StartAutoLength(); - SlObjectIterateBase(object, slt); - auto result = _sl.dumper->StopAutoLength(); - _sl.need_length = NL_WANTLENGTH; - SlSetLength(result.second); - _sl.dumper->CopyBytes(result.first, result.second); - } else { - SlObjectIterateBase(object, slt); + void Load(void *object) const override + { + size_t length = SlGetStructListLength(UINT32_MAX); + for (; length > 0; length--) { + SlObject(object, this->GetLoadDescription()); + } } -} -void SlObjectLoadFiltered(void *object, const SaveLoadTable &slt) -{ - SlObjectIterateBase(object, slt); -} + void LoadCheck(void *object) const override + { + this->Load(object); + } -void SlObjectPtrOrNullFiltered(void *object, const SaveLoadTable &slt) + virtual SaveLoadTable GetDescription() const override + { + return {}; + } + + virtual SaveLoadCompatTable GetCompatDescription() const override + { + NOT_REACHED(); + } +}; + +/** + * Save or Load a table header. + * @note a table-header can never contain more than 65535 fields. + * @param slt The SaveLoad table with objects to save/load. + * @return When loading, the ordered SaveLoad array to use; otherwise an empty list. + */ +std::vector SlTableHeader(const SaveLoadTable &slt) { + /* You can only use SlTableHeader if you are a CH_TABLE. */ + assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); + switch (_sl.action) { - case SLA_PTRS: - SlObjectIterateBase(object, slt); - return; - case SLA_NULL: - SlObjectIterateBase(object, slt); - return; + case SLA_LOAD_CHECK: + case SLA_LOAD: { + std::vector saveloads; + + /* Build a key lookup mapping based on the available fields. */ + std::map key_lookup; + for (auto &sld : slt) { + if (!SlIsObjectValidInSavegame(sld)) continue; + + /* Check that there is only one active SaveLoad for a given name. */ + assert(key_lookup.find(sld.name) == key_lookup.end()); + key_lookup[sld.name] = &sld; + } + + while (true) { + uint8 type; + SlSaveLoadConv(&type, SLE_UINT8); + if (type == SLE_FILE_END) break; + + std::string key; + SlStdString(&key, SLE_STR); + + auto sld_it = key_lookup.find(key); + if (sld_it == key_lookup.end()) { + /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */ + DEBUG(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '%s' of type 0x%02X not found, skipping", key.c_str(), type); + + std::shared_ptr handler = nullptr; + SaveLoadType saveload_type; + switch (type & SLE_FILE_TYPE_MASK) { + case SLE_FILE_STRING: + /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */ + saveload_type = SL_STR; + break; + + case SLE_FILE_STRUCT: + /* Structs are always marked with SLE_FILE_HAS_LENGTH_FIELD as SL_STRUCT is seen as a list of 0/1 in length. */ + saveload_type = SL_STRUCTLIST; + handler = std::make_shared(); + break; + + default: + saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR; + break; + } + + /* We don't know this field, so read to nothing. */ + saveloads.push_back({key, saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, 0, nullptr, 0, handler}); + continue; + } + + /* Validate the type of the field. If it is changed, the + * savegame should have been bumped so we know how to do the + * conversion. If this error triggers, that clearly didn't + * happen and this is a friendly poke to the developer to bump + * the savegame version and add conversion code. */ + uint8 correct_type = GetSavegameFileType(*sld_it->second); + if (correct_type != type) { + DEBUG(sl, 1, "Field type for '%s' was expected to be 0x%02X but 0x%02X was found", key.c_str(), correct_type, type); + SlErrorCorrupt("Field type is different than expected"); + } + saveloads.push_back(*sld_it->second); + } + + for (auto &sld : saveloads) { + if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) { + sld.handler->load_description = SlTableHeader(sld.handler->GetDescription()); + } + } + + return saveloads; + } + + case SLA_SAVE: { + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcTableHeader(slt)); + if (_sl.need_length == NL_CALCLENGTH) break; + } + + for (auto &sld : slt) { + if (!SlIsObjectValidInSavegame(sld)) continue; + /* Make sure we are not storing empty keys. */ + assert(!sld.name.empty()); + + uint8 type = GetSavegameFileType(sld); + assert(type != SLE_FILE_END); + + SlSaveLoadConv(&type, SLE_UINT8); + SlStdString(const_cast(&sld.name), SLE_STR); + } + + /* Add an end-of-header marker. */ + uint8 type = SLE_FILE_END; + SlSaveLoadConv(&type, SLE_UINT8); + + /* After the table, write down any sub-tables we might have. */ + for (auto &sld : slt) { + if (!SlIsObjectValidInSavegame(sld)) continue; + if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) { + /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */ + NeedLength old_need_length = _sl.need_length; + _sl.need_length = NL_NONE; + + SlTableHeader(sld.handler->GetDescription()); + + _sl.need_length = old_need_length; + } + } + + break; + } + default: NOT_REACHED(); } + + return std::vector(); +} + +/** + * Load a table header in a savegame compatible way. If the savegame was made + * before table headers were added, it will fall back to the + * SaveLoadCompatTable for the order of fields while loading. + * + * @note You only have to call this function if the chunk existed as a + * non-table type before converting it to a table. New chunks created as + * table can call SlTableHeader() directly. + * + * @param slt The SaveLoad table with objects to save/load. + * @param slct The SaveLoadCompat table the original order of the fields. + * @return When loading, the ordered SaveLoad array to use; otherwise an empty list. + */ +std::vector SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct) +{ + assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK); + /* CH_TABLE / CH_SPARSE_TABLE always have a header. */ + if (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) return SlTableHeader(slt); + + std::vector saveloads; + + /* Build a key lookup mapping based on the available fields. */ + std::map> key_lookup; + for (auto &sld : slt) { + /* All entries should have a name; otherwise the entry should just be removed. */ + assert(!sld.name.empty()); + + key_lookup[sld.name].push_back(&sld); + } + + for (auto &slc : slct) { + if (slc.name.empty()) { + /* In old savegames there can be data we no longer care for. We + * skip this by simply reading the amount of bytes indicated and + * send those to /dev/null. */ + saveloads.push_back({"", SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, slc.length, slc.version_from, slc.version_to, 0, nullptr, 0, nullptr}); + } else { + auto sld_it = key_lookup.find(slc.name); + /* If this branch triggers, it means that an entry in the + * SaveLoadCompat list is not mentioned in the SaveLoad list. Did + * you rename a field in one and not in the other? */ + if (sld_it == key_lookup.end()) { + /* This isn't an assert, as that leaves no information what + * field was to blame. This way at least we have breadcrumbs. */ + DEBUG(sl, 0, "internal error: saveload compatibility field '%s' not found", slc.name.c_str()); + SlErrorCorrupt("Internal error with savegame compatibility"); + } + for (auto &sld : sld_it->second) { + saveloads.push_back(*sld); + } + } + } + + for (auto &sld : saveloads) { + if (!SlIsObjectValidInSavegame(sld)) continue; + if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) { + sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription()); + } + } + + return saveloads; } /** @@ -2038,90 +1867,26 @@ void SlGlobList(const SaveLoadTable &slt) */ void SlAutolength(AutolengthProc *proc, void *arg) { - assert(_sl.action == SLA_SAVE); - assert(_sl.need_length == NL_WANTLENGTH); - - _sl.need_length = NL_NONE; - _sl.dumper->StartAutoLength(); - proc(arg); - auto result = _sl.dumper->StopAutoLength(); - /* Setup length */ - _sl.need_length = NL_WANTLENGTH; - SlSetLength(result.second); - _sl.dumper->CopyBytes(result.first, result.second); + // removed + NOT_REACHED(); } -/** - * Run proc, saving result to a std::vector - * @param proc The callback procedure that is called - * @param arg The variable that will be used for the callback procedure - */ -std::vector SlSaveToVector(AutolengthProc *proc, void *arg) +void ChunkHandler::LoadCheck(size_t len) const { - assert(_sl.action == SLA_SAVE); - NeedLength orig_need_length = _sl.need_length; - - _sl.need_length = NL_NONE; - _sl.dumper->StartAutoLength(); - proc(arg); - auto result = _sl.dumper->StopAutoLength(); - /* Setup length */ - _sl.need_length = orig_need_length; - return std::vector(result.first, result.first + result.second); -} - -SlLoadFromBufferState SlLoadFromBufferSetup(const byte *buffer, size_t length) -{ - assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK); - - SlLoadFromBufferState state; - - state.old_obj_len = _sl.obj_len; - _sl.obj_len = length; - - ReadBuffer *reader = ReadBuffer::GetCurrent(); - state.old_bufp = reader->bufp; - state.old_bufe = reader->bufe; - reader->bufp = const_cast(buffer); - reader->bufe = const_cast(buffer) + length; - - return state; -} - -void SlLoadFromBufferRestore(const SlLoadFromBufferState &state, const byte *buffer, size_t length) -{ - ReadBuffer *reader = ReadBuffer::GetCurrent(); - if (reader->bufp != reader->bufe || reader->bufe != buffer + length) { - SlErrorCorrupt("SlLoadFromBuffer: Wrong number of bytes read"); - } - - _sl.obj_len = state.old_obj_len; - reader->bufp = state.old_bufp; - reader->bufe = state.old_bufe; -} - -/* - * Notes on extended chunk header: - * - * If the chunk type is CH_EXT_HDR (15), then a u32 flags field follows. - * This flag field may define additional fields which follow the flags field in future. - * The standard chunk header follows, though it my be modified by the flags field. - * At present SLCEHF_BIG_RIFF increases the RIFF size limit to a theoretical 60 bits, - * by adding a further u32 field for the high bits after the existing RIFF size field. - */ - -inline void SlRIFFSpringPPCheck(size_t len) -{ - if (_sl_maybe_springpp) { - _sl_maybe_springpp = false; - if (len == 0) { - extern void SlXvSpringPPSpecialSavegameVersions(); - SlXvSpringPPSpecialSavegameVersions(); - } else if (_sl_version > MAX_LOAD_SAVEGAME_VERSION) { - SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME); - } else if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) { - SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK); - } + switch (_sl.block_mode) { + case CH_TABLE: + case CH_SPARSE_TABLE: + SlTableHeader({}); + FALLTHROUGH; + case CH_ARRAY: + case CH_SPARSE_ARRAY: + SlSkipArray(); + break; + case CH_RIFF: + SlSkipBytes(len); + break; + default: + NOT_REACHED(); } } @@ -2131,222 +1896,95 @@ inline void SlRIFFSpringPPCheck(size_t len) */ static void SlLoadChunk(const ChunkHandler &ch) { - if (ch.special_proc != nullptr) { - if (ch.special_proc(ch.id, CSLSO_PRE_LOAD) == CSLSOR_LOAD_CHUNK_CONSUMED) return; - } - byte m = SlReadByte(); size_t len; size_t endoffs; - _sl.block_mode = m; + _sl.block_mode = m & CH_TYPE_MASK; _sl.obj_len = 0; + _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); - SaveLoadChunkExtHeaderFlags ext_flags = static_cast(0); - if ((m & 0xF) == CH_EXT_HDR) { - ext_flags = static_cast(SlReadUint32()); - - /* read in real header */ - m = SlReadByte(); - _sl.block_mode = m; + /* The header should always be at the start. Read the length; the + * Load() should as first action process the header. */ + if (_sl.expect_table_header) { + SlIterateArray(); } - switch (m) { + switch (_sl.block_mode) { + case CH_TABLE: case CH_ARRAY: _sl.array_index = 0; - ch.load_proc(); + ch.Load(); if (_next_offs != 0) SlErrorCorrupt("Invalid array length"); break; + case CH_SPARSE_TABLE: case CH_SPARSE_ARRAY: - ch.load_proc(); + ch.Load(); if (_next_offs != 0) SlErrorCorrupt("Invalid array length"); break; + case CH_RIFF: + /* Read length */ + len = (SlReadByte() << 16) | ((m >> 4) << 24); + len += SlReadUint16(); + _sl.obj_len = len; + endoffs = SlGetBytesRead() + len; + ch.Load(); + if (SlGetBytesRead() != endoffs) SlErrorCorrupt("Invalid chunk size"); + break; default: - if ((m & 0xF) == CH_RIFF) { - /* Read length */ - len = (SlReadByte() << 16) | ((m >> 4) << 24); - len += SlReadUint16(); - SlRIFFSpringPPCheck(len); - if (SlXvIsFeaturePresent(XSLFI_RIFF_HEADER_60_BIT)) { - if (len != 0) { - SlErrorCorrupt("RIFF chunk too large"); - } - len = SlReadUint32(); - } - if (ext_flags & SLCEHF_BIG_RIFF) { - len |= SlReadUint32() << 28; - } - - _sl.obj_len = len; - endoffs = _sl.reader->GetSize() + len; - ch.load_proc(); - if (_sl.reader->GetSize() != endoffs) { - DEBUG(sl, 1, "Invalid chunk size: " PRINTF_SIZE " != " PRINTF_SIZE ", (" PRINTF_SIZE ")", _sl.reader->GetSize(), endoffs, len); - SlErrorCorruptFmt("Invalid chunk size - expected to be at position " PRINTF_SIZE ", actually at " PRINTF_SIZE ", length: " PRINTF_SIZE, - endoffs, _sl.reader->GetSize(), len); - } - } else { - SlErrorCorrupt("Invalid chunk type"); - } + SlErrorCorrupt("Invalid chunk type"); break; } + + if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); } /** * Load a chunk of data for checking savegames. * If the chunkhandler is nullptr, the chunk is skipped. - * @param ch The chunkhandler that will be used for the operation, this may be nullptr + * @param ch The chunkhandler that will be used for the operation */ -static void SlLoadCheckChunk(const ChunkHandler *ch) +static void SlLoadCheckChunk(const ChunkHandler &ch) { - if (ch && ch->special_proc != nullptr) { - if (ch->special_proc(ch->id, CSLSO_PRE_LOADCHECK) == CSLSOR_LOAD_CHUNK_CONSUMED) return; - } - byte m = SlReadByte(); size_t len; size_t endoffs; - _sl.block_mode = m; + _sl.block_mode = m & CH_TYPE_MASK; _sl.obj_len = 0; + _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); - SaveLoadChunkExtHeaderFlags ext_flags = static_cast(0); - if ((m & 0xF) == CH_EXT_HDR) { - ext_flags = static_cast(SlReadUint32()); - - /* read in real header */ - m = SlReadByte(); - _sl.block_mode = m; + /* The header should always be at the start. Read the length; the + * LoadCheck() should as first action process the header. */ + if (_sl.expect_table_header) { + SlIterateArray(); } - switch (m) { + switch (_sl.block_mode) { + case CH_TABLE: case CH_ARRAY: _sl.array_index = 0; - if (ext_flags) { - SlErrorCorruptFmt("CH_ARRAY does not take chunk header extension flags: 0x%X", ext_flags); - } - if (ch && ch->load_check_proc) { - ch->load_check_proc(); - } else { - SlSkipArray(); - } + ch.LoadCheck(); break; + case CH_SPARSE_TABLE: case CH_SPARSE_ARRAY: - if (ext_flags) { - SlErrorCorruptFmt("CH_SPARSE_ARRAY does not take chunk header extension flags: 0x%X", ext_flags); - } - if (ch && ch->load_check_proc) { - ch->load_check_proc(); - } else { - SlSkipArray(); - } + ch.LoadCheck(); + break; + case CH_RIFF: + /* Read length */ + len = (SlReadByte() << 16) | ((m >> 4) << 24); + len += SlReadUint16(); + _sl.obj_len = len; + endoffs = SlGetBytesRead() + len; + ch.LoadCheck(len); + if (SlGetBytesRead() != endoffs) SlErrorCorrupt("Invalid chunk size"); break; default: - if ((m & 0xF) == CH_RIFF) { - if (ext_flags != (ext_flags & SLCEHF_BIG_RIFF)) { - SlErrorCorruptFmt("Unknown chunk header extension flags for CH_RIFF: 0x%X", ext_flags); - } - /* Read length */ - len = (SlReadByte() << 16) | ((m >> 4) << 24); - len += SlReadUint16(); - SlRIFFSpringPPCheck(len); - if (SlXvIsFeaturePresent(XSLFI_RIFF_HEADER_60_BIT)) { - if (len != 0) { - SlErrorCorrupt("RIFF chunk too large"); - } - len = SlReadUint32(); - if (ext_flags & SLCEHF_BIG_RIFF) SlErrorCorrupt("XSLFI_RIFF_HEADER_60_BIT and SLCEHF_BIG_RIFF both present"); - } - if (ext_flags & SLCEHF_BIG_RIFF) { - uint64 full_len = len | (static_cast(SlReadUint32()) << 28); - if (full_len >= (1LL << 32)) { - SlErrorCorruptFmt("Chunk size too large: " OTTD_PRINTFHEX64, full_len); - } - len = static_cast(full_len); - } - _sl.obj_len = len; - endoffs = _sl.reader->GetSize() + len; - if (ch && ch->load_check_proc) { - ch->load_check_proc(); - } else { - SlSkipBytes(len); - } - if (_sl.reader->GetSize() != endoffs) { - DEBUG(sl, 1, "Invalid chunk size: " PRINTF_SIZE " != " PRINTF_SIZE ", (" PRINTF_SIZE ")", _sl.reader->GetSize(), endoffs, len); - SlErrorCorruptFmt("Invalid chunk size - expected to be at position " PRINTF_SIZE ", actually at " PRINTF_SIZE ", length: " PRINTF_SIZE, - endoffs, _sl.reader->GetSize(), len); - } - } else { - SlErrorCorrupt("Invalid chunk type"); - } + SlErrorCorrupt("Invalid chunk type"); break; } -} -/** - * Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is - * prefixed by an ID identifying it, followed by data, and terminator where appropriate - * @param ch The chunkhandler that will be used for the operation - */ -static void SlSaveChunk(const ChunkHandler &ch) -{ - if (ch.type == CH_UPSTREAM_SAVE) { - SaveLoadVersion old_ver = _sl_version; - _sl_version = MAX_LOAD_SAVEGAME_VERSION; - auto guard = scope_guard([&]() { - _sl_version = old_ver; - }); - upstream_sl::SlSaveChunkChunkByID(ch.id); - return; - } - - ChunkSaveLoadProc *proc = ch.save_proc; - - /* Don't save any chunk information if there is no save handler. */ - if (proc == nullptr) return; - - if (ch.special_proc != nullptr) { - if (ch.special_proc(ch.id, CSLSO_SHOULD_SAVE_CHUNK) == CSLSOR_DONT_SAVE_CHUNK) return; - } - - SlWriteUint32(ch.id); - DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); - - size_t written = 0; - if (_debug_sl_level >= 3) written = SlGetBytesWritten(); - - _sl.block_mode = ch.type; - switch (ch.type) { - 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(); - } - - DEBUG(sl, 3, "Saved chunk %c%c%c%c (" PRINTF_SIZE " bytes)", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id, SlGetBytesWritten() - written); -} - -/** Save all chunks */ -static void SlSaveChunks() -{ - for (auto &ch : ChunkHandlers()) { - SlSaveChunk(ch); - } - - /* Terminator */ - SlWriteUint32(0); + if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); } /** @@ -2357,1474 +1995,149 @@ static void SlSaveChunks() */ static const ChunkHandler *SlFindChunkHandler(uint32 id) { - for (auto &ch : ChunkHandlers()) if (ch.id == id) return &ch; + for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch; return nullptr; } /** Load all chunks */ -static void SlLoadChunks() +void SlLoadChunks() { - if (_sl_upstream_mode) { - upstream_sl::SlLoadChunks(); - return; - } - - for (uint32 id = SlReadUint32(); id != 0; id = SlReadUint32()) { - DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); - size_t read = 0; - if (_debug_sl_level >= 3) read = SlGetBytesRead(); - - if (SlXvIsChunkDiscardable(id)) { - DEBUG(sl, 1, "Discarding chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); - SlLoadCheckChunk(nullptr); - } else { - const ChunkHandler *ch = SlFindChunkHandler(id); - if (ch == nullptr) { - SlErrorCorrupt("Unknown chunk type"); - } else { - SlLoadChunk(*ch); - } - } - DEBUG(sl, 3, "Loaded chunk %c%c%c%c (" PRINTF_SIZE " bytes)", id >> 24, id >> 16, id >> 8, id, SlGetBytesRead() - read); - } -} - -/** Load all chunks for savegame checking */ -static void SlLoadCheckChunks() -{ - if (_sl_upstream_mode) { - upstream_sl::SlLoadCheckChunks(); - return; - } + _sl.action = SLA_LOAD; 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); - size_t read = 0; - if (_debug_sl_level >= 3) read = SlGetBytesRead(); - if (SlXvIsChunkDiscardable(id)) { - ch = nullptr; - } else { - ch = SlFindChunkHandler(id); - if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); - } - SlLoadCheckChunk(ch); - DEBUG(sl, 3, "Loaded chunk %c%c%c%c (" PRINTF_SIZE " bytes)", id >> 24, id >> 16, id >> 8, id, SlGetBytesRead() - read); + ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + SlLoadChunk(*ch); } } -/** Fix all pointers (convert index -> pointer) */ -static void SlFixPointers() +/** Load a chunk */ +void SlLoadChunkByID(uint32 id) { - if (_sl_upstream_mode) { - upstream_sl::SlFixPointers(); - return; - } + _sl.action = SLA_LOAD; + DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); + + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + SlLoadChunk(*ch); +} + +/** Load all chunks for savegame checking */ +void SlLoadCheckChunks() +{ + _sl.action = SLA_LOAD_CHECK; + + 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 == nullptr) SlErrorCorrupt("Unknown chunk type"); + SlLoadCheckChunk(*ch); + } +} + +/** Load a chunk for savegame checking */ +void SlLoadCheckChunkByID(uint32 id) +{ + _sl.action = SLA_LOAD_CHECK; + + DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); + + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + SlLoadCheckChunk(*ch); +} + +/** Fix all pointers (convert index -> pointer) */ +void SlFixPointers() +{ _sl.action = SLA_PTRS; - for (auto &ch : ChunkHandlers()) { - if (ch.ptrs_proc != nullptr) { - DEBUG(sl, 3, "Fixing pointers for %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); - ch.ptrs_proc(); - } + for (const ChunkHandler &ch : ChunkHandlers()) { + DEBUG(sl, 3, "Fixing pointers for %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); + ch.FixPointers(); } assert(_sl.action == SLA_PTRS); } - -/** Yes, simply reading from a file. */ -struct FileReader : LoadFilter { - FILE *file; ///< The file to read from. - long begin; ///< The begin of the file. - - /** - * Create the file reader, so it reads from a specific file. - * @param file The file to read from. - */ - FileReader(FILE *file) : LoadFilter(nullptr), file(file), begin(ftell(file)) - { - } - - /** Make sure everything is cleaned up. */ - ~FileReader() - { - if (this->file != nullptr) fclose(this->file); - this->file = nullptr; - - /* Make sure we don't double free. */ - _sl.sf = nullptr; - } - - size_t Read(byte *buf, size_t size) override - { - /* We're in the process of shutting down, i.e. in "failure" mode. */ - if (this->file == nullptr) return 0; - - return fread(buf, 1, size, this->file); - } - - void Reset() override - { - clearerr(this->file); - if (fseek(this->file, this->begin, SEEK_SET)) { - DEBUG(sl, 1, "Could not reset the file reading"); - } - } -}; - -/** Yes, simply writing to a file. */ -struct FileWriter : SaveFilter { - FILE *file; ///< The file to write to. - - /** - * Create the file writer, so it writes to a specific file. - * @param file The file to write to. - */ - FileWriter(FILE *file) : SaveFilter(nullptr), file(file) - { - } - - /** Make sure everything is cleaned up. */ - ~FileWriter() - { - this->Finish(); - - /* Make sure we don't double free. */ - _sl.sf = nullptr; - } - - void Write(byte *buf, size_t size) override - { - /* We're in the process of shutting down, i.e. in "failure" mode. */ - if (this->file == nullptr) return; - - if (fwrite(buf, 1, size, this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); - } - - void Finish() override - { - if (this->file != nullptr) fclose(this->file); - this->file = nullptr; - } -}; - -/******************************************* - ********** START OF LZO CODE ************** - *******************************************/ - -#ifdef WITH_LZO -#include - -/** Buffer size for the LZO compressor */ -static const uint LZO_BUFFER_SIZE = 8192; - -/** Filter using LZO compression. */ -struct LZOLoadFilter : LoadFilter { - /** - * Initialise this filter. - * @param chain The next filter in this chain. - */ - LZOLoadFilter(LoadFilter *chain) : LoadFilter(chain) - { - if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); - } - - size_t Read(byte *buf, size_t ssize) override - { - assert(ssize >= LZO_BUFFER_SIZE); - - /* Buffer size is from the LZO docs plus the chunk header size. */ - byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2]; - uint32 tmp[2]; - uint32 size; - lzo_uint len = ssize; - - /* Read header*/ - if (this->chain->Read((byte*)tmp, sizeof(tmp)) != sizeof(tmp)) 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 != SL_MIN_VERSION) { - tmp[0] = TO_BE32(tmp[0]); - size = TO_BE32(size); - } - - if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size"); - - /* Read block */ - if (this->chain->Read(out + sizeof(uint32), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); - - /* Verify checksum */ - if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlErrorCorrupt("Bad checksum"); - - /* Decompress */ - int ret = lzo1x_decompress_safe(out + sizeof(uint32) * 1, size, buf, &len, nullptr); - if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); - return len; - } -}; - -/** Filter using LZO compression. */ -struct LZOSaveFilter : SaveFilter { - /** - * Initialise this filter. - * @param chain The next filter in this chain. - * @param compression_level The requested level of compression. - */ - LZOSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) - { - if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); - } - - void Write(byte *buf, size_t size) override - { - const lzo_bytep in = buf; - /* Buffer size is from the LZO docs plus the chunk header size. */ - byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2]; - byte wrkmem[LZO1X_1_MEM_COMPRESS]; - lzo_uint outlen; - - do { - /* Compress up to LZO_BUFFER_SIZE bytes at once. */ - lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size; - lzo1x_1_compress(in, len, out + sizeof(uint32) * 2, &outlen, wrkmem); - ((uint32*)out)[1] = TO_BE32((uint32)outlen); - ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32))); - this->chain->Write(out, outlen + sizeof(uint32) * 2); - - /* Move to next data chunk. */ - size -= len; - in += len; - } while (size > 0); - } -}; - -#endif /* WITH_LZO */ - -/********************************************* - ******** START OF NOCOMP CODE (uncompressed)* - *********************************************/ - -/** Filter without any compression. */ -struct NoCompLoadFilter : LoadFilter { - /** - * Initialise this filter. - * @param chain The next filter in this chain. - */ - NoCompLoadFilter(LoadFilter *chain) : LoadFilter(chain) - { - } - - size_t Read(byte *buf, size_t size) override - { - return this->chain->Read(buf, size); - } -}; - -/** Filter without any compression. */ -struct NoCompSaveFilter : SaveFilter { - /** - * Initialise this filter. - * @param chain The next filter in this chain. - * @param compression_level The requested level of compression. - */ - NoCompSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) - { - } - - void Write(byte *buf, size_t size) override - { - this->chain->Write(buf, size); - } -}; - -/******************************************** - ********** START OF ZLIB CODE ************** - ********************************************/ - -#if defined(WITH_ZLIB) -#include - -/** Filter using Zlib compression. */ -struct ZlibLoadFilter : LoadFilter { - z_stream z; ///< Stream state we are reading from. - byte fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file. - - /** - * Initialise this filter. - * @param chain The next filter in this chain. - */ - ZlibLoadFilter(LoadFilter *chain) : LoadFilter(chain) - { - memset(&this->z, 0, sizeof(this->z)); - if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); - } - - /** Clean everything up. */ - ~ZlibLoadFilter() - { - inflateEnd(&this->z); - } - - size_t Read(byte *buf, size_t size) override - { - this->z.next_out = buf; - this->z.avail_out = (uint)size; - - do { - /* read more bytes from the file? */ - if (this->z.avail_in == 0) { - this->z.next_in = this->fread_buf; - this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf)); - } - - /* inflate the data */ - int r = inflate(&this->z, 0); - if (r == Z_STREAM_END) break; - - if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed"); - } while (this->z.avail_out != 0); - - return size - this->z.avail_out; - } -}; - -/** Filter using Zlib compression. */ -struct ZlibSaveFilter : SaveFilter { - z_stream z; ///< Stream state we are writing to. - byte buf[MEMORY_CHUNK_SIZE]; ///< output buffer - - /** - * Initialise this filter. - * @param chain The next filter in this chain. - * @param compression_level The requested level of compression. - */ - ZlibSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) - { - memset(&this->z, 0, sizeof(this->z)); - if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); - } - - /** Clean up what we allocated. */ - ~ZlibSaveFilter() - { - deflateEnd(&this->z); - } - - /** - * Helper loop for writing the data. - * @param p The bytes to write. - * @param len Amount of bytes to write. - * @param mode Mode for deflate. - */ - void WriteLoop(byte *p, size_t len, int mode) - { - uint n; - this->z.next_in = p; - this->z.avail_in = (uInt)len; - do { - this->z.next_out = this->buf; - this->z.avail_out = sizeof(this->buf); - - /** - * For the poor next soul who sees many valgrind warnings of the - * "Conditional jump or move depends on uninitialised value(s)" kind: - * According to the author of zlib it is not a bug and it won't be fixed. - * http://groups.google.com/group/comp.compression/browse_thread/thread/b154b8def8c2a3ef/cdf9b8729ce17ee2 - * [Mark Adler, Feb 24 2004, 'zlib-1.2.1 valgrind warnings' in the newsgroup comp.compression] - */ - int r = deflate(&this->z, mode); - - /* bytes were emitted? */ - if ((n = sizeof(this->buf) - this->z.avail_out) != 0) { - this->chain->Write(this->buf, n); - } - if (r == Z_STREAM_END) break; - - if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code"); - } while (this->z.avail_in || !this->z.avail_out); - } - - void Write(byte *buf, size_t size) override - { - this->WriteLoop(buf, size, 0); - } - - void Finish() override - { - this->WriteLoop(nullptr, 0, Z_FINISH); - this->chain->Finish(); - } -}; - -#endif /* WITH_ZLIB */ - -/******************************************** - ********** START OF LZMA CODE ************** - ********************************************/ - -#if defined(WITH_LIBLZMA) -#include - -/** - * Have a copy of an initialised LZMA stream. We need this as it's - * impossible to "re"-assign LZMA_STREAM_INIT to a variable in some - * compilers, i.e. LZMA_STREAM_INIT can't be used to set something. - * This var has to be used instead. - */ -static const lzma_stream _lzma_init = LZMA_STREAM_INIT; - -/** Filter without any compression. */ -struct LZMALoadFilter : LoadFilter { - lzma_stream lzma; ///< Stream state that we are reading from. - byte fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file. - - /** - * Initialise this filter. - * @param chain The next filter in this chain. - */ - LZMALoadFilter(LoadFilter *chain) : LoadFilter(chain), lzma(_lzma_init) - { - /* Allow saves up to 256 MB uncompressed */ - if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); - } - - /** Clean everything up. */ - ~LZMALoadFilter() - { - lzma_end(&this->lzma); - } - - size_t Read(byte *buf, size_t size) override - { - this->lzma.next_out = buf; - this->lzma.avail_out = size; - - do { - /* read more bytes from the file? */ - if (this->lzma.avail_in == 0) { - this->lzma.next_in = this->fread_buf; - this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf)); - } - - /* inflate the data */ - lzma_ret r = lzma_code(&this->lzma, LZMA_RUN); - if (r == LZMA_STREAM_END) break; - if (r != LZMA_OK) SlErrorFmt(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code: %u", r); - } while (this->lzma.avail_out != 0); - - return size - this->lzma.avail_out; - } -}; - -/** Filter using LZMA compression. */ -struct LZMASaveFilter : SaveFilter { - lzma_stream lzma; ///< Stream state that we are writing to. - byte buf[MEMORY_CHUNK_SIZE]; ///< output buffer - - /** - * Initialise this filter. - * @param chain The next filter in this chain. - * @param compression_level The requested level of compression. - */ - LZMASaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain), lzma(_lzma_init) - { - if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); - } - - /** Clean up what we allocated. */ - ~LZMASaveFilter() - { - lzma_end(&this->lzma); - } - - /** - * Helper loop for writing the data. - * @param p The bytes to write. - * @param len Amount of bytes to write. - * @param action Action for lzma_code. - */ - void WriteLoop(byte *p, size_t len, lzma_action action) - { - size_t n; - this->lzma.next_in = p; - this->lzma.avail_in = len; - do { - this->lzma.next_out = this->buf; - this->lzma.avail_out = sizeof(this->buf); - - lzma_ret r = lzma_code(&this->lzma, action); - - /* bytes were emitted? */ - if ((n = sizeof(this->buf) - this->lzma.avail_out) != 0) { - this->chain->Write(this->buf, n); - } - if (r == LZMA_STREAM_END) break; - if (r != LZMA_OK) SlErrorFmt(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code: %u", r); - } while (this->lzma.avail_in || !this->lzma.avail_out); - } - - void Write(byte *buf, size_t size) override - { - this->WriteLoop(buf, size, LZMA_RUN); - } - - void Finish() override - { - this->WriteLoop(nullptr, 0, LZMA_FINISH); - this->chain->Finish(); - } -}; - -#endif /* WITH_LIBLZMA */ - -/******************************************** - ********** START OF ZSTD CODE ************** - ********************************************/ - -#if defined(WITH_ZSTD) -#include - - -/** Filter using ZSTD compression. */ -struct ZSTDLoadFilter : LoadFilter { - ZSTD_DCtx *zstd; ///< ZSTD decompression context - byte fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file - ZSTD_inBuffer input; ///< ZSTD input buffer for fread_buf - - /** - * Initialise this filter. - * @param chain The next filter in this chain. - */ - ZSTDLoadFilter(LoadFilter *chain) : LoadFilter(chain) - { - this->zstd = ZSTD_createDCtx(); - if (!this->zstd) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); - this->input = {this->fread_buf, 0, 0}; - } - - /** Clean everything up. */ - ~ZSTDLoadFilter() - { - ZSTD_freeDCtx(this->zstd); - } - - size_t Read(byte *buf, size_t size) override - { - ZSTD_outBuffer output{buf, size, 0}; - - do { - /* read more bytes from the file? */ - if (this->input.pos == this->input.size) { - this->input.size = this->chain->Read(this->fread_buf, sizeof(this->fread_buf)); - this->input.pos = 0; - if (this->input.size == 0) break; - } - - size_t ret = ZSTD_decompressStream(this->zstd, &output, &this->input); - if (ZSTD_isError(ret)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "libzstd returned error code"); - if (ret == 0) break; - } while (output.pos < output.size); - - return output.pos; - } -}; - -/** Filter using ZSTD compression. */ -struct ZSTDSaveFilter : SaveFilter { - ZSTD_CCtx *zstd; ///< ZSTD compression context - byte buf[MEMORY_CHUNK_SIZE]; ///< output buffer - - /** - * Initialise this filter. - * @param chain The next filter in this chain. - * @param compression_level The requested level of compression. - */ - ZSTDSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) - { - this->zstd = ZSTD_createCCtx(); - if (!this->zstd) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); - if (ZSTD_isError(ZSTD_CCtx_setParameter(this->zstd, ZSTD_c_compressionLevel, (int)compression_level - 100))) { - ZSTD_freeCCtx(this->zstd); - SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "invalid compresison level"); - } - } - - /** Clean up what we allocated. */ - ~ZSTDSaveFilter() - { - ZSTD_freeCCtx(this->zstd); - } - - /** - * Helper loop for writing the data. - * @param p The bytes to write. - * @param len Amount of bytes to write. - * @param mode Mode for ZSTD_compressStream2. - */ - void WriteLoop(byte *p, size_t len, ZSTD_EndDirective mode) - { - ZSTD_inBuffer input{p, len, 0}; - - bool finished; - do { - ZSTD_outBuffer output{this->buf, sizeof(this->buf), 0}; - size_t remaining = ZSTD_compressStream2(this->zstd, &output, &input, mode); - if (ZSTD_isError(remaining)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "libzstd returned error code"); - - if (output.pos != 0) this->chain->Write(this->buf, output.pos); - - finished = (mode == ZSTD_e_end ? (remaining == 0) : (input.pos == input.size)); - } while (!finished); - } - - void Write(byte *buf, size_t size) override - { - this->WriteLoop(buf, size, ZSTD_e_continue); - } - - void Finish() override - { - this->WriteLoop(nullptr, 0, ZSTD_e_end); - this->chain->Finish(); - } -}; - -#endif /* WITH_LIBZSTD */ - -/******************************************* - ************* END OF CODE ***************** - *******************************************/ - -enum SaveLoadFormatFlags : byte { - SLF_NONE = 0, - SLF_NO_THREADED_LOAD = 1 << 0, ///< Unsuitable for threaded loading - SLF_REQUIRES_ZSTD = 1 << 1, ///< Automatic selection requires the zstd flag -}; -DECLARE_ENUM_AS_BIT_SET(SaveLoadFormatFlags); - -/** 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 - - LoadFilter *(*init_load)(LoadFilter *chain); ///< Constructor for the load filter. - SaveFilter *(*init_write)(SaveFilter *chain, byte compression); ///< Constructor for the save filter. - - byte min_compression; ///< the minimum compression level of this format - byte default_compression; ///< the default compression level of this format - byte max_compression; ///< the maximum compression level of this format - SaveLoadFormatFlags flags; ///< flags -}; - -/** The different saveload formats known/understood by OpenTTD. */ -static const SaveLoadFormat _saveload_formats[] = { -#if defined(WITH_LZO) - /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */ - {"lzo", TO_BE32X('OTTD'), CreateLoadFilter, CreateSaveFilter, 0, 0, 0, SLF_NO_THREADED_LOAD}, -#else - {"lzo", TO_BE32X('OTTD'), nullptr, nullptr, 0, 0, 0, SLF_NO_THREADED_LOAD}, -#endif - /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */ - {"none", TO_BE32X('OTTN'), CreateLoadFilter, CreateSaveFilter, 0, 0, 0, SLF_NONE}, -#if defined(WITH_ZLIB) - /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is - * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level - * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */ - {"zlib", TO_BE32X('OTTZ'), CreateLoadFilter, CreateSaveFilter, 0, 6, 9, SLF_NONE}, -#else - {"zlib", TO_BE32X('OTTZ'), nullptr, nullptr, 0, 0, 0, SLF_NONE}, -#endif -#if defined(WITH_LIBLZMA) - /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves. - * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower. - * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50% - * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much. - * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */ - {"lzma", TO_BE32X('OTTX'), CreateLoadFilter, CreateSaveFilter, 0, 2, 9, SLF_NONE}, -#else - {"lzma", TO_BE32X('OTTX'), nullptr, nullptr, 0, 0, 0, SLF_NONE}, -#endif -#if defined(WITH_ZSTD) - /* Zstd provides a decent compression rate at a very high compression/decompression speed. Compared to lzma level 2 - * zstd saves are about 40% larger (on level 1) but it has about 30x faster compression and 5x decompression making it - * a good choice for multiplayer servers. And zstd level 1 seems to be the optimal one for client connection speed - * (compress + 10 MB/s download + decompress time), about 3x faster than lzma:2 and 1.5x than zlib:2 and lzo. - * As zstd has negative compression levels the values were increased by 100 moving zstd level range -100..22 into - * openttd 0..122. Also note that value 100 mathes zstd level 0 which is a special value for default level 3 (openttd 103) */ - {"zstd", TO_BE32X('OTTS'), CreateLoadFilter, CreateSaveFilter, 0, 101, 122, SLF_REQUIRES_ZSTD}, -#else - {"zstd", TO_BE32X('OTTS'), nullptr, nullptr, 0, 0, 0, SLF_REQUIRES_ZSTD}, -#endif -}; - -/** - * Return the savegameformat of the game. Whether it was created with ZLIB compression - * uncompressed, or another type - * @param full_name Name of the savegame format. If empty it picks the first available one - * @param compression_level Output for telling what compression level we want. - * @return Pointer to SaveLoadFormat struct giving all characteristics of this type of savegame - */ -static const SaveLoadFormat *GetSavegameFormat(const std::string &full_name, byte *compression_level, SaveModeFlags flags) +void SlFixPointerChunkByID(uint32 id) { - const SaveLoadFormat *def = lastof(_saveload_formats); - - /* find default savegame format, the highest one with which files can be written */ - while (!def->init_write || ((def->flags & SLF_REQUIRES_ZSTD) && !(flags & SMF_ZSTD_OK))) def--; - - if (!full_name.empty()) { - /* Get the ":..." of the compression level out of the way */ - size_t separator = full_name.find(':'); - bool has_comp_level = separator != std::string::npos; - const std::string name(full_name, 0, has_comp_level ? separator : full_name.size()); - - for (const SaveLoadFormat *slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) { - if (slf->init_write != nullptr && name.compare(slf->name) == 0) { - *compression_level = slf->default_compression; - if (has_comp_level) { - const std::string complevel(full_name, separator + 1); - - /* Get the level and determine whether all went fine. */ - size_t processed; - long level = std::stol(complevel, &processed, 10); - if (processed == 0 || level != Clamp(level, slf->min_compression, slf->max_compression)) { - SetDParamStr(0, complevel); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, WL_CRITICAL); - } else { - *compression_level = level; - } - } - return slf; - } - } - - SetDParamStr(0, name); - SetDParamStr(1, def->name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, WL_CRITICAL); - } - *compression_level = def->default_compression; - return def; -} - -/* actual loader/saver function */ -void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings); -extern bool AfterLoadGame(); -extern bool LoadOldSaveGame(const std::string &file); - -/** - * Clear temporary data that is passed between various saveload phases. - */ -static void ResetSaveloadData() -{ - ResetTempEngineData(); - ResetLabelMaps(); - ResetOldWaypoints(); + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + DEBUG(sl, 3, "Fixing pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id); + ch->FixPointers(); } /** - * Clear/free saveload state. + * Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is + * prefixed by an ID identifying it, followed by data, and terminator where appropriate + * @param ch The chunkhandler that will be used for the operation */ -static inline void ClearSaveLoadState() +static void SlSaveChunk(const ChunkHandler &ch) { - delete _sl.dumper; - _sl.dumper = nullptr; + if (ch.type == CH_READONLY) return; - delete _sl.sf; - _sl.sf = nullptr; + SlWriteUint32(ch.id); + DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); - delete _sl.reader; - _sl.reader = nullptr; + _sl.block_mode = ch.type; + _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); - delete _sl.lf; - _sl.lf = nullptr; + _sl.need_length = (_sl.expect_table_header || _sl.block_mode == CH_RIFF) ? NL_WANTLENGTH : NL_NONE; - _sl.save_flags = SMF_NONE; - - GamelogStopAnyAction(); -} - -/** Update the gui accordingly when starting saving and set locks on saveload. */ -static void SaveFileStart() -{ - SetMouseCursorBusy(true); - - InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START); - _sl.saveinprogress = true; -} - -/** Update the gui accordingly when saving is done and release locks on saveload. */ -static void SaveFileDone() -{ - SetMouseCursorBusy(false); - - InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH); - _sl.saveinprogress = false; - -#ifdef __EMSCRIPTEN__ - EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs()); -#endif -} - -/** 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.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_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, INVALID_STRING_ID, WL_ERROR); - SaveFileDone(); -} - -/** - * We have written the whole game into memory, _memory_savegame, now find - * and appropriate compressor and start writing to file. - */ -static SaveOrLoadResult SaveFileToDisk(bool threaded) -{ - try { - byte compression; - const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression, _sl.save_flags); - - DEBUG(sl, 3, "Using compression format: %s, level: %u", fmt->name, compression); - - /* We have written our stuff to memory, now write it to file! */ - uint32 hdr[2] = { fmt->tag, TO_BE32((uint32) (SAVEGAME_VERSION | SAVEGAME_VERSION_EXT) << 16) }; - _sl.sf->Write((byte*)hdr, sizeof(hdr)); - - _sl.sf = fmt->init_write(_sl.sf, compression); - _sl.dumper->Flush(_sl.sf); - - ClearSaveLoadState(); - - if (threaded) SetAsyncSaveFinish(SaveFileDone); - - return SL_OK; - } catch (...) { - ClearSaveLoadState(); - - AsyncSaveFinishProc asfp = SaveFileDone; - - /* We don't want to shout when saving is just - * cancelled due to a client disconnecting. */ - if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) { - /* Skip the "colour" character */ - DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); - asfp = SaveFileError; - } - - if (threaded) { - SetAsyncSaveFinish(asfp); - } else { - asfp(); - } - return SL_ERROR; - } -} - -void WaitTillSaved() -{ - if (!_save_thread.joinable()) return; - - _save_thread.join(); - - /* Make sure every other state is handled properly as well. */ - ProcessAsyncSaveFinish(); -} - -/** - * Actually perform the saving of the savegame. - * General tactics is to first save the game to memory, then write it to file - * using the writer, either in threaded mode if possible, or single-threaded. - * @param writer The filter to write the savegame to. - * @param threaded Whether to try to perform the saving asynchronously. - * @return Return the result of the action. #SL_OK or #SL_ERROR - */ -static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded) -{ - assert(!_sl.saveinprogress); - - _sl.dumper = new MemoryDumper(); - _sl.sf = writer; - - _sl_version = SAVEGAME_VERSION; - SlXvSetCurrentState(); - - SaveViewportBeforeSaveGame(); - SlSaveChunks(); - - SaveFileStart(); - - if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) { - if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode..."); - - SaveOrLoadResult result = SaveFileToDisk(false); - SaveFileDone(); - - return result; - } - - return SL_OK; -} - -/** - * Save the game using a (writer) filter. - * @param writer The filter to write the savegame to. - * @param threaded Whether to try to perform the saving asynchronously. - * @param flags Save mode flags. - * @return Return the result of the action. #SL_OK or #SL_ERROR - */ -SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded, SaveModeFlags flags) -{ - try { - _sl.action = SLA_SAVE; - _sl.save_flags = flags; - return DoSave(writer, threaded); - } catch (...) { - ClearSaveLoadState(); - return SL_ERROR; - } -} - -bool IsNetworkServerSave() -{ - return _sl.save_flags & SMF_NET_SERVER; -} - -bool IsScenarioSave() -{ - return _sl.save_flags & SMF_SCENARIO; -} - -struct ThreadedLoadFilter : LoadFilter { - static const size_t BUFFER_COUNT = 4; - - std::mutex mutex; - std::condition_variable full_cv; - std::condition_variable empty_cv; - uint first_ready = 0; - uint count_ready = 0; - size_t read_offsets[BUFFER_COUNT]; - size_t read_counts[BUFFER_COUNT]; - byte read_buf[MEMORY_CHUNK_SIZE * BUFFER_COUNT]; ///< Buffers for reading from source. - bool no_thread = false; - - bool have_exception = false; - ThreadSlErrorException caught_exception; - - std::thread read_thread; - - /** - * Initialise this filter. - * @param chain The next filter in this chain. - */ - ThreadedLoadFilter(LoadFilter *chain) : LoadFilter(chain) - { - std::unique_lock lk(this->mutex); - if (!StartNewThread(&this->read_thread, "ottd:loadgame", &ThreadedLoadFilter::RunThread, this)) { - DEBUG(sl, 1, "Failed to start load read thread, reading non-threaded"); - this->no_thread = true; - } else { - DEBUG(sl, 2, "Started load read thread"); - } - } - - /** Clean everything up. */ - ~ThreadedLoadFilter() - { - std::unique_lock lk(this->mutex); - this->no_thread = true; - lk.unlock(); - this->empty_cv.notify_all(); - this->full_cv.notify_all(); - if (this->read_thread.joinable()) { - this->read_thread.join(); - DEBUG(sl, 2, "Joined load read thread"); - } - } - - static void RunThread(ThreadedLoadFilter *self) - { - try { - std::unique_lock lk(self->mutex); - while (!self->no_thread) { - if (self->count_ready == BUFFER_COUNT) { - self->full_cv.wait(lk); - continue; - } - - uint buf = (self->first_ready + self->count_ready) % BUFFER_COUNT; - lk.unlock(); - size_t read = self->chain->Read(self->read_buf + (buf * MEMORY_CHUNK_SIZE), MEMORY_CHUNK_SIZE); - lk.lock(); - self->read_offsets[buf] = 0; - self->read_counts[buf] = read; - self->count_ready++; - if (self->count_ready == 1) self->empty_cv.notify_one(); - } - } catch (const ThreadSlErrorException &ex) { - std::unique_lock lk(self->mutex); - self->caught_exception = std::move(ex); - self->have_exception = true; - self->empty_cv.notify_one(); - } - } - - size_t Read(byte *buf, size_t size) override - { - if (this->no_thread) return this->chain->Read(buf, size); - - size_t read = 0; - std::unique_lock lk(this->mutex); - while (read < size || this->have_exception) { - if (this->have_exception) { - this->have_exception = false; - SlError(this->caught_exception.string, this->caught_exception.extra_msg); - } - if (this->count_ready == 0) { - this->empty_cv.wait(lk); - continue; - } - - size_t to_read = std::min(size - read, read_counts[this->first_ready]); - if (to_read == 0) break; - memcpy(buf + read, this->read_buf + (this->first_ready * MEMORY_CHUNK_SIZE) + read_offsets[this->first_ready], to_read); - read += to_read; - read_offsets[this->first_ready] += to_read; - read_counts[this->first_ready] -= to_read; - if (read_counts[this->first_ready] == 0) { - this->first_ready = (this->first_ready + 1) % BUFFER_COUNT; - this->count_ready--; - if (this->count_ready == BUFFER_COUNT - 1) this->full_cv.notify_one(); - } - } - return read; - } -}; - -/** - * Actually perform the loading of a "non-old" savegame. - * @param reader The filter to read the savegame from. - * @param load_check Whether to perform the checking ("preview") or actually load the game. - * @return Return the result of the action. #SL_OK or #SL_REINIT ("unload" the game) - */ -static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check) -{ - _sl.lf = reader; - - if (load_check) { - /* Clear previous check data */ - _load_check_data.Clear(); - /* Mark SL_LOAD_CHECK as supported for this savegame. */ - _load_check_data.checkable = true; - } - - SlXvResetState(); - SlResetVENC(); - SlResetTNNC(); - auto guard = scope_guard([&]() { - SlResetVENC(); - SlResetTNNC(); - }); - - uint32 hdr[2]; - if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); - - /* see if we have any loader for this type. */ - const SaveLoadFormat *fmt = _saveload_formats; - for (;;) { - /* 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"); - _sl.lf->Reset(); - _sl_version = SL_MIN_VERSION; - _sl_minor_version = 0; - SlXvResetState(); - - /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */ - fmt = _saveload_formats; - for (;;) { - if (fmt == endof(_saveload_formats)) { - /* Who removed LZO support? */ - NOT_REACHED(); - } - if (fmt->tag == TO_BE32X('OTTD')) break; - fmt++; - } + switch (_sl.block_mode) { + case CH_RIFF: + ch.Save(); break; - } - - if (fmt->tag == hdr[0]) { - /* check version number */ - _sl_version = (SaveLoadVersion)(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. - * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */ - _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF; - - bool special_version = false; - if (_sl_version & SAVEGAME_VERSION_EXT) { - _sl_version = (SaveLoadVersion)(_sl_version & ~SAVEGAME_VERSION_EXT); - _sl_is_ext_version = true; - } else { - special_version = SlXvCheckSpecialSavegameVersions(); - } - - if (_sl_version >= SLV_SAVELOAD_LIST_LENGTH) { - if (_sl_is_ext_version) { - DEBUG(sl, 0, "Got an extended savegame version with a base version in the upstream mode range, giving up"); - SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME); - } else { - _sl_upstream_mode = true; - } - } - - DEBUG(sl, 1, "Loading savegame version %d%s%s%s%s", _sl_version, _sl_is_ext_version ? " (extended)" : "", - _sl_maybe_springpp ? " which might be SpringPP" : "", _sl_maybe_chillpp ? " which might be ChillPP" : "", _sl_upstream_mode ? " (upstream mode)" : ""); - - /* Is the version higher than the current? */ - if (_sl_version > MAX_LOAD_SAVEGAME_VERSION && !special_version) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME); - if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS && !special_version) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK); + case CH_TABLE: + case CH_ARRAY: + _sl.last_array_index = 0; + SlWriteByte(_sl.block_mode); + ch.Save(); + SlWriteArrayLength(0); // Terminate arrays break; - } - - fmt++; - } - - /* loader for this savegame type is not implemented? */ - if (fmt->init_load == nullptr) { - char err_str[64]; - seprintf(err_str, lastof(err_str), "Loader for '%s' is not available.", fmt->name); - SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str); - } - - _sl.lf = fmt->init_load(_sl.lf); - if (!(fmt->flags & SLF_NO_THREADED_LOAD)) { - _sl.lf = new ThreadedLoadFilter(_sl.lf); - } - _sl.reader = new ReadBuffer(_sl.lf); - _next_offs = 0; - - upstream_sl::SlResetLoadState(); - - if (!load_check) { - ResetSaveloadData(); - - /* 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, true); - - GamelogReset(); - - if (IsSavegameVersionBefore(SLV_4)) { - /* - * NewGRFs were introduced between 0.3,4 and 0.3.5, which both - * shared savegame version 4. Anything before that 'obviously' - * does not have any NewGRFs. Between the introduction and - * savegame version 41 (just before 0.5) the NewGRF settings - * were not stored in the savegame and they were loaded by - * using the settings from the main menu. - * So, to recap: - * - savegame version < 4: do not load any NewGRFs. - * - savegame version >= 41: load NewGRFs from savegame, which is - * already done at this stage by - * overwriting the main menu settings. - * - other savegame versions: use main menu settings. - * - * This means that users *can* crash savegame version 4..40 - * savegames if they set incompatible NewGRFs in the main menu, - * but can't crash anymore for savegame version < 4 savegames. - * - * Note: this is done here because AfterLoadGame is also called - * for TTO/TTD/TTDP savegames which have their own NewGRF logic. - */ - ClearGRFConfigList(&_grfconfig); - } - } - - if (load_check) { - /* Load chunks into _load_check_data. - * No pools are loaded. References are not possible, and thus do not need resolving. */ - SlLoadCheckChunks(); - } else { - /* Load chunks and resolve references */ - SlLoadChunks(); - SlFixPointers(); - } - - ClearSaveLoadState(); - - _savegame_type = SGT_OTTD; - - if (load_check) { - /* The only part from AfterLoadGame() we need */ - if (_load_check_data.want_grf_compatibility) _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig); - _load_check_data.sl_is_ext_version = _sl_is_ext_version; - } else { - GamelogStartAction(GLAT_LOAD); - - /* After loading fix up savegame for any internal changes that - * might have occurred since then. If it fails, load back the old game. */ - if (!AfterLoadGame()) { - GamelogStopAction(); - return SL_REINIT; - } - - GamelogStopAction(); - SlXvSetCurrentState(); - } - - return SL_OK; -} - -/** - * Load the game using a (reader) filter. - * @param reader The filter to read the savegame from. - * @return Return the result of the action. #SL_OK or #SL_REINIT ("unload" the game) - */ -SaveOrLoadResult LoadWithFilter(LoadFilter *reader) -{ - try { - _sl.action = SLA_LOAD; - return DoLoad(reader, false); - } catch (...) { - ClearSaveLoadState(); - - /* Skip the "colour" character */ - DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); - - return SL_REINIT; - } -} - -/** - * 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 fop Save or load mode. Load can also be a TTD(Patch) game. - * @param sb The sub directory to save the savegame in - * @param threaded True when threaded saving is allowed - * @return Return the result of the action. #SL_OK, #SL_ERROR, or #SL_REINIT ("unload" the game) - */ -SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded, SaveModeFlags save_flags) -{ - /* An instance of saving is already active, so don't go saving again */ - if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) { - /* if not an autosave, but a user action, show error message */ - if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR); - return SL_OK; - } - WaitTillSaved(); - - try { - /* Load a TTDLX or TTDPatch game */ - if (fop == SLO_LOAD && dft == DFT_OLD_GAME_FILE) { - ResetSaveloadData(); - - InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused - - /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them - * and if so a new NewGRF list will be made in LoadOldSaveGame. - * Note: this is done here because AfterLoadGame is also called - * for OTTD savegames which have their own NewGRF logic. */ - ClearGRFConfigList(&_grfconfig); - GamelogReset(); - if (!LoadOldSaveGame(filename)) return SL_REINIT; - _sl_version = SL_MIN_VERSION; - _sl_minor_version = 0; - SlXvResetState(); - GamelogStartAction(GLAT_LOAD); - if (!AfterLoadGame()) { - GamelogStopAction(); - return SL_REINIT; - } - GamelogStopAction(); - SlXvSetCurrentState(); - return SL_OK; - } - - assert(dft == DFT_GAME_FILE); - switch (fop) { - case SLO_CHECK: - _sl.action = SLA_LOAD_CHECK; - break; - - case SLO_LOAD: - _sl.action = SLA_LOAD; - break; - - case SLO_SAVE: - _sl.action = SLA_SAVE; - break; - - default: NOT_REACHED(); - } - _sl.save_flags = save_flags; - - FILE *fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb); - - /* Make it a little easier to load savegames from the console */ - if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR); - if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR); - if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR); - - if (fh == nullptr) { - SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); - } - - if (fop == SLO_SAVE) { // SAVE game - DEBUG(desync, 1, "save: date{%08x; %02x; %02x}; %s", _date, _date_fract, _tick_skip_counter, filename.c_str()); - if (!_settings_client.gui.threaded_saves) threaded = false; - - return DoSave(new FileWriter(fh), threaded); - } - - /* LOAD game */ - assert(fop == SLO_LOAD || fop == SLO_CHECK); - DEBUG(desync, 1, "load: %s", filename.c_str()); - return DoLoad(new FileReader(fh), fop == SLO_CHECK); - } catch (...) { - /* This code may be executed both for old and new save games. */ - ClearSaveLoadState(); - - /* Skip the "colour" character */ - if (fop != SLO_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); - - /* A saver/loader exception!! reinitialize all variables to prevent crash! */ - return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR; - } -} - -/** - * Create an autosave or netsave. - * @param counter A reference to the counter variable to be used for rotating the file name. - * @param netsave Indicates if this is a regular autosave or a netsave. - */ -void DoAutoOrNetsave(FiosNumberedSaveName &counter, bool threaded) -{ - char buf[MAX_PATH]; - - if (_settings_client.gui.keep_all_autosave) { - GenerateDefaultSaveName(buf, lastof(buf)); - strecat(buf, counter.Extension().c_str(), lastof(buf)); - } else { - strecpy(buf, counter.Filename().c_str(), lastof(buf)); - } - - DEBUG(sl, 2, "Autosaving to '%s'", buf); - if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, threaded, SMF_ZSTD_OK) != SL_OK) { - ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); - } -} - - -/** Do a save when exiting the game (_settings_client.gui.autosave_on_exit) */ -void DoExitSave() -{ - SaveOrLoad("exit.sav", SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, true, SMF_ZSTD_OK); -} - -/** - * 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 (!Company::IsValidID(cid)) { - for (const Company *c : Company::Iterate()) { - cid = c->index; + case CH_SPARSE_TABLE: + case CH_SPARSE_ARRAY: + SlWriteByte(_sl.block_mode); + ch.Save(); + SlWriteArrayLength(0); // Terminate arrays 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, !Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, last); - SanitizeFilename(buf); + if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); } -/** - * Set the mode and file type of the file to save or load based on the type of file entry at the file system. - * @param ft Type of file entry of the file system. - */ -void FileToSaveLoad::SetMode(FiosType ft) +/** Save a chunk of data */ +void SlSaveChunkChunkByID(uint32 id) { - this->SetMode(SLO_LOAD, GetAbstractFileType(ft), GetDetailedFileType(ft)); + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + + _sl.action = SLA_SAVE; + SlSaveChunk(*ch); } -/** - * Set the mode and file type of the file to save or load. - * @param fop File operation being performed. - * @param aft Abstract file type. - * @param dft Detailed file type. - */ -void FileToSaveLoad::SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft) +/** Reset state prior to a load */ +void SlResetLoadState() { - if (aft == FT_INVALID || aft == FT_NONE) { - this->file_op = SLO_INVALID; - this->detail_ftype = DFT_INVALID; - this->abstract_ftype = FT_INVALID; - return; - } - - this->file_op = fop; - this->detail_ftype = dft; - this->abstract_ftype = aft; + _next_offs = 0; } -/** - * Set the title of the file. - * @param title Title of the file. - */ -void FileToSaveLoad::Set(const FiosItem &item) +SaveLoadTable SaveLoadHandler::GetLoadDescription() const { - this->SetMode(item.type); - this->name = item.name; - this->title = item.title; + assert(this->load_description.has_value()); + return *this->load_description; } -bool SaveLoadFileTypeIsScenario() -{ - return _file_to_saveload.abstract_ftype == FT_SCENARIO; -} - -void SlUnreachablePlaceholder() -{ - NOT_REACHED(); } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 3b12286350..902abc54de 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -10,208 +10,338 @@ #ifndef SAVELOAD_H #define SAVELOAD_H -#include "saveload_types.h" +#include "../sl/saveload_common.h" #include "../fileio_type.h" #include "../fios.h" #include "../strings_type.h" -#include "../scope.h" - -#include -#include +#include "../core/span_type.hpp" +#include #include #include -/** Save or load result codes. */ -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) -}; +extern SaveLoadVersion _sl_version; +extern byte _sl_minor_version; +extern const SaveLoadVersion SAVEGAME_VERSION; +extern const SaveLoadVersion MAX_LOAD_SAVEGAME_VERSION; -/** Deals with the type of the savegame, independent of extension */ -struct FileToSaveLoad { - SaveLoadOperation file_op; ///< File operation to perform. - DetailedFileType detail_ftype; ///< Concrete file type (PNG, BMP, old save, etc). - AbstractFileType abstract_ftype; ///< Abstract type of file (scenario, heightmap, etc). - std::string name; ///< Name of the file. - std::string title; ///< Internal name of the game. +namespace upstream_sl { - void SetMode(FiosType ft); - void SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft); - void Set(const FiosItem &item); -}; - -/** Types of save games. */ -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 - SGT_TTO, ///< TTO savegame - SGT_INVALID = 0xFF, ///< broken savegame (used internally) -}; - -enum SaveModeFlags : byte { - SMF_NONE = 0, - SMF_NET_SERVER = 1 << 0, ///< Network server save - SMF_ZSTD_OK = 1 << 1, ///< Zstd OK - SMF_SCENARIO = 1 << 2, ///< Scenario save -}; -DECLARE_ENUM_AS_BIT_SET(SaveModeFlags); - -extern FileToSaveLoad _file_to_saveload; - -void GenerateDefaultSaveName(char *buf, const char *last); -void SetSaveLoadError(StringID str); -const char *GetSaveLoadErrorString(); -SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true, SaveModeFlags flags = SMF_NONE); -void WaitTillSaved(); -void ProcessAsyncSaveFinish(); -void DoExitSave(); - -void DoAutoOrNetsave(FiosNumberedSaveName &counter, bool threaded); - -SaveOrLoadResult SaveWithFilter(struct SaveFilter *writer, bool threaded, SaveModeFlags flags); -SaveOrLoadResult LoadWithFilter(struct LoadFilter *reader); -bool IsNetworkServerSave(); -bool IsScenarioSave(); - -typedef void ChunkSaveLoadProc(); typedef void AutolengthProc(void *arg); -void SlUnreachablePlaceholder(); - -enum ChunkSaveLoadSpecialOp { - CSLSO_PRE_LOAD, - CSLSO_PRE_LOADCHECK, - CSLSO_SHOULD_SAVE_CHUNK, -}; -enum ChunkSaveLoadSpecialOpResult { - CSLSOR_NONE, - CSLSOR_LOAD_CHUNK_CONSUMED, - CSLSOR_DONT_SAVE_CHUNK, -}; -typedef ChunkSaveLoadSpecialOpResult ChunkSaveLoadSpecialProc(uint32, ChunkSaveLoadSpecialOp); - /** Type of a chunk. */ enum ChunkType { CH_RIFF = 0, CH_ARRAY = 1, CH_SPARSE_ARRAY = 2, - CH_EXT_HDR = 15, ///< Extended chunk header + CH_TABLE = 3, + CH_SPARSE_TABLE = 4, - CH_UPSTREAM_SAVE = 0x80, + CH_TYPE_MASK = 0xf, ///< All ChunkType values have to be within this mask. + CH_READONLY, ///< Chunk is never saved. }; /** Handlers and description of chunk. */ struct ChunkHandler { uint32 id; ///< Unique ID (4 letters). - ChunkSaveLoadProc *save_proc; ///< Save procedure of the chunk. - ChunkSaveLoadProc *load_proc; ///< Load procedure of the chunk. - ChunkSaveLoadProc *ptrs_proc; ///< Manipulate pointers in the chunk. - ChunkSaveLoadProc *load_check_proc; ///< Load procedure for game preview. ChunkType type; ///< Type of the chunk. @see ChunkType - ChunkSaveLoadSpecialProc *special_proc = nullptr; + + ChunkHandler(uint32 id, ChunkType type) : id(id), type(type) {} + + virtual ~ChunkHandler() {} + + /** + * Save the chunk. + * Must be overridden, unless Chunk type is CH_READONLY. + */ + virtual void Save() const { NOT_REACHED(); } + + /** + * Load the chunk. + * Must be overridden. + */ + virtual void Load() const = 0; + + /** + * Fix the pointers. + * Pointers are saved using the index of the pointed object. + * On load, pointers are filled with indices and need to be fixed to point to the real object. + * Must be overridden if the chunk saves any pointer. + */ + virtual void FixPointers() const {} + + /** + * Load the chunk for game preview. + * Default implementation just skips the data. + * @param len Number of bytes to skip. + */ + virtual void LoadCheck(size_t len = 0) const; }; -template -void SlExecWithSlVersion(SaveLoadVersion use_version, F proc) -{ - extern SaveLoadVersion _sl_version; - SaveLoadVersion old_ver = _sl_version; - _sl_version = use_version; - auto guard = scope_guard([&]() { - _sl_version = old_ver; - }); - proc(); -} - -namespace upstream_sl { - template - ChunkHandler MakeUpstreamChunkHandler() - { - extern void SlLoadChunkByID(uint32); - extern void SlLoadCheckChunkByID(uint32); - extern void SlFixPointerChunkByID(uint32); - - ChunkHandler ch = { - id, - nullptr, - SlUnreachablePlaceholder, - []() { - SlExecWithSlVersion(F::GetLoadVersion(), []() { - SlFixPointerChunkByID(id); - }); - }, - SlUnreachablePlaceholder, - CH_UPSTREAM_SAVE - }; - ch.special_proc = [](uint32 chunk_id, ChunkSaveLoadSpecialOp op) -> ChunkSaveLoadSpecialOpResult { - assert(id == chunk_id); - switch (op) { - case CSLSO_PRE_LOAD: - SlExecWithSlVersion(F::GetLoadVersion(), []() { - SlLoadChunkByID(id); - }); - return CSLSOR_LOAD_CHUNK_CONSUMED; - case CSLSO_PRE_LOADCHECK: - SlExecWithSlVersion(F::GetLoadVersion(), []() { - SlLoadCheckChunkByID(id); - }); - return CSLSOR_LOAD_CHUNK_CONSUMED; - default: - return CSLSOR_NONE; - } - }; - return ch; - } -} - -using upstream_sl::MakeUpstreamChunkHandler; - -struct NullStruct { - byte null; -}; +/** A reference to ChunkHandler. */ +using ChunkHandlerRef = std::reference_wrapper; /** A table of ChunkHandler entries. */ -using ChunkHandlerTable = span; +using ChunkHandlerTable = span; + +/** A table of SaveLoadCompat entries. */ +using SaveLoadCompatTable = span; + +/** Handler for saving/loading an object to/from disk. */ +class SaveLoadHandler { +public: + std::optional> load_description; + + virtual ~SaveLoadHandler() {} + + /** + * Save the object to disk. + * @param object The object to store. + */ + virtual void Save(void *object) const {} + + /** + * Load the object from disk. + * @param object The object to load. + */ + virtual void Load(void *object) const {} + + /** + * Similar to load, but used only to validate savegames. + * @param object The object to load. + */ + virtual void LoadCheck(void *object) const {} + + /** + * A post-load callback to fix #SL_REF integers into pointers. + * @param object The object to fix. + */ + virtual void FixPointers(void *object) const {} + + /** + * Get the description of the fields in the savegame. + */ + virtual SaveLoadTable GetDescription() const = 0; + + /** + * Get the pre-header description of the fields in the savegame. + */ + virtual SaveLoadCompatTable GetCompatDescription() const = 0; + + /** + * Get the description for how to load the chunk. Depending on the + * savegame version this can either use the headers in the savegame or + * fall back to backwards compatibility and uses hard-coded headers. + */ + SaveLoadTable GetLoadDescription() const; +}; + +/** + * Default handler for saving/loading an object to/from disk. + * + * This handles a few common things for handlers, meaning the actual handler + * needs less code. + * + * Usage: class SlMine : public DefaultSaveLoadHandler {} + * + * @tparam TImpl The class initializing this template. + * @tparam TObject The class of the object using this SaveLoadHandler. + */ +template +class DefaultSaveLoadHandler : public SaveLoadHandler { +public: + SaveLoadTable GetDescription() const override { return static_cast(this)->description; } + SaveLoadCompatTable GetCompatDescription() const override { return static_cast(this)->compat_description; } + + virtual void Save(TObject *object) const {} + void Save(void *object) const override { this->Save(static_cast(object)); } + + virtual void Load(TObject *object) const {} + void Load(void *object) const override { this->Load(static_cast(object)); } + + virtual void LoadCheck(TObject *object) const {} + void LoadCheck(void *object) const override { this->LoadCheck(static_cast(object)); } + + virtual void FixPointers(TObject *object) const {} + void FixPointers(void *object) const override { this->FixPointers(static_cast(object)); } +}; /** Type of reference (#SLE_REF, #SLE_CONDREF). */ enum SLRefType { - REF_ORDER = 0, ///< Load/save a reference to an order. - REF_VEHICLE = 1, ///< Load/save a reference to a vehicle. - REF_STATION = 2, ///< Load/save a reference to a station. - REF_TOWN = 3, ///< Load/save a reference to a town. - REF_VEHICLE_OLD = 4, ///< Load/save an old-style reference to a vehicle (for pre-4.4 savegames). - REF_ROADSTOPS = 5, ///< Load/save a reference to a bus/truck stop. - REF_ENGINE_RENEWS = 6, ///< Load/save a reference to an engine renewal (autoreplace). - REF_CARGO_PACKET = 7, ///< Load/save a reference to a cargo packet. - REF_ORDERLIST = 8, ///< Load/save a reference to an orderlist. - REF_STORAGE = 9, ///< Load/save a reference to a persistent storage. - REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph. - REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job. - REF_TEMPLATE_VEHICLE = 12, ///< Load/save a reference to a template vehicle + REF_ORDER = 0, ///< Load/save a reference to an order. + REF_VEHICLE = 1, ///< Load/save a reference to a vehicle. + REF_STATION = 2, ///< Load/save a reference to a station. + REF_TOWN = 3, ///< Load/save a reference to a town. + REF_VEHICLE_OLD = 4, ///< Load/save an old-style reference to a vehicle (for pre-4.4 savegames). + REF_ROADSTOPS = 5, ///< Load/save a reference to a bus/truck stop. + REF_ENGINE_RENEWS = 6, ///< Load/save a reference to an engine renewal (autoreplace). + REF_CARGO_PACKET = 7, ///< Load/save a reference to a cargo packet. + REF_ORDERLIST = 8, ///< Load/save a reference to an orderlist. + REF_STORAGE = 9, ///< Load/save a reference to a persistent storage. + REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph. + REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job. }; -/** Flags for chunk extended headers */ -enum SaveLoadChunkExtHeaderFlags { - SLCEHF_BIG_RIFF = 1 << 0, ///< This block uses a 60-bit RIFF chunk size +/** + * 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. + * NOTE: the SLE_FILE_NNN values are stored in the savegame! */ + SLE_FILE_END = 0, ///< Used to mark end-of-header in tables. + SLE_FILE_I8 = 1, + SLE_FILE_U8 = 2, + SLE_FILE_I16 = 3, + SLE_FILE_U16 = 4, + SLE_FILE_I32 = 5, + SLE_FILE_U32 = 6, + SLE_FILE_I64 = 7, + SLE_FILE_U64 = 8, + SLE_FILE_STRINGID = 9, ///< StringID offset into strings-array + SLE_FILE_STRING = 10, + SLE_FILE_STRUCT = 11, + /* 4 more possible file-primitives */ + + SLE_FILE_TYPE_MASK = 0xf, ///< Mask to get the file-type (and not any flags). + SLE_FILE_HAS_LENGTH_FIELD = 1 << 4, ///< Bit stored in savegame to indicate field has a length field for each entry. + + /* 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_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_U32, + SLE_STRINGBUF = SLE_FILE_STRING | SLE_VAR_STRB, + 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_STR = SLE_STRING, + SLE_STRQ = SLE_STRINGQUOTE, + + /* 8 bits allocated for a maximum of 8 flags + * Flags directing saving/loading of a variable */ + SLF_ALLOW_CONTROL = 1 << 8, ///< Allow control codes in the strings. + SLF_ALLOW_NEWLINE = 1 << 9, ///< Allow new lines in the strings. +}; + +typedef uint32 VarType; + +/** Type of data saved. */ +enum SaveLoadType : byte { + SL_VAR = 0, ///< Save/load a variable. + SL_REF = 1, ///< Save/load a reference. + SL_STRUCT = 2, ///< Save/load a struct. + + SL_STR = 3, ///< Save/load a string. + SL_STDSTR = 4, ///< Save/load a \c std::string. + + SL_ARR = 5, ///< Save/load a fixed-size array of #SL_VAR elements. + SL_DEQUE = 6, ///< Save/load a deque of #SL_VAR elements. + SL_VECTOR = 7, ///< Save/load a vector of #SL_VAR elements. + SL_REFLIST = 8, ///< Save/load a list of #SL_REF elements. + SL_STRUCTLIST = 9, ///< Save/load a list of structs. + + SL_SAVEBYTE = 10, ///< Save (but not load) a byte. + SL_NULL = 11, ///< Save null-bytes and load to nowhere. + + SL_REFDEQUE, ///< Save/load a deque of #SL_REF elements. + SL_REFVEC, ///< Save/load a vector of #SL_REF elements. +}; + +typedef void *SaveLoadAddrProc(void *base, size_t extra); + +/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */ +struct SaveLoad { + std::string name; ///< Name of this field (optional, used for tables). + 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; this field combines both FileVarType and MemVarType. + uint16 length; ///< (Conditional) length of the variable (eg. arrays) (max array size is 65536 elements). + SaveLoadVersion version_from; ///< Save/load the variable starting from this savegame version. + SaveLoadVersion version_to; ///< Save/load the variable before this savegame version. + size_t size; ///< The sizeof size. + SaveLoadAddrProc *address_proc; ///< Callback proc the get the actual variable address in memory. + size_t extra_data; ///< Extra data for the callback proc. + std::shared_ptr handler; ///< Custom handler for Save/Load procs. +}; + +/** + * SaveLoad information for backwards compatibility. + * + * At SLV_SETTINGS_NAME a new method of keeping track of fields in a savegame + * was added, where the order of fields is no longer important. For older + * savegames we still need to know the correct order. This struct is the glue + * to make that happen. + */ +struct SaveLoadCompat { + std::string name; ///< Name of the field. + uint16 length; ///< Length of the NULL field. + SaveLoadVersion version_from; ///< Save/load the variable starting from this savegame version. + SaveLoadVersion version_to; ///< Save/load the variable before this savegame version. }; -DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) /** * Storage of simple variables, references (pointers), and arrays. * @param cmd Load/save type. @see SaveLoadType + * @param name Field name for table chunks. + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + * @param extra Extra data to pass to the address callback function. + * @note In general, it is better to use one of the SLE_* macros below. + */ +#define SLE_GENERAL_NAME(cmd, name, base, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast(static_cast(std::addressof(static_cast(b)->variable))); }, extra, nullptr} + +/** + * Storage of simple variables, references (pointers), and arrays with a custom name. + * @param cmd Load/save type. @see SaveLoadType * @param base Name of the class or struct containing the variable. * @param variable Name of the variable in the class or struct referenced by \a base. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the field. * @param to Last savegame version that has the field. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + * @param extra Extra data to pass to the address callback function. * @note In general, it is better to use one of the SLE_* macros below. */ -#define SLE_GENERAL_X(cmd, base, variable, type, length, from, to, extver) SaveLoad {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable), cpp_sizeof(base, variable), extver} -#define SLE_GENERAL(cmd, base, variable, type, length, from, to) SLE_GENERAL_X(cmd, base, variable, type, length, from, to, SlXvFeatureTest()) +#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) SLE_GENERAL_NAME(cmd, #variable, base, variable, type, length, from, to, extra) /** * Storage of a variable in some savegame versions. @@ -220,10 +350,19 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the field. * @param to Last savegame version that has the field. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDVAR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VAR, base, variable, type, 0, from, to, extver) -#define SLE_CONDVAR(base, variable, type, from, to) SLE_CONDVAR_X(base, variable, type, from, to, SlXvFeatureTest()) +#define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to, 0) + +/** + * Storage of a variable in some savegame versions. + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param name Field name for table chunks. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + */ +#define SLE_CONDVARNAME(base, variable, name, type, from, to) SLE_GENERAL_NAME(SL_VAR, name, base, variable, type, 0, from, to, 0) /** * Storage of a reference in some savegame versions. @@ -232,10 +371,8 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) * @param type Type of the reference, a value from #SLRefType. * @param from First savegame version that has the field. * @param to Last savegame version that has the field. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDREF_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REF, base, variable, type, 0, from, to, extver) -#define SLE_CONDREF(base, variable, type, from, to) SLE_CONDREF_X(base, variable, type, from, to, SlXvFeatureTest()) +#define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to, 0) /** * Storage of a fixed-size array of #SL_VAR elements in some savegame versions. @@ -245,10 +382,8 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) * @param length Number of elements in the array. * @param from First savegame version that has the array. * @param to Last savegame version that has the array. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDARR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_ARR, base, variable, type, length, from, to, extver) -#define SLE_CONDARR(base, variable, type, length, from, to) SLE_CONDARR_X(base, variable, type, length, from, to, SlXvFeatureTest()) +#define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to, 0) /** * Storage of a string in some savegame versions. @@ -258,10 +393,8 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) * @param length Number of elements in the string (only used for fixed size buffers). * @param from First savegame version that has the string. * @param to Last savegame version that has the string. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDSTR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_STR, base, variable, type, length, from, to, extver) -#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_CONDSTR_X(base, variable, type, length, from, to, SlXvFeatureTest()) +#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to, 0) /** * Storage of a \c std::string in some savegame versions. @@ -270,10 +403,8 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the string. * @param to Last savegame version that has the string. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDSSTR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_STDSTR, base, variable, type, 0, from, to, extver) -#define SLE_CONDSSTR(base, variable, type, from, to) SLE_GENERAL(SL_STDSTR, base, variable, type, 0, from, to) +#define SLE_CONDSSTR(base, variable, type, from, to) SLE_GENERAL(SL_STDSTR, base, variable, type, 0, from, to, 0) /** * Storage of a list of #SL_REF elements in some savegame versions. @@ -282,46 +413,28 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the list. * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDREFLIST_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REFLIST, base, variable, type, 0, from, to, extver) -#define SLE_CONDREFLIST(base, variable, type, from, to) SLE_CONDREFLIST_X(base, variable, type, from, to, SlXvFeatureTest()) +#define SLE_CONDREFLIST(base, variable, type, from, to) SLE_GENERAL(SL_REFLIST, base, variable, type, 0, from, to, 0) /** - * Storage of a deque in some savegame versions. + * Storage of a deque of #SL_REF elements in some savegame versions. * @param base Name of the class or struct containing the list. * @param variable Name of the variable in the class or struct referenced by \a base. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the list. * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDPTRDEQ_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_PTRDEQ, base, variable, type, 0, from, to, extver) -#define SLE_CONDPTRDEQ(base, variable, type, from, to) SLE_CONDPTRDEQ_X(base, variable, type, from, to, SlXvFeatureTest()) +#define SLE_CONDREFDEQUE(base, variable, type, from, to) SLE_GENERAL(SL_REFDEQUE, base, variable, type, 0, from, to, 0) /** - * Storage of a vector in some savegame versions. + * Storage of a vector of #SL_REF elements in some savegame versions. * @param base Name of the class or struct containing the list. * @param variable Name of the variable in the class or struct referenced by \a base. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the list. * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDVEC_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VEC, base, variable, type, 0, from, to, extver) -#define SLE_CONDVEC(base, variable, type, from, to) SLE_CONDVEC_X(base, variable, type, from, to, SlXvFeatureTest()) - -/** - * Storage of a variable vector in some savegame versions. - * @param base Name of the class or struct containing the list. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field - */ -#define SLE_CONDVARVEC_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VARVEC, base, variable, type, 0, from, to, extver) -#define SLE_CONDVARVEC(base, variable, type, from, to) SLE_CONDVARVEC_X(base, variable, type, from, to, SlXvFeatureTest()) +#define SLE_CONDREFVEC(base, variable, type, from, to) SLE_GENERAL(SL_REFVEC, base, variable, type, 0, from, to, 0) /** * Storage of a deque of #SL_VAR elements in some savegame versions. @@ -330,10 +443,8 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the list. * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLE_CONDDEQUE_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_DEQUE, base, variable, type, 0, from, to, extver) -#define SLE_CONDDEQUE(base, variable, type, from, to) SLE_CONDDEQUE_X(base, variable, type, from, to, SlXvFeatureTest()) +#define SLE_CONDDEQUE(base, variable, type, from, to) SLE_GENERAL(SL_DEQUE, base, variable, type, 0, from, to, 0) /** * Storage of a variable in every version of a savegame. @@ -342,6 +453,7 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) * @param type Storage of the data in memory and in the savegame. */ #define SLE_VAR(base, variable, type) SLE_CONDVAR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLE_VAR2(base, name, variable, type) SLE_CONDVARNAME(base, variable, name, type, SL_MIN_VERSION, SL_MAX_VERSION) /** * Storage of a reference in every version of a savegame. @@ -386,228 +498,250 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) #define SLE_REFLIST(base, variable, type) SLE_CONDREFLIST(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) /** - * Storage of a deque in every savegame version. + * Storage of a deque of #SL_REF elements in every savegame version. * @param base Name of the class or struct containing the list. * @param variable Name of the variable in the class or struct referenced by \a base. * @param type Storage of the data in memory and in the savegame. */ -#define SLE_PTRDEQ(base, variable, type) SLE_CONDPTRDEQ(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLE_REFDEQUE(base, variable, type) SLE_CONDREFDEQUE(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) /** - * Storage of a vector in every savegame version. + * Storage of a vector of #SL_REF elements in every savegame version. * @param base Name of the class or struct containing the list. * @param variable Name of the variable in the class or struct referenced by \a base. * @param type Storage of the data in memory and in the savegame. */ -#define SLE_VEC(base, variable, type) SLE_CONDVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLE_REFVEC(base, variable, type) SLE_CONDREFVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) /** - * Storage of a variable vector in every savegame version. - * @param base Name of the class or struct containing the list. + * Only write byte during saving; never read it during loading. + * When using SLE_SAVEBYTE you will have to read this byte before the table + * this is in is read. This also means SLE_SAVEBYTE can only be used at the + * top of a chunk. + * This is intended to be used to indicate what type of entry this is in a + * list of entries. + * @param base Name of the class or struct containing the variable. * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. */ -#define SLE_VARVEC(base, variable, type) SLE_CONDVARVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Empty space in every savegame version. - * @param length Length of the empty space. - */ -#define SLE_NULL(length) SLE_CONDNULL(length, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Empty space in some savegame versions. - * @param length Length of the empty space. - * @param from First savegame version that has the empty space. - * @param to Last savegame version that has the empty space. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have empty space - */ -#define SLE_CONDNULL_X(length, from, to, extver) SLE_CONDARR_X(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, extver) -#define SLE_CONDNULL(length, from, to) SLE_CONDNULL_X(length, from, to, SlXvFeatureTest()) - -/** Translate values ingame to different values in the savegame and vv. */ -#define SLE_WRITEBYTE(base, variable) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION) - -#define SLE_VEH_INCLUDE() {false, SL_VEH_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, SlXvFeatureTest()} -#define SLE_ST_INCLUDE() {false, SL_ST_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, SlXvFeatureTest()} +#define SLE_SAVEBYTE(base, variable) SLE_GENERAL(SL_SAVEBYTE, base, variable, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, 0) /** * Storage of global simple variables, references (pointers), and arrays. + * @param name The name of the field. * @param cmd Load/save type. @see SaveLoadType * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the field. * @param to Last savegame version that has the field. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + * @param extra Extra data to pass to the address callback function. * @note In general, it is better to use one of the SLEG_* macros below. */ -#define SLEG_GENERAL_X(cmd, variable, type, length, from, to, extver) SaveLoad {true, cmd, type, length, from, to, (void*)&variable, sizeof(variable), extver} -#define SLEG_GENERAL(cmd, variable, type, length, from, to) SLEG_GENERAL_X(cmd, variable, type, length, from, to, SlXvFeatureTest()) +#define SLEG_GENERAL(name, cmd, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast(std::addressof(variable)); }, extra, nullptr} /** * Storage of a global variable in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the field. * @param to Last savegame version that has the field. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLEG_CONDVAR_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VAR, variable, type, 0, from, to, extver) -#define SLEG_CONDVAR(variable, type, from, to) SLEG_CONDVAR_X(variable, type, from, to, SlXvFeatureTest()) +#define SLEG_CONDVAR(name, variable, type, from, to) SLEG_GENERAL(name, SL_VAR, variable, type, 0, from, to, 0) /** * Storage of a global reference in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the field. * @param to Last savegame version that has the field. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLEG_CONDREF_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_REF, variable, type, 0, from, to, extver) -#define SLEG_CONDREF(variable, type, from, to) SLEG_CONDREF_X(variable, type, from, to, SlXvFeatureTest()) +#define SLEG_CONDREF(name, variable, type, from, to) SLEG_GENERAL(name, SL_REF, variable, type, 0, from, to, 0) /** * Storage of a global fixed-size array of #SL_VAR elements in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param length Number of elements in the array. * @param from First savegame version that has the array. * @param to Last savegame version that has the array. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLEG_CONDARR_X(variable, type, length, from, to, extver) SLEG_GENERAL_X(SL_ARR, variable, type, length, from, to, extver) -#define SLEG_CONDARR(variable, type, length, from, to) SLEG_CONDARR_X(variable, type, length, from, to, SlXvFeatureTest()) +#define SLEG_CONDARR(name, variable, type, length, from, to) SLEG_GENERAL(name, SL_ARR, variable, type, length, from, to, 0) /** * Storage of a global string in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param length Number of elements in the string (only used for fixed size buffers). * @param from First savegame version that has the string. * @param to Last savegame version that has the string. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLEG_CONDSTR_X(variable, type, length, from, to, extver) SLEG_GENERAL_X(SL_STR, variable, type, length, from, to, extver) -#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_CONDSTR_X(variable, type, length, from, to, SlXvFeatureTest()) +#define SLEG_CONDSTR(name, variable, type, length, from, to) SLEG_GENERAL(name, SL_STR, variable, type, length, from, to, 0) /** * Storage of a global \c std::string in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the string. * @param to Last savegame version that has the string. */ -#define SLEG_CONDSSTR_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_STDSTR, variable, type, 0, from, to, extver) -#define SLEG_CONDSSTR(variable, type, from, to) SLEG_GENERAL(SL_STDSTR, variable, type, 0, from, to) +#define SLEG_CONDSSTR(name, variable, type, from, to) SLEG_GENERAL(name, SL_STDSTR, variable, type, 0, from, to, 0) + +/** + * Storage of a structs in some savegame versions. + * @param name The name of the field. + * @param handler SaveLoadHandler for the structs. + * @param from First savegame version that has the struct. + * @param to Last savegame version that has the struct. + */ +#define SLEG_CONDSTRUCT(name, handler, from, to) SaveLoad {name, SL_STRUCT, 0, 0, from, to, 0, nullptr, 0, std::make_shared()} /** * Storage of a global reference list in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the list. * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLEG_CONDREFLIST_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_REFLIST, variable, type, 0, from, to, extver) -#define SLEG_CONDREFLIST(variable, type, from, to) SLEG_CONDREFLIST_X(variable, type, from, to, SlXvFeatureTest()) +#define SLEG_CONDREFLIST(name, variable, type, from, to) SLEG_GENERAL(name, SL_REFLIST, variable, type, 0, from, to, 0) /** - * Storage of a global deque in some savegame versions. + * Storage of a global reference deque in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the list. * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLEG_CONDPTRDEQ_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_PTRDEQ, variable, type, 0, from, to, extver) -#define SLEG_CONDPTRDEQ(variable, type, from, to) SLEG_CONDPTRDEQ_X(variable, type, from, to, SlXvFeatureTest()) +#define SLEG_CONDREFDEQUE(name, variable, type, from, to) SLEG_GENERAL(name, SL_REFDEQUE, variable, type, 0, from, to, 0) /** - * Storage of a global vector in some savegame versions. + * Storage of a global reference vector in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the list. * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLEG_CONDVEC_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VEC, variable, type, 0, from, to, extver) -#define SLEG_CONDVEC(variable, type, from, to) SLEG_CONDVEC_X(variable, type, from, to, SlXvFeatureTest()) +#define SLEG_CONDREFVEC(name, variable, type, from, to) SLEG_GENERAL(name, SL_REFVEC, variable, type, 0, from, to, 0) /** - * Storage of a variable vector in some savegame versions. + * Storage of a global vector of #SL_VAR elements in some savegame versions. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. * @param from First savegame version that has the list. * @param to Last savegame version that has the list. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field */ -#define SLEG_CONDVARVEC_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VARVEC, variable, type, 0, from, to, extver) -#define SLEG_CONDVARVEC(variable, type, from, to) SLEG_CONDVARVEC_X(variable, type, from, to, SlXvFeatureTest()) +#define SLEG_CONDVECTOR(name, variable, type, from, to) SLEG_GENERAL(name, SL_VECTOR, variable, type, 0, from, to, 0) + +/** + * Storage of a list of structs in some savegame versions. + * @param name The name of the field. + * @param handler SaveLoadHandler for the list of structs. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + */ +#define SLEG_CONDSTRUCTLIST(name, handler, from, to) SaveLoad {name, SL_STRUCTLIST, 0, 0, from, to, 0, nullptr, 0, std::make_shared()} /** * Storage of a global variable in every savegame version. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_VAR(variable, type) SLEG_CONDVAR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_VAR(name, variable, type) SLEG_CONDVAR(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) /** * Storage of a global reference in every savegame version. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_REF(variable, type) SLEG_CONDREF(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_REF(name, variable, type) SLEG_CONDREF(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) /** * Storage of a global fixed-size array of #SL_VAR elements in every savegame version. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_ARR(variable, type) SLEG_CONDARR(variable, type, lengthof(variable), SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_ARR(name, variable, type) SLEG_CONDARR(name, variable, type, lengthof(variable), SL_MIN_VERSION, SL_MAX_VERSION) /** * Storage of a global string in every savegame version. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, sizeof(variable), SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_STR(name, variable, type) SLEG_CONDSTR(name, variable, type, sizeof(variable), SL_MIN_VERSION, SL_MAX_VERSION) /** * Storage of a global \c std::string in every savegame version. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_SSTR(variable, type) SLEG_CONDSSTR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_SSTR(name, variable, type) SLEG_CONDSSTR(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a structs in every savegame version. + * @param name The name of the field. + * @param handler SaveLoadHandler for the structs. + */ +#define SLEG_STRUCT(name, handler) SLEG_CONDSTRUCT(name, handler, SL_MIN_VERSION, SL_MAX_VERSION) /** * Storage of a global reference list in every savegame version. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_REFLIST(variable, type) SLEG_CONDREFLIST(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_REFLIST(name, variable, type) SLEG_CONDREFLIST(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) /** - * Storage of a global deque in every savegame version. + * Storage of a global reference deque in every savegame version. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_PTRDEQ(variable, type) SLEG_CONDPTRDEQ(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_REFDEQUE(name, variable, type) SLEG_CONDREFDEQUE(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) /** - * Storage of a global vector in every savegame version. + * Storage of a global vector of #SL_VAR elements in every savegame version. + * @param name The name of the field. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_VEC(variable, type) SLEG_CONDVEC(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_VECTOR(name, variable, type) SLEG_CONDVECTOR(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) /** - * Empty global space in some savegame versions. + * Storage of a list of structs in every savegame version. + * @param name The name of the field. + * @param handler SaveLoadHandler for the list of structs. + */ +#define SLEG_STRUCTLIST(name, handler) SLEG_CONDSTRUCTLIST(name, handler, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Field name where the real SaveLoad can be located. + * @param name The name of the field. + */ +#define SLC_VAR(name) {name, 0, SL_MIN_VERSION, SL_MAX_VERSION} + +/** + * Empty space in every savegame version. * @param length Length of the empty space. * @param from First savegame version that has the empty space. * @param to Last savegame version that has the empty space. - * @param extver SlXvFeatureTest to test (along with from and to) which savegames have empty space */ -#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, (void*)nullptr, SlXvFeatureTest()} +#define SLC_NULL(length, from, to) {{}, length, from, to} + +/** End marker of compat variables save or load. */ +#define SLC_END() {{}, 0, SL_MIN_VERSION, SL_MIN_VERSION} /** * Checks whether the savegame is below \a major.\a minor. @@ -617,8 +751,6 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) */ static inline bool IsSavegameVersionBefore(SaveLoadVersion major, byte minor = 0) { - extern SaveLoadVersion _sl_version; - extern byte _sl_minor_version; return _sl_version < major || (minor > 0 && _sl_version == major && _sl_minor_version < minor); } @@ -629,27 +761,11 @@ static inline bool IsSavegameVersionBefore(SaveLoadVersion major, byte minor = 0 * @param major Major number of the version to check against. * @return Savegame version is at most the specified version. */ -static inline bool IsSavegameVersionUntil(SaveLoadVersion major) +static inline bool IsSavegameVersionBeforeOrAt(SaveLoadVersion major) { - extern SaveLoadVersion _sl_version; return _sl_version <= major; } -/** - * Checks if some version from/to combination falls within the range of the - * active savegame version. - * @param version_from Inclusive savegame version lower bound. - * @param version_to Exclusive savegame version upper bound. SL_MAX_VERSION if no upper bound. - * @return Active savegame version falls within the given range. - */ -static inline bool SlIsObjectCurrentlyValid(SaveLoadVersion version_from, SaveLoadVersion version_to, SlXvFeatureTest ext_feature_test) -{ - extern const SaveLoadVersion SAVEGAME_VERSION; - if (!ext_feature_test.IsFeaturePresent(_sl_xv_feature_static_versions, SAVEGAME_VERSION, version_from, version_to)) return false; - - return true; -} - /** * Get the NumberType of a setting. This describes the integer type * as it is represented in memory @@ -683,27 +799,21 @@ static inline bool IsNumericType(VarType conv) } /** - * Get the address of the variable. Which one to pick depends on the object - * pointer. If it is nullptr 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 + * Get the address of the variable. Null-variables don't have an address, + * everything else has a callback function that returns the address based + * on the saveload data and the current object for non-globals. */ static inline void *GetVariableAddress(const void *object, const SaveLoad &sld) { - /* Entry is a global address. */ - if (sld.global) return sld.address; - -#ifdef _DEBUG /* Entry is a null-variable, mostly used to read old savegames etc. */ if (GetVarMemType(sld.conv) == SLE_VAR_NULL) { - assert(sld.address == nullptr); + assert(sld.address_proc == nullptr); return nullptr; } /* Everything else should be a non-null pointer. */ - assert(object != nullptr); -#endif - return const_cast((const byte *)object + (ptrdiff_t)sld.address); + assert(sld.address_proc != nullptr); + return sld.address_proc(const_cast(object), sld.extra_data); } int64 ReadValue(const void *ptr, VarType conv); @@ -712,54 +822,21 @@ void WriteValue(void *ptr, VarType conv, int64 val); void SlSetArrayIndex(uint index); int SlIterateArray(); +void SlSetStructListLength(size_t length); +size_t SlGetStructListLength(size_t limit); + void SlAutolength(AutolengthProc *proc, void *arg); -std::vector SlSaveToVector(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 SaveLoadTable &slt); -struct SlLoadFromBufferState { - size_t old_obj_len; - byte *old_bufp; - byte *old_bufe; -}; +void SlGlobList(const SaveLoadTable &slt); +void SlCopy(void *object, size_t length, VarType conv); +std::vector SlTableHeader(const SaveLoadTable &slt); +std::vector SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct); +void SlObject(void *object, const SaveLoadTable &slt); -/** - * Run proc, loading exactly length bytes from the contents of buffer - * @param proc The callback procedure that is called - */ -template -void SlLoadFromBuffer(const byte *buffer, size_t length, F proc) -{ - extern SlLoadFromBufferState SlLoadFromBufferSetup(const byte *buffer, size_t length); - extern void SlLoadFromBufferRestore(const SlLoadFromBufferState &state, const byte *buffer, size_t length); - - SlLoadFromBufferState state = SlLoadFromBufferSetup(buffer, length); - proc(); - SlLoadFromBufferRestore(state, buffer, length); } -void SlGlobList(const SaveLoadTable &slt); -void SlArray(void *array, size_t length, VarType conv); -void SlObject(void *object, const SaveLoadTable &slt); -bool SlObjectMember(void *object, const SaveLoad &sld); - -std::vector SlFilterObject(const SaveLoadTable &slt); -void SlObjectSaveFiltered(void *object, const SaveLoadTable &slt); -void SlObjectLoadFiltered(void *object, const SaveLoadTable &slt); -void SlObjectPtrOrNullFiltered(void *object, const SaveLoadTable &slt); - -void NORETURN CDECL SlErrorFmt(StringID string, const char *msg, ...) WARN_FORMAT(2, 3); - -bool SaveloadCrashWithMissingNewGRFs(); - -void SlResetVENC(); -void SlProcessVENC(); - -void SlResetTNNC(); - -extern std::string _savegame_format; -extern bool _do_autosave; - #endif /* SAVELOAD_H */ diff --git a/src/saveload/upstream/settings_sl.cpp b/src/saveload/settings_sl.cpp similarity index 97% rename from src/saveload/upstream/settings_sl.cpp rename to src/saveload/settings_sl.cpp index 244a99e938..5224e01d0f 100644 --- a/src/saveload/upstream/settings_sl.cpp +++ b/src/saveload/settings_sl.cpp @@ -7,18 +7,18 @@ /** @file settings_sl.cpp Handles the saveload part of the settings. */ -#include "../../stdafx.h" +#include "../stdafx.h" #include "saveload.h" #include "debug.h" #include "compat/settings_sl_compat.h" -#include "../../settings_type.h" -#include "../../settings_internal.h" -#include "../../network/network.h" -#include "../../fios.h" +#include "../settings_type.h" +#include "../settings_internal.h" +#include "../network/network.h" +#include "../fios.h" -#include "../../safeguards.h" +#include "../safeguards.h" const SettingTable &GetSettingsTableInternal(); diff --git a/src/saveload/signs_sl.cpp b/src/saveload/signs_sl.cpp index 181dc6b7b7..72edcff1e0 100644 --- a/src/saveload/signs_sl.cpp +++ b/src/saveload/signs_sl.cpp @@ -8,13 +8,17 @@ /** @file signs_sl.cpp Code handling saving and loading of economy data */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/signs_sl_compat.h" + #include "../signs_base.h" #include "../fios.h" -#include "saveload.h" - #include "../safeguards.h" +namespace upstream_sl { + /** Description of a sign within the savegame. */ static const SaveLoad _sign_desc[] = { SLE_CONDVAR(Sign, name, SLE_NAME, SL_MIN_VERSION, SLV_84), @@ -24,46 +28,54 @@ static const SaveLoad _sign_desc[] = { SLE_CONDVAR(Sign, x, SLE_INT32, SLV_5, SL_MAX_VERSION), SLE_CONDVAR(Sign, y, SLE_INT32, SLV_5, SL_MAX_VERSION), SLE_CONDVAR(Sign, owner, SLE_UINT8, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR_X(Sign, z, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)), - SLE_CONDVAR_X(Sign, z, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)), + SLE_CONDVAR(Sign, z, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), + SLE_CONDVAR(Sign, z, SLE_INT32, SLV_164, SL_MAX_VERSION), }; -/** Save all signs */ -static void Save_SIGN() -{ - for (Sign *si : Sign::Iterate()) { - SlSetArrayIndex(si->index); - SlObject(si, _sign_desc); - } -} +struct SIGNChunkHandler : ChunkHandler { + SIGNChunkHandler() : ChunkHandler('SIGN', CH_TABLE) {} -/** Load all signs */ -static void Load_SIGN() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Sign *si = new (index) Sign(); - SlObject(si, _sign_desc); - /* Before version 6.1, signs didn't have owner. - * Before version 83, invalid signs were determined by si->str == 0. - * Before version 103, owner could be a bankrupted company. - * - we can't use IsValidCompany() now, so this is fixed in AfterLoadGame() - * All signs that were saved are valid (including those with just 'Sign' and INVALID_OWNER). - * - so set owner to OWNER_NONE if needed (signs from pre-version 6.1 would be lost) */ - if (IsSavegameVersionBefore(SLV_6, 1) || (IsSavegameVersionBefore(SLV_83) && si->owner == INVALID_OWNER)) { - si->owner = OWNER_NONE; - } + void Save() const override + { + SlTableHeader(_sign_desc); - /* Signs placed in scenario editor shall now be OWNER_DEITY */ - if (IsSavegameVersionBefore(SLV_171) && si->owner == OWNER_NONE && _file_to_saveload.abstract_ftype == FT_SCENARIO) { - si->owner = OWNER_DEITY; + for (Sign *si : Sign::Iterate()) { + SlSetArrayIndex(si->index); + SlObject(si, _sign_desc); } } -} -/** Chunk handlers related to signs. */ -static const ChunkHandler sign_chunk_handlers[] = { - { 'SIGN', Save_SIGN, Load_SIGN, nullptr, nullptr, CH_ARRAY }, + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_sign_desc, _sign_sl_compat); + + int index; + while ((index = SlIterateArray()) != -1) { + Sign *si = new (index) Sign(); + SlObject(si, slt); + /* Before version 6.1, signs didn't have owner. + * Before version 83, invalid signs were determined by si->str == 0. + * Before version 103, owner could be a bankrupted company. + * - we can't use IsValidCompany() now, so this is fixed in AfterLoadGame() + * All signs that were saved are valid (including those with just 'Sign' and INVALID_OWNER). + * - so set owner to OWNER_NONE if needed (signs from pre-version 6.1 would be lost) */ + if (IsSavegameVersionBefore(SLV_6, 1) || (IsSavegameVersionBefore(SLV_83) && si->owner == INVALID_OWNER)) { + si->owner = OWNER_NONE; + } + + /* Signs placed in scenario editor shall now be OWNER_DEITY */ + if (IsSavegameVersionBefore(SLV_171) && si->owner == OWNER_NONE && SaveLoadFileTypeIsScenario()) { + si->owner = OWNER_DEITY; + } + } + } +}; + +static const SIGNChunkHandler SIGN; +static const ChunkHandlerRef sign_chunk_handlers[] = { + SIGN, }; extern const ChunkHandlerTable _sign_chunk_handlers(sign_chunk_handlers); + +} diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index d802077f00..4494d17c3d 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -8,250 +8,38 @@ /** @file station_sl.cpp Code handling saving and loading of stations. */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/station_sl_compat.h" + #include "../station_base.h" #include "../waypoint_base.h" #include "../roadstop_base.h" #include "../vehicle_base.h" #include "../newgrf_station.h" #include "../newgrf_roadstop.h" -#include "../core/math_func.hpp" -#include "saveload.h" -#include "saveload_buffer.h" #include "table/strings.h" #include "../safeguards.h" -static byte _old_last_vehicle_type; -static uint8 _num_specs; -static uint8 _num_roadstop_specs; -static uint32 _num_roadstop_custom_tiles; -static std::vector _custom_road_stop_tiles; -static std::vector _custom_road_stop_data; - -/** - * Update the buoy orders to be waypoint orders. - * @param o the order 'list' to check. - */ -static void UpdateWaypointOrder(Order *o) -{ - if (!o->IsType(OT_GOTO_STATION)) return; - - const Station *st = Station::Get(o->GetDestination()); - if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) return; - - o->MakeGoToWaypoint(o->GetDestination()); -} - -/** - * Perform all steps to upgrade from the old station buoys to the new version - * that uses waypoints. This includes some old saveload mechanics. - */ -void MoveBuoysToWaypoints() -{ - /* Buoy orders become waypoint orders */ - for (OrderList *ol : OrderList::Iterate()) { - VehicleType vt = ol->GetFirstSharedVehicle()->type; - if (vt != VEH_SHIP && vt != VEH_TRAIN) continue; - - for (Order *o = ol->GetFirstOrder(); o != nullptr; o = o->next) UpdateWaypointOrder(o); - } - - for (Vehicle *v : Vehicle::Iterate()) { - VehicleType vt = v->type; - if (vt != VEH_SHIP && vt != VEH_TRAIN) continue; - - UpdateWaypointOrder(&v->current_order); - } - - /* Now make the stations waypoints */ - for (Station *st : Station::Iterate()) { - if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) continue; - - StationID index = st->index; - TileIndex xy = st->xy; - Town *town = st->town; - StringID string_id = st->string_id; - TinyString name = std::move(st->name); - Date build_date = st->build_date; - /* TTDPatch could use "buoys with rail station" for rail waypoints */ - bool train = st->train_station.tile != INVALID_TILE; - TileArea train_st = st->train_station; - - /* Delete the station, so we can make it a real waypoint. */ - delete st; - - /* Stations and waypoints are in the same pool, so if a station - * is deleted there must be place for a Waypoint. */ - assert(Waypoint::CanAllocateItem()); - Waypoint *wp = new (index) Waypoint(xy); - wp->town = town; - wp->string_id = train ? STR_SV_STNAME_WAYPOINT : STR_SV_STNAME_BUOY; - wp->name = std::move(name); - wp->delete_ctr = 0; // Just reset delete counter for once. - wp->build_date = build_date; - wp->owner = train ? GetTileOwner(xy) : OWNER_NONE; - - if (IsInsideBS(string_id, STR_SV_STNAME_BUOY, 9)) wp->town_cn = string_id - STR_SV_STNAME_BUOY; - - if (train) { - /* When we make a rail waypoint of the station, convert the map as well. */ - for (TileIndex t : train_st) { - if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != index) continue; - - SB(_me[t].m6, 3, 3, STATION_WAYPOINT); - wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE); - } - - wp->train_station = train_st; - wp->facilities |= FACIL_TRAIN; - } else if (IsBuoyTile(xy) && GetStationIndex(xy) == index) { - wp->rect.BeforeAddTile(xy, StationRect::ADD_FORCE); - wp->facilities |= FACIL_DOCK; - } - } -} - -void AfterLoadStations() -{ - /* Update the speclists of all stations to point to the currently loaded custom stations. */ - for (BaseStation *st : BaseStation::Iterate()) { - for (uint i = 0; i < st->speclist.size(); i++) { - if (st->speclist[i].grfid == 0) continue; - - st->speclist[i].spec = StationClass::GetByGrf(st->speclist[i].grfid, st->speclist[i].localidx, nullptr); - } - for (uint i = 0; i < st->roadstop_speclist.size(); i++) { - if (st->roadstop_speclist[i].grfid == 0) continue; - - st->roadstop_speclist[i].spec = RoadStopClass::GetByGrf(st->roadstop_speclist[i].grfid, st->roadstop_speclist[i].localidx, nullptr); - } - - if (Station::IsExpected(st)) { - Station *sta = Station::From(st); - for (const RoadStop *rs = sta->bus_stops; rs != nullptr; rs = rs->next) sta->bus_station.Add(rs->xy); - for (const RoadStop *rs = sta->truck_stops; rs != nullptr; rs = rs->next) sta->truck_station.Add(rs->xy); - } - - StationUpdateCachedTriggers(st); - StationUpdateRoadStopCachedTriggers(st); - } -} - -/** - * (Re)building of road stop caches after loading a savegame. - */ -void AfterLoadRoadStops() -{ - /* First construct the drive through entries */ - for (RoadStop *rs : RoadStop::Iterate()) { - if (IsDriveThroughStopTile(rs->xy)) rs->MakeDriveThrough(); - } - /* And then rebuild the data in those entries */ - for (RoadStop *rs : RoadStop::Iterate()) { - if (!HasBit(rs->status, RoadStop::RSSFB_BASE_ENTRY)) continue; - - rs->GetEntry(DIAGDIR_NE)->Rebuild(rs); - rs->GetEntry(DIAGDIR_NW)->Rebuild(rs); - } -} +namespace upstream_sl { static const SaveLoad _roadstop_desc[] = { SLE_VAR(RoadStop, xy, SLE_UINT32), - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_45), SLE_VAR(RoadStop, status, SLE_UINT8), - /* Index was saved in some versions, but this is not needed */ - SLE_CONDNULL(4, SL_MIN_VERSION, SLV_9), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_45), - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_26), - SLE_REF(RoadStop, next, REF_ROADSTOPS), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_45), - - SLE_CONDNULL(4, SL_MIN_VERSION, SLV_25), - SLE_CONDNULL(1, SLV_25, SLV_26), -}; - -static const SaveLoad _old_station_desc[] = { - SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Station, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDNULL(4, SL_MIN_VERSION, SLV_6), ///< bus/lorry tile - SLE_CONDVAR(Station, train_station.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Station, train_station.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Station, airport.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Station, airport.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), - SLE_CONDNULL(4, SLV_6, SLV_MULTITILE_DOCKS), - SLE_REF(Station, town, REF_TOWN), - SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16), - SLE_CONDVAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SL_MAX_VERSION), - - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_4), ///< alpha_order - - SLE_VAR(Station, string_id, SLE_STRINGID), - SLE_CONDSTR(Station, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), - SLE_CONDVAR(Station, indtype, SLE_UINT8, SLV_103, SL_MAX_VERSION), - SLE_CONDVAR(Station, had_vehicle_of_type, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SLV_122), - SLE_CONDVAR(Station, had_vehicle_of_type, SLE_UINT8, SLV_122, SL_MAX_VERSION), - - SLE_VAR(Station, time_since_load, SLE_UINT8), - SLE_VAR(Station, time_since_unload, SLE_UINT8), - SLE_CONDVAR_X(Station, delete_ctr, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 0, 3)), - SLE_CONDVAR_X(Station, delete_ctr, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), - SLE_VAR(Station, owner, SLE_UINT8), - SLE_VAR(Station, facilities, SLE_UINT8), - SLE_VAR(Station, airport.type, SLE_UINT8), - - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), ///< Truck/bus stop status - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_5), ///< Blocked months - - SLE_CONDVAR(Station, airport.flags, SLE_VAR_U64 | SLE_FILE_U16, SL_MIN_VERSION, SLV_3), - SLE_CONDVAR(Station, airport.flags, SLE_VAR_U64 | SLE_FILE_U32, SLV_3, SLV_46), - SLE_CONDVAR(Station, airport.flags, SLE_UINT64, SLV_46, SL_MAX_VERSION), - - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_26), ///< last-vehicle - SLEG_CONDVAR_X(_old_last_vehicle_type, SLE_UINT8, SLV_26, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 0, 0)), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), - - SLE_CONDNULL(2, SLV_3, SLV_26), ///< custom station class and id - SLE_CONDVAR(Station, build_date, SLE_FILE_U16 | SLE_VAR_I32, SLV_3, SLV_31), - SLE_CONDVAR(Station, build_date, SLE_INT32, SLV_31, SL_MAX_VERSION), - - SLE_CONDREF(Station, bus_stops, REF_ROADSTOPS, SLV_6, SL_MAX_VERSION), - SLE_CONDREF(Station, truck_stops, REF_ROADSTOPS, SLV_6, SL_MAX_VERSION), - - /* Used by newstations for graphic variations */ - SLE_CONDVAR(Station, random_bits, SLE_UINT16, SLV_27, SL_MAX_VERSION), - SLE_CONDVAR(Station, waiting_triggers, SLE_UINT8, SLV_27, SL_MAX_VERSION), - SLEG_CONDVAR(_num_specs, SLE_UINT8, SLV_27, SL_MAX_VERSION), - - SLE_CONDVEC(Station, loading_vehicles, REF_VEHICLE, SLV_57, SL_MAX_VERSION), - - /* reserve extra space in savegame here. (currently 32 bytes) */ - SLE_CONDNULL(32, SLV_2, SL_MAX_VERSION), }; static uint16 _waiting_acceptance; -static uint32 _num_flows; +static uint32 _old_num_flows; static uint16 _cargo_source; static uint32 _cargo_source_xy; static uint8 _cargo_days; static Money _cargo_feeder_share; -static const SaveLoad _station_speclist_desc[] = { - SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION), - SLE_CONDVAR_X(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 0, 1)), - SLE_CONDVAR_X(StationSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 2)), -}; - -static const SaveLoad _roadstop_speclist_desc[] = { - SLE_CONDVAR(RoadStopSpecList, grfid, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION), - SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 0, 2)), - SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 3)), -}; - CargoPacketList _packets; -uint32 _num_dests; +uint32 _old_num_dests; struct FlowSaveLoad { FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {} @@ -261,58 +49,10 @@ struct FlowSaveLoad { bool restricted; }; -#if 0 -static const SaveLoad _flow_desc[] = { - SLE_VAR(FlowSaveLoad, source, SLE_UINT16), - SLE_VAR(FlowSaveLoad, via, SLE_UINT16), - SLE_VAR(FlowSaveLoad, share, SLE_UINT32), - SLE_CONDVAR(FlowSaveLoad, restricted, SLE_BOOL, SLV_187, SL_MAX_VERSION), -}; -#endif +typedef std::pair StationCargoPair; -/** - * Wrapper function to get the GoodsEntry's internal structure while - * some of the variables itself are private. - * @return the saveload description for GoodsEntry. - */ -SaveLoadTable GetGoodsDesc() -{ - static const SaveLoad goods_desc[] = { - SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68), - SLE_CONDVAR(GoodsEntry, status, SLE_UINT8, SLV_68, SL_MAX_VERSION), - SLE_CONDNULL(2, SLV_51, SLV_68), - SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8), - SLE_CONDNULL_X(6, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), - SLE_VAR(GoodsEntry, rating, SLE_UINT8), - SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), - SLEG_CONDVAR( _cargo_source, SLE_UINT16, SLV_7, SLV_68), - SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), - SLEG_CONDVAR( _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68), - 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, SLV_14, SLV_65), - SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), - SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION), - SLEG_CONDPTRDEQ_X( _packets, REF_CARGO_PACKET, SLV_68, SLV_183, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, 0, 0)), - SLEG_CONDVAR_X( _num_dests, SLE_UINT32, SLV_183, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP)), - SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION), - SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION), - SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION), - SLEG_CONDVAR( _num_flows, SLE_UINT32, SLV_183, SL_MAX_VERSION), - SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), - SLE_CONDVAR_X(GoodsEntry, last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 1)), - }; - - return goods_desc; -} - -typedef std::pair StationCargoPair; - -static const SaveLoad _cargo_list_desc[] = { - SLE_VAR(StationCargoPair, first, SLE_UINT16), - SLE_PTRDEQ(StationCargoPair, second, REF_CARGO_PACKET), -}; +static OldPersistentStorage _old_st_persistent_storage; +static byte _old_last_vehicle_type; /** * Swap the temporary packets with the packets without specific destination in @@ -336,26 +76,216 @@ static void SwapPackets(GoodsEntry *ge) } } -static void Load_STNS() -{ - _cargo_source_xy = 0; - _cargo_days = 0; - _cargo_feeder_share = 0; - _num_specs = 0; +class SlStationSpecList : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION), + SLE_CONDVAR(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SLV_27, SLV_EXTEND_ENTITY_MAPPING), + SLE_CONDVAR(StationSpecList, localidx, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION), + }; + inline const static SaveLoadCompatTable compat_description = _station_spec_list_sl_compat; - uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; - int index; - while ((index = SlIterateArray()) != -1) { - Station *st = new (index) Station(); + void Save(BaseStation *bst) const override + { + SlSetStructListLength(bst->speclist.size()); + for (uint i = 0; i < bst->speclist.size(); i++) { + SlObject(&bst->speclist[i], this->GetDescription()); + } + } - SlObject(st, _old_station_desc); + void Load(BaseStation *bst) const override + { + uint8 num_specs = (uint8)SlGetStructListLength(UINT8_MAX); - _waiting_acceptance = 0; + bst->speclist.resize(num_specs); + for (uint i = 0; i < num_specs; i++) { + SlObject(&bst->speclist[i], this->GetLoadDescription()); + } + } +}; - for (CargoID i = 0; i < num_cargo; i++) { +class SlRoadStopSpecList : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION), + SLE_CONDVAR(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SLV_27, SLV_EXTEND_ENTITY_MAPPING), + SLE_CONDVAR(StationSpecList, localidx, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION), + }; + inline const static SaveLoadCompatTable compat_description = _station_road_stop_spec_list_sl_compat; + + void Save(BaseStation *bst) const override + { + SlSetStructListLength(bst->roadstop_speclist.size()); + for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { + SlObject(&bst->roadstop_speclist[i], this->GetDescription()); + } + } + + void Load(BaseStation *bst) const override + { + uint8 num_specs = (uint8)SlGetStructListLength(UINT8_MAX); + + bst->roadstop_speclist.resize(num_specs); + for (uint i = 0; i < num_specs; i++) { + SlObject(&bst->roadstop_speclist[i], this->GetLoadDescription()); + } + } +}; + +class SlStationCargo : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(StationCargoPair, first, SLE_UINT16), + SLE_REFDEQUE(StationCargoPair, second, REF_CARGO_PACKET), + }; + inline const static SaveLoadCompatTable compat_description = _station_cargo_sl_compat; + + void Save(GoodsEntry *ge) const override + { + // removed + NOT_REACHED(); + } + + void Load(GoodsEntry *ge) const override + { + size_t num_dests = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _old_num_dests : SlGetStructListLength(UINT32_MAX); + + StationCargoPair pair; + for (uint j = 0; j < num_dests; ++j) { + SlObject(&pair, this->GetLoadDescription()); + const_cast(*(ge->cargo.Packets()))[pair.first].swap(pair.second); + assert(pair.second.empty()); + } + } + + void FixPointers(GoodsEntry *ge) const override + { + for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) { + SlObject(const_cast(&(*it)), this->GetDescription()); + } + } +}; + +class SlStationFlow : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(FlowSaveLoad, source, SLE_UINT16), + SLE_VAR(FlowSaveLoad, via, SLE_UINT16), + SLE_VAR(FlowSaveLoad, share, SLE_UINT32), + SLE_CONDVAR(FlowSaveLoad, restricted, SLE_BOOL, SLV_187, SL_MAX_VERSION), + }; + inline const static SaveLoadCompatTable compat_description = _station_flow_sl_compat; + + void Save(GoodsEntry *ge) const override + { + // removed + NOT_REACHED(); + } + + void Load(GoodsEntry *ge) const override + { + size_t num_flows = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _old_num_flows : SlGetStructListLength(UINT32_MAX); + + FlowSaveLoad flow; + FlowStat *fs = nullptr; + StationID prev_source = INVALID_STATION; + for (uint32 j = 0; j < num_flows; ++j) { + SlObject(&flow, this->GetLoadDescription()); + if (fs == nullptr || prev_source != flow.source) { + fs = &(*(ge->flows.insert(ge->flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); + } else { + fs->AppendShare(flow.via, flow.share, flow.restricted); + } + prev_source = flow.source; + } + } +}; + +class SlStationGoods : public DefaultSaveLoadHandler { +public: +#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) + /* This table access private members of other classes; they have this + * class as friend. For MSVC CL 19.15 and 19.16 this doesn't work for + * "inline static const", so we are forced to wrap the table in a + * function. CL 19.16 is the latest for VS2017. */ + inline static const SaveLoad description[] = {{}}; + SaveLoadTable GetDescription() const override { +#else + inline +#endif + static const SaveLoad description[] = { + SLEG_CONDVAR("waiting_acceptance", _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68), + SLE_CONDVAR(GoodsEntry, status, SLE_UINT8, SLV_68, SL_MAX_VERSION), + SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8), + SLE_VAR(GoodsEntry, rating, SLE_UINT8), + SLEG_CONDVAR("cargo_source", _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), + SLEG_CONDVAR("cargo_source", _cargo_source, SLE_UINT16, SLV_7, SLV_68), + SLEG_CONDVAR("cargo_source_xy", _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), + SLEG_CONDVAR("cargo_days", _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68), + SLE_VAR(GoodsEntry, last_speed, SLE_UINT8), + SLE_VAR(GoodsEntry, last_age, SLE_UINT8), + SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, SLV_14, SLV_65), + SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), + SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION), + SLEG_CONDREFDEQUE("packets", _packets, REF_CARGO_PACKET, SLV_68, SLV_183), + SLEG_CONDVAR("old_num_dests", _old_num_dests, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), + SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION), + SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION), + SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION), + SLEG_CONDVAR("old_num_flows", _old_num_flows, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), + SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("flow", SlStationFlow, SLV_183, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("cargo", SlStationCargo, SLV_183, SL_MAX_VERSION), + }; +#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) + return description; + } +#endif + inline const static SaveLoadCompatTable compat_description = _station_goods_sl_compat; + + /** + * Get the number of cargoes used by this savegame version. + * @return The number of cargoes used by this savegame version. + */ + size_t GetNumCargo() const + { + if (IsSavegameVersionBefore(SLV_55)) return 12; + if (IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES)) return 32; + if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return NUM_CARGO; + /* Read from the savegame how long the list is. */ + return SlGetStructListLength(NUM_CARGO); + } + + void Save(BaseStation *bst) const override + { + Station *st = Station::From(bst); + + SlSetStructListLength(NUM_CARGO); + + for (CargoID i = 0; i < NUM_CARGO; i++) { + SlObject(&st->goods[i], this->GetDescription()); + } + } + + void Load(BaseStation *bst) const override + { + Station *st = Station::From(bst); + + /* Before savegame version 161, persistent storages were not stored in a pool. */ + if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_145) && st->facilities & FACIL_AIRPORT) { + /* Store the old persistent storage. The GRFID will be added later. */ + assert(PersistentStorage::CanAllocateItem()); + st->airport.psa = new PersistentStorage(0, 0, 0); + memcpy(st->airport.psa->storage, _old_st_persistent_storage.storage, sizeof(_old_st_persistent_storage.storage)); + } + + size_t num_cargo = this->GetNumCargo(); + for (size_t i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; - SlObject(ge, GetGoodsDesc()); - SwapPackets(ge); + SlObject(ge, this->GetLoadDescription()); + if (IsSavegameVersionBefore(SLV_183)) { + SwapPackets(ge); + } if (IsSavegameVersionBefore(SLV_68)) { SB(ge->status, GoodsEntry::GES_ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); if (GB(_waiting_acceptance, 0, 12) != 0) { @@ -374,463 +304,283 @@ static void Load_STNS() SB(ge->status, GoodsEntry::GES_RATING, 1, 1); } } - if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) ge->last_vehicle_type = _old_last_vehicle_type; } + } - if (_num_specs != 0) { - /* Allocate speclist memory when loading a game */ - st->speclist.resize(_num_specs); - for (uint i = 0; i < st->speclist.size(); i++) { - SlObject(&st->speclist[i], _station_speclist_desc); + void FixPointers(BaseStation *bst) const override + { + Station *st = Station::From(bst); + + uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + for (CargoID i = 0; i < num_cargo; i++) { + GoodsEntry *ge = &st->goods[i]; + if (IsSavegameVersionBefore(SLV_183)) { + SwapPackets(ge); // We have to swap back again to be in the format pre-183 expects. + SlObject(ge, this->GetDescription()); + SwapPackets(ge); + } else { + SlObject(ge, this->GetDescription()); } } } -} +}; -static void Ptrs_STNS() -{ - /* Don't run when savegame version is higher than or equal to 123. */ - if (!IsSavegameVersionBefore(SLV_123)) return; +class SlRoadStopTileData : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(RoadStopTileData, tile, SLE_UINT32), + SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8), + SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8), + }; + inline const static SaveLoadCompatTable compat_description = {}; - uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; - for (Station *st : Station::Iterate()) { - if (!IsSavegameVersionBefore(SLV_68)) { - for (CargoID i = 0; i < num_cargo; i++) { - GoodsEntry *ge = &st->goods[i]; - SwapPackets(ge); - SlObject(ge, GetGoodsDesc()); - SwapPackets(ge); - } + static uint8 last_num_specs; ///< Number of specs of the last loaded station. + + void Save(BaseStation *bst) const override + { + SlSetStructListLength(bst->custom_roadstop_tile_data.size()); + for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { + SlObject(&bst->custom_roadstop_tile_data[i], this->GetDescription()); } - SlObject(st, _old_station_desc); } -} - -static const SaveLoad _base_station_desc[] = { - SLE_VAR(BaseStation, xy, SLE_UINT32), - SLE_REF(BaseStation, town, REF_TOWN), - SLE_VAR(BaseStation, string_id, SLE_STRINGID), - SLE_STR(BaseStation, name, SLE_STR | SLF_ALLOW_CONTROL, 0), - - SLE_CONDVAR_X(Station, delete_ctr, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 0, 3)), - SLE_CONDVAR_X(Station, delete_ctr, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), - SLE_VAR(BaseStation, owner, SLE_UINT8), - SLE_VAR(BaseStation, facilities, SLE_UINT8), - SLE_VAR(BaseStation, build_date, SLE_INT32), - - /* Used by newstations for graphic variations */ - SLE_VAR(BaseStation, random_bits, SLE_UINT16), - SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8), - SLEG_VAR(_num_specs, SLE_UINT8), - SLEG_CONDVAR_X(_num_roadstop_specs, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS)), - SLEG_CONDVARVEC_X(_custom_road_stop_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1)), - SLEG_CONDVARVEC_X(_custom_road_stop_data, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1)), - SLEG_CONDVAR_X(_num_roadstop_custom_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 2)), -}; - -static OldPersistentStorage _old_st_persistent_storage; - -static const SaveLoad _station_desc[] = { - SLE_WRITEBYTE(Station, facilities), - SLE_ST_INCLUDE(), - - SLE_VAR(Station, train_station.tile, SLE_UINT32), - SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16), - SLE_VAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16), - - SLE_REF(Station, bus_stops, REF_ROADSTOPS), - SLE_REF(Station, truck_stops, REF_ROADSTOPS), - SLE_CONDVAR_X(Station, ship_station.tile, SLE_UINT32, SL_MIN_VERSION, SLV_MULTITILE_DOCKS, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 0, 0)), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 1, 1)), - SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVARVEC_X(Station, docking_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 2)), - SLE_VAR(Station, airport.tile, SLE_UINT32), - SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), - SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), - SLE_VAR(Station, airport.type, SLE_UINT8), - SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6)), - SLE_VAR(Station, airport.flags, SLE_UINT64), - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6)), - SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION), - SLEG_CONDARR(_old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161), - SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), - - SLE_VAR(Station, indtype, SLE_UINT8), - SLE_CONDVAR_X(Station, extra_name_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_STATION_NAMES)), - - SLE_VAR(Station, time_since_load, SLE_UINT8), - SLE_VAR(Station, time_since_unload, SLE_UINT8), - SLEG_CONDVAR_X(_old_last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 0, 0)), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), - SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8), - SLE_VEC(Station, loading_vehicles, REF_VEHICLE), - SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES), - SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), - SLE_CONDNULL_X(32 * 24, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_22)), - SLE_CONDVAR_X(Station, station_cargo_history_cargoes, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATION_CARGO_HISTORY)), -}; - -static const SaveLoad _waypoint_desc[] = { - SLE_WRITEBYTE(Waypoint, facilities), - SLE_ST_INCLUDE(), - - SLE_VAR(Waypoint, town_cn, SLE_UINT16), - - SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), - SLE_CONDVAR_X(Waypoint, waypoint_flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_WAYPOINT_FLAGS)), - SLE_CONDVAR_X(Waypoint, road_waypoint_area.tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), - SLE_CONDVAR_X(Waypoint, road_waypoint_area.w, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), - SLE_CONDVAR_X(Waypoint, road_waypoint_area.h, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), -}; - -static const SaveLoad _custom_roadstop_tile_data_desc[] = { - SLE_VAR(RoadStopTileData, tile, SLE_UINT32), - SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8), - SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8), + void Load(BaseStation *bst) const override + { + uint32 num_tiles = (uint32)SlGetStructListLength(UINT32_MAX); + bst->custom_roadstop_tile_data.resize(num_tiles); + for (uint i = 0; i < num_tiles; i++) { + SlObject(&bst->custom_roadstop_tile_data[i], this->GetLoadDescription()); + } + } }; /** - * Get the base station description to be used for SL_ST_INCLUDE - * @return the base station description. + * SaveLoad handler for the BaseStation, which all other stations / waypoints + * make use of. */ -SaveLoadTable GetBaseStationDescription() -{ - return _base_station_desc; -} +class SlStationBase : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(BaseStation, xy, SLE_UINT32), + SLE_REF(BaseStation, town, REF_TOWN), + SLE_VAR(BaseStation, string_id, SLE_STRINGID), + SLE_STR(BaseStation, name, SLE_STR | SLF_ALLOW_CONTROL, 0), + SLE_VAR(BaseStation, delete_ctr, SLE_UINT8), + SLE_VAR(BaseStation, owner, SLE_UINT8), + SLE_VAR(BaseStation, facilities, SLE_UINT8), + SLE_VAR(BaseStation, build_date, SLE_INT32), -std::vector _filtered_station_desc; -std::vector _filtered_waypoint_desc; -std::vector _filtered_goods_desc; -std::vector _filtered_station_speclist_desc; -std::vector _filtered_roadstop_speclist_desc; + /* Used by newstations for graphic variations */ + SLE_VAR(BaseStation, random_bits, SLE_UINT16), + SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8), + }; + inline const static SaveLoadCompatTable compat_description = _station_base_sl_compat; -static void SetupDescs_STNN() -{ - _filtered_station_desc = SlFilterObject(_station_desc); - _filtered_waypoint_desc = SlFilterObject(_waypoint_desc); - _filtered_goods_desc = SlFilterObject(GetGoodsDesc()); - _filtered_station_speclist_desc = SlFilterObject(_station_speclist_desc); - _filtered_roadstop_speclist_desc = SlFilterObject(_roadstop_speclist_desc); -} + void Save(BaseStation *bst) const override + { + SlObject(bst, this->GetDescription()); + } -std::vector _filtered_roadstop_desc; + void Load(BaseStation *bst) const override + { + SlObject(bst, this->GetLoadDescription()); + } -static void SetupDescs_ROADSTOP() -{ - _filtered_roadstop_desc = SlFilterObject(_roadstop_desc); -} + void FixPointers(BaseStation *bst) const override + { + SlObject(bst, this->GetDescription()); + } +}; +/** + * SaveLoad handler for a normal station (read: not a waypoint). + */ +class SlStationNormal : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLEG_STRUCT("base", SlStationBase), + SLE_VAR(Station, train_station.tile, SLE_UINT32), + SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16), + SLE_VAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16), -static void RealSave_STNN(BaseStation *bst) -{ - _num_specs = (uint8)bst->speclist.size(); - _num_roadstop_specs = (uint8)bst->roadstop_speclist.size(); - _num_roadstop_custom_tiles = (uint32)bst->custom_roadstop_tile_data.size(); + SLE_REF(Station, bus_stops, REF_ROADSTOPS), + SLE_REF(Station, truck_stops, REF_ROADSTOPS), + SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_VAR(Station, airport.tile, SLE_UINT32), + SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), + SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), + SLE_VAR(Station, airport.type, SLE_UINT8), + SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION), + SLE_VAR(Station, airport.flags, SLE_UINT64), + SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION), + SLEG_CONDARR("storage", _old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161), + SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), - bool waypoint = (bst->facilities & FACIL_WAYPOINT) != 0; - SlObjectSaveFiltered(bst, waypoint ? SaveLoadTable(_filtered_waypoint_desc) : SaveLoadTable(_filtered_station_desc)); + SLE_VAR(Station, indtype, SLE_UINT8), - MemoryDumper *dumper = MemoryDumper::GetCurrent(); + SLE_VAR(Station, time_since_load, SLE_UINT8), + SLE_VAR(Station, time_since_unload, SLE_UINT8), + SLEG_VAR("last_vehicle_type", _old_last_vehicle_type, SLE_UINT8), + SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8), + SLE_REFVEC(Station, loading_vehicles, REF_VEHICLE), + SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES), + SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("speclist", SlRoadStopTileData, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION), + SLEG_STRUCTLIST("goods", SlStationGoods), + }; + inline const static SaveLoadCompatTable compat_description = _station_normal_sl_compat; + + void Save(BaseStation *bst) const + { + if ((bst->facilities & FACIL_WAYPOINT) != 0) return; + SlObject(bst, this->GetDescription()); + } + + void Load(BaseStation *bst) const + { + if ((bst->facilities & FACIL_WAYPOINT) != 0) return; + SlObject(bst, this->GetLoadDescription()); - if (!waypoint) { - Station *st = Station::From(bst); for (CargoID i = 0; i < NUM_CARGO; i++) { - _num_dests = (uint32)st->goods[i].cargo.Packets()->MapSize(); - _num_flows = (uint32)st->goods[i].flows.size(); - SlObjectSaveFiltered(&st->goods[i], _filtered_goods_desc); - for (FlowStatMap::const_iterator outer_it(st->goods[i].flows.begin()); outer_it != st->goods[i].flows.end(); ++outer_it) { - uint32 sum_shares = 0; - FlowSaveLoad flow; - flow.source = outer_it->GetOrigin(); - dumper->CheckBytes(2 + 4); - dumper->RawWriteUint16(flow.source); - dumper->RawWriteUint32((uint32)outer_it->size()); - FlowStat::const_iterator inner_it(outer_it->begin()); - const FlowStat::const_iterator end(outer_it->end()); - for (; inner_it != end; ++inner_it) { - flow.via = inner_it->second; - flow.share = inner_it->first - sum_shares; - flow.restricted = inner_it->first > outer_it->GetUnrestricted(); - sum_shares = inner_it->first; - assert(flow.share > 0); - - // SlObject(&flow, _flow_desc); /* this is highly performance-sensitive, manually unroll */ - dumper->CheckBytes(2 + 4 + 1); - dumper->RawWriteUint16(flow.via); - dumper->RawWriteUint32(flow.share); - dumper->RawWriteByte(flow.restricted != 0); - } - SlWriteUint16(outer_it->GetRawFlags()); - } - for (StationCargoPacketMap::ConstMapIterator it(st->goods[i].cargo.Packets()->begin()); it != st->goods[i].cargo.Packets()->end(); ++it) { - SlObjectSaveFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals - } - } - - assert(st->station_cargo_history.size() == CountBits(st->station_cargo_history_cargoes)); - dumper->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2); - for (const auto &history : st->station_cargo_history) { - uint i = st->station_cargo_history_offset; - do { - dumper->RawWriteUint16(history[i]); - i++; - if (i == MAX_STATION_CARGO_HISTORY_DAYS) i = 0; - } while (i != st->station_cargo_history_offset); + Station::From(bst)->goods[i].last_vehicle_type = _old_last_vehicle_type; } } - for (uint i = 0; i < bst->speclist.size(); i++) { - SlObjectSaveFiltered(&bst->speclist[i], _filtered_station_speclist_desc); + void FixPointers(BaseStation *bst) const + { + if ((bst->facilities & FACIL_WAYPOINT) != 0) return; + SlObject(bst, this->GetDescription()); + } +}; + +class SlStationWaypoint : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLEG_STRUCT("base", SlStationBase), + SLE_VAR(Waypoint, town_cn, SLE_UINT16), + + SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), + }; + inline const static SaveLoadCompatTable compat_description = _station_waypoint_sl_compat; + + void Save(BaseStation *bst) const + { + if ((bst->facilities & FACIL_WAYPOINT) == 0) return; + SlObject(bst, this->GetDescription()); } - for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { - SlObjectSaveFiltered(&bst->roadstop_speclist[i], _filtered_roadstop_speclist_desc); + void Load(BaseStation *bst) const + { + if ((bst->facilities & FACIL_WAYPOINT) == 0) return; + SlObject(bst, this->GetLoadDescription()); } - for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { - SlObjectSaveFiltered(&bst->custom_roadstop_tile_data[i], _custom_roadstop_tile_data_desc); // _custom_roadstop_tile_data_desc has no conditionals + void FixPointers(BaseStation *bst) const + { + if ((bst->facilities & FACIL_WAYPOINT) == 0) return; + SlObject(bst, this->GetDescription()); } -} +}; -static void Save_STNN() -{ - SetupDescs_STNN(); +static const SaveLoad _station_desc[] = { + SLE_SAVEBYTE(BaseStation, facilities), + SLEG_STRUCT("normal", SlStationNormal), + SLEG_STRUCT("waypoint", SlStationWaypoint), + SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList, SLV_27, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("roadstopspeclist", SlRoadStopSpecList, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION), +}; - /* Write the stations */ - for (BaseStation *st : BaseStation::Iterate()) { - SlSetArrayIndex(st->index); - SlAutolength((AutolengthProc*)RealSave_STNN, st); - } -} +struct STNNChunkHandler : ChunkHandler { + STNNChunkHandler() : ChunkHandler('STNN', CH_TABLE) {} -static void Load_STNN() -{ - SetupDescs_STNN(); + void Save() const override + { + SlTableHeader(_station_desc); - _num_flows = 0; - _num_specs = 0; - _num_roadstop_specs = 0; - _num_roadstop_custom_tiles = 0; - - const uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; - ReadBuffer *buffer = ReadBuffer::GetCurrent(); - - int index; - while ((index = SlIterateArray()) != -1) { - bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0; - - BaseStation *bst = waypoint ? (BaseStation *)new (index) Waypoint() : new (index) Station(); - SlObjectLoadFiltered(bst, waypoint ? SaveLoadTable(_filtered_waypoint_desc) : SaveLoadTable(_filtered_station_desc)); - - if (!waypoint) { - Station *st = Station::From(bst); - - /* Before savegame version 161, persistent storages were not stored in a pool. */ - if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_145) && st->facilities & FACIL_AIRPORT) { - /* Store the old persistent storage. The GRFID will be added later. */ - assert(PersistentStorage::CanAllocateItem()); - st->airport.psa = new PersistentStorage(0, 0, 0); - memcpy(st->airport.psa->storage, _old_st_persistent_storage.storage, sizeof(_old_st_persistent_storage.storage)); - } - - for (CargoID i = 0; i < num_cargo; i++) { - SlObjectLoadFiltered(&st->goods[i], _filtered_goods_desc); - StationID prev_source = INVALID_STATION; - if (SlXvIsFeaturePresent(XSLFI_FLOW_STAT_FLAGS)) { - for (uint32 j = 0; j < _num_flows; ++j) { - FlowSaveLoad flow; - buffer->CheckBytes(2 + 4); - flow.source = buffer->RawReadUint16(); - uint32 flow_count = buffer->RawReadUint32(); - - buffer->CheckBytes(2 + 4 + 1); - flow.via = buffer->RawReadUint16(); - flow.share = buffer->RawReadUint32(); - flow.restricted = (buffer->RawReadByte() != 0); - FlowStat *fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); - for (uint32 k = 1; k < flow_count; ++k) { - buffer->CheckBytes(2 + 4 + 1); - flow.via = buffer->RawReadUint16(); - flow.share = buffer->RawReadUint32(); - flow.restricted = (buffer->RawReadByte() != 0); - fs->AppendShare(flow.via, flow.share, flow.restricted); - } - fs->SetRawFlags(SlReadUint16()); - } - } else if (SlXvIsFeatureMissing(XSLFI_CHILLPP)) { - FlowSaveLoad flow; - FlowStat *fs = nullptr; - for (uint32 j = 0; j < _num_flows; ++j) { - // SlObject(&flow, _flow_desc); /* this is highly performance-sensitive, manually unroll */ - buffer->CheckBytes(2 + 2 + 4); - flow.source = buffer->RawReadUint16(); - flow.via = buffer->RawReadUint16(); - flow.share = buffer->RawReadUint32(); - if (!IsSavegameVersionBefore(SLV_187)) flow.restricted = (buffer->ReadByte() != 0); - - if (fs == nullptr || prev_source != flow.source) { - fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); - } else { - fs->AppendShare(flow.via, flow.share, flow.restricted); - } - prev_source = flow.source; - } - } - if (IsSavegameVersionBefore(SLV_183) && SlXvIsFeatureMissing(XSLFI_CHILLPP)) { - SwapPackets(&st->goods[i]); - } else { - if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { - SlSkipBytes(8); - uint num_links = SlReadUint16(); - uint num_flows = SlReadUint32(); - SlSkipBytes(6); - SlSkipBytes(18 * num_links); - SlSkipBytes(16 * num_flows); - } - - StationCargoPair pair; - for (uint j = 0; j < _num_dests; ++j) { - SlObjectLoadFiltered(&pair, _cargo_list_desc); // _cargo_list_desc has no conditionals - const_cast(*(st->goods[i].cargo.Packets()))[pair.first].swap(pair.second); - assert(pair.second.empty()); - } - } - if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) st->goods[i].last_vehicle_type = _old_last_vehicle_type; - } - - st->station_cargo_history.resize(CountBits(st->station_cargo_history_cargoes)); - buffer->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2); - for (auto &history : st->station_cargo_history) { - for (uint16 &amount : history) { - amount = buffer->RawReadUint16(); - } - } - if (SlXvIsFeaturePresent(XSLFI_STATION_CARGO_HISTORY, 1, 1)) { - for (auto &history : st->station_cargo_history) { - for (uint16 &amount : history) { - amount = RXCompressUint(amount); - } - } - } - st->station_cargo_history_offset = 0; - } - - if (_num_specs != 0) { - /* Allocate speclist memory when loading a game */ - bst->speclist.resize(_num_specs); - for (uint i = 0; i < bst->speclist.size(); i++) { - SlObjectLoadFiltered(&bst->speclist[i], _filtered_station_speclist_desc); - } - } - - if (_num_roadstop_specs != 0) { - /* Allocate speclist memory when loading a game */ - bst->roadstop_speclist.resize(_num_roadstop_specs); - for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { - SlObjectLoadFiltered(&bst->roadstop_speclist[i], _filtered_roadstop_speclist_desc); - } - } - - if (_num_roadstop_custom_tiles != 0) { - /* Allocate custom road stop tile data memory when loading a game */ - bst->custom_roadstop_tile_data.resize(_num_roadstop_custom_tiles); - for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { - SlObjectLoadFiltered(&bst->custom_roadstop_tile_data[i], _custom_roadstop_tile_data_desc); // _custom_roadstop_tile_data_desc has no conditionals - } - } - - if (SlXvIsFeaturePresent(XSLFI_GRF_ROADSTOPS, 1, 1)) { - for (size_t i = 0; i < _custom_road_stop_tiles.size(); i++) { - bst->custom_roadstop_tile_data.push_back({ _custom_road_stop_tiles[i], (uint8)GB(_custom_road_stop_data[i], 0, 8), (uint8)GB(_custom_road_stop_data[i], 8, 8) }); - } - _custom_road_stop_tiles.clear(); - _custom_road_stop_data.clear(); + /* Write the stations */ + for (BaseStation *st : BaseStation::Iterate()) { + SlSetArrayIndex(st->index); + SlObject(st, _station_desc); } } -} -static void Ptrs_STNN() -{ - /* Don't run when savegame version lower than 123. */ - if (IsSavegameVersionBefore(SLV_123)) return; - SetupDescs_STNN(); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_station_desc, _station_sl_compat); - if (!IsSavegameVersionBefore(SLV_183)) { - assert(_filtered_goods_desc.size() == 0); - } + _old_num_flows = 0; - uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; - for (Station *st : Station::Iterate()) { - for (CargoID i = 0; i < num_cargo; i++) { - GoodsEntry *ge = &st->goods[i]; - if (IsSavegameVersionBefore(SLV_183) && SlXvIsFeatureMissing(XSLFI_CHILLPP)) { - SwapPackets(ge); - SlObjectPtrOrNullFiltered(ge, _filtered_goods_desc); - SwapPackets(ge); - } else { - //SlObject(ge, GetGoodsDesc()); - for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) { - SlObjectPtrOrNullFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals - } - } + int index; + while ((index = SlIterateArray()) != -1) { + bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0; + + BaseStation *bst = waypoint ? (BaseStation *)new (index) Waypoint() : new (index) Station(); + SlObject(bst, slt); } - SlObjectPtrOrNullFiltered(st, _filtered_station_desc); } - for (Waypoint *wp : Waypoint::Iterate()) { - SlObjectPtrOrNullFiltered(wp, _filtered_waypoint_desc); + void FixPointers() const override + { + /* From SLV_123 we store stations in STNN; before that in STNS. So do not + * fix pointers when the version is below SLV_123, as that would fix + * pointers twice: once in STNS chunk and once here. */ + if (IsSavegameVersionBefore(SLV_123)) return; + + for (BaseStation *bst : BaseStation::Iterate()) { + SlObject(bst, _station_desc); + } } -} +}; -static void Save_ROADSTOP() -{ - SetupDescs_ROADSTOP(); - for (RoadStop *rs : RoadStop::Iterate()) { - SlSetArrayIndex(rs->index); - SlObjectSaveFiltered(rs, _filtered_roadstop_desc); +struct ROADChunkHandler : ChunkHandler { + ROADChunkHandler() : ChunkHandler('ROAD', CH_TABLE) {} + + void Save() const override + { + SlTableHeader(_roadstop_desc); + + for (RoadStop *rs : RoadStop::Iterate()) { + SlSetArrayIndex(rs->index); + SlObject(rs, _roadstop_desc); + } } -} -static void Load_ROADSTOP() -{ - SetupDescs_ROADSTOP(); - int index; - while ((index = SlIterateArray()) != -1) { - RoadStop *rs = new (index) RoadStop(INVALID_TILE); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_roadstop_desc, _roadstop_sl_compat); - SlObjectLoadFiltered(rs, _filtered_roadstop_desc); + int index; + + while ((index = SlIterateArray()) != -1) { + RoadStop *rs = new (index) RoadStop(INVALID_TILE); + + SlObject(rs, slt); + } } -} -static void Ptrs_ROADSTOP() -{ - SetupDescs_ROADSTOP(); - for (RoadStop *rs : RoadStop::Iterate()) { - SlObjectPtrOrNullFiltered(rs, _filtered_roadstop_desc); + void FixPointers() const override + { + for (RoadStop *rs : RoadStop::Iterate()) { + SlObject(rs, _roadstop_desc); + } } -} +}; -static void Load_DOCK() -{ - extern void SlSkipArray(); - SlSkipArray(); -} - -static const ChunkHandler station_chunk_handlers[] = { - { 'STNS', nullptr, Load_STNS, Ptrs_STNS, nullptr, CH_ARRAY }, - { 'STNN', Save_STNN, Load_STNN, Ptrs_STNN, nullptr, CH_ARRAY }, - { 'ROAD', Save_ROADSTOP, Load_ROADSTOP, Ptrs_ROADSTOP, nullptr, CH_ARRAY }, - { 'DOCK', nullptr, Load_DOCK, nullptr, nullptr, CH_ARRAY }, +static const STNNChunkHandler STNN; +static const ROADChunkHandler ROAD; +static const ChunkHandlerRef station_chunk_handlers[] = { + STNN, + ROAD, }; extern const ChunkHandlerTable _station_chunk_handlers(station_chunk_handlers); + +} diff --git a/src/saveload/storage_sl.cpp b/src/saveload/storage_sl.cpp index 50f858f116..1b1c753e94 100644 --- a/src/saveload/storage_sl.cpp +++ b/src/saveload/storage_sl.cpp @@ -8,11 +8,16 @@ /** @file storage_sl.cpp Code handling saving and loading of persistent storages. */ #include "../stdafx.h" -#include "../newgrf_storage.h" + #include "saveload.h" +#include "compat/storage_sl_compat.h" + +#include "../newgrf_storage.h" #include "../safeguards.h" +namespace upstream_sl { + /** Description of the data to save and load in #PersistentStorage. */ static const SaveLoad _storage_desc[] = { SLE_CONDVAR(PersistentStorage, grfid, SLE_UINT32, SLV_6, SL_MAX_VERSION), @@ -20,32 +25,41 @@ static const SaveLoad _storage_desc[] = { SLE_CONDARR(PersistentStorage, storage, SLE_UINT32, 256, SLV_EXTEND_PERSISTENT_STORAGE, SL_MAX_VERSION), }; -/** Load persistent storage data. */ -static void Load_PSAC() -{ - int index; +/** Persistent storage data. */ +struct PSACChunkHandler : ChunkHandler { + PSACChunkHandler() : ChunkHandler('PSAC', CH_TABLE) {} - while ((index = SlIterateArray()) != -1) { - assert(PersistentStorage::CanAllocateItem()); - PersistentStorage *ps = new (index) PersistentStorage(0, 0, 0); - SlObject(ps, _storage_desc); + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_storage_desc, _storage_sl_compat); + + int index; + + while ((index = SlIterateArray()) != -1) { + assert(PersistentStorage::CanAllocateItem()); + PersistentStorage *ps = new (index) PersistentStorage(0, 0, 0); + SlObject(ps, slt); + } } -} -/** Save persistent storage data. */ -static void Save_PSAC() -{ - /* Write the industries */ - for (PersistentStorage *ps : PersistentStorage::Iterate()) { - ps->ClearChanges(); - SlSetArrayIndex(ps->index); - SlObject(ps, _storage_desc); + void Save() const override + { + SlTableHeader(_storage_desc); + + /* Write the industries */ + for (PersistentStorage *ps : PersistentStorage::Iterate()) { + ps->ClearChanges(); + SlSetArrayIndex(ps->index); + SlObject(ps, _storage_desc); + } } -} +}; -/** Chunk handler for persistent storages. */ -static const ChunkHandler persistent_storage_chunk_handlers[] = { - { 'PSAC', Save_PSAC, Load_PSAC, nullptr, nullptr, CH_ARRAY }, +static const PSACChunkHandler PSAC; +static const ChunkHandlerRef persistent_storage_chunk_handlers[] = { + PSAC, }; extern const ChunkHandlerTable _persistent_storage_chunk_handlers(persistent_storage_chunk_handlers); + +} diff --git a/src/saveload/story_sl.cpp b/src/saveload/story_sl.cpp index fa3c70b2b1..bde9941058 100644 --- a/src/saveload/story_sl.cpp +++ b/src/saveload/story_sl.cpp @@ -8,23 +8,15 @@ /** @file story_sl.cpp Code handling saving and loading of story pages */ #include "../stdafx.h" -#include "../story_base.h" #include "saveload.h" +#include "compat/story_sl_compat.h" + +#include "../story_base.h" #include "../safeguards.h" -/** Called after load to trash broken pages. */ -void AfterLoadStoryBook() -{ - if (IsSavegameVersionBefore(SLV_185)) { - /* Trash all story pages and page elements because - * they were saved with wrong data types. - */ - _story_page_element_pool.CleanPool(); - _story_page_pool.CleanPool(); - } -} +namespace upstream_sl { static const SaveLoad _story_page_elements_desc[] = { SLE_CONDVAR(StoryPageElement, sort_value, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_185), @@ -36,30 +28,38 @@ static const SaveLoad _story_page_elements_desc[] = { SLE_SSTR(StoryPageElement, text, SLE_STR | SLF_ALLOW_CONTROL), }; -static void Save_STORY_PAGE_ELEMENT() -{ - for (StoryPageElement *s : StoryPageElement::Iterate()) { - SlSetArrayIndex(s->index); - SlObject(s, _story_page_elements_desc); - } -} +struct STPEChunkHandler : ChunkHandler { + STPEChunkHandler() : ChunkHandler('STPE', CH_TABLE) {} -static void Load_STORY_PAGE_ELEMENT() -{ - int index; - uint32 max_sort_value = 0; - while ((index = SlIterateArray()) != -1) { - StoryPageElement *s = new (index) StoryPageElement(); - SlObject(s, _story_page_elements_desc); - if (s->sort_value > max_sort_value) { - max_sort_value = s->sort_value; + void Save() const override + { + SlTableHeader(_story_page_elements_desc); + + for (StoryPageElement *s : StoryPageElement::Iterate()) { + SlSetArrayIndex(s->index); + SlObject(s, _story_page_elements_desc); } } - /* Update the next sort value, so that the next - * created page is shown after all existing pages. - */ - _story_page_element_next_sort_value = max_sort_value + 1; -} + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_story_page_elements_desc, _story_page_elements_sl_compat); + + int index; + uint32 max_sort_value = 0; + while ((index = SlIterateArray()) != -1) { + StoryPageElement *s = new (index) StoryPageElement(); + SlObject(s, slt); + if (s->sort_value > max_sort_value) { + max_sort_value = s->sort_value; + } + } + /* Update the next sort value, so that the next + * created page is shown after all existing pages. + */ + _story_page_element_next_sort_value = max_sort_value + 1; + } +}; static const SaveLoad _story_pages_desc[] = { SLE_CONDVAR(StoryPage, sort_value, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_185), @@ -70,34 +70,46 @@ static const SaveLoad _story_pages_desc[] = { SLE_SSTR(StoryPage, title, SLE_STR | SLF_ALLOW_CONTROL), }; -static void Save_STORY_PAGE() -{ - for (StoryPage *s : StoryPage::Iterate()) { - SlSetArrayIndex(s->index); - SlObject(s, _story_pages_desc); - } -} +struct STPAChunkHandler : ChunkHandler { + STPAChunkHandler() : ChunkHandler('STPA', CH_TABLE) {} -static void Load_STORY_PAGE() -{ - int index; - uint32 max_sort_value = 0; - while ((index = SlIterateArray()) != -1) { - StoryPage *s = new (index) StoryPage(); - SlObject(s, _story_pages_desc); - if (s->sort_value > max_sort_value) { - max_sort_value = s->sort_value; + void Save() const override + { + SlTableHeader(_story_pages_desc); + + for (StoryPage *s : StoryPage::Iterate()) { + SlSetArrayIndex(s->index); + SlObject(s, _story_pages_desc); } } - /* Update the next sort value, so that the next - * created page is shown after all existing pages. - */ - _story_page_next_sort_value = max_sort_value + 1; -} -static const ChunkHandler story_page_chunk_handlers[] = { - { 'STPE', Save_STORY_PAGE_ELEMENT, Load_STORY_PAGE_ELEMENT, nullptr, nullptr, CH_ARRAY }, - { 'STPA', Save_STORY_PAGE, Load_STORY_PAGE, nullptr, nullptr, CH_ARRAY }, + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_story_pages_desc, _story_pages_sl_compat); + + int index; + uint32 max_sort_value = 0; + while ((index = SlIterateArray()) != -1) { + StoryPage *s = new (index) StoryPage(); + SlObject(s, slt); + if (s->sort_value > max_sort_value) { + max_sort_value = s->sort_value; + } + } + /* Update the next sort value, so that the next + * created page is shown after all existing pages. + */ + _story_page_next_sort_value = max_sort_value + 1; + } +}; + +static const STPEChunkHandler STPE; +static const STPAChunkHandler STPA; +static const ChunkHandlerRef story_page_chunk_handlers[] = { + STPE, + STPA, }; extern const ChunkHandlerTable _story_page_chunk_handlers(story_page_chunk_handlers); + +} diff --git a/src/saveload/subsidy_sl.cpp b/src/saveload/subsidy_sl.cpp index 9ed65a04ec..46f236c0d4 100644 --- a/src/saveload/subsidy_sl.cpp +++ b/src/saveload/subsidy_sl.cpp @@ -8,12 +8,16 @@ /** @file subsidy_sl.cpp Code handling saving and loading of subsidies */ #include "../stdafx.h" -#include "../subsidy_base.h" #include "saveload.h" +#include "compat/subsidy_sl_compat.h" + +#include "../subsidy_base.h" #include "../safeguards.h" +namespace upstream_sl { + static const SaveLoad _subsidies_desc[] = { SLE_VAR(Subsidy, cargo_type, SLE_UINT8), SLE_CONDVAR(Subsidy, remaining, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_CUSTOM_SUBSIDY_DURATION), @@ -27,25 +31,36 @@ static const SaveLoad _subsidies_desc[] = { SLE_CONDVAR(Subsidy, dst, SLE_UINT16, SLV_5, SL_MAX_VERSION), }; -static void Save_SUBS() -{ - for (Subsidy *s : Subsidy::Iterate()) { - SlSetArrayIndex(s->index); - SlObject(s, _subsidies_desc); - } -} +struct SUBSChunkHandler : ChunkHandler { + SUBSChunkHandler() : ChunkHandler('SUBS', CH_TABLE) {} -static void Load_SUBS() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Subsidy *s = new (index) Subsidy(); - SlObject(s, _subsidies_desc); - } -} + void Save() const override + { + SlTableHeader(_subsidies_desc); -static const ChunkHandler subsidy_chunk_handlers[] = { - { 'SUBS', Save_SUBS, Load_SUBS, nullptr, nullptr, CH_ARRAY }, + for (Subsidy *s : Subsidy::Iterate()) { + SlSetArrayIndex(s->index); + SlObject(s, _subsidies_desc); + } + } + + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_subsidies_desc, _subsidies_sl_compat); + + int index; + while ((index = SlIterateArray()) != -1) { + Subsidy *s = new (index) Subsidy(); + SlObject(s, slt); + } + } +}; + +static const SUBSChunkHandler SUBS; +static const ChunkHandlerRef subsidy_chunk_handlers[] = { + SUBS, }; extern const ChunkHandlerTable _subsidy_chunk_handlers(subsidy_chunk_handlers); + +} diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp index a06b4a1e40..326473688f 100644 --- a/src/saveload/town_sl.cpp +++ b/src/saveload/town_sl.cpp @@ -8,238 +8,169 @@ /** @file town_sl.cpp Code handling saving and loading of towns and houses */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/town_sl_compat.h" + +#include "newgrf_sl.h" #include "../newgrf_house.h" #include "../town.h" #include "../landscape.h" #include "../subsidy_func.h" #include "../strings_func.h" -#include "../network/network.h" - -#include "saveload.h" -#include "newgrf_sl.h" +#include "../tilematrix_type.hpp" #include "../safeguards.h" -static bool _town_zone_radii_no_update = false; +namespace upstream_sl { -extern bool IsGetTownZonesCallbackHandlerPresent(); +typedef TileMatrix AcceptanceMatrix; -HouseID SLGetCleanHouseType(TileIndex t, bool old_map_position) -{ - if (old_map_position && SlXvIsFeatureMissing(XSLFI_MORE_HOUSES)) { - return _m[t].m4 | (GB(_m[t].m3, 6, 1) << 8); - } else { - return GetCleanHouseType(t); - } -} - -/** - * Rebuild all the cached variables of towns. - */ -void RebuildTownCaches(bool cargo_update_required, bool old_map_position) -{ - InitializeBuildingCounts(); - RebuildTownKdtree(); - - /* Reset town population and num_houses */ - for (Town *town : Town::Iterate()) { - town->cache.population = 0; - town->cache.num_houses = 0; - } - - for (TileIndex t = 0; t < MapSize(); t++) { - if (!IsTileType(t, MP_HOUSE)) continue; - - HouseID house_id = GetTranslatedHouseID(SLGetCleanHouseType(t, old_map_position)); - Town *town = Town::GetByTile(t); - IncreaseBuildingCount(town, house_id); - if (IsHouseCompleted(t)) town->cache.population += HouseSpec::Get(house_id)->population; - - /* Increase the number of houses for every house, but only once. */ - if (GetHouseNorthPart(house_id) == 0) town->cache.num_houses++; - } - - if (!_town_zone_radii_no_update) { - /* Update the population and num_house dependent values */ - for (Town *town : Town::Iterate()) { - UpdateTownRadius(town); - } - } -} - -static void CheckMultiTileHouseTypes(bool &cargo_update_required, bool old_map_position, bool translate_house_types) -{ - auto get_clean_house_type = [&](TileIndex t) -> HouseID { - HouseID type = SLGetCleanHouseType(t, old_map_position); - if (translate_house_types) type = GetTranslatedHouseID(type); - return type; +class SlTownSupplied : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_CONDVAR(TransportedCargoStat, old_max, SLE_UINT32, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_max, SLE_UINT32, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, old_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), }; + inline const static SaveLoadCompatTable compat_description = _town_supplied_sl_compat; - /* Check for cases when a NewGRF has set a wrong house substitute type. */ - for (TileIndex t = 0; t < MapSize(); t++) { - if (!IsTileType(t, MP_HOUSE)) continue; + /** + * Get the number of cargoes used by this savegame version. + * @return The number of cargoes used by this savegame version. + */ + size_t GetNumCargo() const + { + if (IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES)) return 32; + if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return NUM_CARGO; + /* Read from the savegame how long the list is. */ + return SlGetStructListLength(NUM_CARGO); + } - HouseID house_type = get_clean_house_type(t); - TileIndex north_tile = t + GetHouseNorthPart(house_type); // modifies 'house_type'! - if (t == north_tile) { - const HouseSpec *hs = HouseSpec::Get(house_type); - bool valid_house = true; - if (hs->building_flags & TILE_SIZE_2x1) { - TileIndex tile = t + TileDiffXY(1, 0); - if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false; - } else if (hs->building_flags & TILE_SIZE_1x2) { - TileIndex tile = t + TileDiffXY(0, 1); - if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false; - } else if (hs->building_flags & TILE_SIZE_2x2) { - TileIndex tile = t + TileDiffXY(0, 1); - if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false; - tile = t + TileDiffXY(1, 0); - if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 2) valid_house = false; - tile = t + TileDiffXY(1, 1); - if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 3) valid_house = false; - } - /* If not all tiles of this house are present remove the house. - * The other tiles will get removed later in this loop because - * their north tile is not the correct type anymore. */ - if (!valid_house) { - DoClearSquare(t); - cargo_update_required = true; - } - } else if (!IsTileType(north_tile, MP_HOUSE) || get_clean_house_type(north_tile) != house_type) { - /* This tile should be part of a multi-tile building but the - * north tile of this house isn't on the map. */ - DoClearSquare(t); - cargo_update_required = true; + void Save(Town *t) const override + { + SlSetStructListLength(NUM_CARGO); + for (CargoID i = 0; i < NUM_CARGO; i++) { + SlObject(&t->supplied[i], this->GetDescription()); } } -} -/** - * 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(bool cargo_update_required, bool old_map_position) -{ - auto get_clean_house_type = [&](TileIndex t) -> HouseID { - return SLGetCleanHouseType(t, old_map_position); + void Load(Town *t) const override + { + size_t num_cargo = this->GetNumCargo(); + for (size_t i = 0; i < num_cargo; i++) { + SlObject(&t->supplied[i], this->GetLoadDescription()); + } + } +}; + +class SlTownReceived : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_CONDVAR(TransportedCargoStat, old_max, SLE_UINT16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_max, SLE_UINT16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, old_act, SLE_UINT16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT16, SLV_165, SL_MAX_VERSION), }; - for (TileIndex t = 0; t < MapSize(); t++) { - if (!IsTileType(t, MP_HOUSE)) continue; + inline const static SaveLoadCompatTable compat_description = _town_received_sl_compat; - HouseID house_id = get_clean_house_type(t); - if (!HouseSpec::Get(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); - if (old_map_position && SlXvIsFeatureMissing(XSLFI_MORE_HOUSES)) { - _m[t].m4 = GB(house_id, 0, 8); - SB(_m[t].m3, 6, 1, GB(house_id, 8, 1)); - } else { - SetHouseType(t, house_id); - } - cargo_update_required = true; + void Save(Town *t) const override + { + SlSetStructListLength(NUM_TE); + for (size_t i = TE_BEGIN; i < TE_END; i++) { + SlObject(&t->received[i], this->GetDescription()); } } - CheckMultiTileHouseTypes(cargo_update_required, old_map_position, false); - if (cargo_update_required || SlXvIsFeatureMissing(XSLFI_MORE_HOUSES, 2)) CheckMultiTileHouseTypes(cargo_update_required, old_map_position, true); + void Load(Town *t) const override + { + size_t length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? (size_t)TE_END : SlGetStructListLength(TE_END); + for (size_t i = 0; i < length; i++) { + SlObject(&t->received[i], this->GetLoadDescription()); + } + } +}; - RebuildTownCaches(cargo_update_required, old_map_position); -} +class SlTownAcceptanceMatrix : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(AcceptanceMatrix, area.tile, SLE_UINT32), + SLE_VAR(AcceptanceMatrix, area.w, SLE_UINT16), + SLE_VAR(AcceptanceMatrix, area.h, SLE_UINT16), + }; + inline const static SaveLoadCompatTable compat_description = _town_acceptance_matrix_sl_compat; + + void Load(Town *t) const override + { + /* Discard now unused acceptance matrix. */ + AcceptanceMatrix dummy; + SlObject(&dummy, this->GetLoadDescription()); + if (dummy.area.w != 0) { + uint arr_len = dummy.area.w / AcceptanceMatrix::GRID * dummy.area.h / AcceptanceMatrix::GRID; + SlSkipBytes(4 * arr_len); + } + } +}; -/** Save and load of towns. */ static const SaveLoad _town_desc[] = { SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), SLE_CONDVAR(Town, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_3), ///< population, no longer in use - SLE_CONDNULL(4, SLV_3, SLV_85), ///< population, no longer in use - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_92), ///< num_houses, no longer in use - SLE_CONDVAR(Town, townnamegrfid, SLE_UINT32, SLV_66, SL_MAX_VERSION), SLE_VAR(Town, townnametype, SLE_UINT16), SLE_VAR(Town, townnameparts, SLE_UINT32), SLE_CONDSTR(Town, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), SLE_VAR(Town, flags, SLE_UINT8), - SLE_CONDVAR_X(Town, church_count, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_MULTI_BUILDING)), - SLE_CONDVAR_X(Town, stadium_count, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_MULTI_BUILDING)), SLE_CONDVAR(Town, statues, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), SLE_CONDVAR(Town, statues, SLE_UINT16, SLV_104, SL_MAX_VERSION), - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_2), ///< sort_index, no longer in use - SLE_CONDVAR(Town, have_ratings, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), SLE_CONDVAR(Town, have_ratings, SLE_UINT16, SLV_104, SL_MAX_VERSION), SLE_CONDARR(Town, ratings, SLE_INT16, 8, SL_MIN_VERSION, SLV_104), SLE_CONDARR(Town, ratings, SLE_INT16, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), - SLE_CONDNULL_X(MAX_COMPANIES, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - /* failed bribe attempts are stored since savegame format 4 */ SLE_CONDARR(Town, unwanted, SLE_INT8, 8, SLV_4, SLV_104), SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), ///< pct_pass_transported / pct_mail_transported, now computed on the fly - SLE_CONDNULL_X(3, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, received[TE_FOOD].old_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), SLE_CONDVAR(Town, received[TE_WATER].old_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), SLE_CONDVAR(Town, received[TE_FOOD].new_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), SLE_CONDVAR(Town, received[TE_WATER].new_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), SLE_CONDARR(Town, goal, SLE_UINT32, NUM_TE, SLV_165, SL_MAX_VERSION), SLE_CONDSSTR(Town, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_168, SL_MAX_VERSION), SLE_CONDVAR(Town, time_until_rebuild, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54), - SLE_CONDVAR(Town, grow_counter, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54), - SLE_CONDVAR(Town, growth_rate, SLE_FILE_U8 | SLE_VAR_I16, SL_MIN_VERSION, SLV_54), - - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT16, SLV_54, SL_MAX_VERSION), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_26)), + SLE_CONDVAR(Town, grow_counter, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54), SLE_CONDVAR(Town, grow_counter, SLE_UINT16, SLV_54, SL_MAX_VERSION), - + SLE_CONDVAR(Town, growth_rate, SLE_FILE_U8 | SLE_VAR_I16, SL_MIN_VERSION, SLV_54), SLE_CONDVAR(Town, growth_rate, SLE_FILE_I16 | SLE_VAR_U16, SLV_54, SLV_165), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_26)), SLE_CONDVAR(Town, growth_rate, SLE_UINT16, SLV_165, 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, SLV_2, SL_MAX_VERSION), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), SLE_CONDVAR(Town, exclusive_counter, SLE_UINT8, SLV_2, SL_MAX_VERSION), SLE_CONDVAR(Town, larger_town, SLE_BOOL, SLV_56, SL_MAX_VERSION), @@ -247,193 +178,61 @@ static const SaveLoad _town_desc[] = { SLE_CONDREFLIST(Town, psa_list, REF_STORAGE, SLV_161, SL_MAX_VERSION), - SLE_CONDNULL(4, SLV_166, SLV_EXTEND_CARGOTYPES), ///< cargo_produced, no longer in use - SLE_CONDNULL(8, SLV_EXTEND_CARGOTYPES, SLV_REMOVE_TOWN_CARGO_CACHE), ///< cargo_produced, no longer in use - SLE_CONDNULL(30, SLV_2, SLV_REMOVE_TOWN_CARGO_CACHE), ///< old reserved space - - SLE_CONDVAR_X(Town, override_flags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)), - SLE_CONDVAR_X(Town, override_values, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)), - SLE_CONDVAR_X(Town, build_tunnels, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)), - SLE_CONDVAR_X(Town, max_road_slope, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)), + SLEG_CONDSTRUCTLIST("supplied", SlTownSupplied, SLV_165, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("received", SlTownReceived, SLV_165, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("acceptance_matrix", SlTownAcceptanceMatrix, SLV_166, SLV_REMOVE_TOWN_CARGO_CACHE), }; -static const SaveLoad _town_supplied_desc[] = { - SLE_CONDVAR(TransportedCargoStat, old_max, SLE_UINT32, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_max, SLE_UINT32, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, old_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), +struct HIDSChunkHandler : NewGRFMappingChunkHandler { + HIDSChunkHandler() : NewGRFMappingChunkHandler('HIDS', _house_mngr) {} }; -static const SaveLoad _town_received_desc[] = { - SLE_CONDVAR(TransportedCargoStat, old_max, SLE_UINT16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_max, SLE_UINT16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, old_act, SLE_UINT16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT16, SLV_165, SL_MAX_VERSION), -}; +struct CITYChunkHandler : ChunkHandler { + CITYChunkHandler() : ChunkHandler('CITY', CH_TABLE) {} -static const SaveLoad _town_received_desc_spp[] = { - SLE_CONDVAR(TransportedCargoStat, old_max, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_max, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, old_act, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_act, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION), -}; + void Save() const override + { + SlTableHeader(_town_desc); -std::vector _filtered_town_desc; -std::vector _filtered_town_supplied_desc; -std::vector _filtered_town_received_desc; - -static void SetupDescs_TOWN() -{ - _filtered_town_desc = SlFilterObject(_town_desc); - _filtered_town_supplied_desc = SlFilterObject(_town_supplied_desc); - _filtered_town_received_desc = SlFilterObject(_town_received_desc); -} - -static void Save_HIDS() -{ - Save_NewGRFMapping(_house_mngr); -} - -static void Load_HIDS() -{ - Load_NewGRFMapping(_house_mngr); -} - -static void RealSave_Town(Town *t) -{ - SlObjectSaveFiltered(t, _filtered_town_desc); - - for (CargoID i = 0; i < NUM_CARGO; i++) { - SlObjectSaveFiltered(&t->supplied[i], _filtered_town_supplied_desc); - } - for (int i = TE_BEGIN; i < NUM_TE; i++) { - SlObjectSaveFiltered(&t->received[i], _filtered_town_received_desc); - } -} - -static void Save_TOWN() -{ - SetupDescs_TOWN(); - for (Town *t : Town::Iterate()) { - SlSetArrayIndex(t->index); - SlAutolength((AutolengthProc*)RealSave_Town, t); - } -} - -static void Load_TOWN() -{ - SetupDescs_TOWN(); - int index; - uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; - - while ((index = SlIterateArray()) != -1) { - Town *t = new (index) Town(); - SlObjectLoadFiltered(t, _filtered_town_desc); - - for (CargoID i = 0; i < num_cargo; i++) { - SlObjectLoadFiltered(&t->supplied[i], _filtered_town_supplied_desc); - } - if (SlXvIsFeaturePresent(XSLFI_SPRINGPP)) { - for (int i = TE_BEGIN; i < NUM_TE; i++) { - SlObject(&t->received[i], _town_received_desc_spp); - } - } else { - for (int i = TE_BEGIN; i < NUM_TE; i++) { - SlObjectLoadFiltered(&t->received[i], _filtered_town_received_desc); - } + for (Town *t : Town::Iterate()) { + SlSetArrayIndex(t->index); + SlObject(t, _town_desc); } + } - if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GetStringTab(t->townnametype) != TEXT_TAB_OLD_CUSTOM) { - SlErrorCorrupt("Invalid town name generator"); - } + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_town_desc, _town_sl_compat); - if ((!IsSavegameVersionBefore(SLV_166) && IsSavegameVersionBefore(SLV_REMOVE_TOWN_CARGO_CACHE)) || SlXvIsFeaturePresent(XSLFI_TOWN_CARGO_MATRIX)) { - SlSkipBytes(4); // tile - uint16 w = SlReadUint16(); - uint16 h = SlReadUint16(); - if (w != 0) { - SlSkipBytes((SlXvIsFeaturePresent(XSLFI_TOWN_CARGO_MATRIX) ? 8 : 4) * ((uint)(w / 4) * (uint)(h / 4))); + int index; + + while ((index = SlIterateArray()) != -1) { + Town *t = new (index) Town(); + SlObject(t, slt); + + if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GetStringTab(t->townnametype) != TEXT_TAB_OLD_CUSTOM) { + SlErrorCorrupt("Invalid town name generator"); } } } -} -/** Fix pointers when loading town data. */ -static void Ptrs_TOWN() -{ - /* Don't run when savegame version lower than 161. */ - if (IsSavegameVersionBefore(SLV_161)) return; + void FixPointers() const override + { + if (IsSavegameVersionBefore(SLV_161)) return; - SetupDescs_TOWN(); - for (Town *t : Town::Iterate()) { - SlObjectPtrOrNullFiltered(t, _filtered_town_desc); - } -} - -void SlResetTNNC() -{ - _town_zone_radii_no_update = false; -} - -void Save_TNNC() -{ - assert(_sl_xv_feature_versions[XSLFI_TNNC_CHUNK] != 0); - - if (!IsNetworkServerSave() || !IsGetTownZonesCallbackHandlerPresent()) { - SlSetLength(0); - return; - } - - SlSetLength(4 + (Town::GetNumItems() * (1 + lengthof(TownCache::squared_town_zone_radius)) * 4)); - - SlWriteUint32((uint32)Town::GetNumItems()); - - for (const Town *t : Town::Iterate()) { - SlWriteUint32(t->index); - for (uint i = 0; i < lengthof(TownCache::squared_town_zone_radius); i++) { - SlWriteUint32(t->cache.squared_town_zone_radius[i]); + for (Town *t : Town::Iterate()) { + SlObject(t, _town_desc); } } -} +}; -void Load_TNNC() -{ - if (SlGetFieldLength() == 0) return; - - if (!_networking || _network_server) { - SlSkipBytes(SlGetFieldLength()); - return; - } - - _town_zone_radii_no_update = true; - - const uint32 count = SlReadUint32(); - for (uint32 idx = 0; idx < count; idx++) { - Town *t = Town::Get(SlReadUint32()); - for (uint i = 0; i < lengthof(TownCache::squared_town_zone_radius); i++) { - t->cache.squared_town_zone_radius[i] = SlReadUint32(); - } - } -} - -static ChunkSaveLoadSpecialOpResult Special_TNNC(uint32 chunk_id, ChunkSaveLoadSpecialOp op) -{ - switch (op) { - case CSLSO_SHOULD_SAVE_CHUNK: - if (_sl_xv_feature_versions[XSLFI_TNNC_CHUNK] == 0) return CSLSOR_DONT_SAVE_CHUNK; - break; - - default: - break; - } - return CSLSOR_NONE; -} - -/** Chunk handler for towns. */ -static const ChunkHandler town_chunk_handlers[] = { - { 'HIDS', Save_HIDS, Load_HIDS, nullptr, nullptr, CH_ARRAY }, - { 'CITY', Save_TOWN, Load_TOWN, Ptrs_TOWN, nullptr, CH_ARRAY }, - { 'TNNC', Save_TNNC, Load_TNNC, nullptr, nullptr, CH_RIFF, Special_TNNC }, +static const HIDSChunkHandler HIDS; +static const CITYChunkHandler CITY; +static const ChunkHandlerRef town_chunk_handlers[] = { + HIDS, + CITY, }; extern const ChunkHandlerTable _town_chunk_handlers(town_chunk_handlers); + +} diff --git a/src/saveload/upstream/ai_sl.cpp b/src/saveload/upstream/ai_sl.cpp deleted file mode 100644 index f099cb0ca4..0000000000 --- a/src/saveload/upstream/ai_sl.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file ai_sl.cpp Handles the saveload part of the AIs */ - -#include "../../stdafx.h" -#include "../../debug.h" - -#include "saveload.h" -#include "compat/ai_sl_compat.h" - -#include "../../company_base.h" -#include "../../string_func.h" - -#include "../../ai/ai.hpp" -#include "../../ai/ai_config.hpp" -#include "../../network/network.h" -#include "../../ai/ai_instance.hpp" - -#include "../../safeguards.h" - -namespace upstream_sl { - -static std::string _ai_saveload_name; -static int _ai_saveload_version; -static std::string _ai_saveload_settings; -static bool _ai_saveload_is_random; - -static const SaveLoad _ai_company_desc[] = { - SLEG_SSTR("name", _ai_saveload_name, SLE_STR), - SLEG_SSTR("settings", _ai_saveload_settings, SLE_STR), - SLEG_CONDVAR("version", _ai_saveload_version, SLE_UINT32, SLV_108, SL_MAX_VERSION), - SLEG_CONDVAR("is_random", _ai_saveload_is_random, SLE_BOOL, SLV_136, SL_MAX_VERSION), -}; - -static void SaveReal_AIPL(int *index_ptr) -{ - CompanyID index = (CompanyID)*index_ptr; - AIConfig *config = AIConfig::GetConfig(index); - - if (config->HasScript()) { - _ai_saveload_name = config->GetName(); - _ai_saveload_version = config->GetVersion(); - } else { - /* No AI is configured for this so store an empty string as name. */ - _ai_saveload_name.clear(); - _ai_saveload_version = -1; - } - - _ai_saveload_is_random = config->IsRandom(); - _ai_saveload_settings = config->SettingsToString(); - - SlObject(nullptr, _ai_company_desc); - /* If the AI was active, store its data too */ - if (Company::IsValidAiID(index)) AI::Save(index); -} - -struct AIPLChunkHandler : ChunkHandler { - AIPLChunkHandler() : ChunkHandler('AIPL', CH_TABLE) {} - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_ai_company_desc, _ai_company_sl_compat); - - /* Free all current data */ - for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { - AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(nullptr); - } - - CompanyID index; - while ((index = (CompanyID)SlIterateArray()) != (CompanyID)-1) { - if (index >= MAX_COMPANIES) SlErrorCorrupt("Too many AI configs"); - - _ai_saveload_is_random = false; - _ai_saveload_version = -1; - SlObject(nullptr, slt); - - if (_game_mode == GM_MENU || (_networking && !_network_server)) { - if (Company::IsValidAiID(index)) AIInstance::LoadEmpty(); - continue; - } - - AIConfig *config = AIConfig::GetConfig(index, AIConfig::SSS_FORCE_GAME); - if (_ai_saveload_name.empty()) { - /* A random AI. */ - config->Change(nullptr, -1, false, true); - } else { - config->Change(_ai_saveload_name.c_str(), _ai_saveload_version, false, _ai_saveload_is_random); - if (!config->HasScript()) { - /* No version of the AI available that can load the data. Try to load the - * latest version of the AI instead. */ - config->Change(_ai_saveload_name.c_str(), -1, false, _ai_saveload_is_random); - if (!config->HasScript()) { - if (_ai_saveload_name.compare("%_dummy") != 0) { - DEBUG(script, 0, "The savegame has an AI by the name '%s', version %u which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version); - DEBUG(script, 0, "A random other AI will be loaded in its place."); - } else { - DEBUG(script, 0, "The savegame had no AIs available at the time of saving."); - DEBUG(script, 0, "A random available AI will be loaded now."); - } - } else { - DEBUG(script, 0, "The savegame has an AI by the name '%s', version %u which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version); - DEBUG(script, 0, "The latest version of that AI has been loaded instead, but it'll not get the savegame data as it's incompatible."); - } - /* Make sure the AI doesn't get the saveload data, as it was not the - * writer of the saveload data in the first place */ - _ai_saveload_version = -1; - } - } - - config->StringToSettings(_ai_saveload_settings); - - /* Load the AI saved data */ - if (Company::IsValidAiID(index)) config->SetToLoadData(AIInstance::Load(_ai_saveload_version)); - } - } - - void Save() const override - { - SlTableHeader(_ai_company_desc); - - for (int i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { - SlSetArrayIndex(i); - SlAutolength((AutolengthProc *)SaveReal_AIPL, &i); - } - } -}; - -static const AIPLChunkHandler AIPL; -static const ChunkHandlerRef ai_chunk_handlers[] = { - AIPL, -}; - -extern const ChunkHandlerTable _ai_chunk_handlers(ai_chunk_handlers); - -} diff --git a/src/saveload/upstream/animated_tile_sl.cpp b/src/saveload/upstream/animated_tile_sl.cpp deleted file mode 100644 index 128bf9c34e..0000000000 --- a/src/saveload/upstream/animated_tile_sl.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file animated_tile_sl.cpp Code handling saving and loading of animated tiles */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/animated_tile_sl_compat.h" - -#include "../../tile_type.h" -#include "../../animated_tile.h" -#include "../../core/alloc_func.hpp" -#include "../../core/smallvec_type.hpp" - -#include "../../safeguards.h" - -namespace upstream_sl { - -static std::vector _tmp_animated_tiles; - -static const SaveLoad _animated_tile_desc[] = { - SLEG_VECTOR("tiles", _tmp_animated_tiles, SLE_UINT32), -}; - -struct ANITChunkHandler : ChunkHandler { - ANITChunkHandler() : ChunkHandler('ANIT', CH_TABLE) {} - - void Save() const override - { - // removed - NOT_REACHED(); - } - - void Load() const override - { - /* Before version 80 we did NOT have a variable length animated tile table */ - if (IsSavegameVersionBefore(SLV_80)) { - /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */ - TileIndex anim_list[256]; - SlCopy(anim_list, 256, IsSavegameVersionBefore(SLV_6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32); - - for (int i = 0; i < 256; i++) { - if (anim_list[i] == 0) break; - _animated_tiles[anim_list[i]] = {}; - } - return; - } - - if (IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY)) { - size_t count = SlGetFieldLength() / sizeof(uint32); - _animated_tiles.clear(); - for (uint i = 0; i < count; i++) { - _animated_tiles[SlReadUint32()] = {}; - } - return; - } - - const std::vector slt = SlCompatTableHeader(_animated_tile_desc, _animated_tile_sl_compat); - - if (SlIterateArray() == -1) return; - SlGlobList(slt); - if (SlIterateArray() != -1) SlErrorCorrupt("Too many ANIT entries"); - - for (TileIndex t : _tmp_animated_tiles) { - _animated_tiles[t] = {}; - } - _tmp_animated_tiles.clear(); - } -}; - - -static const ANITChunkHandler ANIT; -static const ChunkHandlerRef animated_tile_chunk_handlers[] = { - ANIT, -}; - -extern const ChunkHandlerTable _animated_tile_chunk_handlers(animated_tile_chunk_handlers); - -} diff --git a/src/saveload/upstream/autoreplace_sl.cpp b/src/saveload/upstream/autoreplace_sl.cpp deleted file mode 100644 index af0cdbbd0e..0000000000 --- a/src/saveload/upstream/autoreplace_sl.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file autoreplace_sl.cpp Code handling saving and loading of autoreplace rules */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/autoreplace_sl_compat.h" - -#include "../../autoreplace_base.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -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, SLV_60, SL_MAX_VERSION), - SLE_CONDVAR(EngineRenew, replace_when_old, SLE_BOOL, SLV_175, SL_MAX_VERSION), -}; - -struct ERNWChunkHandler : ChunkHandler { - ERNWChunkHandler() : ChunkHandler('ERNW', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_engine_renew_desc); - - for (EngineRenew *er : EngineRenew::Iterate()) { - SlSetArrayIndex(er->index); - SlObject(er, _engine_renew_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_engine_renew_desc, _engine_renew_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - EngineRenew *er = new (index) EngineRenew(); - SlObject(er, slt); - - /* Advanced vehicle lists, ungrouped vehicles got added */ - if (IsSavegameVersionBefore(SLV_60)) { - er->group_id = ALL_GROUP; - } else if (IsSavegameVersionBefore(SLV_71)) { - if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP; - } - } - } - - void FixPointers() const override - { - for (EngineRenew *er : EngineRenew::Iterate()) { - SlObject(er, _engine_renew_desc); - } - } -}; - -static const ERNWChunkHandler ERNW; -static const ChunkHandlerRef autoreplace_chunk_handlers[] = { - ERNW, -}; - -extern const ChunkHandlerTable _autoreplace_chunk_handlers(autoreplace_chunk_handlers); - -} diff --git a/src/saveload/upstream/cargomonitor_sl.cpp b/src/saveload/upstream/cargomonitor_sl.cpp deleted file mode 100644 index 67d48d1ac5..0000000000 --- a/src/saveload/upstream/cargomonitor_sl.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file cargomonitor_sl.cpp Code handling saving and loading of Cargo monitoring. */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/cargomonitor_sl_compat.h" - -#include "../../cargomonitor.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -/** Temporary storage of cargo monitoring data for loading or saving it. */ -struct TempStorage { - CargoMonitorID number; - uint32 amount; -}; - -/** Description of the #TempStorage structure for the purpose of load and save. */ -static const SaveLoad _cargomonitor_pair_desc[] = { - SLE_VAR(TempStorage, number, SLE_UINT32), - SLE_VAR(TempStorage, amount, SLE_UINT32), -}; - -static CargoMonitorID FixupCargoMonitor(CargoMonitorID number) -{ - /* Between SLV_EXTEND_CARGOTYPES and SLV_FIX_CARGO_MONITOR, the - * CargoMonitorID structure had insufficient packing for more - * than 32 cargo types. Here we have to shuffle bits to account - * for the change. - * Company moved from bits 24-31 to 25-28. - * Cargo type increased from bits 19-23 to 19-24. - */ - SB(number, 25, 4, GB(number, 24, 4)); - SB(number, 29, 3, 0); - ClrBit(number, 24); - return number; -} - -/** #_cargo_deliveries monitoring map. */ -struct CMDLChunkHandler : ChunkHandler { - CMDLChunkHandler() : ChunkHandler('CMDL', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_cargomonitor_pair_desc); - - TempStorage storage; - - int i = 0; - CargoMonitorMap::const_iterator iter = _cargo_deliveries.begin(); - while (iter != _cargo_deliveries.end()) { - storage.number = iter->first; - storage.amount = iter->second; - - SlSetArrayIndex(i); - SlObject(&storage, _cargomonitor_pair_desc); - - i++; - iter++; - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_cargomonitor_pair_desc, _cargomonitor_pair_sl_compat); - - TempStorage storage; - bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR); - - ClearCargoDeliveryMonitoring(); - for (;;) { - if (SlIterateArray() < 0) break; - SlObject(&storage, slt); - - if (fix) storage.number = FixupCargoMonitor(storage.number); - - std::pair p(storage.number, storage.amount); - _cargo_deliveries.insert(p); - } - } -}; - -/** #_cargo_pickups monitoring map. */ -struct CMPUChunkHandler : ChunkHandler { - CMPUChunkHandler() : ChunkHandler('CMPU', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_cargomonitor_pair_desc); - - TempStorage storage; - - int i = 0; - CargoMonitorMap::const_iterator iter = _cargo_pickups.begin(); - while (iter != _cargo_pickups.end()) { - storage.number = iter->first; - storage.amount = iter->second; - - SlSetArrayIndex(i); - SlObject(&storage, _cargomonitor_pair_desc); - - i++; - iter++; - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_cargomonitor_pair_desc, _cargomonitor_pair_sl_compat); - - TempStorage storage; - bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR); - - ClearCargoPickupMonitoring(); - for (;;) { - if (SlIterateArray() < 0) break; - SlObject(&storage, slt); - - if (fix) storage.number = FixupCargoMonitor(storage.number); - - std::pair p(storage.number, storage.amount); - _cargo_pickups.insert(p); - } - } -}; - -/** Chunk definition of the cargomonitoring maps. */ -static const CMDLChunkHandler CMDL; -static const CMPUChunkHandler CMPU; -static const ChunkHandlerRef cargomonitor_chunk_handlers[] = { - CMDL, - CMPU, -}; - -extern const ChunkHandlerTable _cargomonitor_chunk_handlers(cargomonitor_chunk_handlers); - -} diff --git a/src/saveload/upstream/cargopacket_sl.cpp b/src/saveload/upstream/cargopacket_sl.cpp deleted file mode 100644 index 9597f5c941..0000000000 --- a/src/saveload/upstream/cargopacket_sl.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file cargopacket_sl.cpp Code handling saving and loading of cargo packets */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/cargopacket_sl_compat.h" - -#include "../../vehicle_base.h" -#include "../../station_base.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -/** - * Wrapper function to get the CargoPacket's internal structure while - * some of the variables itself are private. - * @return the saveload description for CargoPackets. - */ -SaveLoadTable GetCargoPacketDesc() -{ - 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_CONDVAR(CargoPacket, days_in_transit, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_MORE_CARGO_AGE), - SLE_CONDVAR(CargoPacket, days_in_transit, SLE_UINT16, SLV_MORE_CARGO_AGE, SL_MAX_VERSION), - SLE_VAR(CargoPacket, feeder_share, SLE_INT64), - SLE_CONDVAR(CargoPacket, source_type, SLE_UINT8, SLV_125, SL_MAX_VERSION), - SLE_CONDVAR(CargoPacket, source_id, SLE_UINT16, SLV_125, SL_MAX_VERSION), - }; - return _cargopacket_desc; -} - -struct CAPAChunkHandler : ChunkHandler { - CAPAChunkHandler() : ChunkHandler('CAPA', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(GetCargoPacketDesc()); - - for (CargoPacket *cp : CargoPacket::Iterate()) { - SlSetArrayIndex(cp->index); - SlObject(cp, GetCargoPacketDesc()); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(GetCargoPacketDesc(), _cargopacket_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - CargoPacket *cp = new (index) CargoPacket(); - SlObject(cp, slt); - } - } -}; - -static const CAPAChunkHandler CAPA; -static const ChunkHandlerRef cargopacket_chunk_handlers[] = { - CAPA, -}; - -extern const ChunkHandlerTable _cargopacket_chunk_handlers(cargopacket_chunk_handlers); - -} diff --git a/src/saveload/upstream/cheat_sl.cpp b/src/saveload/upstream/cheat_sl.cpp deleted file mode 100644 index 864c49477b..0000000000 --- a/src/saveload/upstream/cheat_sl.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file cheat_sl.cpp Code handling saving and loading of cheats */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/cheat_sl_compat.h" - -#include "../../cheat_type.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -static const SaveLoad _cheats_desc[] = { - SLE_VAR(Cheats, magic_bulldozer.been_used, SLE_BOOL), - SLE_VAR(Cheats, magic_bulldozer.value, SLE_BOOL), - SLE_VAR(Cheats, switch_company.been_used, SLE_BOOL), - SLE_VAR(Cheats, switch_company.value, SLE_BOOL), - SLE_VAR(Cheats, money.been_used, SLE_BOOL), - SLE_VAR(Cheats, money.value, SLE_BOOL), - SLE_VAR(Cheats, crossing_tunnels.been_used, SLE_BOOL), - SLE_VAR(Cheats, crossing_tunnels.value, SLE_BOOL), - SLE_VAR(Cheats, no_jetcrash.been_used, SLE_BOOL), - SLE_VAR(Cheats, no_jetcrash.value, SLE_BOOL), - SLE_VAR(Cheats, change_date.been_used, SLE_BOOL), - SLE_VAR(Cheats, change_date.value, SLE_BOOL), - SLE_VAR(Cheats, setup_prod.been_used, SLE_BOOL), - SLE_VAR(Cheats, setup_prod.value, SLE_BOOL), - SLE_VAR(Cheats, edit_max_hl.been_used, SLE_BOOL), - SLE_VAR(Cheats, edit_max_hl.value, SLE_BOOL), -}; - - -struct CHTSChunkHandler : ChunkHandler { - CHTSChunkHandler() : ChunkHandler('CHTS', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_cheats_desc); - - SlSetArrayIndex(0); - SlObject(&_cheats, _cheats_desc); - } - - void Load() const override - { - std::vector slt = SlCompatTableHeader(_cheats_desc, _cheats_sl_compat); - - if (IsSavegameVersionBefore(SLV_TABLE_CHUNKS)) { - size_t count = SlGetFieldLength(); - std::vector oslt; - - /* Cheats were added over the years without a savegame bump. They are - * stored as 2 SLE_BOOLs per entry. "count" indicates how many SLE_BOOLs - * are stored for this savegame. So read only "count" SLE_BOOLs (and in - * result "count / 2" cheats). */ - for (auto &sld : slt) { - count--; - oslt.push_back(sld); - - if (count == 0) break; - } - slt = oslt; - } - - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; - SlObject(&_cheats, slt); - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many CHTS entries"); - } -}; - -static const CHTSChunkHandler CHTS; -static const ChunkHandlerRef cheat_chunk_handlers[] = { - CHTS, -}; - -extern const ChunkHandlerTable _cheat_chunk_handlers(cheat_chunk_handlers); - -} diff --git a/src/saveload/upstream/company_sl.cpp b/src/saveload/upstream/company_sl.cpp deleted file mode 100644 index e1d64084c0..0000000000 --- a/src/saveload/upstream/company_sl.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file company_sl.cpp Code handling saving and loading of company data */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/company_sl_compat.h" - -#include "../../company_func.h" -#include "../../company_manager_face.h" -#include "../../fios.h" -#include "../../tunnelbridge_map.h" -#include "../../tunnelbridge.h" -#include "../../station_base.h" -#include "../../strings_func.h" - -#include "table/strings.h" - -#include "../../safeguards.h" - -void SetDefaultCompanySettings(CompanyID cid); - -namespace upstream_sl { - -/* We do need to read this single value, as the bigger it gets, the more data is stored */ -struct CompanyOldAI { - uint8 num_build_rec; -}; - -class SlCompanyOldAIBuildRec : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = {{}}; // Needed to keep DefaultSaveLoadHandler happy. - inline const static SaveLoadCompatTable compat_description = _company_old_ai_buildrec_compat; - - SaveLoadTable GetDescription() const override { return {}; } - - void Load(CompanyOldAI *old_ai) const override - { - for (int i = 0; i != old_ai->num_build_rec; i++) { - SlObject(nullptr, this->GetLoadDescription()); - } - } - - void LoadCheck(CompanyOldAI *old_ai) const override { this->Load(old_ai); } -}; - -class SlCompanyOldAI : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_CONDVAR(CompanyOldAI, num_build_rec, SLE_UINT8, SL_MIN_VERSION, SLV_107), - SLEG_STRUCTLIST("buildrec", SlCompanyOldAIBuildRec), - }; - inline const static SaveLoadCompatTable compat_description = _company_old_ai_compat; - - void Load(CompanyProperties *c) const override - { - if (!c->is_ai) return; - - CompanyOldAI old_ai; - SlObject(&old_ai, this->GetLoadDescription()); - } - - void LoadCheck(CompanyProperties *c) const override { this->Load(c); } -}; - -class SlCompanySettings : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - /* Engine renewal settings */ - SLE_CONDREF(CompanyProperties, engine_renew_list, REF_ENGINE_RENEWS, SLV_19, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, settings.engine_renew, SLE_BOOL, SLV_16, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, settings.engine_renew_months, SLE_INT16, SLV_16, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, settings.engine_renew_money, SLE_UINT32, SLV_16, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, settings.renew_keep_length, SLE_BOOL, SLV_2, SL_MAX_VERSION), - - /* Default vehicle settings */ - SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_ispercent, SLE_BOOL, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_trains, SLE_UINT16, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_roadveh, SLE_UINT16, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_aircraft, SLE_UINT16, SLV_120, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, settings.vehicle.servint_ships, SLE_UINT16, SLV_120, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _company_settings_compat; - - void Save(CompanyProperties *c) const override - { - SlObject(c, this->GetDescription()); - } - - void Load(CompanyProperties *c) const override - { - SlObject(c, this->GetLoadDescription()); - } - - void FixPointers(CompanyProperties *c) const override - { - SlObject(c, this->GetDescription()); - } - - void LoadCheck(CompanyProperties *c) const override { this->Load(c); } -}; - -class SlCompanyEconomy : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_CONDVAR(CompanyEconomyEntry, income, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), - SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, SLV_2, SL_MAX_VERSION), - SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), - SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, SLV_2, SL_MAX_VERSION), - SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), - SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, SLV_2, SL_MAX_VERSION), - - SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, SL_MIN_VERSION, SLV_170), - SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, SLV_170, SLV_EXTEND_CARGOTYPES), - SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), - SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), - }; - inline const static SaveLoadCompatTable compat_description = _company_economy_compat; - - void Save(CompanyProperties *c) const override - { - SlObject(&c->cur_economy, this->GetDescription()); - } - - void Load(CompanyProperties *c) const override - { - SlObject(&c->cur_economy, this->GetLoadDescription()); - } - - void FixPointers(CompanyProperties *c) const override - { - SlObject(&c->cur_economy, this->GetDescription()); - } - - void LoadCheck(CompanyProperties *c) const override { this->Load(c); } -}; - -class SlCompanyOldEconomy : public SlCompanyEconomy { -public: - void Save(CompanyProperties *c) const override - { - SlSetStructListLength(c->num_valid_stat_ent); - for (int i = 0; i < c->num_valid_stat_ent; i++) { - SlObject(&c->old_economy[i], this->GetDescription()); - } - } - - void Load(CompanyProperties *c) const override - { - if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { - c->num_valid_stat_ent = (uint8)SlGetStructListLength(UINT8_MAX); - } - if (c->num_valid_stat_ent > lengthof(c->old_economy)) SlErrorCorrupt("Too many old economy entries"); - - for (int i = 0; i < c->num_valid_stat_ent; i++) { - SlObject(&c->old_economy[i], this->GetLoadDescription()); - } - } - - void LoadCheck(CompanyProperties *c) const override { this->Load(c); } -}; - -class SlCompanyLiveries : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_CONDVAR(Livery, in_use, SLE_UINT8, SLV_34, SL_MAX_VERSION), - SLE_CONDVAR(Livery, colour1, SLE_UINT8, SLV_34, SL_MAX_VERSION), - SLE_CONDVAR(Livery, colour2, SLE_UINT8, SLV_34, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _company_liveries_compat; - - /** - * Get the number of liveries used by this savegame version. - * @return The number of liveries used by this savegame version. - */ - size_t GetNumLiveries() const - { - if (IsSavegameVersionBefore(SLV_63)) return LS_END - 4; - if (IsSavegameVersionBefore(SLV_85)) return LS_END - 2; - if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return LS_END; - /* Read from the savegame how long the list is. */ - return SlGetStructListLength(LS_END); - } - - void Save(CompanyProperties *c) const override - { - SlSetStructListLength(LS_END); - for (int i = 0; i < LS_END; i++) { - SlObject(&c->livery[i], this->GetDescription()); - } - } - - void Load(CompanyProperties *c) const override - { - size_t num_liveries = this->GetNumLiveries(); - bool update_in_use = IsSavegameVersionBefore(SLV_GROUP_LIVERIES); - - for (size_t i = 0; i < num_liveries; i++) { - SlObject(&c->livery[i], this->GetLoadDescription()); - if (update_in_use && i != LS_DEFAULT) { - if (c->livery[i].in_use == 0) { - c->livery[i].colour1 = c->livery[LS_DEFAULT].colour1; - c->livery[i].colour2 = c->livery[LS_DEFAULT].colour2; - } else { - c->livery[i].in_use = 3; - } - } - } - - if (IsSavegameVersionBefore(SLV_85)) { - /* 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 (IsSavegameVersionBefore(SLV_63)) { - /* 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]; - } - } - - void LoadCheck(CompanyProperties *c) const override { this->Load(c); } -}; - -/* Save/load of companies */ -static const SaveLoad _company_desc[] = { - SLE_VAR(CompanyProperties, name_2, SLE_UINT32), - SLE_VAR(CompanyProperties, name_1, SLE_STRINGID), - SLE_CONDSSTR(CompanyProperties, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), - - SLE_VAR(CompanyProperties, president_name_1, SLE_STRINGID), - SLE_VAR(CompanyProperties, president_name_2, SLE_UINT32), - SLE_CONDSSTR(CompanyProperties, president_name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), - - SLE_VAR(CompanyProperties, face, SLE_UINT32), - - /* money was changed to a 64 bit field in savegame version 1. */ - SLE_CONDVAR(CompanyProperties, money, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_1), - SLE_CONDVAR(CompanyProperties, money, SLE_INT64, SLV_1, SL_MAX_VERSION), - - SLE_CONDVAR(CompanyProperties, current_loan, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(CompanyProperties, current_loan, SLE_INT64, SLV_65, SL_MAX_VERSION), - - SLE_VAR(CompanyProperties, colour, SLE_UINT8), - SLE_VAR(CompanyProperties, money_fraction, SLE_UINT8), - SLE_VAR(CompanyProperties, block_preview, SLE_UINT8), - - SLE_CONDVAR(CompanyProperties, location_of_HQ, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(CompanyProperties, location_of_HQ, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(CompanyProperties, last_build_coordinate, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, inaugurated_year, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(CompanyProperties, inaugurated_year, SLE_INT32, SLV_31, SL_MAX_VERSION), - - SLE_ARR(CompanyProperties, share_owners, SLE_UINT8, 4), - - SLE_CONDVAR(CompanyProperties, num_valid_stat_ent, SLE_UINT8, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), - - SLE_VAR(CompanyProperties, months_of_bankruptcy, SLE_UINT8), - SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), - SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_UINT16, SLV_104, SL_MAX_VERSION), - SLE_VAR(CompanyProperties, bankrupt_timeout, SLE_INT16), - SLE_CONDVAR(CompanyProperties, bankrupt_value, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(CompanyProperties, bankrupt_value, SLE_INT64, SLV_65, SL_MAX_VERSION), - - /* yearly expenses was changed to 64-bit in savegame version 2. */ - SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, SL_MIN_VERSION, SLV_2), - SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 13, SLV_2, SL_MAX_VERSION), - - SLE_CONDVAR(CompanyProperties, is_ai, SLE_BOOL, SLV_2, SL_MAX_VERSION), - - SLE_CONDVAR(CompanyProperties, terraform_limit, SLE_UINT32, SLV_156, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, clear_limit, SLE_UINT32, SLV_156, SL_MAX_VERSION), - SLE_CONDVAR(CompanyProperties, tree_limit, SLE_UINT32, SLV_175, SL_MAX_VERSION), - SLEG_STRUCT("settings", SlCompanySettings), - SLEG_CONDSTRUCT("old_ai", SlCompanyOldAI, SL_MIN_VERSION, SLV_107), - SLEG_STRUCT("cur_economy", SlCompanyEconomy), - SLEG_STRUCTLIST("old_economy", SlCompanyOldEconomy), - SLEG_CONDSTRUCTLIST("liveries", SlCompanyLiveries, SLV_34, SL_MAX_VERSION), -}; - -struct PLYRChunkHandler : ChunkHandler { - PLYRChunkHandler() : ChunkHandler('PLYR', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_company_desc); - - for (Company *c : Company::Iterate()) { - SlSetArrayIndex(c->index); - SlObject(c, _company_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_company_desc, _company_sl_compat); - - int index; - while ((index = SlIterateArray()) != -1) { - Company *c = new (index) Company(); - SetDefaultCompanySettings(c->index); - SlObject((CompanyProperties *)c, slt); - _company_colours[index] = (Colours)c->colour; - } - } - - - void LoadCheck(size_t) const override - { - const std::vector slt = SlCompatTableHeader(_company_desc, _company_sl_compat); - - int index; - while ((index = SlIterateArray()) != -1) { - CompanyProperties *cprops = new CompanyProperties(); - SlObject(cprops, slt); - - /* We do not load old custom names */ - if (IsSavegameVersionBefore(SLV_84)) { - if (GetStringTab(cprops->name_1) == TEXT_TAB_OLD_CUSTOM) { - cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; - } - - if (GetStringTab(cprops->president_name_1) == TEXT_TAB_OLD_CUSTOM) { - cprops->president_name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; - } - } - - if (cprops->name.empty() && !IsInsideMM(cprops->name_1, SPECSTR_COMPANY_NAME_START, SPECSTR_COMPANY_NAME_LAST + 1) && - cprops->name_1 != STR_GAME_SAVELOAD_NOT_AVAILABLE && cprops->name_1 != STR_SV_UNNAMED && - cprops->name_1 != SPECSTR_ANDCO_NAME && cprops->name_1 != SPECSTR_PRESIDENT_NAME && - cprops->name_1 != SPECSTR_SILLY_NAME) { - cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; - } - - if (!_load_check_data.companies.Insert(index, cprops)) delete cprops; - } - } - - void FixPointers() const override - { - for (Company *c : Company::Iterate()) { - SlObject((CompanyProperties *)c, _company_desc); - } - } -}; - -static const PLYRChunkHandler PLYR; -static const ChunkHandlerRef company_chunk_handlers[] = { - PLYR, -}; - -extern const ChunkHandlerTable _company_chunk_handlers(company_chunk_handlers); - -} diff --git a/src/saveload/upstream/economy_sl.cpp b/src/saveload/upstream/economy_sl.cpp deleted file mode 100644 index e6ea184fb8..0000000000 --- a/src/saveload/upstream/economy_sl.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file economy_sl.cpp Code handling saving and loading of economy data */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/economy_sl_compat.h" - -#include "../../economy_func.h" -#include "../../economy_base.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -static const SaveLoad _economy_desc[] = { - SLE_CONDVAR(Economy, old_max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(Economy, old_max_loan_unround, SLE_INT64, SLV_65, SLV_126), - SLE_CONDVAR(Economy, old_max_loan_unround_fract, SLE_UINT16, SLV_70, SLV_126), - SLE_CONDVAR(Economy, inflation_prices, SLE_UINT64, SLV_126, SL_MAX_VERSION), - SLE_CONDVAR(Economy, inflation_payment, SLE_UINT64, SLV_126, 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, SLV_102, SL_MAX_VERSION), -}; - -/** Economy variables */ -struct ECMYChunkHandler : ChunkHandler { - ECMYChunkHandler() : ChunkHandler('ECMY', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_economy_desc); - - SlSetArrayIndex(0); - SlObject(&_economy, _economy_desc); - } - - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_economy_desc, _economy_sl_compat); - - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; - SlObject(&_economy, slt); - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many ECMY entries"); - - StartupIndustryDailyChanges(IsSavegameVersionBefore(SLV_102)); // old savegames will need to be initialized - } -}; - -static const SaveLoad _cargopayment_desc[] = { - SLE_REF(CargoPayment, front, REF_VEHICLE), - SLE_VAR(CargoPayment, route_profit, SLE_INT64), - SLE_VAR(CargoPayment, visual_profit, SLE_INT64), - SLE_CONDVAR(CargoPayment, visual_transfer, SLE_INT64, SLV_181, SL_MAX_VERSION), -}; - -struct CAPYChunkHandler : ChunkHandler { - CAPYChunkHandler() : ChunkHandler('CAPY', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_cargopayment_desc); - - for (CargoPayment *cp : CargoPayment::Iterate()) { - SlSetArrayIndex(cp->index); - SlObject(cp, _cargopayment_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_cargopayment_desc, _cargopayment_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - CargoPayment *cp = new (index) CargoPayment(); - SlObject(cp, slt); - } - } - - void FixPointers() const override - { - for (CargoPayment *cp : CargoPayment::Iterate()) { - SlObject(cp, _cargopayment_desc); - } - } -}; - -static const CAPYChunkHandler CAPY; -static const ECMYChunkHandler ECMY; -static const ChunkHandlerRef economy_chunk_handlers[] = { - CAPY, - ECMY, -}; - -extern const ChunkHandlerTable _economy_chunk_handlers(economy_chunk_handlers); - -} diff --git a/src/saveload/upstream/engine_sl.cpp b/src/saveload/upstream/engine_sl.cpp deleted file mode 100644 index d4b0437d92..0000000000 --- a/src/saveload/upstream/engine_sl.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file engine_sl.cpp Code handling saving and loading of engines */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/engine_sl_compat.h" - -#include "../../engine_base.h" -#include "../../string_func.h" -#include - -#include "../../safeguards.h" - -Engine *GetTempDataEngine(EngineID index); - -namespace upstream_sl { - -static const SaveLoad _engine_desc[] = { - SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Engine, intro_date, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Engine, age, SLE_INT32, SLV_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, flags, SLE_UINT8), - SLE_CONDVAR(Engine, preview_asked, SLE_UINT16, SLV_179, SL_MAX_VERSION), - SLE_CONDVAR(Engine, preview_company, SLE_UINT8, SLV_179, SL_MAX_VERSION), - SLE_VAR(Engine, preview_wait, SLE_UINT8), - SLE_CONDVAR(Engine, company_avail, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), - SLE_CONDVAR(Engine, company_avail, SLE_UINT16, SLV_104, SL_MAX_VERSION), - SLE_CONDVAR(Engine, company_hidden, SLE_UINT16, SLV_193, SL_MAX_VERSION), - SLE_CONDSTR(Engine, name, SLE_STR, 0, SLV_84, SL_MAX_VERSION), -}; - -struct ENGNChunkHandler : ChunkHandler { - ENGNChunkHandler() : ChunkHandler('ENGN', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_engine_desc); - - for (Engine *e : Engine::Iterate()) { - SlSetArrayIndex(e->index); - SlObject(e, _engine_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_engine_desc, _engine_sl_compat); - - /* 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, slt); - - if (IsSavegameVersionBefore(SLV_179)) { - /* preview_company_rank was replaced with preview_company and preview_asked. - * Just cancel any previews. */ - e->flags &= ~4; // ENGINE_OFFER_WINDOW_OPEN - e->preview_company = INVALID_COMPANY; - e->preview_asked = MAX_UVALUE(CompanyMask); - } - } - } -}; - -/** Save and load the mapping between the engine id in the pool, and the grf file it came from. */ -static const SaveLoad _engine_id_mapping_desc[] = { - SLE_VAR(EngineIDMapping, grfid, SLE_UINT32), - SLE_VAR(EngineIDMapping, internal_id, SLE_UINT16), - SLE_VAR(EngineIDMapping, type, SLE_UINT8), - SLE_VAR(EngineIDMapping, substitute_id, SLE_UINT8), -}; - -struct EIDSChunkHandler : ChunkHandler { - EIDSChunkHandler() : ChunkHandler('EIDS', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_engine_id_mapping_desc); - - uint index = 0; - for (EngineIDMapping &eid : _engine_mngr) { - SlSetArrayIndex(index); - SlObject(&eid, _engine_id_mapping_desc); - index++; - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_engine_id_mapping_desc, _engine_id_mapping_sl_compat); - - _engine_mngr.clear(); - - while (SlIterateArray() != -1) { - EngineIDMapping *eid = &_engine_mngr.emplace_back(); - SlObject(eid, slt); - } - } -}; - -static const EIDSChunkHandler EIDS; -static const ENGNChunkHandler ENGN; -static const ChunkHandlerRef engine_chunk_handlers[] = { - EIDS, - ENGN, -}; - -extern const ChunkHandlerTable _engine_chunk_handlers(engine_chunk_handlers); - -} diff --git a/src/saveload/upstream/game_sl.cpp b/src/saveload/upstream/game_sl.cpp deleted file mode 100644 index 7dddbf2ba7..0000000000 --- a/src/saveload/upstream/game_sl.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file game_sl.cpp Handles the saveload part of the GameScripts */ - -#include "../../stdafx.h" -#include "../../debug.h" - -#include "saveload.h" -#include "compat/game_sl_compat.h" - -#include "../../string_func.h" -#include "../../game/game.hpp" -#include "../../game/game_config.hpp" -#include "../../network/network.h" -#include "../../game/game_instance.hpp" -#include "../../game/game_text.hpp" - -#include "../../safeguards.h" - -extern GameStrings *_current_data; - -namespace upstream_sl { - -static std::string _game_saveload_name; -static int _game_saveload_version; -static std::string _game_saveload_settings; -static bool _game_saveload_is_random; - -static const SaveLoad _game_script_desc[] = { - SLEG_SSTR("name", _game_saveload_name, SLE_STR), - SLEG_SSTR("settings", _game_saveload_settings, SLE_STR), - SLEG_VAR("version", _game_saveload_version, SLE_UINT32), - SLEG_VAR("is_random", _game_saveload_is_random, SLE_BOOL), -}; - -static void SaveReal_GSDT(int *index_ptr) -{ - GameConfig *config = GameConfig::GetConfig(); - - if (config->HasScript()) { - _game_saveload_name = config->GetName(); - _game_saveload_version = config->GetVersion(); - } else { - /* No GameScript is configured for this so store an empty string as name. */ - _game_saveload_name.clear(); - _game_saveload_version = -1; - } - - _game_saveload_is_random = config->IsRandom(); - _game_saveload_settings = config->SettingsToString(); - - SlObject(nullptr, _game_script_desc); - Game::Save(); -} - -struct GSDTChunkHandler : ChunkHandler { - GSDTChunkHandler() : ChunkHandler('GSDT', CH_TABLE) {} - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_game_script_desc, _game_script_sl_compat); - - /* Free all current data */ - GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME)->Change(nullptr); - - if (SlIterateArray() == -1) return; - - _game_saveload_version = -1; - SlObject(nullptr, slt); - - if (_game_mode == GM_MENU || (_networking && !_network_server)) { - GameInstance::LoadEmpty(); - if (SlIterateArray() != -1) SlErrorCorrupt("Too many GameScript configs"); - return; - } - - GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME); - if (!_game_saveload_name.empty()) { - config->Change(_game_saveload_name.c_str(), _game_saveload_version, false, _game_saveload_is_random); - if (!config->HasScript()) { - /* No version of the GameScript available that can load the data. Try to load the - * latest version of the GameScript instead. */ - config->Change(_game_saveload_name.c_str(), -1, false, _game_saveload_is_random); - if (!config->HasScript()) { - if (_game_saveload_name.compare("%_dummy") != 0) { - DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %u which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version); - DEBUG(script, 0, "This game will continue to run without GameScript."); - } else { - DEBUG(script, 0, "The savegame had no GameScript available at the time of saving."); - DEBUG(script, 0, "This game will continue to run without GameScript."); - } - } else { - DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %u which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version); - DEBUG(script, 0, "The latest version of that GameScript has been loaded instead, but it'll not get the savegame data as it's incompatible."); - } - /* Make sure the GameScript doesn't get the saveload data, as it was not the - * writer of the saveload data in the first place */ - _game_saveload_version = -1; - } - } - - config->StringToSettings(_game_saveload_settings); - - /* Load the GameScript saved data */ - config->SetToLoadData(GameInstance::Load(_game_saveload_version)); - - if (SlIterateArray() != -1) SlErrorCorrupt("Too many GameScript configs"); - } - - void Save() const override - { - SlTableHeader(_game_script_desc); - SlSetArrayIndex(0); - SlAutolength((AutolengthProc *)SaveReal_GSDT, nullptr); - } -}; - -static std::string _game_saveload_string; -static uint32 _game_saveload_strings; - -class SlGameLanguageString : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLEG_SSTR("string", _game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL), - }; - inline const static SaveLoadCompatTable compat_description = _game_language_string_sl_compat; - - void Save(LanguageStrings *ls) const override - { - SlSetStructListLength(ls->lines.size()); - - for (const auto &string : ls->lines) { - _game_saveload_string = string; - SlObject(nullptr, this->GetDescription()); - } - } - - void Load(LanguageStrings *ls) const override - { - uint32 length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _game_saveload_strings : (uint32)SlGetStructListLength(UINT32_MAX); - - for (uint32 i = 0; i < length; i++) { - SlObject(nullptr, this->GetLoadDescription()); - ls->lines.emplace_back(_game_saveload_string); - } - } -}; - -static const SaveLoad _game_language_desc[] = { - SLE_SSTR(LanguageStrings, language, SLE_STR), - SLEG_CONDVAR("count", _game_saveload_strings, SLE_UINT32, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), - SLEG_STRUCTLIST("strings", SlGameLanguageString), -}; - -struct GSTRChunkHandler : ChunkHandler { - GSTRChunkHandler() : ChunkHandler('GSTR', CH_TABLE) {} - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_game_language_desc, _game_language_sl_compat); - - delete _current_data; - _current_data = new GameStrings(); - - while (SlIterateArray() != -1) { - LanguageStrings ls; - SlObject(&ls, slt); - _current_data->raw_strings.push_back(std::move(ls)); - } - - /* If there were no strings in the savegame, set GameStrings to nullptr */ - if (_current_data->raw_strings.size() == 0) { - delete _current_data; - _current_data = nullptr; - return; - } - - _current_data->Compile(); - ReconsiderGameScriptLanguage(); - } - - void Save() const override - { - SlTableHeader(_game_language_desc); - - if (_current_data == nullptr) return; - - for (uint i = 0; i < _current_data->raw_strings.size(); i++) { - SlSetArrayIndex(i); - SlObject(&_current_data->raw_strings[i], _game_language_desc); - } - } -}; - -static const GSTRChunkHandler GSTR; -static const GSDTChunkHandler GSDT; -static const ChunkHandlerRef game_chunk_handlers[] = { - GSTR, - GSDT, -}; - -extern const ChunkHandlerTable _game_chunk_handlers(game_chunk_handlers); - -} diff --git a/src/saveload/upstream/gamelog_sl.cpp b/src/saveload/upstream/gamelog_sl.cpp deleted file mode 100644 index 2f6e03dc28..0000000000 --- a/src/saveload/upstream/gamelog_sl.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file gamelog_sl.cpp Code handling saving and loading of gamelog data */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/gamelog_sl_compat.h" - -#include "../../gamelog_internal.h" -#include "../../fios.h" -#include "../../string_func.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -class SlGamelogMode : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(LoggedChange, mode.mode, SLE_UINT8), - SLE_VAR(LoggedChange, mode.landscape, SLE_UINT8), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_mode_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_MODE) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_MODE) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -static char old_revision_text[GAMELOG_REVISION_LENGTH]; -static std::string revision_test; - -class SlGamelogRevision : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLEG_CONDARR("revision.text", old_revision_text, SLE_UINT8, GAMELOG_REVISION_LENGTH, SL_MIN_VERSION, SLV_STRING_GAMELOG), - SLEG_CONDSSTR("revision.text", revision_test, SLE_STR, SLV_STRING_GAMELOG, SL_MAX_VERSION), - SLE_VAR(LoggedChange, revision.newgrf, SLE_UINT32), - SLE_VAR(LoggedChange, revision.slver, SLE_UINT16), - SLE_VAR(LoggedChange, revision.modified, SLE_UINT8), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_revision_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_REVISION) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_REVISION) return; - SlObject(lc, this->GetLoadDescription()); - if (IsSavegameVersionBefore(SLV_STRING_GAMELOG)) { - lc->revision.text = stredup(old_revision_text, lastof(old_revision_text)); - } else { - lc->revision.text = stredup(revision_test.c_str()); - } - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogOldver : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(LoggedChange, oldver.type, SLE_UINT32), - SLE_VAR(LoggedChange, oldver.version, SLE_UINT32), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_oldver_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_OLDVER) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_OLDVER) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogSetting : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_STR(LoggedChange, setting.name, SLE_STR, 128), - SLE_VAR(LoggedChange, setting.oldval, SLE_INT32), - SLE_VAR(LoggedChange, setting.newval, SLE_INT32), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_setting_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_SETTING) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_SETTING) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogGrfadd : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(LoggedChange, grfadd.grfid, SLE_UINT32 ), - SLE_ARR(LoggedChange, grfadd.md5sum, SLE_UINT8, 16), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_grfadd_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFADD) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFADD) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogGrfrem : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(LoggedChange, grfrem.grfid, SLE_UINT32), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_grfrem_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFREM) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFREM) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogGrfcompat : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(LoggedChange, grfcompat.grfid, SLE_UINT32 ), - SLE_ARR(LoggedChange, grfcompat.md5sum, SLE_UINT8, 16), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_grfcompat_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFCOMPAT) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFCOMPAT) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogGrfparam : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(LoggedChange, grfparam.grfid, SLE_UINT32), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_grfparam_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFPARAM) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFPARAM) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogGrfmove : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(LoggedChange, grfmove.grfid, SLE_UINT32), - SLE_VAR(LoggedChange, grfmove.offset, SLE_INT32), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_grfmove_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFMOVE) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFMOVE) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogGrfbug : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(LoggedChange, grfbug.data, SLE_UINT64), - SLE_VAR(LoggedChange, grfbug.grfid, SLE_UINT32), - SLE_VAR(LoggedChange, grfbug.bug, SLE_UINT8), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_grfbug_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFBUG) return; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_GRFBUG) return; - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -static bool _is_emergency_save = true; - -class SlGamelogEmergency : public DefaultSaveLoadHandler { -public: - /* We need to store something, so store a "true" value. */ - inline static const SaveLoad description[] = { - SLEG_CONDVAR("is_emergency_save", _is_emergency_save, SLE_BOOL, SLV_RIFF_TO_ARRAY, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_emergency_sl_compat; - - void Save(LoggedChange *lc) const override - { - if (lc->ct != GLCT_EMERGENCY) return; - - _is_emergency_save = true; - SlObject(lc, this->GetDescription()); - } - - void Load(LoggedChange *lc) const override - { - if (lc->ct != GLCT_EMERGENCY) return; - - SlObject(lc, this->GetLoadDescription()); - } - - void LoadCheck(LoggedChange *lc) const override { this->Load(lc); } -}; - -class SlGamelogAction : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_SAVEBYTE(LoggedChange, ct), - SLEG_STRUCT("mode", SlGamelogMode), - SLEG_STRUCT("revision", SlGamelogRevision), - SLEG_STRUCT("oldver", SlGamelogOldver), - SLEG_STRUCT("setting", SlGamelogSetting), - SLEG_STRUCT("grfadd", SlGamelogGrfadd), - SLEG_STRUCT("grfrem", SlGamelogGrfrem), - SLEG_STRUCT("grfcompat", SlGamelogGrfcompat), - SLEG_STRUCT("grfparam", SlGamelogGrfparam), - SLEG_STRUCT("grfmove", SlGamelogGrfmove), - SLEG_STRUCT("grfbug", SlGamelogGrfbug), - SLEG_STRUCT("emergency", SlGamelogEmergency), - }; - inline const static SaveLoadCompatTable compat_description = _gamelog_action_sl_compat; - - void Save(LoggedAction *la) const override - { - SlSetStructListLength(la->changes); - - const LoggedChange *lcend = &la->change[la->changes]; - for (LoggedChange *lc = la->change; lc != lcend; lc++) { - assert((uint)lc->ct < GLCT_END); - SlObject(lc, this->GetDescription()); - } - } - - void Load(LoggedAction *la) const override - { - if (IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY)) { - byte type; - while ((type = SlReadByte()) != GLCT_NONE) { - if (type >= GLCT_END) SlErrorCorrupt("Invalid gamelog change type"); - GamelogChangeType ct = (GamelogChangeType)type; - - la->change = ReallocT(la->change, la->changes + 1); - - LoggedChange *lc = &la->change[la->changes++]; - memset(lc, 0, sizeof(*lc)); - lc->ct = ct; - - SlObject(lc, this->GetLoadDescription()); - } - return; - } - - size_t length = SlGetStructListLength(UINT32_MAX); - la->change = ReallocT(la->change, length); - la->changes = (uint32)length; - - for (size_t i = 0; i < length; i++) { - LoggedChange *lc = &la->change[i]; - memset(lc, 0, sizeof(*lc)); - - lc->ct = (GamelogChangeType)SlReadByte(); - SlObject(lc, this->GetLoadDescription()); - } - } - - void LoadCheck(LoggedAction *la) const override { this->Load(la); } -}; - -static const SaveLoad _gamelog_desc[] = { - SLE_CONDVAR(LoggedAction, at, SLE_UINT8, SLV_RIFF_TO_ARRAY, SL_MAX_VERSION), - SLE_CONDVAR(LoggedAction, tick, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SLV_U64_TICK_COUNTER), - SLE_CONDVAR(LoggedAction, tick, SLE_UINT64, SLV_U64_TICK_COUNTER, SL_MAX_VERSION), - SLEG_STRUCTLIST("action", SlGamelogAction), -}; - -struct GLOGChunkHandler : ChunkHandler { - GLOGChunkHandler() : ChunkHandler('GLOG', CH_TABLE) {} - - void LoadCommon(LoggedAction *&gamelog_action, uint &gamelog_actions) const - { - assert(gamelog_action == nullptr); - assert(gamelog_actions == 0); - - const std::vector slt = SlCompatTableHeader(_gamelog_desc, _gamelog_sl_compat); - - if (IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY)) { - byte type; - while ((type = SlReadByte()) != GLAT_NONE) { - if (type >= GLAT_END) SlErrorCorrupt("Invalid gamelog action type"); - - gamelog_action = ReallocT(gamelog_action, gamelog_actions + 1); - LoggedAction *la = &gamelog_action[gamelog_actions++]; - memset(la, 0, sizeof(*la)); - - la->at = (GamelogActionType)type; - SlObject(la, slt); - } - return; - } - - while (SlIterateArray() != -1) { - gamelog_action = ReallocT(gamelog_action, gamelog_actions + 1); - LoggedAction *la = &gamelog_action[gamelog_actions++]; - memset(la, 0, sizeof(*la)); - - SlObject(la, slt); - } - } - - void Save() const override - { - SlTableHeader(_gamelog_desc); - - const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; - - uint i = 0; - for (LoggedAction *la = _gamelog_action; la != laend; la++, i++) { - SlSetArrayIndex(i); - SlObject(la, _gamelog_desc); - } - } - - void Load() const override - { - this->LoadCommon(_gamelog_action, _gamelog_actions); - } - - void LoadCheck(size_t) const override - { - this->LoadCommon(_load_check_data.gamelog_action, _load_check_data.gamelog_actions); - } -}; - -static const GLOGChunkHandler GLOG; -static const ChunkHandlerRef gamelog_chunk_handlers[] = { - GLOG, -}; - -extern const ChunkHandlerTable _gamelog_chunk_handlers(gamelog_chunk_handlers); - -} diff --git a/src/saveload/upstream/industry_sl.cpp b/src/saveload/upstream/industry_sl.cpp deleted file mode 100644 index dbf2b983f2..0000000000 --- a/src/saveload/upstream/industry_sl.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file industry_sl.cpp Code handling saving and loading of industries */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/industry_sl_compat.h" - -#include "../../industry.h" -#include "newgrf_sl.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -static const int THIS_MONTH = 0; -static const int LAST_MONTH = 1; - -struct ProducedHistory { - uint16_t production; ///< Total produced - uint16_t transported; ///< Total transported - - uint8_t PctTransported() const - { - if (this->production == 0) return 0; - return ClampTo(this->transported * 256 / this->production); - } -}; - -struct ProducedCargo { - CargoID cargo; ///< Cargo type - uint16_t waiting; ///< Amount of cargo produced - uint8_t rate; ///< Production rate - std::array history; ///< History of cargo produced and transported -}; - -struct AcceptedCargo { - CargoID cargo; ///< Cargo type - uint16_t waiting; ///< Amount of cargo waiting to processed - Date last_accepted; ///< Last day cargo was accepted by this industry -}; - -class SlIndustryAccepted : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(AcceptedCargo, cargo, SLE_UINT8), - SLE_VAR(AcceptedCargo, waiting, SLE_UINT16), - SLE_VAR(AcceptedCargo, last_accepted, SLE_INT32), - }; - inline const static SaveLoadCompatTable compat_description = {}; - - void Save(Industry *i) const override - { - NOT_REACHED(); - } - - void Load(Industry *i) const override - { - size_t len = SlGetStructListLength(INDUSTRY_NUM_INPUTS); - - for (size_t j = 0; j < len; j++) { - AcceptedCargo a = {}; - SlObject(&a, this->GetDescription()); - if (j < INDUSTRY_NUM_INPUTS) { - i->accepts_cargo[j] = a.cargo; - i->incoming_cargo_waiting[j] = a.waiting; - i->last_cargo_accepted_at[j] = a.last_accepted; - } - } - } -}; - -class SlIndustryProducedHistory : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(ProducedHistory, production, SLE_UINT16), - SLE_VAR(ProducedHistory, transported, SLE_UINT16), - }; - inline const static SaveLoadCompatTable compat_description = {}; - - void Save(ProducedCargo *p) const override - { - NOT_REACHED(); - } - - void Load(ProducedCargo *p) const override - { - size_t len = SlGetStructListLength(p->history.size()); - - for (auto &h : p->history) { - if (--len > p->history.size()) break; // unsigned so wraps after hitting zero. - SlObject(&h, this->GetDescription()); - } - } -}; - -class SlIndustryProduced : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(ProducedCargo, cargo, SLE_UINT8), - SLE_VAR(ProducedCargo, waiting, SLE_UINT16), - SLE_VAR(ProducedCargo, rate, SLE_UINT8), - SLEG_STRUCTLIST("history", SlIndustryProducedHistory), - }; - inline const static SaveLoadCompatTable compat_description = {}; - - void Save(Industry *i) const override - { - NOT_REACHED(); - } - - void Load(Industry *i) const override - { - size_t len = SlGetStructListLength(INDUSTRY_NUM_OUTPUTS); - - for (size_t j = 0; j < len; j++) { - ProducedCargo p = {}; - SlObject(&p, this->GetDescription()); - if (j < INDUSTRY_NUM_OUTPUTS) { - i->produced_cargo[j] = p.cargo; - i->produced_cargo_waiting[j] = p.waiting; - i->production_rate[j] = p.rate; - i->this_month_production[j] = p.history[THIS_MONTH].production; - i->this_month_transported[j] = p.history[THIS_MONTH].transported; - i->last_month_production[j] = p.history[LAST_MONTH].production; - i->last_month_transported[j] = p.history[LAST_MONTH].transported; - i->last_month_pct_transported[j] = p.history[LAST_MONTH].PctTransported(); - } - } - } -}; - -static OldPersistentStorage _old_ind_persistent_storage; - -static const SaveLoad _industry_desc[] = { - SLE_CONDVAR(Industry, location.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Industry, location.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_VAR(Industry, location.w, SLE_FILE_U8 | SLE_VAR_U16), - SLE_VAR(Industry, location.h, SLE_FILE_U8 | SLE_VAR_U16), - SLE_REF(Industry, town, REF_TOWN), - SLE_CONDREF(Industry, neutral_station, REF_STATION, SLV_SERVE_NEUTRAL_INDUSTRIES, SL_MAX_VERSION), - SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDARR(Industry, production_rate, SLE_UINT8, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, production_rate, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_VAR(Industry, prod_level, SLE_UINT8), - SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - - SLE_VAR(Industry, counter, SLE_UINT16), - - SLE_VAR(Industry, type, SLE_UINT8), - SLE_VAR(Industry, owner, SLE_UINT8), - SLE_VAR(Industry, random_colour, SLE_UINT8), - SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8), - SLE_CONDVAR(Industry, ctlflags, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION), - - SLE_CONDVAR(Industry, founder, SLE_UINT8, SLV_70, SL_MAX_VERSION), - SLE_CONDVAR(Industry, construction_date, SLE_INT32, SLV_70, SL_MAX_VERSION), - SLE_CONDVAR(Industry, construction_type, SLE_UINT8, SLV_70, SL_MAX_VERSION), - SLE_CONDVAR(Industry, last_cargo_accepted_at[0], SLE_INT32, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), - SLE_CONDARR(Industry, last_cargo_accepted_at, SLE_INT32, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE), - SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, SLV_73, SL_MAX_VERSION), - SLE_CONDVAR(Industry, exclusive_supplier, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION), - SLE_CONDVAR(Industry, exclusive_consumer, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION), - - SLEG_CONDARR("storage", _old_ind_persistent_storage.storage, SLE_UINT32, 16, SLV_76, SLV_161), - SLE_CONDREF(Industry, psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), - - SLE_CONDVAR(Industry, random, SLE_UINT16, SLV_82, SL_MAX_VERSION), - SLE_CONDSSTR(Industry, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_INDUSTRY_TEXT, SL_MAX_VERSION), - - SLEG_CONDSTRUCTLIST("accepted", SlIndustryAccepted, SLV_INDUSTRY_CARGO_REORGANISE, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST("produced", SlIndustryProduced, SLV_INDUSTRY_CARGO_REORGANISE, SL_MAX_VERSION), -}; - -struct INDYChunkHandler : ChunkHandler { - INDYChunkHandler() : ChunkHandler('INDY', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_industry_desc); - - /* Write the industries */ - for (Industry *ind : Industry::Iterate()) { - SlSetArrayIndex(ind->index); - SlObject(ind, _industry_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_industry_desc, _industry_sl_compat); - - int index; - - Industry::ResetIndustryCounts(); - - while ((index = SlIterateArray()) != -1) { - Industry *i = new (index) Industry(); - SlObject(i, slt); - - /* Before savegame version 161, persistent storages were not stored in a pool. */ - if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_76)) { - /* Store the old persistent storage. The GRFID will be added later. */ - assert(PersistentStorage::CanAllocateItem()); - i->psa = new PersistentStorage(0, 0, 0); - memcpy(i->psa->storage, _old_ind_persistent_storage.storage, sizeof(_old_ind_persistent_storage.storage)); - } - Industry::IncIndustryTypeCount(i->type); - } - } - - void FixPointers() const override - { - for (Industry *i : Industry::Iterate()) { - SlObject(i, _industry_desc); - } - } -}; - -struct IIDSChunkHandler : NewGRFMappingChunkHandler { - IIDSChunkHandler() : NewGRFMappingChunkHandler('IIDS', _industry_mngr) {} -}; - -struct TIDSChunkHandler : NewGRFMappingChunkHandler { - TIDSChunkHandler() : NewGRFMappingChunkHandler('TIDS', _industile_mngr) {} -}; - -/** Description of the data to save and load in #IndustryBuildData. */ -static const SaveLoad _industry_builder_desc[] = { - SLEG_VAR("wanted_inds", _industry_builder.wanted_inds, SLE_UINT32), -}; - -/** Industry builder. */ -struct IBLDChunkHandler : ChunkHandler { - IBLDChunkHandler() : ChunkHandler('IBLD', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_industry_builder_desc); - - SlSetArrayIndex(0); - SlGlobList(_industry_builder_desc); - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_industry_builder_desc, _industry_builder_sl_compat); - - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; - SlGlobList(slt); - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many IBLD entries"); - } -}; - -/** Description of the data to save and load in #IndustryTypeBuildData. */ -static const SaveLoad _industrytype_builder_desc[] = { - SLE_VAR(IndustryTypeBuildData, probability, SLE_UINT32), - SLE_VAR(IndustryTypeBuildData, min_number, SLE_UINT8), - SLE_VAR(IndustryTypeBuildData, target_count, SLE_UINT16), - SLE_VAR(IndustryTypeBuildData, max_wait, SLE_UINT16), - SLE_VAR(IndustryTypeBuildData, wait_count, SLE_UINT16), -}; - -/** Industry-type build data. */ -struct ITBLChunkHandler : ChunkHandler { - ITBLChunkHandler() : ChunkHandler('ITBL', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_industrytype_builder_desc); - - for (int i = 0; i < NUM_INDUSTRYTYPES; i++) { - SlSetArrayIndex(i); - SlObject(_industry_builder.builddata + i, _industrytype_builder_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_industrytype_builder_desc, _industrytype_builder_sl_compat); - - for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) { - _industry_builder.builddata[it].Reset(); - } - int index; - while ((index = SlIterateArray()) != -1) { - if ((uint)index >= NUM_INDUSTRYTYPES) SlErrorCorrupt("Too many industry builder datas"); - SlObject(_industry_builder.builddata + index, slt); - } - } -}; - -static const INDYChunkHandler INDY; -static const IIDSChunkHandler IIDS; -static const TIDSChunkHandler TIDS; -static const IBLDChunkHandler IBLD; -static const ITBLChunkHandler ITBL; -static const ChunkHandlerRef industry_chunk_handlers[] = { - INDY, - IIDS, - TIDS, - IBLD, - ITBL, -}; - -extern const ChunkHandlerTable _industry_chunk_handlers(industry_chunk_handlers); - -} diff --git a/src/saveload/upstream/labelmaps_sl.cpp b/src/saveload/upstream/labelmaps_sl.cpp deleted file mode 100644 index 9bc1acdb2f..0000000000 --- a/src/saveload/upstream/labelmaps_sl.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file labelmaps_sl.cpp Code handling saving and loading of rail type label mappings */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/labelmaps_sl_compat.h" - -#include "../../station_map.h" -#include "../../tunnelbridge_map.h" - -#include "../../safeguards.h" - -void ResetLabelMaps(); - -extern std::vector _railtype_list; - -namespace upstream_sl { - -/** Container for a label for SaveLoad system */ -struct LabelObject { - uint32 label; -}; - -static const SaveLoad _label_object_desc[] = { - SLE_VAR(LabelObject, label, SLE_UINT32), -}; - -struct RAILChunkHandler : ChunkHandler { - RAILChunkHandler() : ChunkHandler('RAIL', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_label_object_desc); - - LabelObject lo; - - for (RailType r = RAILTYPE_BEGIN; r != RAILTYPE_END; r++) { - lo.label = GetRailTypeInfo(r)->label; - - SlSetArrayIndex(r); - SlObject(&lo, _label_object_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_label_object_desc, _label_object_sl_compat); - - ResetLabelMaps(); - - LabelObject lo; - - while (SlIterateArray() != -1) { - SlObject(&lo, slt); - _railtype_list.push_back((RailTypeLabel)lo.label); - } - } -}; - -static const RAILChunkHandler RAIL; -static const ChunkHandlerRef labelmaps_chunk_handlers[] = { - RAIL, -}; - -extern const ChunkHandlerTable _labelmaps_chunk_handlers(labelmaps_chunk_handlers); - -} diff --git a/src/saveload/upstream/league_sl.cpp b/src/saveload/upstream/league_sl.cpp deleted file mode 100644 index d15e7aab01..0000000000 --- a/src/saveload/upstream/league_sl.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file league_sl.cpp Code handling saving and loading of league tables */ - -#include "../../stdafx.h" - -#include "saveload.h" - -#include "../../league_base.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -static const SaveLoad _league_table_elements_desc[] = { - SLE_VAR(LeagueTableElement, table, SLE_UINT8), - SLE_CONDVAR(LeagueTableElement, rating, SLE_FILE_U64 | SLE_VAR_I64, SL_MIN_VERSION, SLV_LINKGRAPH_EDGES), - SLE_CONDVAR(LeagueTableElement, rating, SLE_INT64, SLV_LINKGRAPH_EDGES, SL_MAX_VERSION), - SLE_VAR(LeagueTableElement, company, SLE_UINT8), - SLE_SSTR(LeagueTableElement, text, SLE_STR | SLF_ALLOW_CONTROL), - SLE_SSTR(LeagueTableElement, score, SLE_STR | SLF_ALLOW_CONTROL), - SLE_VAR(LeagueTableElement, link.type, SLE_UINT8), - SLE_VAR(LeagueTableElement, link.target, SLE_UINT32), -}; - -struct LEAEChunkHandler : ChunkHandler { - LEAEChunkHandler() : ChunkHandler('LEAE', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_league_table_elements_desc); - - for (LeagueTableElement *lte : LeagueTableElement::Iterate()) { - SlSetArrayIndex(lte->index); - SlObject(lte, _league_table_elements_desc); - } - } - - void Load() const override - { - const std::vector slt = SlTableHeader(_league_table_elements_desc); - - int index; - while ((index = SlIterateArray()) != -1) { - LeagueTableElement *lte = new (index) LeagueTableElement(); - SlObject(lte, slt); - } - } -}; - -static const SaveLoad _league_tables_desc[] = { - SLE_SSTR(LeagueTable, title, SLE_STR | SLF_ALLOW_CONTROL), - SLE_SSTR(LeagueTable, header, SLE_STR | SLF_ALLOW_CONTROL), - SLE_SSTR(LeagueTable, footer, SLE_STR | SLF_ALLOW_CONTROL), -}; - -struct LEATChunkHandler : ChunkHandler { - LEATChunkHandler() : ChunkHandler('LEAT', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_league_tables_desc); - - for (LeagueTable *lt : LeagueTable::Iterate()) { - SlSetArrayIndex(lt->index); - SlObject(lt, _league_tables_desc); - } - } - - void Load() const override - { - const std::vector slt = SlTableHeader(_league_tables_desc); - - int index; - while ((index = SlIterateArray()) != -1) { - LeagueTable *lt = new (index) LeagueTable(); - SlObject(lt, slt); - } - } -}; - -static const LEAEChunkHandler LEAE; -static const LEATChunkHandler LEAT; -static const ChunkHandlerRef league_chunk_handlers[] = { - LEAE, - LEAT, -}; - -extern const ChunkHandlerTable _league_chunk_handlers(league_chunk_handlers); - -} diff --git a/src/saveload/upstream/linkgraph_sl.cpp b/src/saveload/upstream/linkgraph_sl.cpp deleted file mode 100644 index 28d4637842..0000000000 --- a/src/saveload/upstream/linkgraph_sl.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file linkgraph_sl.cpp Code handling saving and loading of link graphs */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/linkgraph_sl_compat.h" - -#include "../../linkgraph/linkgraph.h" -#include "../../linkgraph/linkgraphjob.h" -#include "../../linkgraph/linkgraphschedule.h" -#include "../../network/network.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -typedef LinkGraph::BaseNode Node; -typedef LinkGraph::BaseEdge Edge; - -static uint16 _num_nodes; -static LinkGraph *_linkgraph; ///< Contains the current linkgraph being saved/loaded. -static NodeID _linkgraph_from; ///< Contains the current "from" node being saved/loaded. -static NodeID _edge_dest_node; -static NodeID _edge_next_edge; - -class SlLinkgraphEdge : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(Edge, capacity, SLE_UINT32), - SLE_VAR(Edge, usage, SLE_UINT32), - SLE_CONDVAR(Edge, travel_time_sum, SLE_UINT64, SLV_LINKGRAPH_TRAVEL_TIME, SL_MAX_VERSION), - SLE_VAR(Edge, last_unrestricted_update, SLE_INT32), - SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION), - SLEG_VAR("dest_node", _edge_dest_node, SLE_UINT16), - SLEG_CONDVAR("next_edge", _edge_next_edge, SLE_UINT16, SL_MIN_VERSION, SLV_LINKGRAPH_EDGES), - }; - inline const static SaveLoadCompatTable compat_description = _linkgraph_edge_sl_compat; - - void Save(Node *bn) const override - { - NOT_REACHED(); - } - - void Load(Node *bn) const override - { - uint16 max_size = _linkgraph->Size(); - - if (IsSavegameVersionBefore(SLV_191)) { - NOT_REACHED(); - } - - if (IsSavegameVersionBefore(SLV_LINKGRAPH_EDGES)) { - size_t used_size = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? max_size : SlGetStructListLength(UINT16_MAX); - - /* ... but as that wasted a lot of space we save a sparse matrix now. */ - for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _edge_next_edge) { - if (used_size == 0) SlErrorCorrupt("Link graph structure overflow"); - used_size--; - - if (to >= max_size) SlErrorCorrupt("Link graph structure overflow"); - SlObject(&_linkgraph->edges[std::make_pair(_linkgraph_from, to)], this->GetLoadDescription()); - } - - if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) && used_size > 0) SlErrorCorrupt("Corrupted link graph"); - } else { - /* Edge data is now a simple vector and not any kind of matrix. */ - size_t size = SlGetStructListLength(UINT16_MAX); - for (size_t i = 0; i < size; i++) { - Edge edge; - SlObject(&edge, this->GetLoadDescription()); - if (_edge_dest_node >= max_size) SlErrorCorrupt("Link graph structure overflow"); - _linkgraph->edges[std::make_pair(_linkgraph_from, _edge_dest_node)] = edge; - } - } - } -}; - -class SlLinkgraphNode : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_CONDVAR(Node, xy, SLE_UINT32, SLV_191, SL_MAX_VERSION), - SLE_VAR(Node, supply, SLE_UINT32), - SLE_VAR(Node, demand, SLE_UINT32), - SLE_VAR(Node, station, SLE_UINT16), - SLE_VAR(Node, last_update, SLE_INT32), - SLEG_STRUCTLIST("edges", SlLinkgraphEdge), - }; - inline const static SaveLoadCompatTable compat_description = _linkgraph_node_sl_compat; - - void Save(LinkGraph *lg) const override - { - _linkgraph = lg; - - SlSetStructListLength(lg->Size()); - for (NodeID from = 0; from < lg->Size(); ++from) { - _linkgraph_from = from; - SlObject(&lg->nodes[from], this->GetDescription()); - } - } - - void Load(LinkGraph *lg) const override - { - _linkgraph = lg; - - uint16 length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _num_nodes : (uint16)SlGetStructListLength(UINT16_MAX); - lg->Init(length); - for (NodeID from = 0; from < length; ++from) { - _linkgraph_from = from; - SlObject(&lg->nodes[from], this->GetLoadDescription()); - } - } -}; - -/** - * Get a SaveLoad array for a link graph. - * @return SaveLoad array for link graph. - */ -SaveLoadTable GetLinkGraphDesc() -{ - static const SaveLoad link_graph_desc[] = { - SLE_VAR(LinkGraph, last_compression, SLE_INT32), - SLEG_CONDVAR("num_nodes", _num_nodes, SLE_UINT16, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), - SLE_VAR(LinkGraph, cargo, SLE_UINT8), - SLEG_STRUCTLIST("nodes", SlLinkgraphNode), - }; - return link_graph_desc; -} - -/** - * Proxy to reuse LinkGraph to save/load a LinkGraphJob. - * One of the members of a LinkGraphJob is a LinkGraph, but SLEG_STRUCT() - * doesn't allow us to select a member. So instead, we add a bit of glue to - * accept a LinkGraphJob, get the LinkGraph, and use that to call the - * save/load routines for a regular LinkGraph. - */ -class SlLinkgraphJobProxy : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = {{}}; // Needed to keep DefaultSaveLoadHandler happy. - SaveLoadTable GetDescription() const override { return GetLinkGraphDesc(); } - inline const static SaveLoadCompatTable compat_description = _linkgraph_sl_compat; - - void Save(LinkGraphJob *lgj) const override - { - SlObject(const_cast(&lgj->Graph()), this->GetDescription()); - } - - void Load(LinkGraphJob *lgj) const override - { - SlObject(const_cast(&lgj->Graph()), this->GetLoadDescription()); - } -}; - -/** - * Get a SaveLoad array for a link graph job. The settings struct is derived from - * the global settings saveload array. The exact entries are calculated when the function - * is called the first time. - * It's necessary to keep a copy of the settings for each link graph job so that you can - * change the settings while in-game and still not mess with current link graph runs. - * Of course the settings have to be saved and loaded, too, to avoid desyncs. - * @return Array of SaveLoad structs. - */ -SaveLoadTable GetLinkGraphJobDesc() -{ - static const SaveLoad job_desc[] = { - SLE_VAR2(LinkGraphJob, "linkgraph.recalc_interval", settings.recalc_interval, SLE_UINT16), - SLE_VAR2(LinkGraphJob, "linkgraph.recalc_time", settings.recalc_time, SLE_UINT16), - SLE_VAR2(LinkGraphJob, "linkgraph.distribution_pax", settings.distribution_pax, SLE_UINT8), - SLE_VAR2(LinkGraphJob, "linkgraph.distribution_mail", settings.distribution_mail, SLE_UINT8), - SLE_VAR2(LinkGraphJob, "linkgraph.distribution_armoured", settings.distribution_armoured, SLE_UINT8), - SLE_VAR2(LinkGraphJob, "linkgraph.distribution_default", settings.distribution_default, SLE_UINT8), - SLE_VAR2(LinkGraphJob, "linkgraph.accuracy", settings.accuracy, SLE_UINT8), - SLE_VAR2(LinkGraphJob, "linkgraph.demand_distance", settings.demand_distance, SLE_UINT8), - SLE_VAR2(LinkGraphJob, "linkgraph.demand_size", settings.demand_size, SLE_UINT8), - SLE_VAR2(LinkGraphJob, "linkgraph.short_path_saturation", settings.short_path_saturation, SLE_UINT8), - - SLE_VAR2(LinkGraphJob, "join_date", join_date_ticks, SLE_INT32), - SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16), - SLEG_STRUCT("linkgraph", SlLinkgraphJobProxy), - }; - - return job_desc; -} - -/** - * Get a SaveLoad array for the link graph schedule. - * @return SaveLoad array for the link graph schedule. - */ -SaveLoadTable GetLinkGraphScheduleDesc() -{ - static const SaveLoad schedule_desc[] = { - SLE_REFLIST(LinkGraphSchedule, schedule, REF_LINK_GRAPH), - SLE_REFLIST(LinkGraphSchedule, running, REF_LINK_GRAPH_JOB), - }; - return schedule_desc; -} - -/** - * Spawn the threads for running link graph calculations. - * Has to be done after loading as the cargo classes might have changed. - */ -void AfterLoadLinkGraphs() -{ - if (IsSavegameVersionBefore(SLV_191)) { - for (LinkGraph *lg : LinkGraph::Iterate()) { - for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) { - const Station *st = Station::GetIfValid((*lg)[node_id].Station()); - if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy); - } - } - - for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { - LinkGraph *lg = &(const_cast(lgj->Graph())); - for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) { - const Station *st = Station::GetIfValid((*lg)[node_id].Station()); - if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy); - } - } - } - for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { - GetLinkGraphJobDayLengthScaleAfterLoad(lgj); - } - - LinkGraphSchedule::instance.SpawnAll(); - - if (!_networking || _network_server) { - AfterLoad_LinkGraphPauseControl(); - } -} - -/** - * All link graphs. - */ -struct LGRPChunkHandler : ChunkHandler { - LGRPChunkHandler() : ChunkHandler('LGRP', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(GetLinkGraphDesc()); - - for (LinkGraph *lg : LinkGraph::Iterate()) { - SlSetArrayIndex(lg->index); - SlObject(lg, GetLinkGraphDesc()); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(GetLinkGraphDesc(), _linkgraph_sl_compat); - - int index; - while ((index = SlIterateArray()) != -1) { - LinkGraph *lg = new (index) LinkGraph(); - SlObject(lg, slt); - } - } -}; - -/** - * All link graph jobs. - */ -struct LGRJChunkHandler : ChunkHandler { - LGRJChunkHandler() : ChunkHandler('LGRJ', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(GetLinkGraphJobDesc()); - - for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { - SlSetArrayIndex(lgj->index); - SlObject(lgj, GetLinkGraphJobDesc()); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(GetLinkGraphJobDesc(), _linkgraph_job_sl_compat); - - int index; - while ((index = SlIterateArray()) != -1) { - LinkGraphJob *lgj = new (index) LinkGraphJob(); - SlObject(lgj, slt); - } - } -}; - -/** - * Link graph schedule. - */ -struct LGRSChunkHandler : ChunkHandler { - LGRSChunkHandler() : ChunkHandler('LGRS', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(GetLinkGraphScheduleDesc()); - - SlSetArrayIndex(0); - SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(GetLinkGraphScheduleDesc(), _linkgraph_schedule_sl_compat); - - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; - SlObject(&LinkGraphSchedule::instance, slt); - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many LGRS entries"); - } - - void FixPointers() const override - { - SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); - } -}; - -static const LGRPChunkHandler LGRP; -static const LGRJChunkHandler LGRJ; -static const LGRSChunkHandler LGRS; -static const ChunkHandlerRef linkgraph_chunk_handlers[] = { - LGRP, - LGRJ, - LGRS, -}; - -extern const ChunkHandlerTable _linkgraph_chunk_handlers(linkgraph_chunk_handlers); - -} diff --git a/src/saveload/upstream/map_sl.cpp b/src/saveload/upstream/map_sl.cpp deleted file mode 100644 index ba477d8f76..0000000000 --- a/src/saveload/upstream/map_sl.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file map_sl.cpp Code handling saving and loading of map */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/map_sl_compat.h" - -#include "../../map_func.h" -#include "../../core/bitmath_func.hpp" -#include "../../fios.h" -#include - -#include "../../safeguards.h" - -namespace upstream_sl { - -static uint32 _map_dim_x; -static uint32 _map_dim_y; - -static const SaveLoad _map_desc[] = { - SLEG_CONDVAR("dim_x", _map_dim_x, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLEG_CONDVAR("dim_y", _map_dim_y, SLE_UINT32, SLV_6, SL_MAX_VERSION), -}; - -struct MAPSChunkHandler : ChunkHandler { - MAPSChunkHandler() : ChunkHandler('MAPS', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_map_desc); - - _map_dim_x = MapSizeX(); - _map_dim_y = MapSizeY(); - - SlSetArrayIndex(0); - SlGlobList(_map_desc); - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_map_desc, _map_sl_compat); - - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; - SlGlobList(slt); - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many MAPS entries"); - - AllocateMap(_map_dim_x, _map_dim_y); - } - - void LoadCheck(size_t) const override - { - const std::vector slt = SlCompatTableHeader(_map_desc, _map_sl_compat); - - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; - SlGlobList(slt); - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many MAPS entries"); - - _load_check_data.map_size_x = _map_dim_x; - _load_check_data.map_size_y = _map_dim_y; - } -}; - -static const uint MAP_SL_BUF_SIZE = 4096; - -struct MAPTChunkHandler : ChunkHandler { - MAPTChunkHandler() : ChunkHandler('MAPT', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type = buf[j]; - } - } - - void Save() const override - { - std::array 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; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - } - } -}; - -struct MAPHChunkHandler : ChunkHandler { - MAPHChunkHandler() : ChunkHandler('MAPH', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; - } - } - - void Save() const override - { - std::array 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++].height; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - } - } -}; - -struct MAPOChunkHandler : ChunkHandler { - MAPOChunkHandler() : ChunkHandler('MAPO', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; - } - } - - void Save() const override - { - std::array 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; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - } - } -}; - -struct MAP2ChunkHandler : ChunkHandler { - MAP2ChunkHandler() : ChunkHandler('MAP2', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, - /* In those versions the m2 was 8 bits */ - IsSavegameVersionBefore(SLV_5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16 - ); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j]; - } - } - - void Save() const override - { - std::array 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; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); - } - } -}; - -struct M3LOChunkHandler : ChunkHandler { - M3LOChunkHandler() : ChunkHandler('M3LO', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; - } - } - - void Save() const override - { - std::array 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; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - } - } -}; - -struct M3HIChunkHandler : ChunkHandler { - M3HIChunkHandler() : ChunkHandler('M3HI', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; - } - } - - void Save() const override - { - std::array 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; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - } - } -}; - -struct MAP5ChunkHandler : ChunkHandler { - MAP5ChunkHandler() : ChunkHandler('MAP5', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; - } - } - - void Save() const override - { - std::array 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; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - } - } -}; - -struct MAPEChunkHandler : ChunkHandler { - MAPEChunkHandler() : ChunkHandler('MAPE', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - if (IsSavegameVersionBefore(SLV_42)) { - for (TileIndex i = 0; i != size;) { - /* 1024, otherwise we overflow on 64x64 maps! */ - SlCopy(buf.data(), 1024, SLE_UINT8); - for (uint j = 0; j != 1024; j++) { - _me[i++].m6 = GB(buf[j], 0, 2); - _me[i++].m6 = GB(buf[j], 2, 2); - _me[i++].m6 = GB(buf[j], 4, 2); - _me[i++].m6 = GB(buf[j], 6, 2); - } - } - } else { - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m6 = buf[j]; - } - } - } - - void Save() const override - { - std::array 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++].m6; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - } - } -}; - -struct MAP7ChunkHandler : ChunkHandler { - MAP7ChunkHandler() : ChunkHandler('MAP7', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; - } - } - - void Save() const override - { - std::array 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; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - } - } -}; - -struct MAP8ChunkHandler : ChunkHandler { - MAP8ChunkHandler() : ChunkHandler('MAP8', CH_RIFF) {} - - void Load() const override - { - std::array buf; - TileIndex size = MapSize(); - - for (TileIndex i = 0; i != size;) { - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m8 = buf[j]; - } - } - - void Save() const override - { - std::array 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] = _me[i++].m8; - SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); - } - } -}; - -static const MAPSChunkHandler MAPS; -static const MAPTChunkHandler MAPT; -static const MAPHChunkHandler MAPH; -static const MAPOChunkHandler MAPO; -static const MAP2ChunkHandler MAP2; -static const M3LOChunkHandler M3LO; -static const M3HIChunkHandler M3HI; -static const MAP5ChunkHandler MAP5; -static const MAPEChunkHandler MAPE; -static const MAP7ChunkHandler MAP7; -static const MAP8ChunkHandler MAP8; -static const ChunkHandlerRef map_chunk_handlers[] = { - MAPS, - MAPT, - MAPH, - MAPO, - MAP2, - M3LO, - M3HI, - MAP5, - MAPE, - MAP7, - MAP8, -}; - -extern const ChunkHandlerTable _map_chunk_handlers(map_chunk_handlers); - -} diff --git a/src/saveload/upstream/misc_sl.cpp b/src/saveload/upstream/misc_sl.cpp deleted file mode 100644 index 683c2527e9..0000000000 --- a/src/saveload/upstream/misc_sl.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file misc_sl.cpp Saving and loading of things that didn't fit anywhere else */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/misc_sl_compat.h" - -#include "../../date_func.h" -#include "../../zoom_func.h" -#include "../../window_gui.h" -#include "../../window_func.h" -#include "../../viewport_func.h" -#include "../../gfx_func.h" -#include "../../core/random_func.hpp" -#include "../../fios.h" -#include "../../timer/timer.h" -#include "../../timer/timer_game_tick.h" - -#include "../../safeguards.h" - -extern TileIndex _cur_tileloop_tile; -extern TileIndex _aux_tileloop_tile; -extern uint16 _disaster_delay; -extern byte _trees_tick_ctr; - -/* Keep track of current game position */ -extern int _saved_scrollpos_x; -extern int _saved_scrollpos_y; -extern ZoomLevel _saved_scrollpos_zoom; - -extern byte _age_cargo_skip_counter; ///< Skip aging of cargo? Used before savegame version 162. -extern TimeoutTimer _new_competitor_timeout; - -namespace upstream_sl { - -static const SaveLoad _date_desc[] = { - SLEG_CONDVAR("date", _date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLEG_CONDVAR("date", _date, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLEG_VAR("date_fract", _date_fract, SLE_UINT16), - SLEG_CONDVAR("tick_counter", _tick_counter, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SLV_U64_TICK_COUNTER), - SLEG_CONDVAR("tick_counter", _tick_counter, SLE_UINT64, SLV_U64_TICK_COUNTER, SL_MAX_VERSION), - SLEG_CONDVAR("age_cargo_skip_counter", _age_cargo_skip_counter, SLE_UINT8, SL_MIN_VERSION, SLV_162), - SLEG_CONDVAR("cur_tileloop_tile", _cur_tileloop_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLEG_CONDVAR("cur_tileloop_tile", _cur_tileloop_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLEG_VAR("next_disaster_start", _disaster_delay, SLE_UINT16), - SLEG_VAR("random_state[0]", _random.state[0], SLE_UINT32), - SLEG_VAR("random_state[1]", _random.state[1], SLE_UINT32), - SLEG_VAR("company_tick_counter", _cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32), - SLEG_VAR("trees_tick_counter", _trees_tick_ctr, SLE_UINT8), - SLEG_CONDVAR("pause_mode", _pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION), - /* For older savegames, we load the current value as the "period"; afterload will set the "fired" and "elapsed". */ - SLEG_CONDVAR("next_competitor_start", _new_competitor_timeout.period, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109), - SLEG_CONDVAR("next_competitor_start", _new_competitor_timeout.period, SLE_UINT32, SLV_109, SLV_AI_START_DATE), - SLEG_CONDVAR("competitors_interval", _new_competitor_timeout.period, SLE_UINT32, SLV_AI_START_DATE, SL_MAX_VERSION), - SLEG_CONDVAR("competitors_interval_elapsed", _new_competitor_timeout.storage.elapsed, SLE_UINT32, SLV_AI_START_DATE, SL_MAX_VERSION), - SLEG_CONDVAR("competitors_interval_fired", _new_competitor_timeout.fired, SLE_BOOL, SLV_AI_START_DATE, SL_MAX_VERSION), -}; - -static const SaveLoad _date_check_desc[] = { - SLEG_CONDVAR("date", _load_check_data.current_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLEG_CONDVAR("date", _load_check_data.current_date, SLE_INT32, SLV_31, SL_MAX_VERSION), -}; - -/* Save load date related variables as well as persistent tick counters - * XXX: currently some unrelated stuff is just put here */ -struct DATEChunkHandler : ChunkHandler { - DATEChunkHandler() : ChunkHandler('DATE', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_date_desc); - - SlSetArrayIndex(0); - SlGlobList(_date_desc); - } - - void LoadCommon(const SaveLoadTable &slt, const SaveLoadCompatTable &slct) const - { - const std::vector oslt = SlCompatTableHeader(slt, slct); - - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; - SlGlobList(oslt); - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many DATE entries"); - } - - void Load() const override - { - this->LoadCommon(_date_desc, _date_sl_compat); - } - - - void LoadCheck(size_t) const override - { - this->LoadCommon(_date_check_desc, _date_check_sl_compat); - - if (IsSavegameVersionBefore(SLV_31)) { - _load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR; - } - } -}; - -static const SaveLoad _view_desc[] = { - SLEG_CONDVAR("x", _saved_scrollpos_x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLEG_CONDVAR("x", _saved_scrollpos_x, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLEG_CONDVAR("y", _saved_scrollpos_y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLEG_CONDVAR("y", _saved_scrollpos_y, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLEG_VAR("zoom", _saved_scrollpos_zoom, SLE_UINT8), -}; - -struct VIEWChunkHandler : ChunkHandler { - VIEWChunkHandler() : ChunkHandler('VIEW', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_view_desc); - - SlSetArrayIndex(0); - SlGlobList(_view_desc); - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_view_desc, _view_sl_compat); - - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return; - SlGlobList(slt); - if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many DATE entries"); - } -}; - -static const DATEChunkHandler DATE; -static const VIEWChunkHandler VIEW; -static const ChunkHandlerRef misc_chunk_handlers[] = { - DATE, - VIEW, -}; - -extern const ChunkHandlerTable _misc_chunk_handlers(misc_chunk_handlers); - -} diff --git a/src/saveload/upstream/newgrf_sl.cpp b/src/saveload/upstream/newgrf_sl.cpp deleted file mode 100644 index 84179cfaa7..0000000000 --- a/src/saveload/upstream/newgrf_sl.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file newgrf_sl.cpp Code handling saving and loading of newgrf config */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/newgrf_sl_compat.h" - -#include "newgrf_sl.h" -#include "../../fios.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -/** Save and load the mapping between a spec and the NewGRF it came from. */ -static const SaveLoad _newgrf_mapping_desc[] = { - SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), - SLE_CONDVAR(EntityIDMapping, entity_id, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_EXTEND_ENTITY_MAPPING), - SLE_CONDVAR(EntityIDMapping, entity_id, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION), - SLE_CONDVAR(EntityIDMapping, substitute_id, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_EXTEND_ENTITY_MAPPING), - SLE_CONDVAR(EntityIDMapping, substitute_id, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION), -}; - -/** - * Save a GRF ID + local id -> OpenTTD's id mapping. - */ -void NewGRFMappingChunkHandler::Save() const -{ - // removed - NOT_REACHED(); -} - -/** - * Load a GRF ID + local id -> OpenTTD's id mapping. - */ -void NewGRFMappingChunkHandler::Load() const -{ - const std::vector slt = SlCompatTableHeader(_newgrf_mapping_desc, _newgrf_mapping_sl_compat); - - /* Clear the current mapping stored. - * This will create the manager if ever it is not yet done */ - this->mapping.ResetMapping(); - - uint max_id = this->mapping.GetMaxMapping(); - - int index; - while ((index = SlIterateArray()) != -1) { - if ((uint)index >= max_id) SlErrorCorrupt("Too many NewGRF entity mappings"); - SlObject(&this->mapping.mappings[index], slt); - } -} - - -static const SaveLoad _grfconfig_desc[] = { - SLE_SSTR(GRFConfig, filename, SLE_STR), - SLE_VAR(GRFConfig, ident.grfid, SLE_UINT32), - SLE_ARR(GRFConfig, ident.md5sum, SLE_UINT8, 16), - SLE_CONDVAR(GRFConfig, version, SLE_UINT32, SLV_151, SL_MAX_VERSION), - SLE_ARR(GRFConfig, param, SLE_UINT32, 0x80), - SLE_VAR(GRFConfig, num_params, SLE_UINT8), - SLE_CONDVAR(GRFConfig, palette, SLE_UINT8, SLV_101, SL_MAX_VERSION), -}; - - -struct NGRFChunkHandler : ChunkHandler { - NGRFChunkHandler() : ChunkHandler('NGRF', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_grfconfig_desc); - - int index = 0; - - for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) { - if (HasBit(c->flags, GCF_STATIC) || HasBit(c->flags, GCF_INIT_ONLY)) continue; - SlSetArrayIndex(index++); - SlObject(c, _grfconfig_desc); - } - } - - - void LoadCommon(GRFConfig *&grfconfig) const - { - const std::vector slt = SlCompatTableHeader(_grfconfig_desc, _grfconfig_sl_compat); - - ClearGRFConfigList(&grfconfig); - while (SlIterateArray() != -1) { - GRFConfig *c = new GRFConfig(); - SlObject(c, slt); - if (IsSavegameVersionBefore(SLV_101)) c->SetSuitablePalette(); - AppendToGRFConfigList(&grfconfig, c); - } - } - - void Load() const override - { - this->LoadCommon(_grfconfig); - - if (_game_mode == GM_MENU) { - /* Intro game must not have NewGRF. */ - if (_grfconfig != nullptr) SlErrorCorrupt("The intro game must not use NewGRF"); - - /* Activate intro NewGRFs (townnames) */ - ResetGRFConfig(false); - } else { - /* Append static NewGRF configuration */ - AppendStaticGRFConfigs(&_grfconfig); - } - } - - void LoadCheck(size_t) const override - { - this->LoadCommon(_load_check_data.grfconfig); - } -}; - -static const NGRFChunkHandler NGRF; -static const ChunkHandlerRef newgrf_chunk_handlers[] = { - NGRF, -}; - -extern const ChunkHandlerTable _newgrf_chunk_handlers(newgrf_chunk_handlers); - -} diff --git a/src/saveload/upstream/order_sl.cpp b/src/saveload/upstream/order_sl.cpp deleted file mode 100644 index d7172da9ef..0000000000 --- a/src/saveload/upstream/order_sl.cpp +++ /dev/null @@ -1,273 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file order_sl.cpp Code handling saving and loading of orders */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/order_sl_compat.h" - -#include "../../order_backup.h" -#include "../../order_base.h" -#include "../../settings_type.h" -#include "../../network/network.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -/** - * 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.IsType(OT_NOTHING) && packed != 0) order.MakeDummy(); - - return order; -} - -SaveLoadTable GetOrderDescription() -{ - static const SaveLoad _order_desc[] = { - SLE_VAR(Order, type, SLE_UINT8), - SLE_VAR(Order, flags, SLE_FILE_U8 | SLE_VAR_U16), - SLE_VAR(Order, dest, SLE_UINT16), - SLE_REF(Order, next, REF_ORDER), - SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION), - SLE_CONDVAR(Order, wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), - SLE_CONDVAR(Order, travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), - SLE_CONDVAR(Order, max_speed, SLE_UINT16, SLV_172, SL_MAX_VERSION), - }; - - return _order_desc; -} - -struct ORDRChunkHandler : ChunkHandler { - ORDRChunkHandler() : ChunkHandler('ORDR', CH_TABLE) {} - - void Save() const override - { - const SaveLoadTable slt = GetOrderDescription(); - SlTableHeader(slt); - - for (Order *order : Order::Iterate()) { - SlSetArrayIndex(order->index); - SlObject(order, slt); - } - } - - void Load() const override - { - if (IsSavegameVersionBefore(SLV_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(); - - if (IsSavegameVersionBefore(SLV_5)) { - /* Pre-version 5 had another layout for orders - * (uint16 instead of uint32) */ - len /= sizeof(uint16); - uint16 *orders = MallocT(len + 1); - - SlCopy(orders, len, SLE_UINT16); - - for (size_t i = 0; i < len; ++i) { - Order *o = new (i) Order(); - o->AssignOrder(UnpackVersion4Order(orders[i])); - } - - free(orders); - } else if (IsSavegameVersionBefore(SLV_5, 2)) { - len /= sizeof(uint32); - uint32 *orders = MallocT(len + 1); - - SlCopy(orders, len, SLE_UINT32); - - for (size_t i = 0; i < len; ++i) { - new (i) Order(orders[i]); - } - - free(orders); - } - - /* Update all the next pointer */ - for (Order *o : Order::Iterate()) { - size_t order_index = o->index; - /* Delete invalid orders */ - if (o->IsType(OT_NOTHING)) { - delete o; - continue; - } - /* The orders were built like this: - * While the order is valid, set the previous will get its next pointer set */ - Order *prev = Order::GetIfValid(order_index - 1); - if (prev != nullptr) prev->next = o; - } - } else { - const std::vector slt = SlCompatTableHeader(GetOrderDescription(), _order_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - Order *order = new (index) Order(); - SlObject(order, slt); - } - } - } - - void FixPointers() const override - { - /* Orders from old savegames have pointers corrected in Load_ORDR */ - if (IsSavegameVersionBefore(SLV_5, 2)) return; - - for (Order *o : Order::Iterate()) { - SlObject(o, GetOrderDescription()); - } - } -}; - -SaveLoadTable GetOrderListDescription() -{ - static const SaveLoad _orderlist_desc[] = { - SLE_REF(OrderList, first, REF_ORDER), - }; - - return _orderlist_desc; -} - -struct ORDLChunkHandler : ChunkHandler { - ORDLChunkHandler() : ChunkHandler('ORDL', CH_TABLE) {} - - void Save() const override - { - const SaveLoadTable slt = GetOrderListDescription(); - SlTableHeader(slt); - - for (OrderList *list : OrderList::Iterate()) { - SlSetArrayIndex(list->index); - SlObject(list, slt); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(GetOrderListDescription(), _orderlist_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - /* set num_orders to 0 so it's a valid OrderList */ - OrderList *list = new (index) OrderList(0); - SlObject(list, slt); - } - - } - - void FixPointers() const override - { - for (OrderList *list : OrderList::Iterate()) { - SlObject(list, GetOrderListDescription()); - } - } -}; - -SaveLoadTable GetOrderBackupDescription() -{ - static const SaveLoad _order_backup_desc[] = { - SLE_VAR(OrderBackup, user, SLE_UINT32), - SLE_VAR(OrderBackup, tile, SLE_UINT32), - SLE_VAR(OrderBackup, group, SLE_UINT16), - SLE_CONDVAR(OrderBackup, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SL_MIN_VERSION, SLV_192), - SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION), - SLE_SSTR(OrderBackup, name, SLE_STR), - SLE_CONDREF(OrderBackup, clone, REF_VEHICLE, SLV_192, SL_MAX_VERSION), - SLE_VAR(OrderBackup, cur_real_order_index, SLE_FILE_U8 | SLE_VAR_U16), - SLE_CONDVAR(OrderBackup, cur_implicit_order_index, SLE_FILE_U8 | SLE_VAR_U16, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR(OrderBackup, lateness_counter, SLE_INT32, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR(OrderBackup, timetable_start, SLE_INT32, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_176, SLV_180), - SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION), - SLE_REF(OrderBackup, orders, REF_ORDER), - }; - - return _order_backup_desc; -} - -struct BKORChunkHandler : ChunkHandler { - BKORChunkHandler() : ChunkHandler('BKOR', CH_TABLE) {} - - void Save() const override - { - const SaveLoadTable slt = GetOrderBackupDescription(); - SlTableHeader(slt); - - /* We only save this when we're a network server - * as we want this information on our clients. For - * normal games this information isn't needed. */ - if (!_networking || !_network_server) return; - - for (OrderBackup *ob : OrderBackup::Iterate()) { - SlSetArrayIndex(ob->index); - SlObject(ob, slt); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(GetOrderBackupDescription(), _order_backup_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - /* set num_orders to 0 so it's a valid OrderList */ - OrderBackup *ob = new (index) OrderBackup(); - SlObject(ob, slt); - if (ob->cur_real_order_index == 0xFF) ob->cur_real_order_index = INVALID_VEH_ORDER_ID; - if (ob->cur_implicit_order_index == 0xFF) ob->cur_implicit_order_index = INVALID_VEH_ORDER_ID; - } - } - - void FixPointers() const override - { - for (OrderBackup *ob : OrderBackup::Iterate()) { - SlObject(ob, GetOrderBackupDescription()); - } - } -}; - -static const BKORChunkHandler BKOR; -static const ORDRChunkHandler ORDR; -static const ORDLChunkHandler ORDL; -static const ChunkHandlerRef order_chunk_handlers[] = { - BKOR, - ORDR, - ORDL, -}; - -extern const ChunkHandlerTable _order_chunk_handlers(order_chunk_handlers); - -} diff --git a/src/saveload/upstream/saveload.cpp b/src/saveload/upstream/saveload.cpp deleted file mode 100644 index b3b5b146ff..0000000000 --- a/src/saveload/upstream/saveload.cpp +++ /dev/null @@ -1,2143 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** - * @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 "saveload.h" -#include "../../debug.h" -#include "../../string_func.h" -#include "../../strings_func.h" -#include "../../core/bitmath_func.hpp" -#include "../../vehicle_base.h" -#include "../../station_base.h" -#include "../../linkgraph/linkgraph.h" -#include "../../linkgraph/linkgraphjob.h" -#include "../../town.h" -#include "../../roadstop_base.h" -#include "../../autoreplace_base.h" - -#include -#include -#include -#include -#include -#include - -#include "../../safeguards.h" - -StringID RemapOldStringID(StringID s); -std::string CopyFromOldName(StringID id); - -namespace upstream_sl { - -/** What are we currently doing? */ -enum SaveLoadAction { - SLA_LOAD, ///< loading - SLA_SAVE, ///< saving - SLA_PTRS, ///< fixing pointers - SLA_NULL, ///< null all pointers (on loading error) - SLA_LOAD_CHECK, ///< partial loading into #_load_check_data -}; - -enum NeedLength { - NL_NONE = 0, ///< not working in NeedLength mode - NL_WANTLENGTH = 1, ///< writing length and data - NL_CALCLENGTH = 2, ///< need to calculate the length -}; - -/** The saveload struct, containing reader-writer functions, buffer, version, etc. */ -struct SaveLoadParams { - SaveLoadAction action; ///< are we doing a save or a load atm. - NeedLength need_length; ///< working in NeedLength (Autolength) mode? - byte block_mode; ///< ??? - - 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 - bool expect_table_header; ///< In the case of a table, if the header is saved/loaded. -}; - -static SaveLoadParams _sl; ///< Parameters used for/at saveload. - -static const std::vector &ChunkHandlers() -{ - /* These define the chunks */ - extern const ChunkHandlerTable _gamelog_chunk_handlers; - extern const ChunkHandlerTable _map_chunk_handlers; - extern const ChunkHandlerTable _misc_chunk_handlers; - //extern const ChunkHandlerTable _name_chunk_handlers; - extern const ChunkHandlerTable _cheat_chunk_handlers; - extern const ChunkHandlerTable _setting_chunk_handlers; - extern const ChunkHandlerTable _company_chunk_handlers; - extern const ChunkHandlerTable _engine_chunk_handlers; - extern const ChunkHandlerTable _veh_chunk_handlers; - //extern const ChunkHandlerTable _waypoint_chunk_handlers; - extern const ChunkHandlerTable _depot_chunk_handlers; - extern const ChunkHandlerTable _order_chunk_handlers; - extern const ChunkHandlerTable _town_chunk_handlers; - extern const ChunkHandlerTable _sign_chunk_handlers; - extern const ChunkHandlerTable _station_chunk_handlers; - extern const ChunkHandlerTable _industry_chunk_handlers; - extern const ChunkHandlerTable _economy_chunk_handlers; - extern const ChunkHandlerTable _subsidy_chunk_handlers; - extern const ChunkHandlerTable _cargomonitor_chunk_handlers; - extern const ChunkHandlerTable _goal_chunk_handlers; - extern const ChunkHandlerTable _story_page_chunk_handlers; - extern const ChunkHandlerTable _league_chunk_handlers; - extern const ChunkHandlerTable _ai_chunk_handlers; - extern const ChunkHandlerTable _game_chunk_handlers; - extern const ChunkHandlerTable _animated_tile_chunk_handlers; - extern const ChunkHandlerTable _newgrf_chunk_handlers; - extern const ChunkHandlerTable _group_chunk_handlers; - extern const ChunkHandlerTable _cargopacket_chunk_handlers; - extern const ChunkHandlerTable _autoreplace_chunk_handlers; - extern const ChunkHandlerTable _labelmaps_chunk_handlers; - extern const ChunkHandlerTable _linkgraph_chunk_handlers; - extern const ChunkHandlerTable _airport_chunk_handlers; - extern const ChunkHandlerTable _object_chunk_handlers; - extern const ChunkHandlerTable _persistent_storage_chunk_handlers; - - /** List of all chunks in a savegame. */ - static const ChunkHandlerTable _chunk_handler_tables[] = { - _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, - _cargomonitor_chunk_handlers, - _goal_chunk_handlers, - _story_page_chunk_handlers, - _league_chunk_handlers, - _engine_chunk_handlers, - _town_chunk_handlers, - _sign_chunk_handlers, - _station_chunk_handlers, - _company_chunk_handlers, - _ai_chunk_handlers, - _game_chunk_handlers, - _animated_tile_chunk_handlers, - _newgrf_chunk_handlers, - _group_chunk_handlers, - _cargopacket_chunk_handlers, - _autoreplace_chunk_handlers, - _labelmaps_chunk_handlers, - _linkgraph_chunk_handlers, - _airport_chunk_handlers, - _object_chunk_handlers, - _persistent_storage_chunk_handlers, - }; - - static std::vector _chunk_handlers; - - if (_chunk_handlers.empty()) { - for (auto &chunk_handler_table : _chunk_handler_tables) { - for (auto &chunk_handler : chunk_handler_table) { - _chunk_handlers.push_back(chunk_handler); - } - } - } - - return _chunk_handlers; -} - -/** Null all pointers (convert index -> nullptr) */ -void SlNullPointers() -{ - _sl.action = SLA_NULL; - - /* We don't want any savegame conversion code to run - * during NULLing; especially those that try to get - * pointers from other pools. */ - _sl_version = SAVEGAME_VERSION; - - for (const ChunkHandler &ch : ChunkHandlers()) { - DEBUG(sl, 3, "Nulling pointers for %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); - ch.FixPointers(); - } - - assert(_sl.action == SLA_NULL); -} - -/** - * 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)) { - i &= ~0x10; - if (HasBit(i, 3)) { - SlErrorCorrupt("Unsupported gamma"); - } - i = SlReadByte(); // 32 bits only. - } - 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 - * 11110--- xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - * We could extend the scheme ad infinum to support arbitrarily - * large chunks, but as sizeof(size_t) == 4 is still very common - * we don't support anything above 32 bits. That's why in the last - * case the 3 most significant bits are unused. - * @param i Index being written - */ - -static void SlWriteSimpleGamma(size_t i) -{ - if (i >= (1 << 7)) { - if (i >= (1 << 14)) { - if (i >= (1 << 21)) { - if (i >= (1 << 28)) { - assert(i <= UINT32_MAX); // We can only support 32 bits for now. - SlWriteByte((byte)(0xF0)); - SlWriteByte((byte)(i >> 24)); - } else { - 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)) + (i >= (1 << 28)); -} - -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); -} - -/** - * Return the type as saved/loaded inside the savegame. - */ -static uint8 GetSavegameFileType(const SaveLoad &sld) -{ - switch (sld.cmd) { - case SL_VAR: - return GetVarFileType(sld.conv); break; - - case SL_STR: - case SL_STDSTR: - case SL_ARR: - case SL_VECTOR: - case SL_DEQUE: - return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break; - - case SL_REF: - return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32; - - case SL_REFLIST: - case SL_REFDEQUE: - case SL_REFVEC: - return (IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32) | SLE_FILE_HAS_LENGTH_FIELD; - - case SL_SAVEBYTE: - return SLE_FILE_U8; - - case SL_STRUCT: - case SL_STRUCTLIST: - return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD; - - default: NOT_REACHED(); - } -} - -/** - * 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 uint SlCalcConvMemLen(VarType conv) -{ - static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0}; - - switch (GetVarMemType(conv)) { - case SLE_VAR_STRB: - case SLE_VAR_STR: - case SLE_VAR_STRQ: - return SlReadArrayLength(); - - default: - uint8 type = GetVarMemType(conv) >> 4; - assert(type < lengthof(conv_mem_size)); - return conv_mem_size[type]; - } -} - -/** - * 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[] = {0, 1, 1, 2, 2, 4, 4, 8, 8, 2}; - - uint8 type = GetVarFileType(conv); - assert(type < lengthof(conv_file_size)); - return conv_file_size[type]; -} - -/** Return the size in bytes of a reference (pointer) */ -static inline size_t SlCalcRefLen() -{ - return IsSavegameVersionBefore(SLV_69) ? 2 : 4; -} - -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 && SlGetBytesRead() != _next_offs) SlErrorCorrupt("Invalid chunk size"); - - for (;;) { - uint length = SlReadArrayLength(); - if (length == 0) { - assert(!_sl.expect_table_header); - _next_offs = 0; - return -1; - } - - _sl.obj_len = --length; - _next_offs = SlGetBytesRead() + length; - - if (_sl.expect_table_header) { - _sl.expect_table_header = false; - return INT32_MAX; - } - - switch (_sl.block_mode) { - case CH_SPARSE_TABLE: - case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break; - case CH_TABLE: - case CH_ARRAY: index = _sl.array_index++; break; - default: - DEBUG(sl, 0, "SlIterateArray error"); - return -1; // error - } - - if (length != 0) return index; - } -} - -/** - * Skip an array or sparse array - */ -void SlSkipArray() -{ - while (SlIterateArray() != -1) { - SlSkipBytes(_next_offs - SlGetBytesRead()); - } -} - -/** - * 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.action == SLA_SAVE); - - switch (_sl.need_length) { - case NL_WANTLENGTH: - _sl.need_length = NL_NONE; - if ((_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) && _sl.expect_table_header) { - _sl.expect_table_header = false; - SlWriteArrayLength(length + 1); - break; - } - - 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_TABLE: - 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_TABLE: - 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; - - default: NOT_REACHED(); - } -} - -/** - * 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; - - switch (_sl.action) { - case SLA_LOAD_CHECK: - case SLA_LOAD: - for (; length != 0; length--) *p++ = SlReadByte(); - break; - case SLA_SAVE: - for (; length != 0; length--) SlWriteByte(*p++); - break; - default: NOT_REACHED(); - } -} - -/** 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 (*(const bool *)ptr != 0); - case SLE_VAR_I8: return *(const int8 *)ptr; - case SLE_VAR_U8: return *(const byte *)ptr; - case SLE_VAR_I16: return *(const int16 *)ptr; - case SLE_VAR_U16: return *(const uint16*)ptr; - case SLE_VAR_I32: return *(const int32 *)ptr; - case SLE_VAR_U32: return *(const uint32*)ptr; - case SLE_VAR_I64: return *(const int64 *)ptr; - case SLE_VAR_U64: return *(const uint64*)ptr; - case SLE_VAR_NULL:return 0; - default: NOT_REACHED(); - } -} - -/** - * 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: *reinterpret_cast(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) -{ - switch (_sl.action) { - case SLA_SAVE: { - int64 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(); - } - break; - } - case SLA_LOAD_CHECK: - case SLA_LOAD: { - int64 x; - /* 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); - break; - } - case SLA_PTRS: break; - case SLA_NULL: break; - default: NOT_REACHED(); - } -} - -/** - * 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 == nullptr) return 0; - return std::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 * const *)ptr; - len = SIZE_MAX; - break; - case SLE_VAR_STRB: - str = (const char *)ptr; - len = length; - break; - } - - len = SlCalcNetStringLen(str, len); - return len + SlGetArrayLength(len); // also include the length of the index -} - -/** - * 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 \c std::string. - * @return The gross length of the string. - */ -static inline size_t SlCalcStdStringLen(const void *ptr) -{ - const std::string *str = reinterpret_cast(ptr); - - size_t len = str->length(); - 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) -{ - switch (_sl.action) { - case SLA_SAVE: { - size_t len; - switch (GetVarMemType(conv)) { - default: NOT_REACHED(); - case SLE_VAR_STRB: - 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); - break; - } - case SLA_LOAD_CHECK: - case SLA_LOAD: { - size_t len = SlReadArrayLength(); - - switch (GetVarMemType(conv)) { - default: NOT_REACHED(); - case SLE_VAR_NULL: - SlSkipBytes(len); - return; - case SLE_VAR_STRB: - 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 = nullptr; - return; - } else { - *(char **)ptr = MallocT(len + 1); // terminating '\0' - ptr = *(char **)ptr; - SlCopyBytes(ptr, len); - } - break; - } - - ((char *)ptr)[len] = '\0'; // properly terminate the string - StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK; - if ((conv & SLF_ALLOW_CONTROL) != 0) { - settings = settings | SVS_ALLOW_CONTROL_CODE; - if (IsSavegameVersionBefore(SLV_169)) { - str_fix_scc_encoded((char *)ptr, (char *)ptr + len); - } - } - if ((conv & SLF_ALLOW_NEWLINE) != 0) { - settings = settings | SVS_ALLOW_NEWLINE; - } - StrMakeValidInPlace((char *)ptr, (char *)ptr + len, settings); - break; - } - case SLA_PTRS: break; - case SLA_NULL: break; - default: NOT_REACHED(); - } -} - -/** - * Save/Load a \c std::string. - * @param ptr the string being manipulated - * @param conv must be SLE_FILE_STRING - */ -static void SlStdString(void *ptr, VarType conv) -{ - std::string *str = reinterpret_cast(ptr); - - switch (_sl.action) { - case SLA_SAVE: { - size_t len = str->length(); - SlWriteArrayLength(len); - SlCopyBytes(const_cast(static_cast(str->c_str())), len); - break; - } - - case SLA_LOAD_CHECK: - case SLA_LOAD: { - size_t len = SlReadArrayLength(); - if (GetVarMemType(conv) == SLE_VAR_NULL) { - SlSkipBytes(len); - return; - } - - char *buf = AllocaM(char, len + 1); - SlCopyBytes(buf, len); - buf[len] = '\0'; // properly terminate the string - - StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK; - if ((conv & SLF_ALLOW_CONTROL) != 0) { - settings = settings | SVS_ALLOW_CONTROL_CODE; - if (IsSavegameVersionBefore(SLV_169)) { - str_fix_scc_encoded(buf, buf + len); - } - } - if ((conv & SLF_ALLOW_NEWLINE) != 0) { - settings = settings | SVS_ALLOW_NEWLINE; - } - - StrMakeValidInPlace(buf, buf + len, settings); - - // Store sanitized string. - str->assign(buf); - } - - case SLA_PTRS: break; - case SLA_NULL: break; - default: NOT_REACHED(); - } -} - -/** - * Internal function to save/Load a list of SL_VARs. - * SlCopy() and SlArray() are very similar, with the exception of the header. - * This function represents the common part. - * @param object The object being manipulated. - * @param length The length of the object in elements - * @param conv VarType type of the items. - */ -static void SlCopyInternal(void *object, size_t length, VarType conv) -{ - if (GetVarMemType(conv) == SLE_VAR_NULL) { - assert(_sl.action != SLA_SAVE); // Use SL_NULL if you want to write null-bytes - SlSkipBytes(length * SlCalcConvFileLen(conv)); - return; - } - - /* NOTICE - handle some buggy stuff, in really old versions everything was saved - * as a byte-type. So detect this, and adjust object size accordingly */ - if (_sl.action != SLA_SAVE && _sl_version == 0) { - /* all objects except difficulty settings */ - if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID || - conv == SLE_INT32 || conv == SLE_UINT32) { - SlCopyBytes(object, 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*)object)[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(object, length); - } else { - byte *a = (byte*)object; - byte mem_size = SlCalcConvMemLen(conv); - - for (; length != 0; length --) { - SlSaveLoadConv(a, conv); - a += mem_size; // get size - } - } -} - -/** - * Copy a list of SL_VARs to/from a savegame. - * These entries are copied as-is, and you as caller have to make sure things - * like length-fields are calculated correctly. - * @param object The object being manipulated. - * @param length The length of the object in elements - * @param conv VarType type of the items. - */ -void SlCopy(void *object, size_t length, VarType conv) -{ - if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return; - - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(length * SlCalcConvFileLen(conv)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; - } - - SlCopyInternal(object, length, conv); -} - -/** - * 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 + SlGetArrayLength(length); -} - -/** - * Save/Load the length of the array followed by the array of SL_VAR elements. - * @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.) - */ -static void SlArray(void *array, size_t length, VarType conv) -{ - switch (_sl.action) { - case SLA_SAVE: - SlWriteArrayLength(length); - SlCopyInternal(array, length, conv); - return; - - case SLA_LOAD_CHECK: - case SLA_LOAD: { - if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { - size_t sv_length = SlReadArrayLength(); - if (GetVarMemType(conv) == SLE_VAR_NULL) { - /* We don't know this field, so we assume the length in the savegame is correct. */ - length = sv_length; - } else if (sv_length != length) { - /* If the SLE_ARR changes size, a savegame bump is required - * and the developer should have written conversion lines. - * Error out to make this more visible. */ - SlErrorCorrupt("Fixed-length array is of wrong length"); - } - } - - SlCopyInternal(array, length, conv); - return; - } - - case SLA_PTRS: - case SLA_NULL: - return; - - default: - NOT_REACHED(); - } -} - -/** - * 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 nullptr item has value 0, and all - * indices 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 size_t ReferenceToInt(const void *obj, SLRefType rt) -{ - assert(_sl.action == SLA_SAVE); - - if (obj == nullptr) return 0; - - switch (rt) { - case REF_VEHICLE_OLD: // Old vehicles we save as new ones - 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; - case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1; - case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1; - case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1; - default: NOT_REACHED(); - } -} - -/** - * Pointers cannot be loaded from a savegame, so this function - * gets the index from the savegame and returns the appropriate - * pointer from the already loaded base. - * Remember that an index of 0 is a nullptr pointer so all indices - * 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(size_t index, SLRefType rt) -{ - static_assert(sizeof(size_t) <= sizeof(void *)); - - assert(_sl.action == SLA_PTRS); - - /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE, - * and should be loaded like that */ - if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(SLV_4, 4)) { - rt = REF_VEHICLE; - } - - /* No need to look up nullptr pointers, just return immediately */ - if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return nullptr; - - /* Correct index. Old vehicles were saved differently: - * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */ - if (rt != REF_VEHICLE_OLD) index--; - - switch (rt) { - case REF_ORDERLIST: - if (OrderList::IsValidID(index)) return OrderList::Get(index); - SlErrorCorrupt("Referencing invalid OrderList"); - - case REF_ORDER: - if (Order::IsValidID(index)) return Order::Get(index); - /* in old versions, invalid order was used to mark end of order list */ - if (IsSavegameVersionBefore(SLV_5, 2)) return nullptr; - SlErrorCorrupt("Referencing invalid Order"); - - case REF_VEHICLE_OLD: - case REF_VEHICLE: - if (Vehicle::IsValidID(index)) return Vehicle::Get(index); - SlErrorCorrupt("Referencing invalid Vehicle"); - - case REF_STATION: - if (Station::IsValidID(index)) return Station::Get(index); - SlErrorCorrupt("Referencing invalid Station"); - - case REF_TOWN: - if (Town::IsValidID(index)) return Town::Get(index); - SlErrorCorrupt("Referencing invalid Town"); - - case REF_ROADSTOPS: - if (RoadStop::IsValidID(index)) return RoadStop::Get(index); - SlErrorCorrupt("Referencing invalid RoadStop"); - - case REF_ENGINE_RENEWS: - if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index); - SlErrorCorrupt("Referencing invalid EngineRenew"); - - case REF_CARGO_PACKET: - if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index); - SlErrorCorrupt("Referencing invalid CargoPacket"); - - case REF_STORAGE: - if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index); - SlErrorCorrupt("Referencing invalid PersistentStorage"); - - case REF_LINK_GRAPH: - if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index); - SlErrorCorrupt("Referencing invalid LinkGraph"); - - case REF_LINK_GRAPH_JOB: - if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index); - SlErrorCorrupt("Referencing invalid LinkGraphJob"); - - default: NOT_REACHED(); - } -} - -/** - * Handle conversion for references. - * @param ptr The object being filled/read. - * @param conv VarType type of the current element of the struct. - */ -void SlSaveLoadRef(void *ptr, VarType conv) -{ - switch (_sl.action) { - case SLA_SAVE: - SlWriteUint32((uint32)ReferenceToInt(*(void **)ptr, (SLRefType)conv)); - break; - case SLA_LOAD_CHECK: - case SLA_LOAD: - *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); - break; - case SLA_PTRS: - *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv); - break; - case SLA_NULL: - *(void **)ptr = nullptr; - break; - default: NOT_REACHED(); - } -} - -/** - * Template class to help with list-like types. - */ -template typename Tstorage, typename Tvar, typename Tallocator = std::allocator> -class SlStorageHelper { - typedef Tstorage SlStorageT; -public: - /** - * Internal templated helper to return the size in bytes of a list-like type. - * @param storage The storage to find the size of - * @param conv VarType type of variable that is used for calculating the size - * @param cmd The SaveLoadType ware are saving/loading. - */ - static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SL_VAR) - { - assert(cmd == SL_VAR || cmd == SL_REF); - - const SlStorageT *list = static_cast(storage); - - int type_size = SlGetArrayLength(list->size()); - int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : (VarType)SLE_FILE_U32); - return list->size() * item_size + type_size; - } - - static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv) - { - switch (cmd) { - case SL_VAR: SlSaveLoadConv(item, conv); break; - case SL_REF: SlSaveLoadRef(item, conv); break; - default: - NOT_REACHED(); - } - } - - /** - * Internal templated helper to save/load a list-like type. - * @param storage The storage being manipulated. - * @param conv VarType type of variable that is used for calculating the size. - * @param cmd The SaveLoadType ware are saving/loading. - */ - static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR) - { - assert(cmd == SL_VAR || cmd == SL_REF); - - SlStorageT *list = static_cast(storage); - - switch (_sl.action) { - case SLA_SAVE: - SlWriteArrayLength(list->size()); - - for (auto &item : *list) { - SlSaveLoadMember(cmd, &item, conv); - } - break; - - case SLA_LOAD_CHECK: - case SLA_LOAD: { - size_t length; - switch (cmd) { - case SL_VAR: length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break; - case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? SlReadUint32() : SlReadArrayLength(); break; - default: NOT_REACHED(); - } - - /* Load each value and push to the end of the storage. */ - for (size_t i = 0; i < length; i++) { - Tvar &data = list->emplace_back(); - SlSaveLoadMember(cmd, &data, conv); - } - break; - } - - case SLA_PTRS: - for (auto &item : *list) { - SlSaveLoadMember(cmd, &item, conv); - } - break; - - case SLA_NULL: - list->clear(); - break; - - default: NOT_REACHED(); - } - } -}; - -/** - * Return the size in bytes of a list. - * @param list The std::list to find the size of. - * @param conv VarType type of variable that is used for calculating the size. - */ -static inline size_t SlCalcRefListLen(const void *list, VarType conv) -{ - return SlStorageHelper::SlCalcLen(list, conv, SL_REF); -} - -/** - * Return the size in bytes of a deque. - * @param list The std::list to find the size of. - * @param conv VarType type of variable that is used for calculating the size. - */ -static inline size_t SlCalcRefDequeLen(const void *list, VarType conv) -{ - return SlStorageHelper::SlCalcLen(list, conv, SL_REF); -} - -/** - * Return the size in bytes of a vector. - * @param list The std::list to find the size of. - * @param conv VarType type of variable that is used for calculating the size. - */ -static inline size_t SlCalcRefVectorLen(const void *list, VarType conv) -{ - return SlStorageHelper::SlCalcLen(list, conv, SL_REF); -} - -/** - * Save/Load a list. - * @param list The list being manipulated. - * @param conv VarType type of variable that is used for calculating the size. - */ -static void SlRefList(void *list, VarType conv) -{ - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcRefListLen(list, conv)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; - } - - SlStorageHelper::SlSaveLoad(list, conv, SL_REF); -} - -/** - * Save/Load a deque. - * @param list The list being manipulated. - * @param conv VarType type of variable that is used for calculating the size. - */ -static void SlRefDeque(void *list, VarType conv) -{ - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcRefDequeLen(list, conv)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; - } - - SlStorageHelper::SlSaveLoad(list, conv, SL_REF); -} - -/** - * Save/Load a deque. - * @param list The list being manipulated. - * @param conv VarType type of variable that is used for calculating the size. - */ -static void SlRefVector(void *list, VarType conv) -{ - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcRefVectorLen(list, conv)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; - } - - SlStorageHelper::SlSaveLoad(list, conv, SL_REF); -} - -/** - * Return the size in bytes of a std::deque. - * @param deque The std::deque to find the size of - * @param conv VarType type of variable that is used for calculating the size - */ -static inline size_t SlCalcDequeLen(const void *deque, VarType conv) -{ - switch (GetVarMemType(conv)) { - case SLE_VAR_BL: return SlStorageHelper::SlCalcLen(deque, conv); - case SLE_VAR_I8: return SlStorageHelper::SlCalcLen(deque, conv); - case SLE_VAR_U8: return SlStorageHelper::SlCalcLen(deque, conv); - case SLE_VAR_I16: return SlStorageHelper::SlCalcLen(deque, conv); - case SLE_VAR_U16: return SlStorageHelper::SlCalcLen(deque, conv); - case SLE_VAR_I32: return SlStorageHelper::SlCalcLen(deque, conv); - case SLE_VAR_U32: return SlStorageHelper::SlCalcLen(deque, conv); - case SLE_VAR_I64: return SlStorageHelper::SlCalcLen(deque, conv); - case SLE_VAR_U64: return SlStorageHelper::SlCalcLen(deque, conv); - default: NOT_REACHED(); - } -} - -/** - * Save/load a std::deque. - * @param deque The std::deque being manipulated - * @param conv VarType type of variable that is used for calculating the size - */ -static void SlDeque(void *deque, VarType conv) -{ - switch (GetVarMemType(conv)) { - case SLE_VAR_BL: SlStorageHelper::SlSaveLoad(deque, conv); break; - case SLE_VAR_I8: SlStorageHelper::SlSaveLoad(deque, conv); break; - case SLE_VAR_U8: SlStorageHelper::SlSaveLoad(deque, conv); break; - case SLE_VAR_I16: SlStorageHelper::SlSaveLoad(deque, conv); break; - case SLE_VAR_U16: SlStorageHelper::SlSaveLoad(deque, conv); break; - case SLE_VAR_I32: SlStorageHelper::SlSaveLoad(deque, conv); break; - case SLE_VAR_U32: SlStorageHelper::SlSaveLoad(deque, conv); break; - case SLE_VAR_I64: SlStorageHelper::SlSaveLoad(deque, conv); break; - case SLE_VAR_U64: SlStorageHelper::SlSaveLoad(deque, conv); break; - default: NOT_REACHED(); - } -} - -/** - * Return the size in bytes of a std::vector. - * @param vector The std::vector to find the size of - * @param conv VarType type of variable that is used for calculating the size - */ -static inline size_t SlCalcVectorLen(const void *vector, VarType conv) -{ - switch (GetVarMemType(conv)) { - case SLE_VAR_BL: NOT_REACHED(); // Not supported - case SLE_VAR_I8: return SlStorageHelper::SlCalcLen(vector, conv); - case SLE_VAR_U8: return SlStorageHelper::SlCalcLen(vector, conv); - case SLE_VAR_I16: return SlStorageHelper::SlCalcLen(vector, conv); - case SLE_VAR_U16: return SlStorageHelper::SlCalcLen(vector, conv); - case SLE_VAR_I32: return SlStorageHelper::SlCalcLen(vector, conv); - case SLE_VAR_U32: return SlStorageHelper::SlCalcLen(vector, conv); - case SLE_VAR_I64: return SlStorageHelper::SlCalcLen(vector, conv); - case SLE_VAR_U64: return SlStorageHelper::SlCalcLen(vector, conv); - default: NOT_REACHED(); - } -} - -/** - * Save/load a std::vector. - * @param vector The std::vector being manipulated - * @param conv VarType type of variable that is used for calculating the size - */ -static void SlVector(void *vector, VarType conv) -{ - switch (GetVarMemType(conv)) { - case SLE_VAR_BL: NOT_REACHED(); // Not supported - case SLE_VAR_I8: SlStorageHelper::SlSaveLoad(vector, conv); break; - case SLE_VAR_U8: SlStorageHelper::SlSaveLoad(vector, conv); break; - case SLE_VAR_I16: SlStorageHelper::SlSaveLoad(vector, conv); break; - case SLE_VAR_U16: SlStorageHelper::SlSaveLoad(vector, conv); break; - case SLE_VAR_I32: SlStorageHelper::SlSaveLoad(vector, conv); break; - case SLE_VAR_U32: SlStorageHelper::SlSaveLoad(vector, conv); break; - case SLE_VAR_I64: SlStorageHelper::SlSaveLoad(vector, conv); break; - case SLE_VAR_U64: SlStorageHelper::SlSaveLoad(vector, conv); break; - default: NOT_REACHED(); - } -} - -/** Are we going to save this object or not? */ -static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld) -{ - return (_sl_version >= sld.version_from && _sl_version < sld.version_to); -} - -/** - * Calculate the size of the table header. - * @param slt The SaveLoad table with objects to save/load. - * @return size of given object. - */ -static size_t SlCalcTableHeader(const SaveLoadTable &slt) -{ - size_t length = 0; - - for (auto &sld : slt) { - if (!SlIsObjectValidInSavegame(sld)) continue; - - length += SlCalcConvFileLen(SLE_UINT8); - length += SlCalcStdStringLen(&sld.name); - } - - length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry. - - for (auto &sld : slt) { - if (!SlIsObjectValidInSavegame(sld)) continue; - if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) { - length += SlCalcTableHeader(sld.handler->GetDescription()); - } - } - - return length; -} - -/** - * Calculate the size of an object. - * @param object to be measured. - * @param slt The SaveLoad table with objects to save/load. - * @return size of given object. - */ -size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt) -{ - size_t length = 0; - - /* Need to determine the length and write a length tag. */ - for (auto &sld : slt) { - length += SlCalcObjMemberLength(object, sld); - } - return length; -} - -size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld) -{ - assert(_sl.action == SLA_SAVE); - - if (!SlIsObjectValidInSavegame(sld)) return 0; - - 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_REFLIST: return SlCalcRefListLen(GetVariableAddress(object, sld), sld.conv); - case SL_REFDEQUE: return SlCalcRefDequeLen(GetVariableAddress(object, sld), sld.conv); - case SL_REFVEC: return SlCalcRefVectorLen(GetVariableAddress(object, sld), sld.conv); - case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv); - case SL_VECTOR: return SlCalcVectorLen(GetVariableAddress(object, sld), sld.conv); - case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld)); - case SL_SAVEBYTE: return 1; // a byte is logically of size 1 - case SL_NULL: return SlCalcConvFileLen(sld.conv) * sld.length; - - case SL_STRUCT: - case SL_STRUCTLIST: { - NeedLength old_need_length = _sl.need_length; - size_t old_obj_len = _sl.obj_len; - - _sl.need_length = NL_CALCLENGTH; - _sl.obj_len = 0; - - /* Pretend that we are saving to collect the object size. Other - * means are difficult, as we don't know the length of the list we - * are about to store. */ - sld.handler->Save(const_cast(object)); - size_t length = _sl.obj_len; - - _sl.obj_len = old_obj_len; - _sl.need_length = old_need_length; - - if (sld.cmd == SL_STRUCT) { - length += SlGetArrayLength(1); - } - - return length; - } - - default: NOT_REACHED(); - } - return 0; -} - -/** - * Check whether the variable size of the variable in the saveload configuration - * matches with the actual variable size. - * @param sld The saveload configuration to test. - */ -[[maybe_unused]] static bool IsVariableSizeRight(const SaveLoad &sld) -{ - if (GetVarMemType(sld.conv) == SLE_VAR_NULL) return true; - - switch (sld.cmd) { - case SL_VAR: - switch (GetVarMemType(sld.conv)) { - case SLE_VAR_BL: - return sld.size == sizeof(bool); - case SLE_VAR_I8: - case SLE_VAR_U8: - return sld.size == sizeof(int8); - case SLE_VAR_I16: - case SLE_VAR_U16: - return sld.size == sizeof(int16); - case SLE_VAR_I32: - case SLE_VAR_U32: - return sld.size == sizeof(int32); - case SLE_VAR_I64: - case SLE_VAR_U64: - return sld.size == sizeof(int64); - case SLE_VAR_NAME: - return sld.size == sizeof(std::string); - default: - return sld.size == sizeof(void *); - } - case SL_REF: - /* These should all be pointer sized. */ - return sld.size == sizeof(void *); - - case SL_STR: - /* These should be pointer sized, or fixed array. */ - return sld.size == sizeof(void *) || sld.size == sld.length; - - case SL_STDSTR: - /* These should be all pointers to std::string. */ - return sld.size == sizeof(std::string); - - default: - return true; - } -} - -static bool SlObjectMember(void *object, const SaveLoad &sld) -{ - assert_msg(IsVariableSizeRight(sld), "%s, size: %u, length: %u, cmd: %u, conv: 0x%02X", sld.name.c_str(), (uint) sld.size, sld.length, sld.cmd, sld.conv); - - if (!SlIsObjectValidInSavegame(sld)) return false; - - VarType conv = GB(sld.conv, 0, 8); - switch (sld.cmd) { - case SL_VAR: - case SL_REF: - case SL_ARR: - case SL_STR: - case SL_REFLIST: - case SL_REFDEQUE: - case SL_REFVEC: - case SL_DEQUE: - case SL_VECTOR: - case SL_STDSTR: { - void *ptr = GetVariableAddress(object, sld); - - switch (sld.cmd) { - case SL_VAR: SlSaveLoadConv(ptr, conv); break; - case SL_REF: SlSaveLoadRef(ptr, conv); break; - case SL_ARR: SlArray(ptr, sld.length, conv); break; - case SL_STR: SlString(ptr, sld.length, sld.conv); break; - case SL_REFLIST: SlRefList(ptr, conv); break; - case SL_REFDEQUE: SlRefDeque(ptr, conv); break; - case SL_REFVEC: SlRefVector(ptr, conv); break; - case SL_DEQUE: SlDeque(ptr, conv); break; - case SL_VECTOR: SlVector(ptr, conv); break; - case SL_STDSTR: SlStdString(ptr, sld.conv); break; - default: NOT_REACHED(); - } - break; - } - - /* SL_SAVEBYTE writes a value to the savegame to identify the type of an object. - * When loading, the value is read explicitly with SlReadByte() to determine which - * object description to use. */ - case SL_SAVEBYTE: { - void *ptr = GetVariableAddress(object, sld); - - switch (_sl.action) { - case SLA_SAVE: SlWriteByte(*(uint8 *)ptr); break; - case SLA_LOAD_CHECK: - case SLA_LOAD: - case SLA_PTRS: - case SLA_NULL: break; - default: NOT_REACHED(); - } - break; - } - - case SL_NULL: { - assert(GetVarMemType(sld.conv) == SLE_VAR_NULL); - - switch (_sl.action) { - case SLA_LOAD_CHECK: - case SLA_LOAD: SlSkipBytes(SlCalcConvFileLen(sld.conv) * sld.length); break; - case SLA_SAVE: for (int i = 0; i < SlCalcConvFileLen(sld.conv) * sld.length; i++) SlWriteByte(0); break; - case SLA_PTRS: - case SLA_NULL: break; - default: NOT_REACHED(); - } - break; - } - - case SL_STRUCT: - case SL_STRUCTLIST: - switch (_sl.action) { - case SLA_SAVE: { - if (sld.cmd == SL_STRUCT) { - /* Store in the savegame if this struct was written or not. */ - SlSetStructListLength(SlCalcObjMemberLength(object, sld) > SlGetArrayLength(1) ? 1 : 0); - } - sld.handler->Save(object); - break; - } - - case SLA_LOAD_CHECK: { - if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { - SlGetStructListLength(1); - } - sld.handler->LoadCheck(object); - break; - } - - case SLA_LOAD: { - if (sld.cmd == SL_STRUCT && !IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { - SlGetStructListLength(1); - } - sld.handler->Load(object); - break; - } - - case SLA_PTRS: - sld.handler->FixPointers(object); - break; - - case SLA_NULL: break; - default: NOT_REACHED(); - } - break; - - default: NOT_REACHED(); - } - return true; -} - -/** - * Set the length of this list. - * @param The length of the list. - */ -void SlSetStructListLength(size_t length) -{ - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlGetArrayLength(length)); - if (_sl.need_length == NL_CALCLENGTH) return; - } - - SlWriteArrayLength(length); -} - -/** - * Get the length of this list; if it exceeds the limit, error out. - * @param limit The maximum size the list can be. - * @return The length of the list. - */ -size_t SlGetStructListLength(size_t limit) -{ - size_t length = SlReadArrayLength(); - if (length > limit) SlErrorCorrupt("List exceeds storage size"); - - return length; -} - -/** - * Main SaveLoad function. - * @param object The object that is being saved or loaded. - * @param slt The SaveLoad table with objects to save/load. - */ -void SlObject(void *object, const SaveLoadTable &slt) -{ - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcObjLength(object, slt)); - if (_sl.need_length == NL_CALCLENGTH) return; - } - - for (auto &sld : slt) { - SlObjectMember(object, sld); - } -} - -/** - * Handler that is assigned when there is a struct read in the savegame which - * is not known to the code. This means we are going to skip it. - */ -class SlSkipHandler : public SaveLoadHandler { - void Save(void *object) const override - { - NOT_REACHED(); - } - - void Load(void *object) const override - { - size_t length = SlGetStructListLength(UINT32_MAX); - for (; length > 0; length--) { - SlObject(object, this->GetLoadDescription()); - } - } - - void LoadCheck(void *object) const override - { - this->Load(object); - } - - virtual SaveLoadTable GetDescription() const override - { - return {}; - } - - virtual SaveLoadCompatTable GetCompatDescription() const override - { - NOT_REACHED(); - } -}; - -/** - * Save or Load a table header. - * @note a table-header can never contain more than 65535 fields. - * @param slt The SaveLoad table with objects to save/load. - * @return When loading, the ordered SaveLoad array to use; otherwise an empty list. - */ -std::vector SlTableHeader(const SaveLoadTable &slt) -{ - /* You can only use SlTableHeader if you are a CH_TABLE. */ - assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); - - switch (_sl.action) { - case SLA_LOAD_CHECK: - case SLA_LOAD: { - std::vector saveloads; - - /* Build a key lookup mapping based on the available fields. */ - std::map key_lookup; - for (auto &sld : slt) { - if (!SlIsObjectValidInSavegame(sld)) continue; - - /* Check that there is only one active SaveLoad for a given name. */ - assert(key_lookup.find(sld.name) == key_lookup.end()); - key_lookup[sld.name] = &sld; - } - - while (true) { - uint8 type; - SlSaveLoadConv(&type, SLE_UINT8); - if (type == SLE_FILE_END) break; - - std::string key; - SlStdString(&key, SLE_STR); - - auto sld_it = key_lookup.find(key); - if (sld_it == key_lookup.end()) { - /* SLA_LOADCHECK triggers this debug statement a lot and is perfectly normal. */ - DEBUG(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '%s' of type 0x%02X not found, skipping", key.c_str(), type); - - std::shared_ptr handler = nullptr; - SaveLoadType saveload_type; - switch (type & SLE_FILE_TYPE_MASK) { - case SLE_FILE_STRING: - /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */ - saveload_type = SL_STR; - break; - - case SLE_FILE_STRUCT: - /* Structs are always marked with SLE_FILE_HAS_LENGTH_FIELD as SL_STRUCT is seen as a list of 0/1 in length. */ - saveload_type = SL_STRUCTLIST; - handler = std::make_shared(); - break; - - default: - saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR; - break; - } - - /* We don't know this field, so read to nothing. */ - saveloads.push_back({key, saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, 0, nullptr, 0, handler}); - continue; - } - - /* Validate the type of the field. If it is changed, the - * savegame should have been bumped so we know how to do the - * conversion. If this error triggers, that clearly didn't - * happen and this is a friendly poke to the developer to bump - * the savegame version and add conversion code. */ - uint8 correct_type = GetSavegameFileType(*sld_it->second); - if (correct_type != type) { - DEBUG(sl, 1, "Field type for '%s' was expected to be 0x%02X but 0x%02X was found", key.c_str(), correct_type, type); - SlErrorCorrupt("Field type is different than expected"); - } - saveloads.push_back(*sld_it->second); - } - - for (auto &sld : saveloads) { - if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) { - sld.handler->load_description = SlTableHeader(sld.handler->GetDescription()); - } - } - - return saveloads; - } - - case SLA_SAVE: { - /* Automatically calculate the length? */ - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcTableHeader(slt)); - if (_sl.need_length == NL_CALCLENGTH) break; - } - - for (auto &sld : slt) { - if (!SlIsObjectValidInSavegame(sld)) continue; - /* Make sure we are not storing empty keys. */ - assert(!sld.name.empty()); - - uint8 type = GetSavegameFileType(sld); - assert(type != SLE_FILE_END); - - SlSaveLoadConv(&type, SLE_UINT8); - SlStdString(const_cast(&sld.name), SLE_STR); - } - - /* Add an end-of-header marker. */ - uint8 type = SLE_FILE_END; - SlSaveLoadConv(&type, SLE_UINT8); - - /* After the table, write down any sub-tables we might have. */ - for (auto &sld : slt) { - if (!SlIsObjectValidInSavegame(sld)) continue; - if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) { - /* SlCalcTableHeader already looks in sub-lists, so avoid the length being added twice. */ - NeedLength old_need_length = _sl.need_length; - _sl.need_length = NL_NONE; - - SlTableHeader(sld.handler->GetDescription()); - - _sl.need_length = old_need_length; - } - } - - break; - } - - default: NOT_REACHED(); - } - - return std::vector(); -} - -/** - * Load a table header in a savegame compatible way. If the savegame was made - * before table headers were added, it will fall back to the - * SaveLoadCompatTable for the order of fields while loading. - * - * @note You only have to call this function if the chunk existed as a - * non-table type before converting it to a table. New chunks created as - * table can call SlTableHeader() directly. - * - * @param slt The SaveLoad table with objects to save/load. - * @param slct The SaveLoadCompat table the original order of the fields. - * @return When loading, the ordered SaveLoad array to use; otherwise an empty list. - */ -std::vector SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct) -{ - assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK); - /* CH_TABLE / CH_SPARSE_TABLE always have a header. */ - if (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE) return SlTableHeader(slt); - - std::vector saveloads; - - /* Build a key lookup mapping based on the available fields. */ - std::map> key_lookup; - for (auto &sld : slt) { - /* All entries should have a name; otherwise the entry should just be removed. */ - assert(!sld.name.empty()); - - key_lookup[sld.name].push_back(&sld); - } - - for (auto &slc : slct) { - if (slc.name.empty()) { - /* In old savegames there can be data we no longer care for. We - * skip this by simply reading the amount of bytes indicated and - * send those to /dev/null. */ - saveloads.push_back({"", SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, slc.length, slc.version_from, slc.version_to, 0, nullptr, 0, nullptr}); - } else { - auto sld_it = key_lookup.find(slc.name); - /* If this branch triggers, it means that an entry in the - * SaveLoadCompat list is not mentioned in the SaveLoad list. Did - * you rename a field in one and not in the other? */ - if (sld_it == key_lookup.end()) { - /* This isn't an assert, as that leaves no information what - * field was to blame. This way at least we have breadcrumbs. */ - DEBUG(sl, 0, "internal error: saveload compatibility field '%s' not found", slc.name.c_str()); - SlErrorCorrupt("Internal error with savegame compatibility"); - } - for (auto &sld : sld_it->second) { - saveloads.push_back(*sld); - } - } - } - - for (auto &sld : saveloads) { - if (!SlIsObjectValidInSavegame(sld)) continue; - if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) { - sld.handler->load_description = SlCompatTableHeader(sld.handler->GetDescription(), sld.handler->GetCompatDescription()); - } - } - - return saveloads; -} - -/** - * Save or Load (a list of) global variables. - * @param slt The SaveLoad table with objects to save/load. - */ -void SlGlobList(const SaveLoadTable &slt) -{ - SlObject(nullptr, slt); -} - -/** - * 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) -{ - // removed - NOT_REACHED(); -} - -void ChunkHandler::LoadCheck(size_t len) const -{ - switch (_sl.block_mode) { - case CH_TABLE: - case CH_SPARSE_TABLE: - SlTableHeader({}); - FALLTHROUGH; - case CH_ARRAY: - case CH_SPARSE_ARRAY: - SlSkipArray(); - break; - case CH_RIFF: - SlSkipBytes(len); - break; - default: - NOT_REACHED(); - } -} - -/** - * 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 & CH_TYPE_MASK; - _sl.obj_len = 0; - _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); - - /* The header should always be at the start. Read the length; the - * Load() should as first action process the header. */ - if (_sl.expect_table_header) { - SlIterateArray(); - } - - switch (_sl.block_mode) { - case CH_TABLE: - case CH_ARRAY: - _sl.array_index = 0; - ch.Load(); - if (_next_offs != 0) SlErrorCorrupt("Invalid array length"); - break; - case CH_SPARSE_TABLE: - case CH_SPARSE_ARRAY: - ch.Load(); - if (_next_offs != 0) SlErrorCorrupt("Invalid array length"); - break; - case CH_RIFF: - /* Read length */ - len = (SlReadByte() << 16) | ((m >> 4) << 24); - len += SlReadUint16(); - _sl.obj_len = len; - endoffs = SlGetBytesRead() + len; - ch.Load(); - if (SlGetBytesRead() != endoffs) SlErrorCorrupt("Invalid chunk size"); - break; - default: - SlErrorCorrupt("Invalid chunk type"); - break; - } - - if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); -} - -/** - * Load a chunk of data for checking savegames. - * If the chunkhandler is nullptr, the chunk is skipped. - * @param ch The chunkhandler that will be used for the operation - */ -static void SlLoadCheckChunk(const ChunkHandler &ch) -{ - byte m = SlReadByte(); - size_t len; - size_t endoffs; - - _sl.block_mode = m & CH_TYPE_MASK; - _sl.obj_len = 0; - _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); - - /* The header should always be at the start. Read the length; the - * LoadCheck() should as first action process the header. */ - if (_sl.expect_table_header) { - SlIterateArray(); - } - - switch (_sl.block_mode) { - case CH_TABLE: - case CH_ARRAY: - _sl.array_index = 0; - ch.LoadCheck(); - break; - case CH_SPARSE_TABLE: - case CH_SPARSE_ARRAY: - ch.LoadCheck(); - break; - case CH_RIFF: - /* Read length */ - len = (SlReadByte() << 16) | ((m >> 4) << 24); - len += SlReadUint16(); - _sl.obj_len = len; - endoffs = SlGetBytesRead() + len; - ch.LoadCheck(len); - if (SlGetBytesRead() != endoffs) SlErrorCorrupt("Invalid chunk size"); - break; - default: - SlErrorCorrupt("Invalid chunk type"); - break; - } - - if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); -} - -/** - * 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 appropriate chunkhandler - */ -static const ChunkHandler *SlFindChunkHandler(uint32 id) -{ - for (const ChunkHandler &ch : ChunkHandlers()) if (ch.id == id) return &ch; - return nullptr; -} - -/** Load all chunks */ -void SlLoadChunks() -{ - _sl.action = SLA_LOAD; - - 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 == nullptr) SlErrorCorrupt("Unknown chunk type"); - SlLoadChunk(*ch); - } -} - -/** Load a chunk */ -void SlLoadChunkByID(uint32 id) -{ - _sl.action = SLA_LOAD; - - DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); - - const ChunkHandler *ch = SlFindChunkHandler(id); - if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); - SlLoadChunk(*ch); -} - -/** Load all chunks for savegame checking */ -void SlLoadCheckChunks() -{ - _sl.action = SLA_LOAD_CHECK; - - 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 == nullptr) SlErrorCorrupt("Unknown chunk type"); - SlLoadCheckChunk(*ch); - } -} - -/** Load a chunk for savegame checking */ -void SlLoadCheckChunkByID(uint32 id) -{ - _sl.action = SLA_LOAD_CHECK; - - DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); - - const ChunkHandler *ch = SlFindChunkHandler(id); - if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); - SlLoadCheckChunk(*ch); -} - -/** Fix all pointers (convert index -> pointer) */ -void SlFixPointers() -{ - _sl.action = SLA_PTRS; - - for (const ChunkHandler &ch : ChunkHandlers()) { - DEBUG(sl, 3, "Fixing pointers for %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); - ch.FixPointers(); - } - - assert(_sl.action == SLA_PTRS); -} - -void SlFixPointerChunkByID(uint32 id) -{ - const ChunkHandler *ch = SlFindChunkHandler(id); - if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); - DEBUG(sl, 3, "Fixing pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id); - ch->FixPointers(); -} - -/** - * Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is - * prefixed by an ID identifying it, followed by data, and terminator where appropriate - * @param ch The chunkhandler that will be used for the operation - */ -static void SlSaveChunk(const ChunkHandler &ch) -{ - if (ch.type == CH_READONLY) return; - - SlWriteUint32(ch.id); - DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); - - _sl.block_mode = ch.type; - _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); - - _sl.need_length = (_sl.expect_table_header || _sl.block_mode == CH_RIFF) ? NL_WANTLENGTH : NL_NONE; - - switch (_sl.block_mode) { - case CH_RIFF: - ch.Save(); - break; - case CH_TABLE: - case CH_ARRAY: - _sl.last_array_index = 0; - SlWriteByte(_sl.block_mode); - ch.Save(); - SlWriteArrayLength(0); // Terminate arrays - break; - case CH_SPARSE_TABLE: - case CH_SPARSE_ARRAY: - SlWriteByte(_sl.block_mode); - ch.Save(); - SlWriteArrayLength(0); // Terminate arrays - break; - default: NOT_REACHED(); - } - - if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); -} - -/** Save a chunk of data */ -void SlSaveChunkChunkByID(uint32 id) -{ - const ChunkHandler *ch = SlFindChunkHandler(id); - if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); - - _sl.action = SLA_SAVE; - SlSaveChunk(*ch); -} - -/** Reset state prior to a load */ -void SlResetLoadState() -{ - _next_offs = 0; -} - -SaveLoadTable SaveLoadHandler::GetLoadDescription() const -{ - assert(this->load_description.has_value()); - return *this->load_description; -} - -} diff --git a/src/saveload/upstream/saveload.h b/src/saveload/upstream/saveload.h deleted file mode 100644 index 19c661a709..0000000000 --- a/src/saveload/upstream/saveload.h +++ /dev/null @@ -1,842 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file saveload.h Functions/types related to saving and loading games. */ - -#ifndef UPSTREAM_SAVELOAD_H -#define UPSTREAM_SAVELOAD_H - -#include "../saveload_common.h" -#include "../../fileio_type.h" -#include "../../fios.h" -#include "../../strings_type.h" -#include "../../core/span_type.hpp" -#include -#include -#include - -extern SaveLoadVersion _sl_version; -extern byte _sl_minor_version; -extern const SaveLoadVersion SAVEGAME_VERSION; -extern const SaveLoadVersion MAX_LOAD_SAVEGAME_VERSION; - -namespace upstream_sl { - -typedef void AutolengthProc(void *arg); - -/** Type of a chunk. */ -enum ChunkType { - CH_RIFF = 0, - CH_ARRAY = 1, - CH_SPARSE_ARRAY = 2, - CH_TABLE = 3, - CH_SPARSE_TABLE = 4, - - CH_TYPE_MASK = 0xf, ///< All ChunkType values have to be within this mask. - CH_READONLY, ///< Chunk is never saved. -}; - -/** Handlers and description of chunk. */ -struct ChunkHandler { - uint32 id; ///< Unique ID (4 letters). - ChunkType type; ///< Type of the chunk. @see ChunkType - - ChunkHandler(uint32 id, ChunkType type) : id(id), type(type) {} - - virtual ~ChunkHandler() {} - - /** - * Save the chunk. - * Must be overridden, unless Chunk type is CH_READONLY. - */ - virtual void Save() const { NOT_REACHED(); } - - /** - * Load the chunk. - * Must be overridden. - */ - virtual void Load() const = 0; - - /** - * Fix the pointers. - * Pointers are saved using the index of the pointed object. - * On load, pointers are filled with indices and need to be fixed to point to the real object. - * Must be overridden if the chunk saves any pointer. - */ - virtual void FixPointers() const {} - - /** - * Load the chunk for game preview. - * Default implementation just skips the data. - * @param len Number of bytes to skip. - */ - virtual void LoadCheck(size_t len = 0) const; -}; - -/** A reference to ChunkHandler. */ -using ChunkHandlerRef = std::reference_wrapper; - -/** A table of ChunkHandler entries. */ -using ChunkHandlerTable = span; - -/** A table of SaveLoadCompat entries. */ -using SaveLoadCompatTable = span; - -/** Handler for saving/loading an object to/from disk. */ -class SaveLoadHandler { -public: - std::optional> load_description; - - virtual ~SaveLoadHandler() {} - - /** - * Save the object to disk. - * @param object The object to store. - */ - virtual void Save(void *object) const {} - - /** - * Load the object from disk. - * @param object The object to load. - */ - virtual void Load(void *object) const {} - - /** - * Similar to load, but used only to validate savegames. - * @param object The object to load. - */ - virtual void LoadCheck(void *object) const {} - - /** - * A post-load callback to fix #SL_REF integers into pointers. - * @param object The object to fix. - */ - virtual void FixPointers(void *object) const {} - - /** - * Get the description of the fields in the savegame. - */ - virtual SaveLoadTable GetDescription() const = 0; - - /** - * Get the pre-header description of the fields in the savegame. - */ - virtual SaveLoadCompatTable GetCompatDescription() const = 0; - - /** - * Get the description for how to load the chunk. Depending on the - * savegame version this can either use the headers in the savegame or - * fall back to backwards compatibility and uses hard-coded headers. - */ - SaveLoadTable GetLoadDescription() const; -}; - -/** - * Default handler for saving/loading an object to/from disk. - * - * This handles a few common things for handlers, meaning the actual handler - * needs less code. - * - * Usage: class SlMine : public DefaultSaveLoadHandler {} - * - * @tparam TImpl The class initializing this template. - * @tparam TObject The class of the object using this SaveLoadHandler. - */ -template -class DefaultSaveLoadHandler : public SaveLoadHandler { -public: - SaveLoadTable GetDescription() const override { return static_cast(this)->description; } - SaveLoadCompatTable GetCompatDescription() const override { return static_cast(this)->compat_description; } - - virtual void Save(TObject *object) const {} - void Save(void *object) const override { this->Save(static_cast(object)); } - - virtual void Load(TObject *object) const {} - void Load(void *object) const override { this->Load(static_cast(object)); } - - virtual void LoadCheck(TObject *object) const {} - void LoadCheck(void *object) const override { this->LoadCheck(static_cast(object)); } - - virtual void FixPointers(TObject *object) const {} - void FixPointers(void *object) const override { this->FixPointers(static_cast(object)); } -}; - -/** Type of reference (#SLE_REF, #SLE_CONDREF). */ -enum SLRefType { - REF_ORDER = 0, ///< Load/save a reference to an order. - REF_VEHICLE = 1, ///< Load/save a reference to a vehicle. - REF_STATION = 2, ///< Load/save a reference to a station. - REF_TOWN = 3, ///< Load/save a reference to a town. - REF_VEHICLE_OLD = 4, ///< Load/save an old-style reference to a vehicle (for pre-4.4 savegames). - REF_ROADSTOPS = 5, ///< Load/save a reference to a bus/truck stop. - REF_ENGINE_RENEWS = 6, ///< Load/save a reference to an engine renewal (autoreplace). - REF_CARGO_PACKET = 7, ///< Load/save a reference to a cargo packet. - REF_ORDERLIST = 8, ///< Load/save a reference to an orderlist. - REF_STORAGE = 9, ///< Load/save a reference to a persistent storage. - REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph. - REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job. -}; - -/** - * 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. - * NOTE: the SLE_FILE_NNN values are stored in the savegame! */ - SLE_FILE_END = 0, ///< Used to mark end-of-header in tables. - SLE_FILE_I8 = 1, - SLE_FILE_U8 = 2, - SLE_FILE_I16 = 3, - SLE_FILE_U16 = 4, - SLE_FILE_I32 = 5, - SLE_FILE_U32 = 6, - SLE_FILE_I64 = 7, - SLE_FILE_U64 = 8, - SLE_FILE_STRINGID = 9, ///< StringID offset into strings-array - SLE_FILE_STRING = 10, - SLE_FILE_STRUCT = 11, - /* 4 more possible file-primitives */ - - SLE_FILE_TYPE_MASK = 0xf, ///< Mask to get the file-type (and not any flags). - SLE_FILE_HAS_LENGTH_FIELD = 1 << 4, ///< Bit stored in savegame to indicate field has a length field for each entry. - - /* 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_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_U32, - SLE_STRINGBUF = SLE_FILE_STRING | SLE_VAR_STRB, - 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_STR = SLE_STRING, - SLE_STRQ = SLE_STRINGQUOTE, - - /* 8 bits allocated for a maximum of 8 flags - * Flags directing saving/loading of a variable */ - SLF_ALLOW_CONTROL = 1 << 8, ///< Allow control codes in the strings. - SLF_ALLOW_NEWLINE = 1 << 9, ///< Allow new lines in the strings. -}; - -typedef uint32 VarType; - -/** Type of data saved. */ -enum SaveLoadType : byte { - SL_VAR = 0, ///< Save/load a variable. - SL_REF = 1, ///< Save/load a reference. - SL_STRUCT = 2, ///< Save/load a struct. - - SL_STR = 3, ///< Save/load a string. - SL_STDSTR = 4, ///< Save/load a \c std::string. - - SL_ARR = 5, ///< Save/load a fixed-size array of #SL_VAR elements. - SL_DEQUE = 6, ///< Save/load a deque of #SL_VAR elements. - SL_VECTOR = 7, ///< Save/load a vector of #SL_VAR elements. - SL_REFLIST = 8, ///< Save/load a list of #SL_REF elements. - SL_STRUCTLIST = 9, ///< Save/load a list of structs. - - SL_SAVEBYTE = 10, ///< Save (but not load) a byte. - SL_NULL = 11, ///< Save null-bytes and load to nowhere. - - SL_REFDEQUE, ///< Save/load a deque of #SL_REF elements. - SL_REFVEC, ///< Save/load a vector of #SL_REF elements. -}; - -typedef void *SaveLoadAddrProc(void *base, size_t extra); - -/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */ -struct SaveLoad { - std::string name; ///< Name of this field (optional, used for tables). - 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; this field combines both FileVarType and MemVarType. - uint16 length; ///< (Conditional) length of the variable (eg. arrays) (max array size is 65536 elements). - SaveLoadVersion version_from; ///< Save/load the variable starting from this savegame version. - SaveLoadVersion version_to; ///< Save/load the variable before this savegame version. - size_t size; ///< The sizeof size. - SaveLoadAddrProc *address_proc; ///< Callback proc the get the actual variable address in memory. - size_t extra_data; ///< Extra data for the callback proc. - std::shared_ptr handler; ///< Custom handler for Save/Load procs. -}; - -/** - * SaveLoad information for backwards compatibility. - * - * At SLV_SETTINGS_NAME a new method of keeping track of fields in a savegame - * was added, where the order of fields is no longer important. For older - * savegames we still need to know the correct order. This struct is the glue - * to make that happen. - */ -struct SaveLoadCompat { - std::string name; ///< Name of the field. - uint16 length; ///< Length of the NULL field. - SaveLoadVersion version_from; ///< Save/load the variable starting from this savegame version. - SaveLoadVersion version_to; ///< Save/load the variable before this savegame version. -}; - -/** - * Storage of simple variables, references (pointers), and arrays. - * @param cmd Load/save type. @see SaveLoadType - * @param name Field name for table chunks. - * @param base Name of the class or struct containing the variable. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the field. - * @param to Last savegame version that has the field. - * @param extra Extra data to pass to the address callback function. - * @note In general, it is better to use one of the SLE_* macros below. - */ -#define SLE_GENERAL_NAME(cmd, name, base, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast(static_cast(std::addressof(static_cast(b)->variable))); }, extra, nullptr} - -/** - * Storage of simple variables, references (pointers), and arrays with a custom name. - * @param cmd Load/save type. @see SaveLoadType - * @param base Name of the class or struct containing the variable. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the field. - * @param to Last savegame version that has the field. - * @param extra Extra data to pass to the address callback function. - * @note In general, it is better to use one of the SLE_* macros below. - */ -#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) SLE_GENERAL_NAME(cmd, #variable, base, variable, type, length, from, to, extra) - -/** - * Storage of a variable in some savegame versions. - * @param base Name of the class or struct containing the variable. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the field. - * @param to Last savegame version that has the field. - */ -#define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to, 0) - -/** - * Storage of a variable in some savegame versions. - * @param base Name of the class or struct containing the variable. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param name Field name for table chunks. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the field. - * @param to Last savegame version that has the field. - */ -#define SLE_CONDVARNAME(base, variable, name, type, from, to) SLE_GENERAL_NAME(SL_VAR, name, base, variable, type, 0, from, to, 0) - -/** - * Storage of a reference in some savegame versions. - * @param base Name of the class or struct containing the variable. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Type of the reference, a value from #SLRefType. - * @param from First savegame version that has the field. - * @param to Last savegame version that has the field. - */ -#define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to, 0) - -/** - * Storage of a fixed-size array of #SL_VAR elements in some savegame versions. - * @param base Name of the class or struct containing the array. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param length Number of elements in the array. - * @param from First savegame version that has the array. - * @param to Last savegame version that has the array. - */ -#define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to, 0) - -/** - * Storage of a string in some savegame versions. - * @param base Name of the class or struct containing the string. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param length Number of elements in the string (only used for fixed size buffers). - * @param from First savegame version that has the string. - * @param to Last savegame version that has the string. - */ -#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to, 0) - -/** - * Storage of a \c std::string in some savegame versions. - * @param base Name of the class or struct containing the string. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the string. - * @param to Last savegame version that has the string. - */ -#define SLE_CONDSSTR(base, variable, type, from, to) SLE_GENERAL(SL_STDSTR, base, variable, type, 0, from, to, 0) - -/** - * Storage of a list of #SL_REF elements in some savegame versions. - * @param base Name of the class or struct containing the list. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLE_CONDREFLIST(base, variable, type, from, to) SLE_GENERAL(SL_REFLIST, base, variable, type, 0, from, to, 0) - -/** - * Storage of a deque of #SL_REF elements in some savegame versions. - * @param base Name of the class or struct containing the list. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLE_CONDREFDEQUE(base, variable, type, from, to) SLE_GENERAL(SL_REFDEQUE, base, variable, type, 0, from, to, 0) - -/** - * Storage of a vector of #SL_REF elements in some savegame versions. - * @param base Name of the class or struct containing the list. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLE_CONDREFVEC(base, variable, type, from, to) SLE_GENERAL(SL_REFVEC, base, variable, type, 0, from, to, 0) - -/** - * Storage of a deque of #SL_VAR elements in some savegame versions. - * @param base Name of the class or struct containing the list. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLE_CONDDEQUE(base, variable, type, from, to) SLE_GENERAL(SL_DEQUE, base, variable, type, 0, from, to, 0) - -/** - * Storage of a variable in every version of a savegame. - * @param base Name of the class or struct containing the variable. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLE_VAR(base, variable, type) SLE_CONDVAR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) -#define SLE_VAR2(base, name, variable, type) SLE_CONDVARNAME(base, variable, name, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a reference in every version of a savegame. - * @param base Name of the class or struct containing the variable. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Type of the reference, a value from #SLRefType. - */ -#define SLE_REF(base, variable, type) SLE_CONDREF(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of fixed-size array of #SL_VAR elements in every version of a savegame. - * @param base Name of the class or struct containing the array. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param length Number of elements in the array. - */ -#define SLE_ARR(base, variable, type, length) SLE_CONDARR(base, variable, type, length, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a string in every savegame version. - * @param base Name of the class or struct containing the string. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - * @param length Number of elements in the string (only used for fixed size buffers). - */ -#define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a \c std::string in every savegame version. - * @param base Name of the class or struct containing the string. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLE_SSTR(base, variable, type) SLE_CONDSSTR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a list of #SL_REF elements in every savegame version. - * @param base Name of the class or struct containing the list. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLE_REFLIST(base, variable, type) SLE_CONDREFLIST(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a deque of #SL_REF elements in every savegame version. - * @param base Name of the class or struct containing the list. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLE_REFDEQUE(base, variable, type) SLE_CONDREFDEQUE(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a vector of #SL_REF elements in every savegame version. - * @param base Name of the class or struct containing the list. - * @param variable Name of the variable in the class or struct referenced by \a base. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLE_REFVEC(base, variable, type) SLE_CONDREFVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Only write byte during saving; never read it during loading. - * When using SLE_SAVEBYTE you will have to read this byte before the table - * this is in is read. This also means SLE_SAVEBYTE can only be used at the - * top of a chunk. - * This is intended to be used to indicate what type of entry this is in a - * list of entries. - * @param base Name of the class or struct containing the variable. - * @param variable Name of the variable in the class or struct referenced by \a base. - */ -#define SLE_SAVEBYTE(base, variable) SLE_GENERAL(SL_SAVEBYTE, base, variable, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, 0) - -/** - * Storage of global simple variables, references (pointers), and arrays. - * @param name The name of the field. - * @param cmd Load/save type. @see SaveLoadType - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the field. - * @param to Last savegame version that has the field. - * @param extra Extra data to pass to the address callback function. - * @note In general, it is better to use one of the SLEG_* macros below. - */ -#define SLEG_GENERAL(name, cmd, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast(std::addressof(variable)); }, extra, nullptr} - -/** - * Storage of a global variable in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the field. - * @param to Last savegame version that has the field. - */ -#define SLEG_CONDVAR(name, variable, type, from, to) SLEG_GENERAL(name, SL_VAR, variable, type, 0, from, to, 0) - -/** - * Storage of a global reference in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the field. - * @param to Last savegame version that has the field. - */ -#define SLEG_CONDREF(name, variable, type, from, to) SLEG_GENERAL(name, SL_REF, variable, type, 0, from, to, 0) - -/** - * Storage of a global fixed-size array of #SL_VAR elements in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param length Number of elements in the array. - * @param from First savegame version that has the array. - * @param to Last savegame version that has the array. - */ -#define SLEG_CONDARR(name, variable, type, length, from, to) SLEG_GENERAL(name, SL_ARR, variable, type, length, from, to, 0) - -/** - * Storage of a global string in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param length Number of elements in the string (only used for fixed size buffers). - * @param from First savegame version that has the string. - * @param to Last savegame version that has the string. - */ -#define SLEG_CONDSTR(name, variable, type, length, from, to) SLEG_GENERAL(name, SL_STR, variable, type, length, from, to, 0) - -/** - * Storage of a global \c std::string in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the string. - * @param to Last savegame version that has the string. - */ -#define SLEG_CONDSSTR(name, variable, type, from, to) SLEG_GENERAL(name, SL_STDSTR, variable, type, 0, from, to, 0) - -/** - * Storage of a structs in some savegame versions. - * @param name The name of the field. - * @param handler SaveLoadHandler for the structs. - * @param from First savegame version that has the struct. - * @param to Last savegame version that has the struct. - */ -#define SLEG_CONDSTRUCT(name, handler, from, to) SaveLoad {name, SL_STRUCT, 0, 0, from, to, 0, nullptr, 0, std::make_shared()} - -/** - * Storage of a global reference list in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLEG_CONDREFLIST(name, variable, type, from, to) SLEG_GENERAL(name, SL_REFLIST, variable, type, 0, from, to, 0) - -/** - * Storage of a global reference deque in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLEG_CONDREFDEQUE(name, variable, type, from, to) SLEG_GENERAL(name, SL_REFDEQUE, variable, type, 0, from, to, 0) - -/** - * Storage of a global reference vector in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLEG_CONDREFVEC(name, variable, type, from, to) SLEG_GENERAL(name, SL_REFVEC, variable, type, 0, from, to, 0) - -/** - * Storage of a global vector of #SL_VAR elements in some savegame versions. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLEG_CONDVECTOR(name, variable, type, from, to) SLEG_GENERAL(name, SL_VECTOR, variable, type, 0, from, to, 0) - -/** - * Storage of a list of structs in some savegame versions. - * @param name The name of the field. - * @param handler SaveLoadHandler for the list of structs. - * @param from First savegame version that has the list. - * @param to Last savegame version that has the list. - */ -#define SLEG_CONDSTRUCTLIST(name, handler, from, to) SaveLoad {name, SL_STRUCTLIST, 0, 0, from, to, 0, nullptr, 0, std::make_shared()} - -/** - * Storage of a global variable in every savegame version. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLEG_VAR(name, variable, type) SLEG_CONDVAR(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a global reference in every savegame version. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLEG_REF(name, variable, type) SLEG_CONDREF(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a global fixed-size array of #SL_VAR elements in every savegame version. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLEG_ARR(name, variable, type) SLEG_CONDARR(name, variable, type, lengthof(variable), SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a global string in every savegame version. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLEG_STR(name, variable, type) SLEG_CONDSTR(name, variable, type, sizeof(variable), SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a global \c std::string in every savegame version. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLEG_SSTR(name, variable, type) SLEG_CONDSSTR(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a structs in every savegame version. - * @param name The name of the field. - * @param handler SaveLoadHandler for the structs. - */ -#define SLEG_STRUCT(name, handler) SLEG_CONDSTRUCT(name, handler, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a global reference list in every savegame version. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLEG_REFLIST(name, variable, type) SLEG_CONDREFLIST(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a global reference deque in every savegame version. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLEG_REFDEQUE(name, variable, type) SLEG_CONDREFDEQUE(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a global vector of #SL_VAR elements in every savegame version. - * @param name The name of the field. - * @param variable Name of the global variable. - * @param type Storage of the data in memory and in the savegame. - */ -#define SLEG_VECTOR(name, variable, type) SLEG_CONDVECTOR(name, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Storage of a list of structs in every savegame version. - * @param name The name of the field. - * @param handler SaveLoadHandler for the list of structs. - */ -#define SLEG_STRUCTLIST(name, handler) SLEG_CONDSTRUCTLIST(name, handler, SL_MIN_VERSION, SL_MAX_VERSION) - -/** - * Field name where the real SaveLoad can be located. - * @param name The name of the field. - */ -#define SLC_VAR(name) {name, 0, SL_MIN_VERSION, SL_MAX_VERSION} - -/** - * Empty space in every savegame version. - * @param length Length of the empty space. - * @param from First savegame version that has the empty space. - * @param to Last savegame version that has the empty space. - */ -#define SLC_NULL(length, from, to) {{}, length, from, to} - -/** End marker of compat variables save or load. */ -#define SLC_END() {{}, 0, SL_MIN_VERSION, SL_MIN_VERSION} - -/** - * Checks whether the savegame is below \a major.\a minor. - * @param major Major number of the version to check against. - * @param minor Minor number of the version to check against. If \a minor is 0 or not specified, only the major number is checked. - * @return Savegame version is earlier than the specified version. - */ -static inline bool IsSavegameVersionBefore(SaveLoadVersion major, byte minor = 0) -{ - return _sl_version < major || (minor > 0 && _sl_version == major && _sl_minor_version < minor); -} - -/** - * Checks whether the savegame is below or at \a major. This should be used to repair data from existing - * savegames which is no longer corrupted in new savegames, but for which otherwise no savegame - * bump is required. - * @param major Major number of the version to check against. - * @return Savegame version is at most the specified version. - */ -static inline bool IsSavegameVersionBeforeOrAt(SaveLoadVersion major) -{ - return _sl_version <= major; -} - -/** - * 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 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 file-type - * @return the SLE_FILE_* part of a variable-type description - */ -static inline VarType GetVarFileType(VarType type) -{ - return type & 0xF; // GB(type, 0, 4); -} - -/** - * Check if the given saveload type is a numeric type. - * @param conv the type to check - * @return True if it's a numeric type. - */ -static inline bool IsNumericType(VarType conv) -{ - return GetVarMemType(conv) <= SLE_VAR_U64; -} - -/** - * Get the address of the variable. Null-variables don't have an address, - * everything else has a callback function that returns the address based - * on the saveload data and the current object for non-globals. - */ -static inline void *GetVariableAddress(const void *object, const SaveLoad &sld) -{ - /* Entry is a null-variable, mostly used to read old savegames etc. */ - if (GetVarMemType(sld.conv) == SLE_VAR_NULL) { - assert(sld.address_proc == nullptr); - return nullptr; - } - - /* Everything else should be a non-null pointer. */ - assert(sld.address_proc != nullptr); - return sld.address_proc(const_cast(object), sld.extra_data); -} - -int64 ReadValue(const void *ptr, VarType conv); -void WriteValue(void *ptr, VarType conv, int64 val); - -void SlSetArrayIndex(uint index); -int SlIterateArray(); - -void SlSetStructListLength(size_t length); -size_t SlGetStructListLength(size_t limit); - -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 SaveLoadTable &slt); - -void SlGlobList(const SaveLoadTable &slt); -void SlCopy(void *object, size_t length, VarType conv); -std::vector SlTableHeader(const SaveLoadTable &slt); -std::vector SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct); -void SlObject(void *object, const SaveLoadTable &slt); - -} - -#endif /* UPSTREAM_SAVELOAD_H */ diff --git a/src/saveload/upstream/signs_sl.cpp b/src/saveload/upstream/signs_sl.cpp deleted file mode 100644 index 10b458d2f2..0000000000 --- a/src/saveload/upstream/signs_sl.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file signs_sl.cpp Code handling saving and loading of economy data */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/signs_sl_compat.h" - -#include "../../signs_base.h" -#include "../../fios.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -/** Description of a sign within the savegame. */ -static const SaveLoad _sign_desc[] = { - SLE_CONDVAR(Sign, name, SLE_NAME, SL_MIN_VERSION, SLV_84), - SLE_CONDSSTR(Sign, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), - SLE_CONDVAR(Sign, x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Sign, y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Sign, x, SLE_INT32, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR(Sign, y, SLE_INT32, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR(Sign, owner, SLE_UINT8, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Sign, z, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), - SLE_CONDVAR(Sign, z, SLE_INT32, SLV_164, SL_MAX_VERSION), -}; - -struct SIGNChunkHandler : ChunkHandler { - SIGNChunkHandler() : ChunkHandler('SIGN', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_sign_desc); - - for (Sign *si : Sign::Iterate()) { - SlSetArrayIndex(si->index); - SlObject(si, _sign_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_sign_desc, _sign_sl_compat); - - int index; - while ((index = SlIterateArray()) != -1) { - Sign *si = new (index) Sign(); - SlObject(si, slt); - /* Before version 6.1, signs didn't have owner. - * Before version 83, invalid signs were determined by si->str == 0. - * Before version 103, owner could be a bankrupted company. - * - we can't use IsValidCompany() now, so this is fixed in AfterLoadGame() - * All signs that were saved are valid (including those with just 'Sign' and INVALID_OWNER). - * - so set owner to OWNER_NONE if needed (signs from pre-version 6.1 would be lost) */ - if (IsSavegameVersionBefore(SLV_6, 1) || (IsSavegameVersionBefore(SLV_83) && si->owner == INVALID_OWNER)) { - si->owner = OWNER_NONE; - } - - /* Signs placed in scenario editor shall now be OWNER_DEITY */ - if (IsSavegameVersionBefore(SLV_171) && si->owner == OWNER_NONE && SaveLoadFileTypeIsScenario()) { - si->owner = OWNER_DEITY; - } - } - } -}; - -static const SIGNChunkHandler SIGN; -static const ChunkHandlerRef sign_chunk_handlers[] = { - SIGN, -}; - -extern const ChunkHandlerTable _sign_chunk_handlers(sign_chunk_handlers); - -} diff --git a/src/saveload/upstream/station_sl.cpp b/src/saveload/upstream/station_sl.cpp deleted file mode 100644 index aa79c11941..0000000000 --- a/src/saveload/upstream/station_sl.cpp +++ /dev/null @@ -1,586 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file station_sl.cpp Code handling saving and loading of stations. */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/station_sl_compat.h" - -#include "../../station_base.h" -#include "../../waypoint_base.h" -#include "../../roadstop_base.h" -#include "../../vehicle_base.h" -#include "../../newgrf_station.h" -#include "../../newgrf_roadstop.h" - -#include "table/strings.h" - -#include "../../safeguards.h" - -namespace upstream_sl { - -static const SaveLoad _roadstop_desc[] = { - SLE_VAR(RoadStop, xy, SLE_UINT32), - SLE_VAR(RoadStop, status, SLE_UINT8), - SLE_REF(RoadStop, next, REF_ROADSTOPS), -}; - -static uint16 _waiting_acceptance; -static uint32 _old_num_flows; -static uint16 _cargo_source; -static uint32 _cargo_source_xy; -static uint8 _cargo_days; -static Money _cargo_feeder_share; - -CargoPacketList _packets; -uint32 _old_num_dests; - -struct FlowSaveLoad { - FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {} - StationID source; - StationID via; - uint32 share; - bool restricted; -}; - -typedef std::pair StationCargoPair; - -static OldPersistentStorage _old_st_persistent_storage; -static byte _old_last_vehicle_type; - -/** - * Swap the temporary packets with the packets without specific destination in - * the given goods entry. Assert that at least one of those is empty. - * @param ge Goods entry to swap with. - */ -static void SwapPackets(GoodsEntry *ge) -{ - StationCargoPacketMap &ge_packets = const_cast(*ge->cargo.Packets()); - - if (_packets.empty()) { - std::map::iterator it(ge_packets.find(INVALID_STATION)); - if (it == ge_packets.end()) { - return; - } else { - it->second.swap(_packets); - } - } else { - assert(ge_packets[INVALID_STATION].empty()); - ge_packets[INVALID_STATION].swap(_packets); - } -} - -class SlStationSpecList : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION), - SLE_CONDVAR(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SLV_27, SLV_EXTEND_ENTITY_MAPPING), - SLE_CONDVAR(StationSpecList, localidx, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _station_spec_list_sl_compat; - - void Save(BaseStation *bst) const override - { - SlSetStructListLength(bst->speclist.size()); - for (uint i = 0; i < bst->speclist.size(); i++) { - SlObject(&bst->speclist[i], this->GetDescription()); - } - } - - void Load(BaseStation *bst) const override - { - uint8 num_specs = (uint8)SlGetStructListLength(UINT8_MAX); - - bst->speclist.resize(num_specs); - for (uint i = 0; i < num_specs; i++) { - SlObject(&bst->speclist[i], this->GetLoadDescription()); - } - } -}; - -class SlRoadStopSpecList : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION), - SLE_CONDVAR(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SLV_27, SLV_EXTEND_ENTITY_MAPPING), - SLE_CONDVAR(StationSpecList, localidx, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _station_road_stop_spec_list_sl_compat; - - void Save(BaseStation *bst) const override - { - SlSetStructListLength(bst->roadstop_speclist.size()); - for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { - SlObject(&bst->roadstop_speclist[i], this->GetDescription()); - } - } - - void Load(BaseStation *bst) const override - { - uint8 num_specs = (uint8)SlGetStructListLength(UINT8_MAX); - - bst->roadstop_speclist.resize(num_specs); - for (uint i = 0; i < num_specs; i++) { - SlObject(&bst->roadstop_speclist[i], this->GetLoadDescription()); - } - } -}; - -class SlStationCargo : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(StationCargoPair, first, SLE_UINT16), - SLE_REFDEQUE(StationCargoPair, second, REF_CARGO_PACKET), - }; - inline const static SaveLoadCompatTable compat_description = _station_cargo_sl_compat; - - void Save(GoodsEntry *ge) const override - { - // removed - NOT_REACHED(); - } - - void Load(GoodsEntry *ge) const override - { - size_t num_dests = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _old_num_dests : SlGetStructListLength(UINT32_MAX); - - StationCargoPair pair; - for (uint j = 0; j < num_dests; ++j) { - SlObject(&pair, this->GetLoadDescription()); - const_cast(*(ge->cargo.Packets()))[pair.first].swap(pair.second); - assert(pair.second.empty()); - } - } - - void FixPointers(GoodsEntry *ge) const override - { - for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) { - SlObject(const_cast(&(*it)), this->GetDescription()); - } - } -}; - -class SlStationFlow : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(FlowSaveLoad, source, SLE_UINT16), - SLE_VAR(FlowSaveLoad, via, SLE_UINT16), - SLE_VAR(FlowSaveLoad, share, SLE_UINT32), - SLE_CONDVAR(FlowSaveLoad, restricted, SLE_BOOL, SLV_187, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _station_flow_sl_compat; - - void Save(GoodsEntry *ge) const override - { - // removed - NOT_REACHED(); - } - - void Load(GoodsEntry *ge) const override - { - size_t num_flows = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _old_num_flows : SlGetStructListLength(UINT32_MAX); - - FlowSaveLoad flow; - FlowStat *fs = nullptr; - StationID prev_source = INVALID_STATION; - for (uint32 j = 0; j < num_flows; ++j) { - SlObject(&flow, this->GetLoadDescription()); - if (fs == nullptr || prev_source != flow.source) { - fs = &(*(ge->flows.insert(ge->flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); - } else { - fs->AppendShare(flow.via, flow.share, flow.restricted); - } - prev_source = flow.source; - } - } -}; - -class SlStationGoods : public DefaultSaveLoadHandler { -public: -#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) - /* This table access private members of other classes; they have this - * class as friend. For MSVC CL 19.15 and 19.16 this doesn't work for - * "inline static const", so we are forced to wrap the table in a - * function. CL 19.16 is the latest for VS2017. */ - inline static const SaveLoad description[] = {{}}; - SaveLoadTable GetDescription() const override { -#else - inline -#endif - static const SaveLoad description[] = { - SLEG_CONDVAR("waiting_acceptance", _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68), - SLE_CONDVAR(GoodsEntry, status, SLE_UINT8, SLV_68, SL_MAX_VERSION), - SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8), - SLE_VAR(GoodsEntry, rating, SLE_UINT8), - SLEG_CONDVAR("cargo_source", _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), - SLEG_CONDVAR("cargo_source", _cargo_source, SLE_UINT16, SLV_7, SLV_68), - SLEG_CONDVAR("cargo_source_xy", _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), - SLEG_CONDVAR("cargo_days", _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68), - SLE_VAR(GoodsEntry, last_speed, SLE_UINT8), - SLE_VAR(GoodsEntry, last_age, SLE_UINT8), - SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, SLV_14, SLV_65), - SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), - SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION), - SLEG_CONDREFDEQUE("packets", _packets, REF_CARGO_PACKET, SLV_68, SLV_183), - SLEG_CONDVAR("old_num_dests", _old_num_dests, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), - SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION), - SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION), - SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION), - SLEG_CONDVAR("old_num_flows", _old_num_flows, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), - SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST("flow", SlStationFlow, SLV_183, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST("cargo", SlStationCargo, SLV_183, SL_MAX_VERSION), - }; -#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) - return description; - } -#endif - inline const static SaveLoadCompatTable compat_description = _station_goods_sl_compat; - - /** - * Get the number of cargoes used by this savegame version. - * @return The number of cargoes used by this savegame version. - */ - size_t GetNumCargo() const - { - if (IsSavegameVersionBefore(SLV_55)) return 12; - if (IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES)) return 32; - if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return NUM_CARGO; - /* Read from the savegame how long the list is. */ - return SlGetStructListLength(NUM_CARGO); - } - - void Save(BaseStation *bst) const override - { - Station *st = Station::From(bst); - - SlSetStructListLength(NUM_CARGO); - - for (CargoID i = 0; i < NUM_CARGO; i++) { - SlObject(&st->goods[i], this->GetDescription()); - } - } - - void Load(BaseStation *bst) const override - { - Station *st = Station::From(bst); - - /* Before savegame version 161, persistent storages were not stored in a pool. */ - if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_145) && st->facilities & FACIL_AIRPORT) { - /* Store the old persistent storage. The GRFID will be added later. */ - assert(PersistentStorage::CanAllocateItem()); - st->airport.psa = new PersistentStorage(0, 0, 0); - memcpy(st->airport.psa->storage, _old_st_persistent_storage.storage, sizeof(_old_st_persistent_storage.storage)); - } - - size_t num_cargo = this->GetNumCargo(); - for (size_t i = 0; i < num_cargo; i++) { - GoodsEntry *ge = &st->goods[i]; - SlObject(ge, this->GetLoadDescription()); - if (IsSavegameVersionBefore(SLV_183)) { - SwapPackets(ge); - } - if (IsSavegameVersionBefore(SLV_68)) { - SB(ge->status, GoodsEntry::GES_ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); - if (GB(_waiting_acceptance, 0, 12) != 0) { - /* In old versions, enroute_from used 0xFF as INVALID_STATION */ - StationID source = (IsSavegameVersionBefore(SLV_7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source; - - /* Make sure we can allocate the CargoPacket. This is safe - * as there can only be ~64k stations and 32 cargoes in these - * savegame versions. As the CargoPacketPool has more than - * 16 million entries; it fits by an order of magnitude. */ - assert(CargoPacket::CanAllocateItem()); - - /* Don't construct the packet with station here, because that'll fail with old savegames */ - CargoPacket *cp = new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share); - ge->cargo.Append(cp, INVALID_STATION); - SB(ge->status, GoodsEntry::GES_RATING, 1, 1); - } - } - } - } - - void FixPointers(BaseStation *bst) const override - { - Station *st = Station::From(bst); - - uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; - for (CargoID i = 0; i < num_cargo; i++) { - GoodsEntry *ge = &st->goods[i]; - if (IsSavegameVersionBefore(SLV_183)) { - SwapPackets(ge); // We have to swap back again to be in the format pre-183 expects. - SlObject(ge, this->GetDescription()); - SwapPackets(ge); - } else { - SlObject(ge, this->GetDescription()); - } - } - } -}; - -class SlRoadStopTileData : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(RoadStopTileData, tile, SLE_UINT32), - SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8), - SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8), - }; - inline const static SaveLoadCompatTable compat_description = {}; - - static uint8 last_num_specs; ///< Number of specs of the last loaded station. - - void Save(BaseStation *bst) const override - { - SlSetStructListLength(bst->custom_roadstop_tile_data.size()); - for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { - SlObject(&bst->custom_roadstop_tile_data[i], this->GetDescription()); - } - } - - void Load(BaseStation *bst) const override - { - uint32 num_tiles = (uint32)SlGetStructListLength(UINT32_MAX); - bst->custom_roadstop_tile_data.resize(num_tiles); - for (uint i = 0; i < num_tiles; i++) { - SlObject(&bst->custom_roadstop_tile_data[i], this->GetLoadDescription()); - } - } -}; - -/** - * SaveLoad handler for the BaseStation, which all other stations / waypoints - * make use of. - */ -class SlStationBase : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(BaseStation, xy, SLE_UINT32), - SLE_REF(BaseStation, town, REF_TOWN), - SLE_VAR(BaseStation, string_id, SLE_STRINGID), - SLE_STR(BaseStation, name, SLE_STR | SLF_ALLOW_CONTROL, 0), - SLE_VAR(BaseStation, delete_ctr, SLE_UINT8), - SLE_VAR(BaseStation, owner, SLE_UINT8), - SLE_VAR(BaseStation, facilities, SLE_UINT8), - SLE_VAR(BaseStation, build_date, SLE_INT32), - - /* Used by newstations for graphic variations */ - SLE_VAR(BaseStation, random_bits, SLE_UINT16), - SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8), - }; - inline const static SaveLoadCompatTable compat_description = _station_base_sl_compat; - - void Save(BaseStation *bst) const override - { - SlObject(bst, this->GetDescription()); - } - - void Load(BaseStation *bst) const override - { - SlObject(bst, this->GetLoadDescription()); - } - - void FixPointers(BaseStation *bst) const override - { - SlObject(bst, this->GetDescription()); - } -}; - -/** - * SaveLoad handler for a normal station (read: not a waypoint). - */ -class SlStationNormal : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLEG_STRUCT("base", SlStationBase), - SLE_VAR(Station, train_station.tile, SLE_UINT32), - SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16), - SLE_VAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16), - - SLE_REF(Station, bus_stops, REF_ROADSTOPS), - SLE_REF(Station, truck_stops, REF_ROADSTOPS), - SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_VAR(Station, airport.tile, SLE_UINT32), - SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), - SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), - SLE_VAR(Station, airport.type, SLE_UINT8), - SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION), - SLE_VAR(Station, airport.flags, SLE_UINT64), - SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION), - SLEG_CONDARR("storage", _old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161), - SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), - - SLE_VAR(Station, indtype, SLE_UINT8), - - SLE_VAR(Station, time_since_load, SLE_UINT8), - SLE_VAR(Station, time_since_unload, SLE_UINT8), - SLEG_VAR("last_vehicle_type", _old_last_vehicle_type, SLE_UINT8), - SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8), - SLE_REFVEC(Station, loading_vehicles, REF_VEHICLE), - SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES), - SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST("speclist", SlRoadStopTileData, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION), - SLEG_STRUCTLIST("goods", SlStationGoods), - }; - inline const static SaveLoadCompatTable compat_description = _station_normal_sl_compat; - - void Save(BaseStation *bst) const - { - if ((bst->facilities & FACIL_WAYPOINT) != 0) return; - SlObject(bst, this->GetDescription()); - } - - void Load(BaseStation *bst) const - { - if ((bst->facilities & FACIL_WAYPOINT) != 0) return; - SlObject(bst, this->GetLoadDescription()); - - for (CargoID i = 0; i < NUM_CARGO; i++) { - Station::From(bst)->goods[i].last_vehicle_type = _old_last_vehicle_type; - } - } - - void FixPointers(BaseStation *bst) const - { - if ((bst->facilities & FACIL_WAYPOINT) != 0) return; - SlObject(bst, this->GetDescription()); - } -}; - -class SlStationWaypoint : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLEG_STRUCT("base", SlStationBase), - SLE_VAR(Waypoint, town_cn, SLE_UINT16), - - SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _station_waypoint_sl_compat; - - void Save(BaseStation *bst) const - { - if ((bst->facilities & FACIL_WAYPOINT) == 0) return; - SlObject(bst, this->GetDescription()); - } - - void Load(BaseStation *bst) const - { - if ((bst->facilities & FACIL_WAYPOINT) == 0) return; - SlObject(bst, this->GetLoadDescription()); - } - - void FixPointers(BaseStation *bst) const - { - if ((bst->facilities & FACIL_WAYPOINT) == 0) return; - SlObject(bst, this->GetDescription()); - } -}; - -static const SaveLoad _station_desc[] = { - SLE_SAVEBYTE(BaseStation, facilities), - SLEG_STRUCT("normal", SlStationNormal), - SLEG_STRUCT("waypoint", SlStationWaypoint), - SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList, SLV_27, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST("roadstopspeclist", SlRoadStopSpecList, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION), -}; - -struct STNNChunkHandler : ChunkHandler { - STNNChunkHandler() : ChunkHandler('STNN', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_station_desc); - - /* Write the stations */ - for (BaseStation *st : BaseStation::Iterate()) { - SlSetArrayIndex(st->index); - SlObject(st, _station_desc); - } - } - - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_station_desc, _station_sl_compat); - - _old_num_flows = 0; - - int index; - while ((index = SlIterateArray()) != -1) { - bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0; - - BaseStation *bst = waypoint ? (BaseStation *)new (index) Waypoint() : new (index) Station(); - SlObject(bst, slt); - } - } - - void FixPointers() const override - { - /* From SLV_123 we store stations in STNN; before that in STNS. So do not - * fix pointers when the version is below SLV_123, as that would fix - * pointers twice: once in STNS chunk and once here. */ - if (IsSavegameVersionBefore(SLV_123)) return; - - for (BaseStation *bst : BaseStation::Iterate()) { - SlObject(bst, _station_desc); - } - } -}; - -struct ROADChunkHandler : ChunkHandler { - ROADChunkHandler() : ChunkHandler('ROAD', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_roadstop_desc); - - for (RoadStop *rs : RoadStop::Iterate()) { - SlSetArrayIndex(rs->index); - SlObject(rs, _roadstop_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_roadstop_desc, _roadstop_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - RoadStop *rs = new (index) RoadStop(INVALID_TILE); - - SlObject(rs, slt); - } - } - - void FixPointers() const override - { - for (RoadStop *rs : RoadStop::Iterate()) { - SlObject(rs, _roadstop_desc); - } - } -}; - -static const STNNChunkHandler STNN; -static const ROADChunkHandler ROAD; -static const ChunkHandlerRef station_chunk_handlers[] = { - STNN, - ROAD, -}; - -extern const ChunkHandlerTable _station_chunk_handlers(station_chunk_handlers); - -} diff --git a/src/saveload/upstream/town_sl.cpp b/src/saveload/upstream/town_sl.cpp deleted file mode 100644 index c6628fc810..0000000000 --- a/src/saveload/upstream/town_sl.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file town_sl.cpp Code handling saving and loading of towns and houses */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/town_sl_compat.h" - -#include "newgrf_sl.h" -#include "../../newgrf_house.h" -#include "../../town.h" -#include "../../landscape.h" -#include "../../subsidy_func.h" -#include "../../strings_func.h" -#include "../../tilematrix_type.hpp" - -#include "../../safeguards.h" - -namespace upstream_sl { - -typedef TileMatrix AcceptanceMatrix; - -class SlTownSupplied : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_CONDVAR(TransportedCargoStat, old_max, SLE_UINT32, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_max, SLE_UINT32, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, old_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _town_supplied_sl_compat; - - /** - * Get the number of cargoes used by this savegame version. - * @return The number of cargoes used by this savegame version. - */ - size_t GetNumCargo() const - { - if (IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES)) return 32; - if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return NUM_CARGO; - /* Read from the savegame how long the list is. */ - return SlGetStructListLength(NUM_CARGO); - } - - void Save(Town *t) const override - { - SlSetStructListLength(NUM_CARGO); - for (CargoID i = 0; i < NUM_CARGO; i++) { - SlObject(&t->supplied[i], this->GetDescription()); - } - } - - void Load(Town *t) const override - { - size_t num_cargo = this->GetNumCargo(); - for (size_t i = 0; i < num_cargo; i++) { - SlObject(&t->supplied[i], this->GetLoadDescription()); - } - } -}; - -class SlTownReceived : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_CONDVAR(TransportedCargoStat, old_max, SLE_UINT16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_max, SLE_UINT16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, old_act, SLE_UINT16, SLV_165, SL_MAX_VERSION), - SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT16, SLV_165, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _town_received_sl_compat; - - void Save(Town *t) const override - { - SlSetStructListLength(NUM_TE); - for (size_t i = TE_BEGIN; i < TE_END; i++) { - SlObject(&t->received[i], this->GetDescription()); - } - } - - void Load(Town *t) const override - { - size_t length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? (size_t)TE_END : SlGetStructListLength(TE_END); - for (size_t i = 0; i < length; i++) { - SlObject(&t->received[i], this->GetLoadDescription()); - } - } -}; - -class SlTownAcceptanceMatrix : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(AcceptanceMatrix, area.tile, SLE_UINT32), - SLE_VAR(AcceptanceMatrix, area.w, SLE_UINT16), - SLE_VAR(AcceptanceMatrix, area.h, SLE_UINT16), - }; - inline const static SaveLoadCompatTable compat_description = _town_acceptance_matrix_sl_compat; - - void Load(Town *t) const override - { - /* Discard now unused acceptance matrix. */ - AcceptanceMatrix dummy; - SlObject(&dummy, this->GetLoadDescription()); - if (dummy.area.w != 0) { - uint arr_len = dummy.area.w / AcceptanceMatrix::GRID * dummy.area.h / AcceptanceMatrix::GRID; - SlSkipBytes(4 * arr_len); - } - } -}; - -static const SaveLoad _town_desc[] = { - SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Town, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION), - - SLE_CONDVAR(Town, townnamegrfid, SLE_UINT32, SLV_66, SL_MAX_VERSION), - SLE_VAR(Town, townnametype, SLE_UINT16), - SLE_VAR(Town, townnameparts, SLE_UINT32), - SLE_CONDSTR(Town, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), - - SLE_VAR(Town, flags, SLE_UINT8), - SLE_CONDVAR(Town, statues, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), - SLE_CONDVAR(Town, statues, SLE_UINT16, SLV_104, SL_MAX_VERSION), - - SLE_CONDVAR(Town, have_ratings, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), - SLE_CONDVAR(Town, have_ratings, SLE_UINT16, SLV_104, SL_MAX_VERSION), - SLE_CONDARR(Town, ratings, SLE_INT16, 8, SL_MIN_VERSION, SLV_104), - SLE_CONDARR(Town, ratings, SLE_INT16, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), - SLE_CONDARR(Town, unwanted, SLE_INT8, 8, SLV_4, SLV_104), - SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), - - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_UINT32, SLV_9, SLV_165), - - SLE_CONDVAR(Town, received[TE_FOOD].old_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), - SLE_CONDVAR(Town, received[TE_WATER].old_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), - SLE_CONDVAR(Town, received[TE_FOOD].new_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), - SLE_CONDVAR(Town, received[TE_WATER].new_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), - - SLE_CONDARR(Town, goal, SLE_UINT32, NUM_TE, SLV_165, SL_MAX_VERSION), - - SLE_CONDSSTR(Town, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_168, SL_MAX_VERSION), - - SLE_CONDVAR(Town, time_until_rebuild, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54), - SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT16, SLV_54, SL_MAX_VERSION), - SLE_CONDVAR(Town, grow_counter, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54), - SLE_CONDVAR(Town, grow_counter, SLE_UINT16, SLV_54, SL_MAX_VERSION), - SLE_CONDVAR(Town, growth_rate, SLE_FILE_U8 | SLE_VAR_I16, SL_MIN_VERSION, SLV_54), - SLE_CONDVAR(Town, growth_rate, SLE_FILE_I16 | SLE_VAR_U16, SLV_54, SLV_165), - SLE_CONDVAR(Town, growth_rate, SLE_UINT16, SLV_165, 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, SLV_2, SL_MAX_VERSION), - SLE_CONDVAR(Town, exclusive_counter, SLE_UINT8, SLV_2, SL_MAX_VERSION), - - SLE_CONDVAR(Town, larger_town, SLE_BOOL, SLV_56, SL_MAX_VERSION), - SLE_CONDVAR(Town, layout, SLE_UINT8, SLV_113, SL_MAX_VERSION), - - SLE_CONDREFLIST(Town, psa_list, REF_STORAGE, SLV_161, SL_MAX_VERSION), - - SLEG_CONDSTRUCTLIST("supplied", SlTownSupplied, SLV_165, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST("received", SlTownReceived, SLV_165, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST("acceptance_matrix", SlTownAcceptanceMatrix, SLV_166, SLV_REMOVE_TOWN_CARGO_CACHE), -}; - -struct HIDSChunkHandler : NewGRFMappingChunkHandler { - HIDSChunkHandler() : NewGRFMappingChunkHandler('HIDS', _house_mngr) {} -}; - -struct CITYChunkHandler : ChunkHandler { - CITYChunkHandler() : ChunkHandler('CITY', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_town_desc); - - for (Town *t : Town::Iterate()) { - SlSetArrayIndex(t->index); - SlObject(t, _town_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_town_desc, _town_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - Town *t = new (index) Town(); - SlObject(t, slt); - - if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GetStringTab(t->townnametype) != TEXT_TAB_OLD_CUSTOM) { - SlErrorCorrupt("Invalid town name generator"); - } - } - } - - void FixPointers() const override - { - if (IsSavegameVersionBefore(SLV_161)) return; - - for (Town *t : Town::Iterate()) { - SlObject(t, _town_desc); - } - } -}; - -static const HIDSChunkHandler HIDS; -static const CITYChunkHandler CITY; -static const ChunkHandlerRef town_chunk_handlers[] = { - HIDS, - CITY, -}; - -extern const ChunkHandlerTable _town_chunk_handlers(town_chunk_handlers); - -} diff --git a/src/saveload/upstream/vehicle_sl.cpp b/src/saveload/upstream/vehicle_sl.cpp deleted file mode 100644 index 4bf52cde27..0000000000 --- a/src/saveload/upstream/vehicle_sl.cpp +++ /dev/null @@ -1,551 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file vehicle_sl.cpp Code handling saving and loading of vehicles */ - -#include "../../stdafx.h" - -#include "saveload.h" -#include "compat/vehicle_sl_compat.h" - -#include "../../vehicle_func.h" -#include "../../train.h" -#include "../../roadveh.h" -#include "../../ship.h" -#include "../../aircraft.h" -#include "../../station_base.h" -#include "../../effectvehicle_base.h" -#include "../../company_base.h" -#include "../../company_func.h" -#include "../../disaster_vehicle.h" - -#include - -#include "../../safeguards.h" - -void AfterLoadVehicles(bool part_of_load); -bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // From train_cmd.cpp -void ReverseTrainDirection(Train *v); -void ReverseTrainSwapVeh(Train *v, int l, int r); - -namespace upstream_sl { - -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; - -class SlVehicleCommon : public DefaultSaveLoadHandler { -public: -#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) - /* This table access private members of other classes; they have this - * class as friend. For MSVC CL 19.15 and 19.16 this doesn't work for - * "inline static const", so we are forced to wrap the table in a - * function. CL 19.16 is the latest for VS2017. */ - inline static const SaveLoad description[] = {{}}; - SaveLoadTable GetDescription() const override { -#else - inline -#endif - static const SaveLoad description[] = { - SLE_VAR(Vehicle, subtype, SLE_UINT8), - - SLE_REF(Vehicle, next, REF_VEHICLE_OLD), - //SLE_CONDVAR(Vehicle, name, SLE_NAME, SL_MIN_VERSION, SLV_84), - SLE_CONDSTR(Vehicle, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_8), - SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, SLV_8, SL_MAX_VERSION), - SLE_VAR(Vehicle, owner, SLE_UINT8), - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), - SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION), - SLE_VAR(Vehicle, direction, SLE_UINT8), - - SLE_VAR(Vehicle, spritenum, SLE_UINT8), - SLE_VAR(Vehicle, engine_type, SLE_UINT16), - SLE_VAR(Vehicle, cur_speed, SLE_UINT16), - SLE_VAR(Vehicle, subspeed, SLE_UINT8), - SLE_VAR(Vehicle, acceleration, SLE_UINT8), - SLE_CONDVAR(Vehicle, motion_counter, SLE_UINT32, SLV_VEH_MOTION_COUNTER, SL_MAX_VERSION), - SLE_VAR(Vehicle, progress, SLE_UINT8), - - SLE_VAR(Vehicle, vehstatus, SLE_UINT8), - SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, last_loading_station, SLE_UINT16, SLV_182, SL_MAX_VERSION), - - SLE_VAR(Vehicle, cargo_type, SLE_UINT8), - SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, SLV_35, SL_MAX_VERSION), - SLEG_CONDVAR("cargo_days", _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68), - SLEG_CONDVAR("cargo_source", _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), - SLEG_CONDVAR("cargo_source", _cargo_source, SLE_UINT16, SLV_7, SLV_68), - SLEG_CONDVAR("cargo_source_xy", _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), - SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), - SLE_CONDVAR(Vehicle, refit_cap, SLE_UINT16, SLV_182, SL_MAX_VERSION), - SLEG_CONDVAR("cargo_count", _cargo_count, SLE_UINT16, SL_MIN_VERSION, SLV_68), - SLE_CONDREFDEQUE(Vehicle, cargo.packets, REF_CARGO_PACKET, SLV_68, SL_MAX_VERSION), - SLE_CONDARR(Vehicle, cargo.action_counts, SLE_UINT, VehicleCargoList::NUM_MOVE_TO_ACTION, SLV_181, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, cargo_age_counter, SLE_UINT16, SLV_162, SL_MAX_VERSION), - - SLE_VAR(Vehicle, day_counter, SLE_UINT8), - SLE_VAR(Vehicle, tick_counter, SLE_UINT8), - SLE_CONDVAR(Vehicle, running_ticks, SLE_FILE_U8 | SLE_VAR_U16, SLV_88, SL_MAX_VERSION), - - SLE_VAR(Vehicle, cur_implicit_order_index, SLE_FILE_U8 | SLE_VAR_U16), - SLE_CONDVAR(Vehicle, cur_real_order_index, SLE_FILE_U8 | SLE_VAR_U16, SLV_158, SL_MAX_VERSION), - - /* 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_CONDVAR(Vehicle, current_order.type, SLE_UINT8, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), - - /* Orders for version 5 and on */ - SLE_CONDVAR(Vehicle, current_order.type, SLE_UINT8, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, current_order.flags, SLE_FILE_U8 | SLE_VAR_U16, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, current_order.dest, SLE_UINT16, SLV_5, SL_MAX_VERSION), - - /* Refit in current order */ - SLE_CONDVAR(Vehicle, current_order.refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION), - - /* Timetable in current order */ - SLE_CONDVAR(Vehicle, current_order.wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, current_order.travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, SLV_174, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, timetable_start, SLE_INT32, SLV_129, SL_MAX_VERSION), - - SLE_CONDREF(Vehicle, orders, REF_ORDER, SL_MIN_VERSION, SLV_105), - SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, SLV_105, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, max_age, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SLV_31, SLV_180), - SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SLV_180, 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, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, build_year, SLE_INT32, SLV_31, SL_MAX_VERSION), - - SLE_VAR(Vehicle, load_unload_ticks, SLE_UINT16), - SLEG_CONDVAR("cargo_paid_for", _cargo_paid_for, SLE_UINT16, SLV_45, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_40, SLV_180), - SLE_CONDVAR(Vehicle, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, SLV_65, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, SLV_65, SL_MAX_VERSION), - SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64, SLV_51, SLV_65), - SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), - SLEG_CONDVAR("cargo_loaded_at_xy", _cargo_loaded_at_xy, SLE_UINT32, SLV_51, SLV_68), - SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(Vehicle, value, SLE_INT64, SLV_65, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, random_bits, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SLV_EXTEND_VEHICLE_RANDOM), - SLE_CONDVAR(Vehicle, random_bits, SLE_UINT16, SLV_EXTEND_VEHICLE_RANDOM, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, SLV_2, SL_MAX_VERSION), - - SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, SLV_2, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, SLV_60, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, current_order_time, SLE_UINT32, SLV_67, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, last_loading_tick, SLE_UINT64, SLV_LAST_LOADING_TICK, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, lateness_counter, SLE_INT32, SLV_67, SL_MAX_VERSION), - }; -#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) - return description; - } -#endif - inline const static SaveLoadCompatTable compat_description = _vehicle_common_sl_compat; - - void Save(Vehicle *v) const override - { - SlObject(v, this->GetDescription()); - } - - void Load(Vehicle *v) const override - { - SlObject(v, this->GetLoadDescription()); - } - - void FixPointers(Vehicle *v) const override - { - SlObject(v, this->GetDescription()); - } -}; - -class SlVehicleTrain : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLEG_STRUCT("common", SlVehicleCommon), - SLE_VAR(Train, crash_anim_pos, SLE_UINT16), - SLE_VAR(Train, force_proceed, SLE_UINT8), - SLE_VAR(Train, railtype, SLE_UINT8), - SLE_VAR(Train, track, SLE_UINT8), - - SLE_CONDVAR(Train, flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_2, SLV_100), - SLE_CONDVAR(Train, flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_100, SL_MAX_VERSION), - SLE_CONDVAR(Train, wait_counter, SLE_UINT16, SLV_136, SL_MAX_VERSION), - SLE_CONDVAR(Train, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _vehicle_train_sl_compat; - - void Save(Vehicle *v) const override - { - if (v->type != VEH_TRAIN) return; - SlObject(v, this->GetDescription()); - } - - void Load(Vehicle *v) const override - { - if (v->type != VEH_TRAIN) return; - SlObject(v, this->GetLoadDescription()); - if (v->cur_real_order_index == 0xFF) v->cur_real_order_index = INVALID_VEH_ORDER_ID; - if (v->cur_implicit_order_index == 0xFF) v->cur_implicit_order_index = INVALID_VEH_ORDER_ID; - } - - void FixPointers(Vehicle *v) const override - { - if (v->type != VEH_TRAIN) return; - SlObject(v, this->GetDescription()); - } -}; - -class SlVehicleRoadVeh : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLEG_STRUCT("common", SlVehicleCommon), - SLE_VAR(RoadVehicle, state, SLE_UINT8), - SLE_VAR(RoadVehicle, frame, SLE_UINT8), - SLE_VAR(RoadVehicle, blocked_ctr, SLE_UINT16), - SLE_VAR(RoadVehicle, overtaking, SLE_UINT8), - SLE_VAR(RoadVehicle, overtaking_ctr, SLE_UINT8), - SLE_VAR(RoadVehicle, crashed_ctr, SLE_UINT16), - SLE_VAR(RoadVehicle, reverse_ctr, SLE_UINT8), - SLE_CONDDEQUE(RoadVehicle, path.td, SLE_UINT8, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION), - SLE_CONDDEQUE(RoadVehicle, path.tile, SLE_UINT32, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION), - SLE_CONDVAR(RoadVehicle, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _vehicle_roadveh_sl_compat; - - void Save(Vehicle *v) const override - { - if (v->type != VEH_ROAD) return; - SlObject(v, this->GetDescription()); - } - - void Load(Vehicle *v) const override - { - if (v->type != VEH_ROAD) return; - SlObject(v, this->GetLoadDescription()); - } - - void FixPointers(Vehicle *v) const override - { - if (v->type != VEH_ROAD) return; - SlObject(v, this->GetDescription()); - } -}; - -class SlVehicleShip : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLEG_STRUCT("common", SlVehicleCommon), - SLE_VAR(Ship, state, SLE_UINT8), - SLE_CONDDEQUE(Ship, path, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION), - SLE_CONDVAR(Ship, rotation, SLE_UINT8, SLV_SHIP_ROTATION, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _vehicle_ship_sl_compat; - - void Save(Vehicle *v) const override - { - if (v->type != VEH_SHIP) return; - SlObject(v, this->GetDescription()); - } - - void Load(Vehicle *v) const override - { - if (v->type != VEH_SHIP) return; - SlObject(v, this->GetLoadDescription()); - } - - void FixPointers(Vehicle *v) const override - { - if (v->type != VEH_SHIP) return; - SlObject(v, this->GetDescription()); - } -}; - -class SlVehicleAircraft : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLEG_STRUCT("common", SlVehicleCommon), - SLE_VAR(Aircraft, crashed_counter, SLE_UINT16), - SLE_VAR(Aircraft, pos, SLE_UINT8), - - SLE_CONDVAR(Aircraft, targetairport, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Aircraft, targetairport, SLE_UINT16, SLV_5, SL_MAX_VERSION), - - SLE_VAR(Aircraft, state, SLE_UINT8), - - SLE_CONDVAR(Aircraft, previous_pos, SLE_UINT8, SLV_2, SL_MAX_VERSION), - SLE_CONDVAR(Aircraft, last_direction, SLE_UINT8, SLV_2, SL_MAX_VERSION), - SLE_CONDVAR(Aircraft, number_consecutive_turns, SLE_UINT8, SLV_2, SL_MAX_VERSION), - - SLE_CONDVAR(Aircraft, turn_counter, SLE_UINT8, SLV_136, SL_MAX_VERSION), - SLE_CONDVAR(Aircraft, flags, SLE_UINT8, SLV_167, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _vehicle_aircraft_sl_compat; - - void Save(Vehicle *v) const override - { - if (v->type != VEH_AIRCRAFT) return; - SlObject(v, this->GetDescription()); - } - - void Load(Vehicle *v) const override - { - if (v->type != VEH_AIRCRAFT) return; - SlObject(v, this->GetLoadDescription()); - } - - void FixPointers(Vehicle *v) const override - { - if (v->type != VEH_AIRCRAFT) return; - SlObject(v, this->GetDescription()); - } -}; - -class SlVehicleEffect : public DefaultSaveLoadHandler { -public: - inline static const SaveLoad description[] = { - SLE_VAR(Vehicle, subtype, SLE_UINT8), - - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), - SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION), - - SLE_VAR2(Vehicle, "sprite_cache.sprite_seq.seq[0].sprite", sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), - SLE_VAR(Vehicle, progress, SLE_UINT8), - SLE_VAR(Vehicle, vehstatus, SLE_UINT8), - - SLE_VAR(EffectVehicle, animation_state, SLE_UINT16), - SLE_VAR(EffectVehicle, animation_substate, SLE_UINT8), - - SLE_CONDVAR(Vehicle, spritenum, SLE_UINT8, SLV_2, SL_MAX_VERSION), - }; - inline const static SaveLoadCompatTable compat_description = _vehicle_effect_sl_compat; - - void Save(Vehicle *v) const override - { - if (v->type != VEH_EFFECT) return; - SlObject(v, this->GetDescription()); - } - - void Load(Vehicle *v) const override - { - if (v->type != VEH_EFFECT) return; - SlObject(v, this->GetLoadDescription()); - } - - void FixPointers(Vehicle *v) const override - { - if (v->type != VEH_EFFECT) return; - SlObject(v, this->GetDescription()); - } -}; - -class SlVehicleDisaster : public DefaultSaveLoadHandler { -public: -#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) - /* This table access private members of other classes; they have this - * class as friend. For MSVC CL 19.15 and 19.16 this doesn't work for - * "inline static const", so we are forced to wrap the table in a - * function. CL 19.16 is the latest for VS2017. */ - inline static const SaveLoad description[] = {{}}; - SaveLoadTable GetDescription() const override { -#else - inline -#endif - static const SaveLoad description[] = { - SLE_REF(Vehicle, next, REF_VEHICLE_OLD), - - SLE_VAR(Vehicle, subtype, SLE_UINT8), - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), - SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION), - SLE_VAR(Vehicle, direction, SLE_UINT8), - - SLE_VAR(Vehicle, owner, SLE_UINT8), - SLE_VAR(Vehicle, vehstatus, SLE_UINT8), - SLE_CONDVARNAME(DisasterVehicle, state, "current_order.dest", SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), - SLE_CONDVARNAME(DisasterVehicle, state, "current_order.dest", SLE_UINT16, SLV_5, SLV_DISASTER_VEH_STATE), - SLE_CONDVAR(DisasterVehicle, state, SLE_UINT16, SLV_DISASTER_VEH_STATE, SL_MAX_VERSION), - - - SLE_VAR2(Vehicle, "sprite_cache.sprite_seq.seq[0].sprite", sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), - SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_VAR(Vehicle, tick_counter, SLE_UINT8), - - SLE_CONDVAR(DisasterVehicle, image_override, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_191), - SLE_CONDVAR(DisasterVehicle, image_override, SLE_UINT32, SLV_191, SL_MAX_VERSION), - SLE_CONDVAR(DisasterVehicle, big_ufo_destroyer_target, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_191), - SLE_CONDVAR(DisasterVehicle, big_ufo_destroyer_target, SLE_UINT32, SLV_191, SL_MAX_VERSION), - SLE_CONDVAR(DisasterVehicle, flags, SLE_UINT8, SLV_194, SL_MAX_VERSION), - }; -#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) - return description; - } -#endif - inline const static SaveLoadCompatTable compat_description = _vehicle_disaster_sl_compat; - - void Save(Vehicle *v) const override - { - if (v->type != VEH_DISASTER) return; - SlObject(v, this->GetDescription()); - } - - void Load(Vehicle *v) const override - { - if (v->type != VEH_DISASTER) return; - SlObject(v, this->GetLoadDescription()); - } - - void FixPointers(Vehicle *v) const override - { - if (v->type != VEH_DISASTER) return; - SlObject(v, this->GetDescription()); - } -}; - -const static SaveLoad _vehicle_desc[] = { - SLE_SAVEBYTE(Vehicle, type), - SLEG_STRUCT("train", SlVehicleTrain), - SLEG_STRUCT("roadveh", SlVehicleRoadVeh), - SLEG_STRUCT("ship", SlVehicleShip), - SLEG_STRUCT("aircraft", SlVehicleAircraft), - SLEG_STRUCT("effect", SlVehicleEffect), - SLEG_STRUCT("disaster", SlVehicleDisaster), -}; - -struct VEHSChunkHandler : ChunkHandler { - VEHSChunkHandler() : ChunkHandler('VEHS', CH_SPARSE_TABLE) {} - - void Save() const override - { - SlTableHeader(_vehicle_desc); - - /* Write the vehicles */ - for (Vehicle *v : Vehicle::Iterate()) { - SlSetArrayIndex(v->index); - SlObject(v, _vehicle_desc); - } - } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_vehicle_desc, _vehicle_sl_compat); - - 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: // Savegame shouldn't contain invalid vehicles - default: SlErrorCorrupt("Invalid vehicle type"); - } - - SlObject(v, slt); - - if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v) && CargoPacket::CanAllocateItem()) { - /* Don't construct the packet with station here, because that'll fail with old savegames */ - CargoPacket *cp = new CargoPacket(_cargo_count, _cargo_days, _cargo_source, _cargo_source_xy, _cargo_loaded_at_xy, _cargo_feeder_share); - v->cargo.Append(cp); - } - -#if 0 - /* Old savegames used 'last_station_visited = 0xFF' */ - if (IsSavegameVersionBefore(SLV_5) && v->last_station_visited == 0xFF) { - v->last_station_visited = INVALID_STATION; - } - - if (IsSavegameVersionBefore(SLV_182)) v->last_loading_station = INVALID_STATION; - - if (IsSavegameVersionBefore(SLV_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 (IsSavegameVersionBefore(SLV_60)) v->group_id = DEFAULT_GROUP; -#endif - } - } - - void FixPointers() const override - { - for (Vehicle *v : Vehicle::Iterate()) { - SlObject(v, _vehicle_desc); - } - } -}; - -static const VEHSChunkHandler VEHS; -static const ChunkHandlerRef veh_chunk_handlers[] = { - VEHS, -}; - -extern const ChunkHandlerTable _veh_chunk_handlers(veh_chunk_handlers); - -} diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 4a05a98018..2bf915b133 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -8,6 +8,10 @@ /** @file vehicle_sl.cpp Code handling saving and loading of vehicles */ #include "../stdafx.h" + +#include "saveload.h" +#include "compat/vehicle_sl_compat.h" + #include "../vehicle_func.h" #include "../train.h" #include "../roadveh.h" @@ -18,622 +22,17 @@ #include "../company_base.h" #include "../company_func.h" #include "../disaster_vehicle.h" -#include "../scope_info.h" -#include "../string_func.h" -#include "../error.h" -#include "../strings_func.h" -#include "../3rdparty/cpp-btree/btree_map.h" -#include "../3rdparty/fmt/format.h" - -#include "saveload.h" #include #include "../safeguards.h" -extern btree::btree_multimap _pending_speed_restriction_change_map; - -/** - * Link front and rear multiheaded engines to each other - * This is done when loading a savegame - */ -void ConnectMultiheadedTrains() -{ - for (Train *v : Train::Iterate()) { - v->other_multiheaded_part = nullptr; - } - - for (Train *v : Train::Iterate()) { - if (v->IsFrontEngine() || v->IsFreeWagon()) { - /* 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 = v->IsFrontEngine(); - - for (Train *u = v; u != nullptr; u = u->GetNextVehicle()) { - if (u->other_multiheaded_part != nullptr) continue; // we already linked this one - - if (u->IsMultiheaded()) { - if (!u->IsEngine()) { - /* we got a rear car without a front car. We will convert it to a front one */ - u->SetEngine(); - u->spritenum--; - } - - /* Find a matching back part */ - EngineID eid = u->engine_type; - Train *w; - if (sequential_matching) { - for (w = u->GetNextVehicle(); w != nullptr; w = w->GetNextVehicle()) { - if (w->engine_type != eid || w->other_multiheaded_part != nullptr || !w->IsMultiheaded()) continue; - - /* we found a car to partner with this engine. Now we will make sure it face the right way */ - if (w->IsEngine()) { - w->ClearEngine(); - w->spritenum++; - } - break; - } - } else { - uint stack_pos = 0; - for (w = u->GetNextVehicle(); w != nullptr; w = w->GetNextVehicle()) { - if (w->engine_type != eid || w->other_multiheaded_part != nullptr || !w->IsMultiheaded()) continue; - - if (w->IsEngine()) { - stack_pos++; - } else { - if (stack_pos == 0) break; - stack_pos--; - } - } - } - - if (w != nullptr) { - w->other_multiheaded_part = u; - u->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 */ - u->ClearMultiheaded(); - } - } - } - } - } -} - -/** - * 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() -{ - for (Train *t : Train::Iterate()) SetBit(t->subtype, 7); // indicates that it's the old format and needs to be converted in the next loop - - for (Train *t : Train::Iterate()) { - if (HasBit(t->subtype, 7) && ((t->subtype & ~0x80) == 0 || (t->subtype & ~0x80) == 4)) { - for (Train *u = t; u != nullptr; 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) u->SetMultiheaded(); - u->SetFrontEngine(); - u->SetEngine(); - break; - - case 1: // TS_Artic_Part - u->subtype = 0; - u->SetArticulatedPart(); - break; - - case 2: // TS_Not_First - u->subtype = 0; - if (rvi->railveh_type == RAILVEH_WAGON) { - /* normal wagon */ - u->SetWagon(); - break; - } - if (rvi->railveh_type == RAILVEH_MULTIHEAD && rvi->image_index == u->spritenum - 1) { - /* rear end of a multiheaded engine */ - u->SetMultiheaded(); - break; - } - if (rvi->railveh_type == RAILVEH_MULTIHEAD) u->SetMultiheaded(); - u->SetEngine(); - break; - - case 4: // TS_Free_Car - u->subtype = 0; - u->SetWagon(); - u->SetFreeWagon(); - break; - default: SlErrorCorrupt("Invalid train subtype"); - } - } - } - } -} - - -/** need to be called to load aircraft from old version */ -void UpdateOldAircraft() -{ - /* set airport_flags to 0 for all airports just to be sure */ - for (Station *st : Station::Iterate()) { - st->airport.flags = 0; // reset airport - } - - for (Aircraft *a : Aircraft::Iterate()) { - /* airplane has another vehicle with subtype 4 (shadow), helicopter also has 3 (rotor) - * skip those */ - if (a->IsNormalAircraft()) { - /* airplane in terminal stopped doesn't hurt anyone, so goto next */ - if ((a->vehstatus & VS_STOPPED) && a->state == 0) { - a->state = HANGAR; - continue; - } - - AircraftLeaveHangar(a, a->direction); // make airplane visible if it was in a depot for example - a->vehstatus &= ~VS_STOPPED; // make airplane moving - UpdateAircraftCache(a); - a->cur_speed = a->vcache.cached_max_speed; // so aircraft don't have zero speed while in air - if (!a->current_order.IsType(OT_GOTO_STATION) && !a->current_order.IsType(OT_GOTO_DEPOT)) { - /* reset current order so aircraft doesn't have invalid "station-only" order */ - a->current_order.MakeDummy(); - } - a->state = FLYING; - AircraftNextAirportPos_and_Order(a); // move it to the entry point of the airport - GetNewVehiclePosResult gp = GetNewVehiclePos(a); - a->tile = 0; // aircraft in air is tile=0 - - /* correct speed of helicopter-rotors */ - if (a->subtype == AIR_HELICOPTER) a->Next()->Next()->cur_speed = 32; - - /* set new position x,y,z */ - GetAircraftFlightLevelBounds(a, &a->z_pos, nullptr); - SetAircraftPosition(a, gp.x, gp.y, GetAircraftFlightLevel(a)); - } - } -} - -/** - * Check all vehicles to ensure their engine type is valid - * for the currently loaded NewGRFs (that includes none...) - * This only makes a difference if NewGRFs are missing, otherwise - * all vehicles will be valid. This does not make such a game - * playable, it only prevents crash. - */ -static void CheckValidVehicles() -{ - size_t total_engines = Engine::GetPoolSize(); - EngineID first_engine[4] = { INVALID_ENGINE, INVALID_ENGINE, INVALID_ENGINE, INVALID_ENGINE }; - - for (const Engine *e : Engine::IterateType(VEH_TRAIN)) { first_engine[VEH_TRAIN] = e->index; break; } - for (const Engine *e : Engine::IterateType(VEH_ROAD)) { first_engine[VEH_ROAD] = e->index; break; } - for (const Engine *e : Engine::IterateType(VEH_SHIP)) { first_engine[VEH_SHIP] = e->index; break; } - for (const Engine *e : Engine::IterateType(VEH_AIRCRAFT)) { first_engine[VEH_AIRCRAFT] = e->index; break; } - - for (Vehicle *v : Vehicle::Iterate()) { - /* Test if engine types match */ - switch (v->type) { - case VEH_TRAIN: - case VEH_ROAD: - case VEH_SHIP: - case VEH_AIRCRAFT: - if (v->engine_type >= total_engines || v->type != v->GetEngine()->type) { - v->engine_type = first_engine[v->type]; - } - break; - - default: - break; - } - } -} - -extern byte _age_cargo_skip_counter; // From misc_sl.cpp - -static std::vector _load_invalid_vehicles_to_delete; - -/** Called after load to update coordinates */ -void AfterLoadVehicles(bool part_of_load) -{ - _load_invalid_vehicles_to_delete.clear(); - - const Vehicle *si_v = nullptr; - SCOPE_INFO_FMT([&si_v], "AfterLoadVehicles: %s", scope_dumper().VehicleInfo(si_v)); - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - /* Reinstate the previous pointer */ - if (v->Next() != nullptr) { - v->Next()->previous = v; - if (v->type == VEH_TRAIN && (HasBit(v->subtype, GVSF_VIRTUAL) != HasBit(v->Next()->subtype, GVSF_VIRTUAL))) { - SlErrorCorrupt("Mixed virtual/non-virtual train consist"); - } - } - if (v->NextShared() != nullptr) v->NextShared()->previous_shared = v; - - if (part_of_load) v->fill_percent_te_id = INVALID_TE_ID; - v->first = nullptr; - if (v->IsGroundVehicle()) v->GetGroundVehicleCache()->first_engine = INVALID_ENGINE; - } - - /* 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 (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - if (v->old_orders != nullptr) { - if (IsSavegameVersionBefore(SLV_105)) { // Pre-105 didn't save an OrderList - if (mapping[v->old_orders] == nullptr) { - /* This adds the whole shared vehicle chain for case b */ - - /* Creating an OrderList here is safe because the number of vehicles - * allowed in these savegames matches the number of OrderLists. As - * such each vehicle can get an OrderList and it will (still) fit. */ - assert(OrderList::CanAllocateItem()); - v->orders = mapping[v->old_orders] = new OrderList(v->old_orders, v); - } else { - v->orders = mapping[v->old_orders]; - /* For old games (case a) we must create the shared vehicle chain */ - if (IsSavegameVersionBefore(SLV_5, 2)) { - v->AddToShared(v->orders->GetFirstSharedVehicle()); - } - } - } else { // OrderList was saved as such, only recalculate not saved values - if (v->PreviousShared() == nullptr) { - v->orders->Initialize(v->orders->first, v); - } - } - } - } - } - - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - /* Fill the first pointers */ - if (v->Previous() == nullptr) { - for (Vehicle *u = v; u != nullptr; u = u->Next()) { - u->first = v; - } - } - } - - if (part_of_load) { - if (IsSavegameVersionBefore(SLV_105)) { - /* Before 105 there was no order for shared orders, thus it messed up horribly */ - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - if (v->First() != v || v->orders != nullptr || v->previous_shared != nullptr || v->next_shared == nullptr) continue; - - /* As above, allocating OrderList here is safe. */ - assert(OrderList::CanAllocateItem()); - v->orders = new OrderList(nullptr, v); - for (Vehicle *u = v; u != nullptr; u = u->next_shared) { - u->orders = v->orders; - } - } - } - - if (IsSavegameVersionBefore(SLV_157)) { - /* The road vehicle subtype was converted to a flag. */ - for (RoadVehicle *rv : RoadVehicle::Iterate()) { - si_v = rv; - if (rv->subtype == 0) { - /* The road vehicle is at the front. */ - rv->SetFrontEngine(); - } else if (rv->subtype == 1) { - /* The road vehicle is an articulated part. */ - rv->subtype = 0; - rv->SetArticulatedPart(); - } else { - SlErrorCorrupt("Invalid road vehicle subtype"); - } - } - } - - if (IsSavegameVersionBefore(SLV_160)) { - /* In some old savegames there might be some "crap" stored. */ - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - if (!v->IsPrimaryVehicle() && v->type != VEH_DISASTER) { - v->current_order.Free(); - v->unitnumber = 0; - } - } - } - - if (IsSavegameVersionBefore(SLV_162)) { - /* Set the vehicle-local cargo age counter from the old global counter. */ - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - v->cargo_age_counter = _age_cargo_skip_counter; - } - } - - if (IsSavegameVersionBefore(SLV_180)) { - /* Set service interval flags */ - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - if (!v->IsPrimaryVehicle()) continue; - - const Company *c = Company::Get(v->owner); - int interval = CompanyServiceInterval(c, v->type); - - v->SetServiceIntervalIsCustom(v->GetServiceInterval() != interval); - v->SetServiceIntervalIsPercent(c->settings.vehicle.servint_ispercent); - } - } - - if (IsSavegameVersionBefore(SLV_SHIP_ROTATION)) { - /* Ship rotation added */ - for (Ship *s : Ship::Iterate()) { - s->rotation = s->direction; - } - } else { - for (Ship *s : Ship::Iterate()) { - if (s->rotation == s->direction) continue; - /* In case we are rotating on gameload, set the rotation position to - * the current position, otherwise the applied workaround offset would - * be with respect to 0,0. - */ - s->rotation_x_pos = s->x_pos; - s->rotation_y_pos = s->y_pos; - } - } - - if (SlXvIsFeaturePresent(XSLFI_TEMPLATE_REPLACEMENT) && (_network_server || !_networking)) { - for (Train *t : Train::Iterate()) { - si_v = t; - if (t->IsVirtual() && t->First() == t) { - delete t; - } - } - } - } - si_v = nullptr; - - CheckValidVehicles(); - - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - assert(v->first != nullptr); - - v->trip_occupancy = CalcPercentVehicleFilled(v, nullptr); - - switch (v->type) { - case VEH_TRAIN: { - Train *t = Train::From(v); - if (t->IsFrontEngine() || t->IsFreeWagon()) { - t->gcache.last_speed = t->cur_speed; // update displayed train speed - t->ConsistChanged(CCF_SAVELOAD); - } - break; - } - - case VEH_ROAD: { - RoadVehicle *rv = RoadVehicle::From(v); - if (rv->IsFrontEngine()) { - rv->gcache.last_speed = rv->cur_speed; // update displayed road vehicle speed - - rv->roadtype = Engine::Get(rv->engine_type)->u.road.roadtype; - rv->compatible_roadtypes = GetRoadTypeInfo(rv->roadtype)->powered_roadtypes; - bool is_invalid = false; - for (RoadVehicle *u = rv; u != nullptr; u = u->Next()) { - u->roadtype = rv->roadtype; - u->compatible_roadtypes = rv->compatible_roadtypes; - if (GetRoadType(u->tile, GetRoadTramType(u->roadtype)) == INVALID_ROADTYPE) is_invalid = true; - } - - if (is_invalid && part_of_load) { - _load_invalid_vehicles_to_delete.push_back(rv); - break; - } - - RoadVehUpdateCache(rv); - if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) { - rv->CargoChanged(); - } - } - break; - } - - case VEH_SHIP: - if (Ship::From(v)->IsPrimaryVehicle()) { - Ship::From(v)->UpdateCache(); - } - break; - - default: break; - } - } - - /* Stop non-front engines */ - if (part_of_load && IsSavegameVersionBefore(SLV_112)) { - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - if (v->type == VEH_TRAIN) { - Train *t = Train::From(v); - if (!t->IsFrontEngine()) { - if (t->IsEngine()) t->vehstatus |= VS_STOPPED; - /* cur_speed is now relevant for non-front parts - nonzero breaks - * moving-wagons-inside-depot- and autoreplace- code */ - t->cur_speed = 0; - } - } - /* trains weren't stopping gradually in old OTTD versions (and TTO/TTD) - * other vehicle types didn't have zero speed while stopped (even in 'recent' OTTD versions) */ - if ((v->vehstatus & VS_STOPPED) && (v->type != VEH_TRAIN || IsSavegameVersionBefore(SLV_2, 1))) { - v->cur_speed = 0; - } - } - } - - for (Vehicle *v : Vehicle::Iterate()) { - si_v = v; - switch (v->type) { - case VEH_ROAD: - case VEH_TRAIN: - case VEH_SHIP: - v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq); - v->UpdateSpriteSeqBound(); - break; - - case VEH_AIRCRAFT: - if (Aircraft::From(v)->IsNormalAircraft()) { - v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq); - v->UpdateSpriteSeqBound(); - - /* The aircraft's shadow will have the same image as the aircraft, but no colour */ - Vehicle *shadow = v->Next(); - if (shadow == nullptr) SlErrorCorrupt("Missing shadow for aircraft"); - shadow->sprite_seq.CopyWithoutPalette(v->sprite_seq); - shadow->sprite_seq_bounds = v->sprite_seq_bounds; - - /* In the case of a helicopter we will update the rotor sprites */ - if (v->subtype == AIR_HELICOPTER) { - Vehicle *rotor = shadow->Next(); - if (rotor == nullptr) SlErrorCorrupt("Missing rotor for helicopter"); - GetRotorImage(Aircraft::From(v), EIT_ON_MAP, &rotor->sprite_seq); - rotor->UpdateSpriteSeqBound(); - } - - UpdateAircraftCache(Aircraft::From(v), true); - } - break; - default: break; - } - - v->UpdateDeltaXY(); - v->coord.left = INVALID_COORD; - v->UpdatePosition(); - v->UpdateViewport(false); - v->cargo.AssertCountConsistency(); - } -} - -void AfterLoadVehiclesRemoveAnyFoundInvalid() -{ - if (!_load_invalid_vehicles_to_delete.empty()) { - DEBUG(sl, 0, "Removing %u vehicles found to be uncorrectably invalid during load", (uint)_load_invalid_vehicles_to_delete.size()); - SetDParam(0, (uint)_load_invalid_vehicles_to_delete.size()); - ShowErrorMessage(STR_WARNING_LOADGAME_REMOVED_UNCORRECTABLE_VEHICLES, INVALID_STRING_ID, WL_CRITICAL); - GroupStatistics::UpdateAfterLoad(); - } - - for (Vehicle *v : _load_invalid_vehicles_to_delete) { - delete v; - } - _load_invalid_vehicles_to_delete.clear(); -} - +void AfterLoadVehicles(bool part_of_load); bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // From train_cmd.cpp void ReverseTrainDirection(Train *v); void ReverseTrainSwapVeh(Train *v, int l, int r); -/** Fixup old train spacing. */ -void FixupTrainLengths() -{ - /* Vehicle center was moved from 4 units behind the front to half the length - * behind the front. Move vehicles so they end up on the same spot. */ - for (Vehicle *v : Vehicle::Iterate()) { - if (v->type == VEH_TRAIN && v->IsPrimaryVehicle()) { - /* The vehicle center is now more to the front depending on vehicle length, - * so we need to move all vehicles forward to cover the difference to the - * old center, otherwise wagon spacing in trains would be broken upon load. */ - for (Train *u = Train::From(v); u != nullptr; u = u->Next()) { - if (u->track == TRACK_BIT_DEPOT || (u->vehstatus & VS_CRASHED)) continue; - - Train *next = u->Next(); - - /* Try to pull the vehicle half its length forward. */ - int diff = (VEHICLE_LENGTH - u->gcache.cached_veh_length) / 2; - int done; - for (done = 0; done < diff; done++) { - if (!TrainController(u, next, false)) break; - } - - if (next != nullptr && done < diff && u->IsFrontEngine()) { - /* Pulling the front vehicle forwards failed, we either encountered a dead-end - * or a red signal. To fix this, we try to move the whole train the required - * space backwards and re-do the fix up of the front vehicle. */ - - /* Ignore any signals when backtracking. */ - TrainForceProceeding old_tfp = u->force_proceed; - u->force_proceed = TFP_SIGNAL; - - /* Swap start<>end, start+1<>end-1, ... */ - int r = CountVehiclesInChain(u) - 1; // number of vehicles - 1 - int l = 0; - do ReverseTrainSwapVeh(u, l++, r--); while (l <= r); - - /* We moved the first vehicle which is now the last. Move it back to the - * original position as we will fix up the last vehicle later in the loop. */ - for (int i = 0; i < done; i++) TrainController(u->Last(), nullptr); - - /* Move the train backwards to get space for the first vehicle. As the stopping - * distance from a line end is rounded up, move the train one unit more to cater - * for front vehicles with odd lengths. */ - int moved; - for (moved = 0; moved < diff + 1; moved++) { - if (!TrainController(u, nullptr, false)) break; - } - - /* Swap start<>end, start+1<>end-1, ... again. */ - r = CountVehiclesInChain(u) - 1; // number of vehicles - 1 - l = 0; - do ReverseTrainSwapVeh(u, l++, r--); while (l <= r); - - u->force_proceed = old_tfp; - - /* Tracks are too short to fix the train length. The player has to fix the - * train in a depot. Bail out so we don't damage the vehicle chain any more. */ - if (moved < diff + 1) break; - - /* Re-do the correction for the first vehicle. */ - for (done = 0; done < diff; done++) TrainController(u, next, false); - - /* We moved one unit more backwards than needed for even-length front vehicles, - * try to move that unit forward again. We don't care if this step fails. */ - TrainController(u, nullptr, false); - } - - /* If the next wagon is still in a depot, check if it shouldn't be outside already. */ - if (next != nullptr && next->track == TRACK_BIT_DEPOT) { - int d = TicksToLeaveDepot(u); - if (d <= 0) { - /* Next vehicle should have left the depot already, show it and pull forward. */ - next->vehstatus &= ~VS_HIDDEN; - next->track = TrackToTrackBits(GetRailDepotTrack(next->tile)); - for (int i = 0; i >= d; i--) TrainController(next, nullptr); - } - } - } - - /* Update all cached properties after moving the vehicle chain around. */ - Train::From(v)->ConsistChanged(CCF_TRACK); - } - } -} +namespace upstream_sl { static uint8 _cargo_days; static uint16 _cargo_source; @@ -642,214 +41,204 @@ static uint16 _cargo_count; static uint16 _cargo_paid_for; static Money _cargo_feeder_share; static uint32 _cargo_loaded_at_xy; -CargoPacketList _cpp_packets; -std::map _veh_cpp_packets; -static uint32 _old_ahead_separation; +class SlVehicleCommon : public DefaultSaveLoadHandler { +public: +#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) + /* This table access private members of other classes; they have this + * class as friend. For MSVC CL 19.15 and 19.16 this doesn't work for + * "inline static const", so we are forced to wrap the table in a + * function. CL 19.16 is the latest for VS2017. */ + inline static const SaveLoad description[] = {{}}; + SaveLoadTable GetDescription() const override { +#else + inline +#endif + static const SaveLoad description[] = { + SLE_VAR(Vehicle, subtype, SLE_UINT8), -/** - * 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 - */ -SaveLoadTable 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, SL_MIN_VERSION, SLV_84), + SLE_CONDSTR(Vehicle, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_8), + SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, SLV_8, SL_MAX_VERSION), + SLE_VAR(Vehicle, owner, SLE_UINT8), + SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_REF(Vehicle, next, REF_VEHICLE_OLD), - SLE_CONDVAR(Vehicle, name, SLE_CNAME, SL_MIN_VERSION, SLV_84), - SLE_CONDSTR(Vehicle, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_8), - SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, SLV_8, SL_MAX_VERSION), - SLE_VAR(Vehicle, owner, SLE_UINT8), - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), + SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION), + SLE_VAR(Vehicle, direction, SLE_UINT8), - SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)), - SLE_CONDVAR_X(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)), - SLE_VAR(Vehicle, direction, SLE_UINT8), + SLE_VAR(Vehicle, spritenum, SLE_UINT8), + SLE_VAR(Vehicle, engine_type, SLE_UINT16), + SLE_VAR(Vehicle, cur_speed, SLE_UINT16), + SLE_VAR(Vehicle, subspeed, SLE_UINT8), + SLE_VAR(Vehicle, acceleration, SLE_UINT8), + SLE_CONDVAR(Vehicle, motion_counter, SLE_UINT32, SLV_VEH_MOTION_COUNTER, SL_MAX_VERSION), + SLE_VAR(Vehicle, progress, SLE_UINT8), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_58), - SLE_VAR(Vehicle, spritenum, SLE_UINT8), - SLE_CONDNULL(5, SL_MIN_VERSION, SLV_58), - SLE_VAR(Vehicle, engine_type, SLE_UINT16), + SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, last_loading_station, SLE_UINT16, SLV_182, SL_MAX_VERSION), - SLE_CONDNULL(2, SL_MIN_VERSION, SLV_152), - SLE_VAR(Vehicle, cur_speed, SLE_UINT16), - SLE_VAR(Vehicle, subspeed, SLE_UINT8), - SLE_VAR(Vehicle, acceleration, SLE_UINT8), - SLE_CONDVAR(Vehicle, motion_counter, SLE_UINT32, SLV_VEH_MOTION_COUNTER, SL_MAX_VERSION), - SLE_VAR(Vehicle, progress, SLE_UINT8), + SLE_VAR(Vehicle, cargo_type, SLE_UINT8), + SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, SLV_35, SL_MAX_VERSION), + SLEG_CONDVAR("cargo_days", _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68), + SLEG_CONDVAR("cargo_source", _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), + SLEG_CONDVAR("cargo_source", _cargo_source, SLE_UINT16, SLV_7, SLV_68), + SLEG_CONDVAR("cargo_source_xy", _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), + SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), + SLE_CONDVAR(Vehicle, refit_cap, SLE_UINT16, SLV_182, SL_MAX_VERSION), + SLEG_CONDVAR("cargo_count", _cargo_count, SLE_UINT16, SL_MIN_VERSION, SLV_68), + SLE_CONDREFDEQUE(Vehicle, cargo.packets, REF_CARGO_PACKET, SLV_68, SL_MAX_VERSION), + SLE_CONDARR(Vehicle, cargo.action_counts, SLE_UINT, VehicleCargoList::NUM_MOVE_TO_ACTION, SLV_181, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, cargo_age_counter, SLE_UINT16, SLV_162, SL_MAX_VERSION), - SLE_VAR(Vehicle, vehstatus, SLE_UINT8), - SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, last_loading_station, SLE_UINT16, SLV_182, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_VAR(Vehicle, day_counter, SLE_UINT8), + SLE_VAR(Vehicle, tick_counter, SLE_UINT8), + SLE_CONDVAR(Vehicle, running_ticks, SLE_FILE_U8 | SLE_VAR_U16, SLV_88, SL_MAX_VERSION), - SLE_VAR(Vehicle, cargo_type, SLE_UINT8), - SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, SLV_35, SL_MAX_VERSION), - SLEG_CONDVAR( _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68), - SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), - SLEG_CONDVAR( _cargo_source, SLE_UINT16, SLV_7, SLV_68), - SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), - SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), - SLE_CONDVAR(Vehicle, refit_cap, SLE_UINT16, SLV_182, SL_MAX_VERSION), - SLEG_CONDVAR( _cargo_count, SLE_UINT16, SL_MIN_VERSION, SLV_68), - SLE_CONDPTRDEQ(Vehicle, cargo.packets, REF_CARGO_PACKET, SLV_68, SL_MAX_VERSION), - SLEG_CONDPTRDEQ_X( _cpp_packets, REF_CARGO_PACKET, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP)), - SLE_CONDARR(Vehicle, cargo.action_counts, SLE_UINT, VehicleCargoList::NUM_MOVE_TO_ACTION, SLV_181, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, cargo_age_counter, SLE_UINT16, SLV_162, SL_MAX_VERSION), - - SLE_VAR(Vehicle, day_counter, SLE_UINT8), - SLE_VAR(Vehicle, tick_counter, SLE_UINT8), - SLE_CONDVAR_X(Vehicle, running_ticks, SLE_FILE_U8 | SLE_VAR_U16, SLV_88, SL_MAX_VERSION, SlXvFeatureTest([](uint16 version, bool version_in_range, const std::array &feature_versions) -> bool { - return version_in_range && !(SlXvIsFeaturePresent(feature_versions, XSLFI_SPRINGPP, 3) || SlXvIsFeaturePresent(feature_versions, XSLFI_JOKERPP) || SlXvIsFeaturePresent(feature_versions, XSLFI_CHILLPP) || SlXvIsFeaturePresent(feature_versions, XSLFI_VARIABLE_DAY_LENGTH, 2)); - })), - SLE_CONDVAR_X(Vehicle, running_ticks, SLE_UINT16, SLV_88, SL_MAX_VERSION, SlXvFeatureTest([](uint16 version, bool version_in_range, const std::array &feature_versions) -> bool { - return version_in_range && (SlXvIsFeaturePresent(feature_versions, XSLFI_SPRINGPP, 2) || SlXvIsFeaturePresent(feature_versions, XSLFI_JOKERPP) || SlXvIsFeaturePresent(feature_versions, XSLFI_CHILLPP) || SlXvIsFeaturePresent(feature_versions, XSLFI_VARIABLE_DAY_LENGTH, 2)); - })), - - SLE_VAR(Vehicle, cur_implicit_order_index, SLE_VEHORDERID), - SLE_CONDVAR(Vehicle, cur_real_order_index, SLE_VEHORDERID, SLV_158, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, cur_timetable_order_index, SLE_VEHORDERID, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)), - /* num_orders is now part of OrderList and is not saved but counted */ - SLE_CONDNULL(1, SL_MIN_VERSION, SLV_105), + SLE_VAR(Vehicle, cur_implicit_order_index, SLE_FILE_U8 | SLE_VAR_U16), + SLE_CONDVAR(Vehicle, cur_real_order_index, SLE_FILE_U8 | SLE_VAR_U16, SLV_158, SL_MAX_VERSION), /* 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_CONDVAR(Vehicle, current_order.type, SLE_UINT8, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), + type and flags (which were both 4 bits) into type. Later on this is + converted correctly */ + SLE_CONDVAR(Vehicle, current_order.type, SLE_UINT8, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), /* Orders for version 5 and on */ - SLE_CONDVAR(Vehicle, current_order.type, SLE_UINT8, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, current_order.flags, SLE_FILE_U8 | SLE_VAR_U16, SLV_5, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 0, 0)), - SLE_CONDVAR_X(Vehicle, current_order.flags, SLE_UINT16, SLV_5, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 1)), - SLE_CONDVAR(Vehicle, current_order.dest, SLE_UINT16, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, current_order.type, SLE_UINT8, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, current_order.flags, SLE_FILE_U8 | SLE_VAR_U16, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, current_order.dest, SLE_UINT16, SLV_5, SL_MAX_VERSION), /* Refit in current order */ - SLE_CONDVAR(Vehicle, current_order.refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION), - SLE_CONDNULL(1, SLV_36, SLV_182), // refit_subtype + SLE_CONDVAR(Vehicle, current_order.refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION), /* Timetable in current order */ - SLE_CONDVAR_X(Vehicle, current_order.wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), - SLE_CONDVAR_X(Vehicle, current_order.wait_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), - SLE_CONDVAR_X(Vehicle, current_order.travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), - SLE_CONDVAR_X(Vehicle, current_order.travel_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), - SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, SLV_174, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, timetable_start, SLE_INT32, SLV_129, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, timetable_start_subticks, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)), + SLE_CONDVAR(Vehicle, current_order.wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, current_order.travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, SLV_174, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, timetable_start, SLE_INT32, SLV_129, SL_MAX_VERSION), - SLE_CONDREF(Vehicle, orders, REF_ORDER, SL_MIN_VERSION, SLV_105), - SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, SLV_105, SL_MAX_VERSION), + SLE_CONDREF(Vehicle, orders, REF_ORDER, SL_MIN_VERSION, SLV_105), + SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, SLV_105, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, max_age, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SLV_31, SLV_180), - SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SLV_180, 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_X(Vehicle, breakdown_chance_factor, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 3)), - SLE_CONDVAR_X(Vehicle, breakdown_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS)), - SLE_CONDVAR_X(Vehicle, breakdown_severity, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS)), - SLE_CONDVAR(Vehicle, build_year, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, build_year, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, max_age, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SLV_31, SLV_180), + SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SLV_180, 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, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, build_year, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_VAR(Vehicle, load_unload_ticks, SLE_UINT16), - SLEG_CONDVAR( _cargo_paid_for, SLE_UINT16, SLV_45, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_40, SLV_180), - SLE_CONDVAR_X(Vehicle, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 0, 0)), - SLE_CONDVAR_X(Vehicle, vehicle_flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 1)), + SLE_VAR(Vehicle, load_unload_ticks, SLE_UINT16), + SLEG_CONDVAR("cargo_paid_for", _cargo_paid_for, SLE_UINT16, SLV_45, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_40, SLV_180), + SLE_CONDVAR(Vehicle, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, SLV_65, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, SLV_65, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle,profit_lifetime, SLE_INT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEH_LIFETIME_PROFIT)), - SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64, SLV_51, SLV_65), - SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), - SLEG_CONDVAR( _cargo_loaded_at_xy, SLE_UINT32, SLV_51, SLV_68), - SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), - SLE_CONDVAR(Vehicle, value, SLE_INT64, SLV_65, SL_MAX_VERSION), - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_REPAIR_COST, 1, 1)), + SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, SLV_65, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, SLV_65, SL_MAX_VERSION), + SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64, SLV_51, SLV_65), + SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), + SLEG_CONDVAR("cargo_loaded_at_xy", _cargo_loaded_at_xy, SLE_UINT32, SLV_51, SLV_68), + SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(Vehicle, value, SLE_INT64, SLV_65, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, random_bits, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTEND_VEHICLE_RANDOM, 0, 0)), - SLE_CONDVAR_X(Vehicle, random_bits, SLE_UINT16, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTEND_VEHICLE_RANDOM, 1)), - SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, SLV_2, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, random_bits, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SLV_EXTEND_VEHICLE_RANDOM), + SLE_CONDVAR(Vehicle, random_bits, SLE_UINT16, SLV_EXTEND_VEHICLE_RANDOM, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, SLV_2, SL_MAX_VERSION), - SLEG_CONDVAR_X(_old_ahead_separation, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 1, 4)), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 1, 4)), + SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, SLV_2, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, SLV_60, SL_MAX_VERSION), - SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, SLV_2, SL_MAX_VERSION), - SLE_CONDNULL(2, SLV_2, SLV_69), - SLE_CONDNULL(4, SLV_69, SLV_101), - - SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, SLV_60, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, current_order_time, SLE_UINT32, SLV_67, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, current_loading_time, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE)), - SLE_CONDVAR_X(Vehicle, current_loading_time, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_23)), - SLE_CONDVAR_X(Vehicle, last_loading_tick, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LAST_LOADING_TICK)), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - SLE_CONDVAR(Vehicle, lateness_counter, SLE_INT32, SLV_67, SL_MAX_VERSION), - - SLE_CONDNULL(10, SLV_2, SLV_144), // old reserved space - - SLE_CONDNULL_X((8 + 8 + 2 + 2 + 4 + 4 + 1 + 1) * 30, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - SLE_CONDNULL_X((8 + 8 + 2 + 2 + 4 + 4 + 1 + 1) * 70, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - - SLE_CONDNULL_X(160, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + SLE_CONDVAR(Vehicle, current_order_time, SLE_UINT32, SLV_67, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, last_loading_tick, SLE_UINT64, SLV_LAST_LOADING_TICK, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, lateness_counter, SLE_INT32, SLV_67, SL_MAX_VERSION), }; +#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) + return description; + } +#endif + inline const static SaveLoadCompatTable compat_description = _vehicle_common_sl_compat; - static const SaveLoad _train_desc[] = { - SLE_WRITEBYTE(Vehicle, type), - SLE_VEH_INCLUDE(), + void Save(Vehicle *v) const override + { + SlObject(v, this->GetDescription()); + } + + void Load(Vehicle *v) const override + { + SlObject(v, this->GetLoadDescription()); + } + + void FixPointers(Vehicle *v) const override + { + SlObject(v, this->GetDescription()); + } +}; + +class SlVehicleTrain : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLEG_STRUCT("common", SlVehicleCommon), SLE_VAR(Train, crash_anim_pos, SLE_UINT16), SLE_VAR(Train, force_proceed, SLE_UINT8), SLE_VAR(Train, railtype, SLE_UINT8), SLE_VAR(Train, track, SLE_UINT8), - SLE_CONDVAR(Train, flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_2, SLV_100), - SLE_CONDVAR_X(Train, flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_100, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_FLAGS_EXTRA, 0, 0)), - SLE_CONDVAR_X(Train, flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_FLAGS_EXTRA, 1)), - SLE_CONDNULL(2, SLV_2, SLV_60), - + SLE_CONDVAR(Train, flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_2, SLV_100), + SLE_CONDVAR(Train, flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_100, SL_MAX_VERSION), SLE_CONDVAR(Train, wait_counter, SLE_UINT16, SLV_136, SL_MAX_VERSION), - SLE_CONDVAR_X(Train, tunnel_bridge_signal_num, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SIG_TUNNEL_BRIDGE, 5)), - - SLE_CONDNULL(2, SLV_2, SLV_20), SLE_CONDVAR(Train, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), - SLE_CONDNULL_X(2 , SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDNULL(11, SLV_2, SLV_144), // old reserved space - SLE_CONDVAR_X(Train, reverse_distance, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REVERSE_AT_WAYPOINT)), - SLE_CONDVAR_X(Train, speed_restriction, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPEED_RESTRICTION)), - SLE_CONDVAR_X(Train, signal_speed_restriction, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_SPEED_ADAPTATION)), - SLE_CONDVAR_X(Train, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 2)), }; + inline const static SaveLoadCompatTable compat_description = _vehicle_train_sl_compat; - static const SaveLoad _roadveh_desc[] = { - SLE_WRITEBYTE(Vehicle, type), - SLE_VEH_INCLUDE(), + void Save(Vehicle *v) const override + { + if (v->type != VEH_TRAIN) return; + SlObject(v, this->GetDescription()); + } + + void Load(Vehicle *v) const override + { + if (v->type != VEH_TRAIN) return; + SlObject(v, this->GetLoadDescription()); + if (v->cur_real_order_index == 0xFF) v->cur_real_order_index = INVALID_VEH_ORDER_ID; + if (v->cur_implicit_order_index == 0xFF) v->cur_implicit_order_index = INVALID_VEH_ORDER_ID; + } + + void FixPointers(Vehicle *v) const override + { + if (v->type != VEH_TRAIN) return; + SlObject(v, this->GetDescription()); + } +}; + +class SlVehicleRoadVeh : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLEG_STRUCT("common", SlVehicleCommon), SLE_VAR(RoadVehicle, state, SLE_UINT8), SLE_VAR(RoadVehicle, frame, SLE_UINT8), SLE_VAR(RoadVehicle, blocked_ctr, SLE_UINT16), @@ -859,31 +248,62 @@ SaveLoadTable GetVehicleDescription(VehicleType vt) SLE_VAR(RoadVehicle, reverse_ctr, SLE_UINT8), SLE_CONDDEQUE(RoadVehicle, path.td, SLE_UINT8, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION), SLE_CONDDEQUE(RoadVehicle, path.tile, SLE_UINT32, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION), - SLE_CONDVAR_X(RoadVehicle, path.layout_ctr, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), - - SLE_CONDNULL(2, SLV_6, SLV_69), SLE_CONDVAR(RoadVehicle, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), - SLE_CONDNULL(4, SLV_69, SLV_131), - SLE_CONDNULL(2, SLV_6, SLV_131), - SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space - SLE_CONDVAR_X(RoadVehicle, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 6)), }; + inline const static SaveLoadCompatTable compat_description = _vehicle_roadveh_sl_compat; - static const SaveLoad _ship_desc[] = { - SLE_WRITEBYTE(Vehicle, type), - SLE_VEH_INCLUDE(), + void Save(Vehicle *v) const override + { + if (v->type != VEH_ROAD) return; + SlObject(v, this->GetDescription()); + } + + void Load(Vehicle *v) const override + { + if (v->type != VEH_ROAD) return; + SlObject(v, this->GetLoadDescription()); + } + + void FixPointers(Vehicle *v) const override + { + if (v->type != VEH_ROAD) return; + SlObject(v, this->GetDescription()); + } +}; + +class SlVehicleShip : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLEG_STRUCT("common", SlVehicleCommon), SLE_VAR(Ship, state, SLE_UINT8), SLE_CONDDEQUE(Ship, path, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION), SLE_CONDVAR(Ship, rotation, SLE_UINT8, SLV_SHIP_ROTATION, SL_MAX_VERSION), - SLE_CONDVAR_X(Ship, lost_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SHIP_LOST_COUNTER)), - SLE_CONDVAR_X(Ship, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 8)), - - SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space }; + inline const static SaveLoadCompatTable compat_description = _vehicle_ship_sl_compat; - static const SaveLoad _aircraft_desc[] = { - SLE_WRITEBYTE(Vehicle, type), - SLE_VEH_INCLUDE(), + void Save(Vehicle *v) const override + { + if (v->type != VEH_SHIP) return; + SlObject(v, this->GetDescription()); + } + + void Load(Vehicle *v) const override + { + if (v->type != VEH_SHIP) return; + SlObject(v, this->GetLoadDescription()); + } + + void FixPointers(Vehicle *v) const override + { + if (v->type != VEH_SHIP) return; + SlObject(v, this->GetDescription()); + } +}; + +class SlVehicleAircraft : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLEG_STRUCT("common", SlVehicleCommon), SLE_VAR(Aircraft, crashed_counter, SLE_UINT16), SLE_VAR(Aircraft, pos, SLE_UINT8), @@ -895,22 +315,36 @@ SaveLoadTable GetVehicleDescription(VehicleType vt) SLE_CONDVAR(Aircraft, previous_pos, SLE_UINT8, SLV_2, SL_MAX_VERSION), SLE_CONDVAR(Aircraft, last_direction, SLE_UINT8, SLV_2, SL_MAX_VERSION), SLE_CONDVAR(Aircraft, number_consecutive_turns, SLE_UINT8, SLV_2, SL_MAX_VERSION), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP)), SLE_CONDVAR(Aircraft, turn_counter, SLE_UINT8, SLV_136, SL_MAX_VERSION), SLE_CONDVAR(Aircraft, flags, SLE_UINT8, SLV_167, SL_MAX_VERSION), - - SLE_CONDNULL(13, SLV_2, SLV_144), // old reserved space }; + inline const static SaveLoadCompatTable compat_description = _vehicle_aircraft_sl_compat; - static const SaveLoad _special_desc[] = { - SLE_WRITEBYTE(Vehicle, type), + void Save(Vehicle *v) const override + { + if (v->type != VEH_AIRCRAFT) return; + SlObject(v, this->GetDescription()); + } + void Load(Vehicle *v) const override + { + if (v->type != VEH_AIRCRAFT) return; + SlObject(v, this->GetLoadDescription()); + } + + void FixPointers(Vehicle *v) const override + { + if (v->type != VEH_AIRCRAFT) return; + SlObject(v, this->GetDescription()); + } +}; + +class SlVehicleEffect : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { SLE_VAR(Vehicle, subtype, SLE_UINT8), - SLE_CONDNULL_X(5, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), @@ -918,11 +352,10 @@ SaveLoadTable GetVehicleDescription(VehicleType vt) SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)), - SLE_CONDVAR_X(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)), + SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), + SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION), - SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), - SLE_CONDNULL(5, SL_MIN_VERSION, SLV_59), + SLE_VAR2(Vehicle, "sprite_cache.sprite_seq.seq[0].sprite", sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), SLE_VAR(Vehicle, progress, SLE_UINT8), SLE_VAR(Vehicle, vehstatus, SLE_UINT8), @@ -930,604 +363,189 @@ SaveLoadTable GetVehicleDescription(VehicleType vt) SLE_VAR(EffectVehicle, animation_substate, SLE_UINT8), SLE_CONDVAR(Vehicle, spritenum, SLE_UINT8, SLV_2, SL_MAX_VERSION), - - SLE_CONDNULL(15, SLV_2, SLV_144), // old reserved space }; + inline const static SaveLoadCompatTable compat_description = _vehicle_effect_sl_compat; - static const SaveLoad _disaster_desc[] = { - SLE_WRITEBYTE(Vehicle, type), + void Save(Vehicle *v) const override + { + if (v->type != VEH_EFFECT) return; + SlObject(v, this->GetDescription()); + } - SLE_REF(Vehicle, next, REF_VEHICLE_OLD), + void Load(Vehicle *v) const override + { + if (v->type != VEH_EFFECT) return; + SlObject(v, this->GetLoadDescription()); + } - SLE_VAR(Vehicle, subtype, SLE_UINT8), - SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - - SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)), - SLE_CONDVAR_X(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)), - SLE_VAR(Vehicle, direction, SLE_UINT8), - - SLE_CONDNULL(5, SL_MIN_VERSION, SLV_58), - SLE_VAR(Vehicle, owner, SLE_UINT8), - SLE_VAR(Vehicle, vehstatus, SLE_UINT8), - SLE_CONDVAR_X(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5, SlXvFeatureTest(XSLFTO_AND, XSLFI_DISASTER_VEH_STATE, 0, 0)), - SLE_CONDVAR_X(Vehicle, current_order.dest, SLE_UINT16, SLV_5, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_DISASTER_VEH_STATE, 0, 0)), - SLE_CONDVAR_X(DisasterVehicle, state, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_DISASTER_VEH_STATE, 1)), - - SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), - SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), - SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION), - SLE_VAR(Vehicle, tick_counter, SLE_UINT8), - - SLE_CONDVAR(DisasterVehicle, image_override, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_191), - SLE_CONDVAR(DisasterVehicle, image_override, SLE_UINT32, SLV_191, SL_MAX_VERSION), - SLE_CONDVAR(DisasterVehicle, big_ufo_destroyer_target, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_191), - SLE_CONDVAR(DisasterVehicle, big_ufo_destroyer_target, SLE_UINT32, SLV_191, SL_MAX_VERSION), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), - SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP)), - SLE_CONDVAR(DisasterVehicle, flags, SLE_UINT8, SLV_194, SL_MAX_VERSION), - - SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space - }; - - - static const SaveLoadTable _veh_descs[] = { - _train_desc, - _roadveh_desc, - _ship_desc, - _aircraft_desc, - _special_desc, - _disaster_desc, - _common_veh_desc, - }; - - return _veh_descs[vt]; -} - -static std::vector _filtered_train_desc; -static std::vector _filtered_roadveh_desc; -static std::vector _filtered_ship_desc; -static std::vector _filtered_aircraft_desc; -static std::vector _filtered_special_desc; -static std::vector _filtered_disaster_desc; - -static std::vector * const _filtered_veh_descs[] = { - &_filtered_train_desc, - &_filtered_roadveh_desc, - &_filtered_ship_desc, - &_filtered_aircraft_desc, - &_filtered_special_desc, - &_filtered_disaster_desc, + void FixPointers(Vehicle *v) const override + { + if (v->type != VEH_EFFECT) return; + SlObject(v, this->GetDescription()); + } }; -const SaveLoadTable GetVehicleDescriptionFiltered(VehicleType vt) -{ - return *(_filtered_veh_descs[vt]); -} +class SlVehicleDisaster : public DefaultSaveLoadHandler { +public: +#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) + /* This table access private members of other classes; they have this + * class as friend. For MSVC CL 19.15 and 19.16 this doesn't work for + * "inline static const", so we are forced to wrap the table in a + * function. CL 19.16 is the latest for VS2017. */ + inline static const SaveLoad description[] = {{}}; + SaveLoadTable GetDescription() const override { +#else + inline +#endif + static const SaveLoad description[] = { + SLE_REF(Vehicle, next, REF_VEHICLE_OLD), -static void SetupDescs_VEHS() -{ - for (size_t i = 0; i < lengthof(_filtered_veh_descs); i++) { - *(_filtered_veh_descs[i]) = SlFilterObject(GetVehicleDescription((VehicleType) i)); + SLE_VAR(Vehicle, subtype, SLE_UINT8), + SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164), + SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION), + SLE_VAR(Vehicle, direction, SLE_UINT8), + + SLE_VAR(Vehicle, owner, SLE_UINT8), + SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + SLE_CONDVARNAME(DisasterVehicle, state, "current_order.dest", SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), + SLE_CONDVARNAME(DisasterVehicle, state, "current_order.dest", SLE_UINT16, SLV_5, SLV_DISASTER_VEH_STATE), + SLE_CONDVAR(DisasterVehicle, state, SLE_UINT16, SLV_DISASTER_VEH_STATE, SL_MAX_VERSION), + + + SLE_VAR2(Vehicle, "sprite_cache.sprite_seq.seq[0].sprite", sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), + SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_VAR(Vehicle, tick_counter, SLE_UINT8), + + SLE_CONDVAR(DisasterVehicle, image_override, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_191), + SLE_CONDVAR(DisasterVehicle, image_override, SLE_UINT32, SLV_191, SL_MAX_VERSION), + SLE_CONDVAR(DisasterVehicle, big_ufo_destroyer_target, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_191), + SLE_CONDVAR(DisasterVehicle, big_ufo_destroyer_target, SLE_UINT32, SLV_191, SL_MAX_VERSION), + SLE_CONDVAR(DisasterVehicle, flags, SLE_UINT8, SLV_194, SL_MAX_VERSION), + }; +#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916) + return description; } -} +#endif + inline const static SaveLoadCompatTable compat_description = _vehicle_disaster_sl_compat; -/** Will be called when the vehicles need to be saved. */ -static void Save_VEHS() -{ - SetupDescs_VEHS(); - /* Write the vehicles */ - for (Vehicle *v : Vehicle::Iterate()) { - SlSetArrayIndex(v->index); - SlObjectSaveFiltered(v, GetVehicleDescriptionFiltered(v->type)); + void Save(Vehicle *v) const override + { + if (v->type != VEH_DISASTER) return; + SlObject(v, this->GetDescription()); } -} -/** Will be called when vehicles need to be loaded. */ -void Load_VEHS() -{ - SetupDescs_VEHS(); - - int index; - - _cargo_count = 0; - - _cpp_packets.clear(); - _veh_cpp_packets.clear(); - - 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: // Savegame shouldn't contain invalid vehicles - default: SlErrorCorrupt("Invalid vehicle type"); - } - - SlObjectLoadFiltered(v, GetVehicleDescriptionFiltered(vtype)); - - if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v) && CargoPacket::CanAllocateItem()) { - /* Don't construct the packet with station here, because that'll fail with old savegames */ - CargoPacket *cp = new CargoPacket(_cargo_count, _cargo_days, _cargo_source, _cargo_source_xy, _cargo_loaded_at_xy, _cargo_feeder_share); - v->cargo.Append(cp); - } - - /* Old savegames used 'last_station_visited = 0xFF' */ - if (IsSavegameVersionBefore(SLV_5) && v->last_station_visited == 0xFF) { - v->last_station_visited = INVALID_STATION; - } - - if (IsSavegameVersionBefore(SLV_182) && !SlXvIsFeaturePresent(XSLFI_CHILLPP)) v->last_loading_station = INVALID_STATION; - - if (IsSavegameVersionBefore(SLV_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 (IsSavegameVersionBefore(SLV_60)) v->group_id = DEFAULT_GROUP; - - if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { - _veh_cpp_packets[index] = std::move(_cpp_packets); - _cpp_packets.clear(); - } - - if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 4)) { - SB(v->vehicle_flags, VF_SEPARATION_ACTIVE, 1, _old_ahead_separation ? 1 : 0); - } + void Load(Vehicle *v) const override + { + if (v->type != VEH_DISASTER) return; + SlObject(v, this->GetLoadDescription()); } -} -static void Ptrs_VEHS() -{ - SetupDescs_VEHS(); - - for (Vehicle *v : Vehicle::Iterate()) { - if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) _cpp_packets = std::move(_veh_cpp_packets[v->index]); - SlObjectPtrOrNullFiltered(v, GetVehicleDescriptionFiltered(v->type)); - if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) _veh_cpp_packets[v->index] = std::move(_cpp_packets); + void FixPointers(Vehicle *v) const override + { + if (v->type != VEH_DISASTER) return; + SlObject(v, this->GetDescription()); } -} +}; -const SaveLoadTable GetOrderExtraInfoDescription(); +const static SaveLoad _vehicle_desc[] = { + SLE_SAVEBYTE(Vehicle, type), + SLEG_STRUCT("train", SlVehicleTrain), + SLEG_STRUCT("roadveh", SlVehicleRoadVeh), + SLEG_STRUCT("ship", SlVehicleShip), + SLEG_STRUCT("aircraft", SlVehicleAircraft), + SLEG_STRUCT("effect", SlVehicleEffect), + SLEG_STRUCT("disaster", SlVehicleDisaster), +}; -void Save_VEOX() -{ - /* save extended order info for vehicle current order */ - for (Vehicle *v : Vehicle::Iterate()) { - if (v->current_order.extra) { +struct VEHSChunkHandler : ChunkHandler { + VEHSChunkHandler() : ChunkHandler('VEHS', CH_SPARSE_TABLE) {} + + void Save() const override + { + SlTableHeader(_vehicle_desc); + + /* Write the vehicles */ + for (Vehicle *v : Vehicle::Iterate()) { SlSetArrayIndex(v->index); - SlObject(v->current_order.extra.get(), GetOrderExtraInfoDescription()); + SlObject(v, _vehicle_desc); } } -} -void Load_VEOX() -{ - /* load extended order info for vehicle current order */ - int index; - while ((index = SlIterateArray()) != -1) { - Vehicle *v = Vehicle::GetIfValid(index); - assert(v != nullptr); - v->current_order.AllocExtraInfo(); - SlObject(v->current_order.extra.get(), GetOrderExtraInfoDescription()); - } -} + void Load() const override + { + const std::vector slt = SlCompatTableHeader(_vehicle_desc, _vehicle_sl_compat); -const SaveLoadTable GetVehicleSpeedRestrictionDescription() -{ - static const SaveLoad _vehicle_speed_restriction_desc[] = { - SLE_VAR(PendingSpeedRestrictionChange, distance, SLE_UINT16), - SLE_VAR(PendingSpeedRestrictionChange, new_speed, SLE_UINT16), - SLE_VAR(PendingSpeedRestrictionChange, prev_speed, SLE_UINT16), - SLE_VAR(PendingSpeedRestrictionChange, flags, SLE_UINT16), - }; + int index; - return _vehicle_speed_restriction_desc; -} + _cargo_count = 0; -void Save_VESR() -{ - for (auto &it : _pending_speed_restriction_change_map) { - SlSetArrayIndex(it.first); - PendingSpeedRestrictionChange *ptr = &(it.second); - SlObject(ptr, GetVehicleSpeedRestrictionDescription()); - } -} + while ((index = SlIterateArray()) != -1) { + Vehicle *v; + VehicleType vtype = (VehicleType)SlReadByte(); -void Load_VESR() -{ - int index; - while ((index = SlIterateArray()) != -1) { - auto iter = _pending_speed_restriction_change_map.insert({ static_cast(index), {} }); - PendingSpeedRestrictionChange *ptr = &(iter->second); - SlObject(ptr, GetVehicleSpeedRestrictionDescription()); - } -} + 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: // Savegame shouldn't contain invalid vehicles + default: SlErrorCorrupt("Invalid vehicle type"); + } -struct vehicle_venc { - VehicleID id; - VehicleCache vcache; -}; + SlObject(v, slt); -struct train_venc { - VehicleID id; - GroundVehicleCache gvcache; - int cached_curve_speed_mod; - uint8 cached_tflags; - uint8 cached_num_engines; - uint16 cached_centre_mass; - uint16 cached_braking_length; - uint16 cached_veh_weight; - uint16 cached_uncapped_decel; - uint8 cached_deceleration; - byte user_def_data; - int cached_max_curve_speed; -}; + if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v) && CargoPacket::CanAllocateItem()) { + /* Don't construct the packet with station here, because that'll fail with old savegames */ + CargoPacket *cp = new CargoPacket(_cargo_count, _cargo_days, _cargo_source, _cargo_source_xy, _cargo_loaded_at_xy, _cargo_feeder_share); + v->cargo.Append(cp); + } -struct roadvehicle_venc { - VehicleID id; - GroundVehicleCache gvcache; -}; +#if 0 + /* Old savegames used 'last_station_visited = 0xFF' */ + if (IsSavegameVersionBefore(SLV_5) && v->last_station_visited == 0xFF) { + v->last_station_visited = INVALID_STATION; + } -struct aircraft_venc { - VehicleID id; - uint16 cached_max_range; -}; + if (IsSavegameVersionBefore(SLV_182)) v->last_loading_station = INVALID_STATION; -static std::vector _vehicle_vencs; -static std::vector _train_vencs; -static std::vector _roadvehicle_vencs; -static std::vector _aircraft_vencs; + if (IsSavegameVersionBefore(SLV_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; + } -void Save_VENC() -{ - assert(_sl_xv_feature_versions[XSLFI_VENC_CHUNK] != 0); - - if (!IsNetworkServerSave()) { - SlSetLength(0); - return; + /* Advanced vehicle lists got added */ + if (IsSavegameVersionBefore(SLV_60)) v->group_id = DEFAULT_GROUP; +#endif + } } - SlAutolength([](void *) { - int types[4] = {}; - int total = 0; + void FixPointers() const override + { for (Vehicle *v : Vehicle::Iterate()) { - total++; - if (v->type < VEH_COMPANY_END) types[v->type]++; - } - - /* vehicle cache */ - SlWriteUint32(total); - for (Vehicle *v : Vehicle::Iterate()) { - SlWriteUint32(v->index); - SlWriteUint16(v->vcache.cached_max_speed); - SlWriteUint16(v->vcache.cached_cargo_age_period); - SlWriteByte(v->vcache.cached_vis_effect); - SlWriteByte(v->vcache.cached_veh_flags); - } - - auto write_gv_cache = [&](const GroundVehicleCache &cache) { - SlWriteUint32(cache.cached_weight); - SlWriteUint32(cache.cached_slope_resistance); - SlWriteUint32(cache.cached_max_te); - SlWriteUint32(cache.cached_axle_resistance); - SlWriteUint32(cache.cached_max_track_speed); - SlWriteUint32(cache.cached_power); - SlWriteUint32(cache.cached_air_drag); - SlWriteUint16(cache.cached_total_length); - SlWriteUint16(cache.first_engine); - SlWriteByte(cache.cached_veh_length); - }; - - /* train */ - SlWriteUint32(types[VEH_TRAIN]); - for (Train *t : Train::Iterate()) { - SlWriteUint32(t->index); - write_gv_cache(t->gcache); - SlWriteUint32(t->tcache.cached_curve_speed_mod); - SlWriteByte(t->tcache.cached_tflags); - SlWriteByte(t->tcache.cached_num_engines); - SlWriteUint16(t->tcache.cached_centre_mass); - SlWriteUint16(t->tcache.cached_braking_length); - SlWriteUint16(t->tcache.cached_veh_weight); - SlWriteUint16(t->tcache.cached_uncapped_decel); - SlWriteByte(t->tcache.cached_deceleration); - SlWriteByte(t->tcache.user_def_data); - SlWriteUint32(t->tcache.cached_max_curve_speed); - } - - /* road vehicle */ - SlWriteUint32(types[VEH_ROAD]); - for (RoadVehicle *rv : RoadVehicle::Iterate()) { - SlWriteUint32(rv->index); - write_gv_cache(rv->gcache); - } - - /* aircraft */ - SlWriteUint32(types[VEH_AIRCRAFT]); - for (Aircraft *a : Aircraft::Iterate()) { - SlWriteUint32(a->index); - SlWriteUint16(a->acache.cached_max_range); - } - }, nullptr); -} - -void Load_VENC() -{ - if (SlGetFieldLength() == 0) return; - - if (!_networking || _network_server) { - SlSkipBytes(SlGetFieldLength()); - return; - } - - _vehicle_vencs.resize(SlReadUint32()); - for (vehicle_venc &venc : _vehicle_vencs) { - venc.id = SlReadUint32(); - venc.vcache.cached_max_speed = SlReadUint16(); - venc.vcache.cached_cargo_age_period = SlReadUint16(); - venc.vcache.cached_vis_effect = SlReadByte(); - venc.vcache.cached_veh_flags = SlReadByte(); - } - - auto read_gv_cache = [&](GroundVehicleCache &cache) { - cache.cached_weight = SlReadUint32(); - cache.cached_slope_resistance = SlReadUint32(); - cache.cached_max_te = SlReadUint32(); - cache.cached_axle_resistance = SlReadUint32(); - cache.cached_max_track_speed = SlReadUint32(); - cache.cached_power = SlReadUint32(); - cache.cached_air_drag = SlReadUint32(); - cache.cached_total_length = SlReadUint16(); - cache.first_engine = SlReadUint16(); - cache.cached_veh_length = SlReadByte(); - }; - - _train_vencs.resize(SlReadUint32()); - for (train_venc &venc : _train_vencs) { - venc.id = SlReadUint32(); - read_gv_cache(venc.gvcache); - venc.cached_curve_speed_mod = SlReadUint32(); - venc.cached_tflags = SlReadByte(); - venc.cached_num_engines = SlReadByte(); - venc.cached_centre_mass = SlReadUint16(); - venc.cached_braking_length = SlReadUint16(); - venc.cached_veh_weight = SlReadUint16(); - venc.cached_uncapped_decel = SlReadUint16(); - venc.cached_deceleration = SlReadByte(); - venc.user_def_data = SlReadByte(); - venc.cached_max_curve_speed = SlReadUint32(); - } - - _roadvehicle_vencs.resize(SlReadUint32()); - for (roadvehicle_venc &venc : _roadvehicle_vencs) { - venc.id = SlReadUint32(); - read_gv_cache(venc.gvcache); - } - - _aircraft_vencs.resize(SlReadUint32()); - for (aircraft_venc &venc : _aircraft_vencs) { - venc.id = SlReadUint32(); - venc.cached_max_range = SlReadUint16(); - } -} - -void SlResetVENC() -{ - _vehicle_vencs.clear(); - _train_vencs.clear(); - _roadvehicle_vencs.clear(); - _aircraft_vencs.clear(); -} - -static void LogVehicleVENCMessage(const Vehicle *v, const char *var) -{ - char log_buffer[1024]; - - char *p = log_buffer + seprintf(log_buffer, lastof(log_buffer), "[load]: vehicle cache mismatch: %s", var); - - extern void WriteVehicleInfo(char *&p, const char *last, const Vehicle *u, const Vehicle *v, uint length); - uint length = 0; - for (const Vehicle *u = v->First(); u != v; u = u->Next()) { - length++; - } - WriteVehicleInfo(p, lastof(log_buffer), v, v->First(), length); - DEBUG(desync, 0, "%s", log_buffer); - LogDesyncMsg(log_buffer); -} - -template -void CheckVehicleVENCProp(T &v_prop, T venc_prop, const Vehicle *v, const char *var) -{ - if (v_prop != venc_prop) { - std::string data = fmt::format("{} [{:X} != {:X}]", var, v_prop, venc_prop); - v_prop = venc_prop; - LogVehicleVENCMessage(v, data.c_str()); - } -} - -void SlProcessVENC() -{ - for (const vehicle_venc &venc : _vehicle_vencs) { - Vehicle *v = Vehicle::GetIfValid(venc.id); - if (v == nullptr) continue; - CheckVehicleVENCProp(v->vcache.cached_max_speed, venc.vcache.cached_max_speed, v, "cached_max_speed"); - CheckVehicleVENCProp(v->vcache.cached_cargo_age_period, venc.vcache.cached_cargo_age_period, v, "cached_cargo_age_period"); - CheckVehicleVENCProp(v->vcache.cached_vis_effect, venc.vcache.cached_vis_effect, v, "cached_vis_effect"); - if (HasBit(v->vcache.cached_veh_flags ^ venc.vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT)) { - SB(v->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT, 1, HasBit(venc.vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT) ? 1 : 0); - LogVehicleVENCMessage(v, "VCF_LAST_VISUAL_EFFECT"); + SlObject(v, _vehicle_desc); } } +}; - auto check_gv_cache = [&](GroundVehicleCache &v_gvcache, const GroundVehicleCache &venc_gvcache, const Vehicle *v) { - CheckVehicleVENCProp(v_gvcache.cached_weight, venc_gvcache.cached_weight, v, "cached_weight"); - CheckVehicleVENCProp(v_gvcache.cached_slope_resistance, venc_gvcache.cached_slope_resistance, v, "cached_slope_resistance"); - CheckVehicleVENCProp(v_gvcache.cached_max_te, venc_gvcache.cached_max_te, v, "cached_max_te"); - CheckVehicleVENCProp(v_gvcache.cached_axle_resistance, venc_gvcache.cached_axle_resistance, v, "cached_axle_resistance"); - CheckVehicleVENCProp(v_gvcache.cached_max_track_speed, venc_gvcache.cached_max_track_speed, v, "cached_max_track_speed"); - CheckVehicleVENCProp(v_gvcache.cached_power, venc_gvcache.cached_power, v, "cached_power"); - CheckVehicleVENCProp(v_gvcache.cached_air_drag, venc_gvcache.cached_air_drag, v, "cached_air_drag"); - CheckVehicleVENCProp(v_gvcache.cached_total_length, venc_gvcache.cached_total_length, v, "cached_total_length"); - CheckVehicleVENCProp(v_gvcache.first_engine, venc_gvcache.first_engine, v, "first_engine"); - CheckVehicleVENCProp(v_gvcache.cached_veh_length, venc_gvcache.cached_veh_length, v, "cached_veh_length"); - }; - - for (const train_venc &venc : _train_vencs) { - Train *t = Train::GetIfValid(venc.id); - if (t == nullptr) continue; - check_gv_cache(t->gcache, venc.gvcache, t); - CheckVehicleVENCProp(t->tcache.cached_curve_speed_mod, venc.cached_curve_speed_mod, t, "cached_curve_speed_mod"); - CheckVehicleVENCProp(t->tcache.cached_tflags, (TrainCacheFlags)venc.cached_tflags, t, "cached_tflags"); - CheckVehicleVENCProp(t->tcache.cached_num_engines, venc.cached_num_engines, t, "cached_num_engines"); - CheckVehicleVENCProp(t->tcache.cached_centre_mass, venc.cached_centre_mass, t, "cached_centre_mass"); - CheckVehicleVENCProp(t->tcache.cached_braking_length, venc.cached_braking_length, t, "cached_braking_length"); - CheckVehicleVENCProp(t->tcache.cached_veh_weight, venc.cached_veh_weight, t, "cached_veh_weight"); - CheckVehicleVENCProp(t->tcache.cached_uncapped_decel, venc.cached_uncapped_decel, t, "cached_uncapped_decel"); - CheckVehicleVENCProp(t->tcache.cached_deceleration, venc.cached_deceleration, t, "cached_deceleration"); - CheckVehicleVENCProp(t->tcache.user_def_data, venc.user_def_data, t, "user_def_data"); - CheckVehicleVENCProp(t->tcache.cached_max_curve_speed, venc.cached_max_curve_speed, t, "cached_max_curve_speed"); - } - - for (const roadvehicle_venc &venc : _roadvehicle_vencs) { - RoadVehicle *rv = RoadVehicle::GetIfValid(venc.id); - if (rv == nullptr) continue; - check_gv_cache(rv->gcache, venc.gvcache, rv); - } - - for (const aircraft_venc &venc : _aircraft_vencs) { - Aircraft *a = Aircraft::GetIfValid(venc.id); - if (a == nullptr) continue; - if (a->acache.cached_max_range != venc.cached_max_range) { - a->acache.cached_max_range = venc.cached_max_range; - a->acache.cached_max_range_sqr = venc.cached_max_range * venc.cached_max_range; - LogVehicleVENCMessage(a, "cached_max_range"); - } - } -} - -static ChunkSaveLoadSpecialOpResult Special_VENC(uint32 chunk_id, ChunkSaveLoadSpecialOp op) -{ - switch (op) { - case CSLSO_SHOULD_SAVE_CHUNK: - if (_sl_xv_feature_versions[XSLFI_VENC_CHUNK] == 0) return CSLSOR_DONT_SAVE_CHUNK; - break; - - default: - break; - } - return CSLSOR_NONE; -} - -const SaveLoadTable GetVehicleLookAheadDescription() -{ - static const SaveLoad _vehicle_look_ahead_desc[] = { - SLE_VAR(TrainReservationLookAhead, reservation_end_tile, SLE_UINT32), - SLE_VAR(TrainReservationLookAhead, reservation_end_trackdir, SLE_UINT8), - SLE_VAR(TrainReservationLookAhead, current_position, SLE_INT32), - SLE_VAR(TrainReservationLookAhead, reservation_end_position, SLE_INT32), - SLE_CONDVAR_X(TrainReservationLookAhead, lookahead_end_position, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 9)), - SLE_VAR(TrainReservationLookAhead, reservation_end_z, SLE_INT16), - SLE_VAR(TrainReservationLookAhead, tunnel_bridge_reserved_tiles, SLE_INT16), - SLE_VAR(TrainReservationLookAhead, flags, SLE_UINT16), - SLE_VAR(TrainReservationLookAhead, speed_restriction, SLE_UINT16), - SLE_CONDVAR_X(TrainReservationLookAhead, next_extend_position, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 5)), - SLE_CONDVAR_X(TrainReservationLookAhead, cached_zpos, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 6)), - SLE_CONDVAR_X(TrainReservationLookAhead, zpos_refresh_remaining, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 6)), - }; - - return _vehicle_look_ahead_desc; -} - -const SaveLoadTable GetVehicleLookAheadItemDescription() -{ - static const SaveLoad _vehicle_look_ahead_item_desc[] = { - SLE_VAR(TrainReservationLookAheadItem, start, SLE_INT32), - SLE_VAR(TrainReservationLookAheadItem, end, SLE_INT32), - SLE_VAR(TrainReservationLookAheadItem, z_pos, SLE_INT16), - SLE_VAR(TrainReservationLookAheadItem, data_id, SLE_UINT16), - SLE_CONDVAR_X(TrainReservationLookAheadItem, data_aux, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 9)), - SLE_VAR(TrainReservationLookAheadItem, type, SLE_UINT8), - }; - - return _vehicle_look_ahead_item_desc; -} - -const SaveLoadTable GetVehicleLookAheadCurveDescription() -{ - static const SaveLoad _vehicle_look_ahead_curve_desc[] = { - SLE_VAR(TrainReservationLookAheadCurve, position, SLE_INT32), - SLE_VAR(TrainReservationLookAheadCurve, dir_diff, SLE_UINT8), - }; - - return _vehicle_look_ahead_curve_desc; -} - -static void RealSave_VLKA(TrainReservationLookAhead *lookahead) -{ - SlObject(lookahead, GetVehicleLookAheadDescription()); - SlWriteUint32((uint32)lookahead->items.size()); - for (TrainReservationLookAheadItem &item : lookahead->items) { - SlObject(&item, GetVehicleLookAheadItemDescription()); - } - SlWriteUint32((uint32)lookahead->curves.size()); - for (TrainReservationLookAheadCurve &curve : lookahead->curves) { - SlObject(&curve, GetVehicleLookAheadCurveDescription()); - } -} - -void Save_VLKA() -{ - for (Train *t : Train::Iterate()) { - if (t->lookahead != nullptr) { - SlSetArrayIndex(t->index); - SlAutolength((AutolengthProc*) RealSave_VLKA, t->lookahead.get()); - } - } -} - -void Load_VLKA() -{ - int index; - while ((index = SlIterateArray()) != -1) { - Train *t = Train::GetIfValid(index); - assert(t != nullptr); - t->lookahead.reset(new TrainReservationLookAhead()); - SlObject(t->lookahead.get(), GetVehicleLookAheadDescription()); - uint32 items = SlReadUint32(); - t->lookahead->items.resize(items); - for (uint i = 0; i < items; i++) { - SlObject(&t->lookahead->items[i], GetVehicleLookAheadItemDescription()); - } - uint32 curves = SlReadUint32(); - t->lookahead->curves.resize(curves); - for (uint i = 0; i < curves; i++) { - SlObject(&t->lookahead->curves[i], GetVehicleLookAheadCurveDescription()); - } - } -} - -static const ChunkHandler veh_chunk_handlers[] = { - { 'VEHS', Save_VEHS, Load_VEHS, Ptrs_VEHS, nullptr, CH_SPARSE_ARRAY }, - { 'VEOX', Save_VEOX, Load_VEOX, nullptr, nullptr, CH_SPARSE_ARRAY }, - { 'VESR', Save_VESR, Load_VESR, nullptr, nullptr, CH_SPARSE_ARRAY }, - { 'VENC', Save_VENC, Load_VENC, nullptr, nullptr, CH_RIFF, Special_VENC }, - { 'VLKA', Save_VLKA, Load_VLKA, nullptr, nullptr, CH_SPARSE_ARRAY }, +static const VEHSChunkHandler VEHS; +static const ChunkHandlerRef veh_chunk_handlers[] = { + VEHS, }; extern const ChunkHandlerTable _veh_chunk_handlers(veh_chunk_handlers); + +} diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 93cceb05c4..0236e6fc68 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -16,7 +16,7 @@ #include "blitter/factory.hpp" #include "zoom_func.h" #include "core/endian_func.hpp" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "company_base.h" #include "company_func.h" #include "strings_func.h" diff --git a/src/script/script_instance.cpp b/src/script/script_instance.cpp index ed4864b864..04c9970188 100644 --- a/src/script/script_instance.cpp +++ b/src/script/script_instance.cpp @@ -9,7 +9,7 @@ #include "../stdafx.h" #include "../debug.h" -#include "../saveload/saveload.h" +#include "../sl/saveload.h" #include "../script/squirrel_class.hpp" #include "../script/squirrel_std.hpp" diff --git a/src/settings.cpp b/src/settings.cpp index 0f5399cf19..a5421b7c50 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -86,7 +86,7 @@ #define HAS_TRUETYPE_FONT #endif -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "table/strings.h" #include "table/settings.h" diff --git a/src/settings_internal.h b/src/settings_internal.h index 2d7587581d..dedc88c9a5 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -10,7 +10,7 @@ #ifndef SETTINGS_INTERNAL_H #define SETTINGS_INTERNAL_H -#include "saveload/saveload_types.h" +#include "sl/saveload_types.h" enum SettingFlag : uint32 { SF_NONE = 0, diff --git a/src/saveload/upstream/CMakeLists.txt b/src/sl/CMakeLists.txt similarity index 56% rename from src/saveload/upstream/CMakeLists.txt rename to src/sl/CMakeLists.txt index 665e2550a4..88f233e715 100644 --- a/src/saveload/upstream/CMakeLists.txt +++ b/src/sl/CMakeLists.txt @@ -1,17 +1,19 @@ -add_subdirectory(compat) - add_files( ai_sl.cpp airport_sl.cpp animated_tile_sl.cpp autoreplace_sl.cpp + bridge_signal_sl.cpp cargomonitor_sl.cpp cargopacket_sl.cpp cheat_sl.cpp company_sl.cpp + debug_sl.cpp depot_sl.cpp economy_sl.cpp engine_sl.cpp + extended_ver_sl.cpp + extended_ver_sl.h game_sl.cpp gamelog_sl.cpp goal_sl.cpp @@ -24,16 +26,33 @@ add_files( misc_sl.cpp newgrf_sl.cpp newgrf_sl.h + newsignals_sl.cpp object_sl.cpp + oldloader.cpp + oldloader.h + oldloader_sl.cpp order_sl.cpp + plans_sl.cpp saveload.cpp saveload.h - settings_sl.cpp + saveload_buffer.h + saveload_common.h + saveload_filter.h + saveload_internal.h + saveload_types.h + signal_sl.cpp signs_sl.cpp station_sl.cpp storage_sl.cpp + strings_sl.cpp story_sl.cpp subsidy_sl.cpp + tbtr_template_replacement_sl.cpp + tbtr_template_veh_sl.cpp town_sl.cpp + tracerestrict_sl.cpp + train_speed_adaptation.cpp + tunnel_sl.cpp vehicle_sl.cpp + waypoint_sl.cpp ) diff --git a/src/sl/ai_sl.cpp b/src/sl/ai_sl.cpp new file mode 100644 index 0000000000..0b5b9f6e54 --- /dev/null +++ b/src/sl/ai_sl.cpp @@ -0,0 +1,124 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file ai_sl.cpp Handles the saveload part of the AIs */ + +#include "../stdafx.h" +#include "../company_base.h" +#include "../debug.h" +#include "saveload.h" +#include "../string_func.h" + +#include "../ai/ai.hpp" +#include "../ai/ai_config.hpp" +#include "../network/network.h" +#include "../ai/ai_instance.hpp" + +#include "../safeguards.h" + +static std::string _ai_saveload_name; +static int _ai_saveload_version; +static std::string _ai_saveload_settings; +static bool _ai_saveload_is_random; + +static const SaveLoad _ai_company[] = { + SLEG_SSTR(_ai_saveload_name, SLE_STR), + SLEG_SSTR(_ai_saveload_settings, SLE_STR), + SLEG_CONDVAR(_ai_saveload_version, SLE_UINT32, SLV_108, SL_MAX_VERSION), + SLEG_CONDVAR(_ai_saveload_is_random, SLE_BOOL, SLV_136, SL_MAX_VERSION), +}; + +static void SaveReal_AIPL(int *index_ptr) +{ + CompanyID index = (CompanyID)*index_ptr; + AIConfig *config = AIConfig::GetConfig(index); + + if (config->HasScript()) { + _ai_saveload_name = config->GetName(); + _ai_saveload_version = config->GetVersion(); + } else { + /* No AI is configured for this so store an empty string as name. */ + _ai_saveload_name.clear(); + _ai_saveload_version = -1; + } + + _ai_saveload_is_random = config->IsRandom(); + _ai_saveload_settings = config->SettingsToString(); + + SlObject(nullptr, _ai_company); + /* If the AI was active, store its data too */ + if (Company::IsValidAiID(index)) AI::Save(index); +} + +static void Load_AIPL() +{ + /* Free all current data */ + for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { + AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(nullptr); + } + + CompanyID index; + while ((index = (CompanyID)SlIterateArray()) != (CompanyID)-1) { + if (index >= MAX_COMPANIES) SlErrorCorrupt("Too many AI configs"); + + _ai_saveload_is_random = false; + _ai_saveload_version = -1; + SlObject(nullptr, _ai_company); + + if (_game_mode == GM_MENU || (_networking && !_network_server)) { + if (Company::IsValidAiID(index)) AIInstance::LoadEmpty(); + continue; + } + + AIConfig *config = AIConfig::GetConfig(index, AIConfig::SSS_FORCE_GAME); + if (_ai_saveload_name.empty()) { + /* A random AI. */ + config->Change(nullptr, -1, false, true); + } else { + config->Change(_ai_saveload_name.c_str(), _ai_saveload_version, false, _ai_saveload_is_random); + if (!config->HasScript()) { + /* No version of the AI available that can load the data. Try to load the + * latest version of the AI instead. */ + config->Change(_ai_saveload_name.c_str(), -1, false, _ai_saveload_is_random); + if (!config->HasScript()) { + if (_ai_saveload_name.compare("%_dummy") != 0) { + DEBUG(script, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version); + DEBUG(script, 0, "A random other AI will be loaded in its place."); + } else { + DEBUG(script, 0, "The savegame had no AIs available at the time of saving."); + DEBUG(script, 0, "A random available AI will be loaded now."); + } + } else { + DEBUG(script, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version); + DEBUG(script, 0, "The latest version of that AI has been loaded instead, but it'll not get the savegame data as it's incompatible."); + } + /* Make sure the AI doesn't get the saveload data, as it was not the + * writer of the saveload data in the first place */ + _ai_saveload_version = -1; + } + } + + config->StringToSettings(_ai_saveload_settings); + + /* Load the AI saved data */ + if (Company::IsValidAiID(index)) config->SetToLoadData(AIInstance::Load(_ai_saveload_version)); + } +} + +static void Save_AIPL() +{ + for (int i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + SlSetArrayIndex(i); + SlAutolength((AutolengthProc *)SaveReal_AIPL, &i); + } +} + +static const ChunkHandler ai_chunk_handlers[] = { + { 'AIPL', Save_AIPL, Load_AIPL, nullptr, nullptr, CH_ARRAY }, +}; + +extern const ChunkHandlerTable _ai_chunk_handlers(ai_chunk_handlers); diff --git a/src/saveload/upstream/airport_sl.cpp b/src/sl/airport_sl.cpp similarity index 61% rename from src/saveload/upstream/airport_sl.cpp rename to src/sl/airport_sl.cpp index 969f83ab9d..114e5672ab 100644 --- a/src/saveload/upstream/airport_sl.cpp +++ b/src/sl/airport_sl.cpp @@ -7,30 +7,36 @@ /** @file airport_sl.cpp Code handling saving and loading airport ids */ -#include "../../stdafx.h" +#include "../stdafx.h" #include "saveload.h" #include "newgrf_sl.h" -#include "../../safeguards.h" +#include "../safeguards.h" -namespace upstream_sl { +static void Save_APID() +{ + Save_NewGRFMapping(_airport_mngr); +} -struct APIDChunkHandler : NewGRFMappingChunkHandler { - APIDChunkHandler() : NewGRFMappingChunkHandler('APID', _airport_mngr) {} -}; +static void Load_APID() +{ + Load_NewGRFMapping(_airport_mngr); +} -struct ATIDChunkHandler : NewGRFMappingChunkHandler { - ATIDChunkHandler() : NewGRFMappingChunkHandler('ATID', _airporttile_mngr) {} -}; +static void Save_ATID() +{ + Save_NewGRFMapping(_airporttile_mngr); +} -static const ATIDChunkHandler ATID; -static const APIDChunkHandler APID; -static const ChunkHandlerRef airport_chunk_handlers[] = { - ATID, - APID, +static void Load_ATID() +{ + Load_NewGRFMapping(_airporttile_mngr); +} + +static const ChunkHandler airport_chunk_handlers[] = { + { 'ATID', Save_ATID, Load_ATID, nullptr, nullptr, CH_ARRAY }, + { 'APID', Save_APID, Load_APID, nullptr, nullptr, CH_ARRAY }, }; extern const ChunkHandlerTable _airport_chunk_handlers(airport_chunk_handlers); - -} diff --git a/src/sl/animated_tile_sl.cpp b/src/sl/animated_tile_sl.cpp new file mode 100644 index 0000000000..b9ccc44092 --- /dev/null +++ b/src/sl/animated_tile_sl.cpp @@ -0,0 +1,80 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file animated_tile_sl.cpp Code handling saving and loading of animated tiles */ + +#include "../stdafx.h" +#include "../animated_tile.h" +#include "../tile_type.h" +#include "../core/alloc_func.hpp" +#include "../core/smallvec_type.hpp" + +#include "saveload.h" + +#include "../safeguards.h" + +/** + * Save the ANIT chunk. + */ +static void Save_ANIT() +{ + uint count = 0; + for (const auto &it : _animated_tiles) { + if (!it.second.pending_deletion) count++; + } + SlSetLength(count * 5); + for (const auto &it : _animated_tiles) { + if (it.second.pending_deletion) continue; + SlWriteUint32(it.first); + SlWriteByte(it.second.speed); + } +} + +/** + * 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 (IsSavegameVersionBefore(SLV_80)) { + /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */ + TileIndex anim_list[256]; + SlArray(anim_list, 256, IsSavegameVersionBefore(SLV_6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32); + + for (int i = 0; i < 256; i++) { + if (anim_list[i] == 0) break; + _animated_tiles[anim_list[i]] = {}; + } + return; + } + + _animated_tiles.clear(); + if (SlXvIsFeaturePresent(XSLFI_ANIMATED_TILE_EXTRA)) { + uint count = (uint)SlGetFieldLength() / 5; + for (uint i = 0; i < count; i++) { + TileIndex tile = SlReadUint32(); + AnimatedTileInfo info = {}; + info.speed = SlReadByte(); + _animated_tiles[tile] = info; + } + } else { + uint count = (uint)SlGetFieldLength() / 4; + for (uint i = 0; i < count; i++) { + _animated_tiles[SlReadUint32()] = {}; + } + } +} + +/** + * "Definition" imported by the saveload code to be able to load and save + * the animated tile table. + */ +static const ChunkHandler animated_tile_chunk_handlers[] = { + { 'ANIT', Save_ANIT, Load_ANIT, nullptr, nullptr, CH_RIFF }, +}; + +extern const ChunkHandlerTable _animated_tile_chunk_handlers(animated_tile_chunk_handlers); diff --git a/src/sl/autoreplace_sl.cpp b/src/sl/autoreplace_sl.cpp new file mode 100644 index 0000000000..f54866c107 --- /dev/null +++ b/src/sl/autoreplace_sl.cpp @@ -0,0 +1,62 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file autoreplace_sl.cpp Code handling saving and loading of autoreplace rules */ + +#include "../stdafx.h" +#include "../autoreplace_base.h" + +#include "saveload.h" + +#include "../safeguards.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, SLV_60, SL_MAX_VERSION), + SLE_CONDVAR(EngineRenew, replace_when_old, SLE_BOOL, SLV_175, SL_MAX_VERSION), +}; + +static void Save_ERNW() +{ + for (EngineRenew *er : EngineRenew::Iterate()) { + 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 (IsSavegameVersionBefore(SLV_60)) { + er->group_id = ALL_GROUP; + } else if (IsSavegameVersionBefore(SLV_71)) { + if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP; + } + } +} + +static void Ptrs_ERNW() +{ + for (EngineRenew *er : EngineRenew::Iterate()) { + SlObject(er, _engine_renew_desc); + } +} + +static const ChunkHandler autoreplace_chunk_handlers[] = { + { 'ERNW', Save_ERNW, Load_ERNW, Ptrs_ERNW, nullptr, CH_ARRAY }, +}; + +extern const ChunkHandlerTable _autoreplace_chunk_handlers(autoreplace_chunk_handlers); diff --git a/src/saveload/bridge_signal_sl.cpp b/src/sl/bridge_signal_sl.cpp similarity index 100% rename from src/saveload/bridge_signal_sl.cpp rename to src/sl/bridge_signal_sl.cpp diff --git a/src/sl/cargomonitor_sl.cpp b/src/sl/cargomonitor_sl.cpp new file mode 100644 index 0000000000..abb0d59b99 --- /dev/null +++ b/src/sl/cargomonitor_sl.cpp @@ -0,0 +1,125 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file cargomonitor_sl.cpp Code handling saving and loading of Cargo monitoring. */ + +#include "../stdafx.h" +#include "../cargomonitor.h" + +#include "saveload.h" + +#include "../safeguards.h" + +/** Temporary storage of cargo monitoring data for loading or saving it. */ +struct TempStorage { + CargoMonitorID number; + uint32 amount; +}; + +/** Description of the #TempStorage structure for the purpose of load and save. */ +static const SaveLoad _cargomonitor_pair_desc[] = { + SLE_VAR(TempStorage, number, SLE_UINT32), + SLE_VAR(TempStorage, amount, SLE_UINT32), +}; + +static CargoMonitorID FixupCargoMonitor(CargoMonitorID number) +{ + /* Between SLV_EXTEND_CARGOTYPES and SLV_FIX_CARGO_MONITOR, the + * CargoMonitorID structure had insufficient packing for more + * than 32 cargo types. Here we have to shuffle bits to account + * for the change. + * Company moved from bits 24-31 to 25-28. + * Cargo type increased from bits 19-23 to 19-24. + */ + SB(number, 25, 4, GB(number, 24, 4)); + SB(number, 29, 3, 0); + ClrBit(number, 24); + return number; +} + +/** Save the #_cargo_deliveries monitoring map. */ +static void SaveDelivery() +{ + TempStorage storage; + + int i = 0; + CargoMonitorMap::const_iterator iter = _cargo_deliveries.begin(); + while (iter != _cargo_deliveries.end()) { + storage.number = iter->first; + storage.amount = iter->second; + + SlSetArrayIndex(i); + SlObject(&storage, _cargomonitor_pair_desc); + + i++; + iter++; + } +} + +/** Load the #_cargo_deliveries monitoring map. */ +static void LoadDelivery() +{ + TempStorage storage; + bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR); + + ClearCargoDeliveryMonitoring(); + for (;;) { + if (SlIterateArray() < 0) break; + SlObject(&storage, _cargomonitor_pair_desc); + + if (fix) storage.number = FixupCargoMonitor(storage.number); + + std::pair p(storage.number, storage.amount); + _cargo_deliveries.insert(p); + } +} + + +/** Save the #_cargo_pickups monitoring map. */ +static void SavePickup() +{ + TempStorage storage; + + int i = 0; + CargoMonitorMap::const_iterator iter = _cargo_pickups.begin(); + while (iter != _cargo_pickups.end()) { + storage.number = iter->first; + storage.amount = iter->second; + + SlSetArrayIndex(i); + SlObject(&storage, _cargomonitor_pair_desc); + + i++; + iter++; + } +} + +/** Load the #_cargo_pickups monitoring map. */ +static void LoadPickup() +{ + TempStorage storage; + bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR); + + ClearCargoPickupMonitoring(); + for (;;) { + if (SlIterateArray() < 0) break; + SlObject(&storage, _cargomonitor_pair_desc); + + if (fix) storage.number = FixupCargoMonitor(storage.number); + + std::pair p(storage.number, storage.amount); + _cargo_pickups.insert(p); + } +} + +/** Chunk definition of the cargomonitoring maps. */ +static const ChunkHandler cargomonitor_chunk_handlers[] = { + { 'CMDL', SaveDelivery, LoadDelivery, nullptr, nullptr, CH_ARRAY }, + { 'CMPU', SavePickup, LoadPickup, nullptr, nullptr, CH_ARRAY }, +}; + +extern const ChunkHandlerTable _cargomonitor_chunk_handlers(cargomonitor_chunk_handlers); diff --git a/src/sl/cargopacket_sl.cpp b/src/sl/cargopacket_sl.cpp new file mode 100644 index 0000000000..69ad66fec4 --- /dev/null +++ b/src/sl/cargopacket_sl.cpp @@ -0,0 +1,195 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file cargopacket_sl.cpp Code handling saving and loading of cargo packets */ + +#include "../stdafx.h" +#include "../vehicle_base.h" +#include "../station_base.h" +#include "../scope_info.h" +#include "../3rdparty/cpp-btree/btree_map.h" + +#include "saveload.h" + +#include "../safeguards.h" + +extern btree::btree_map _cargo_packet_deferred_payments; + +/** + * Savegame conversion for cargopackets. + */ +/* static */ void CargoPacket::AfterLoad() +{ + if (IsSavegameVersionBefore(SLV_44)) { + /* If we remove a station while cargo from it is still en route, 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 (const Vehicle *v : Vehicle::Iterate()) { + const CargoPacketList *packets = v->cargo.Packets(); + for (VehicleCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) { + CargoPacket *cp = *it; + cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : v->tile; + cp->loaded_at_xy = cp->source_xy; + } + } + + /* 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 */ + for (Station *st : Station::Iterate()) { + for (CargoID c = 0; c < NUM_CARGO; c++) { + GoodsEntry *ge = &st->goods[c]; + + const StationCargoPacketMap *packets = ge->cargo.Packets(); + for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) { + CargoPacket *cp = *it; + cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : st->xy; + cp->loaded_at_xy = cp->source_xy; + } + } + } + } + + if (IsSavegameVersionBefore(SLV_120)) { + /* CargoPacket's source should be either INVALID_STATION or a valid station */ + for (CargoPacket *cp : CargoPacket::Iterate()) { + if (!Station::IsValidID(cp->source)) cp->source = INVALID_STATION; + } + } + + if (!IsSavegameVersionBefore(SLV_68)) { + /* Only since version 68 we have cargo packets. Savegames from before used + * 'new CargoPacket' + cargolist.Append so their caches are already + * correct and do not need rebuilding. */ + for (Vehicle *v : Vehicle::Iterate()) v->cargo.InvalidateCache(); + + for (Station *st : Station::Iterate()) { + for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache(); + } + } + + if (IsSavegameVersionBefore(SLV_181)) { + for (Vehicle *v : Vehicle::Iterate()) v->cargo.KeepAll(); + } +} + +/** + * Savegame conversion for cargopackets. + */ +/* static */ void CargoPacket::PostVehiclesAfterLoad() +{ + if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { + extern std::map _veh_cpp_packets; + for (auto &iter : _veh_cpp_packets) { + if (iter.second.empty()) continue; + Vehicle *v = Vehicle::Get(iter.first); + Station *st = Station::Get(v->First()->last_station_visited); + assert_msg(st != nullptr, "%s", scope_dumper().VehicleInfo(v)); + for (CargoPacket *cp : iter.second) { + st->goods[v->cargo_type].cargo.AfterLoadIncreaseReservationCount(cp->count); + v->cargo.Append(cp, VehicleCargoList::MTA_LOAD); + } + } + _veh_cpp_packets.clear(); + } +} + +/** + * Wrapper function to get the CargoPacket's internal structure while + * some of the variables itself are private. + * @return the saveload description for CargoPackets. + */ +SaveLoadTable GetCargoPacketDesc() +{ + 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_CONDVAR_X(CargoPacket, days_in_transit, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_CARGO_AGE, 0, 0)), + SLE_CONDVAR_X(CargoPacket, days_in_transit, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_CARGO_AGE)), + SLE_VAR(CargoPacket, feeder_share, SLE_INT64), + SLE_CONDVAR(CargoPacket, source_type, SLE_UINT8, SLV_125, SL_MAX_VERSION), + SLE_CONDVAR(CargoPacket, source_id, SLE_UINT16, SLV_125, SL_MAX_VERSION), + + /* Used to be paid_for, but that got changed. */ + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_121), + }; + return _cargopacket_desc; +} + +/** + * Save the cargo packets. + */ +static void Save_CAPA() +{ + std::vector filtered_packet_desc = SlFilterObject(GetCargoPacketDesc()); + for (CargoPacket *cp : CargoPacket::Iterate()) { + SlSetArrayIndex(cp->index); + SlObjectSaveFiltered(cp, filtered_packet_desc); + } +} + +/** + * Load the cargo packets. + */ +static void Load_CAPA() +{ + std::vector filtered_packet_desc = SlFilterObject(GetCargoPacketDesc()); + int index; + while ((index = SlIterateArray()) != -1) { + CargoPacket *cp = new (index) CargoPacket(); + SlObjectLoadFiltered(cp, filtered_packet_desc); + } +} + +/** + * Save cargo packet deferred payments. + */ +void Save_CPDP() +{ + SlSetLength(16 * _cargo_packet_deferred_payments.size()); + + for (auto &it : _cargo_packet_deferred_payments) { + SlWriteUint64(it.first); + SlWriteUint64(it.second); + } +} + +/** + * Load cargo packet deferred payments. + */ +void Load_CPDP() +{ + uint count = static_cast(SlGetFieldLength() / 16); + uint last_cargo_packet_id = std::numeric_limits::max(); + + for (uint i = 0; i < count; i++) { + uint64 k = SlReadUint64(); + uint64 v = SlReadUint64(); + _cargo_packet_deferred_payments[k] = v; + if (k >> 32 != last_cargo_packet_id) { + last_cargo_packet_id = k >> 32; + CargoPacket::Get(last_cargo_packet_id)->flags |= CargoPacket::CPF_HAS_DEFERRED_PAYMENT; + } + } +} + + + +/** Chunk handlers related to cargo packets. */ +static const ChunkHandler cargopacket_chunk_handlers[] = { + { 'CAPA', Save_CAPA, Load_CAPA, nullptr, nullptr, CH_ARRAY }, + { 'CPDP', Save_CPDP, Load_CPDP, nullptr, nullptr, CH_RIFF }, +}; + +extern const ChunkHandlerTable _cargopacket_chunk_handlers(cargopacket_chunk_handlers); diff --git a/src/sl/cheat_sl.cpp b/src/sl/cheat_sl.cpp new file mode 100644 index 0000000000..d92f9945f3 --- /dev/null +++ b/src/sl/cheat_sl.cpp @@ -0,0 +1,186 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file cheat_sl.cpp Code handling saving and loading of cheats */ + +#include "../stdafx.h" +#include "../cheat_type.h" +#include "../debug.h" + +#include "saveload.h" + +#include +#include + +#include "../safeguards.h" + +extern std::map _unknown_cheats; + +struct ExtraCheatNameDesc { + const char *name; + Cheat *cht; +}; + +static ExtraCheatNameDesc _extra_cheat_descs[] = { + { "inflation_cost", &_extra_cheats.inflation_cost }, + { "inflation_income", &_extra_cheats.inflation_income }, + { "station_rating", &_extra_cheats.station_rating }, + { "town_rating", &_extra_cheats.town_rating }, +}; + +static const SaveLoad _cheats_desc[] = { + SLE_VAR(Cheats, magic_bulldozer.been_used, SLE_BOOL), + SLE_VAR(Cheats, magic_bulldozer.value, SLE_BOOL), + SLE_VAR(Cheats, switch_company.been_used, SLE_BOOL), + SLE_VAR(Cheats, switch_company.value, SLE_BOOL), + SLE_VAR(Cheats, money.been_used, SLE_BOOL), + SLE_VAR(Cheats, money.value, SLE_BOOL), + SLE_VAR(Cheats, crossing_tunnels.been_used, SLE_BOOL), + SLE_VAR(Cheats, crossing_tunnels.value, SLE_BOOL), + SLE_NULL(1), + SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS(). + SLE_VAR(Cheats, no_jetcrash.been_used, SLE_BOOL), + SLE_VAR(Cheats, no_jetcrash.value, SLE_BOOL), + SLE_NULL(1), + SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS(). + SLE_VAR(Cheats, change_date.been_used, SLE_BOOL), + SLE_VAR(Cheats, change_date.value, SLE_BOOL), + SLE_VAR(Cheats, setup_prod.been_used, SLE_BOOL), + SLE_VAR(Cheats, setup_prod.value, SLE_BOOL), + SLE_NULL(1), + SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS(). + SLE_VAR(Cheats, edit_max_hl.been_used, SLE_BOOL), + SLE_VAR(Cheats, edit_max_hl.value, SLE_BOOL), +}; + +/** + * Save the cheat values. + */ +static void Save_CHTS() +{ + SlSetLength(std::size(_cheats_desc)); + SlObject(&_cheats, _cheats_desc); +} + +/** + * Load the cheat values. + */ +static void Load_CHTS() +{ + size_t count = SlGetFieldLength(); + std::vector slt; + + /* Cheats were added over the years without a savegame bump. They are + * stored as 2 SLE_BOOLs per entry. "count" indicates how many SLE_BOOLs + * are stored for this savegame. So read only "count" SLE_BOOLs (and in + * result "count / 2" cheats). */ + for (auto &sld : _cheats_desc) { + count--; + slt.push_back(sld); + + if (count == 0) break; + } + + SlObject(&_cheats, slt); +} + +/** + * Load the extra cheat values. + */ +static void Load_CHTX() +{ + struct CheatsExtLoad { + char name[256]; + Cheat cht; + }; + + static const SaveLoad _cheats_ext_load_desc[] = { + SLE_STR(CheatsExtLoad, name, SLE_STRB, 256), + SLE_VAR(CheatsExtLoad, cht.been_used, SLE_BOOL), + SLE_VAR(CheatsExtLoad, cht.value, SLE_BOOL), + }; + + CheatsExtLoad current_cheat; + + uint32 chunk_flags = SlReadUint32(); + // flags are not in use yet, reserve for future expansion + if (chunk_flags != 0) SlErrorCorruptFmt("CHTX chunk: unknown chunk header flags: 0x%X", chunk_flags); + + uint32 cheat_count = SlReadUint32(); + for (uint32 i = 0; i < cheat_count; i++) { + SlObject(¤t_cheat, _cheats_ext_load_desc); + + bool found = false; + for (uint j = 0; j < lengthof(_extra_cheat_descs); j++) { + const ExtraCheatNameDesc &desc = _extra_cheat_descs[j]; + if (strcmp(desc.name, current_cheat.name) == 0) { + *(desc.cht) = current_cheat.cht; + found = true; + break; + } + } + if (!found) { + DEBUG(sl, 1, "CHTX chunk: Could not find cheat: '%s'", current_cheat.name); + _unknown_cheats[current_cheat.name] = current_cheat.cht; + } + } +} + +/** + * Save the extra cheat values. + */ +static void Save_CHTX() +{ + struct CheatsExtSave { + const char *name; + Cheat cht; + }; + + static const SaveLoad _cheats_ext_save_desc[] = { + SLE_STR(CheatsExtSave, name, SLE_STR, 0), + SLE_VAR(CheatsExtSave, cht.been_used, SLE_BOOL), + SLE_VAR(CheatsExtSave, cht.value, SLE_BOOL), + }; + + SlAutolength([](void *) { + SlWriteUint32(0); // flags + SlWriteUint32((uint32)(lengthof(_extra_cheat_descs) + _unknown_cheats.size())); // cheat count + + for (uint j = 0; j < lengthof(_extra_cheat_descs); j++) { + CheatsExtSave save = { _extra_cheat_descs[j].name, *(_extra_cheat_descs[j].cht) }; + SlObject(&save, _cheats_ext_save_desc); + } + for (const auto &iter : _unknown_cheats) { + CheatsExtSave save = { iter.first.c_str(), iter.second }; + SlObject(&save, _cheats_ext_save_desc); + } + }, nullptr); +} + +/** + * Internal structure used in SaveSettingsPatx() and SaveSettingsPlyx() + */ +struct SettingsExtSave { + uint32 flags; + const char *name; + uint32 setting_length; +}; + +static const SaveLoad _settings_ext_save_desc[] = { + SLE_VAR(SettingsExtSave, flags, SLE_UINT32), + SLE_STR(SettingsExtSave, name, SLE_STR, 0), + SLE_VAR(SettingsExtSave, setting_length, SLE_UINT32), +}; + + +/** Chunk handlers related to cheats. */ +static const ChunkHandler cheat_chunk_handlers[] = { + { 'CHTS', Save_CHTS, Load_CHTS, nullptr, nullptr, CH_RIFF }, + { 'CHTX', Save_CHTX, Load_CHTX, nullptr, nullptr, CH_RIFF }, +}; + +extern const ChunkHandlerTable _cheat_chunk_handlers(cheat_chunk_handlers); diff --git a/src/sl/company_sl.cpp b/src/sl/company_sl.cpp new file mode 100644 index 0000000000..9f3118470c --- /dev/null +++ b/src/sl/company_sl.cpp @@ -0,0 +1,703 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file company_sl.cpp Code handling saving and loading of company data */ + +#include "../stdafx.h" +#include "../company_func.h" +#include "../company_manager_face.h" +#include "../fios.h" +#include "../tunnelbridge_map.h" +#include "../tunnelbridge.h" +#include "../station_base.h" +#include "../settings_func.h" +#include "../strings_func.h" +#include "../network/network.h" +#include "../network/network_func.h" +#include "../network/network_server.h" +#include "../3rdparty/randombytes/randombytes.h" +#include "../3rdparty/monocypher/monocypher.h" + +#include "saveload.h" +#include "saveload_buffer.h" + +#include "table/strings.h" + +#include "../safeguards.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 colour + * - 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, std::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; +} + +/** Rebuilding of company statistics after loading a savegame. */ +void AfterLoadCompanyStats() +{ + /* Reset infrastructure statistics to zero. */ + for (Company *c : Company::Iterate()) MemSetT(&c->infrastructure, 0); + + /* Collect airport count. */ + for (const Station *st : Station::Iterate()) { + if ((st->facilities & FACIL_AIRPORT) && Company::IsValidID(st->owner)) { + Company::Get(st->owner)->infrastructure.airport++; + } + } + + Company *c; + for (TileIndex tile = 0; tile < MapSize(); tile++) { + switch (GetTileType(tile)) { + case MP_RAILWAY: + c = Company::GetIfValid(GetTileOwner(tile)); + if (c != nullptr) { + uint pieces = 1; + if (IsPlainRail(tile)) { + TrackBits bits = GetTrackBits(tile); + if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) { + c->infrastructure.rail[GetSecondaryRailType(tile)]++; + } else { + pieces = CountBits(bits); + if (TracksOverlap(bits)) pieces *= pieces; + } + } + c->infrastructure.rail[GetRailType(tile)] += pieces; + + if (HasSignals(tile)) c->infrastructure.signal += CountBits(GetPresentSignals(tile)); + } + break; + + case MP_ROAD: { + if (IsLevelCrossing(tile)) { + c = Company::GetIfValid(GetTileOwner(tile)); + if (c != nullptr) c->infrastructure.rail[GetRailType(tile)] += LEVELCROSSING_TRACKBIT_FACTOR; + } + + /* Iterate all present road types as each can have a different owner. */ + for (RoadTramType rtt : _roadtramtypes) { + RoadType rt = GetRoadType(tile, rtt); + if (rt == INVALID_ROADTYPE) continue; + c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rtt)); + /* A level crossings and depots have two road bits. */ + if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rtt)) : 2; + } + break; + } + + case MP_STATION: + c = Company::GetIfValid(GetTileOwner(tile)); + if (c != nullptr && GetStationType(tile) != STATION_AIRPORT && !IsBuoy(tile)) c->infrastructure.station++; + + switch (GetStationType(tile)) { + case STATION_RAIL: + case STATION_WAYPOINT: + if (c != nullptr && !IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]++; + break; + + case STATION_BUS: + case STATION_TRUCK: + case STATION_ROADWAYPOINT: { + /* Iterate all present road types as each can have a different owner. */ + for (RoadTramType rtt : _roadtramtypes) { + RoadType rt = GetRoadType(tile, rtt); + if (rt == INVALID_ROADTYPE) continue; + c = Company::GetIfValid(GetRoadOwner(tile, rtt)); + if (c != nullptr) c->infrastructure.road[rt] += 2; // A road stop has two road bits. + } + break; + } + + case STATION_DOCK: + case STATION_BUOY: + if (GetWaterClass(tile) == WATER_CLASS_CANAL) { + if (c != nullptr) c->infrastructure.water++; + } + break; + + default: + break; + } + break; + + case MP_WATER: + if (IsShipDepot(tile) || IsLock(tile)) { + c = Company::GetIfValid(GetTileOwner(tile)); + if (c != nullptr) { + if (IsShipDepot(tile)) c->infrastructure.water += LOCK_DEPOT_TILE_FACTOR; + if (IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE) { + /* The middle tile specifies the owner of the lock. */ + c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // the middle tile specifies the owner of the + break; // do not count the middle tile as canal + } + } + } + FALLTHROUGH; + + case MP_OBJECT: + if (GetWaterClass(tile) == WATER_CLASS_CANAL) { + c = Company::GetIfValid(GetTileOwner(tile)); + if (c != nullptr) c->infrastructure.water++; + } + break; + + case MP_TUNNELBRIDGE: { + /* Only count the tunnel/bridge if we're on the western end tile. */ + if (GetTunnelBridgeDirection(tile) < DIAGDIR_SW) { + TileIndex other_end = GetOtherTunnelBridgeEnd(tile); + + /* Count each tunnel/bridge TUNNELBRIDGE_TRACKBIT_FACTOR times to simulate + * the higher structural maintenance needs, and don't forget the end tiles. */ + const uint middle_len = GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR; + + switch (GetTunnelBridgeTransportType(tile)) { + case TRANSPORT_RAIL: + AddRailTunnelBridgeInfrastructure(tile, other_end); + break; + + case TRANSPORT_ROAD: { + AddRoadTunnelBridgeInfrastructure(tile, other_end); + break; + } + + case TRANSPORT_WATER: + c = Company::GetIfValid(GetTileOwner(tile)); + if (c != nullptr) c->infrastructure.water += middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR); + break; + + default: + break; + } + } + break; + } + + default: + break; + } + } +} + + + +/* Save/load of companies */ +static const SaveLoad _company_desc[] = { + SLE_VAR(CompanyProperties, name_2, SLE_UINT32), + SLE_VAR(CompanyProperties, name_1, SLE_STRINGID), + SLE_CONDSSTR(CompanyProperties, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), + + SLE_VAR(CompanyProperties, president_name_1, SLE_STRINGID), + SLE_VAR(CompanyProperties, president_name_2, SLE_UINT32), + SLE_CONDSSTR(CompanyProperties, president_name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), + + SLE_VAR(CompanyProperties, face, SLE_UINT32), + + /* money was changed to a 64 bit field in savegame version 1. */ + SLE_CONDVAR(CompanyProperties, money, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_1), + SLE_CONDVAR(CompanyProperties, money, SLE_INT64, SLV_1, SL_MAX_VERSION), + + SLE_CONDVAR(CompanyProperties, current_loan, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(CompanyProperties, current_loan, SLE_INT64, SLV_65, SL_MAX_VERSION), + + SLE_VAR(CompanyProperties, colour, SLE_UINT8), + SLE_VAR(CompanyProperties, money_fraction, SLE_UINT8), + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_58), ///< avail_railtypes + SLE_VAR(CompanyProperties, block_preview, SLE_UINT8), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_94), ///< cargo_types + SLE_CONDNULL(4, SLV_94, SLV_170), ///< cargo_types + SLE_CONDVAR(CompanyProperties, location_of_HQ, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(CompanyProperties, location_of_HQ, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(CompanyProperties, last_build_coordinate, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, inaugurated_year, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(CompanyProperties, inaugurated_year, SLE_INT32, SLV_31, SL_MAX_VERSION), + + SLE_ARR(CompanyProperties, share_owners, SLE_UINT8, 4), + + SLE_VAR(CompanyProperties, num_valid_stat_ent, SLE_UINT8), + + SLE_VAR(CompanyProperties, months_of_bankruptcy, SLE_UINT8), + SLE_CONDVAR_X(CompanyProperties, bankrupt_last_asked, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BANKRUPTCY_EXTRA)), + SLE_CONDVAR_X(CompanyProperties, bankrupt_flags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BANKRUPTCY_EXTRA, 2)), + SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), + SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_UINT16, SLV_104, SL_MAX_VERSION), + SLE_VAR(CompanyProperties, bankrupt_timeout, SLE_INT16), + SLE_CONDVAR(CompanyProperties, bankrupt_value, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(CompanyProperties, bankrupt_value, SLE_INT64, SLV_65, SL_MAX_VERSION), + + /* yearly expenses was changed to 64-bit in savegame version 2. */ + SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, SL_MIN_VERSION, SLV_2), + SLE_CONDARR_X(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 13, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING, 0, 0)), + SLE_CONDARR_X(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 15, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)), + + SLE_CONDVAR(CompanyProperties, is_ai, SLE_BOOL, SLV_2, SL_MAX_VERSION), + SLE_CONDNULL(1, SLV_107, SLV_112), ///< is_noai + SLE_CONDNULL(1, SLV_4, SLV_100), + + SLE_CONDVAR(CompanyProperties, terraform_limit, SLE_UINT32, SLV_156, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, clear_limit, SLE_UINT32, SLV_156, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, tree_limit, SLE_UINT32, SLV_175, SL_MAX_VERSION), + SLE_CONDVAR_X(CompanyProperties, purchase_land_limit, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BUY_LAND_RATE_LIMIT)), + SLE_CONDVAR_X(CompanyProperties, build_object_limit, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BUILD_OBJECT_RATE_LIMIT)), +}; + +static const SaveLoad _company_settings_desc[] = { + /* Engine renewal settings */ + SLE_CONDNULL(512, SLV_16, SLV_19), + SLE_CONDREF(Company, engine_renew_list, REF_ENGINE_RENEWS, SLV_19, SL_MAX_VERSION), + SLE_CONDVAR(Company, settings.engine_renew, SLE_BOOL, SLV_16, SL_MAX_VERSION), + SLE_CONDVAR(Company, settings.engine_renew_months, SLE_INT16, SLV_16, SL_MAX_VERSION), + SLE_CONDVAR(Company, settings.engine_renew_money, SLE_UINT32, SLV_16, SL_MAX_VERSION), + SLE_CONDVAR(Company, settings.renew_keep_length, SLE_BOOL, SLV_2, SL_MAX_VERSION), + + /* Default vehicle settings */ + SLE_CONDVAR(Company, settings.vehicle.servint_ispercent, SLE_BOOL, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR(Company, settings.vehicle.servint_trains, SLE_UINT16, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR(Company, settings.vehicle.servint_roadveh, SLE_UINT16, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR(Company, settings.vehicle.servint_aircraft, SLE_UINT16, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR(Company, settings.vehicle.servint_ships, SLE_UINT16, SLV_120, SL_MAX_VERSION), + SLE_CONDVAR_X(Company, settings.vehicle.auto_timetable_by_default, SLE_BOOL, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 2, 2)), + + SLE_CONDNULL(63, SLV_2, SLV_144), // old reserved space +}; + +static const SaveLoad _company_settings_skip_desc[] = { + /* Engine renewal settings */ + SLE_CONDNULL(512, SLV_16, SLV_19), + SLE_CONDNULL(2, SLV_19, SLV_69), // engine_renew_list + SLE_CONDNULL(4, SLV_69, SL_MAX_VERSION), // engine_renew_list + SLE_CONDNULL(1, SLV_16, SL_MAX_VERSION), // settings.engine_renew + SLE_CONDNULL(2, SLV_16, SL_MAX_VERSION), // settings.engine_renew_months + SLE_CONDNULL(4, SLV_16, SL_MAX_VERSION), // settings.engine_renew_money + SLE_CONDNULL(1, SLV_2, SL_MAX_VERSION), // settings.renew_keep_length + + /* Default vehicle settings */ + SLE_CONDNULL(1, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_ispercent + SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_trains + SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_roadveh + SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_aircraft + SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_ships + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 2, 2)), // settings.vehicle.auto_timetable_by_default + + SLE_CONDNULL(63, SLV_2, SLV_144), // old reserved space +}; + +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, SL_MIN_VERSION, SLV_2), + SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, SLV_2, SL_MAX_VERSION), + SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), + SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, SLV_2, SL_MAX_VERSION), + SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2), + SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, SLV_2, SL_MAX_VERSION), + + SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, SL_MIN_VERSION, SLV_170), + SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, SLV_170, SLV_EXTEND_CARGOTYPES), + SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), + SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), +}; + +/* We do need to read this single value, as the bigger it gets, the more data is stored */ +struct CompanyOldAI { + uint8 num_build_rec; +}; + +static const SaveLoad _company_ai_desc[] = { + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_13), + SLE_CONDNULL(4, SLV_13, SLV_107), + SLE_CONDNULL(8, SL_MIN_VERSION, SLV_107), + SLE_CONDVAR(CompanyOldAI, num_build_rec, SLE_UINT8, SL_MIN_VERSION, SLV_107), + SLE_CONDNULL(3, SL_MIN_VERSION, SLV_107), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), + SLE_CONDNULL(4, SLV_6, SLV_107), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), + SLE_CONDNULL(4, SLV_6, SLV_107), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), + SLE_CONDNULL(4, SLV_6, SLV_107), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), + SLE_CONDNULL(4, SLV_6, SLV_107), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_69), + SLE_CONDNULL(4, SLV_69, SLV_107), + + SLE_CONDNULL(18, SL_MIN_VERSION, SLV_107), + SLE_CONDNULL(20, SL_MIN_VERSION, SLV_107), + SLE_CONDNULL(32, SL_MIN_VERSION, SLV_107), + + SLE_CONDNULL(64, SLV_2, SLV_107), +}; + +static const SaveLoad _company_ai_build_rec_desc[] = { + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), + SLE_CONDNULL(4, SLV_6, SLV_107), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), + SLE_CONDNULL(4, SLV_6, SLV_107), + SLE_CONDNULL(8, SL_MIN_VERSION, SLV_107), +}; + +static const SaveLoad _company_livery_desc[] = { + SLE_CONDVAR(Livery, in_use, SLE_UINT8, SLV_34, SL_MAX_VERSION), + SLE_CONDVAR(Livery, colour1, SLE_UINT8, SLV_34, SL_MAX_VERSION), + SLE_CONDVAR(Livery, colour2, SLE_UINT8, SLV_34, SL_MAX_VERSION), +}; + +static void SaveLoad_PLYR_common(Company *c, CompanyProperties *cprops) +{ + int i; + + SlObject(cprops, _company_desc); + if (c != nullptr) { + SlObject(c, _company_settings_desc); + } else { + char nothing; + SlObject(¬hing, _company_settings_skip_desc); + } + + /* Keep backwards compatible for savegames, so load the old AI block */ + if (IsSavegameVersionBefore(SLV_107) && cprops->is_ai) { + CompanyOldAI old_ai; + char nothing; + + SlObject(&old_ai, _company_ai_desc); + for (i = 0; i != old_ai.num_build_rec; i++) { + SlObject(¬hing, _company_ai_build_rec_desc); + } + } + + /* Write economy */ + SlObject(&cprops->cur_economy, _company_economy_desc); + + /* Write old economy entries. */ + if (cprops->num_valid_stat_ent > lengthof(cprops->old_economy)) SlErrorCorrupt("Too many old economy entries"); + for (i = 0; i < cprops->num_valid_stat_ent; i++) { + SlObject(&cprops->old_economy[i], _company_economy_desc); + } + + /* Write each livery entry. */ + int num_liveries = IsSavegameVersionBefore(SLV_63) ? LS_END - 4 : (IsSavegameVersionBefore(SLV_85) ? LS_END - 2: LS_END); + bool update_in_use = IsSavegameVersionBefore(SLV_GROUP_LIVERIES); + if (c != nullptr) { + for (i = 0; i < num_liveries; i++) { + SlObject(&c->livery[i], _company_livery_desc); + if (update_in_use && i != LS_DEFAULT) { + if (c->livery[i].in_use == 0) { + c->livery[i].colour1 = c->livery[LS_DEFAULT].colour1; + c->livery[i].colour2 = c->livery[LS_DEFAULT].colour2; + } else { + c->livery[i].in_use = 3; + } + } + } + + 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]; + } + } else { + /* Skip liveries */ + Livery dummy_livery; + for (i = 0; i < num_liveries; i++) { + SlObject(&dummy_livery, _company_livery_desc); + } + } +} + +static void SaveLoad_PLYR(Company *c) +{ + SaveLoad_PLYR_common(c, c); +} + +static void Save_PLYR() +{ + for (Company *c : Company::Iterate()) { + SlSetArrayIndex(c->index); + SlAutolength((AutolengthProc*)SaveLoad_PLYR, c); + } +} + +static void Load_PLYR() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Company *c = new (index) Company(); + SetDefaultCompanySettings(c->index); + SaveLoad_PLYR(c); + _company_colours[index] = (Colours)c->colour; + + // settings moved from game settings to company settings + if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 2)) { + c->settings.auto_timetable_separation_rate = _settings_game.order.old_timetable_separation_rate; + } + if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 3)) { + c->settings.vehicle.auto_separation_by_default = _settings_game.order.old_timetable_separation; + } + } +} + +static void Check_PLYR() +{ + int index; + while ((index = SlIterateArray()) != -1) { + CompanyProperties *cprops = new CompanyProperties(); + SaveLoad_PLYR_common(nullptr, cprops); + + /* We do not load old custom names */ + if (IsSavegameVersionBefore(SLV_84)) { + if (GetStringTab(cprops->name_1) == TEXT_TAB_OLD_CUSTOM) { + cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; + } + + if (GetStringTab(cprops->president_name_1) == TEXT_TAB_OLD_CUSTOM) { + cprops->president_name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; + } + } + + if (cprops->name.empty() && !IsInsideMM(cprops->name_1, SPECSTR_COMPANY_NAME_START, SPECSTR_COMPANY_NAME_LAST + 1) && + cprops->name_1 != STR_GAME_SAVELOAD_NOT_AVAILABLE && cprops->name_1 != STR_SV_UNNAMED && + cprops->name_1 != SPECSTR_ANDCO_NAME && cprops->name_1 != SPECSTR_PRESIDENT_NAME && + cprops->name_1 != SPECSTR_SILLY_NAME) { + cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE; + } + + if (!_load_check_data.companies.Insert(index, cprops)) delete cprops; + } +} + +static void Ptrs_PLYR() +{ + for (Company *c : Company::Iterate()) { + SlObject(c, _company_settings_desc); + } +} + +extern void LoadSettingsPlyx(bool skip); +extern void SaveSettingsPlyx(); + +static void Load_PLYX() +{ + LoadSettingsPlyx(false); +} + +static void Check_PLYX() +{ + LoadSettingsPlyx(true); +} + +static void Save_PLYX() +{ + SaveSettingsPlyx(); +} + +static void Load_PLYP() +{ + size_t size = SlGetFieldLength(); + CompanyMask invalid_mask = 0; + if (SlXvIsFeaturePresent(XSLFI_COMPANY_PW, 2)) { + if (size <= 2) return; + invalid_mask = SlReadUint16(); + size -= 2; + } + if (size <= 16 + 24 + 16 || (_networking && !_network_server)) { + SlSkipBytes(size); + return; + } + if (!_network_server) { + extern CompanyMask _saved_PLYP_invalid_mask; + extern std::vector _saved_PLYP_data; + + _saved_PLYP_invalid_mask = invalid_mask; + _saved_PLYP_data.resize(size); + ReadBuffer::GetCurrent()->CopyBytes(_saved_PLYP_data.data(), _saved_PLYP_data.size()); + return; + } + + uint8 token[16]; + ReadBuffer::GetCurrent()->CopyBytes(token, 16); + if (memcmp(token, _network_company_password_storage_token, 16) != 0) { + DEBUG(sl, 2, "Skipping encrypted company passwords"); + SlSkipBytes(size - 16); + return; + } + + uint8 nonce[24]; + uint8 mac[16]; + ReadBuffer::GetCurrent()->CopyBytes(nonce, 24); + ReadBuffer::GetCurrent()->CopyBytes(mac, 16); + + std::vector buffer(size - 16 - 24 - 16); + ReadBuffer::GetCurrent()->CopyBytes(buffer.data(), buffer.size()); + + if (crypto_unlock(buffer.data(), _network_company_password_storage_key, nonce, mac, buffer.data(), buffer.size()) == 0) { + SlLoadFromBuffer(buffer.data(), buffer.size(), [invalid_mask]() { + _network_company_server_id.resize(SlReadUint32()); + ReadBuffer::GetCurrent()->CopyBytes((uint8 *)_network_company_server_id.data(), _network_company_server_id.size()); + + while (true) { + uint16 cid = SlReadUint16(); + if (cid >= MAX_COMPANIES) break; + std::string password; + password.resize(SlReadUint32()); + ReadBuffer::GetCurrent()->CopyBytes((uint8 *)password.data(), password.size()); + if (!HasBit(invalid_mask, cid)) { + NetworkServerSetCompanyPassword((CompanyID)cid, password, true); + } + } + + ReadBuffer::GetCurrent()->SkipBytes(SlReadByte()); // Skip padding + }); + DEBUG(sl, 2, "Decrypted company passwords"); + } else { + DEBUG(sl, 2, "Failed to decrypt company passwords"); + } +} + +static void Save_PLYP() +{ + if ((_networking && !_network_server) || IsNetworkServerSave()) { + SlSetLength(0); + return; + } + if (!_network_server) { + extern CompanyMask _saved_PLYP_invalid_mask; + extern std::vector _saved_PLYP_data; + + if (_saved_PLYP_data.empty()) { + SlSetLength(0); + } else { + SlSetLength(2 + _saved_PLYP_data.size()); + SlWriteUint16(_saved_PLYP_invalid_mask); + MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)_saved_PLYP_data.data(), _saved_PLYP_data.size()); + } + return; + } + + uint8 nonce[24]; /* Use only once per key: random */ + if (randombytes(nonce, 24) < 0) { + /* Can't get a random nonce, just give up */ + SlSetLength(0); + return; + } + + std::vector buffer = SlSaveToVector([](void *) { + SlWriteUint32((uint32)_network_company_server_id.size()); + MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)_network_company_server_id.data(), _network_company_server_id.size()); + + for (const Company *c : Company::Iterate()) { + SlWriteUint16(c->index); + + const std::string &password = _network_company_states[c->index].password; + SlWriteUint32((uint32)password.size()); + MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)password.data(), password.size()); + } + + SlWriteUint16(0xFFFF); + + /* Add some random length padding to not make it too obvious from the length whether passwords are set or not */ + uint8 padding[256]; + if (randombytes(padding, 256) >= 0) { + SlWriteByte(padding[0]); + MemoryDumper::GetCurrent()->CopyBytes(padding + 1, padding[0]); + } else { + SlWriteByte(0); + } + }, nullptr); + + + uint8 mac[16]; /* Message authentication code */ + + /* Encrypt in place */ + crypto_lock(mac, buffer.data(), _network_company_password_storage_key, nonce, buffer.data(), buffer.size()); + + SlSetLength(2 + 16 + 24 + 16 + buffer.size()); + SlWriteUint16(0); // Invalid mask + MemoryDumper::GetCurrent()->CopyBytes(_network_company_password_storage_token, 16); + MemoryDumper::GetCurrent()->CopyBytes(nonce, 24); + MemoryDumper::GetCurrent()->CopyBytes(mac, 16); + MemoryDumper::GetCurrent()->CopyBytes(buffer.data(), buffer.size()); +} + +static const ChunkHandler company_chunk_handlers[] = { + { 'PLYR', Save_PLYR, Load_PLYR, Ptrs_PLYR, Check_PLYR, CH_ARRAY }, + { 'PLYX', Save_PLYX, Load_PLYX, nullptr, Check_PLYX, CH_RIFF }, + { 'PLYP', Save_PLYP, Load_PLYP, nullptr, nullptr, CH_RIFF }, +}; + +extern const ChunkHandlerTable _company_chunk_handlers(company_chunk_handlers); diff --git a/src/saveload/debug_sl.cpp b/src/sl/debug_sl.cpp similarity index 100% rename from src/saveload/debug_sl.cpp rename to src/sl/debug_sl.cpp diff --git a/src/saveload/upstream/depot_sl.cpp b/src/sl/depot_sl.cpp similarity index 52% rename from src/saveload/upstream/depot_sl.cpp rename to src/sl/depot_sl.cpp index b8ea8d7490..73cf0cd4cf 100644 --- a/src/saveload/upstream/depot_sl.cpp +++ b/src/sl/depot_sl.cpp @@ -7,72 +7,58 @@ /** @file depot_sl.cpp Code handling saving and loading of depots */ -#include "../../stdafx.h" +#include "../stdafx.h" +#include "../depot_base.h" +#include "../town.h" #include "saveload.h" -#include "compat/depot_sl_compat.h" -#include "../../depot_base.h" -#include "../../town.h" - -#include "../../safeguards.h" - -namespace upstream_sl { +#include "../safeguards.h" static TownID _town_index; static const SaveLoad _depot_desc[] = { SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), SLE_CONDVAR(Depot, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLEG_CONDVAR("town_index", _town_index, SLE_UINT16, SL_MIN_VERSION, SLV_141), + SLEG_CONDVAR(_town_index, SLE_UINT16, SL_MIN_VERSION, SLV_141), SLE_CONDREF(Depot, town, REF_TOWN, SLV_141, SL_MAX_VERSION), SLE_CONDVAR(Depot, town_cn, SLE_UINT16, SLV_141, SL_MAX_VERSION), SLE_CONDSTR(Depot, name, SLE_STR, 0, SLV_141, SL_MAX_VERSION), SLE_CONDVAR(Depot, build_date, SLE_INT32, SLV_142, SL_MAX_VERSION), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 5)), }; -struct DEPTChunkHandler : ChunkHandler { - DEPTChunkHandler() : ChunkHandler('DEPT', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_depot_desc); - - for (Depot *depot : Depot::Iterate()) { - SlSetArrayIndex(depot->index); - SlObject(depot, _depot_desc); - } +static void Save_DEPT() +{ + for (Depot *depot : Depot::Iterate()) { + SlSetArrayIndex(depot->index); + SlObject(depot, _depot_desc); } +} - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_depot_desc, _depot_sl_compat); +static void Load_DEPT() +{ + int index; - int index; + while ((index = SlIterateArray()) != -1) { + Depot *depot = new (index) Depot(); + SlObject(depot, _depot_desc); - while ((index = SlIterateArray()) != -1) { - Depot *depot = new (index) Depot(); - SlObject(depot, slt); - - /* Set the town 'pointer' so we can restore it later. */ - if (IsSavegameVersionBefore(SLV_141)) depot->town = (Town *)(size_t)_town_index; - } + /* Set the town 'pointer' so we can restore it later. */ + if (IsSavegameVersionBefore(SLV_141)) depot->town = (Town *)(size_t)_town_index; } +} - void FixPointers() const override - { - for (Depot *depot : Depot::Iterate()) { - SlObject(depot, _depot_desc); - if (IsSavegameVersionBefore(SLV_141)) depot->town = Town::Get((size_t)depot->town); - } +static void Ptrs_DEPT() +{ + for (Depot *depot : Depot::Iterate()) { + SlObject(depot, _depot_desc); + if (IsSavegameVersionBefore(SLV_141)) depot->town = Town::Get((size_t)depot->town); } -}; +} -static const DEPTChunkHandler DEPT; -static const ChunkHandlerRef depot_chunk_handlers[] = { - DEPT, +static const ChunkHandler depot_chunk_handlers[] = { + { 'DEPT', Save_DEPT, Load_DEPT, Ptrs_DEPT, nullptr, CH_ARRAY }, }; extern const ChunkHandlerTable _depot_chunk_handlers(depot_chunk_handlers); - -} diff --git a/src/sl/economy_sl.cpp b/src/sl/economy_sl.cpp new file mode 100644 index 0000000000..6cc2a8629b --- /dev/null +++ b/src/sl/economy_sl.cpp @@ -0,0 +1,105 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file economy_sl.cpp Code handling saving and loading of economy data */ + +#include "../stdafx.h" +#include "../economy_func.h" +#include "../economy_base.h" + +#include "saveload.h" + +#include "../safeguards.h" + +/** Prices in pre 126 savegames */ +static void Load_PRIC() +{ + /* Old games store 49 base prices, very old games store them as int32 */ + int vt = IsSavegameVersionBefore(SLV_65) ? SLE_FILE_I32 : SLE_FILE_I64; + SlArray(nullptr, 49, vt | SLE_VAR_NULL); + SlArray(nullptr, 49, SLE_FILE_U16 | SLE_VAR_NULL); +} + +/** Cargo payment rates in pre 126 savegames */ +static void Load_CAPR() +{ + uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + int vt = IsSavegameVersionBefore(SLV_65) ? SLE_FILE_I32 : SLE_FILE_I64; + SlArray(nullptr, num_cargo, vt | SLE_VAR_NULL); + SlArray(nullptr, num_cargo, SLE_FILE_U16 | SLE_VAR_NULL); +} + +static const SaveLoad _economy_desc[] = { + SLE_CONDNULL(4, SL_MIN_VERSION, SLV_65), // max_loan + SLE_CONDNULL(8, SLV_65, SLV_144), // max_loan + SLE_CONDVAR(Economy, old_max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(Economy, old_max_loan_unround, SLE_INT64, SLV_65, SLV_126), + SLE_CONDVAR(Economy, old_max_loan_unround_fract, SLE_UINT16, SLV_70, SLV_126), + SLE_CONDVAR(Economy, inflation_prices, SLE_UINT64, SLV_126, SL_MAX_VERSION), + SLE_CONDVAR(Economy, inflation_payment, SLE_UINT64, SLV_126, 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, SLV_102, SL_MAX_VERSION), + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), +}; + +/** Economy variables */ +static void Save_ECMY() +{ + SlObject(&_economy, _economy_desc); +} + +/** Economy variables */ +static void Load_ECMY() +{ + SlObject(&_economy, _economy_desc); + StartupIndustryDailyChanges(IsSavegameVersionBefore(SLV_102)); // old savegames will need to be initialized +} + +static const SaveLoad _cargopayment_desc[] = { + SLE_REF(CargoPayment, front, REF_VEHICLE), + SLE_VAR(CargoPayment, route_profit, SLE_INT64), + SLE_VAR(CargoPayment, visual_profit, SLE_INT64), + SLE_CONDVAR_X(CargoPayment, visual_transfer, SLE_INT64, SLV_181, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP)), +}; + +static void Save_CAPY() +{ + for (CargoPayment *cp : CargoPayment::Iterate()) { + SlSetArrayIndex(cp->index); + SlObject(cp, _cargopayment_desc); + } +} + +static void Load_CAPY() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + CargoPayment *cp = new (index) CargoPayment(); + SlObject(cp, _cargopayment_desc); + } +} + +static void Ptrs_CAPY() +{ + for (CargoPayment *cp : CargoPayment::Iterate()) { + SlObject(cp, _cargopayment_desc); + } +} + + +static const ChunkHandler economy_chunk_handlers[] = { + { 'CAPY', Save_CAPY, Load_CAPY, Ptrs_CAPY, nullptr, CH_ARRAY }, + { 'PRIC', nullptr, Load_PRIC, nullptr, nullptr, CH_RIFF }, + { 'CAPR', nullptr, Load_CAPR, nullptr, nullptr, CH_RIFF }, + { 'ECMY', Save_ECMY, Load_ECMY, nullptr, nullptr, CH_RIFF }, +}; + +extern const ChunkHandlerTable _economy_chunk_handlers(economy_chunk_handlers); diff --git a/src/sl/engine_sl.cpp b/src/sl/engine_sl.cpp new file mode 100644 index 0000000000..9f1eebefe7 --- /dev/null +++ b/src/sl/engine_sl.cpp @@ -0,0 +1,208 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file engine_sl.cpp Code handling saving and loading of engines */ + +#include "../stdafx.h" +#include "saveload_internal.h" +#include "../engine_base.h" +#include "../engine_func.h" +#include "../string_func.h" +#include + +#include "../safeguards.h" + +static const SaveLoad _engine_desc[] = { + SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Engine, intro_date, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Engine, age, SLE_INT32, SLV_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_CONDNULL(1, SL_MIN_VERSION, SLV_121), + SLE_VAR(Engine, flags, SLE_UINT8), + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_179), // old preview_company_rank + SLE_CONDVAR(Engine, preview_asked, SLE_UINT16, SLV_179, SL_MAX_VERSION), + SLE_CONDVAR(Engine, preview_company, SLE_UINT8, SLV_179, SL_MAX_VERSION), + SLE_VAR(Engine, preview_wait, SLE_UINT8), + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_45), + SLE_CONDVAR(Engine, company_avail, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), + SLE_CONDVAR(Engine, company_avail, SLE_UINT16, SLV_104, SL_MAX_VERSION), + SLE_CONDVAR(Engine, company_hidden, SLE_UINT16, SLV_193, SL_MAX_VERSION), + SLE_CONDSTR(Engine, name, SLE_STR, 0, SLV_84, SL_MAX_VERSION), + + SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space +}; + +static std::vector _temp_engine; + +/** + * Allocate an Engine structure, but not using the pools. + * The allocated Engine must be freed using FreeEngine; + * @return Allocated engine. + */ +static Engine* CallocEngine() +{ + uint8 *zero = CallocT(sizeof(Engine)); + Engine *engine = new (zero) Engine(); + return engine; +} + +/** + * Deallocate an Engine constructed by CallocEngine. + * @param e Engine to free. + */ +static void FreeEngine(Engine *e) +{ + if (e != nullptr) { + e->~Engine(); + free(e); + } +} + +Engine *GetTempDataEngine(EngineID index) +{ + if (index < _temp_engine.size()) { + return _temp_engine[index]; + } else if (index == _temp_engine.size()) { + _temp_engine.push_back(CallocEngine()); + return _temp_engine[index]; + } else { + NOT_REACHED(); + } +} + +static void Save_ENGN() +{ + for (Engine *e : Engine::Iterate()) { + 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); + + if (IsSavegameVersionBefore(SLV_179)) { + /* preview_company_rank was replaced with preview_company and preview_asked. + * Just cancel any previews. */ + e->flags &= ~4; // ENGINE_OFFER_WINDOW_OPEN + e->preview_company = INVALID_COMPANY; + e->preview_asked = MAX_UVALUE(CompanyMask); + } + } +} + +/** + * Copy data from temporary engine array into the real engine pool. + */ +void CopyTempEngineData() +{ + for (Engine *e : Engine::Iterate()) { + 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->flags = se->flags; + e->preview_asked = se->preview_asked; + e->preview_company = se->preview_company; + e->preview_wait = se->preview_wait; + e->company_avail = se->company_avail; + e->company_hidden = se->company_hidden; + e->name = se->name; + } + + ResetTempEngineData(); +} + +void ResetTempEngineData() +{ + /* Get rid of temporary data */ + for (std::vector::iterator it = _temp_engine.begin(); it != _temp_engine.end(); ++it) { + FreeEngine(*it); + } + _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]); + } +} + +/** Save and load the mapping between the engine id in the pool, and the grf file it came from. */ +static const SaveLoad _engine_id_mapping_desc[] = { + SLE_VAR(EngineIDMapping, grfid, SLE_UINT32), + SLE_VAR(EngineIDMapping, internal_id, SLE_UINT16), + SLE_VAR(EngineIDMapping, type, SLE_UINT8), + SLE_VAR(EngineIDMapping, substitute_id, SLE_UINT8), +}; + +static void Save_EIDS() +{ + uint index = 0; + for (EngineIDMapping &eid : _engine_mngr) { + SlSetArrayIndex(index); + SlObject(&eid, _engine_id_mapping_desc); + index++; + } +} + +static void Load_EIDS() +{ + _engine_mngr.clear(); + + while (SlIterateArray() != -1) { + EngineIDMapping *eid = &_engine_mngr.emplace_back(); + SlObject(eid, _engine_id_mapping_desc); + } +} + +void AfterLoadEngines() +{ + AnalyseEngineCallbacks(); +} + +static const ChunkHandler engine_chunk_handlers[] = { + { 'EIDS', Save_EIDS, Load_EIDS, nullptr, nullptr, CH_ARRAY }, + { 'ENGN', Save_ENGN, Load_ENGN, nullptr, nullptr, CH_ARRAY }, + { 'ENGS', nullptr, Load_ENGS, nullptr, nullptr, CH_RIFF }, +}; + +extern const ChunkHandlerTable _engine_chunk_handlers(engine_chunk_handlers); diff --git a/src/saveload/extended_ver_sl.cpp b/src/sl/extended_ver_sl.cpp similarity index 100% rename from src/saveload/extended_ver_sl.cpp rename to src/sl/extended_ver_sl.cpp diff --git a/src/saveload/extended_ver_sl.h b/src/sl/extended_ver_sl.h similarity index 99% rename from src/saveload/extended_ver_sl.h rename to src/sl/extended_ver_sl.h index 843d50e4d7..90840d5782 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/sl/extended_ver_sl.h @@ -7,8 +7,8 @@ /** @file extended_ver_sl.h Functions/types related to handling save/load extended version info. */ -#ifndef EXTENDED_VER_SL_H -#define EXTENDED_VER_SL_H +#ifndef SL_EXTENDED_VER_SL_H +#define SL_EXTENDED_VER_SL_H #include "../core/bitmath_func.hpp" #include "../core/enum_type.hpp" @@ -274,4 +274,4 @@ bool SlXvCheckSpecialSavegameVersions(); bool SlXvIsChunkDiscardable(uint32 id); -#endif /* EXTENDED_VER_SL_H */ +#endif /* SL_EXTENDED_VER_SL_H */ diff --git a/src/sl/game_sl.cpp b/src/sl/game_sl.cpp new file mode 100644 index 0000000000..7da7da71ed --- /dev/null +++ b/src/sl/game_sl.cpp @@ -0,0 +1,180 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file game_sl.cpp Handles the saveload part of the GameScripts */ + +#include "../stdafx.h" +#include "../debug.h" +#include "saveload.h" +#include "../string_func.h" + +#include "../game/game.hpp" +#include "../game/game_config.hpp" +#include "../network/network.h" +#include "../game/game_instance.hpp" +#include "../game/game_text.hpp" + +#include "../safeguards.h" + +static std::string _game_saveload_name; +static int _game_saveload_version; +static std::string _game_saveload_settings; +static bool _game_saveload_is_random; + +static const SaveLoad _game_script[] = { + SLEG_SSTR(_game_saveload_name, SLE_STR), + SLEG_SSTR(_game_saveload_settings, SLE_STR), + SLEG_VAR(_game_saveload_version, SLE_UINT32), + SLEG_VAR(_game_saveload_is_random, SLE_BOOL), +}; + +static void SaveReal_GSDT(int *index_ptr) +{ + GameConfig *config = GameConfig::GetConfig(); + + if (config->HasScript()) { + _game_saveload_name = config->GetName(); + _game_saveload_version = config->GetVersion(); + } else { + /* No GameScript is configured for this so store an empty string as name. */ + _game_saveload_name.clear(); + _game_saveload_version = -1; + } + + _game_saveload_is_random = config->IsRandom(); + _game_saveload_settings = config->SettingsToString(); + + SlObject(nullptr, _game_script); + Game::Save(); +} + +static void Load_GSDT() +{ + /* Free all current data */ + GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME)->Change(nullptr); + + if ((CompanyID)SlIterateArray() == (CompanyID)-1) return; + + _game_saveload_version = -1; + SlObject(nullptr, _game_script); + + if (_game_mode == GM_MENU || (_networking && !_network_server)) { + GameInstance::LoadEmpty(); + if ((CompanyID)SlIterateArray() != (CompanyID)-1) SlErrorCorrupt("Too many GameScript configs"); + return; + } + + GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME); + if (!_game_saveload_name.empty()) { + config->Change(_game_saveload_name.c_str(), _game_saveload_version, false, _game_saveload_is_random); + if (!config->HasScript()) { + /* No version of the GameScript available that can load the data. Try to load the + * latest version of the GameScript instead. */ + config->Change(_game_saveload_name.c_str(), -1, false, _game_saveload_is_random); + if (!config->HasScript()) { + if (_game_saveload_name.compare("%_dummy") != 0) { + DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %d which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version); + DEBUG(script, 0, "This game will continue to run without GameScript."); + } else { + DEBUG(script, 0, "The savegame had no GameScript available at the time of saving."); + DEBUG(script, 0, "This game will continue to run without GameScript."); + } + } else { + DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %d which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version); + DEBUG(script, 0, "The latest version of that GameScript has been loaded instead, but it'll not get the savegame data as it's incompatible."); + } + /* Make sure the GameScript doesn't get the saveload data, as it was not the + * writer of the saveload data in the first place */ + _game_saveload_version = -1; + } + } + + config->StringToSettings(_game_saveload_settings); + + /* Load the GameScript saved data */ + config->SetToLoadData(GameInstance::Load(_game_saveload_version)); + + if ((CompanyID)SlIterateArray() != (CompanyID)-1) SlErrorCorrupt("Too many GameScript configs"); +} + +static void Save_GSDT() +{ + SlSetArrayIndex(0); + SlAutolength((AutolengthProc *)SaveReal_GSDT, nullptr); +} + +extern GameStrings *_current_data; + +static std::string _game_saveload_string; +static uint _game_saveload_strings; + +static const SaveLoad _game_language_header[] = { + SLEG_SSTR(_game_saveload_string, SLE_STR), + SLEG_VAR(_game_saveload_strings, SLE_UINT32), +}; + +static const SaveLoad _game_language_string[] = { + SLEG_SSTR(_game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL), +}; + +static void SaveReal_GSTR(const LanguageStrings *ls) +{ + _game_saveload_string = ls->language.c_str(); + _game_saveload_strings = (uint)ls->lines.size(); + + SlObject(nullptr, _game_language_header); + for (const auto &i : ls->lines) { + _game_saveload_string = i.c_str(); + SlObject(nullptr, _game_language_string); + } +} + +static void Load_GSTR() +{ + delete _current_data; + _current_data = new GameStrings(); + + while (SlIterateArray() != -1) { + _game_saveload_string.clear(); + SlObject(nullptr, _game_language_header); + + LanguageStrings ls(_game_saveload_string); + for (uint i = 0; i < _game_saveload_strings; i++) { + SlObject(nullptr, _game_language_string); + ls.lines.emplace_back(_game_saveload_string); + } + + _current_data->raw_strings.push_back(std::move(ls)); + } + + /* If there were no strings in the savegame, set GameStrings to nullptr */ + if (_current_data->raw_strings.size() == 0) { + delete _current_data; + _current_data = nullptr; + return; + } + + _current_data->Compile(); + ReconsiderGameScriptLanguage(); +} + +static void Save_GSTR() +{ + if (_current_data == nullptr) return; + + for (uint i = 0; i < _current_data->raw_strings.size(); i++) { + SlSetArrayIndex(i); + SlAutolength((AutolengthProc *)SaveReal_GSTR, &_current_data->raw_strings[i]); + } +} + +static const ChunkHandler game_chunk_handlers[] = { + { 'GSTR', Save_GSTR, Load_GSTR, nullptr, nullptr, CH_ARRAY }, + { 'GSDT', Save_GSDT, Load_GSDT, nullptr, nullptr, CH_ARRAY }, +}; + +extern const ChunkHandlerTable _game_chunk_handlers(game_chunk_handlers); diff --git a/src/sl/gamelog_sl.cpp b/src/sl/gamelog_sl.cpp new file mode 100644 index 0000000000..8270190c53 --- /dev/null +++ b/src/sl/gamelog_sl.cpp @@ -0,0 +1,183 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file gamelog_sl.cpp Code handling saving and loading of gamelog data */ + +#include "../stdafx.h" +#include "../gamelog_internal.h" +#include "../fios.h" +#include "../string_func.h" + +#include "saveload.h" + +#include "../safeguards.h" + +static const SaveLoad _glog_action_desc[] = { + SLE_CONDVAR_X(LoggedAction, tick, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)), + SLE_CONDVAR_X(LoggedAction, tick, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)), +}; + +static const SaveLoad _glog_mode_desc[] = { + SLE_VAR(LoggedChange, mode.mode, SLE_UINT8), + SLE_VAR(LoggedChange, mode.landscape, SLE_UINT8), +}; + +static char old_revision_text[GAMELOG_REVISION_LENGTH]; + +static const SaveLoad _glog_revision_desc[] = { + SLEG_CONDARR_X(old_revision_text, SLE_UINT8, GAMELOG_REVISION_LENGTH, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTENDED_GAMELOG, 0, 0)), + SLE_CONDSTR_X(LoggedChange, revision.text, SLE_STR, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTENDED_GAMELOG)), + SLE_VAR(LoggedChange, revision.newgrf, SLE_UINT32), + SLE_VAR(LoggedChange, revision.slver, SLE_UINT16), + SLE_VAR(LoggedChange, revision.modified, SLE_UINT8), +}; + +static const SaveLoad _glog_oldver_desc[] = { + SLE_VAR(LoggedChange, oldver.type, SLE_UINT32), + SLE_VAR(LoggedChange, oldver.version, SLE_UINT32), +}; + +static const SaveLoad _glog_setting_desc[] = { + SLE_STR(LoggedChange, setting.name, SLE_STR, 128), + SLE_VAR(LoggedChange, setting.oldval, SLE_INT32), + SLE_VAR(LoggedChange, setting.newval, SLE_INT32), +}; + +static const SaveLoad _glog_grfadd_desc[] = { + SLE_VAR(LoggedChange, grfadd.grfid, SLE_UINT32 ), + SLE_ARR(LoggedChange, grfadd.md5sum, SLE_UINT8, 16), +}; + +static const SaveLoad _glog_grfrem_desc[] = { + SLE_VAR(LoggedChange, grfrem.grfid, SLE_UINT32), +}; + +static const SaveLoad _glog_grfcompat_desc[] = { + SLE_VAR(LoggedChange, grfcompat.grfid, SLE_UINT32 ), + SLE_ARR(LoggedChange, grfcompat.md5sum, SLE_UINT8, 16), +}; + +static const SaveLoad _glog_grfparam_desc[] = { + SLE_VAR(LoggedChange, grfparam.grfid, SLE_UINT32), +}; + +static const SaveLoad _glog_grfmove_desc[] = { + SLE_VAR(LoggedChange, grfmove.grfid, SLE_UINT32), + SLE_VAR(LoggedChange, grfmove.offset, SLE_INT32), +}; + +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), +}; + +static const SaveLoad _glog_emergency_desc[] = { + SLE_CONDNULL(0, SL_MIN_VERSION, SL_MIN_VERSION), // Just an empty list, to keep the rest of the code easier. +}; + +static const SaveLoadTable _glog_desc[] = { + _glog_mode_desc, + _glog_revision_desc, + _glog_oldver_desc, + _glog_setting_desc, + _glog_grfadd_desc, + _glog_grfrem_desc, + _glog_grfcompat_desc, + _glog_grfparam_desc, + _glog_grfmove_desc, + _glog_grfbug_desc, + _glog_emergency_desc, +}; + +static_assert(lengthof(_glog_desc) == GLCT_END); + +static void Load_GLOG_common(LoggedAction *&gamelog_action, uint &gamelog_actions) +{ + assert(gamelog_action == nullptr); + assert(gamelog_actions == 0); + + byte type; + while ((type = SlReadByte()) != GLAT_NONE) { + if (type >= GLAT_END) SlErrorCorrupt("Invalid gamelog action type"); + GamelogActionType at = (GamelogActionType)type; + + 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 = nullptr; + la->changes = 0; + + while ((type = SlReadByte()) != GLCT_NONE) { + if (type >= GLCT_END) SlErrorCorrupt("Invalid gamelog change type"); + GamelogChangeType ct = (GamelogChangeType)type; + + 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 nullptr */ + memset(lc, 0, sizeof(*lc)); + lc->ct = ct; + + SlObject(lc, _glog_desc[ct]); + if (ct == GLCT_REVISION && SlXvIsFeatureMissing(XSLFI_EXTENDED_GAMELOG)) { + lc->revision.text = stredup(old_revision_text, lastof(old_revision_text)); + } + } + } +} + +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 += 10; + } + 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); +} + +static void Load_GLOG() +{ + Load_GLOG_common(_gamelog_action, _gamelog_actions); +} + +static void Check_GLOG() +{ + Load_GLOG_common(_load_check_data.gamelog_action, _load_check_data.gamelog_actions); +} + +static const ChunkHandler gamelog_chunk_handlers[] = { + { 'GLOG', Save_GLOG, Load_GLOG, nullptr, Check_GLOG, CH_RIFF } +}; + +extern const ChunkHandlerTable _gamelog_chunk_handlers(gamelog_chunk_handlers); diff --git a/src/saveload/upstream/goal_sl.cpp b/src/sl/goal_sl.cpp similarity index 61% rename from src/saveload/upstream/goal_sl.cpp rename to src/sl/goal_sl.cpp index 293436ef83..c062107e46 100644 --- a/src/saveload/upstream/goal_sl.cpp +++ b/src/sl/goal_sl.cpp @@ -7,16 +7,12 @@ /** @file goal_sl.cpp Code handling saving and loading of goals */ -#include "../../stdafx.h" +#include "../stdafx.h" +#include "../goal_base.h" #include "saveload.h" -#include "compat/goal_sl_compat.h" -#include "../../goal_base.h" - -#include "../../safeguards.h" - -namespace upstream_sl { +#include "../safeguards.h" static const SaveLoad _goals_desc[] = { SLE_VAR(Goal, company, SLE_FILE_U16 | SLE_VAR_U8), @@ -27,36 +23,25 @@ static const SaveLoad _goals_desc[] = { SLE_CONDVAR(Goal, completed, SLE_BOOL, SLV_182, SL_MAX_VERSION), }; -struct GOALChunkHandler : ChunkHandler { - GOALChunkHandler() : ChunkHandler('GOAL', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_goals_desc); - - for (Goal *s : Goal::Iterate()) { - SlSetArrayIndex(s->index); - SlObject(s, _goals_desc); - } +static void Save_GOAL() +{ + for (Goal *s : Goal::Iterate()) { + SlSetArrayIndex(s->index); + SlObject(s, _goals_desc); } +} - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_goals_desc, _goals_sl_compat); - - int index; - while ((index = SlIterateArray()) != -1) { - Goal *s = new (index) Goal(); - SlObject(s, slt); - } +static void Load_GOAL() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Goal *s = new (index) Goal(); + SlObject(s, _goals_desc); } -}; +} -static const GOALChunkHandler GOAL; -static const ChunkHandlerRef goal_chunk_handlers[] = { - GOAL, +static const ChunkHandler goal_chunk_handlers[] = { + { 'GOAL', Save_GOAL, Load_GOAL, nullptr, nullptr, CH_ARRAY }, }; extern const ChunkHandlerTable _goal_chunk_handlers(goal_chunk_handlers); - -} diff --git a/src/saveload/upstream/group_sl.cpp b/src/sl/group_sl.cpp similarity index 60% rename from src/saveload/upstream/group_sl.cpp rename to src/sl/group_sl.cpp index d70eb9f363..f41091611c 100644 --- a/src/saveload/upstream/group_sl.cpp +++ b/src/sl/group_sl.cpp @@ -7,20 +7,18 @@ /** @file group_sl.cpp Code handling saving and loading of economy data */ -#include "../../stdafx.h" -#include "../../group.h" -#include "../../company_base.h" +#include "../stdafx.h" +#include "../group.h" +#include "../company_base.h" #include "saveload.h" -#include "compat/group_sl_compat.h" -#include "../../safeguards.h" - -namespace upstream_sl { +#include "../safeguards.h" static const SaveLoad _group_desc[] = { SLE_CONDVAR(Group, name, SLE_NAME, SL_MIN_VERSION, SLV_84), SLE_CONDSSTR(Group, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), // num_vehicle SLE_VAR(Group, owner, SLE_UINT8), SLE_VAR(Group, vehicle_type, SLE_UINT8), SLE_VAR(Group, flags, SLE_UINT8), @@ -30,46 +28,35 @@ static const SaveLoad _group_desc[] = { SLE_CONDVAR(Group, parent, SLE_UINT16, SLV_189, SL_MAX_VERSION), }; -struct GRPSChunkHandler : ChunkHandler { - GRPSChunkHandler() : ChunkHandler('GRPS', CH_TABLE) {} +static void Save_GRPS() +{ + for (Group *g : Group::Iterate()) { + SlSetArrayIndex(g->index); + SlObject(g, _group_desc); + } +} - void Save() const override - { - SlTableHeader(_group_desc); - for (Group *g : Group::Iterate()) { - SlSetArrayIndex(g->index); - SlObject(g, _group_desc); +static void Load_GRPS() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + Group *g = new (index) Group(); + SlObject(g, _group_desc); + + if (IsSavegameVersionBefore(SLV_189)) g->parent = INVALID_GROUP; + + if (IsSavegameVersionBefore(SLV_GROUP_LIVERIES)) { + const Company *c = Company::Get(g->owner); + g->livery.colour1 = c->livery[LS_DEFAULT].colour1; + g->livery.colour2 = c->livery[LS_DEFAULT].colour2; } } +} - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_group_desc, _group_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - Group *g = new (index) Group(); - SlObject(g, slt); - - if (IsSavegameVersionBefore(SLV_189)) g->parent = INVALID_GROUP; - - if (IsSavegameVersionBefore(SLV_GROUP_LIVERIES)) { - const Company *c = Company::Get(g->owner); - g->livery.colour1 = c->livery[LS_DEFAULT].colour1; - g->livery.colour2 = c->livery[LS_DEFAULT].colour2; - } - } - } -}; - -static const GRPSChunkHandler GRPS; -static const ChunkHandlerRef group_chunk_handlers[] = { - GRPS, +static const ChunkHandler group_chunk_handlers[] = { + { 'GRPS', Save_GRPS, Load_GRPS, nullptr, nullptr, CH_ARRAY }, }; extern const ChunkHandlerTable _group_chunk_handlers(group_chunk_handlers); - -} diff --git a/src/sl/industry_sl.cpp b/src/sl/industry_sl.cpp new file mode 100644 index 0000000000..c75674069e --- /dev/null +++ b/src/sl/industry_sl.cpp @@ -0,0 +1,187 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file industry_sl.cpp Code handling saving and loading of industries */ + +#include "../stdafx.h" +#include "../industry.h" + +#include "saveload.h" +#include "newgrf_sl.h" + +#include "../safeguards.h" + +static OldPersistentStorage _old_ind_persistent_storage; + +static const SaveLoad _industry_desc[] = { + SLE_CONDVAR(Industry, location.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Industry, location.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_VAR(Industry, location.w, SLE_FILE_U8 | SLE_VAR_U16), + SLE_VAR(Industry, location.h, SLE_FILE_U8 | SLE_VAR_U16), + SLE_REF(Industry, town, REF_TOWN), + SLE_CONDREF(Industry, neutral_station, REF_STATION, SLV_SERVE_NEUTRAL_INDUSTRIES, SL_MAX_VERSION), + SLE_CONDNULL( 2, SL_MIN_VERSION, SLV_61), ///< used to be industry's produced_cargo + SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, production_rate, SLE_UINT8, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, production_rate, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDNULL( 3, SL_MIN_VERSION, SLV_61), ///< used to be industry's accepts_cargo + SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_VAR(Industry, prod_level, SLE_UINT8), + SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + + SLE_VAR(Industry, counter, SLE_UINT16), + + SLE_VAR(Industry, type, SLE_UINT8), + SLE_VAR(Industry, owner, SLE_UINT8), + SLE_VAR(Industry, random_colour, SLE_UINT8), + SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8), + SLE_CONDVAR(Industry, ctlflags, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION), + + SLE_CONDVAR(Industry, founder, SLE_UINT8, SLV_70, SL_MAX_VERSION), + SLE_CONDVAR(Industry, construction_date, SLE_INT32, SLV_70, SL_MAX_VERSION), + SLE_CONDVAR(Industry, construction_type, SLE_UINT8, SLV_70, SL_MAX_VERSION), + SLE_CONDVAR(Industry, last_cargo_accepted_at[0], SLE_INT32, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS), + SLE_CONDARR(Industry, last_cargo_accepted_at, SLE_INT32, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION), + SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, SLV_73, SL_MAX_VERSION), + SLE_CONDVAR(Industry, exclusive_supplier, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION), + SLE_CONDVAR(Industry, exclusive_consumer, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION), + + SLEG_CONDARR(_old_ind_persistent_storage.storage, SLE_UINT32, 16, SLV_76, SLV_161), + SLE_CONDREF(Industry, psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), + + SLE_CONDNULL(1, SLV_82, SLV_197), // random_triggers + SLE_CONDVAR(Industry, random, SLE_UINT16, SLV_82, SL_MAX_VERSION), + SLE_CONDSSTR(Industry, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_INDUSTRY_TEXT, SL_MAX_VERSION), + + SLE_CONDNULL(32, SLV_2, SLV_144), // old reserved space +}; + +static void Save_INDY() +{ + /* Write the industries */ + for (Industry *ind : Industry::Iterate()) { + SlSetArrayIndex(ind->index); + SlObject(ind, _industry_desc); + } +} + +static void Save_IIDS() +{ + Save_NewGRFMapping(_industry_mngr); +} + +static void Save_TIDS() +{ + Save_NewGRFMapping(_industile_mngr); +} + +static void Load_INDY() +{ + int index; + + Industry::ResetIndustryCounts(); + + while ((index = SlIterateArray()) != -1) { + Industry *i = new (index) Industry(); + SlObject(i, _industry_desc); + + /* Before savegame version 161, persistent storages were not stored in a pool. */ + if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_76)) { + /* Store the old persistent storage. The GRFID will be added later. */ + assert(PersistentStorage::CanAllocateItem()); + i->psa = new PersistentStorage(0, 0, 0); + memcpy(i->psa->storage, _old_ind_persistent_storage.storage, sizeof(_old_ind_persistent_storage.storage)); + } + Industry::IncIndustryTypeCount(i->type); + } +} + +static void Load_IIDS() +{ + Load_NewGRFMapping(_industry_mngr); +} + +static void Load_TIDS() +{ + Load_NewGRFMapping(_industile_mngr); +} + +static void Ptrs_INDY() +{ + for (Industry *i : Industry::Iterate()) { + SlObject(i, _industry_desc); + } +} + +/** Description of the data to save and load in #IndustryBuildData. */ +static const SaveLoad _industry_builder_desc[] = { + SLEG_VAR(_industry_builder.wanted_inds, SLE_UINT32), +}; + +/** Load/save industry builder. */ +static void LoadSave_IBLD() +{ + SlGlobList(_industry_builder_desc); +} + +/** Description of the data to save and load in #IndustryTypeBuildData. */ +static const SaveLoad _industrytype_builder_desc[] = { + SLE_VAR(IndustryTypeBuildData, probability, SLE_UINT32), + SLE_VAR(IndustryTypeBuildData, min_number, SLE_UINT8), + SLE_VAR(IndustryTypeBuildData, target_count, SLE_UINT16), + SLE_VAR(IndustryTypeBuildData, max_wait, SLE_UINT16), + SLE_VAR(IndustryTypeBuildData, wait_count, SLE_UINT16), +}; + +/** Save industry-type build data. */ +static void Save_ITBL() +{ + for (int i = 0; i < NUM_INDUSTRYTYPES; i++) { + SlSetArrayIndex(i); + SlObject(_industry_builder.builddata + i, _industrytype_builder_desc); + } +} + +/** Load industry-type build data. */ +static void Load_ITBL() +{ + for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) { + _industry_builder.builddata[it].Reset(); + } + int index; + while ((index = SlIterateArray()) != -1) { + if ((uint)index >= NUM_INDUSTRYTYPES) SlErrorCorrupt("Too many industry builder datas"); + SlObject(_industry_builder.builddata + index, _industrytype_builder_desc); + } +} + +static const ChunkHandler industry_chunk_handlers[] = { + { 'INDY', Save_INDY, Load_INDY, Ptrs_INDY, nullptr, CH_ARRAY }, + { 'IIDS', Save_IIDS, Load_IIDS, nullptr, nullptr, CH_ARRAY }, + { 'TIDS', Save_TIDS, Load_TIDS, nullptr, nullptr, CH_ARRAY }, + { 'IBLD', LoadSave_IBLD, LoadSave_IBLD, nullptr, nullptr, CH_RIFF }, + { 'ITBL', Save_ITBL, Load_ITBL, nullptr, nullptr, CH_ARRAY }, +}; + +extern const ChunkHandlerTable _industry_chunk_handlers(industry_chunk_handlers); diff --git a/src/sl/labelmaps_sl.cpp b/src/sl/labelmaps_sl.cpp new file mode 100644 index 0000000000..9d8c084b94 --- /dev/null +++ b/src/sl/labelmaps_sl.cpp @@ -0,0 +1,138 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file labelmaps_sl.cpp Code handling saving and loading of rail type label mappings */ + +#include "../stdafx.h" +#include "../station_map.h" +#include "../tunnelbridge_map.h" + +#include "saveload.h" +#include "saveload_internal.h" + +#include "../safeguards.h" + +std::vector _railtype_list; + +/** + * Test if any saved rail type labels are different to the currently loaded + * rail types, which therefore requires conversion. + * @return true if (and only if) conversion due to rail type changes is needed. + */ +static bool NeedRailTypeConversion() +{ + for (uint i = 0; i < _railtype_list.size(); i++) { + if ((RailType)i < RAILTYPE_END) { + const RailtypeInfo *rti = GetRailTypeInfo((RailType)i); + if (rti->label != _railtype_list[i]) return true; + } else { + if (_railtype_list[i] != 0) return true; + } + } + + /* No rail type conversion is necessary */ + return false; +} + +void AfterLoadLabelMaps() +{ + if (NeedRailTypeConversion()) { + RailType railtype_conversion_map[RAILTYPE_END]; + + for (uint i = 0; i < _railtype_list.size(); i++) { + RailType r = GetRailTypeByLabel(_railtype_list[i]); + if (r == INVALID_RAILTYPE) r = RAILTYPE_BEGIN; + + railtype_conversion_map[i] = r; + } + for (uint i = (uint)_railtype_list.size(); i < RAILTYPE_END; i++) { + railtype_conversion_map[i] = RAILTYPE_RAIL; + } + + auto convert = [&](TileIndex t) { + SetRailType(t, railtype_conversion_map[GetRailType(t)]); + RailType secondary = GetTileSecondaryRailTypeIfValid(t); + if (secondary != INVALID_RAILTYPE) SetSecondaryRailType(t, railtype_conversion_map[secondary]); + }; + + for (TileIndex t = 0; t < MapSize(); t++) { + switch (GetTileType(t)) { + case MP_RAILWAY: + convert(t); + break; + + case MP_ROAD: + if (IsLevelCrossing(t)) { + convert(t); + } + break; + + case MP_STATION: + if (HasStationRail(t)) { + convert(t); + } + break; + + case MP_TUNNELBRIDGE: + if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) { + convert(t); + } + break; + + default: + break; + } + } + } + + ResetLabelMaps(); +} + +void ResetLabelMaps() +{ + _railtype_list.clear(); +} + +/** Container for a label for SaveLoad system */ +struct LabelObject { + uint32 label; +}; + +static const SaveLoad _label_object_desc[] = { + SLE_VAR(LabelObject, label, SLE_UINT32), +}; + +static void Save_RAIL() +{ + LabelObject lo; + + for (RailType r = RAILTYPE_BEGIN; r != RAILTYPE_END; r++) { + lo.label = GetRailTypeInfo(r)->label; + + SlSetArrayIndex(r); + SlObject(&lo, _label_object_desc); + } +} + +static void Load_RAIL() +{ + ResetLabelMaps(); + + LabelObject lo; + + while (SlIterateArray() != -1) { + SlObject(&lo, _label_object_desc); + _railtype_list.push_back((RailTypeLabel)lo.label); + } +} + +static const ChunkHandler labelmaps_chunk_handlers[] = { + { 'RAIL', Save_RAIL, Load_RAIL, nullptr, nullptr, CH_ARRAY }, +}; + +extern const ChunkHandlerTable _labelmaps_chunk_handlers(labelmaps_chunk_handlers); + diff --git a/src/sl/league_sl.cpp b/src/sl/league_sl.cpp new file mode 100644 index 0000000000..4f70d34208 --- /dev/null +++ b/src/sl/league_sl.cpp @@ -0,0 +1,28 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file league_sl.cpp Code handling saving and loading of league tables */ +#include "../stdafx.h" + +#include "saveload.h" + +extern SaveLoadVersion _sl_xv_upstream_version; + +struct GetLeagueChunkLoadInfo +{ + static SaveLoadVersion GetLoadVersion() + { + return _sl_xv_upstream_version != SL_MIN_VERSION ? _sl_xv_upstream_version : SLV_MULTITRACK_LEVEL_CROSSINGS; + } +}; + +static const ChunkHandler league_chunk_handlers[] = { + MakeUpstreamChunkHandler<'LEAE', GetLeagueChunkLoadInfo>(), + MakeUpstreamChunkHandler<'LEAT', GetLeagueChunkLoadInfo>(), +}; + +extern const ChunkHandlerTable _league_chunk_handlers(league_chunk_handlers); diff --git a/src/sl/linkgraph_sl.cpp b/src/sl/linkgraph_sl.cpp new file mode 100644 index 0000000000..77594c625c --- /dev/null +++ b/src/sl/linkgraph_sl.cpp @@ -0,0 +1,371 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file linkgraph_sl.cpp Code handling saving and loading of link graphs */ + +#include "../stdafx.h" +#include "../linkgraph/linkgraph.h" +#include "../linkgraph/linkgraphjob.h" +#include "../linkgraph/linkgraphschedule.h" +#include "../network/network.h" +#include "../settings_internal.h" +#include "saveload.h" + +#include "../safeguards.h" + +typedef LinkGraph::BaseNode Node; +typedef LinkGraph::BaseEdge Edge; + +const SettingDesc *GetSettingDescription(uint index); + +static uint16 _num_nodes; + +/** + * Get a SaveLoad array for a link graph. + * @return SaveLoad array for link graph. + */ +SaveLoadTable GetLinkGraphDesc() +{ + static const SaveLoad link_graph_desc[] = { + SLE_VAR(LinkGraph, last_compression, SLE_INT32), + SLEG_VAR(_num_nodes, SLE_UINT16), + SLE_VAR(LinkGraph, cargo, SLE_UINT8), + }; + return link_graph_desc; +} + +void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj) +{ + lgj->join_date_ticks *= DAY_TICKS; + lgj->join_date_ticks += LinkGraphSchedule::SPAWN_JOIN_TICK; + lgj->start_date_ticks = lgj->join_date_ticks - (lgj->Settings().recalc_time * DAY_TICKS); +} + +/** + * Get a SaveLoad array for a link graph job. The settings struct is derived from + * the global settings saveload array. The exact entries are calculated when the function + * is called the first time. + * It's necessary to keep a copy of the settings for each link graph job so that you can + * change the settings while in-game and still not mess with current link graph runs. + * Of course the settings have to be saved and loaded, too, to avoid desyncs. + * @return Array of SaveLoad structs. + */ +SaveLoadTable GetLinkGraphJobDesc() +{ + static std::vector saveloads; + static const char *prefix = "linkgraph."; + + /* Build the SaveLoad array on first call and don't touch it later on */ + if (saveloads.size() == 0) { + size_t offset_gamesettings = cpp_offsetof(GameSettings, linkgraph); + size_t offset_component = cpp_offsetof(LinkGraphJob, settings); + + size_t prefixlen = strlen(prefix); + + int setting = 0; + const SettingDesc *desc = GetSettingDescription(setting); + while (desc != nullptr) { + if (desc->name != nullptr && strncmp(desc->name, prefix, prefixlen) == 0) { + SaveLoad sl = desc->save; + if (GetVarMemType(sl.conv) != SLE_VAR_NULL) { + char *&address = reinterpret_cast(sl.address); + address -= offset_gamesettings; + address += offset_component; + } + saveloads.push_back(sl); + } + desc = GetSettingDescription(++setting); + } + + const SaveLoad job_desc[] = { + SLE_VAR(LinkGraphJob, join_date_ticks, SLE_INT32), + SLE_CONDVAR_X(LinkGraphJob, start_date_ticks, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_DAY_SCALE)), + SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16), + }; + + for (auto &sld : job_desc) { + saveloads.push_back(sld); + } + } + + return saveloads; +} + +/** + * Get a SaveLoad array for the link graph schedule. + * @return SaveLoad array for the link graph schedule. + */ +SaveLoadTable GetLinkGraphScheduleDesc() +{ + static const SaveLoad schedule_desc[] = { + SLE_REFLIST(LinkGraphSchedule, schedule, REF_LINK_GRAPH), + SLE_REFLIST(LinkGraphSchedule, running, REF_LINK_GRAPH_JOB), + }; + return schedule_desc; +} + +/* Edges and nodes are saved in the correct order, so we don't need to save their IDs. */ + +/** + * SaveLoad desc for a link graph node. + */ +static const SaveLoad _node_desc[] = { + SLE_CONDVAR(Node, xy, SLE_UINT32, SLV_191, SL_MAX_VERSION), + SLE_VAR(Node, supply, SLE_UINT32), + SLE_VAR(Node, demand, SLE_UINT32), + SLE_VAR(Node, station, SLE_UINT16), + SLE_VAR(Node, last_update, SLE_INT32), +}; + +/** + * SaveLoad desc for a link graph edge. + */ +static const SaveLoad _edge_desc[] = { + SLE_CONDNULL(4, SL_MIN_VERSION, SLV_191), // distance + SLE_VAR(Edge, capacity, SLE_UINT32), + SLE_VAR(Edge, usage, SLE_UINT32), + SLE_CONDVAR_X(Edge, travel_time_sum, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_TRAVEL_TIME)), + SLE_VAR(Edge, last_unrestricted_update, SLE_INT32), + SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION), + SLE_CONDVAR_X(Edge, last_aircraft_update, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_AIRCRAFT)), +// SLE_VAR(Edge, next_edge, SLE_UINT16), // Removed since XSLFI_LINKGRAPH_SPARSE_EDGES +}; + +std::vector _filtered_node_desc; +std::vector _filtered_edge_desc; +std::vector _filtered_job_desc; + +static void FilterDescs() +{ + _filtered_node_desc = SlFilterObject(_node_desc); + _filtered_edge_desc = SlFilterObject(_edge_desc); + _filtered_job_desc = SlFilterObject(GetLinkGraphJobDesc()); +} + +/** + * Save a link graph. + * @param lg Link graph to be saved or loaded. + */ +void Save_LinkGraph(LinkGraph &lg) +{ + uint16 size = lg.Size(); + auto edge_iter = lg.edges.begin(); + auto edge_end = lg.edges.end(); + for (NodeID from = 0; from < size; ++from) { + Node *node = &lg.nodes[from]; + SlObjectSaveFiltered(node, _filtered_node_desc); + + while (edge_iter != edge_end && edge_iter->first.first == from) { + SlWriteUint16(edge_iter->first.second); + Edge *edge = &edge_iter->second; + SlObjectSaveFiltered(edge, _filtered_edge_desc); + ++edge_iter; + } + SlWriteUint16(INVALID_NODE); + } +} + +/** + * Load a link graph. + * @param lg Link graph to be saved or loaded. + */ +void Load_LinkGraph(LinkGraph &lg) +{ + uint size = lg.Size(); + if (SlXvIsFeaturePresent(XSLFI_LINKGRAPH_SPARSE_EDGES)) { + for (NodeID from = 0; from < size; ++from) { + Node *node = &lg.nodes[from]; + SlObjectLoadFiltered(node, _filtered_node_desc); + while (true) { + NodeID to = SlReadUint16(); + if (to == INVALID_NODE) break; + SlObjectLoadFiltered(&lg.edges[std::make_pair(from, to)], _filtered_edge_desc); + } + } + } else if (IsSavegameVersionBefore(SLV_191)) { + std::vector temp_edges; + std::vector temp_next_edges; + temp_edges.resize(size); + temp_next_edges.resize(size); + for (NodeID from = 0; from < size; ++from) { + Node *node = &lg.nodes[from]; + SlObjectLoadFiltered(node, _filtered_node_desc); + /* We used to save the full matrix ... */ + for (NodeID to = 0; to < size; ++to) { + SlObjectLoadFiltered(&temp_edges[to], _filtered_edge_desc); + temp_next_edges[to] = SlReadUint16(); + } + for (NodeID to = from; to != INVALID_NODE; to = temp_next_edges[to]) { + lg.edges[std::make_pair(from, to)] = temp_edges[to]; + } + } + } else { + for (NodeID from = 0; from < size; ++from) { + Node *node = &lg.nodes[from]; + SlObjectLoadFiltered(node, _filtered_node_desc); + /* ... but as that wasted a lot of space we save a sparse matrix now. */ + for (NodeID to = from; to != INVALID_NODE;) { + if (to >= size) SlErrorCorrupt("Link graph structure overflow"); + SlObjectLoadFiltered(&lg.edges[std::make_pair(from, to)], _filtered_edge_desc); + to = SlReadUint16(); + } + } + } +} + +/** + * Save a link graph job. + * @param lgj LinkGraphJob to be saved. + */ +static void DoSave_LGRJ(LinkGraphJob *lgj) +{ + SlObjectSaveFiltered(lgj, _filtered_job_desc); + _num_nodes = lgj->Size(); + SlObjectSaveFiltered(const_cast(&lgj->Graph()), GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals + Save_LinkGraph(const_cast(lgj->Graph())); +} + +/** + * Save a link graph. + * @param lg LinkGraph to be saved. + */ +static void DoSave_LGRP(LinkGraph *lg) +{ + _num_nodes = lg->Size(); + SlObjectSaveFiltered(lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals + Save_LinkGraph(*lg); +} + +/** + * Load all link graphs. + */ +static void Load_LGRP() +{ + FilterDescs(); + int index; + while ((index = SlIterateArray()) != -1) { + if (!LinkGraph::CanAllocateItem()) { + /* Impossible as they have been present in previous game. */ + NOT_REACHED(); + } + LinkGraph *lg = new (index) LinkGraph(); + SlObjectLoadFiltered(lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals + lg->Init(_num_nodes); + Load_LinkGraph(*lg); + } +} + +/** + * Load all link graph jobs. + */ +static void Load_LGRJ() +{ + FilterDescs(); + int index; + while ((index = SlIterateArray()) != -1) { + if (!LinkGraphJob::CanAllocateItem()) { + /* Impossible as they have been present in previous game. */ + NOT_REACHED(); + } + LinkGraphJob *lgj = new (index) LinkGraphJob(); + SlObjectLoadFiltered(lgj, _filtered_job_desc); + if (SlXvIsFeatureMissing(XSLFI_LINKGRAPH_DAY_SCALE)) { + extern void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj); + GetLinkGraphJobDayLengthScaleAfterLoad(lgj); + } + LinkGraph &lg = const_cast(lgj->Graph()); + SlObjectLoadFiltered(&lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals + lg.Init(_num_nodes); + Load_LinkGraph(lg); + } +} + +/** + * Load the link graph schedule. + */ +static void Load_LGRS() +{ + SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); +} + +/** + * Spawn the threads for running link graph calculations. + * Has to be done after loading as the cargo classes might have changed. + */ +void AfterLoadLinkGraphs() +{ + if (IsSavegameVersionBefore(SLV_191)) { + for (LinkGraph *lg : LinkGraph::Iterate()) { + for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) { + const Station *st = Station::GetIfValid((*lg)[node_id].Station()); + if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy); + } + } + + for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { + LinkGraph *lg = &(const_cast(lgj->Graph())); + for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) { + const Station *st = Station::GetIfValid((*lg)[node_id].Station()); + if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy); + } + } + } + + LinkGraphSchedule::instance.SpawnAll(); + + if (!_networking || _network_server) { + AfterLoad_LinkGraphPauseControl(); + } +} + +/** + * Save all link graphs. + */ +static void Save_LGRP() +{ + FilterDescs(); + for (LinkGraph *lg : LinkGraph::Iterate()) { + SlSetArrayIndex(lg->index); + SlAutolength((AutolengthProc*)DoSave_LGRP, lg); + } +} + +/** + * Save all link graph jobs. + */ +static void Save_LGRJ() +{ + FilterDescs(); + for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { + SlSetArrayIndex(lgj->index); + SlAutolength((AutolengthProc*)DoSave_LGRJ, lgj); + } +} + +/** + * Save the link graph schedule. + */ +static void Save_LGRS() +{ + SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); +} + +/** + * Substitute pointers in link graph schedule. + */ +static void Ptrs_LGRS() +{ + SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc()); +} + +static const ChunkHandler linkgraph_chunk_handlers[] = { + { 'LGRP', Save_LGRP, Load_LGRP, nullptr, nullptr, CH_ARRAY }, + { 'LGRJ', Save_LGRJ, Load_LGRJ, nullptr, nullptr, CH_ARRAY }, + { 'LGRS', Save_LGRS, Load_LGRS, Ptrs_LGRS, nullptr, CH_RIFF } +}; + +extern const ChunkHandlerTable _linkgraph_chunk_handlers(linkgraph_chunk_handlers); diff --git a/src/sl/map_sl.cpp b/src/sl/map_sl.cpp new file mode 100644 index 0000000000..5017b7557e --- /dev/null +++ b/src/sl/map_sl.cpp @@ -0,0 +1,420 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file map_sl.cpp Code handling saving and loading of map */ + +#include "../stdafx.h" +#include "../map_func.h" +#include "../core/bitmath_func.hpp" +#include "../core/endian_func.hpp" +#include "../core/endian_type.hpp" +#include "../fios.h" +#include + +#include "saveload.h" +#include "saveload_buffer.h" + +#include "../safeguards.h" + +static uint32 _map_dim_x; +static uint32 _map_dim_y; + +extern bool _sl_maybe_chillpp; + +static const SaveLoad _map_dimensions[] = { + SLEG_CONDVAR(_map_dim_x, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLEG_CONDVAR(_map_dim_y, SLE_UINT32, SLV_6, SL_MAX_VERSION), +}; + +static void Save_MAPS() +{ + _map_dim_x = MapSizeX(); + _map_dim_y = MapSizeY(); + SlGlobList(_map_dimensions); +} + +static void Load_MAPS() +{ + SlGlobList(_map_dimensions); + if (!ValidateMapSize(_map_dim_x, _map_dim_y)) { + SlErrorCorruptFmt("Invalid map size: %u x %u", _map_dim_x, _map_dim_y); + } + AllocateMap(_map_dim_x, _map_dim_y); +} + +static void Check_MAPS() +{ + SlGlobList(_map_dimensions); + _load_check_data.map_size_x = _map_dim_x; + _load_check_data.map_size_y = _map_dim_y; +} + +static const uint MAP_SL_BUF_SIZE = 4096; + +static void Load_MAPT() +{ + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type = buf[j]; + } +} + +static void Check_MAPH_common() +{ + if (_sl_maybe_chillpp && (SlGetFieldLength() == 0 || SlGetFieldLength() == (size_t)_map_dim_x * (size_t)_map_dim_y * 2)) { + _sl_maybe_chillpp = false; + extern void SlXvChillPPSpecialSavegameVersions(); + SlXvChillPPSpecialSavegameVersions(); + } +} + +static void Check_MAPH() +{ + Check_MAPH_common(); + SlSkipBytes(SlGetFieldLength()); +} + +static void Load_MAPH() +{ + Check_MAPH_common(); + if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { + if (SlGetFieldLength() != 0) { + _sl_xv_feature_versions[XSLFI_HEIGHT_8_BIT] = 2; + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; + } + } + return; + } + + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; + } +} + +static void Load_MAP1() +{ + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; + } +} + +static void Load_MAP2() +{ + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, + /* In those versions the m2 was 8 bits */ + IsSavegameVersionBefore(SLV_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 Load_MAP3() +{ + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; + } +} + +static void Load_MAP4() +{ + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; + } +} + +static void Load_MAP5() +{ + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; + } +} + +static void Load_MAP6() +{ + std::array buf; + TileIndex size = MapSize(); + + if (IsSavegameVersionBefore(SLV_42)) { + for (TileIndex i = 0; i != size;) { + /* 1024, otherwise we overflow on 64x64 maps! */ + SlArray(buf.data(), 1024, SLE_UINT8); + for (uint j = 0; j != 1024; j++) { + _me[i++].m6 = GB(buf[j], 0, 2); + _me[i++].m6 = GB(buf[j], 2, 2); + _me[i++].m6 = GB(buf[j], 4, 2); + _me[i++].m6 = GB(buf[j], 6, 2); + } + } + } else { + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m6 = buf[j]; + } + } +} + +static void Load_MAP7() +{ + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; + } +} + +static void Load_MAP8() +{ + std::array buf; + TileIndex size = MapSize(); + + for (TileIndex i = 0; i != size;) { + SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m8 = buf[j]; + } +} + +static void Load_WMAP() +{ + static_assert(sizeof(Tile) == 8); + static_assert(sizeof(TileExtended) == 4); + assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 1 || _sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2); + + ReadBuffer *reader = ReadBuffer::GetCurrent(); + const TileIndex size = MapSize(); + +#if TTD_ENDIAN == TTD_LITTLE_ENDIAN + reader->CopyBytes((byte *) _m, size * 8); +#else + for (TileIndex i = 0; i != size; i++) { + reader->CheckBytes(8); + _m[i].type = reader->RawReadByte(); + _m[i].height = reader->RawReadByte(); + uint16 m2 = reader->RawReadByte(); + m2 |= ((uint16) reader->RawReadByte()) << 8; + _m[i].m2 = m2; + _m[i].m1 = reader->RawReadByte(); + _m[i].m3 = reader->RawReadByte(); + _m[i].m4 = reader->RawReadByte(); + _m[i].m5 = reader->RawReadByte(); + } +#endif + + if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 1) { + for (TileIndex i = 0; i != size; i++) { + reader->CheckBytes(2); + _me[i].m6 = reader->RawReadByte(); + _me[i].m7 = reader->RawReadByte(); + } + } else if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2) { +#if TTD_ENDIAN == TTD_LITTLE_ENDIAN + reader->CopyBytes((byte *) _me, size * 4); +#else + for (TileIndex i = 0; i != size; i++) { + reader->CheckBytes(4); + _me[i].m6 = reader->RawReadByte(); + _me[i].m7 = reader->RawReadByte(); + uint16 m8 = reader->RawReadByte(); + m8 |= ((uint16) reader->RawReadByte()) << 8; + _me[i].m8 = m8; + } +#endif + } else { + NOT_REACHED(); + } +} + +static void Save_WMAP() +{ + static_assert(sizeof(Tile) == 8); + static_assert(sizeof(TileExtended) == 4); + assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2); + + MemoryDumper *dumper = MemoryDumper::GetCurrent(); + const TileIndex size = MapSize(); + SlSetLength(size * 12); + +#if TTD_ENDIAN == TTD_LITTLE_ENDIAN + dumper->CopyBytes((byte *) _m, size * 8); + dumper->CopyBytes((byte *) _me, size * 4); +#else + for (TileIndex i = 0; i != size; i++) { + dumper->CheckBytes(8); + dumper->RawWriteByte(_m[i].type); + dumper->RawWriteByte(_m[i].height); + dumper->RawWriteByte(GB(_m[i].m2, 0, 8)); + dumper->RawWriteByte(GB(_m[i].m2, 8, 8)); + dumper->RawWriteByte(_m[i].m1); + dumper->RawWriteByte(_m[i].m3); + dumper->RawWriteByte(_m[i].m4); + dumper->RawWriteByte(_m[i].m5); + } + for (TileIndex i = 0; i != size; i++) { + dumper->CheckBytes(4); + dumper->RawWriteByte(_me[i].m6); + dumper->RawWriteByte(_me[i].m7); + dumper->RawWriteByte(GB(_me[i].m8, 0, 8)); + dumper->RawWriteByte(GB(_me[i].m8, 8, 8)); + } +#endif +} + +struct MAPT { + typedef uint8 FieldT; + static const FieldT &GetField(TileIndex t) { return _m[t].type; } +}; + +struct MAPH { + typedef uint8 FieldT; + static const FieldT &GetField(TileIndex t) { return _m[t].height; } +}; + +struct MAP1 { + typedef uint8 FieldT; + static const FieldT &GetField(TileIndex t) { return _m[t].m1; } +}; + +struct MAP2 { + typedef uint16 FieldT; + static const FieldT &GetField(TileIndex t) { return _m[t].m2; } +}; + +struct MAP3 { + typedef uint8 FieldT; + static const FieldT &GetField(TileIndex t) { return _m[t].m3; } +}; + +struct MAP4 { + typedef uint8 FieldT; + static const FieldT &GetField(TileIndex t) { return _m[t].m4; } +}; + +struct MAP5 { + typedef uint8 FieldT; + static const FieldT &GetField(TileIndex t) { return _m[t].m5; } +}; + +struct MAP6 { + typedef uint8 FieldT; + static const FieldT &GetField(TileIndex t) { return _me[t].m6; } +}; + +struct MAP7 { + typedef uint8 FieldT; + static const FieldT &GetField(TileIndex t) { return _me[t].m7; } +}; + +struct MAP8 { + typedef uint16 FieldT; + static const FieldT &GetField(TileIndex t) { return _me[t].m8; } +}; + +template +struct MAP_VarType {}; + +template <> +struct MAP_VarType +{ + static const VarType var_type = SLE_UINT8; +}; + +template <> +struct MAP_VarType +{ + static const VarType var_type = SLE_UINT16; +}; + +template +static void Save_MAP() +{ + assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 0); + + std::array buf; + TileIndex size = MapSize(); + + SlSetLength(size * sizeof(typename T::FieldT)); + for (TileIndex i = 0; i != size;) { + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = T::GetField(i++); + SlArray(buf.data(), MAP_SL_BUF_SIZE, MAP_VarType::var_type); + } +} + +static ChunkSaveLoadSpecialOpResult Special_WMAP(uint32 chunk_id, ChunkSaveLoadSpecialOp op) +{ + switch (op) { + case CSLSO_SHOULD_SAVE_CHUNK: + if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 0) return CSLSOR_DONT_SAVE_CHUNK; + break; + + default: + break; + } + return CSLSOR_NONE; +} + +static ChunkSaveLoadSpecialOpResult Special_MAP_Chunks(uint32 chunk_id, ChunkSaveLoadSpecialOp op) +{ + switch (op) { + case CSLSO_SHOULD_SAVE_CHUNK: + if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] != 0) return CSLSOR_DONT_SAVE_CHUNK; + break; + + default: + break; + } + return CSLSOR_NONE; +} + +static const ChunkHandler map_chunk_handlers[] = { + { 'MAPS', Save_MAPS, Load_MAPS, nullptr, Check_MAPS, CH_RIFF }, + { 'MAPT', Save_MAP, Load_MAPT, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'MAPH', Save_MAP, Load_MAPH, nullptr, Check_MAPH, CH_RIFF, Special_MAP_Chunks }, + { 'MAPO', Save_MAP, Load_MAP1, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'MAP2', Save_MAP, Load_MAP2, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'M3LO', Save_MAP, Load_MAP3, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'M3HI', Save_MAP, Load_MAP4, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'MAP5', Save_MAP, Load_MAP5, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'MAPE', Save_MAP, Load_MAP6, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'MAP7', Save_MAP, Load_MAP7, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'MAP8', Save_MAP, Load_MAP8, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks }, + { 'WMAP', Save_WMAP, Load_WMAP, nullptr, nullptr, CH_RIFF, Special_WMAP }, +}; + +extern const ChunkHandlerTable _map_chunk_handlers(map_chunk_handlers); diff --git a/src/sl/misc_sl.cpp b/src/sl/misc_sl.cpp new file mode 100644 index 0000000000..26071eef7a --- /dev/null +++ b/src/sl/misc_sl.cpp @@ -0,0 +1,186 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file misc_sl.cpp Saving and loading of things that didn't fit anywhere else */ + +#include "../stdafx.h" +#include "../date_func.h" +#include "../zoom_func.h" +#include "../window_gui.h" +#include "../window_func.h" +#include "../viewport_func.h" +#include "../gfx_func.h" +#include "../core/random_func.hpp" +#include "../fios.h" +#include "../road_type.h" +#include "../core/checksum_func.hpp" +#include "../event_logs.h" +#include "../timer/timer.h" +#include "../timer/timer_game_tick.h" + +#include "saveload.h" + +#include "../safeguards.h" + +extern TileIndex _cur_tileloop_tile; +extern TileIndex _aux_tileloop_tile; +extern uint16 _disaster_delay; +extern byte _trees_tick_ctr; +extern uint64 _aspect_cfg_hash; + +/* Keep track of current game position */ +int _saved_scrollpos_x; +int _saved_scrollpos_y; +ZoomLevel _saved_scrollpos_zoom; + +void SaveViewportBeforeSaveGame() +{ + const Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + + if (w != nullptr) { + _saved_scrollpos_x = w->viewport->scrollpos_x; + _saved_scrollpos_y = w->viewport->scrollpos_y; + _saved_scrollpos_zoom = w->viewport->zoom; + } +} + +void ResetViewportAfterLoadGame() +{ + Window *w = GetMainWindow(); + + 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 = std::min(_saved_scrollpos_zoom, ZOOM_LVL_MAX); + vp->virtual_width = ScaleByZoom(vp->width, vp->zoom); + vp->virtual_height = ScaleByZoom(vp->height, vp->zoom); + + /* If zoom_max is ZOOM_LVL_MIN then the setting has not been loaded yet, therefore all levels are allowed. */ + if (_settings_client.gui.zoom_max != ZOOM_LVL_MIN) { + /* Ensure zoom level is allowed */ + while (vp->zoom < _settings_client.gui.zoom_min) DoZoomInOutWindow(ZOOM_OUT, w); + while (vp->zoom > _settings_client.gui.zoom_max) DoZoomInOutWindow(ZOOM_IN, w); + } + + DoZoomInOutWindow(ZOOM_NONE, w); // update button status + MarkWholeScreenDirty(); +} + +byte _age_cargo_skip_counter; ///< Skip aging of cargo? Used before savegame version 162. +extern TimeoutTimer _new_competitor_timeout; + +static const SaveLoad _date_desc[] = { + SLEG_CONDVAR(_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLEG_CONDVAR(_date, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLEG_VAR(_date_fract, SLE_UINT16), + SLEG_CONDVAR_X(_tick_counter, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)), + SLEG_CONDVAR_X(_tick_counter, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)), + SLEG_CONDVAR_X(_tick_skip_counter, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH)), + SLEG_CONDVAR_X(_scaled_tick_counter, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), + SLEG_CONDVAR_X(_scaled_date_ticks_offset, SLE_INT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_157), // _vehicle_id_ctr_day + SLEG_CONDVAR(_age_cargo_skip_counter, SLE_UINT8, SL_MIN_VERSION, SLV_162), + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_46), + SLEG_CONDVAR(_cur_tileloop_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLEG_CONDVAR(_cur_tileloop_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLEG_VAR(_disaster_delay, SLE_UINT16), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120), + SLEG_VAR(_random.state[0], SLE_UINT32), + SLEG_VAR(_random.state[1], SLE_UINT32), + SLEG_CONDVAR_X(_state_checksum.state, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATE_CHECKSUM)), + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_10), + SLE_CONDNULL(4, SLV_10, SLV_120), + SLEG_VAR(_cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32), + SLEG_CONDVAR(_new_competitor_timeout.period, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109), + SLEG_CONDVAR_X(_new_competitor_timeout.period, SLE_UINT32, SLV_109, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE, 0, 0)), + SLEG_VAR(_trees_tick_ctr, SLE_UINT8), + SLEG_CONDVAR(_pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION), + SLEG_CONDVAR_X(_game_events_overall, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)), + SLEG_CONDVAR_X(_road_layout_change_counter, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 4, 6)), // _extra_aspects + SLEG_CONDVAR_X(_aspect_cfg_hash, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 7)), + SLEG_CONDVAR_X(_aux_tileloop_tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUX_TILE_LOOP)), + SLE_CONDNULL(4, SLV_11, SLV_120), + SLEG_CONDVAR_X(_new_competitor_timeout.period, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), + SLEG_CONDVAR_X(_new_competitor_timeout.storage.elapsed, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), + SLEG_CONDVAR_X(_new_competitor_timeout.fired, SLE_BOOL, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), +}; + +static const SaveLoad _date_check_desc[] = { + SLEG_CONDVAR(_load_check_data.current_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLEG_CONDVAR(_load_check_data.current_date, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_NULL(2), // _date_fract + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)), // _tick_counter + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)), // _tick_counter + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH)), // _tick_skip_counter + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), // _scaled_tick_counter + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), // _scaled_date_ticks_offset + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_157), // _vehicle_id_ctr_day + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_162), // _age_cargo_skip_counter + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_46), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), // _cur_tileloop_tile + SLE_CONDNULL(4, SLV_6, SL_MAX_VERSION), // _cur_tileloop_tile + SLE_NULL(2), // _disaster_delay + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120), + SLE_NULL(4), // _random.state[0] + SLE_NULL(4), // _random.state[1] + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATE_CHECKSUM)), // _state_checksum.state + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_10), + SLE_CONDNULL(4, SLV_10, SLV_120), + SLE_NULL(1), // _cur_company_tick_index + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_109), // _new_competitor_timeout.period + SLE_CONDNULL_X(4, SLV_109, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE, 0, 0)), // _new_competitor_timeout.period + SLE_NULL(1), // _trees_tick_ctr + SLE_CONDNULL(1, SLV_4, SL_MAX_VERSION), // _pause_mode + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)), // _game_events_overall + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), // _road_layout_change_counter + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 4, 6)), // _extra_aspects + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 7)), // _aspect_cfg_hash + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUX_TILE_LOOP)), // _aux_tileloop_tile + SLE_CONDNULL(4, SLV_11, SLV_120), + SLE_CONDNULL_X(9, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), // _new_competitor_timeout +}; + +/* 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); + SetScaledTickVariables(); +} + +static void Check_DATE() +{ + SlGlobList(_date_check_desc); + if (IsSavegameVersionBefore(SLV_31)) { + _load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR; + } +} + + +static const SaveLoad _view_desc[] = { + SLEG_CONDVAR(_saved_scrollpos_x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLEG_CONDVAR(_saved_scrollpos_x, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLEG_CONDVAR(_saved_scrollpos_y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLEG_CONDVAR(_saved_scrollpos_y, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLEG_VAR(_saved_scrollpos_zoom, SLE_UINT8), +}; + +static void SaveLoad_VIEW() +{ + SlGlobList(_view_desc); +} + +static const ChunkHandler misc_chunk_handlers[] = { + { 'DATE', SaveLoad_DATE, SaveLoad_DATE, nullptr, Check_DATE, CH_RIFF }, + { 'VIEW', SaveLoad_VIEW, SaveLoad_VIEW, nullptr, nullptr, CH_RIFF }, +}; + +extern const ChunkHandlerTable _misc_chunk_handlers(misc_chunk_handlers); diff --git a/src/sl/newgrf_sl.cpp b/src/sl/newgrf_sl.cpp new file mode 100644 index 0000000000..dd5932a750 --- /dev/null +++ b/src/sl/newgrf_sl.cpp @@ -0,0 +1,133 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file newgrf_sl.cpp Code handling saving and loading of newgrf config */ + +#include "../stdafx.h" +#include "../fios.h" +#include "../string_func.h" + +#include "saveload.h" +#include "newgrf_sl.h" + +#include "../safeguards.h" + +/** Save and load the mapping between a spec and the NewGRF it came from. */ +static const SaveLoad _newgrf_mapping_desc_old[] = { + SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), + SLE_VAR(EntityIDMapping, entity_id, SLE_FILE_U8 | SLE_VAR_U16), + SLE_VAR(EntityIDMapping, substitute_id, SLE_FILE_U8 | SLE_VAR_U16), +}; +static const SaveLoad _newgrf_mapping_desc_new[] = { + SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), + SLE_VAR(EntityIDMapping, entity_id, SLE_UINT16), + SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT16), +}; + +/** + * Save a GRF ID + local id -> OpenTTD's id mapping. + * @param mapping The mapping to save. + */ +void Save_NewGRFMapping(const OverrideManagerBase &mapping) +{ + for (uint i = 0; i < mapping.GetMaxMapping(); i++) { + if (mapping.mappings[i].grfid == 0 && + mapping.mappings[i].entity_id == 0) continue; + SlSetArrayIndex(i); + SlSetLength(4 + 2 + 2); + SlObjectSaveFiltered(const_cast(&mapping.mappings[i]), _newgrf_mapping_desc_new); // _newgrf_mapping_desc_new has no conditionals + } +} + +/** + * Load a GRF ID + local id -> OpenTTD's id mapping. + * @param mapping The mapping to load. + */ +void Load_NewGRFMapping(OverrideManagerBase &mapping) +{ + /* Clear the current mapping stored. + * This will create the manager if ever it is not yet done */ + mapping.ResetMapping(); + + uint max_id = mapping.GetMaxMapping(); + + SaveLoadTable slt = SlXvIsFeaturePresent(XSLFI_NEWGRF_ENTITY_EXTRA) ? SaveLoadTable(_newgrf_mapping_desc_new) : SaveLoadTable(_newgrf_mapping_desc_old); + + int index; + while ((index = SlIterateArray()) != -1) { + if (unlikely((uint)index >= max_id)) SlErrorCorrupt("Too many NewGRF entity mappings"); + SlObjectLoadFiltered(&mapping.mappings[index], slt); // _newgrf_mapping_desc_old/_newgrf_mapping_desc_new has no conditionals + } +} + +static std::string _grf_name; + +static const SaveLoad _grfconfig_desc[] = { + SLE_SSTR(GRFConfig, filename, SLE_STR), + SLE_VAR(GRFConfig, ident.grfid, SLE_UINT32), + SLE_ARR(GRFConfig, ident.md5sum, SLE_UINT8, 16), + SLE_CONDVAR(GRFConfig, version, SLE_UINT32, SLV_151, SL_MAX_VERSION), + SLE_ARR(GRFConfig, param, SLE_UINT32, 0x80), + SLE_VAR(GRFConfig, num_params, SLE_UINT8), + SLE_CONDVAR(GRFConfig, palette, SLE_UINT8, SLV_101, SL_MAX_VERSION), + SLEG_CONDSSTR_X(_grf_name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_INFO_EXTRA)), +}; + + +static void Save_NGRF() +{ + int index = 0; + + for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) { + if (HasBit(c->flags, GCF_STATIC) || HasBit(c->flags, GCF_INIT_ONLY)) continue; + SlSetArrayIndex(index++); + _grf_name = str_strip_all_scc(GetDefaultLangGRFStringFromGRFText(c->name)); + SlObject(c, _grfconfig_desc); + } +} + + +static void Load_NGRF_common(GRFConfig *&grfconfig) +{ + ClearGRFConfigList(&grfconfig); + while (SlIterateArray() != -1) { + GRFConfig *c = new GRFConfig(); + SlObject(c, _grfconfig_desc); + if (SlXvIsFeaturePresent(XSLFI_NEWGRF_INFO_EXTRA)) { + AddGRFTextToList(c->name, 0x7F, c->ident.grfid, false, _grf_name.c_str()); + } + if (IsSavegameVersionBefore(SLV_101)) c->SetSuitablePalette(); + AppendToGRFConfigList(&grfconfig, c); + } +} + +static void Load_NGRF() +{ + Load_NGRF_common(_grfconfig); + + if (_game_mode == GM_MENU) { + /* Intro game must not have NewGRF. */ + if (_grfconfig != nullptr) SlErrorCorrupt("The intro game must not use NewGRF"); + + /* Activate intro NewGRFs (townnames) */ + ResetGRFConfig(false); + } else { + /* Append static NewGRF configuration */ + AppendStaticGRFConfigs(&_grfconfig); + } +} + +static void Check_NGRF() +{ + Load_NGRF_common(_load_check_data.grfconfig); +} + +static const ChunkHandler newgrf_chunk_handlers[] = { + { 'NGRF', Save_NGRF, Load_NGRF, nullptr, Check_NGRF, CH_ARRAY } +}; + +extern const ChunkHandlerTable _newgrf_chunk_handlers(newgrf_chunk_handlers); diff --git a/src/saveload/upstream/newgrf_sl.h b/src/sl/newgrf_sl.h similarity index 60% rename from src/saveload/upstream/newgrf_sl.h rename to src/sl/newgrf_sl.h index 99136fba28..79382a9813 100644 --- a/src/saveload/upstream/newgrf_sl.h +++ b/src/sl/newgrf_sl.h @@ -7,21 +7,12 @@ /** @file newgrf_sl.h Code handling saving and loading of NewGRF mappings. */ -#ifndef SAVELOAD_UPSTREAM_NEWGRF_SL_H -#define SAVELOAD_UPSTREAM_NEWGRF_SL_H +#ifndef SL_NEWGRF_SL_H +#define SL_NEWGRF_SL_H -#include "../../newgrf_commons.h" +#include "../newgrf_commons.h" -namespace upstream_sl { +void Save_NewGRFMapping(const OverrideManagerBase &mapping); +void Load_NewGRFMapping(OverrideManagerBase &mapping); -struct NewGRFMappingChunkHandler : ChunkHandler { - OverrideManagerBase &mapping; - - NewGRFMappingChunkHandler(uint32 id, OverrideManagerBase &mapping) : ChunkHandler(id, CH_TABLE), mapping(mapping) {} - void Save() const override; - void Load() const override; -}; - -} - -#endif /* SAVELOAD_UPSTREAM_NEWGRF_SL_H */ +#endif /* SL_NEWGRF_SL_H */ diff --git a/src/saveload/newsignals_sl.cpp b/src/sl/newsignals_sl.cpp similarity index 100% rename from src/saveload/newsignals_sl.cpp rename to src/sl/newsignals_sl.cpp diff --git a/src/saveload/upstream/object_sl.cpp b/src/sl/object_sl.cpp similarity index 54% rename from src/saveload/upstream/object_sl.cpp rename to src/sl/object_sl.cpp index 852e52558e..dcf5fd65cb 100644 --- a/src/saveload/upstream/object_sl.cpp +++ b/src/sl/object_sl.cpp @@ -7,18 +7,14 @@ /** @file object_sl.cpp Code handling saving and loading of objects */ -#include "../../stdafx.h" +#include "../stdafx.h" +#include "../object_base.h" +#include "../object_map.h" #include "saveload.h" -#include "compat/object_sl_compat.h" - -#include "../../object_base.h" -#include "../../object_map.h" #include "newgrf_sl.h" -#include "../../safeguards.h" - -namespace upstream_sl { +#include "../safeguards.h" static const SaveLoad _object_desc[] = { SLE_VAR(Object, location.tile, SLE_UINT32), @@ -31,54 +27,48 @@ static const SaveLoad _object_desc[] = { SLE_CONDVAR(Object, type, SLE_UINT16, SLV_186, SL_MAX_VERSION), }; -struct OBJSChunkHandler : ChunkHandler { - OBJSChunkHandler() : ChunkHandler('OBJS', CH_TABLE) {} +static void Save_OBJS() +{ + /* Write the objects */ + for (Object *o : Object::Iterate()) { + SlSetArrayIndex(o->index); + SlObject(o, _object_desc); + } +} - void Save() const override - { - SlTableHeader(_object_desc); +static void Load_OBJS() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Object *o = new (index) Object(); + SlObject(o, _object_desc); + } +} - /* Write the objects */ - for (Object *o : Object::Iterate()) { - SlSetArrayIndex(o->index); - SlObject(o, _object_desc); +static void Ptrs_OBJS() +{ + for (Object *o : Object::Iterate()) { + SlObject(o, _object_desc); + if (IsSavegameVersionBefore(SLV_148) && !IsTileType(o->location.tile, MP_OBJECT)) { + /* Due to a small bug stale objects could remain. */ + delete o; } } +} - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_object_desc, _object_sl_compat); +static void Save_OBID() +{ + Save_NewGRFMapping(_object_mngr); +} - int index; - while ((index = SlIterateArray()) != -1) { - Object *o = new (index) Object(); - SlObject(o, slt); - } - } +static void Load_OBID() +{ + Load_NewGRFMapping(_object_mngr); +} - void FixPointers() const override - { - for (Object *o : Object::Iterate()) { - SlObject(o, _object_desc); - if (IsSavegameVersionBefore(SLV_148) && !IsTileType(o->location.tile, MP_OBJECT)) { - /* Due to a small bug stale objects could remain. */ - delete o; - } - } - } -}; - -struct OBIDChunkHandler : NewGRFMappingChunkHandler { - OBIDChunkHandler() : NewGRFMappingChunkHandler('OBID', _object_mngr) {} -}; - -static const OBIDChunkHandler OBID; -static const OBJSChunkHandler OBJS; -static const ChunkHandlerRef object_chunk_handlers[] = { - OBID, - OBJS, +static const ChunkHandler object_chunk_handlers[] = { + { 'OBID', Save_OBID, Load_OBID, nullptr, nullptr, CH_ARRAY }, + { 'OBJS', Save_OBJS, Load_OBJS, Ptrs_OBJS, nullptr, CH_ARRAY }, }; extern const ChunkHandlerTable _object_chunk_handlers(object_chunk_handlers); - -} diff --git a/src/saveload/oldloader.cpp b/src/sl/oldloader.cpp similarity index 100% rename from src/saveload/oldloader.cpp rename to src/sl/oldloader.cpp diff --git a/src/saveload/oldloader.h b/src/sl/oldloader.h similarity index 98% rename from src/saveload/oldloader.h rename to src/sl/oldloader.h index 1600a9ac22..3022822c2d 100644 --- a/src/saveload/oldloader.h +++ b/src/sl/oldloader.h @@ -7,8 +7,8 @@ /** @file oldloader.h Declarations of strctures and function used in loader of old savegames */ -#ifndef OLDLOADER_H -#define OLDLOADER_H +#ifndef SL_OLDLOADER_H +#define SL_OLDLOADER_H #include "saveload.h" #include "../tile_type.h" @@ -132,4 +132,4 @@ static inline uint32 ReadUint32(LoadgameState *ls) #define OCL_NULL(amount) OCL_CNULL((OldChunkType)0, amount) #define OCL_CHUNK(amount, proc) OCL_CCHUNK((OldChunkType)0, amount, proc) -#endif /* OLDLOADER_H */ +#endif /* SL_OLDLOADER_H */ diff --git a/src/saveload/oldloader_sl.cpp b/src/sl/oldloader_sl.cpp similarity index 100% rename from src/saveload/oldloader_sl.cpp rename to src/sl/oldloader_sl.cpp diff --git a/src/sl/order_sl.cpp b/src/sl/order_sl.cpp new file mode 100644 index 0000000000..721a5a0d30 --- /dev/null +++ b/src/sl/order_sl.cpp @@ -0,0 +1,416 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file order_sl.cpp Code handling saving and loading of orders */ + +#include "../stdafx.h" +#include "../order_backup.h" +#include "../settings_type.h" +#include "../network/network.h" + +#include "saveload_internal.h" + +#include "../safeguards.h" + +static uint32 _jokerpp_separation_mode; +std::vector _jokerpp_auto_separation; +std::vector _jokerpp_non_auto_separation; + +/** + * Converts this order from an old savegame's version; + * it moves all bits to the new location. + */ +void Order::ConvertFromOldSavegame() +{ + uint8 old_flags = this->flags; + this->flags = 0; + + /* First handle non-stop - use value from savegame if possible, else use value from config file */ + if (_settings_client.gui.sg_new_nonstop || (IsSavegameVersionBefore(SLV_22) && _savegame_type != SGT_TTO && _savegame_type != SGT_TTD && (_settings_client.gui.new_nonstop || _settings_game.order.nonstop_only))) { + /* 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 { + /* old OTTD versions stored full_load_any in config file - assume it was enabled when loading */ + this->SetLoadType(_settings_client.gui.sg_full_load_any || IsSavegameVersionBefore(SLV_22) ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD); + } + + if (this->IsType(OT_GOTO_STATION)) this->SetStopLocation(OSL_PLATFORM_FAR_END); + + /* 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(((uint64) GB(packed, 8, 8)) << 24 | ((uint64) GB(packed, 4, 4)) << 8 | ((uint64) GB(packed, 0, 4))); +} + +/** + * Unpacks a order from savegames with version 5.1 and lower + * @param packed packed order + * @return unpacked order + */ +static Order UnpackVersion5Order(uint32 packed) +{ + return Order(((uint64) GB(packed, 16, 16)) << 24 | ((uint64) GB(packed, 8, 8)) << 8 | ((uint64) GB(packed, 0, 8))); +} + +/** + * 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.IsType(OT_NOTHING) && packed != 0) order.MakeDummy(); + + return order; +} + +SaveLoadTable GetOrderDescription() +{ + static const SaveLoad _order_desc[] = { + SLE_VAR(Order, type, SLE_UINT8), + SLE_CONDVAR_X(Order, flags, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 0, 0)), + SLE_CONDVAR_X(Order, flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 1)), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_VAR(Order, dest, SLE_UINT16), + SLE_REF(Order, next, REF_ORDER), + SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION), + SLE_CONDNULL(1, SLV_36, SLV_182), // refit_subtype + SLE_CONDVAR_X(Order, occupancy, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_OCCUPANCY)), + SLE_CONDVAR_X(Order, wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), + SLE_CONDVAR_X(Order, wait_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), + SLE_CONDVAR_X(Order, travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), + SLE_CONDVAR_X(Order, travel_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), + SLE_CONDVAR(Order, max_speed, SLE_UINT16, SLV_172, SL_MAX_VERSION), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_COND_ORDERS, 1, 6)), // jump_counter + + /* 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, SLV_5, SLV_36), + }; + + return _order_desc; +} + +static std::vector _filtered_desc; + +static void Save_ORDR() +{ + _filtered_desc = SlFilterObject(GetOrderDescription()); + for (Order *order : Order::Iterate()) { + SlSetArrayIndex(order->index); + SlObjectSaveFiltered(order, _filtered_desc); + } +} + +static void Load_ORDR() +{ + if (IsSavegameVersionBefore(SLV_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(); + + if (IsSavegameVersionBefore(SLV_5)) { + /* Pre-version 5 had another layout for orders + * (uint16 instead of uint32) */ + len /= sizeof(uint16); + uint16 *orders = MallocT(len + 1); + + SlArray(orders, len, SLE_UINT16); + + for (size_t i = 0; i < len; ++i) { + Order *o = new (i) Order(); + o->AssignOrder(UnpackVersion4Order(orders[i])); + } + + free(orders); + } else if (IsSavegameVersionBefore(SLV_5, 2)) { + len /= sizeof(uint32); + uint32 *orders = MallocT(len + 1); + + SlArray(orders, len, SLE_UINT32); + + for (size_t i = 0; i < len; ++i) { + Order *o = new (i) Order(); + o->AssignOrder(UnpackVersion5Order(orders[i])); + } + + free(orders); + } + + /* Update all the next pointer */ + for (Order *o : Order::Iterate()) { + size_t order_index = o->index; + /* Delete invalid orders */ + if (o->IsType(OT_NOTHING)) { + delete o; + continue; + } + /* The orders were built like this: + * While the order is valid, set the previous will get its next pointer set */ + Order *prev = Order::GetIfValid(order_index - 1); + if (prev != nullptr) prev->next = o; + } + } else { + _filtered_desc = SlFilterObject(GetOrderDescription()); + int index; + + while ((index = SlIterateArray()) != -1) { + Order *order = new (index) Order(); + SlObjectLoadFiltered(order, _filtered_desc); + } + } +} + +const SaveLoadTable GetOrderExtraInfoDescription() +{ + static const SaveLoad _order_extra_info_desc[] = { + SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, 32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 1, 2)), + SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, NUM_CARGO, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 3)), + SLE_CONDVAR_X(OrderExtraInfo, xflags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)), + SLE_CONDVAR_X(OrderExtraInfo, xdata, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA)), + SLE_CONDVAR_X(OrderExtraInfo, dispatch_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 3)), + SLE_CONDVAR_X(OrderExtraInfo, colour, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA, 2)), + }; + + return _order_extra_info_desc; +} + +void Save_ORDX() +{ + _filtered_desc = SlFilterObject(GetOrderExtraInfoDescription()); + for (Order *order : Order::Iterate()) { + if (order->extra) { + SlSetArrayIndex(order->index); + SlObjectSaveFiltered(order->extra.get(), _filtered_desc); + } + } +} + +void Load_ORDX() +{ + _filtered_desc = SlFilterObject(GetOrderExtraInfoDescription()); + int index; + while ((index = SlIterateArray()) != -1) { + Order *order = Order::GetIfValid(index); + assert(order != nullptr); + order->AllocExtraInfo(); + SlObjectLoadFiltered(order->extra.get(), _filtered_desc); + } +} + +static void Ptrs_ORDR() +{ + /* Orders from old savegames have pointers corrected in Load_ORDR */ + if (IsSavegameVersionBefore(SLV_5, 2)) return; + + for (Order *o : Order::Iterate()) { + SlObject(o, GetOrderDescription()); + } +} + +SaveLoadTable GetDispatchScheduleDescription() +{ + static const SaveLoad _order_extra_info_desc[] = { + SLE_VARVEC(DispatchSchedule, scheduled_dispatch, SLE_UINT32), + SLE_VAR(DispatchSchedule, scheduled_dispatch_duration, SLE_UINT32), + SLE_VAR(DispatchSchedule, scheduled_dispatch_start_date, SLE_INT32), + SLE_VAR(DispatchSchedule, scheduled_dispatch_start_full_date_fract, SLE_UINT16), + SLE_VAR(DispatchSchedule, scheduled_dispatch_last_dispatch, SLE_INT32), + SLE_VAR(DispatchSchedule, scheduled_dispatch_max_delay, SLE_INT32), + SLE_CONDSSTR_X(DispatchSchedule, name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 4)), + }; + + return _order_extra_info_desc; +} + +SaveLoadTable GetOrderListDescription() +{ + static const SaveLoad _orderlist_desc[] = { + SLE_REF(OrderList, first, REF_ORDER), + SLEG_CONDVAR_X(_jokerpp_separation_mode, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + SLE_CONDNULL_X(21, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + }; + + return _orderlist_desc; +} + +static void Save_ORDL() +{ + for (OrderList *list : OrderList::Iterate()) { + SlSetArrayIndex(list->index); + SlAutolength([](void *data) { + OrderList *list = static_cast(data); + SlObject(list, GetOrderListDescription()); + SlWriteUint32(list->GetScheduledDispatchScheduleCount()); + for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) { + SlObject(&ds, GetDispatchScheduleDescription()); + } + }, list); + } +} + +static void Load_ORDL() +{ + _jokerpp_auto_separation.clear(); + _jokerpp_non_auto_separation.clear(); + int index; + + while ((index = SlIterateArray()) != -1) { + /* set num_orders to 0 so it's a valid OrderList */ + OrderList *list = new (index) OrderList(0); + SlObject(list, GetOrderListDescription()); + if (SlXvIsFeaturePresent(XSLFI_JOKERPP)) { + if (_jokerpp_separation_mode == 0) { + _jokerpp_auto_separation.push_back(list); + } else { + _jokerpp_non_auto_separation.push_back(list); + } + } + if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH)) { + uint count = SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3) ? SlReadUint32() : 1; + list->GetScheduledDispatchScheduleSet().resize(count); + for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) { + SlObject(&ds, GetDispatchScheduleDescription()); + } + } + } +} + +void Ptrs_ORDL() +{ + for (OrderList *list : OrderList::Iterate()) { + SlObject(list, GetOrderListDescription()); + list->ReindexOrderList(); + } +} + +SaveLoadTable GetOrderBackupDescription() +{ + static const SaveLoad _order_backup_desc[] = { + SLE_VAR(OrderBackup, user, SLE_UINT32), + SLE_VAR(OrderBackup, tile, SLE_UINT32), + SLE_VAR(OrderBackup, group, SLE_UINT16), + SLE_CONDVAR(OrderBackup, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SL_MIN_VERSION, SLV_192), + SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION), + SLE_STR(OrderBackup, name, SLE_STR, 0), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_192), // clone (2 bytes of pointer, i.e. garbage) + SLE_CONDREF(OrderBackup, clone, REF_VEHICLE, SLV_192, SL_MAX_VERSION), + SLE_VAR(OrderBackup, cur_real_order_index, SLE_VEHORDERID), + SLE_CONDVAR(OrderBackup, cur_implicit_order_index, SLE_VEHORDERID, SLV_176, SL_MAX_VERSION), + SLE_CONDVAR_X(OrderBackup, cur_timetable_order_index, SLE_VEHORDERID, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)), + SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, SLV_176, SL_MAX_VERSION), + SLE_CONDVAR(OrderBackup, lateness_counter, SLE_INT32, SLV_176, SL_MAX_VERSION), + SLE_CONDVAR(OrderBackup, timetable_start, SLE_INT32, SLV_176, SL_MAX_VERSION), + SLE_CONDVAR_X(OrderBackup,timetable_start_subticks, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)), + SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_176, SLV_180), + SLE_CONDVAR_X(OrderBackup, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 0, 0)), + SLE_CONDVAR_X(OrderBackup, vehicle_flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 1)), + SLE_REF(OrderBackup, orders, REF_ORDER), + SLE_CONDNULL_X(18, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2, 2)), + }; + + return _order_backup_desc; +} + +void Save_BKOR() +{ + /* We only save this when we're a network server + * as we want this information on our clients. For + * normal games this information isn't needed. */ + if (!_networking || !_network_server) return; + + for (OrderBackup *ob : OrderBackup::Iterate()) { + SlSetArrayIndex(ob->index); + SlAutolength([](void *data) { + OrderBackup *ob = static_cast(data); + SlObject(ob, GetOrderBackupDescription()); + SlWriteUint32((uint)ob->dispatch_schedules.size()); + for (DispatchSchedule &ds : ob->dispatch_schedules) { + SlObject(&ds, GetDispatchScheduleDescription()); + } + }, ob); + } +} + +void Load_BKOR() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + /* set num_orders to 0 so it's a valid OrderList */ + OrderBackup *ob = new (index) OrderBackup(); + SlObject(ob, GetOrderBackupDescription()); + if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3)) { + uint count = SlReadUint32(); + ob->dispatch_schedules.resize(count); + for (DispatchSchedule &ds : ob->dispatch_schedules) { + SlObject(&ds, GetDispatchScheduleDescription()); + } + } + } +} + +static void Ptrs_BKOR() +{ + for (OrderBackup *ob : OrderBackup::Iterate()) { + SlObject(ob, GetOrderBackupDescription()); + } +} + +static const ChunkHandler order_chunk_handlers[] = { + { 'BKOR', Save_BKOR, Load_BKOR, Ptrs_BKOR, nullptr, CH_ARRAY }, + { 'ORDR', Save_ORDR, Load_ORDR, Ptrs_ORDR, nullptr, CH_ARRAY }, + { 'ORDL', Save_ORDL, Load_ORDL, Ptrs_ORDL, nullptr, CH_ARRAY }, + { 'ORDX', Save_ORDX, Load_ORDX, nullptr, nullptr, CH_SPARSE_ARRAY }, +}; + +extern const ChunkHandlerTable _order_chunk_handlers(order_chunk_handlers); diff --git a/src/saveload/plans_sl.cpp b/src/sl/plans_sl.cpp similarity index 100% rename from src/saveload/plans_sl.cpp rename to src/sl/plans_sl.cpp diff --git a/src/sl/saveload.cpp b/src/sl/saveload.cpp new file mode 100644 index 0000000000..fc4bf9f1fd --- /dev/null +++ b/src/sl/saveload.cpp @@ -0,0 +1,3830 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @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 "../debug.h" +#include "../station_base.h" +#include "../thread.h" +#include "../town.h" +#include "../network/network.h" +#include "../window_func.h" +#include "../strings_func.h" +#include "../core/endian_func.hpp" +#include "../vehicle_base.h" +#include "../company_func.h" +#include "../date_func.h" +#include "../autoreplace_base.h" +#include "../roadstop_base.h" +#include "../linkgraph/linkgraph.h" +#include "../linkgraph/linkgraphjob.h" +#include "../statusbar_gui.h" +#include "../fileio_func.h" +#include "../gamelog.h" +#include "../string_func.h" +#include "../string_func_extra.h" +#include "../fios.h" +#include "../error.h" +#include "../scope.h" +#include +#include +#include +#ifdef __EMSCRIPTEN__ +# include +#endif + +#include "../tbtr_template_vehicle.h" + +#include "table/strings.h" + +#include "saveload_internal.h" +#include "saveload_filter.h" +#include "saveload_buffer.h" +#include "extended_ver_sl.h" + +#include +#include + +#include "../thread.h" +#include +#include +#if defined(__MINGW32__) +#include "../3rdparty/mingw-std-threads/mingw.mutex.h" +#include "../3rdparty/mingw-std-threads/mingw.condition_variable.h" +#endif + +#include "../safeguards.h" + +extern const SaveLoadVersion SAVEGAME_VERSION = SLV_CUSTOM_SUBSIDY_DURATION; ///< Current savegame version of OpenTTD. +extern const SaveLoadVersion MAX_LOAD_SAVEGAME_VERSION = (SaveLoadVersion)(SL_MAX_VERSION - 1); ///< Max loadable savegame version of OpenTTD. + +const SaveLoadVersion SAVEGAME_VERSION_EXT = (SaveLoadVersion)(0x8000); ///< Savegame extension indicator mask + +SavegameType _savegame_type; ///< type of savegame we are loading +FileToSaveLoad _file_to_saveload; ///< File to save or load in the openttd loop. + +uint32 _ttdp_version; ///< version of TTDP savegame (if applicable) +SaveLoadVersion _sl_version; ///< the major savegame version identifier +byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! +std::string _savegame_format; ///< how to compress savegames +bool _do_autosave; ///< are we doing an autosave at the moment? + +extern bool _sl_is_ext_version; +extern bool _sl_maybe_springpp; +extern bool _sl_maybe_chillpp; +extern bool _sl_upstream_mode; + +namespace upstream_sl { + void SlNullPointers(); + void SlLoadChunks(); + void SlLoadChunkByID(uint32 id); + void SlLoadCheckChunks(); + void SlLoadCheckChunkByID(uint32 id); + void SlFixPointers(); + void SlFixPointerChunkByID(uint32 id); + void SlSaveChunkChunkByID(uint32 id); + void SlResetLoadState(); +} + +/** What are we currently doing? */ +enum SaveLoadAction { + SLA_LOAD, ///< loading + SLA_SAVE, ///< saving + SLA_PTRS, ///< fixing pointers + SLA_NULL, ///< null all pointers (on loading error) + SLA_LOAD_CHECK, ///< partial loading into #_load_check_data +}; + +enum NeedLength { + NL_NONE = 0, ///< not working in NeedLength mode + NL_WANTLENGTH = 1, ///< writing length and data +}; + +void ReadBuffer::SkipBytesSlowPath(size_t bytes) +{ + bytes -= (this->bufe - this->bufp); + while (true) { + size_t len = this->reader->Read(this->buf, lengthof(this->buf)); + if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); + this->read += len; + if (len >= bytes) { + this->bufp = this->buf + bytes; + this->bufe = this->buf + len; + return; + } else { + bytes -= len; + } + } +} + +void ReadBuffer::AcquireBytes() +{ + size_t remainder = this->bufe - this->bufp; + if (remainder) { + memmove(this->buf, this->bufp, remainder); + } + size_t len = this->reader->Read(this->buf + remainder, lengthof(this->buf) - remainder); + if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); + + this->read += len; + this->bufp = this->buf; + this->bufe = this->buf + remainder + len; +} + +void MemoryDumper::FinaliseBlock() +{ + assert(this->saved_buf == nullptr); + if (!this->blocks.empty()) { + size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf); + this->blocks.back().size = s; + this->completed_block_bytes += s; + } + this->buf = this->bufe = nullptr; +} + +void MemoryDumper::AllocateBuffer() +{ + if (this->saved_buf) { + const size_t offset = this->buf - this->autolen_buf; + const size_t size = (this->autolen_buf_end - this->autolen_buf) * 2; + this->autolen_buf = ReallocT(this->autolen_buf, size); + this->autolen_buf_end = this->autolen_buf + size; + this->buf = this->autolen_buf + offset; + this->bufe = this->autolen_buf_end; + return; + } + this->FinaliseBlock(); + this->buf = MallocT(MEMORY_CHUNK_SIZE); + this->blocks.emplace_back(this->buf); + this->bufe = this->buf + MEMORY_CHUNK_SIZE; +} + +/** + * Flush this dumper into a writer. + * @param writer The filter we want to use. + */ +void MemoryDumper::Flush(SaveFilter *writer) +{ + this->FinaliseBlock(); + + size_t block_count = this->blocks.size(); + for (size_t i = 0; i < block_count; i++) { + writer->Write(this->blocks[i].data, this->blocks[i].size); + } + + writer->Finish(); +} + +void MemoryDumper::StartAutoLength() +{ + assert(this->saved_buf == nullptr); + + this->saved_buf = this->buf; + this->saved_bufe = this->bufe; + this->buf = this->autolen_buf; + this->bufe = this->autolen_buf_end; +} + +std::pair MemoryDumper::StopAutoLength() +{ + assert(this->saved_buf != nullptr); + auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf); + + this->buf = this->saved_buf; + this->bufe = this->saved_bufe; + this->saved_buf = this->saved_bufe = nullptr; + return res; +} + +/** + * Get the size of the memory dump made so far. + * @return The size. + */ +size_t MemoryDumper::GetSize() const +{ + assert(this->saved_buf == nullptr); + return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0); +} + +/** The saveload struct, containing reader-writer functions, buffer, version, etc. */ +struct SaveLoadParams { + SaveLoadAction action; ///< are we doing a save or a load atm. + NeedLength need_length; ///< working in NeedLength (Autolength) mode? + 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 + + MemoryDumper *dumper; ///< Memory dumper to write the savegame to. + SaveFilter *sf; ///< Filter to write the savegame to. + + ReadBuffer *reader; ///< Savegame reading buffer. + LoadFilter *lf; ///< Filter to read the savegame from. + + StringID error_str; ///< the translatable error message to show + std::string extra_msg; ///< the error message + + bool saveinprogress; ///< Whether there is currently a save in progress. + SaveModeFlags save_flags; ///< Save mode flags +}; + +static SaveLoadParams _sl; ///< Parameters used for/at saveload. + +ReadBuffer *ReadBuffer::GetCurrent() +{ + return _sl.reader; +} + +MemoryDumper *MemoryDumper::GetCurrent() +{ + return _sl.dumper; +} + +static const std::vector &ChunkHandlers() +{ + /* These define the chunks */ + extern const ChunkHandlerTable _version_ext_chunk_handlers; + extern const ChunkHandlerTable _gamelog_chunk_handlers; + extern const ChunkHandlerTable _map_chunk_handlers; + extern const ChunkHandlerTable _misc_chunk_handlers; + extern const ChunkHandlerTable _name_chunk_handlers; + extern const ChunkHandlerTable _cheat_chunk_handlers; + extern const ChunkHandlerTable _setting_chunk_handlers; + extern const ChunkHandlerTable _company_chunk_handlers; + extern const ChunkHandlerTable _engine_chunk_handlers; + extern const ChunkHandlerTable _veh_chunk_handlers; + extern const ChunkHandlerTable _waypoint_chunk_handlers; + extern const ChunkHandlerTable _depot_chunk_handlers; + extern const ChunkHandlerTable _order_chunk_handlers; + extern const ChunkHandlerTable _town_chunk_handlers; + extern const ChunkHandlerTable _sign_chunk_handlers; + extern const ChunkHandlerTable _station_chunk_handlers; + extern const ChunkHandlerTable _industry_chunk_handlers; + extern const ChunkHandlerTable _economy_chunk_handlers; + extern const ChunkHandlerTable _subsidy_chunk_handlers; + extern const ChunkHandlerTable _cargomonitor_chunk_handlers; + extern const ChunkHandlerTable _goal_chunk_handlers; + extern const ChunkHandlerTable _story_page_chunk_handlers; + extern const ChunkHandlerTable _league_chunk_handlers; + extern const ChunkHandlerTable _ai_chunk_handlers; + extern const ChunkHandlerTable _game_chunk_handlers; + extern const ChunkHandlerTable _animated_tile_chunk_handlers; + extern const ChunkHandlerTable _newgrf_chunk_handlers; + extern const ChunkHandlerTable _group_chunk_handlers; + extern const ChunkHandlerTable _cargopacket_chunk_handlers; + extern const ChunkHandlerTable _autoreplace_chunk_handlers; + extern const ChunkHandlerTable _labelmaps_chunk_handlers; + extern const ChunkHandlerTable _linkgraph_chunk_handlers; + extern const ChunkHandlerTable _airport_chunk_handlers; + extern const ChunkHandlerTable _object_chunk_handlers; + extern const ChunkHandlerTable _persistent_storage_chunk_handlers; + extern const ChunkHandlerTable _trace_restrict_chunk_handlers; + extern const ChunkHandlerTable _signal_chunk_handlers; + extern const ChunkHandlerTable _plan_chunk_handlers; + extern const ChunkHandlerTable _template_replacement_chunk_handlers; + extern const ChunkHandlerTable _template_vehicle_chunk_handlers; + extern const ChunkHandlerTable _bridge_signal_chunk_handlers; + extern const ChunkHandlerTable _tunnel_chunk_handlers; + extern const ChunkHandlerTable _train_speed_adaptation_chunk_handlers; + extern const ChunkHandlerTable _new_signal_chunk_handlers; + extern const ChunkHandlerTable _debug_chunk_handlers; + + /** List of all chunks in a savegame. */ + static const ChunkHandlerTable _chunk_handler_tables[] = { + _version_ext_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, + _cargomonitor_chunk_handlers, + _goal_chunk_handlers, + _story_page_chunk_handlers, + _league_chunk_handlers, + _engine_chunk_handlers, + _town_chunk_handlers, + _sign_chunk_handlers, + _station_chunk_handlers, + _company_chunk_handlers, + _ai_chunk_handlers, + _game_chunk_handlers, + _animated_tile_chunk_handlers, + _newgrf_chunk_handlers, + _group_chunk_handlers, + _cargopacket_chunk_handlers, + _autoreplace_chunk_handlers, + _labelmaps_chunk_handlers, + _linkgraph_chunk_handlers, + _airport_chunk_handlers, + _object_chunk_handlers, + _persistent_storage_chunk_handlers, + _trace_restrict_chunk_handlers, + _signal_chunk_handlers, + _plan_chunk_handlers, + _template_replacement_chunk_handlers, + _template_vehicle_chunk_handlers, + _bridge_signal_chunk_handlers, + _tunnel_chunk_handlers, + _train_speed_adaptation_chunk_handlers, + _new_signal_chunk_handlers, + _debug_chunk_handlers, + }; + + static std::vector _chunk_handlers; + + if (_chunk_handlers.empty()) { + for (auto &chunk_handler_table : _chunk_handler_tables) { + for (auto &chunk_handler : chunk_handler_table) { + _chunk_handlers.push_back(chunk_handler); + } + } + } + + return _chunk_handlers; +} + +/** Null all pointers (convert index -> nullptr) */ +static void SlNullPointers() +{ + if (_sl_upstream_mode) { + upstream_sl::SlNullPointers(); + return; + } + + _sl.action = SLA_NULL; + + /* We don't want any savegame conversion code to run + * during NULLing; especially those that try to get + * pointers from other pools. */ + _sl_version = SAVEGAME_VERSION; + SlXvSetCurrentState(); + + for (auto &ch : ChunkHandlers()) { + if (ch.ptrs_proc != nullptr) { + DEBUG(sl, 3, "Nulling pointers for %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); + ch.ptrs_proc(); + } + } + + assert(_sl.action == SLA_NULL); +} + +struct ThreadSlErrorException { + StringID string; + std::string extra_msg; +}; + +/** + * Error handler. Sets everything up to show an error message and to clean + * up the mess of a partial savegame load. + * @param string The translatable error message to show. + * @param extra_msg An extra error message coming from one of the APIs. + * @note This function does never return as it throws an exception to + * break out of all the saveload code. + */ +void NORETURN SlError(StringID string, std::string extra_msg) +{ + if (IsNonMainThread() && IsNonGameThread() && _sl.action != SLA_SAVE) { + throw ThreadSlErrorException{ string, std::move(extra_msg) }; + } + + /* Distinguish between loading into _load_check_data vs. normal save/load. */ + if (_sl.action == SLA_LOAD_CHECK) { + _load_check_data.error = string; + _load_check_data.error_msg = std::move(extra_msg); + } else { + _sl.error_str = string; + _sl.extra_msg = std::move(extra_msg); + } + + /* We have to nullptr all pointers here; we might be in a state where + * the pointers are actually filled with indices, which means that + * when we access them during cleaning the pool dereferences of + * those indices will be made with segmentation faults as result. */ + if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers(); + + /* Logging could be active. */ + GamelogStopAnyAction(); + + throw std::exception(); +} + +/** + * As SlError, except that it takes a format string and additional parameters + */ +void NORETURN CDECL SlErrorFmt(StringID string, const char *msg, ...) +{ + va_list va; + va_start(va, msg); + std::string str = stdstr_vfmt(msg, va); + va_end(va); + SlError(string, std::move(str)); +} + +/** + * Error handler for corrupt savegames. Sets everything up to show the + * error message and to clean up the mess of a partial savegame load. + * @param msg Location the corruption has been spotted. + * @note This function does never return as it throws an exception to + * break out of all the saveload code. + */ +void NORETURN SlErrorCorrupt(std::string msg) +{ + SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, std::move(msg)); +} + +/** + * Issue an SlErrorCorrupt with a format string. + * @param format format string + * @param ... arguments to format string + * @note This function does never return as it throws an exception to + * break out of all the saveload code. + */ +void NORETURN CDECL SlErrorCorruptFmt(const char *format, ...) +{ + va_list va; + va_start(va, format); + std::string str = stdstr_vfmt(format, va); + va_end(va); + SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, std::move(str)); +} + +typedef void (*AsyncSaveFinishProc)(); ///< Callback for when the savegame loading is finished. +static std::atomic _async_save_finish; ///< Callback to call when the savegame loading is finished. +static std::thread _save_thread; ///< The thread we're using to compress and write a savegame + +/** + * Called by save thread to tell we finished saving. + * @param proc The callback to call when saving is done. + */ +static void SetAsyncSaveFinish(AsyncSaveFinishProc proc) +{ + if (_exit_game) return; + while (_async_save_finish.load(std::memory_order_acquire) != nullptr) CSleep(10); + + _async_save_finish.store(proc, std::memory_order_release); +} + +/** + * Handle async save finishes. + */ +void ProcessAsyncSaveFinish() +{ + AsyncSaveFinishProc proc = _async_save_finish.exchange(nullptr, std::memory_order_acq_rel); + if (proc == nullptr) return; + + proc(); + + if (_save_thread.joinable()) { + _save_thread.join(); + } +} + +/** + * Wrapper for reading a byte from the buffer. + * @return The read byte. + */ +byte SlReadByte() +{ + return _sl.reader->ReadByte(); +} + +/** + * 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 + */ +void SlSkipBytes(size_t length) +{ + return _sl.reader->SkipBytes(length); +} + +int SlReadUint16() +{ + _sl.reader->CheckBytes(2); + return _sl.reader->RawReadUint16(); +} + +uint32 SlReadUint32() +{ + _sl.reader->CheckBytes(4); + return _sl.reader->RawReadUint32(); +} + +uint64 SlReadUint64() +{ + _sl.reader->CheckBytes(8); + return _sl.reader->RawReadUint64(); +} + +/** + * Wrapper for writing a byte to the dumper. + * @param b The byte to write. + */ +void SlWriteByte(byte b) +{ + _sl.dumper->WriteByte(b); +} + +void SlWriteUint16(uint16 v) +{ + _sl.dumper->CheckBytes(2); + _sl.dumper->RawWriteUint16(v); +} + +void SlWriteUint32(uint32 v) +{ + _sl.dumper->CheckBytes(4); + _sl.dumper->RawWriteUint32(v); +} + +void SlWriteUint64(uint64 v) +{ + _sl.dumper->CheckBytes(8); + _sl.dumper->RawWriteUint64(v); +} + +/** + * Returns number of bytes read so far + * May only be called during a load/load check action + */ +size_t SlGetBytesRead() +{ + assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK); + return _sl.reader->GetSize(); +} + +/** + * Returns number of bytes written so far + * May only be called during a save action + */ +size_t SlGetBytesWritten() +{ + assert(_sl.action == SLA_SAVE); + return _sl.dumper->GetSize(); +} + +/** + * 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)) { + i &= ~0x10; + if (HasBit(i, 3)) { + SlErrorCorrupt("Unsupported gamma"); + } + i = SlReadByte(); // 32 bits only. + } + 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 + * 11110--- xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx + * We could extend the scheme ad infinum to support arbitrarily + * large chunks, but as sizeof(size_t) == 4 is still very common + * we don't support anything above 32 bits. That's why in the last + * case the 3 most significant bits are unused. + * @param i Index being written + */ + +static void SlWriteSimpleGamma(size_t i) +{ + if (i >= (1 << 7)) { + if (i >= (1 << 14)) { + if (i >= (1 << 21)) { + if (i >= (1 << 28)) { + assert(i <= UINT32_MAX); // We can only support 32 bits for now. + SlWriteByte((byte)(0xF0)); + SlWriteByte((byte)(i >> 24)); + } else { + 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)) + (i >= (1 << 28)); +} + +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); +} + +/** + * 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 uint SlCalcConvMemLen(VarType conv) +{ + static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0}; + + switch (GetVarMemType(conv)) { + case SLE_VAR_STRB: + case SLE_VAR_STR: + case SLE_VAR_STRQ: + return SlReadArrayLength(); + + default: + uint8 type = GetVarMemType(conv) >> 4; + assert(type < lengthof(conv_mem_size)); + return conv_mem_size[type]; + } +} + +/** + * 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) +{ + uint8 type = GetVarFileType(conv); + if (type == SLE_FILE_VEHORDERID) return SlXvIsFeaturePresent(XSLFI_MORE_VEHICLE_ORDERS) ? 2 : 1; + static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2}; + assert(type < lengthof(conv_file_size)); + return conv_file_size[type]; +} + +/** Return the size in bytes of a reference (pointer) */ +static inline size_t SlCalcRefLen() +{ + return IsSavegameVersionBefore(SLV_69) ? 2 : 4; +} + +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 && _sl.reader->GetSize() != _next_offs) { + DEBUG(sl, 1, "Invalid chunk size: " PRINTF_SIZE " != " PRINTF_SIZE, _sl.reader->GetSize(), _next_offs); + SlErrorCorruptFmt("Invalid chunk size iterating array - expected to be at position " PRINTF_SIZE ", actually at " PRINTF_SIZE, _next_offs, _sl.reader->GetSize()); + } + + for (;;) { + uint length = SlReadArrayLength(); + if (length == 0) { + _next_offs = 0; + return -1; + } + + _sl.obj_len = --length; + _next_offs = _sl.reader->GetSize() + 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; + } +} + +/** + * Skip an array or sparse array + */ +void SlSkipArray() +{ + while (SlIterateArray() != -1) { + SlSkipBytes(_next_offs - _sl.reader->GetSize()); + } +} + +/** + * 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.action == SLA_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 + * + * If we have more than 28 bits, use an extra uint32 and + * signal this using the extended chunk header */ +#ifdef POINTER_IS_64BIT + assert(length < (1LL << 32)); +#endif + if (length >= (1 << 28)) { + /* write out extended chunk header */ + SlWriteByte(CH_EXT_HDR); + SlWriteUint32(static_cast(SLCEHF_BIG_RIFF)); + } + SlWriteUint32(static_cast((length & 0xFFFFFF) | ((length >> 24) << 28))); + if (length >= (1 << 28)) { + SlWriteUint32(static_cast(length >> 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; + + default: NOT_REACHED(); + } +} + +/** + * 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; + + switch (_sl.action) { + case SLA_LOAD_CHECK: + case SLA_LOAD: + _sl.reader->CopyBytes(p, length); + break; + case SLA_SAVE: + _sl.dumper->CopyBytes(p, length); + break; + default: NOT_REACHED(); + } +} + +/** 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 (*(const bool *)ptr != 0); + case SLE_VAR_I8: return *(const int8 *)ptr; + case SLE_VAR_U8: return *(const byte *)ptr; + case SLE_VAR_I16: return *(const int16 *)ptr; + case SLE_VAR_U16: return *(const uint16*)ptr; + case SLE_VAR_I32: return *(const int32 *)ptr; + case SLE_VAR_U32: return *(const uint32*)ptr; + case SLE_VAR_I64: return *(const int64 *)ptr; + case SLE_VAR_U64: return *(const uint64*)ptr; + case SLE_VAR_NULL:return 0; + default: NOT_REACHED(); + } +} + +/** + * 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: *reinterpret_cast(ptr) = CopyFromOldName(val); break; + case SLE_VAR_CNAME: *(TinyString*)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 + */ +template +static void SlSaveLoadConvGeneric(void *ptr, VarType conv) +{ + switch (action) { + case SLA_SAVE: { + int64 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_VEHORDERID: + 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(); + } + break; + } + case SLA_LOAD_CHECK: + case SLA_LOAD: { + int64 x; + /* 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; + case SLE_FILE_VEHORDERID: + if (SlXvIsFeaturePresent(XSLFI_MORE_VEHICLE_ORDERS)) { + x = (uint16)SlReadUint16(); + } else { + VehicleOrderID id = (byte)SlReadByte(); + x = (id == 0xFF) ? INVALID_VEH_ORDER_ID : id; + } + break; + default: NOT_REACHED(); + } + + /* Write The value to the struct. These ARE endian safe. */ + WriteValue(ptr, conv, x); + break; + } + case SLA_PTRS: break; + case SLA_NULL: break; + default: NOT_REACHED(); + } +} + +void SlSaveLoadConv(void *ptr, VarType conv) +{ + switch (_sl.action) { + case SLA_SAVE: + SlSaveLoadConvGeneric(ptr, conv); + return; + case SLA_LOAD_CHECK: + case SLA_LOAD: + SlSaveLoadConvGeneric(ptr, conv); + return; + case SLA_PTRS: + case SLA_NULL: + return; + default: NOT_REACHED(); + } +} + +/** + * 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 == nullptr) return 0; + return std::min(strlen(ptr), length - 1); +} + +/** + * Calculate the gross length of the std::string that it + * will occupy in the savegame. This includes the real length, + * and the length that the index will occupy. + * @param str reference to the std::string + * @return return the gross length of the string + */ +static inline size_t SlCalcStdStrLen(const std::string &str) +{ + return str.size() + SlGetArrayLength(str.size()); // also include the length of the index +} + +/** + * 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 * const *)ptr; + len = SIZE_MAX; + break; + case SLE_VAR_STRB: + 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) +{ + switch (_sl.action) { + case SLA_SAVE: { + size_t len; + switch (GetVarMemType(conv)) { + default: NOT_REACHED(); + case SLE_VAR_STRB: + 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); + break; + } + case SLA_LOAD_CHECK: + case SLA_LOAD: { + size_t len = SlReadArrayLength(); + + switch (GetVarMemType(conv)) { + default: NOT_REACHED(); + case SLE_VAR_NULL: + SlSkipBytes(len); + return; + case SLE_VAR_STRB: + 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 = nullptr; + return; + } else { + *(char **)ptr = MallocT(len + 1); // terminating '\0' + ptr = *(char **)ptr; + SlCopyBytes(ptr, len); + } + break; + } + + ((char *)ptr)[len] = '\0'; // properly terminate the string + StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK; + if ((conv & SLF_ALLOW_CONTROL) != 0) { + settings = settings | SVS_ALLOW_CONTROL_CODE; + if (IsSavegameVersionBefore(SLV_169)) { + str_fix_scc_encoded((char *)ptr, (char *)ptr + len); + } + } + if ((conv & SLF_ALLOW_NEWLINE) != 0) { + settings = settings | SVS_ALLOW_NEWLINE; + } + StrMakeValidInPlace((char *)ptr, (char *)ptr + len, settings); + break; + } + case SLA_PTRS: break; + case SLA_NULL: break; + default: NOT_REACHED(); + } +} + +/** + * Save/Load a \c std::string. + * @param ptr the string being manipulated + * @param conv must be SLE_FILE_STRING + */ +static void SlStdString(std::string &str, VarType conv) +{ + switch (_sl.action) { + case SLA_SAVE: { + SlWriteArrayLength(str.size()); + SlCopyBytes(str.data(), str.size()); + break; + } + case SLA_LOAD_CHECK: + case SLA_LOAD: { + size_t len = SlReadArrayLength(); + if (GetVarMemType(conv) == SLE_VAR_NULL) { + SlSkipBytes(len); + return; + } + + str.resize(len); + SlCopyBytes(str.data(), len); + + StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK; + if ((conv & SLF_ALLOW_CONTROL) != 0) { + settings = settings | SVS_ALLOW_CONTROL_CODE; + if (IsSavegameVersionBefore(SLV_169)) { + char *buf = str.data(); + str.resize(str_fix_scc_encoded(buf, buf + str.size()) - buf); + } + } + if ((conv & SLF_ALLOW_NEWLINE) != 0) { + settings = settings | SVS_ALLOW_NEWLINE; + } + StrMakeValidInPlace(str, settings); + break; + } + case SLA_PTRS: break; + case SLA_NULL: break; + default: NOT_REACHED(); + } +} + +/** + * 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) +{ + if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return; + + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcArrayLen(length, conv)); + } + + /* 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.action != SLA_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 + } + } +} + + +/** + * 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 nullptr item has value 0, and all + * indices 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 size_t ReferenceToInt(const void *obj, SLRefType rt) +{ + assert(_sl.action == SLA_SAVE); + + if (obj == nullptr) return 0; + + switch (rt) { + case REF_VEHICLE_OLD: // Old vehicles we save as new ones + case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1; + case REF_TEMPLATE_VEHICLE: return ((const TemplateVehicle*)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; + case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1; + case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1; + case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1; + default: NOT_REACHED(); + } +} + +/** + * Pointers cannot be loaded from a savegame, so this function + * gets the index from the savegame and returns the appropriate + * pointer from the already loaded base. + * Remember that an index of 0 is a nullptr pointer so all indices + * 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(size_t index, SLRefType rt) +{ + static_assert(sizeof(size_t) <= sizeof(void *)); + + assert(_sl.action == SLA_PTRS); + + /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE, + * and should be loaded like that */ + if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(SLV_4, 4)) { + rt = REF_VEHICLE; + } + + /* No need to look up nullptr pointers, just return immediately */ + if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return nullptr; + + /* Correct index. Old vehicles were saved differently: + * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */ + if (rt != REF_VEHICLE_OLD) index--; + + switch (rt) { + case REF_ORDERLIST: + if (OrderList::IsValidID(index)) return OrderList::Get(index); + SlErrorCorrupt("Referencing invalid OrderList"); + + case REF_ORDER: + if (Order::IsValidID(index)) return Order::Get(index); + /* in old versions, invalid order was used to mark end of order list */ + if (IsSavegameVersionBefore(SLV_5, 2)) return nullptr; + SlErrorCorrupt("Referencing invalid Order"); + + case REF_VEHICLE_OLD: + case REF_VEHICLE: + if (Vehicle::IsValidID(index)) return Vehicle::Get(index); + SlErrorCorrupt("Referencing invalid Vehicle"); + + case REF_TEMPLATE_VEHICLE: + if (TemplateVehicle::IsValidID(index)) return TemplateVehicle::Get(index); + SlErrorCorrupt("Referencing invalid TemplateVehicle"); + + case REF_STATION: + if (Station::IsValidID(index)) return Station::Get(index); + SlErrorCorrupt("Referencing invalid Station"); + + case REF_TOWN: + if (Town::IsValidID(index)) return Town::Get(index); + SlErrorCorrupt("Referencing invalid Town"); + + case REF_ROADSTOPS: + if (RoadStop::IsValidID(index)) return RoadStop::Get(index); + SlErrorCorrupt("Referencing invalid RoadStop"); + + case REF_ENGINE_RENEWS: + if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index); + SlErrorCorrupt("Referencing invalid EngineRenew"); + + case REF_CARGO_PACKET: + if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index); + SlErrorCorrupt("Referencing invalid CargoPacket"); + + case REF_STORAGE: + if (PersistentStorage::IsValidID(index)) return PersistentStorage::Get(index); + SlErrorCorrupt("Referencing invalid PersistentStorage"); + + case REF_LINK_GRAPH: + if (LinkGraph::IsValidID(index)) return LinkGraph::Get(index); + SlErrorCorrupt("Referencing invalid LinkGraph"); + + case REF_LINK_GRAPH_JOB: + if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index); + SlErrorCorrupt("Referencing invalid LinkGraphJob"); + + default: NOT_REACHED(); + } +} + +/** + * Handle conversion for references. + * @param ptr The object being filled/read. + * @param conv VarType type of the current element of the struct. + */ +void SlSaveLoadRef(void *ptr, VarType conv) +{ + switch (_sl.action) { + case SLA_SAVE: + SlWriteUint32((uint32)ReferenceToInt(*(void **)ptr, (SLRefType)conv)); + break; + case SLA_LOAD_CHECK: + case SLA_LOAD: + *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); + break; + case SLA_PTRS: + *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv); + break; + case SLA_NULL: + *(void **)ptr = nullptr; + break; + default: NOT_REACHED(); + } +} + +/** + * Template class to help with list-like types. + */ +template typename Tstorage, typename Tvar, typename Tallocator = std::allocator> +class SlStorageHelper { + typedef Tstorage SlStorageT; +public: + /** + * Internal templated helper to return the size in bytes of a list-like type. + * @param storage The storage to find the size of + * @param conv VarType type of variable that is used for calculating the size + * @param cmd The SaveLoadType ware are saving/loading. + */ + static size_t SlCalcLen(const void *storage, VarType conv, SaveLoadType cmd = SL_VAR) + { + assert(cmd == SL_VAR || cmd == SL_REF); + + const SlStorageT *list = static_cast(storage); + + int type_size = SlCalcConvFileLen(SLE_FILE_U32); // Size of the length of the list. + int item_size = SlCalcConvFileLen(cmd == SL_VAR ? conv : (VarType)SLE_FILE_U32); + return list->size() * item_size + type_size; + } + + static void SlSaveLoadMember(SaveLoadType cmd, Tvar *item, VarType conv) + { + switch (cmd) { + case SL_VAR: SlSaveLoadConv(item, conv); break; + case SL_REF: SlSaveLoadRef(item, conv); break; + default: + NOT_REACHED(); + } + } + + /** + * Internal templated helper to save/load a list-like type. + * @param storage The storage being manipulated. + * @param conv VarType type of variable that is used for calculating the size. + * @param cmd The SaveLoadType ware are saving/loading. + */ + static void SlSaveLoad(void *storage, VarType conv, SaveLoadType cmd = SL_VAR) + { + assert(cmd == SL_VAR || cmd == SL_REF); + + SlStorageT *list = static_cast(storage); + + switch (_sl.action) { + case SLA_SAVE: + SlWriteUint32((uint32)list->size()); + + for (auto &item : *list) { + SlSaveLoadMember(cmd, &item, conv); + } + break; + + case SLA_LOAD_CHECK: + case SLA_LOAD: { + size_t length; + switch (cmd) { + case SL_VAR: length = SlReadUint32(); break; + case SL_REF: length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); break; + default: NOT_REACHED(); + } + + /* Load each value and push to the end of the storage. */ + for (size_t i = 0; i < length; i++) { + Tvar &data = list->emplace_back(); + SlSaveLoadMember(cmd, &data, conv); + } + break; + } + + case SLA_PTRS: + for (auto &item : *list) { + SlSaveLoadMember(cmd, &item, conv); + } + break; + + case SLA_NULL: + list->clear(); + break; + + default: NOT_REACHED(); + } + } +}; + +/** + * Return the size in bytes of a list. + * @param list The std::list to find the size of. + * @param conv VarType type of variable that is used for calculating the size. + */ + template +static inline size_t SlCalcRefListLen(const void *list) +{ + const PtrList *l = (const PtrList *) list; + + int type_size = IsSavegameVersionBefore(SLV_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; +} + +/** + * Return the size in bytes of a list + * @param list The std::list to find the size of + */ + template +static inline size_t SlCalcVarListLen(const void *list, size_t item_size) +{ + const PtrList *l = (const PtrList *) list; + /* Each entry is saved as item_size bytes, plus 4 bytes are used for the length + * of the list */ + return l->size() * item_size + 4; +} + +/** + * Save/Load a list. + * @param list The list being manipulated. + * @param conv VarType type of variable that is used for calculating the size. + */ +template +static void SlRefList(void *list, SLRefType conv) +{ + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcRefListLen(list)); + } + + PtrList *l = (PtrList *)list; + + switch (_sl.action) { + case SLA_SAVE: { + SlWriteUint32((uint32)l->size()); + + typename PtrList::iterator iter; + for (iter = l->begin(); iter != l->end(); ++iter) { + void *ptr = *iter; + SlWriteUint32((uint32)ReferenceToInt(ptr, conv)); + } + break; + } + case SLA_LOAD_CHECK: + case SLA_LOAD: { + size_t length = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); + + /* Load each reference and push to the end of the list */ + for (size_t i = 0; i < length; i++) { + size_t data = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); + l->push_back((void *)data); + } + break; + } + case SLA_PTRS: { + PtrList temp = *l; + + l->clear(); + typename PtrList::iterator iter; + for (iter = temp.begin(); iter != temp.end(); ++iter) { + void *ptr = IntToReference((size_t)*iter, conv); + l->push_back(ptr); + } + break; + } + case SLA_NULL: + l->clear(); + break; + default: NOT_REACHED(); + } +} + +/** + * Save/Load a list. + * @param list The list being manipulated + * @param conv VarType type of the list + */ +template +static void SlVarList(void *list, VarType conv) +{ + const size_t size_len = SlCalcConvMemLen(conv); + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcVarListLen(list, size_len)); + } + + PtrList *l = (PtrList *)list; + + switch (_sl.action) { + case SLA_SAVE: { + SlWriteUint32((uint32)l->size()); + + typename PtrList::iterator iter; + for (iter = l->begin(); iter != l->end(); ++iter) { + SlSaveLoadConv(&(*iter), conv); + } + break; + } + case SLA_LOAD_CHECK: + case SLA_LOAD: { + size_t length = SlReadUint32(); + l->resize(length); + + typename PtrList::iterator iter; + iter = l->begin(); + + for (size_t i = 0; i < length; i++) { + SlSaveLoadConv(&(*iter), conv); + ++iter; + } + break; + } + case SLA_PTRS: break; + case SLA_NULL: + l->clear(); + break; + default: NOT_REACHED(); + } +} + +/** + * Return the size in bytes of a std::deque. + * @param deque The std::deque to find the size of + * @param conv VarType type of variable that is used for calculating the size + */ +static inline size_t SlCalcDequeLen(const void *deque, VarType conv) +{ + switch (GetVarMemType(conv)) { + case SLE_VAR_BL: return SlStorageHelper::SlCalcLen(deque, conv); + case SLE_VAR_I8: return SlStorageHelper::SlCalcLen(deque, conv); + case SLE_VAR_U8: return SlStorageHelper::SlCalcLen(deque, conv); + case SLE_VAR_I16: return SlStorageHelper::SlCalcLen(deque, conv); + case SLE_VAR_U16: return SlStorageHelper::SlCalcLen(deque, conv); + case SLE_VAR_I32: return SlStorageHelper::SlCalcLen(deque, conv); + case SLE_VAR_U32: return SlStorageHelper::SlCalcLen(deque, conv); + case SLE_VAR_I64: return SlStorageHelper::SlCalcLen(deque, conv); + case SLE_VAR_U64: return SlStorageHelper::SlCalcLen(deque, conv); + default: NOT_REACHED(); + } +} + +/** + * Save/load a std::deque. + * @param deque The std::deque being manipulated + * @param conv VarType type of variable that is used for calculating the size + */ +static void SlDeque(void *deque, VarType conv) +{ + switch (GetVarMemType(conv)) { + case SLE_VAR_BL: SlStorageHelper::SlSaveLoad(deque, conv); break; + case SLE_VAR_I8: SlStorageHelper::SlSaveLoad(deque, conv); break; + case SLE_VAR_U8: SlStorageHelper::SlSaveLoad(deque, conv); break; + case SLE_VAR_I16: SlStorageHelper::SlSaveLoad(deque, conv); break; + case SLE_VAR_U16: SlStorageHelper::SlSaveLoad(deque, conv); break; + case SLE_VAR_I32: SlStorageHelper::SlSaveLoad(deque, conv); break; + case SLE_VAR_U32: SlStorageHelper::SlSaveLoad(deque, conv); break; + case SLE_VAR_I64: SlStorageHelper::SlSaveLoad(deque, conv); break; + case SLE_VAR_U64: SlStorageHelper::SlSaveLoad(deque, conv); break; + default: NOT_REACHED(); + } +} + +/** Are we going to save this object or not? */ +static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld) +{ + return sld.ext_feature_test.IsFeaturePresent(_sl_version, sld.version_from, sld.version_to); +} + +/** + * Calculate the size of an object. + * @param object to be measured. + * @param slt The SaveLoad table with objects to save/load. + * @return size of given object. + */ +size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt) +{ + size_t length = 0; + + /* Need to determine the length and write a length tag. */ + for (auto &sld : slt) { + length += SlCalcObjMemberLength(object, sld); + } + return length; +} + +size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld) +{ + assert(_sl.action == SLA_SAVE); + + switch (sld.cmd) { + case SL_VAR: + case SL_REF: + case SL_ARR: + case SL_STR: + case SL_REFLIST: + case SL_PTRDEQ: + case SL_VEC: + case SL_DEQUE: + case SL_STDSTR: + case SL_VARVEC: + /* 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_REFLIST: return SlCalcRefListLen>(GetVariableAddress(object, sld)); + case SL_PTRDEQ: return SlCalcRefListLen>(GetVariableAddress(object, sld)); + case SL_VEC: return SlCalcRefListLen>(GetVariableAddress(object, sld)); + case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld.conv); + case SL_VARVEC: { + const size_t size_len = SlCalcConvMemLen(sld.conv); + switch (size_len) { + case 1: return SlCalcVarListLen>(GetVariableAddress(object, sld), 1); + case 2: return SlCalcVarListLen>(GetVariableAddress(object, sld), 2); + case 4: return SlCalcVarListLen>(GetVariableAddress(object, sld), 4); + case 8: return SlCalcVarListLen>(GetVariableAddress(object, sld), 8); + default: NOT_REACHED(); + } + } + case SL_STDSTR: return SlCalcStdStrLen(*static_cast(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)); + case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription()); + default: NOT_REACHED(); + } + return 0; +} + +/** + * Check whether the variable size of the variable in the saveload configuration + * matches with the actual variable size. + * @param sld The saveload configuration to test. + */ +[[maybe_unused]] static bool IsVariableSizeRight(const SaveLoad &sld) +{ + if (GetVarMemType(sld.conv) == SLE_VAR_NULL) return true; + + switch (sld.cmd) { + case SL_VAR: + switch (GetVarMemType(sld.conv)) { + case SLE_VAR_BL: + return sld.size == sizeof(bool); + case SLE_VAR_I8: + case SLE_VAR_U8: + return sld.size == sizeof(int8); + case SLE_VAR_I16: + case SLE_VAR_U16: + return sld.size == sizeof(int16); + case SLE_VAR_I32: + case SLE_VAR_U32: + return sld.size == sizeof(int32); + case SLE_VAR_I64: + case SLE_VAR_U64: + return sld.size == sizeof(int64); + case SLE_VAR_NAME: + return sld.size == sizeof(std::string); + default: + return sld.size == sizeof(void *); + } + case SL_REF: + /* These should all be pointer sized. */ + return sld.size == sizeof(void *); + + case SL_STR: + /* These should be pointer sized, or fixed array. */ + return sld.size == sizeof(void *) || sld.size == sld.length; + + case SL_STDSTR: + /* These should be all pointers to std::string. */ + return sld.size == sizeof(std::string); + + default: + return true; + } +} + +void SlFilterObject(const SaveLoadTable &slt, std::vector &save); + +static void SlFilterObjectMember(const SaveLoad &sld, std::vector &save) +{ + assert(IsVariableSizeRight(sld)); + + switch (sld.cmd) { + case SL_VAR: + case SL_REF: + case SL_ARR: + case SL_STR: + case SL_REFLIST: + case SL_PTRDEQ: + case SL_VEC: + case SL_DEQUE: + case SL_STDSTR: + case SL_VARVEC: + /* CONDITIONAL saveload types depend on the savegame version */ + if (!SlIsObjectValidInSavegame(sld)) return; + + switch (_sl.action) { + case SLA_SAVE: + case SLA_LOAD_CHECK: + case SLA_LOAD: + break; + case SLA_PTRS: + case SLA_NULL: + switch (sld.cmd) { + case SL_REF: + case SL_REFLIST: + case SL_PTRDEQ: + case SL_VEC: + break; + + /* non-ptr types do not require SLA_PTRS or SLA_NULL actions */ + default: + return; + } + break; + default: NOT_REACHED(); + } + + save.push_back(sld); + break; + + /* SL_WRITEBYTE writes a value to the savegame to identify the type of an object. + * When loading, the value is read explictly with SlReadByte() to determine which + * object description to use. */ + case SL_WRITEBYTE: + if (_sl.action == SLA_SAVE) save.push_back(sld); + break; + + /* SL_VEH_INCLUDE loads common code for vehicles */ + case SL_VEH_INCLUDE: + SlFilterObject(GetVehicleDescription(VEH_END), save); + break; + + case SL_ST_INCLUDE: + SlFilterObject(GetBaseStationDescription(), save); + break; + + default: NOT_REACHED(); + } +} + +void SlFilterObject(const SaveLoadTable &slt, std::vector &save) +{ + for (auto &sld : slt) { + SlFilterObjectMember(sld, save); + } +} + +std::vector SlFilterObject(const SaveLoadTable &slt) +{ + std::vector save; + SlFilterObject(slt, save); + return save; +} + +template +bool SlObjectMemberGeneric(void *object, const SaveLoad &sld) +{ + void *ptr = GetVariableAddress(object, sld); + + if (check_version) assert(IsVariableSizeRight(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_REFLIST: + case SL_PTRDEQ: + case SL_VEC: + case SL_DEQUE: + case SL_STDSTR: + case SL_VARVEC: + /* CONDITIONAL saveload types depend on the savegame version */ + if (check_version) { + if (!SlIsObjectValidInSavegame(sld)) return false; + } + + switch (sld.cmd) { + case SL_VAR: SlSaveLoadConvGeneric(ptr, conv); break; + case SL_REF: // Reference variable, translate + switch (action) { + case SLA_SAVE: + SlWriteUint32((uint32)ReferenceToInt(*(void **)ptr, (SLRefType)conv)); + break; + case SLA_LOAD_CHECK: + case SLA_LOAD: + *(size_t *)ptr = IsSavegameVersionBefore(SLV_69) ? SlReadUint16() : SlReadUint32(); + break; + case SLA_PTRS: + *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv); + break; + case SLA_NULL: + *(void **)ptr = nullptr; + break; + default: NOT_REACHED(); + } + break; + case SL_ARR: SlArray(ptr, sld.length, conv); break; + case SL_STR: SlString(ptr, sld.length, sld.conv); break; + case SL_REFLIST: SlRefList>(ptr, (SLRefType)conv); break; + case SL_PTRDEQ: SlRefList>(ptr, (SLRefType)conv); break; + case SL_VEC: SlRefList>(ptr, (SLRefType)conv); break; + case SL_DEQUE: SlDeque(ptr, conv); break; + case SL_VARVEC: { + const size_t size_len = SlCalcConvMemLen(sld.conv); + switch (size_len) { + case 1: SlVarList>(ptr, conv); break; + case 2: SlVarList>(ptr, conv); break; + case 4: SlVarList>(ptr, conv); break; + case 8: SlVarList>(ptr, conv); break; + default: NOT_REACHED(); + } + break; + } + case SL_STDSTR: SlStdString(*static_cast(ptr), sld.conv); break; + default: NOT_REACHED(); + } + break; + + /* SL_WRITEBYTE writes a value to the savegame to identify the type of an object. + * When loading, the value is read explicitly with SlReadByte() to determine which + * object description to use. */ + case SL_WRITEBYTE: + switch (action) { + case SLA_SAVE: SlWriteByte(*(uint8 *)ptr); break; + case SLA_LOAD_CHECK: + case SLA_LOAD: + case SLA_PTRS: + case SLA_NULL: break; + default: NOT_REACHED(); + } + break; + + /* SL_VEH_INCLUDE loads common code for vehicles */ + case SL_VEH_INCLUDE: + SlObject(ptr, GetVehicleDescription(VEH_END)); + break; + + case SL_ST_INCLUDE: + SlObject(ptr, GetBaseStationDescription()); + break; + + default: NOT_REACHED(); + } + return true; +} + +bool SlObjectMember(void *object, const SaveLoad &sld) +{ + switch (_sl.action) { + case SLA_SAVE: + return SlObjectMemberGeneric(object, sld); + case SLA_LOAD_CHECK: + case SLA_LOAD: + return SlObjectMemberGeneric(object, sld); + case SLA_PTRS: + return SlObjectMemberGeneric(object, sld); + case SLA_NULL: + return SlObjectMemberGeneric(object, sld); + default: NOT_REACHED(); + } +} + +/** + * Main SaveLoad function. + * @param object The object that is being saved or loaded. + * @param slt The SaveLoad table with objects to save/load. + */ +void SlObject(void *object, const SaveLoadTable &slt) +{ + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcObjLength(object, slt)); + } + + for (auto &sld : slt) { + SlObjectMember(object, sld); + } +} + +template +void SlObjectIterateBase(void *object, const SaveLoadTable &slt) +{ + for (auto &sld : slt) { + SlObjectMemberGeneric(object, sld); + } +} + +void SlObjectSaveFiltered(void *object, const SaveLoadTable &slt) +{ + if (_sl.need_length != NL_NONE) { + _sl.need_length = NL_NONE; + _sl.dumper->StartAutoLength(); + SlObjectIterateBase(object, slt); + auto result = _sl.dumper->StopAutoLength(); + _sl.need_length = NL_WANTLENGTH; + SlSetLength(result.second); + _sl.dumper->CopyBytes(result.first, result.second); + } else { + SlObjectIterateBase(object, slt); + } +} + +void SlObjectLoadFiltered(void *object, const SaveLoadTable &slt) +{ + SlObjectIterateBase(object, slt); +} + +void SlObjectPtrOrNullFiltered(void *object, const SaveLoadTable &slt) +{ + switch (_sl.action) { + case SLA_PTRS: + SlObjectIterateBase(object, slt); + return; + case SLA_NULL: + SlObjectIterateBase(object, slt); + return; + default: NOT_REACHED(); + } +} + +/** + * Save or Load (a list of) global variables. + * @param slt The SaveLoad table with objects to save/load. + */ +void SlGlobList(const SaveLoadTable &slt) +{ + SlObject(nullptr, slt); +} + +/** + * 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) +{ + assert(_sl.action == SLA_SAVE); + assert(_sl.need_length == NL_WANTLENGTH); + + _sl.need_length = NL_NONE; + _sl.dumper->StartAutoLength(); + proc(arg); + auto result = _sl.dumper->StopAutoLength(); + /* Setup length */ + _sl.need_length = NL_WANTLENGTH; + SlSetLength(result.second); + _sl.dumper->CopyBytes(result.first, result.second); +} + +/** + * Run proc, saving result to a std::vector + * @param proc The callback procedure that is called + * @param arg The variable that will be used for the callback procedure + */ +std::vector SlSaveToVector(AutolengthProc *proc, void *arg) +{ + assert(_sl.action == SLA_SAVE); + NeedLength orig_need_length = _sl.need_length; + + _sl.need_length = NL_NONE; + _sl.dumper->StartAutoLength(); + proc(arg); + auto result = _sl.dumper->StopAutoLength(); + /* Setup length */ + _sl.need_length = orig_need_length; + return std::vector(result.first, result.first + result.second); +} + +SlLoadFromBufferState SlLoadFromBufferSetup(const byte *buffer, size_t length) +{ + assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK); + + SlLoadFromBufferState state; + + state.old_obj_len = _sl.obj_len; + _sl.obj_len = length; + + ReadBuffer *reader = ReadBuffer::GetCurrent(); + state.old_bufp = reader->bufp; + state.old_bufe = reader->bufe; + reader->bufp = const_cast(buffer); + reader->bufe = const_cast(buffer) + length; + + return state; +} + +void SlLoadFromBufferRestore(const SlLoadFromBufferState &state, const byte *buffer, size_t length) +{ + ReadBuffer *reader = ReadBuffer::GetCurrent(); + if (reader->bufp != reader->bufe || reader->bufe != buffer + length) { + SlErrorCorrupt("SlLoadFromBuffer: Wrong number of bytes read"); + } + + _sl.obj_len = state.old_obj_len; + reader->bufp = state.old_bufp; + reader->bufe = state.old_bufe; +} + +/* + * Notes on extended chunk header: + * + * If the chunk type is CH_EXT_HDR (15), then a u32 flags field follows. + * This flag field may define additional fields which follow the flags field in future. + * The standard chunk header follows, though it my be modified by the flags field. + * At present SLCEHF_BIG_RIFF increases the RIFF size limit to a theoretical 60 bits, + * by adding a further u32 field for the high bits after the existing RIFF size field. + */ + +inline void SlRIFFSpringPPCheck(size_t len) +{ + if (_sl_maybe_springpp) { + _sl_maybe_springpp = false; + if (len == 0) { + extern void SlXvSpringPPSpecialSavegameVersions(); + SlXvSpringPPSpecialSavegameVersions(); + } else if (_sl_version > MAX_LOAD_SAVEGAME_VERSION) { + SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME); + } else if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) { + SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK); + } + } +} + +/** + * 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) +{ + if (ch.special_proc != nullptr) { + if (ch.special_proc(ch.id, CSLSO_PRE_LOAD) == CSLSOR_LOAD_CHUNK_CONSUMED) return; + } + + byte m = SlReadByte(); + size_t len; + size_t endoffs; + + _sl.block_mode = m; + _sl.obj_len = 0; + + SaveLoadChunkExtHeaderFlags ext_flags = static_cast(0); + if ((m & 0xF) == CH_EXT_HDR) { + ext_flags = static_cast(SlReadUint32()); + + /* read in real header */ + m = SlReadByte(); + _sl.block_mode = m; + } + + switch (m) { + case CH_ARRAY: + _sl.array_index = 0; + ch.load_proc(); + if (_next_offs != 0) SlErrorCorrupt("Invalid array length"); + break; + case CH_SPARSE_ARRAY: + ch.load_proc(); + if (_next_offs != 0) SlErrorCorrupt("Invalid array length"); + break; + default: + if ((m & 0xF) == CH_RIFF) { + /* Read length */ + len = (SlReadByte() << 16) | ((m >> 4) << 24); + len += SlReadUint16(); + SlRIFFSpringPPCheck(len); + if (SlXvIsFeaturePresent(XSLFI_RIFF_HEADER_60_BIT)) { + if (len != 0) { + SlErrorCorrupt("RIFF chunk too large"); + } + len = SlReadUint32(); + } + if (ext_flags & SLCEHF_BIG_RIFF) { + len |= SlReadUint32() << 28; + } + + _sl.obj_len = len; + endoffs = _sl.reader->GetSize() + len; + ch.load_proc(); + if (_sl.reader->GetSize() != endoffs) { + DEBUG(sl, 1, "Invalid chunk size: " PRINTF_SIZE " != " PRINTF_SIZE ", (" PRINTF_SIZE ")", _sl.reader->GetSize(), endoffs, len); + SlErrorCorruptFmt("Invalid chunk size - expected to be at position " PRINTF_SIZE ", actually at " PRINTF_SIZE ", length: " PRINTF_SIZE, + endoffs, _sl.reader->GetSize(), len); + } + } else { + SlErrorCorrupt("Invalid chunk type"); + } + break; + } +} + +/** + * Load a chunk of data for checking savegames. + * If the chunkhandler is nullptr, the chunk is skipped. + * @param ch The chunkhandler that will be used for the operation, this may be nullptr + */ +static void SlLoadCheckChunk(const ChunkHandler *ch) +{ + if (ch && ch->special_proc != nullptr) { + if (ch->special_proc(ch->id, CSLSO_PRE_LOADCHECK) == CSLSOR_LOAD_CHUNK_CONSUMED) return; + } + + byte m = SlReadByte(); + size_t len; + size_t endoffs; + + _sl.block_mode = m; + _sl.obj_len = 0; + + SaveLoadChunkExtHeaderFlags ext_flags = static_cast(0); + if ((m & 0xF) == CH_EXT_HDR) { + ext_flags = static_cast(SlReadUint32()); + + /* read in real header */ + m = SlReadByte(); + _sl.block_mode = m; + } + + switch (m) { + case CH_ARRAY: + _sl.array_index = 0; + if (ext_flags) { + SlErrorCorruptFmt("CH_ARRAY does not take chunk header extension flags: 0x%X", ext_flags); + } + if (ch && ch->load_check_proc) { + ch->load_check_proc(); + } else { + SlSkipArray(); + } + break; + case CH_SPARSE_ARRAY: + if (ext_flags) { + SlErrorCorruptFmt("CH_SPARSE_ARRAY does not take chunk header extension flags: 0x%X", ext_flags); + } + if (ch && ch->load_check_proc) { + ch->load_check_proc(); + } else { + SlSkipArray(); + } + break; + default: + if ((m & 0xF) == CH_RIFF) { + if (ext_flags != (ext_flags & SLCEHF_BIG_RIFF)) { + SlErrorCorruptFmt("Unknown chunk header extension flags for CH_RIFF: 0x%X", ext_flags); + } + /* Read length */ + len = (SlReadByte() << 16) | ((m >> 4) << 24); + len += SlReadUint16(); + SlRIFFSpringPPCheck(len); + if (SlXvIsFeaturePresent(XSLFI_RIFF_HEADER_60_BIT)) { + if (len != 0) { + SlErrorCorrupt("RIFF chunk too large"); + } + len = SlReadUint32(); + if (ext_flags & SLCEHF_BIG_RIFF) SlErrorCorrupt("XSLFI_RIFF_HEADER_60_BIT and SLCEHF_BIG_RIFF both present"); + } + if (ext_flags & SLCEHF_BIG_RIFF) { + uint64 full_len = len | (static_cast(SlReadUint32()) << 28); + if (full_len >= (1LL << 32)) { + SlErrorCorruptFmt("Chunk size too large: " OTTD_PRINTFHEX64, full_len); + } + len = static_cast(full_len); + } + _sl.obj_len = len; + endoffs = _sl.reader->GetSize() + len; + if (ch && ch->load_check_proc) { + ch->load_check_proc(); + } else { + SlSkipBytes(len); + } + if (_sl.reader->GetSize() != endoffs) { + DEBUG(sl, 1, "Invalid chunk size: " PRINTF_SIZE " != " PRINTF_SIZE ", (" PRINTF_SIZE ")", _sl.reader->GetSize(), endoffs, len); + SlErrorCorruptFmt("Invalid chunk size - expected to be at position " PRINTF_SIZE ", actually at " PRINTF_SIZE ", length: " PRINTF_SIZE, + endoffs, _sl.reader->GetSize(), len); + } + } else { + SlErrorCorrupt("Invalid chunk type"); + } + break; + } +} + +/** + * Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is + * prefixed by an ID identifying it, followed by data, and terminator where appropriate + * @param ch The chunkhandler that will be used for the operation + */ +static void SlSaveChunk(const ChunkHandler &ch) +{ + if (ch.type == CH_UPSTREAM_SAVE) { + SaveLoadVersion old_ver = _sl_version; + _sl_version = MAX_LOAD_SAVEGAME_VERSION; + auto guard = scope_guard([&]() { + _sl_version = old_ver; + }); + upstream_sl::SlSaveChunkChunkByID(ch.id); + return; + } + + ChunkSaveLoadProc *proc = ch.save_proc; + + /* Don't save any chunk information if there is no save handler. */ + if (proc == nullptr) return; + + if (ch.special_proc != nullptr) { + if (ch.special_proc(ch.id, CSLSO_SHOULD_SAVE_CHUNK) == CSLSOR_DONT_SAVE_CHUNK) return; + } + + SlWriteUint32(ch.id); + DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); + + size_t written = 0; + if (_debug_sl_level >= 3) written = SlGetBytesWritten(); + + _sl.block_mode = ch.type; + switch (ch.type) { + 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(); + } + + DEBUG(sl, 3, "Saved chunk %c%c%c%c (" PRINTF_SIZE " bytes)", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id, SlGetBytesWritten() - written); +} + +/** Save all chunks */ +static void SlSaveChunks() +{ + for (auto &ch : ChunkHandlers()) { + SlSaveChunk(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 appropriate chunkhandler + */ +static const ChunkHandler *SlFindChunkHandler(uint32 id) +{ + for (auto &ch : ChunkHandlers()) if (ch.id == id) return &ch; + return nullptr; +} + +/** Load all chunks */ +static void SlLoadChunks() +{ + if (_sl_upstream_mode) { + upstream_sl::SlLoadChunks(); + return; + } + + for (uint32 id = SlReadUint32(); id != 0; id = SlReadUint32()) { + DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); + size_t read = 0; + if (_debug_sl_level >= 3) read = SlGetBytesRead(); + + if (SlXvIsChunkDiscardable(id)) { + DEBUG(sl, 1, "Discarding chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); + SlLoadCheckChunk(nullptr); + } else { + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) { + SlErrorCorrupt("Unknown chunk type"); + } else { + SlLoadChunk(*ch); + } + } + DEBUG(sl, 3, "Loaded chunk %c%c%c%c (" PRINTF_SIZE " bytes)", id >> 24, id >> 16, id >> 8, id, SlGetBytesRead() - read); + } +} + +/** Load all chunks for savegame checking */ +static void SlLoadCheckChunks() +{ + if (_sl_upstream_mode) { + upstream_sl::SlLoadCheckChunks(); + return; + } + + 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); + size_t read = 0; + if (_debug_sl_level >= 3) read = SlGetBytesRead(); + + if (SlXvIsChunkDiscardable(id)) { + ch = nullptr; + } else { + ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + } + SlLoadCheckChunk(ch); + DEBUG(sl, 3, "Loaded chunk %c%c%c%c (" PRINTF_SIZE " bytes)", id >> 24, id >> 16, id >> 8, id, SlGetBytesRead() - read); + } +} + +/** Fix all pointers (convert index -> pointer) */ +static void SlFixPointers() +{ + if (_sl_upstream_mode) { + upstream_sl::SlFixPointers(); + return; + } + + _sl.action = SLA_PTRS; + + for (auto &ch : ChunkHandlers()) { + if (ch.ptrs_proc != nullptr) { + DEBUG(sl, 3, "Fixing pointers for %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); + ch.ptrs_proc(); + } + } + + assert(_sl.action == SLA_PTRS); +} + + +/** Yes, simply reading from a file. */ +struct FileReader : LoadFilter { + FILE *file; ///< The file to read from. + long begin; ///< The begin of the file. + + /** + * Create the file reader, so it reads from a specific file. + * @param file The file to read from. + */ + FileReader(FILE *file) : LoadFilter(nullptr), file(file), begin(ftell(file)) + { + } + + /** Make sure everything is cleaned up. */ + ~FileReader() + { + if (this->file != nullptr) fclose(this->file); + this->file = nullptr; + + /* Make sure we don't double free. */ + _sl.sf = nullptr; + } + + size_t Read(byte *buf, size_t size) override + { + /* We're in the process of shutting down, i.e. in "failure" mode. */ + if (this->file == nullptr) return 0; + + return fread(buf, 1, size, this->file); + } + + void Reset() override + { + clearerr(this->file); + if (fseek(this->file, this->begin, SEEK_SET)) { + DEBUG(sl, 1, "Could not reset the file reading"); + } + } +}; + +/** Yes, simply writing to a file. */ +struct FileWriter : SaveFilter { + FILE *file; ///< The file to write to. + + /** + * Create the file writer, so it writes to a specific file. + * @param file The file to write to. + */ + FileWriter(FILE *file) : SaveFilter(nullptr), file(file) + { + } + + /** Make sure everything is cleaned up. */ + ~FileWriter() + { + this->Finish(); + + /* Make sure we don't double free. */ + _sl.sf = nullptr; + } + + void Write(byte *buf, size_t size) override + { + /* We're in the process of shutting down, i.e. in "failure" mode. */ + if (this->file == nullptr) return; + + if (fwrite(buf, 1, size, this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE); + } + + void Finish() override + { + if (this->file != nullptr) fclose(this->file); + this->file = nullptr; + } +}; + +/******************************************* + ********** START OF LZO CODE ************** + *******************************************/ + +#ifdef WITH_LZO +#include + +/** Buffer size for the LZO compressor */ +static const uint LZO_BUFFER_SIZE = 8192; + +/** Filter using LZO compression. */ +struct LZOLoadFilter : LoadFilter { + /** + * Initialise this filter. + * @param chain The next filter in this chain. + */ + LZOLoadFilter(LoadFilter *chain) : LoadFilter(chain) + { + if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); + } + + size_t Read(byte *buf, size_t ssize) override + { + assert(ssize >= LZO_BUFFER_SIZE); + + /* Buffer size is from the LZO docs plus the chunk header size. */ + byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2]; + uint32 tmp[2]; + uint32 size; + lzo_uint len = ssize; + + /* Read header*/ + if (this->chain->Read((byte*)tmp, sizeof(tmp)) != sizeof(tmp)) 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 != SL_MIN_VERSION) { + tmp[0] = TO_BE32(tmp[0]); + size = TO_BE32(size); + } + + if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size"); + + /* Read block */ + if (this->chain->Read(out + sizeof(uint32), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); + + /* Verify checksum */ + if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlErrorCorrupt("Bad checksum"); + + /* Decompress */ + int ret = lzo1x_decompress_safe(out + sizeof(uint32) * 1, size, buf, &len, nullptr); + if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); + return len; + } +}; + +/** Filter using LZO compression. */ +struct LZOSaveFilter : SaveFilter { + /** + * Initialise this filter. + * @param chain The next filter in this chain. + * @param compression_level The requested level of compression. + */ + LZOSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) + { + if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); + } + + void Write(byte *buf, size_t size) override + { + const lzo_bytep in = buf; + /* Buffer size is from the LZO docs plus the chunk header size. */ + byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2]; + byte wrkmem[LZO1X_1_MEM_COMPRESS]; + lzo_uint outlen; + + do { + /* Compress up to LZO_BUFFER_SIZE bytes at once. */ + lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size; + lzo1x_1_compress(in, len, out + sizeof(uint32) * 2, &outlen, wrkmem); + ((uint32*)out)[1] = TO_BE32((uint32)outlen); + ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32))); + this->chain->Write(out, outlen + sizeof(uint32) * 2); + + /* Move to next data chunk. */ + size -= len; + in += len; + } while (size > 0); + } +}; + +#endif /* WITH_LZO */ + +/********************************************* + ******** START OF NOCOMP CODE (uncompressed)* + *********************************************/ + +/** Filter without any compression. */ +struct NoCompLoadFilter : LoadFilter { + /** + * Initialise this filter. + * @param chain The next filter in this chain. + */ + NoCompLoadFilter(LoadFilter *chain) : LoadFilter(chain) + { + } + + size_t Read(byte *buf, size_t size) override + { + return this->chain->Read(buf, size); + } +}; + +/** Filter without any compression. */ +struct NoCompSaveFilter : SaveFilter { + /** + * Initialise this filter. + * @param chain The next filter in this chain. + * @param compression_level The requested level of compression. + */ + NoCompSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) + { + } + + void Write(byte *buf, size_t size) override + { + this->chain->Write(buf, size); + } +}; + +/******************************************** + ********** START OF ZLIB CODE ************** + ********************************************/ + +#if defined(WITH_ZLIB) +#include + +/** Filter using Zlib compression. */ +struct ZlibLoadFilter : LoadFilter { + z_stream z; ///< Stream state we are reading from. + byte fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file. + + /** + * Initialise this filter. + * @param chain The next filter in this chain. + */ + ZlibLoadFilter(LoadFilter *chain) : LoadFilter(chain) + { + memset(&this->z, 0, sizeof(this->z)); + if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); + } + + /** Clean everything up. */ + ~ZlibLoadFilter() + { + inflateEnd(&this->z); + } + + size_t Read(byte *buf, size_t size) override + { + this->z.next_out = buf; + this->z.avail_out = (uint)size; + + do { + /* read more bytes from the file? */ + if (this->z.avail_in == 0) { + this->z.next_in = this->fread_buf; + this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf)); + } + + /* inflate the data */ + int r = inflate(&this->z, 0); + if (r == Z_STREAM_END) break; + + if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed"); + } while (this->z.avail_out != 0); + + return size - this->z.avail_out; + } +}; + +/** Filter using Zlib compression. */ +struct ZlibSaveFilter : SaveFilter { + z_stream z; ///< Stream state we are writing to. + byte buf[MEMORY_CHUNK_SIZE]; ///< output buffer + + /** + * Initialise this filter. + * @param chain The next filter in this chain. + * @param compression_level The requested level of compression. + */ + ZlibSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) + { + memset(&this->z, 0, sizeof(this->z)); + if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); + } + + /** Clean up what we allocated. */ + ~ZlibSaveFilter() + { + deflateEnd(&this->z); + } + + /** + * Helper loop for writing the data. + * @param p The bytes to write. + * @param len Amount of bytes to write. + * @param mode Mode for deflate. + */ + void WriteLoop(byte *p, size_t len, int mode) + { + uint n; + this->z.next_in = p; + this->z.avail_in = (uInt)len; + do { + this->z.next_out = this->buf; + this->z.avail_out = sizeof(this->buf); + + /** + * For the poor next soul who sees many valgrind warnings of the + * "Conditional jump or move depends on uninitialised value(s)" kind: + * According to the author of zlib it is not a bug and it won't be fixed. + * http://groups.google.com/group/comp.compression/browse_thread/thread/b154b8def8c2a3ef/cdf9b8729ce17ee2 + * [Mark Adler, Feb 24 2004, 'zlib-1.2.1 valgrind warnings' in the newsgroup comp.compression] + */ + int r = deflate(&this->z, mode); + + /* bytes were emitted? */ + if ((n = sizeof(this->buf) - this->z.avail_out) != 0) { + this->chain->Write(this->buf, n); + } + if (r == Z_STREAM_END) break; + + if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code"); + } while (this->z.avail_in || !this->z.avail_out); + } + + void Write(byte *buf, size_t size) override + { + this->WriteLoop(buf, size, 0); + } + + void Finish() override + { + this->WriteLoop(nullptr, 0, Z_FINISH); + this->chain->Finish(); + } +}; + +#endif /* WITH_ZLIB */ + +/******************************************** + ********** START OF LZMA CODE ************** + ********************************************/ + +#if defined(WITH_LIBLZMA) +#include + +/** + * Have a copy of an initialised LZMA stream. We need this as it's + * impossible to "re"-assign LZMA_STREAM_INIT to a variable in some + * compilers, i.e. LZMA_STREAM_INIT can't be used to set something. + * This var has to be used instead. + */ +static const lzma_stream _lzma_init = LZMA_STREAM_INIT; + +/** Filter without any compression. */ +struct LZMALoadFilter : LoadFilter { + lzma_stream lzma; ///< Stream state that we are reading from. + byte fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file. + + /** + * Initialise this filter. + * @param chain The next filter in this chain. + */ + LZMALoadFilter(LoadFilter *chain) : LoadFilter(chain), lzma(_lzma_init) + { + /* Allow saves up to 256 MB uncompressed */ + if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); + } + + /** Clean everything up. */ + ~LZMALoadFilter() + { + lzma_end(&this->lzma); + } + + size_t Read(byte *buf, size_t size) override + { + this->lzma.next_out = buf; + this->lzma.avail_out = size; + + do { + /* read more bytes from the file? */ + if (this->lzma.avail_in == 0) { + this->lzma.next_in = this->fread_buf; + this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf)); + } + + /* inflate the data */ + lzma_ret r = lzma_code(&this->lzma, LZMA_RUN); + if (r == LZMA_STREAM_END) break; + if (r != LZMA_OK) SlErrorFmt(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code: %u", r); + } while (this->lzma.avail_out != 0); + + return size - this->lzma.avail_out; + } +}; + +/** Filter using LZMA compression. */ +struct LZMASaveFilter : SaveFilter { + lzma_stream lzma; ///< Stream state that we are writing to. + byte buf[MEMORY_CHUNK_SIZE]; ///< output buffer + + /** + * Initialise this filter. + * @param chain The next filter in this chain. + * @param compression_level The requested level of compression. + */ + LZMASaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain), lzma(_lzma_init) + { + if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); + } + + /** Clean up what we allocated. */ + ~LZMASaveFilter() + { + lzma_end(&this->lzma); + } + + /** + * Helper loop for writing the data. + * @param p The bytes to write. + * @param len Amount of bytes to write. + * @param action Action for lzma_code. + */ + void WriteLoop(byte *p, size_t len, lzma_action action) + { + size_t n; + this->lzma.next_in = p; + this->lzma.avail_in = len; + do { + this->lzma.next_out = this->buf; + this->lzma.avail_out = sizeof(this->buf); + + lzma_ret r = lzma_code(&this->lzma, action); + + /* bytes were emitted? */ + if ((n = sizeof(this->buf) - this->lzma.avail_out) != 0) { + this->chain->Write(this->buf, n); + } + if (r == LZMA_STREAM_END) break; + if (r != LZMA_OK) SlErrorFmt(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code: %u", r); + } while (this->lzma.avail_in || !this->lzma.avail_out); + } + + void Write(byte *buf, size_t size) override + { + this->WriteLoop(buf, size, LZMA_RUN); + } + + void Finish() override + { + this->WriteLoop(nullptr, 0, LZMA_FINISH); + this->chain->Finish(); + } +}; + +#endif /* WITH_LIBLZMA */ + +/******************************************** + ********** START OF ZSTD CODE ************** + ********************************************/ + +#if defined(WITH_ZSTD) +#include + + +/** Filter using ZSTD compression. */ +struct ZSTDLoadFilter : LoadFilter { + ZSTD_DCtx *zstd; ///< ZSTD decompression context + byte fread_buf[MEMORY_CHUNK_SIZE]; ///< Buffer for reading from the file + ZSTD_inBuffer input; ///< ZSTD input buffer for fread_buf + + /** + * Initialise this filter. + * @param chain The next filter in this chain. + */ + ZSTDLoadFilter(LoadFilter *chain) : LoadFilter(chain) + { + this->zstd = ZSTD_createDCtx(); + if (!this->zstd) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); + this->input = {this->fread_buf, 0, 0}; + } + + /** Clean everything up. */ + ~ZSTDLoadFilter() + { + ZSTD_freeDCtx(this->zstd); + } + + size_t Read(byte *buf, size_t size) override + { + ZSTD_outBuffer output{buf, size, 0}; + + do { + /* read more bytes from the file? */ + if (this->input.pos == this->input.size) { + this->input.size = this->chain->Read(this->fread_buf, sizeof(this->fread_buf)); + this->input.pos = 0; + if (this->input.size == 0) break; + } + + size_t ret = ZSTD_decompressStream(this->zstd, &output, &this->input); + if (ZSTD_isError(ret)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "libzstd returned error code"); + if (ret == 0) break; + } while (output.pos < output.size); + + return output.pos; + } +}; + +/** Filter using ZSTD compression. */ +struct ZSTDSaveFilter : SaveFilter { + ZSTD_CCtx *zstd; ///< ZSTD compression context + byte buf[MEMORY_CHUNK_SIZE]; ///< output buffer + + /** + * Initialise this filter. + * @param chain The next filter in this chain. + * @param compression_level The requested level of compression. + */ + ZSTDSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) + { + this->zstd = ZSTD_createCCtx(); + if (!this->zstd) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); + if (ZSTD_isError(ZSTD_CCtx_setParameter(this->zstd, ZSTD_c_compressionLevel, (int)compression_level - 100))) { + ZSTD_freeCCtx(this->zstd); + SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "invalid compresison level"); + } + } + + /** Clean up what we allocated. */ + ~ZSTDSaveFilter() + { + ZSTD_freeCCtx(this->zstd); + } + + /** + * Helper loop for writing the data. + * @param p The bytes to write. + * @param len Amount of bytes to write. + * @param mode Mode for ZSTD_compressStream2. + */ + void WriteLoop(byte *p, size_t len, ZSTD_EndDirective mode) + { + ZSTD_inBuffer input{p, len, 0}; + + bool finished; + do { + ZSTD_outBuffer output{this->buf, sizeof(this->buf), 0}; + size_t remaining = ZSTD_compressStream2(this->zstd, &output, &input, mode); + if (ZSTD_isError(remaining)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "libzstd returned error code"); + + if (output.pos != 0) this->chain->Write(this->buf, output.pos); + + finished = (mode == ZSTD_e_end ? (remaining == 0) : (input.pos == input.size)); + } while (!finished); + } + + void Write(byte *buf, size_t size) override + { + this->WriteLoop(buf, size, ZSTD_e_continue); + } + + void Finish() override + { + this->WriteLoop(nullptr, 0, ZSTD_e_end); + this->chain->Finish(); + } +}; + +#endif /* WITH_LIBZSTD */ + +/******************************************* + ************* END OF CODE ***************** + *******************************************/ + +enum SaveLoadFormatFlags : byte { + SLF_NONE = 0, + SLF_NO_THREADED_LOAD = 1 << 0, ///< Unsuitable for threaded loading + SLF_REQUIRES_ZSTD = 1 << 1, ///< Automatic selection requires the zstd flag +}; +DECLARE_ENUM_AS_BIT_SET(SaveLoadFormatFlags); + +/** 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 + + LoadFilter *(*init_load)(LoadFilter *chain); ///< Constructor for the load filter. + SaveFilter *(*init_write)(SaveFilter *chain, byte compression); ///< Constructor for the save filter. + + byte min_compression; ///< the minimum compression level of this format + byte default_compression; ///< the default compression level of this format + byte max_compression; ///< the maximum compression level of this format + SaveLoadFormatFlags flags; ///< flags +}; + +/** The different saveload formats known/understood by OpenTTD. */ +static const SaveLoadFormat _saveload_formats[] = { +#if defined(WITH_LZO) + /* Roughly 75% larger than zlib level 6 at only ~7% of the CPU usage. */ + {"lzo", TO_BE32X('OTTD'), CreateLoadFilter, CreateSaveFilter, 0, 0, 0, SLF_NO_THREADED_LOAD}, +#else + {"lzo", TO_BE32X('OTTD'), nullptr, nullptr, 0, 0, 0, SLF_NO_THREADED_LOAD}, +#endif + /* Roughly 5 times larger at only 1% of the CPU usage over zlib level 6. */ + {"none", TO_BE32X('OTTN'), CreateLoadFilter, CreateSaveFilter, 0, 0, 0, SLF_NONE}, +#if defined(WITH_ZLIB) + /* After level 6 the speed reduction is significant (1.5x to 2.5x slower per level), but the reduction in filesize is + * fairly insignificant (~1% for each step). Lower levels become ~5-10% bigger by each level than level 6 while level + * 1 is "only" 3 times as fast. Level 0 results in uncompressed savegames at about 8 times the cost of "none". */ + {"zlib", TO_BE32X('OTTZ'), CreateLoadFilter, CreateSaveFilter, 0, 6, 9, SLF_NONE}, +#else + {"zlib", TO_BE32X('OTTZ'), nullptr, nullptr, 0, 0, 0, SLF_NONE}, +#endif +#if defined(WITH_LIBLZMA) + /* Level 2 compression is speed wise as fast as zlib level 6 compression (old default), but results in ~10% smaller saves. + * Higher compression levels are possible, and might improve savegame size by up to 25%, but are also up to 10 times slower. + * The next significant reduction in file size is at level 4, but that is already 4 times slower. Level 3 is primarily 50% + * slower while not improving the filesize, while level 0 and 1 are faster, but don't reduce savegame size much. + * It's OTTX and not e.g. OTTL because liblzma is part of xz-utils and .tar.xz is preferred over .tar.lzma. */ + {"lzma", TO_BE32X('OTTX'), CreateLoadFilter, CreateSaveFilter, 0, 2, 9, SLF_NONE}, +#else + {"lzma", TO_BE32X('OTTX'), nullptr, nullptr, 0, 0, 0, SLF_NONE}, +#endif +#if defined(WITH_ZSTD) + /* Zstd provides a decent compression rate at a very high compression/decompression speed. Compared to lzma level 2 + * zstd saves are about 40% larger (on level 1) but it has about 30x faster compression and 5x decompression making it + * a good choice for multiplayer servers. And zstd level 1 seems to be the optimal one for client connection speed + * (compress + 10 MB/s download + decompress time), about 3x faster than lzma:2 and 1.5x than zlib:2 and lzo. + * As zstd has negative compression levels the values were increased by 100 moving zstd level range -100..22 into + * openttd 0..122. Also note that value 100 mathes zstd level 0 which is a special value for default level 3 (openttd 103) */ + {"zstd", TO_BE32X('OTTS'), CreateLoadFilter, CreateSaveFilter, 0, 101, 122, SLF_REQUIRES_ZSTD}, +#else + {"zstd", TO_BE32X('OTTS'), nullptr, nullptr, 0, 0, 0, SLF_REQUIRES_ZSTD}, +#endif +}; + +/** + * Return the savegameformat of the game. Whether it was created with ZLIB compression + * uncompressed, or another type + * @param full_name Name of the savegame format. If empty it picks the first available one + * @param compression_level Output for telling what compression level we want. + * @return Pointer to SaveLoadFormat struct giving all characteristics of this type of savegame + */ +static const SaveLoadFormat *GetSavegameFormat(const std::string &full_name, byte *compression_level, SaveModeFlags flags) +{ + const SaveLoadFormat *def = lastof(_saveload_formats); + + /* find default savegame format, the highest one with which files can be written */ + while (!def->init_write || ((def->flags & SLF_REQUIRES_ZSTD) && !(flags & SMF_ZSTD_OK))) def--; + + if (!full_name.empty()) { + /* Get the ":..." of the compression level out of the way */ + size_t separator = full_name.find(':'); + bool has_comp_level = separator != std::string::npos; + const std::string name(full_name, 0, has_comp_level ? separator : full_name.size()); + + for (const SaveLoadFormat *slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) { + if (slf->init_write != nullptr && name.compare(slf->name) == 0) { + *compression_level = slf->default_compression; + if (has_comp_level) { + const std::string complevel(full_name, separator + 1); + + /* Get the level and determine whether all went fine. */ + size_t processed; + long level = std::stol(complevel, &processed, 10); + if (processed == 0 || level != Clamp(level, slf->min_compression, slf->max_compression)) { + SetDParamStr(0, complevel); + ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, WL_CRITICAL); + } else { + *compression_level = level; + } + } + return slf; + } + } + + SetDParamStr(0, name); + SetDParamStr(1, def->name); + ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, WL_CRITICAL); + } + *compression_level = def->default_compression; + return def; +} + +/* actual loader/saver function */ +void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings); +extern bool AfterLoadGame(); +extern bool LoadOldSaveGame(const std::string &file); + +/** + * Clear temporary data that is passed between various saveload phases. + */ +static void ResetSaveloadData() +{ + ResetTempEngineData(); + ResetLabelMaps(); + ResetOldWaypoints(); +} + +/** + * Clear/free saveload state. + */ +static inline void ClearSaveLoadState() +{ + delete _sl.dumper; + _sl.dumper = nullptr; + + delete _sl.sf; + _sl.sf = nullptr; + + delete _sl.reader; + _sl.reader = nullptr; + + delete _sl.lf; + _sl.lf = nullptr; + + _sl.save_flags = SMF_NONE; + + GamelogStopAnyAction(); +} + +/** Update the gui accordingly when starting saving and set locks on saveload. */ +static void SaveFileStart() +{ + SetMouseCursorBusy(true); + + InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START); + _sl.saveinprogress = true; +} + +/** Update the gui accordingly when saving is done and release locks on saveload. */ +static void SaveFileDone() +{ + SetMouseCursorBusy(false); + + InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH); + _sl.saveinprogress = false; + +#ifdef __EMSCRIPTEN__ + EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs()); +#endif +} + +/** 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.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_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, INVALID_STRING_ID, WL_ERROR); + SaveFileDone(); +} + +/** + * We have written the whole game into memory, _memory_savegame, now find + * and appropriate compressor and start writing to file. + */ +static SaveOrLoadResult SaveFileToDisk(bool threaded) +{ + try { + byte compression; + const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression, _sl.save_flags); + + DEBUG(sl, 3, "Using compression format: %s, level: %u", fmt->name, compression); + + /* We have written our stuff to memory, now write it to file! */ + uint32 hdr[2] = { fmt->tag, TO_BE32((uint32) (SAVEGAME_VERSION | SAVEGAME_VERSION_EXT) << 16) }; + _sl.sf->Write((byte*)hdr, sizeof(hdr)); + + _sl.sf = fmt->init_write(_sl.sf, compression); + _sl.dumper->Flush(_sl.sf); + + ClearSaveLoadState(); + + if (threaded) SetAsyncSaveFinish(SaveFileDone); + + return SL_OK; + } catch (...) { + ClearSaveLoadState(); + + AsyncSaveFinishProc asfp = SaveFileDone; + + /* We don't want to shout when saving is just + * cancelled due to a client disconnecting. */ + if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) { + /* Skip the "colour" character */ + DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); + asfp = SaveFileError; + } + + if (threaded) { + SetAsyncSaveFinish(asfp); + } else { + asfp(); + } + return SL_ERROR; + } +} + +void WaitTillSaved() +{ + if (!_save_thread.joinable()) return; + + _save_thread.join(); + + /* Make sure every other state is handled properly as well. */ + ProcessAsyncSaveFinish(); +} + +/** + * Actually perform the saving of the savegame. + * General tactics is to first save the game to memory, then write it to file + * using the writer, either in threaded mode if possible, or single-threaded. + * @param writer The filter to write the savegame to. + * @param threaded Whether to try to perform the saving asynchronously. + * @return Return the result of the action. #SL_OK or #SL_ERROR + */ +static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded) +{ + assert(!_sl.saveinprogress); + + _sl.dumper = new MemoryDumper(); + _sl.sf = writer; + + _sl_version = SAVEGAME_VERSION; + SlXvSetCurrentState(); + + SaveViewportBeforeSaveGame(); + SlSaveChunks(); + + SaveFileStart(); + + if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) { + if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode..."); + + SaveOrLoadResult result = SaveFileToDisk(false); + SaveFileDone(); + + return result; + } + + return SL_OK; +} + +/** + * Save the game using a (writer) filter. + * @param writer The filter to write the savegame to. + * @param threaded Whether to try to perform the saving asynchronously. + * @param flags Save mode flags. + * @return Return the result of the action. #SL_OK or #SL_ERROR + */ +SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded, SaveModeFlags flags) +{ + try { + _sl.action = SLA_SAVE; + _sl.save_flags = flags; + return DoSave(writer, threaded); + } catch (...) { + ClearSaveLoadState(); + return SL_ERROR; + } +} + +bool IsNetworkServerSave() +{ + return _sl.save_flags & SMF_NET_SERVER; +} + +bool IsScenarioSave() +{ + return _sl.save_flags & SMF_SCENARIO; +} + +struct ThreadedLoadFilter : LoadFilter { + static const size_t BUFFER_COUNT = 4; + + std::mutex mutex; + std::condition_variable full_cv; + std::condition_variable empty_cv; + uint first_ready = 0; + uint count_ready = 0; + size_t read_offsets[BUFFER_COUNT]; + size_t read_counts[BUFFER_COUNT]; + byte read_buf[MEMORY_CHUNK_SIZE * BUFFER_COUNT]; ///< Buffers for reading from source. + bool no_thread = false; + + bool have_exception = false; + ThreadSlErrorException caught_exception; + + std::thread read_thread; + + /** + * Initialise this filter. + * @param chain The next filter in this chain. + */ + ThreadedLoadFilter(LoadFilter *chain) : LoadFilter(chain) + { + std::unique_lock lk(this->mutex); + if (!StartNewThread(&this->read_thread, "ottd:loadgame", &ThreadedLoadFilter::RunThread, this)) { + DEBUG(sl, 1, "Failed to start load read thread, reading non-threaded"); + this->no_thread = true; + } else { + DEBUG(sl, 2, "Started load read thread"); + } + } + + /** Clean everything up. */ + ~ThreadedLoadFilter() + { + std::unique_lock lk(this->mutex); + this->no_thread = true; + lk.unlock(); + this->empty_cv.notify_all(); + this->full_cv.notify_all(); + if (this->read_thread.joinable()) { + this->read_thread.join(); + DEBUG(sl, 2, "Joined load read thread"); + } + } + + static void RunThread(ThreadedLoadFilter *self) + { + try { + std::unique_lock lk(self->mutex); + while (!self->no_thread) { + if (self->count_ready == BUFFER_COUNT) { + self->full_cv.wait(lk); + continue; + } + + uint buf = (self->first_ready + self->count_ready) % BUFFER_COUNT; + lk.unlock(); + size_t read = self->chain->Read(self->read_buf + (buf * MEMORY_CHUNK_SIZE), MEMORY_CHUNK_SIZE); + lk.lock(); + self->read_offsets[buf] = 0; + self->read_counts[buf] = read; + self->count_ready++; + if (self->count_ready == 1) self->empty_cv.notify_one(); + } + } catch (const ThreadSlErrorException &ex) { + std::unique_lock lk(self->mutex); + self->caught_exception = std::move(ex); + self->have_exception = true; + self->empty_cv.notify_one(); + } + } + + size_t Read(byte *buf, size_t size) override + { + if (this->no_thread) return this->chain->Read(buf, size); + + size_t read = 0; + std::unique_lock lk(this->mutex); + while (read < size || this->have_exception) { + if (this->have_exception) { + this->have_exception = false; + SlError(this->caught_exception.string, this->caught_exception.extra_msg); + } + if (this->count_ready == 0) { + this->empty_cv.wait(lk); + continue; + } + + size_t to_read = std::min(size - read, read_counts[this->first_ready]); + if (to_read == 0) break; + memcpy(buf + read, this->read_buf + (this->first_ready * MEMORY_CHUNK_SIZE) + read_offsets[this->first_ready], to_read); + read += to_read; + read_offsets[this->first_ready] += to_read; + read_counts[this->first_ready] -= to_read; + if (read_counts[this->first_ready] == 0) { + this->first_ready = (this->first_ready + 1) % BUFFER_COUNT; + this->count_ready--; + if (this->count_ready == BUFFER_COUNT - 1) this->full_cv.notify_one(); + } + } + return read; + } +}; + +/** + * Actually perform the loading of a "non-old" savegame. + * @param reader The filter to read the savegame from. + * @param load_check Whether to perform the checking ("preview") or actually load the game. + * @return Return the result of the action. #SL_OK or #SL_REINIT ("unload" the game) + */ +static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check) +{ + _sl.lf = reader; + + if (load_check) { + /* Clear previous check data */ + _load_check_data.Clear(); + /* Mark SL_LOAD_CHECK as supported for this savegame. */ + _load_check_data.checkable = true; + } + + SlXvResetState(); + SlResetVENC(); + SlResetTNNC(); + auto guard = scope_guard([&]() { + SlResetVENC(); + SlResetTNNC(); + }); + + uint32 hdr[2]; + if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); + + /* see if we have any loader for this type. */ + const SaveLoadFormat *fmt = _saveload_formats; + for (;;) { + /* 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"); + _sl.lf->Reset(); + _sl_version = SL_MIN_VERSION; + _sl_minor_version = 0; + SlXvResetState(); + + /* Try to find the LZO savegame format; it uses 'OTTD' as tag. */ + fmt = _saveload_formats; + for (;;) { + if (fmt == endof(_saveload_formats)) { + /* Who removed LZO support? */ + NOT_REACHED(); + } + if (fmt->tag == TO_BE32X('OTTD')) break; + fmt++; + } + break; + } + + if (fmt->tag == hdr[0]) { + /* check version number */ + _sl_version = (SaveLoadVersion)(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. + * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */ + _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF; + + bool special_version = false; + if (_sl_version & SAVEGAME_VERSION_EXT) { + _sl_version = (SaveLoadVersion)(_sl_version & ~SAVEGAME_VERSION_EXT); + _sl_is_ext_version = true; + } else { + special_version = SlXvCheckSpecialSavegameVersions(); + } + + if (_sl_version >= SLV_SAVELOAD_LIST_LENGTH) { + if (_sl_is_ext_version) { + DEBUG(sl, 0, "Got an extended savegame version with a base version in the upstream mode range, giving up"); + SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME); + } else { + _sl_upstream_mode = true; + } + } + + DEBUG(sl, 1, "Loading savegame version %d%s%s%s%s", _sl_version, _sl_is_ext_version ? " (extended)" : "", + _sl_maybe_springpp ? " which might be SpringPP" : "", _sl_maybe_chillpp ? " which might be ChillPP" : "", _sl_upstream_mode ? " (upstream mode)" : ""); + + /* Is the version higher than the current? */ + if (_sl_version > MAX_LOAD_SAVEGAME_VERSION && !special_version) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME); + if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS && !special_version) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK); + break; + } + + fmt++; + } + + /* loader for this savegame type is not implemented? */ + if (fmt->init_load == nullptr) { + char err_str[64]; + seprintf(err_str, lastof(err_str), "Loader for '%s' is not available.", fmt->name); + SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str); + } + + _sl.lf = fmt->init_load(_sl.lf); + if (!(fmt->flags & SLF_NO_THREADED_LOAD)) { + _sl.lf = new ThreadedLoadFilter(_sl.lf); + } + _sl.reader = new ReadBuffer(_sl.lf); + _next_offs = 0; + + upstream_sl::SlResetLoadState(); + + if (!load_check) { + ResetSaveloadData(); + + /* 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, true); + + GamelogReset(); + + if (IsSavegameVersionBefore(SLV_4)) { + /* + * NewGRFs were introduced between 0.3,4 and 0.3.5, which both + * shared savegame version 4. Anything before that 'obviously' + * does not have any NewGRFs. Between the introduction and + * savegame version 41 (just before 0.5) the NewGRF settings + * were not stored in the savegame and they were loaded by + * using the settings from the main menu. + * So, to recap: + * - savegame version < 4: do not load any NewGRFs. + * - savegame version >= 41: load NewGRFs from savegame, which is + * already done at this stage by + * overwriting the main menu settings. + * - other savegame versions: use main menu settings. + * + * This means that users *can* crash savegame version 4..40 + * savegames if they set incompatible NewGRFs in the main menu, + * but can't crash anymore for savegame version < 4 savegames. + * + * Note: this is done here because AfterLoadGame is also called + * for TTO/TTD/TTDP savegames which have their own NewGRF logic. + */ + ClearGRFConfigList(&_grfconfig); + } + } + + if (load_check) { + /* Load chunks into _load_check_data. + * No pools are loaded. References are not possible, and thus do not need resolving. */ + SlLoadCheckChunks(); + } else { + /* Load chunks and resolve references */ + SlLoadChunks(); + SlFixPointers(); + } + + ClearSaveLoadState(); + + _savegame_type = SGT_OTTD; + + if (load_check) { + /* The only part from AfterLoadGame() we need */ + if (_load_check_data.want_grf_compatibility) _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig); + _load_check_data.sl_is_ext_version = _sl_is_ext_version; + } else { + GamelogStartAction(GLAT_LOAD); + + /* After loading fix up savegame for any internal changes that + * might have occurred since then. If it fails, load back the old game. */ + if (!AfterLoadGame()) { + GamelogStopAction(); + return SL_REINIT; + } + + GamelogStopAction(); + SlXvSetCurrentState(); + } + + return SL_OK; +} + +/** + * Load the game using a (reader) filter. + * @param reader The filter to read the savegame from. + * @return Return the result of the action. #SL_OK or #SL_REINIT ("unload" the game) + */ +SaveOrLoadResult LoadWithFilter(LoadFilter *reader) +{ + try { + _sl.action = SLA_LOAD; + return DoLoad(reader, false); + } catch (...) { + ClearSaveLoadState(); + + /* Skip the "colour" character */ + DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); + + return SL_REINIT; + } +} + +/** + * 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 fop Save or load mode. Load can also be a TTD(Patch) game. + * @param sb The sub directory to save the savegame in + * @param threaded True when threaded saving is allowed + * @return Return the result of the action. #SL_OK, #SL_ERROR, or #SL_REINIT ("unload" the game) + */ +SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded, SaveModeFlags save_flags) +{ + /* An instance of saving is already active, so don't go saving again */ + if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) { + /* if not an autosave, but a user action, show error message */ + if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR); + return SL_OK; + } + WaitTillSaved(); + + try { + /* Load a TTDLX or TTDPatch game */ + if (fop == SLO_LOAD && dft == DFT_OLD_GAME_FILE) { + ResetSaveloadData(); + + InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused + + /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them + * and if so a new NewGRF list will be made in LoadOldSaveGame. + * Note: this is done here because AfterLoadGame is also called + * for OTTD savegames which have their own NewGRF logic. */ + ClearGRFConfigList(&_grfconfig); + GamelogReset(); + if (!LoadOldSaveGame(filename)) return SL_REINIT; + _sl_version = SL_MIN_VERSION; + _sl_minor_version = 0; + SlXvResetState(); + GamelogStartAction(GLAT_LOAD); + if (!AfterLoadGame()) { + GamelogStopAction(); + return SL_REINIT; + } + GamelogStopAction(); + SlXvSetCurrentState(); + return SL_OK; + } + + assert(dft == DFT_GAME_FILE); + switch (fop) { + case SLO_CHECK: + _sl.action = SLA_LOAD_CHECK; + break; + + case SLO_LOAD: + _sl.action = SLA_LOAD; + break; + + case SLO_SAVE: + _sl.action = SLA_SAVE; + break; + + default: NOT_REACHED(); + } + _sl.save_flags = save_flags; + + FILE *fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb); + + /* Make it a little easier to load savegames from the console */ + if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR); + if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR); + if (fh == nullptr && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR); + + if (fh == nullptr) { + SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); + } + + if (fop == SLO_SAVE) { // SAVE game + DEBUG(desync, 1, "save: date{%08x; %02x; %02x}; %s", _date, _date_fract, _tick_skip_counter, filename.c_str()); + if (!_settings_client.gui.threaded_saves) threaded = false; + + return DoSave(new FileWriter(fh), threaded); + } + + /* LOAD game */ + assert(fop == SLO_LOAD || fop == SLO_CHECK); + DEBUG(desync, 1, "load: %s", filename.c_str()); + return DoLoad(new FileReader(fh), fop == SLO_CHECK); + } catch (...) { + /* This code may be executed both for old and new save games. */ + ClearSaveLoadState(); + + /* Skip the "colour" character */ + if (fop != SLO_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); + + /* A saver/loader exception!! reinitialize all variables to prevent crash! */ + return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR; + } +} + +/** + * Create an autosave or netsave. + * @param counter A reference to the counter variable to be used for rotating the file name. + * @param netsave Indicates if this is a regular autosave or a netsave. + */ +void DoAutoOrNetsave(FiosNumberedSaveName &counter, bool threaded) +{ + char buf[MAX_PATH]; + + if (_settings_client.gui.keep_all_autosave) { + GenerateDefaultSaveName(buf, lastof(buf)); + strecat(buf, counter.Extension().c_str(), lastof(buf)); + } else { + strecpy(buf, counter.Filename().c_str(), lastof(buf)); + } + + DEBUG(sl, 2, "Autosaving to '%s'", buf); + if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, threaded, SMF_ZSTD_OK) != SL_OK) { + ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); + } +} + + +/** Do a save when exiting the game (_settings_client.gui.autosave_on_exit) */ +void DoExitSave() +{ + SaveOrLoad("exit.sav", SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, true, SMF_ZSTD_OK); +} + +/** + * 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 (!Company::IsValidID(cid)) { + for (const Company *c : Company::Iterate()) { + 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, !Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, last); + SanitizeFilename(buf); +} + +/** + * Set the mode and file type of the file to save or load based on the type of file entry at the file system. + * @param ft Type of file entry of the file system. + */ +void FileToSaveLoad::SetMode(FiosType ft) +{ + this->SetMode(SLO_LOAD, GetAbstractFileType(ft), GetDetailedFileType(ft)); +} + +/** + * Set the mode and file type of the file to save or load. + * @param fop File operation being performed. + * @param aft Abstract file type. + * @param dft Detailed file type. + */ +void FileToSaveLoad::SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft) +{ + if (aft == FT_INVALID || aft == FT_NONE) { + this->file_op = SLO_INVALID; + this->detail_ftype = DFT_INVALID; + this->abstract_ftype = FT_INVALID; + return; + } + + this->file_op = fop; + this->detail_ftype = dft; + this->abstract_ftype = aft; +} + +/** + * Set the title of the file. + * @param title Title of the file. + */ +void FileToSaveLoad::Set(const FiosItem &item) +{ + this->SetMode(item.type); + this->name = item.name; + this->title = item.title; +} + +bool SaveLoadFileTypeIsScenario() +{ + return _file_to_saveload.abstract_ftype == FT_SCENARIO; +} + +void SlUnreachablePlaceholder() +{ + NOT_REACHED(); +} diff --git a/src/sl/saveload.h b/src/sl/saveload.h new file mode 100644 index 0000000000..d849bb0e4d --- /dev/null +++ b/src/sl/saveload.h @@ -0,0 +1,765 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file saveload.h Functions/types related to saving and loading games. */ + +#ifndef SL_SAVELOAD_H +#define SL_SAVELOAD_H + +#include "saveload_types.h" +#include "../fileio_type.h" +#include "../fios.h" +#include "../strings_type.h" +#include "../scope.h" + +#include +#include +#include +#include + +/** Save or load result codes. */ +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) +}; + +/** Deals with the type of the savegame, independent of extension */ +struct FileToSaveLoad { + SaveLoadOperation file_op; ///< File operation to perform. + DetailedFileType detail_ftype; ///< Concrete file type (PNG, BMP, old save, etc). + AbstractFileType abstract_ftype; ///< Abstract type of file (scenario, heightmap, etc). + std::string name; ///< Name of the file. + std::string title; ///< Internal name of the game. + + void SetMode(FiosType ft); + void SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft); + void Set(const FiosItem &item); +}; + +/** Types of save games. */ +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 + SGT_TTO, ///< TTO savegame + SGT_INVALID = 0xFF, ///< broken savegame (used internally) +}; + +enum SaveModeFlags : byte { + SMF_NONE = 0, + SMF_NET_SERVER = 1 << 0, ///< Network server save + SMF_ZSTD_OK = 1 << 1, ///< Zstd OK + SMF_SCENARIO = 1 << 2, ///< Scenario save +}; +DECLARE_ENUM_AS_BIT_SET(SaveModeFlags); + +extern FileToSaveLoad _file_to_saveload; + +void GenerateDefaultSaveName(char *buf, const char *last); +void SetSaveLoadError(StringID str); +const char *GetSaveLoadErrorString(); +SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true, SaveModeFlags flags = SMF_NONE); +void WaitTillSaved(); +void ProcessAsyncSaveFinish(); +void DoExitSave(); + +void DoAutoOrNetsave(FiosNumberedSaveName &counter, bool threaded); + +SaveOrLoadResult SaveWithFilter(struct SaveFilter *writer, bool threaded, SaveModeFlags flags); +SaveOrLoadResult LoadWithFilter(struct LoadFilter *reader); +bool IsNetworkServerSave(); +bool IsScenarioSave(); + +typedef void ChunkSaveLoadProc(); +typedef void AutolengthProc(void *arg); + +void SlUnreachablePlaceholder(); + +enum ChunkSaveLoadSpecialOp { + CSLSO_PRE_LOAD, + CSLSO_PRE_LOADCHECK, + CSLSO_SHOULD_SAVE_CHUNK, +}; +enum ChunkSaveLoadSpecialOpResult { + CSLSOR_NONE, + CSLSOR_LOAD_CHUNK_CONSUMED, + CSLSOR_DONT_SAVE_CHUNK, +}; +typedef ChunkSaveLoadSpecialOpResult ChunkSaveLoadSpecialProc(uint32, ChunkSaveLoadSpecialOp); + +/** Type of a chunk. */ +enum ChunkType { + CH_RIFF = 0, + CH_ARRAY = 1, + CH_SPARSE_ARRAY = 2, + CH_EXT_HDR = 15, ///< Extended chunk header + + CH_UPSTREAM_SAVE = 0x80, +}; + +/** Handlers and description of chunk. */ +struct ChunkHandler { + uint32 id; ///< Unique ID (4 letters). + ChunkSaveLoadProc *save_proc; ///< Save procedure of the chunk. + ChunkSaveLoadProc *load_proc; ///< Load procedure of the chunk. + ChunkSaveLoadProc *ptrs_proc; ///< Manipulate pointers in the chunk. + ChunkSaveLoadProc *load_check_proc; ///< Load procedure for game preview. + ChunkType type; ///< Type of the chunk. @see ChunkType + ChunkSaveLoadSpecialProc *special_proc = nullptr; +}; + +template +void SlExecWithSlVersion(SaveLoadVersion use_version, F proc) +{ + extern SaveLoadVersion _sl_version; + SaveLoadVersion old_ver = _sl_version; + _sl_version = use_version; + auto guard = scope_guard([&]() { + _sl_version = old_ver; + }); + proc(); +} + +namespace upstream_sl { + template + ChunkHandler MakeUpstreamChunkHandler() + { + extern void SlLoadChunkByID(uint32); + extern void SlLoadCheckChunkByID(uint32); + extern void SlFixPointerChunkByID(uint32); + + ChunkHandler ch = { + id, + nullptr, + SlUnreachablePlaceholder, + []() { + SlExecWithSlVersion(F::GetLoadVersion(), []() { + SlFixPointerChunkByID(id); + }); + }, + SlUnreachablePlaceholder, + CH_UPSTREAM_SAVE + }; + ch.special_proc = [](uint32 chunk_id, ChunkSaveLoadSpecialOp op) -> ChunkSaveLoadSpecialOpResult { + assert(id == chunk_id); + switch (op) { + case CSLSO_PRE_LOAD: + SlExecWithSlVersion(F::GetLoadVersion(), []() { + SlLoadChunkByID(id); + }); + return CSLSOR_LOAD_CHUNK_CONSUMED; + case CSLSO_PRE_LOADCHECK: + SlExecWithSlVersion(F::GetLoadVersion(), []() { + SlLoadCheckChunkByID(id); + }); + return CSLSOR_LOAD_CHUNK_CONSUMED; + default: + return CSLSOR_NONE; + } + }; + return ch; + } +} + +using upstream_sl::MakeUpstreamChunkHandler; + +struct NullStruct { + byte null; +}; + +/** A table of ChunkHandler entries. */ +using ChunkHandlerTable = span; + +/** Type of reference (#SLE_REF, #SLE_CONDREF). */ +enum SLRefType { + REF_ORDER = 0, ///< Load/save a reference to an order. + REF_VEHICLE = 1, ///< Load/save a reference to a vehicle. + REF_STATION = 2, ///< Load/save a reference to a station. + REF_TOWN = 3, ///< Load/save a reference to a town. + REF_VEHICLE_OLD = 4, ///< Load/save an old-style reference to a vehicle (for pre-4.4 savegames). + REF_ROADSTOPS = 5, ///< Load/save a reference to a bus/truck stop. + REF_ENGINE_RENEWS = 6, ///< Load/save a reference to an engine renewal (autoreplace). + REF_CARGO_PACKET = 7, ///< Load/save a reference to a cargo packet. + REF_ORDERLIST = 8, ///< Load/save a reference to an orderlist. + REF_STORAGE = 9, ///< Load/save a reference to a persistent storage. + REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph. + REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job. + REF_TEMPLATE_VEHICLE = 12, ///< Load/save a reference to a template vehicle +}; + +/** Flags for chunk extended headers */ +enum SaveLoadChunkExtHeaderFlags { + SLCEHF_BIG_RIFF = 1 << 0, ///< This block uses a 60-bit RIFF chunk size +}; +DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) + +/** + * Storage of simple variables, references (pointers), and arrays. + * @param cmd Load/save type. @see SaveLoadType + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + * @note In general, it is better to use one of the SLE_* macros below. + */ +#define SLE_GENERAL_X(cmd, base, variable, type, length, from, to, extver) SaveLoad {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable), cpp_sizeof(base, variable), extver} +#define SLE_GENERAL(cmd, base, variable, type, length, from, to) SLE_GENERAL_X(cmd, base, variable, type, length, from, to, SlXvFeatureTest()) + +/** + * Storage of a variable in some savegame versions. + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDVAR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VAR, base, variable, type, 0, from, to, extver) +#define SLE_CONDVAR(base, variable, type, from, to) SLE_CONDVAR_X(base, variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a reference in some savegame versions. + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Type of the reference, a value from #SLRefType. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDREF_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REF, base, variable, type, 0, from, to, extver) +#define SLE_CONDREF(base, variable, type, from, to) SLE_CONDREF_X(base, variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a fixed-size array of #SL_VAR elements in some savegame versions. + * @param base Name of the class or struct containing the array. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param length Number of elements in the array. + * @param from First savegame version that has the array. + * @param to Last savegame version that has the array. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDARR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_ARR, base, variable, type, length, from, to, extver) +#define SLE_CONDARR(base, variable, type, length, from, to) SLE_CONDARR_X(base, variable, type, length, from, to, SlXvFeatureTest()) + +/** + * Storage of a string in some savegame versions. + * @param base Name of the class or struct containing the string. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param length Number of elements in the string (only used for fixed size buffers). + * @param from First savegame version that has the string. + * @param to Last savegame version that has the string. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDSTR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_STR, base, variable, type, length, from, to, extver) +#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_CONDSTR_X(base, variable, type, length, from, to, SlXvFeatureTest()) + +/** + * Storage of a \c std::string in some savegame versions. + * @param base Name of the class or struct containing the string. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the string. + * @param to Last savegame version that has the string. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDSSTR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_STDSTR, base, variable, type, 0, from, to, extver) +#define SLE_CONDSSTR(base, variable, type, from, to) SLE_GENERAL(SL_STDSTR, base, variable, type, 0, from, to) + +/** + * Storage of a list of #SL_REF elements in some savegame versions. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDREFLIST_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REFLIST, base, variable, type, 0, from, to, extver) +#define SLE_CONDREFLIST(base, variable, type, from, to) SLE_CONDREFLIST_X(base, variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a deque in some savegame versions. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDPTRDEQ_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_PTRDEQ, base, variable, type, 0, from, to, extver) +#define SLE_CONDPTRDEQ(base, variable, type, from, to) SLE_CONDPTRDEQ_X(base, variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a vector in some savegame versions. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDVEC_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VEC, base, variable, type, 0, from, to, extver) +#define SLE_CONDVEC(base, variable, type, from, to) SLE_CONDVEC_X(base, variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a variable vector in some savegame versions. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDVARVEC_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VARVEC, base, variable, type, 0, from, to, extver) +#define SLE_CONDVARVEC(base, variable, type, from, to) SLE_CONDVARVEC_X(base, variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a deque of #SL_VAR elements in some savegame versions. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDDEQUE_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_DEQUE, base, variable, type, 0, from, to, extver) +#define SLE_CONDDEQUE(base, variable, type, from, to) SLE_CONDDEQUE_X(base, variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a variable in every version of a savegame. + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_VAR(base, variable, type) SLE_CONDVAR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a reference in every version of a savegame. + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Type of the reference, a value from #SLRefType. + */ +#define SLE_REF(base, variable, type) SLE_CONDREF(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of fixed-size array of #SL_VAR elements in every version of a savegame. + * @param base Name of the class or struct containing the array. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param length Number of elements in the array. + */ +#define SLE_ARR(base, variable, type, length) SLE_CONDARR(base, variable, type, length, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a string in every savegame version. + * @param base Name of the class or struct containing the string. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param length Number of elements in the string (only used for fixed size buffers). + */ +#define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a \c std::string in every savegame version. + * @param base Name of the class or struct containing the string. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_SSTR(base, variable, type) SLE_CONDSSTR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a list of #SL_REF elements in every savegame version. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_REFLIST(base, variable, type) SLE_CONDREFLIST(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a deque in every savegame version. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_PTRDEQ(base, variable, type) SLE_CONDPTRDEQ(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a vector in every savegame version. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_VEC(base, variable, type) SLE_CONDVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a variable vector in every savegame version. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_VARVEC(base, variable, type) SLE_CONDVARVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Empty space in every savegame version. + * @param length Length of the empty space. + */ +#define SLE_NULL(length) SLE_CONDNULL(length, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Empty space in some savegame versions. + * @param length Length of the empty space. + * @param from First savegame version that has the empty space. + * @param to Last savegame version that has the empty space. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have empty space + */ +#define SLE_CONDNULL_X(length, from, to, extver) SLE_CONDARR_X(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, extver) +#define SLE_CONDNULL(length, from, to) SLE_CONDNULL_X(length, from, to, SlXvFeatureTest()) + +/** Translate values ingame to different values in the savegame and vv. */ +#define SLE_WRITEBYTE(base, variable) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION) + +#define SLE_VEH_INCLUDE() {false, SL_VEH_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, SlXvFeatureTest()} +#define SLE_ST_INCLUDE() {false, SL_ST_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, SlXvFeatureTest()} + +/** + * Storage of global simple variables, references (pointers), and arrays. + * @param cmd Load/save type. @see SaveLoadType + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + * @note In general, it is better to use one of the SLEG_* macros below. + */ +#define SLEG_GENERAL_X(cmd, variable, type, length, from, to, extver) SaveLoad {true, cmd, type, length, from, to, (void*)&variable, sizeof(variable), extver} +#define SLEG_GENERAL(cmd, variable, type, length, from, to) SLEG_GENERAL_X(cmd, variable, type, length, from, to, SlXvFeatureTest()) + +/** + * Storage of a global variable in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDVAR_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VAR, variable, type, 0, from, to, extver) +#define SLEG_CONDVAR(variable, type, from, to) SLEG_CONDVAR_X(variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a global reference in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDREF_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_REF, variable, type, 0, from, to, extver) +#define SLEG_CONDREF(variable, type, from, to) SLEG_CONDREF_X(variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a global fixed-size array of #SL_VAR elements in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param length Number of elements in the array. + * @param from First savegame version that has the array. + * @param to Last savegame version that has the array. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDARR_X(variable, type, length, from, to, extver) SLEG_GENERAL_X(SL_ARR, variable, type, length, from, to, extver) +#define SLEG_CONDARR(variable, type, length, from, to) SLEG_CONDARR_X(variable, type, length, from, to, SlXvFeatureTest()) + +/** + * Storage of a global string in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param length Number of elements in the string (only used for fixed size buffers). + * @param from First savegame version that has the string. + * @param to Last savegame version that has the string. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDSTR_X(variable, type, length, from, to, extver) SLEG_GENERAL_X(SL_STR, variable, type, length, from, to, extver) +#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_CONDSTR_X(variable, type, length, from, to, SlXvFeatureTest()) + +/** + * Storage of a global \c std::string in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the string. + * @param to Last savegame version that has the string. + */ +#define SLEG_CONDSSTR_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_STDSTR, variable, type, 0, from, to, extver) +#define SLEG_CONDSSTR(variable, type, from, to) SLEG_GENERAL(SL_STDSTR, variable, type, 0, from, to) + +/** + * Storage of a global reference list in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDREFLIST_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_REFLIST, variable, type, 0, from, to, extver) +#define SLEG_CONDREFLIST(variable, type, from, to) SLEG_CONDREFLIST_X(variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a global deque in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDPTRDEQ_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_PTRDEQ, variable, type, 0, from, to, extver) +#define SLEG_CONDPTRDEQ(variable, type, from, to) SLEG_CONDPTRDEQ_X(variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a global vector in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDVEC_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VEC, variable, type, 0, from, to, extver) +#define SLEG_CONDVEC(variable, type, from, to) SLEG_CONDVEC_X(variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a variable vector in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDVARVEC_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VARVEC, variable, type, 0, from, to, extver) +#define SLEG_CONDVARVEC(variable, type, from, to) SLEG_CONDVARVEC_X(variable, type, from, to, SlXvFeatureTest()) + +/** + * Storage of a global variable in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_VAR(variable, type) SLEG_CONDVAR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a global reference in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_REF(variable, type) SLEG_CONDREF(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a global fixed-size array of #SL_VAR elements in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_ARR(variable, type) SLEG_CONDARR(variable, type, lengthof(variable), SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a global string in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, sizeof(variable), SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a global \c std::string in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_SSTR(variable, type) SLEG_CONDSSTR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a global reference list in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_REFLIST(variable, type) SLEG_CONDREFLIST(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a global deque in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_PTRDEQ(variable, type) SLEG_CONDPTRDEQ(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Storage of a global vector in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_VEC(variable, type) SLEG_CONDVEC(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** + * Empty global space in some savegame versions. + * @param length Length of the empty space. + * @param from First savegame version that has the empty space. + * @param to Last savegame version that has the empty space. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have empty space + */ +#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, (void*)nullptr, SlXvFeatureTest()} + +/** + * Checks whether the savegame is below \a major.\a minor. + * @param major Major number of the version to check against. + * @param minor Minor number of the version to check against. If \a minor is 0 or not specified, only the major number is checked. + * @return Savegame version is earlier than the specified version. + */ +static inline bool IsSavegameVersionBefore(SaveLoadVersion major, byte minor = 0) +{ + extern SaveLoadVersion _sl_version; + extern byte _sl_minor_version; + return _sl_version < major || (minor > 0 && _sl_version == major && _sl_minor_version < minor); +} + +/** + * Checks whether the savegame is below or at \a major. This should be used to repair data from existing + * savegames which is no longer corrupted in new savegames, but for which otherwise no savegame + * bump is required. + * @param major Major number of the version to check against. + * @return Savegame version is at most the specified version. + */ +static inline bool IsSavegameVersionUntil(SaveLoadVersion major) +{ + extern SaveLoadVersion _sl_version; + return _sl_version <= major; +} + +/** + * Checks if some version from/to combination falls within the range of the + * active savegame version. + * @param version_from Inclusive savegame version lower bound. + * @param version_to Exclusive savegame version upper bound. SL_MAX_VERSION if no upper bound. + * @return Active savegame version falls within the given range. + */ +static inline bool SlIsObjectCurrentlyValid(SaveLoadVersion version_from, SaveLoadVersion version_to, SlXvFeatureTest ext_feature_test) +{ + extern const SaveLoadVersion SAVEGAME_VERSION; + if (!ext_feature_test.IsFeaturePresent(_sl_xv_feature_static_versions, SAVEGAME_VERSION, version_from, 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 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 file-type + * @return the SLE_FILE_* part of a variable-type description + */ +static inline VarType GetVarFileType(VarType type) +{ + return type & 0xF; // GB(type, 0, 4); +} + +/** + * Check if the given saveload type is a numeric type. + * @param conv the type to check + * @return True if it's a numeric type. + */ +static inline bool IsNumericType(VarType conv) +{ + return GetVarMemType(conv) <= SLE_VAR_U64; +} + +/** + * Get the address of the variable. Which one to pick depends on the object + * pointer. If it is nullptr 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) +{ + /* Entry is a global address. */ + if (sld.global) return sld.address; + +#ifdef _DEBUG + /* Entry is a null-variable, mostly used to read old savegames etc. */ + if (GetVarMemType(sld.conv) == SLE_VAR_NULL) { + assert(sld.address == nullptr); + return nullptr; + } + + /* Everything else should be a non-null pointer. */ + assert(object != nullptr); +#endif + return const_cast((const byte *)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); +std::vector SlSaveToVector(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 SaveLoadTable &slt); + +struct SlLoadFromBufferState { + size_t old_obj_len; + byte *old_bufp; + byte *old_bufe; +}; + +/** + * Run proc, loading exactly length bytes from the contents of buffer + * @param proc The callback procedure that is called + */ +template +void SlLoadFromBuffer(const byte *buffer, size_t length, F proc) +{ + extern SlLoadFromBufferState SlLoadFromBufferSetup(const byte *buffer, size_t length); + extern void SlLoadFromBufferRestore(const SlLoadFromBufferState &state, const byte *buffer, size_t length); + + SlLoadFromBufferState state = SlLoadFromBufferSetup(buffer, length); + proc(); + SlLoadFromBufferRestore(state, buffer, length); +} + +void SlGlobList(const SaveLoadTable &slt); +void SlArray(void *array, size_t length, VarType conv); +void SlObject(void *object, const SaveLoadTable &slt); +bool SlObjectMember(void *object, const SaveLoad &sld); + +std::vector SlFilterObject(const SaveLoadTable &slt); +void SlObjectSaveFiltered(void *object, const SaveLoadTable &slt); +void SlObjectLoadFiltered(void *object, const SaveLoadTable &slt); +void SlObjectPtrOrNullFiltered(void *object, const SaveLoadTable &slt); + +void NORETURN CDECL SlErrorFmt(StringID string, const char *msg, ...) WARN_FORMAT(2, 3); + +bool SaveloadCrashWithMissingNewGRFs(); + +void SlResetVENC(); +void SlProcessVENC(); + +void SlResetTNNC(); + +extern std::string _savegame_format; +extern bool _do_autosave; + +#endif /* SL_SAVELOAD_H */ diff --git a/src/saveload/saveload_buffer.h b/src/sl/saveload_buffer.h similarity index 98% rename from src/saveload/saveload_buffer.h rename to src/sl/saveload_buffer.h index 5cde4f9640..6148fd13f9 100644 --- a/src/saveload/saveload_buffer.h +++ b/src/sl/saveload_buffer.h @@ -7,8 +7,8 @@ /** @file saveload_buffer.h Functions/types related to buffers used for saving and loading games. */ -#ifndef SAVELOAD_BUFFER_H -#define SAVELOAD_BUFFER_H +#ifndef SL_SAVELOAD_BUFFER_H +#define SL_SAVELOAD_BUFFER_H #include "../core/alloc_func.hpp" #include "../core/endian_type.hpp" @@ -261,4 +261,4 @@ struct MemoryDumper { std::pair StopAutoLength(); }; -#endif +#endif /* SL_SAVELOAD_BUFFER_H */ diff --git a/src/saveload/saveload_common.h b/src/sl/saveload_common.h similarity index 99% rename from src/saveload/saveload_common.h rename to src/sl/saveload_common.h index 94957a1553..1aafac5584 100644 --- a/src/saveload/saveload_common.h +++ b/src/sl/saveload_common.h @@ -7,8 +7,8 @@ /** @file saveload_common.h Common functions/types for saving and loading games. */ -#ifndef SAVELOAD_COMMON_H -#define SAVELOAD_COMMON_H +#ifndef SL_SAVELOAD_COMMON_H +#define SL_SAVELOAD_COMMON_H #include "../strings_type.h" #include "../core/span_type.hpp" @@ -419,4 +419,4 @@ void NORETURN CDECL SlErrorCorruptFmt(const char *format, ...) WARN_FORMAT(1, 2) bool SaveLoadFileTypeIsScenario(); -#endif +#endif /* SL_SAVELOAD_COMMON_H */ diff --git a/src/saveload/saveload_filter.h b/src/sl/saveload_filter.h similarity index 96% rename from src/saveload/saveload_filter.h rename to src/sl/saveload_filter.h index 490daec872..1785167ddc 100644 --- a/src/saveload/saveload_filter.h +++ b/src/sl/saveload_filter.h @@ -7,8 +7,8 @@ /** @file saveload_filter.h Declaration of filters used for saving and loading savegames. */ -#ifndef SAVELOAD_FILTER_H -#define SAVELOAD_FILTER_H +#ifndef SL_SAVELOAD_FILTER_H +#define SL_SAVELOAD_FILTER_H /** Interface for filtering a savegame till it is loaded. */ struct LoadFilter { @@ -102,4 +102,4 @@ template SaveFilter *CreateSaveFilter(SaveFilter *chain, byte compr return new T(chain, compression_level); } -#endif /* SAVELOAD_FILTER_H */ +#endif /* SL_SAVELOAD_FILTER_H */ diff --git a/src/saveload/saveload_internal.h b/src/sl/saveload_internal.h similarity index 95% rename from src/saveload/saveload_internal.h rename to src/sl/saveload_internal.h index 41f4a8367d..3ed4443b8f 100644 --- a/src/saveload/saveload_internal.h +++ b/src/sl/saveload_internal.h @@ -7,8 +7,8 @@ /** @file saveload_internal.h Declaration of functions used in more save/load files */ -#ifndef SAVELOAD_INTERNAL_H -#define SAVELOAD_INTERNAL_H +#ifndef SL_SAVELOAD_INTERNAL_H +#define SL_SAVELOAD_INTERNAL_H #include "../company_manager_face.h" #include "../order_base.h" @@ -67,4 +67,4 @@ CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face); Order UnpackOldOrder(uint16 packed); -#endif /* SAVELOAD_INTERNAL_H */ +#endif /* SL_SAVELOAD_INTERNAL_H */ diff --git a/src/saveload/saveload_types.h b/src/sl/saveload_types.h similarity index 98% rename from src/saveload/saveload_types.h rename to src/sl/saveload_types.h index 5e59328d59..2cadf73714 100644 --- a/src/saveload/saveload_types.h +++ b/src/sl/saveload_types.h @@ -7,8 +7,8 @@ /** @file saveload_common.h Common functions/types for saving and loading games. */ -#ifndef SAVELOAD_TYPES_H -#define SAVELOAD_TYPES_H +#ifndef SL_SAVELOAD_TYPES_H +#define SL_SAVELOAD_TYPES_H #include "saveload_common.h" #include "extended_ver_sl.h" @@ -132,4 +132,4 @@ struct SaveLoad { SlXvFeatureTest ext_feature_test; ///< extended feature test }; -#endif +#endif /* SL_SAVELOAD_TYPES_H */ diff --git a/src/saveload/signal_sl.cpp b/src/sl/signal_sl.cpp similarity index 100% rename from src/saveload/signal_sl.cpp rename to src/sl/signal_sl.cpp diff --git a/src/sl/signs_sl.cpp b/src/sl/signs_sl.cpp new file mode 100644 index 0000000000..181dc6b7b7 --- /dev/null +++ b/src/sl/signs_sl.cpp @@ -0,0 +1,69 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file signs_sl.cpp Code handling saving and loading of economy data */ + +#include "../stdafx.h" +#include "../signs_base.h" +#include "../fios.h" + +#include "saveload.h" + +#include "../safeguards.h" + +/** Description of a sign within the savegame. */ +static const SaveLoad _sign_desc[] = { + SLE_CONDVAR(Sign, name, SLE_NAME, SL_MIN_VERSION, SLV_84), + SLE_CONDSSTR(Sign, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION), + SLE_CONDVAR(Sign, x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Sign, y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Sign, x, SLE_INT32, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Sign, y, SLE_INT32, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Sign, owner, SLE_UINT8, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR_X(Sign, z, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)), + SLE_CONDVAR_X(Sign, z, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)), +}; + +/** Save all signs */ +static void Save_SIGN() +{ + for (Sign *si : Sign::Iterate()) { + 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); + /* Before version 6.1, signs didn't have owner. + * Before version 83, invalid signs were determined by si->str == 0. + * Before version 103, owner could be a bankrupted company. + * - we can't use IsValidCompany() now, so this is fixed in AfterLoadGame() + * All signs that were saved are valid (including those with just 'Sign' and INVALID_OWNER). + * - so set owner to OWNER_NONE if needed (signs from pre-version 6.1 would be lost) */ + if (IsSavegameVersionBefore(SLV_6, 1) || (IsSavegameVersionBefore(SLV_83) && si->owner == INVALID_OWNER)) { + si->owner = OWNER_NONE; + } + + /* Signs placed in scenario editor shall now be OWNER_DEITY */ + if (IsSavegameVersionBefore(SLV_171) && si->owner == OWNER_NONE && _file_to_saveload.abstract_ftype == FT_SCENARIO) { + si->owner = OWNER_DEITY; + } + } +} + +/** Chunk handlers related to signs. */ +static const ChunkHandler sign_chunk_handlers[] = { + { 'SIGN', Save_SIGN, Load_SIGN, nullptr, nullptr, CH_ARRAY }, +}; + +extern const ChunkHandlerTable _sign_chunk_handlers(sign_chunk_handlers); diff --git a/src/sl/station_sl.cpp b/src/sl/station_sl.cpp new file mode 100644 index 0000000000..d802077f00 --- /dev/null +++ b/src/sl/station_sl.cpp @@ -0,0 +1,836 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file station_sl.cpp Code handling saving and loading of stations. */ + +#include "../stdafx.h" +#include "../station_base.h" +#include "../waypoint_base.h" +#include "../roadstop_base.h" +#include "../vehicle_base.h" +#include "../newgrf_station.h" +#include "../newgrf_roadstop.h" +#include "../core/math_func.hpp" + +#include "saveload.h" +#include "saveload_buffer.h" +#include "table/strings.h" + +#include "../safeguards.h" + +static byte _old_last_vehicle_type; +static uint8 _num_specs; +static uint8 _num_roadstop_specs; +static uint32 _num_roadstop_custom_tiles; +static std::vector _custom_road_stop_tiles; +static std::vector _custom_road_stop_data; + +/** + * Update the buoy orders to be waypoint orders. + * @param o the order 'list' to check. + */ +static void UpdateWaypointOrder(Order *o) +{ + if (!o->IsType(OT_GOTO_STATION)) return; + + const Station *st = Station::Get(o->GetDestination()); + if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) return; + + o->MakeGoToWaypoint(o->GetDestination()); +} + +/** + * Perform all steps to upgrade from the old station buoys to the new version + * that uses waypoints. This includes some old saveload mechanics. + */ +void MoveBuoysToWaypoints() +{ + /* Buoy orders become waypoint orders */ + for (OrderList *ol : OrderList::Iterate()) { + VehicleType vt = ol->GetFirstSharedVehicle()->type; + if (vt != VEH_SHIP && vt != VEH_TRAIN) continue; + + for (Order *o = ol->GetFirstOrder(); o != nullptr; o = o->next) UpdateWaypointOrder(o); + } + + for (Vehicle *v : Vehicle::Iterate()) { + VehicleType vt = v->type; + if (vt != VEH_SHIP && vt != VEH_TRAIN) continue; + + UpdateWaypointOrder(&v->current_order); + } + + /* Now make the stations waypoints */ + for (Station *st : Station::Iterate()) { + if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) continue; + + StationID index = st->index; + TileIndex xy = st->xy; + Town *town = st->town; + StringID string_id = st->string_id; + TinyString name = std::move(st->name); + Date build_date = st->build_date; + /* TTDPatch could use "buoys with rail station" for rail waypoints */ + bool train = st->train_station.tile != INVALID_TILE; + TileArea train_st = st->train_station; + + /* Delete the station, so we can make it a real waypoint. */ + delete st; + + /* Stations and waypoints are in the same pool, so if a station + * is deleted there must be place for a Waypoint. */ + assert(Waypoint::CanAllocateItem()); + Waypoint *wp = new (index) Waypoint(xy); + wp->town = town; + wp->string_id = train ? STR_SV_STNAME_WAYPOINT : STR_SV_STNAME_BUOY; + wp->name = std::move(name); + wp->delete_ctr = 0; // Just reset delete counter for once. + wp->build_date = build_date; + wp->owner = train ? GetTileOwner(xy) : OWNER_NONE; + + if (IsInsideBS(string_id, STR_SV_STNAME_BUOY, 9)) wp->town_cn = string_id - STR_SV_STNAME_BUOY; + + if (train) { + /* When we make a rail waypoint of the station, convert the map as well. */ + for (TileIndex t : train_st) { + if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != index) continue; + + SB(_me[t].m6, 3, 3, STATION_WAYPOINT); + wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE); + } + + wp->train_station = train_st; + wp->facilities |= FACIL_TRAIN; + } else if (IsBuoyTile(xy) && GetStationIndex(xy) == index) { + wp->rect.BeforeAddTile(xy, StationRect::ADD_FORCE); + wp->facilities |= FACIL_DOCK; + } + } +} + +void AfterLoadStations() +{ + /* Update the speclists of all stations to point to the currently loaded custom stations. */ + for (BaseStation *st : BaseStation::Iterate()) { + for (uint i = 0; i < st->speclist.size(); i++) { + if (st->speclist[i].grfid == 0) continue; + + st->speclist[i].spec = StationClass::GetByGrf(st->speclist[i].grfid, st->speclist[i].localidx, nullptr); + } + for (uint i = 0; i < st->roadstop_speclist.size(); i++) { + if (st->roadstop_speclist[i].grfid == 0) continue; + + st->roadstop_speclist[i].spec = RoadStopClass::GetByGrf(st->roadstop_speclist[i].grfid, st->roadstop_speclist[i].localidx, nullptr); + } + + if (Station::IsExpected(st)) { + Station *sta = Station::From(st); + for (const RoadStop *rs = sta->bus_stops; rs != nullptr; rs = rs->next) sta->bus_station.Add(rs->xy); + for (const RoadStop *rs = sta->truck_stops; rs != nullptr; rs = rs->next) sta->truck_station.Add(rs->xy); + } + + StationUpdateCachedTriggers(st); + StationUpdateRoadStopCachedTriggers(st); + } +} + +/** + * (Re)building of road stop caches after loading a savegame. + */ +void AfterLoadRoadStops() +{ + /* First construct the drive through entries */ + for (RoadStop *rs : RoadStop::Iterate()) { + if (IsDriveThroughStopTile(rs->xy)) rs->MakeDriveThrough(); + } + /* And then rebuild the data in those entries */ + for (RoadStop *rs : RoadStop::Iterate()) { + if (!HasBit(rs->status, RoadStop::RSSFB_BASE_ENTRY)) continue; + + rs->GetEntry(DIAGDIR_NE)->Rebuild(rs); + rs->GetEntry(DIAGDIR_NW)->Rebuild(rs); + } +} + +static const SaveLoad _roadstop_desc[] = { + SLE_VAR(RoadStop, xy, SLE_UINT32), + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_45), + SLE_VAR(RoadStop, status, SLE_UINT8), + /* Index was saved in some versions, but this is not needed */ + SLE_CONDNULL(4, SL_MIN_VERSION, SLV_9), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_45), + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_26), + + SLE_REF(RoadStop, next, REF_ROADSTOPS), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_45), + + SLE_CONDNULL(4, SL_MIN_VERSION, SLV_25), + SLE_CONDNULL(1, SLV_25, SLV_26), +}; + +static const SaveLoad _old_station_desc[] = { + SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Station, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDNULL(4, SL_MIN_VERSION, SLV_6), ///< bus/lorry tile + SLE_CONDVAR(Station, train_station.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Station, train_station.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Station, airport.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Station, airport.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), + SLE_CONDNULL(4, SLV_6, SLV_MULTITILE_DOCKS), + SLE_REF(Station, town, REF_TOWN), + SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16), + SLE_CONDVAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SL_MAX_VERSION), + + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_4), ///< alpha_order + + SLE_VAR(Station, string_id, SLE_STRINGID), + SLE_CONDSTR(Station, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), + SLE_CONDVAR(Station, indtype, SLE_UINT8, SLV_103, SL_MAX_VERSION), + SLE_CONDVAR(Station, had_vehicle_of_type, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SLV_122), + SLE_CONDVAR(Station, had_vehicle_of_type, SLE_UINT8, SLV_122, SL_MAX_VERSION), + + SLE_VAR(Station, time_since_load, SLE_UINT8), + SLE_VAR(Station, time_since_unload, SLE_UINT8), + SLE_CONDVAR_X(Station, delete_ctr, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 0, 3)), + SLE_CONDVAR_X(Station, delete_ctr, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), + SLE_VAR(Station, owner, SLE_UINT8), + SLE_VAR(Station, facilities, SLE_UINT8), + SLE_VAR(Station, airport.type, SLE_UINT8), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), ///< Truck/bus stop status + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_5), ///< Blocked months + + SLE_CONDVAR(Station, airport.flags, SLE_VAR_U64 | SLE_FILE_U16, SL_MIN_VERSION, SLV_3), + SLE_CONDVAR(Station, airport.flags, SLE_VAR_U64 | SLE_FILE_U32, SLV_3, SLV_46), + SLE_CONDVAR(Station, airport.flags, SLE_UINT64, SLV_46, SL_MAX_VERSION), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_26), ///< last-vehicle + SLEG_CONDVAR_X(_old_last_vehicle_type, SLE_UINT8, SLV_26, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 0, 0)), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + + SLE_CONDNULL(2, SLV_3, SLV_26), ///< custom station class and id + SLE_CONDVAR(Station, build_date, SLE_FILE_U16 | SLE_VAR_I32, SLV_3, SLV_31), + SLE_CONDVAR(Station, build_date, SLE_INT32, SLV_31, SL_MAX_VERSION), + + SLE_CONDREF(Station, bus_stops, REF_ROADSTOPS, SLV_6, SL_MAX_VERSION), + SLE_CONDREF(Station, truck_stops, REF_ROADSTOPS, SLV_6, SL_MAX_VERSION), + + /* Used by newstations for graphic variations */ + SLE_CONDVAR(Station, random_bits, SLE_UINT16, SLV_27, SL_MAX_VERSION), + SLE_CONDVAR(Station, waiting_triggers, SLE_UINT8, SLV_27, SL_MAX_VERSION), + SLEG_CONDVAR(_num_specs, SLE_UINT8, SLV_27, SL_MAX_VERSION), + + SLE_CONDVEC(Station, loading_vehicles, REF_VEHICLE, SLV_57, SL_MAX_VERSION), + + /* reserve extra space in savegame here. (currently 32 bytes) */ + SLE_CONDNULL(32, SLV_2, SL_MAX_VERSION), +}; + +static uint16 _waiting_acceptance; +static uint32 _num_flows; +static uint16 _cargo_source; +static uint32 _cargo_source_xy; +static uint8 _cargo_days; +static Money _cargo_feeder_share; + +static const SaveLoad _station_speclist_desc[] = { + SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION), + SLE_CONDVAR_X(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 0, 1)), + SLE_CONDVAR_X(StationSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 2)), +}; + +static const SaveLoad _roadstop_speclist_desc[] = { + SLE_CONDVAR(RoadStopSpecList, grfid, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION), + SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 0, 2)), + SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 3)), +}; + +CargoPacketList _packets; +uint32 _num_dests; + +struct FlowSaveLoad { + FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {} + StationID source; + StationID via; + uint32 share; + bool restricted; +}; + +#if 0 +static const SaveLoad _flow_desc[] = { + SLE_VAR(FlowSaveLoad, source, SLE_UINT16), + SLE_VAR(FlowSaveLoad, via, SLE_UINT16), + SLE_VAR(FlowSaveLoad, share, SLE_UINT32), + SLE_CONDVAR(FlowSaveLoad, restricted, SLE_BOOL, SLV_187, SL_MAX_VERSION), +}; +#endif + +/** + * Wrapper function to get the GoodsEntry's internal structure while + * some of the variables itself are private. + * @return the saveload description for GoodsEntry. + */ +SaveLoadTable GetGoodsDesc() +{ + static const SaveLoad goods_desc[] = { + SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68), + SLE_CONDVAR(GoodsEntry, status, SLE_UINT8, SLV_68, SL_MAX_VERSION), + SLE_CONDNULL(2, SLV_51, SLV_68), + SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8), + SLE_CONDNULL_X(6, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), + SLE_VAR(GoodsEntry, rating, SLE_UINT8), + SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), + SLEG_CONDVAR( _cargo_source, SLE_UINT16, SLV_7, SLV_68), + SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), + SLEG_CONDVAR( _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68), + 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, SLV_14, SLV_65), + SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), + SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION), + SLEG_CONDPTRDEQ_X( _packets, REF_CARGO_PACKET, SLV_68, SLV_183, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, 0, 0)), + SLEG_CONDVAR_X( _num_dests, SLE_UINT32, SLV_183, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP)), + SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION), + SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION), + SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION), + SLEG_CONDVAR( _num_flows, SLE_UINT32, SLV_183, SL_MAX_VERSION), + SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + SLE_CONDVAR_X(GoodsEntry, last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 1)), + }; + + return goods_desc; +} + +typedef std::pair StationCargoPair; + +static const SaveLoad _cargo_list_desc[] = { + SLE_VAR(StationCargoPair, first, SLE_UINT16), + SLE_PTRDEQ(StationCargoPair, second, REF_CARGO_PACKET), +}; + +/** + * Swap the temporary packets with the packets without specific destination in + * the given goods entry. Assert that at least one of those is empty. + * @param ge Goods entry to swap with. + */ +static void SwapPackets(GoodsEntry *ge) +{ + StationCargoPacketMap &ge_packets = const_cast(*ge->cargo.Packets()); + + if (_packets.empty()) { + std::map::iterator it(ge_packets.find(INVALID_STATION)); + if (it == ge_packets.end()) { + return; + } else { + it->second.swap(_packets); + } + } else { + assert(ge_packets[INVALID_STATION].empty()); + ge_packets[INVALID_STATION].swap(_packets); + } +} + +static void Load_STNS() +{ + _cargo_source_xy = 0; + _cargo_days = 0; + _cargo_feeder_share = 0; + _num_specs = 0; + + uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + int index; + while ((index = SlIterateArray()) != -1) { + Station *st = new (index) Station(); + + SlObject(st, _old_station_desc); + + _waiting_acceptance = 0; + + for (CargoID i = 0; i < num_cargo; i++) { + GoodsEntry *ge = &st->goods[i]; + SlObject(ge, GetGoodsDesc()); + SwapPackets(ge); + if (IsSavegameVersionBefore(SLV_68)) { + SB(ge->status, GoodsEntry::GES_ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); + if (GB(_waiting_acceptance, 0, 12) != 0) { + /* In old versions, enroute_from used 0xFF as INVALID_STATION */ + StationID source = (IsSavegameVersionBefore(SLV_7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source; + + /* Make sure we can allocate the CargoPacket. This is safe + * as there can only be ~64k stations and 32 cargoes in these + * savegame versions. As the CargoPacketPool has more than + * 16 million entries; it fits by an order of magnitude. */ + assert(CargoPacket::CanAllocateItem()); + + /* Don't construct the packet with station here, because that'll fail with old savegames */ + CargoPacket *cp = new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share); + ge->cargo.Append(cp, INVALID_STATION); + SB(ge->status, GoodsEntry::GES_RATING, 1, 1); + } + } + if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) ge->last_vehicle_type = _old_last_vehicle_type; + } + + if (_num_specs != 0) { + /* Allocate speclist memory when loading a game */ + st->speclist.resize(_num_specs); + for (uint i = 0; i < st->speclist.size(); i++) { + SlObject(&st->speclist[i], _station_speclist_desc); + } + } + } +} + +static void Ptrs_STNS() +{ + /* Don't run when savegame version is higher than or equal to 123. */ + if (!IsSavegameVersionBefore(SLV_123)) return; + + uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + for (Station *st : Station::Iterate()) { + if (!IsSavegameVersionBefore(SLV_68)) { + for (CargoID i = 0; i < num_cargo; i++) { + GoodsEntry *ge = &st->goods[i]; + SwapPackets(ge); + SlObject(ge, GetGoodsDesc()); + SwapPackets(ge); + } + } + SlObject(st, _old_station_desc); + } +} + + +static const SaveLoad _base_station_desc[] = { + SLE_VAR(BaseStation, xy, SLE_UINT32), + SLE_REF(BaseStation, town, REF_TOWN), + SLE_VAR(BaseStation, string_id, SLE_STRINGID), + SLE_STR(BaseStation, name, SLE_STR | SLF_ALLOW_CONTROL, 0), + + SLE_CONDVAR_X(Station, delete_ctr, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 0, 3)), + SLE_CONDVAR_X(Station, delete_ctr, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), + SLE_VAR(BaseStation, owner, SLE_UINT8), + SLE_VAR(BaseStation, facilities, SLE_UINT8), + SLE_VAR(BaseStation, build_date, SLE_INT32), + + /* Used by newstations for graphic variations */ + SLE_VAR(BaseStation, random_bits, SLE_UINT16), + SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8), + SLEG_VAR(_num_specs, SLE_UINT8), + SLEG_CONDVAR_X(_num_roadstop_specs, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS)), + SLEG_CONDVARVEC_X(_custom_road_stop_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1)), + SLEG_CONDVARVEC_X(_custom_road_stop_data, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1)), + SLEG_CONDVAR_X(_num_roadstop_custom_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 2)), +}; + +static OldPersistentStorage _old_st_persistent_storage; + +static const SaveLoad _station_desc[] = { + SLE_WRITEBYTE(Station, facilities), + SLE_ST_INCLUDE(), + + SLE_VAR(Station, train_station.tile, SLE_UINT32), + SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16), + SLE_VAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16), + + SLE_REF(Station, bus_stops, REF_ROADSTOPS), + SLE_REF(Station, truck_stops, REF_ROADSTOPS), + SLE_CONDVAR_X(Station, ship_station.tile, SLE_UINT32, SL_MIN_VERSION, SLV_MULTITILE_DOCKS, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 0, 0)), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 1, 1)), + SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVARVEC_X(Station, docking_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 2)), + SLE_VAR(Station, airport.tile, SLE_UINT32), + SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), + SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), + SLE_VAR(Station, airport.type, SLE_UINT8), + SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6)), + SLE_VAR(Station, airport.flags, SLE_UINT64), + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6)), + SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION), + SLEG_CONDARR(_old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161), + SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), + + SLE_VAR(Station, indtype, SLE_UINT8), + SLE_CONDVAR_X(Station, extra_name_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_STATION_NAMES)), + + SLE_VAR(Station, time_since_load, SLE_UINT8), + SLE_VAR(Station, time_since_unload, SLE_UINT8), + SLEG_CONDVAR_X(_old_last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 0, 0)), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8), + SLE_VEC(Station, loading_vehicles, REF_VEHICLE), + SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES), + SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), + SLE_CONDNULL_X(32 * 24, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_22)), + SLE_CONDVAR_X(Station, station_cargo_history_cargoes, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATION_CARGO_HISTORY)), +}; + +static const SaveLoad _waypoint_desc[] = { + SLE_WRITEBYTE(Waypoint, facilities), + SLE_ST_INCLUDE(), + + SLE_VAR(Waypoint, town_cn, SLE_UINT16), + + SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), + SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), + SLE_CONDVAR_X(Waypoint, waypoint_flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_WAYPOINT_FLAGS)), + SLE_CONDVAR_X(Waypoint, road_waypoint_area.tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), + SLE_CONDVAR_X(Waypoint, road_waypoint_area.w, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), + SLE_CONDVAR_X(Waypoint, road_waypoint_area.h, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), +}; + +static const SaveLoad _custom_roadstop_tile_data_desc[] = { + SLE_VAR(RoadStopTileData, tile, SLE_UINT32), + SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8), + SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8), +}; + +/** + * Get the base station description to be used for SL_ST_INCLUDE + * @return the base station description. + */ +SaveLoadTable GetBaseStationDescription() +{ + return _base_station_desc; +} + +std::vector _filtered_station_desc; +std::vector _filtered_waypoint_desc; +std::vector _filtered_goods_desc; +std::vector _filtered_station_speclist_desc; +std::vector _filtered_roadstop_speclist_desc; + +static void SetupDescs_STNN() +{ + _filtered_station_desc = SlFilterObject(_station_desc); + _filtered_waypoint_desc = SlFilterObject(_waypoint_desc); + _filtered_goods_desc = SlFilterObject(GetGoodsDesc()); + _filtered_station_speclist_desc = SlFilterObject(_station_speclist_desc); + _filtered_roadstop_speclist_desc = SlFilterObject(_roadstop_speclist_desc); +} + +std::vector _filtered_roadstop_desc; + +static void SetupDescs_ROADSTOP() +{ + _filtered_roadstop_desc = SlFilterObject(_roadstop_desc); +} + + +static void RealSave_STNN(BaseStation *bst) +{ + _num_specs = (uint8)bst->speclist.size(); + _num_roadstop_specs = (uint8)bst->roadstop_speclist.size(); + _num_roadstop_custom_tiles = (uint32)bst->custom_roadstop_tile_data.size(); + + bool waypoint = (bst->facilities & FACIL_WAYPOINT) != 0; + SlObjectSaveFiltered(bst, waypoint ? SaveLoadTable(_filtered_waypoint_desc) : SaveLoadTable(_filtered_station_desc)); + + MemoryDumper *dumper = MemoryDumper::GetCurrent(); + + if (!waypoint) { + Station *st = Station::From(bst); + for (CargoID i = 0; i < NUM_CARGO; i++) { + _num_dests = (uint32)st->goods[i].cargo.Packets()->MapSize(); + _num_flows = (uint32)st->goods[i].flows.size(); + SlObjectSaveFiltered(&st->goods[i], _filtered_goods_desc); + for (FlowStatMap::const_iterator outer_it(st->goods[i].flows.begin()); outer_it != st->goods[i].flows.end(); ++outer_it) { + uint32 sum_shares = 0; + FlowSaveLoad flow; + flow.source = outer_it->GetOrigin(); + dumper->CheckBytes(2 + 4); + dumper->RawWriteUint16(flow.source); + dumper->RawWriteUint32((uint32)outer_it->size()); + FlowStat::const_iterator inner_it(outer_it->begin()); + const FlowStat::const_iterator end(outer_it->end()); + for (; inner_it != end; ++inner_it) { + flow.via = inner_it->second; + flow.share = inner_it->first - sum_shares; + flow.restricted = inner_it->first > outer_it->GetUnrestricted(); + sum_shares = inner_it->first; + assert(flow.share > 0); + + // SlObject(&flow, _flow_desc); /* this is highly performance-sensitive, manually unroll */ + dumper->CheckBytes(2 + 4 + 1); + dumper->RawWriteUint16(flow.via); + dumper->RawWriteUint32(flow.share); + dumper->RawWriteByte(flow.restricted != 0); + } + SlWriteUint16(outer_it->GetRawFlags()); + } + for (StationCargoPacketMap::ConstMapIterator it(st->goods[i].cargo.Packets()->begin()); it != st->goods[i].cargo.Packets()->end(); ++it) { + SlObjectSaveFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals + } + } + + assert(st->station_cargo_history.size() == CountBits(st->station_cargo_history_cargoes)); + dumper->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2); + for (const auto &history : st->station_cargo_history) { + uint i = st->station_cargo_history_offset; + do { + dumper->RawWriteUint16(history[i]); + i++; + if (i == MAX_STATION_CARGO_HISTORY_DAYS) i = 0; + } while (i != st->station_cargo_history_offset); + } + } + + for (uint i = 0; i < bst->speclist.size(); i++) { + SlObjectSaveFiltered(&bst->speclist[i], _filtered_station_speclist_desc); + } + + for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { + SlObjectSaveFiltered(&bst->roadstop_speclist[i], _filtered_roadstop_speclist_desc); + } + + for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { + SlObjectSaveFiltered(&bst->custom_roadstop_tile_data[i], _custom_roadstop_tile_data_desc); // _custom_roadstop_tile_data_desc has no conditionals + } +} + +static void Save_STNN() +{ + SetupDescs_STNN(); + + /* Write the stations */ + for (BaseStation *st : BaseStation::Iterate()) { + SlSetArrayIndex(st->index); + SlAutolength((AutolengthProc*)RealSave_STNN, st); + } +} + +static void Load_STNN() +{ + SetupDescs_STNN(); + + _num_flows = 0; + _num_specs = 0; + _num_roadstop_specs = 0; + _num_roadstop_custom_tiles = 0; + + const uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + ReadBuffer *buffer = ReadBuffer::GetCurrent(); + + int index; + while ((index = SlIterateArray()) != -1) { + bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0; + + BaseStation *bst = waypoint ? (BaseStation *)new (index) Waypoint() : new (index) Station(); + SlObjectLoadFiltered(bst, waypoint ? SaveLoadTable(_filtered_waypoint_desc) : SaveLoadTable(_filtered_station_desc)); + + if (!waypoint) { + Station *st = Station::From(bst); + + /* Before savegame version 161, persistent storages were not stored in a pool. */ + if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_145) && st->facilities & FACIL_AIRPORT) { + /* Store the old persistent storage. The GRFID will be added later. */ + assert(PersistentStorage::CanAllocateItem()); + st->airport.psa = new PersistentStorage(0, 0, 0); + memcpy(st->airport.psa->storage, _old_st_persistent_storage.storage, sizeof(_old_st_persistent_storage.storage)); + } + + for (CargoID i = 0; i < num_cargo; i++) { + SlObjectLoadFiltered(&st->goods[i], _filtered_goods_desc); + StationID prev_source = INVALID_STATION; + if (SlXvIsFeaturePresent(XSLFI_FLOW_STAT_FLAGS)) { + for (uint32 j = 0; j < _num_flows; ++j) { + FlowSaveLoad flow; + buffer->CheckBytes(2 + 4); + flow.source = buffer->RawReadUint16(); + uint32 flow_count = buffer->RawReadUint32(); + + buffer->CheckBytes(2 + 4 + 1); + flow.via = buffer->RawReadUint16(); + flow.share = buffer->RawReadUint32(); + flow.restricted = (buffer->RawReadByte() != 0); + FlowStat *fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); + for (uint32 k = 1; k < flow_count; ++k) { + buffer->CheckBytes(2 + 4 + 1); + flow.via = buffer->RawReadUint16(); + flow.share = buffer->RawReadUint32(); + flow.restricted = (buffer->RawReadByte() != 0); + fs->AppendShare(flow.via, flow.share, flow.restricted); + } + fs->SetRawFlags(SlReadUint16()); + } + } else if (SlXvIsFeatureMissing(XSLFI_CHILLPP)) { + FlowSaveLoad flow; + FlowStat *fs = nullptr; + for (uint32 j = 0; j < _num_flows; ++j) { + // SlObject(&flow, _flow_desc); /* this is highly performance-sensitive, manually unroll */ + buffer->CheckBytes(2 + 2 + 4); + flow.source = buffer->RawReadUint16(); + flow.via = buffer->RawReadUint16(); + flow.share = buffer->RawReadUint32(); + if (!IsSavegameVersionBefore(SLV_187)) flow.restricted = (buffer->ReadByte() != 0); + + if (fs == nullptr || prev_source != flow.source) { + fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); + } else { + fs->AppendShare(flow.via, flow.share, flow.restricted); + } + prev_source = flow.source; + } + } + if (IsSavegameVersionBefore(SLV_183) && SlXvIsFeatureMissing(XSLFI_CHILLPP)) { + SwapPackets(&st->goods[i]); + } else { + if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { + SlSkipBytes(8); + uint num_links = SlReadUint16(); + uint num_flows = SlReadUint32(); + SlSkipBytes(6); + SlSkipBytes(18 * num_links); + SlSkipBytes(16 * num_flows); + } + + StationCargoPair pair; + for (uint j = 0; j < _num_dests; ++j) { + SlObjectLoadFiltered(&pair, _cargo_list_desc); // _cargo_list_desc has no conditionals + const_cast(*(st->goods[i].cargo.Packets()))[pair.first].swap(pair.second); + assert(pair.second.empty()); + } + } + if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) st->goods[i].last_vehicle_type = _old_last_vehicle_type; + } + + st->station_cargo_history.resize(CountBits(st->station_cargo_history_cargoes)); + buffer->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2); + for (auto &history : st->station_cargo_history) { + for (uint16 &amount : history) { + amount = buffer->RawReadUint16(); + } + } + if (SlXvIsFeaturePresent(XSLFI_STATION_CARGO_HISTORY, 1, 1)) { + for (auto &history : st->station_cargo_history) { + for (uint16 &amount : history) { + amount = RXCompressUint(amount); + } + } + } + st->station_cargo_history_offset = 0; + } + + if (_num_specs != 0) { + /* Allocate speclist memory when loading a game */ + bst->speclist.resize(_num_specs); + for (uint i = 0; i < bst->speclist.size(); i++) { + SlObjectLoadFiltered(&bst->speclist[i], _filtered_station_speclist_desc); + } + } + + if (_num_roadstop_specs != 0) { + /* Allocate speclist memory when loading a game */ + bst->roadstop_speclist.resize(_num_roadstop_specs); + for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { + SlObjectLoadFiltered(&bst->roadstop_speclist[i], _filtered_roadstop_speclist_desc); + } + } + + if (_num_roadstop_custom_tiles != 0) { + /* Allocate custom road stop tile data memory when loading a game */ + bst->custom_roadstop_tile_data.resize(_num_roadstop_custom_tiles); + for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { + SlObjectLoadFiltered(&bst->custom_roadstop_tile_data[i], _custom_roadstop_tile_data_desc); // _custom_roadstop_tile_data_desc has no conditionals + } + } + + if (SlXvIsFeaturePresent(XSLFI_GRF_ROADSTOPS, 1, 1)) { + for (size_t i = 0; i < _custom_road_stop_tiles.size(); i++) { + bst->custom_roadstop_tile_data.push_back({ _custom_road_stop_tiles[i], (uint8)GB(_custom_road_stop_data[i], 0, 8), (uint8)GB(_custom_road_stop_data[i], 8, 8) }); + } + _custom_road_stop_tiles.clear(); + _custom_road_stop_data.clear(); + } + } +} + +static void Ptrs_STNN() +{ + /* Don't run when savegame version lower than 123. */ + if (IsSavegameVersionBefore(SLV_123)) return; + + SetupDescs_STNN(); + + if (!IsSavegameVersionBefore(SLV_183)) { + assert(_filtered_goods_desc.size() == 0); + } + + uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + for (Station *st : Station::Iterate()) { + for (CargoID i = 0; i < num_cargo; i++) { + GoodsEntry *ge = &st->goods[i]; + if (IsSavegameVersionBefore(SLV_183) && SlXvIsFeatureMissing(XSLFI_CHILLPP)) { + SwapPackets(ge); + SlObjectPtrOrNullFiltered(ge, _filtered_goods_desc); + SwapPackets(ge); + } else { + //SlObject(ge, GetGoodsDesc()); + for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) { + SlObjectPtrOrNullFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals + } + } + } + SlObjectPtrOrNullFiltered(st, _filtered_station_desc); + } + + for (Waypoint *wp : Waypoint::Iterate()) { + SlObjectPtrOrNullFiltered(wp, _filtered_waypoint_desc); + } +} + +static void Save_ROADSTOP() +{ + SetupDescs_ROADSTOP(); + for (RoadStop *rs : RoadStop::Iterate()) { + SlSetArrayIndex(rs->index); + SlObjectSaveFiltered(rs, _filtered_roadstop_desc); + } +} + +static void Load_ROADSTOP() +{ + SetupDescs_ROADSTOP(); + int index; + while ((index = SlIterateArray()) != -1) { + RoadStop *rs = new (index) RoadStop(INVALID_TILE); + + SlObjectLoadFiltered(rs, _filtered_roadstop_desc); + } +} + +static void Ptrs_ROADSTOP() +{ + SetupDescs_ROADSTOP(); + for (RoadStop *rs : RoadStop::Iterate()) { + SlObjectPtrOrNullFiltered(rs, _filtered_roadstop_desc); + } +} + +static void Load_DOCK() +{ + extern void SlSkipArray(); + SlSkipArray(); +} + +static const ChunkHandler station_chunk_handlers[] = { + { 'STNS', nullptr, Load_STNS, Ptrs_STNS, nullptr, CH_ARRAY }, + { 'STNN', Save_STNN, Load_STNN, Ptrs_STNN, nullptr, CH_ARRAY }, + { 'ROAD', Save_ROADSTOP, Load_ROADSTOP, Ptrs_ROADSTOP, nullptr, CH_ARRAY }, + { 'DOCK', nullptr, Load_DOCK, nullptr, nullptr, CH_ARRAY }, +}; + +extern const ChunkHandlerTable _station_chunk_handlers(station_chunk_handlers); diff --git a/src/saveload/upstream/storage_sl.cpp b/src/sl/storage_sl.cpp similarity index 56% rename from src/saveload/upstream/storage_sl.cpp rename to src/sl/storage_sl.cpp index ac817a9454..50f858f116 100644 --- a/src/saveload/upstream/storage_sl.cpp +++ b/src/sl/storage_sl.cpp @@ -7,16 +7,11 @@ /** @file storage_sl.cpp Code handling saving and loading of persistent storages. */ -#include "../../stdafx.h" - +#include "../stdafx.h" +#include "../newgrf_storage.h" #include "saveload.h" -#include "compat/storage_sl_compat.h" -#include "../../newgrf_storage.h" - -#include "../../safeguards.h" - -namespace upstream_sl { +#include "../safeguards.h" /** Description of the data to save and load in #PersistentStorage. */ static const SaveLoad _storage_desc[] = { @@ -25,41 +20,32 @@ static const SaveLoad _storage_desc[] = { SLE_CONDARR(PersistentStorage, storage, SLE_UINT32, 256, SLV_EXTEND_PERSISTENT_STORAGE, SL_MAX_VERSION), }; -/** Persistent storage data. */ -struct PSACChunkHandler : ChunkHandler { - PSACChunkHandler() : ChunkHandler('PSAC', CH_TABLE) {} +/** Load persistent storage data. */ +static void Load_PSAC() +{ + int index; - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_storage_desc, _storage_sl_compat); - - int index; - - while ((index = SlIterateArray()) != -1) { - assert(PersistentStorage::CanAllocateItem()); - PersistentStorage *ps = new (index) PersistentStorage(0, 0, 0); - SlObject(ps, slt); - } + while ((index = SlIterateArray()) != -1) { + assert(PersistentStorage::CanAllocateItem()); + PersistentStorage *ps = new (index) PersistentStorage(0, 0, 0); + SlObject(ps, _storage_desc); } +} - void Save() const override - { - SlTableHeader(_storage_desc); - - /* Write the industries */ - for (PersistentStorage *ps : PersistentStorage::Iterate()) { - ps->ClearChanges(); - SlSetArrayIndex(ps->index); - SlObject(ps, _storage_desc); - } +/** Save persistent storage data. */ +static void Save_PSAC() +{ + /* Write the industries */ + for (PersistentStorage *ps : PersistentStorage::Iterate()) { + ps->ClearChanges(); + SlSetArrayIndex(ps->index); + SlObject(ps, _storage_desc); } -}; +} -static const PSACChunkHandler PSAC; -static const ChunkHandlerRef persistent_storage_chunk_handlers[] = { - PSAC, +/** Chunk handler for persistent storages. */ +static const ChunkHandler persistent_storage_chunk_handlers[] = { + { 'PSAC', Save_PSAC, Load_PSAC, nullptr, nullptr, CH_ARRAY }, }; extern const ChunkHandlerTable _persistent_storage_chunk_handlers(persistent_storage_chunk_handlers); - -} diff --git a/src/saveload/upstream/story_sl.cpp b/src/sl/story_sl.cpp similarity index 50% rename from src/saveload/upstream/story_sl.cpp rename to src/sl/story_sl.cpp index e524587cd5..fa3c70b2b1 100644 --- a/src/saveload/upstream/story_sl.cpp +++ b/src/sl/story_sl.cpp @@ -7,16 +7,24 @@ /** @file story_sl.cpp Code handling saving and loading of story pages */ -#include "../../stdafx.h" +#include "../stdafx.h" +#include "../story_base.h" #include "saveload.h" -#include "compat/story_sl_compat.h" -#include "../../story_base.h" +#include "../safeguards.h" -#include "../../safeguards.h" - -namespace upstream_sl { +/** Called after load to trash broken pages. */ +void AfterLoadStoryBook() +{ + if (IsSavegameVersionBefore(SLV_185)) { + /* Trash all story pages and page elements because + * they were saved with wrong data types. + */ + _story_page_element_pool.CleanPool(); + _story_page_pool.CleanPool(); + } +} static const SaveLoad _story_page_elements_desc[] = { SLE_CONDVAR(StoryPageElement, sort_value, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_185), @@ -28,38 +36,30 @@ static const SaveLoad _story_page_elements_desc[] = { SLE_SSTR(StoryPageElement, text, SLE_STR | SLF_ALLOW_CONTROL), }; -struct STPEChunkHandler : ChunkHandler { - STPEChunkHandler() : ChunkHandler('STPE', CH_TABLE) {} +static void Save_STORY_PAGE_ELEMENT() +{ + for (StoryPageElement *s : StoryPageElement::Iterate()) { + SlSetArrayIndex(s->index); + SlObject(s, _story_page_elements_desc); + } +} - void Save() const override - { - SlTableHeader(_story_page_elements_desc); - - for (StoryPageElement *s : StoryPageElement::Iterate()) { - SlSetArrayIndex(s->index); - SlObject(s, _story_page_elements_desc); +static void Load_STORY_PAGE_ELEMENT() +{ + int index; + uint32 max_sort_value = 0; + while ((index = SlIterateArray()) != -1) { + StoryPageElement *s = new (index) StoryPageElement(); + SlObject(s, _story_page_elements_desc); + if (s->sort_value > max_sort_value) { + max_sort_value = s->sort_value; } } - - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_story_page_elements_desc, _story_page_elements_sl_compat); - - int index; - uint32 max_sort_value = 0; - while ((index = SlIterateArray()) != -1) { - StoryPageElement *s = new (index) StoryPageElement(); - SlObject(s, slt); - if (s->sort_value > max_sort_value) { - max_sort_value = s->sort_value; - } - } - /* Update the next sort value, so that the next - * created page is shown after all existing pages. - */ - _story_page_element_next_sort_value = max_sort_value + 1; - } -}; + /* Update the next sort value, so that the next + * created page is shown after all existing pages. + */ + _story_page_element_next_sort_value = max_sort_value + 1; +} static const SaveLoad _story_pages_desc[] = { SLE_CONDVAR(StoryPage, sort_value, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_185), @@ -70,46 +70,34 @@ static const SaveLoad _story_pages_desc[] = { SLE_SSTR(StoryPage, title, SLE_STR | SLF_ALLOW_CONTROL), }; -struct STPAChunkHandler : ChunkHandler { - STPAChunkHandler() : ChunkHandler('STPA', CH_TABLE) {} +static void Save_STORY_PAGE() +{ + for (StoryPage *s : StoryPage::Iterate()) { + SlSetArrayIndex(s->index); + SlObject(s, _story_pages_desc); + } +} - void Save() const override - { - SlTableHeader(_story_pages_desc); - - for (StoryPage *s : StoryPage::Iterate()) { - SlSetArrayIndex(s->index); - SlObject(s, _story_pages_desc); +static void Load_STORY_PAGE() +{ + int index; + uint32 max_sort_value = 0; + while ((index = SlIterateArray()) != -1) { + StoryPage *s = new (index) StoryPage(); + SlObject(s, _story_pages_desc); + if (s->sort_value > max_sort_value) { + max_sort_value = s->sort_value; } } + /* Update the next sort value, so that the next + * created page is shown after all existing pages. + */ + _story_page_next_sort_value = max_sort_value + 1; +} - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_story_pages_desc, _story_pages_sl_compat); - - int index; - uint32 max_sort_value = 0; - while ((index = SlIterateArray()) != -1) { - StoryPage *s = new (index) StoryPage(); - SlObject(s, slt); - if (s->sort_value > max_sort_value) { - max_sort_value = s->sort_value; - } - } - /* Update the next sort value, so that the next - * created page is shown after all existing pages. - */ - _story_page_next_sort_value = max_sort_value + 1; - } -}; - -static const STPEChunkHandler STPE; -static const STPAChunkHandler STPA; -static const ChunkHandlerRef story_page_chunk_handlers[] = { - STPE, - STPA, +static const ChunkHandler story_page_chunk_handlers[] = { + { 'STPE', Save_STORY_PAGE_ELEMENT, Load_STORY_PAGE_ELEMENT, nullptr, nullptr, CH_ARRAY }, + { 'STPA', Save_STORY_PAGE, Load_STORY_PAGE, nullptr, nullptr, CH_ARRAY }, }; extern const ChunkHandlerTable _story_page_chunk_handlers(story_page_chunk_handlers); - -} diff --git a/src/saveload/strings_sl.cpp b/src/sl/strings_sl.cpp similarity index 100% rename from src/saveload/strings_sl.cpp rename to src/sl/strings_sl.cpp diff --git a/src/saveload/upstream/subsidy_sl.cpp b/src/sl/subsidy_sl.cpp similarity index 70% rename from src/saveload/upstream/subsidy_sl.cpp rename to src/sl/subsidy_sl.cpp index 88dfb7d8d7..9ed65a04ec 100644 --- a/src/saveload/upstream/subsidy_sl.cpp +++ b/src/sl/subsidy_sl.cpp @@ -7,16 +7,12 @@ /** @file subsidy_sl.cpp Code handling saving and loading of subsidies */ -#include "../../stdafx.h" +#include "../stdafx.h" +#include "../subsidy_base.h" #include "saveload.h" -#include "compat/subsidy_sl_compat.h" -#include "../../subsidy_base.h" - -#include "../../safeguards.h" - -namespace upstream_sl { +#include "../safeguards.h" static const SaveLoad _subsidies_desc[] = { SLE_VAR(Subsidy, cargo_type, SLE_UINT8), @@ -31,36 +27,25 @@ static const SaveLoad _subsidies_desc[] = { SLE_CONDVAR(Subsidy, dst, SLE_UINT16, SLV_5, SL_MAX_VERSION), }; -struct SUBSChunkHandler : ChunkHandler { - SUBSChunkHandler() : ChunkHandler('SUBS', CH_TABLE) {} - - void Save() const override - { - SlTableHeader(_subsidies_desc); - - for (Subsidy *s : Subsidy::Iterate()) { - SlSetArrayIndex(s->index); - SlObject(s, _subsidies_desc); - } +static void Save_SUBS() +{ + for (Subsidy *s : Subsidy::Iterate()) { + SlSetArrayIndex(s->index); + SlObject(s, _subsidies_desc); } +} - void Load() const override - { - const std::vector slt = SlCompatTableHeader(_subsidies_desc, _subsidies_sl_compat); - - int index; - while ((index = SlIterateArray()) != -1) { - Subsidy *s = new (index) Subsidy(); - SlObject(s, slt); - } +static void Load_SUBS() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Subsidy *s = new (index) Subsidy(); + SlObject(s, _subsidies_desc); } -}; +} -static const SUBSChunkHandler SUBS; -static const ChunkHandlerRef subsidy_chunk_handlers[] = { - SUBS, +static const ChunkHandler subsidy_chunk_handlers[] = { + { 'SUBS', Save_SUBS, Load_SUBS, nullptr, nullptr, CH_ARRAY }, }; extern const ChunkHandlerTable _subsidy_chunk_handlers(subsidy_chunk_handlers); - -} diff --git a/src/saveload/tbtr_template_replacement_sl.cpp b/src/sl/tbtr_template_replacement_sl.cpp similarity index 100% rename from src/saveload/tbtr_template_replacement_sl.cpp rename to src/sl/tbtr_template_replacement_sl.cpp diff --git a/src/saveload/tbtr_template_veh_sl.cpp b/src/sl/tbtr_template_veh_sl.cpp similarity index 100% rename from src/saveload/tbtr_template_veh_sl.cpp rename to src/sl/tbtr_template_veh_sl.cpp diff --git a/src/sl/town_sl.cpp b/src/sl/town_sl.cpp new file mode 100644 index 0000000000..a06b4a1e40 --- /dev/null +++ b/src/sl/town_sl.cpp @@ -0,0 +1,439 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file town_sl.cpp Code handling saving and loading of towns and houses */ + +#include "../stdafx.h" +#include "../newgrf_house.h" +#include "../town.h" +#include "../landscape.h" +#include "../subsidy_func.h" +#include "../strings_func.h" +#include "../network/network.h" + +#include "saveload.h" +#include "newgrf_sl.h" + +#include "../safeguards.h" + +static bool _town_zone_radii_no_update = false; + +extern bool IsGetTownZonesCallbackHandlerPresent(); + +HouseID SLGetCleanHouseType(TileIndex t, bool old_map_position) +{ + if (old_map_position && SlXvIsFeatureMissing(XSLFI_MORE_HOUSES)) { + return _m[t].m4 | (GB(_m[t].m3, 6, 1) << 8); + } else { + return GetCleanHouseType(t); + } +} + +/** + * Rebuild all the cached variables of towns. + */ +void RebuildTownCaches(bool cargo_update_required, bool old_map_position) +{ + InitializeBuildingCounts(); + RebuildTownKdtree(); + + /* Reset town population and num_houses */ + for (Town *town : Town::Iterate()) { + town->cache.population = 0; + town->cache.num_houses = 0; + } + + for (TileIndex t = 0; t < MapSize(); t++) { + if (!IsTileType(t, MP_HOUSE)) continue; + + HouseID house_id = GetTranslatedHouseID(SLGetCleanHouseType(t, old_map_position)); + Town *town = Town::GetByTile(t); + IncreaseBuildingCount(town, house_id); + if (IsHouseCompleted(t)) town->cache.population += HouseSpec::Get(house_id)->population; + + /* Increase the number of houses for every house, but only once. */ + if (GetHouseNorthPart(house_id) == 0) town->cache.num_houses++; + } + + if (!_town_zone_radii_no_update) { + /* Update the population and num_house dependent values */ + for (Town *town : Town::Iterate()) { + UpdateTownRadius(town); + } + } +} + +static void CheckMultiTileHouseTypes(bool &cargo_update_required, bool old_map_position, bool translate_house_types) +{ + auto get_clean_house_type = [&](TileIndex t) -> HouseID { + HouseID type = SLGetCleanHouseType(t, old_map_position); + if (translate_house_types) type = GetTranslatedHouseID(type); + return type; + }; + + /* Check for cases when a NewGRF has set a wrong house substitute type. */ + for (TileIndex t = 0; t < MapSize(); t++) { + if (!IsTileType(t, MP_HOUSE)) continue; + + HouseID house_type = get_clean_house_type(t); + TileIndex north_tile = t + GetHouseNorthPart(house_type); // modifies 'house_type'! + if (t == north_tile) { + const HouseSpec *hs = HouseSpec::Get(house_type); + bool valid_house = true; + if (hs->building_flags & TILE_SIZE_2x1) { + TileIndex tile = t + TileDiffXY(1, 0); + if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false; + } else if (hs->building_flags & TILE_SIZE_1x2) { + TileIndex tile = t + TileDiffXY(0, 1); + if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false; + } else if (hs->building_flags & TILE_SIZE_2x2) { + TileIndex tile = t + TileDiffXY(0, 1); + if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false; + tile = t + TileDiffXY(1, 0); + if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 2) valid_house = false; + tile = t + TileDiffXY(1, 1); + if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 3) valid_house = false; + } + /* If not all tiles of this house are present remove the house. + * The other tiles will get removed later in this loop because + * their north tile is not the correct type anymore. */ + if (!valid_house) { + DoClearSquare(t); + cargo_update_required = true; + } + } else if (!IsTileType(north_tile, MP_HOUSE) || get_clean_house_type(north_tile) != house_type) { + /* This tile should be part of a multi-tile building but the + * north tile of this house isn't on the map. */ + DoClearSquare(t); + cargo_update_required = true; + } + } +} + +/** + * 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(bool cargo_update_required, bool old_map_position) +{ + auto get_clean_house_type = [&](TileIndex t) -> HouseID { + return SLGetCleanHouseType(t, old_map_position); + }; + for (TileIndex t = 0; t < MapSize(); t++) { + if (!IsTileType(t, MP_HOUSE)) continue; + + HouseID house_id = get_clean_house_type(t); + if (!HouseSpec::Get(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); + if (old_map_position && SlXvIsFeatureMissing(XSLFI_MORE_HOUSES)) { + _m[t].m4 = GB(house_id, 0, 8); + SB(_m[t].m3, 6, 1, GB(house_id, 8, 1)); + } else { + SetHouseType(t, house_id); + } + cargo_update_required = true; + } + } + + CheckMultiTileHouseTypes(cargo_update_required, old_map_position, false); + if (cargo_update_required || SlXvIsFeatureMissing(XSLFI_MORE_HOUSES, 2)) CheckMultiTileHouseTypes(cargo_update_required, old_map_position, true); + + RebuildTownCaches(cargo_update_required, old_map_position); +} + +/** Save and load of towns. */ +static const SaveLoad _town_desc[] = { + SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Town, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_3), ///< population, no longer in use + SLE_CONDNULL(4, SLV_3, SLV_85), ///< population, no longer in use + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_92), ///< num_houses, no longer in use + + SLE_CONDVAR(Town, townnamegrfid, SLE_UINT32, SLV_66, SL_MAX_VERSION), + SLE_VAR(Town, townnametype, SLE_UINT16), + SLE_VAR(Town, townnameparts, SLE_UINT32), + SLE_CONDSTR(Town, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), + + SLE_VAR(Town, flags, SLE_UINT8), + SLE_CONDVAR_X(Town, church_count, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_MULTI_BUILDING)), + SLE_CONDVAR_X(Town, stadium_count, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_MULTI_BUILDING)), + SLE_CONDVAR(Town, statues, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), + SLE_CONDVAR(Town, statues, SLE_UINT16, SLV_104, SL_MAX_VERSION), + + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_2), ///< sort_index, no longer in use + + SLE_CONDVAR(Town, have_ratings, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), + SLE_CONDVAR(Town, have_ratings, SLE_UINT16, SLV_104, SL_MAX_VERSION), + SLE_CONDARR(Town, ratings, SLE_INT16, 8, SL_MIN_VERSION, SLV_104), + SLE_CONDARR(Town, ratings, SLE_INT16, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), + SLE_CONDNULL_X(MAX_COMPANIES, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + /* failed bribe attempts are stored since savegame format 4 */ + SLE_CONDARR(Town, unwanted, SLE_INT8, 8, SLV_4, SLV_104), + SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), + + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_UINT32, SLV_9, SLV_165), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), ///< pct_pass_transported / pct_mail_transported, now computed on the fly + SLE_CONDNULL_X(3, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + + SLE_CONDVAR(Town, received[TE_FOOD].old_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), + SLE_CONDVAR(Town, received[TE_WATER].old_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, received[TE_FOOD].new_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), + SLE_CONDVAR(Town, received[TE_WATER].new_act, SLE_UINT16, SL_MIN_VERSION, SLV_165), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + + SLE_CONDARR(Town, goal, SLE_UINT32, NUM_TE, SLV_165, SL_MAX_VERSION), + + SLE_CONDSSTR(Town, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_168, SL_MAX_VERSION), + + SLE_CONDVAR(Town, time_until_rebuild, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54), + SLE_CONDVAR(Town, grow_counter, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54), + SLE_CONDVAR(Town, growth_rate, SLE_FILE_U8 | SLE_VAR_I16, SL_MIN_VERSION, SLV_54), + + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT16, SLV_54, SL_MAX_VERSION), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_26)), + SLE_CONDVAR(Town, grow_counter, SLE_UINT16, SLV_54, SL_MAX_VERSION), + + SLE_CONDVAR(Town, growth_rate, SLE_FILE_I16 | SLE_VAR_U16, SLV_54, SLV_165), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_26)), + SLE_CONDVAR(Town, growth_rate, SLE_UINT16, SLV_165, 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, SLV_2, SL_MAX_VERSION), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDVAR(Town, exclusive_counter, SLE_UINT8, SLV_2, SL_MAX_VERSION), + + SLE_CONDVAR(Town, larger_town, SLE_BOOL, SLV_56, SL_MAX_VERSION), + SLE_CONDVAR(Town, layout, SLE_UINT8, SLV_113, SL_MAX_VERSION), + + SLE_CONDREFLIST(Town, psa_list, REF_STORAGE, SLV_161, SL_MAX_VERSION), + + SLE_CONDNULL(4, SLV_166, SLV_EXTEND_CARGOTYPES), ///< cargo_produced, no longer in use + SLE_CONDNULL(8, SLV_EXTEND_CARGOTYPES, SLV_REMOVE_TOWN_CARGO_CACHE), ///< cargo_produced, no longer in use + SLE_CONDNULL(30, SLV_2, SLV_REMOVE_TOWN_CARGO_CACHE), ///< old reserved space + + SLE_CONDVAR_X(Town, override_flags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)), + SLE_CONDVAR_X(Town, override_values, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)), + SLE_CONDVAR_X(Town, build_tunnels, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)), + SLE_CONDVAR_X(Town, max_road_slope, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)), +}; + +static const SaveLoad _town_supplied_desc[] = { + SLE_CONDVAR(TransportedCargoStat, old_max, SLE_UINT32, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_max, SLE_UINT32, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, old_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), +}; + +static const SaveLoad _town_received_desc[] = { + SLE_CONDVAR(TransportedCargoStat, old_max, SLE_UINT16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_max, SLE_UINT16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, old_act, SLE_UINT16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT16, SLV_165, SL_MAX_VERSION), +}; + +static const SaveLoad _town_received_desc_spp[] = { + SLE_CONDVAR(TransportedCargoStat, old_max, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_max, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, old_act, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION), + SLE_CONDVAR(TransportedCargoStat, new_act, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION), +}; + +std::vector _filtered_town_desc; +std::vector _filtered_town_supplied_desc; +std::vector _filtered_town_received_desc; + +static void SetupDescs_TOWN() +{ + _filtered_town_desc = SlFilterObject(_town_desc); + _filtered_town_supplied_desc = SlFilterObject(_town_supplied_desc); + _filtered_town_received_desc = SlFilterObject(_town_received_desc); +} + +static void Save_HIDS() +{ + Save_NewGRFMapping(_house_mngr); +} + +static void Load_HIDS() +{ + Load_NewGRFMapping(_house_mngr); +} + +static void RealSave_Town(Town *t) +{ + SlObjectSaveFiltered(t, _filtered_town_desc); + + for (CargoID i = 0; i < NUM_CARGO; i++) { + SlObjectSaveFiltered(&t->supplied[i], _filtered_town_supplied_desc); + } + for (int i = TE_BEGIN; i < NUM_TE; i++) { + SlObjectSaveFiltered(&t->received[i], _filtered_town_received_desc); + } +} + +static void Save_TOWN() +{ + SetupDescs_TOWN(); + for (Town *t : Town::Iterate()) { + SlSetArrayIndex(t->index); + SlAutolength((AutolengthProc*)RealSave_Town, t); + } +} + +static void Load_TOWN() +{ + SetupDescs_TOWN(); + int index; + uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + + while ((index = SlIterateArray()) != -1) { + Town *t = new (index) Town(); + SlObjectLoadFiltered(t, _filtered_town_desc); + + for (CargoID i = 0; i < num_cargo; i++) { + SlObjectLoadFiltered(&t->supplied[i], _filtered_town_supplied_desc); + } + if (SlXvIsFeaturePresent(XSLFI_SPRINGPP)) { + for (int i = TE_BEGIN; i < NUM_TE; i++) { + SlObject(&t->received[i], _town_received_desc_spp); + } + } else { + for (int i = TE_BEGIN; i < NUM_TE; i++) { + SlObjectLoadFiltered(&t->received[i], _filtered_town_received_desc); + } + } + + if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GetStringTab(t->townnametype) != TEXT_TAB_OLD_CUSTOM) { + SlErrorCorrupt("Invalid town name generator"); + } + + if ((!IsSavegameVersionBefore(SLV_166) && IsSavegameVersionBefore(SLV_REMOVE_TOWN_CARGO_CACHE)) || SlXvIsFeaturePresent(XSLFI_TOWN_CARGO_MATRIX)) { + SlSkipBytes(4); // tile + uint16 w = SlReadUint16(); + uint16 h = SlReadUint16(); + if (w != 0) { + SlSkipBytes((SlXvIsFeaturePresent(XSLFI_TOWN_CARGO_MATRIX) ? 8 : 4) * ((uint)(w / 4) * (uint)(h / 4))); + } + } + } +} + +/** Fix pointers when loading town data. */ +static void Ptrs_TOWN() +{ + /* Don't run when savegame version lower than 161. */ + if (IsSavegameVersionBefore(SLV_161)) return; + + SetupDescs_TOWN(); + for (Town *t : Town::Iterate()) { + SlObjectPtrOrNullFiltered(t, _filtered_town_desc); + } +} + +void SlResetTNNC() +{ + _town_zone_radii_no_update = false; +} + +void Save_TNNC() +{ + assert(_sl_xv_feature_versions[XSLFI_TNNC_CHUNK] != 0); + + if (!IsNetworkServerSave() || !IsGetTownZonesCallbackHandlerPresent()) { + SlSetLength(0); + return; + } + + SlSetLength(4 + (Town::GetNumItems() * (1 + lengthof(TownCache::squared_town_zone_radius)) * 4)); + + SlWriteUint32((uint32)Town::GetNumItems()); + + for (const Town *t : Town::Iterate()) { + SlWriteUint32(t->index); + for (uint i = 0; i < lengthof(TownCache::squared_town_zone_radius); i++) { + SlWriteUint32(t->cache.squared_town_zone_radius[i]); + } + } +} + +void Load_TNNC() +{ + if (SlGetFieldLength() == 0) return; + + if (!_networking || _network_server) { + SlSkipBytes(SlGetFieldLength()); + return; + } + + _town_zone_radii_no_update = true; + + const uint32 count = SlReadUint32(); + for (uint32 idx = 0; idx < count; idx++) { + Town *t = Town::Get(SlReadUint32()); + for (uint i = 0; i < lengthof(TownCache::squared_town_zone_radius); i++) { + t->cache.squared_town_zone_radius[i] = SlReadUint32(); + } + } +} + +static ChunkSaveLoadSpecialOpResult Special_TNNC(uint32 chunk_id, ChunkSaveLoadSpecialOp op) +{ + switch (op) { + case CSLSO_SHOULD_SAVE_CHUNK: + if (_sl_xv_feature_versions[XSLFI_TNNC_CHUNK] == 0) return CSLSOR_DONT_SAVE_CHUNK; + break; + + default: + break; + } + return CSLSOR_NONE; +} + +/** Chunk handler for towns. */ +static const ChunkHandler town_chunk_handlers[] = { + { 'HIDS', Save_HIDS, Load_HIDS, nullptr, nullptr, CH_ARRAY }, + { 'CITY', Save_TOWN, Load_TOWN, Ptrs_TOWN, nullptr, CH_ARRAY }, + { 'TNNC', Save_TNNC, Load_TNNC, nullptr, nullptr, CH_RIFF, Special_TNNC }, +}; + +extern const ChunkHandlerTable _town_chunk_handlers(town_chunk_handlers); diff --git a/src/saveload/tracerestrict_sl.cpp b/src/sl/tracerestrict_sl.cpp similarity index 100% rename from src/saveload/tracerestrict_sl.cpp rename to src/sl/tracerestrict_sl.cpp diff --git a/src/saveload/train_speed_adaptation.cpp b/src/sl/train_speed_adaptation.cpp similarity index 100% rename from src/saveload/train_speed_adaptation.cpp rename to src/sl/train_speed_adaptation.cpp diff --git a/src/saveload/tunnel_sl.cpp b/src/sl/tunnel_sl.cpp similarity index 100% rename from src/saveload/tunnel_sl.cpp rename to src/sl/tunnel_sl.cpp diff --git a/src/sl/vehicle_sl.cpp b/src/sl/vehicle_sl.cpp new file mode 100644 index 0000000000..4a05a98018 --- /dev/null +++ b/src/sl/vehicle_sl.cpp @@ -0,0 +1,1533 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file vehicle_sl.cpp Code handling saving and loading of vehicles */ + +#include "../stdafx.h" +#include "../vehicle_func.h" +#include "../train.h" +#include "../roadveh.h" +#include "../ship.h" +#include "../aircraft.h" +#include "../station_base.h" +#include "../effectvehicle_base.h" +#include "../company_base.h" +#include "../company_func.h" +#include "../disaster_vehicle.h" +#include "../scope_info.h" +#include "../string_func.h" +#include "../error.h" +#include "../strings_func.h" +#include "../3rdparty/cpp-btree/btree_map.h" +#include "../3rdparty/fmt/format.h" + +#include "saveload.h" + +#include + +#include "../safeguards.h" + +extern btree::btree_multimap _pending_speed_restriction_change_map; + +/** + * Link front and rear multiheaded engines to each other + * This is done when loading a savegame + */ +void ConnectMultiheadedTrains() +{ + for (Train *v : Train::Iterate()) { + v->other_multiheaded_part = nullptr; + } + + for (Train *v : Train::Iterate()) { + if (v->IsFrontEngine() || v->IsFreeWagon()) { + /* 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 = v->IsFrontEngine(); + + for (Train *u = v; u != nullptr; u = u->GetNextVehicle()) { + if (u->other_multiheaded_part != nullptr) continue; // we already linked this one + + if (u->IsMultiheaded()) { + if (!u->IsEngine()) { + /* we got a rear car without a front car. We will convert it to a front one */ + u->SetEngine(); + u->spritenum--; + } + + /* Find a matching back part */ + EngineID eid = u->engine_type; + Train *w; + if (sequential_matching) { + for (w = u->GetNextVehicle(); w != nullptr; w = w->GetNextVehicle()) { + if (w->engine_type != eid || w->other_multiheaded_part != nullptr || !w->IsMultiheaded()) continue; + + /* we found a car to partner with this engine. Now we will make sure it face the right way */ + if (w->IsEngine()) { + w->ClearEngine(); + w->spritenum++; + } + break; + } + } else { + uint stack_pos = 0; + for (w = u->GetNextVehicle(); w != nullptr; w = w->GetNextVehicle()) { + if (w->engine_type != eid || w->other_multiheaded_part != nullptr || !w->IsMultiheaded()) continue; + + if (w->IsEngine()) { + stack_pos++; + } else { + if (stack_pos == 0) break; + stack_pos--; + } + } + } + + if (w != nullptr) { + w->other_multiheaded_part = u; + u->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 */ + u->ClearMultiheaded(); + } + } + } + } + } +} + +/** + * 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() +{ + for (Train *t : Train::Iterate()) SetBit(t->subtype, 7); // indicates that it's the old format and needs to be converted in the next loop + + for (Train *t : Train::Iterate()) { + if (HasBit(t->subtype, 7) && ((t->subtype & ~0x80) == 0 || (t->subtype & ~0x80) == 4)) { + for (Train *u = t; u != nullptr; 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) u->SetMultiheaded(); + u->SetFrontEngine(); + u->SetEngine(); + break; + + case 1: // TS_Artic_Part + u->subtype = 0; + u->SetArticulatedPart(); + break; + + case 2: // TS_Not_First + u->subtype = 0; + if (rvi->railveh_type == RAILVEH_WAGON) { + /* normal wagon */ + u->SetWagon(); + break; + } + if (rvi->railveh_type == RAILVEH_MULTIHEAD && rvi->image_index == u->spritenum - 1) { + /* rear end of a multiheaded engine */ + u->SetMultiheaded(); + break; + } + if (rvi->railveh_type == RAILVEH_MULTIHEAD) u->SetMultiheaded(); + u->SetEngine(); + break; + + case 4: // TS_Free_Car + u->subtype = 0; + u->SetWagon(); + u->SetFreeWagon(); + break; + default: SlErrorCorrupt("Invalid train subtype"); + } + } + } + } +} + + +/** need to be called to load aircraft from old version */ +void UpdateOldAircraft() +{ + /* set airport_flags to 0 for all airports just to be sure */ + for (Station *st : Station::Iterate()) { + st->airport.flags = 0; // reset airport + } + + for (Aircraft *a : Aircraft::Iterate()) { + /* airplane has another vehicle with subtype 4 (shadow), helicopter also has 3 (rotor) + * skip those */ + if (a->IsNormalAircraft()) { + /* airplane in terminal stopped doesn't hurt anyone, so goto next */ + if ((a->vehstatus & VS_STOPPED) && a->state == 0) { + a->state = HANGAR; + continue; + } + + AircraftLeaveHangar(a, a->direction); // make airplane visible if it was in a depot for example + a->vehstatus &= ~VS_STOPPED; // make airplane moving + UpdateAircraftCache(a); + a->cur_speed = a->vcache.cached_max_speed; // so aircraft don't have zero speed while in air + if (!a->current_order.IsType(OT_GOTO_STATION) && !a->current_order.IsType(OT_GOTO_DEPOT)) { + /* reset current order so aircraft doesn't have invalid "station-only" order */ + a->current_order.MakeDummy(); + } + a->state = FLYING; + AircraftNextAirportPos_and_Order(a); // move it to the entry point of the airport + GetNewVehiclePosResult gp = GetNewVehiclePos(a); + a->tile = 0; // aircraft in air is tile=0 + + /* correct speed of helicopter-rotors */ + if (a->subtype == AIR_HELICOPTER) a->Next()->Next()->cur_speed = 32; + + /* set new position x,y,z */ + GetAircraftFlightLevelBounds(a, &a->z_pos, nullptr); + SetAircraftPosition(a, gp.x, gp.y, GetAircraftFlightLevel(a)); + } + } +} + +/** + * Check all vehicles to ensure their engine type is valid + * for the currently loaded NewGRFs (that includes none...) + * This only makes a difference if NewGRFs are missing, otherwise + * all vehicles will be valid. This does not make such a game + * playable, it only prevents crash. + */ +static void CheckValidVehicles() +{ + size_t total_engines = Engine::GetPoolSize(); + EngineID first_engine[4] = { INVALID_ENGINE, INVALID_ENGINE, INVALID_ENGINE, INVALID_ENGINE }; + + for (const Engine *e : Engine::IterateType(VEH_TRAIN)) { first_engine[VEH_TRAIN] = e->index; break; } + for (const Engine *e : Engine::IterateType(VEH_ROAD)) { first_engine[VEH_ROAD] = e->index; break; } + for (const Engine *e : Engine::IterateType(VEH_SHIP)) { first_engine[VEH_SHIP] = e->index; break; } + for (const Engine *e : Engine::IterateType(VEH_AIRCRAFT)) { first_engine[VEH_AIRCRAFT] = e->index; break; } + + for (Vehicle *v : Vehicle::Iterate()) { + /* Test if engine types match */ + switch (v->type) { + case VEH_TRAIN: + case VEH_ROAD: + case VEH_SHIP: + case VEH_AIRCRAFT: + if (v->engine_type >= total_engines || v->type != v->GetEngine()->type) { + v->engine_type = first_engine[v->type]; + } + break; + + default: + break; + } + } +} + +extern byte _age_cargo_skip_counter; // From misc_sl.cpp + +static std::vector _load_invalid_vehicles_to_delete; + +/** Called after load to update coordinates */ +void AfterLoadVehicles(bool part_of_load) +{ + _load_invalid_vehicles_to_delete.clear(); + + const Vehicle *si_v = nullptr; + SCOPE_INFO_FMT([&si_v], "AfterLoadVehicles: %s", scope_dumper().VehicleInfo(si_v)); + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + /* Reinstate the previous pointer */ + if (v->Next() != nullptr) { + v->Next()->previous = v; + if (v->type == VEH_TRAIN && (HasBit(v->subtype, GVSF_VIRTUAL) != HasBit(v->Next()->subtype, GVSF_VIRTUAL))) { + SlErrorCorrupt("Mixed virtual/non-virtual train consist"); + } + } + if (v->NextShared() != nullptr) v->NextShared()->previous_shared = v; + + if (part_of_load) v->fill_percent_te_id = INVALID_TE_ID; + v->first = nullptr; + if (v->IsGroundVehicle()) v->GetGroundVehicleCache()->first_engine = INVALID_ENGINE; + } + + /* 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 (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + if (v->old_orders != nullptr) { + if (IsSavegameVersionBefore(SLV_105)) { // Pre-105 didn't save an OrderList + if (mapping[v->old_orders] == nullptr) { + /* This adds the whole shared vehicle chain for case b */ + + /* Creating an OrderList here is safe because the number of vehicles + * allowed in these savegames matches the number of OrderLists. As + * such each vehicle can get an OrderList and it will (still) fit. */ + assert(OrderList::CanAllocateItem()); + v->orders = mapping[v->old_orders] = new OrderList(v->old_orders, v); + } else { + v->orders = mapping[v->old_orders]; + /* For old games (case a) we must create the shared vehicle chain */ + if (IsSavegameVersionBefore(SLV_5, 2)) { + v->AddToShared(v->orders->GetFirstSharedVehicle()); + } + } + } else { // OrderList was saved as such, only recalculate not saved values + if (v->PreviousShared() == nullptr) { + v->orders->Initialize(v->orders->first, v); + } + } + } + } + } + + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + /* Fill the first pointers */ + if (v->Previous() == nullptr) { + for (Vehicle *u = v; u != nullptr; u = u->Next()) { + u->first = v; + } + } + } + + if (part_of_load) { + if (IsSavegameVersionBefore(SLV_105)) { + /* Before 105 there was no order for shared orders, thus it messed up horribly */ + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + if (v->First() != v || v->orders != nullptr || v->previous_shared != nullptr || v->next_shared == nullptr) continue; + + /* As above, allocating OrderList here is safe. */ + assert(OrderList::CanAllocateItem()); + v->orders = new OrderList(nullptr, v); + for (Vehicle *u = v; u != nullptr; u = u->next_shared) { + u->orders = v->orders; + } + } + } + + if (IsSavegameVersionBefore(SLV_157)) { + /* The road vehicle subtype was converted to a flag. */ + for (RoadVehicle *rv : RoadVehicle::Iterate()) { + si_v = rv; + if (rv->subtype == 0) { + /* The road vehicle is at the front. */ + rv->SetFrontEngine(); + } else if (rv->subtype == 1) { + /* The road vehicle is an articulated part. */ + rv->subtype = 0; + rv->SetArticulatedPart(); + } else { + SlErrorCorrupt("Invalid road vehicle subtype"); + } + } + } + + if (IsSavegameVersionBefore(SLV_160)) { + /* In some old savegames there might be some "crap" stored. */ + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + if (!v->IsPrimaryVehicle() && v->type != VEH_DISASTER) { + v->current_order.Free(); + v->unitnumber = 0; + } + } + } + + if (IsSavegameVersionBefore(SLV_162)) { + /* Set the vehicle-local cargo age counter from the old global counter. */ + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + v->cargo_age_counter = _age_cargo_skip_counter; + } + } + + if (IsSavegameVersionBefore(SLV_180)) { + /* Set service interval flags */ + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + if (!v->IsPrimaryVehicle()) continue; + + const Company *c = Company::Get(v->owner); + int interval = CompanyServiceInterval(c, v->type); + + v->SetServiceIntervalIsCustom(v->GetServiceInterval() != interval); + v->SetServiceIntervalIsPercent(c->settings.vehicle.servint_ispercent); + } + } + + if (IsSavegameVersionBefore(SLV_SHIP_ROTATION)) { + /* Ship rotation added */ + for (Ship *s : Ship::Iterate()) { + s->rotation = s->direction; + } + } else { + for (Ship *s : Ship::Iterate()) { + if (s->rotation == s->direction) continue; + /* In case we are rotating on gameload, set the rotation position to + * the current position, otherwise the applied workaround offset would + * be with respect to 0,0. + */ + s->rotation_x_pos = s->x_pos; + s->rotation_y_pos = s->y_pos; + } + } + + if (SlXvIsFeaturePresent(XSLFI_TEMPLATE_REPLACEMENT) && (_network_server || !_networking)) { + for (Train *t : Train::Iterate()) { + si_v = t; + if (t->IsVirtual() && t->First() == t) { + delete t; + } + } + } + } + si_v = nullptr; + + CheckValidVehicles(); + + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + assert(v->first != nullptr); + + v->trip_occupancy = CalcPercentVehicleFilled(v, nullptr); + + switch (v->type) { + case VEH_TRAIN: { + Train *t = Train::From(v); + if (t->IsFrontEngine() || t->IsFreeWagon()) { + t->gcache.last_speed = t->cur_speed; // update displayed train speed + t->ConsistChanged(CCF_SAVELOAD); + } + break; + } + + case VEH_ROAD: { + RoadVehicle *rv = RoadVehicle::From(v); + if (rv->IsFrontEngine()) { + rv->gcache.last_speed = rv->cur_speed; // update displayed road vehicle speed + + rv->roadtype = Engine::Get(rv->engine_type)->u.road.roadtype; + rv->compatible_roadtypes = GetRoadTypeInfo(rv->roadtype)->powered_roadtypes; + bool is_invalid = false; + for (RoadVehicle *u = rv; u != nullptr; u = u->Next()) { + u->roadtype = rv->roadtype; + u->compatible_roadtypes = rv->compatible_roadtypes; + if (GetRoadType(u->tile, GetRoadTramType(u->roadtype)) == INVALID_ROADTYPE) is_invalid = true; + } + + if (is_invalid && part_of_load) { + _load_invalid_vehicles_to_delete.push_back(rv); + break; + } + + RoadVehUpdateCache(rv); + if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) { + rv->CargoChanged(); + } + } + break; + } + + case VEH_SHIP: + if (Ship::From(v)->IsPrimaryVehicle()) { + Ship::From(v)->UpdateCache(); + } + break; + + default: break; + } + } + + /* Stop non-front engines */ + if (part_of_load && IsSavegameVersionBefore(SLV_112)) { + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + if (v->type == VEH_TRAIN) { + Train *t = Train::From(v); + if (!t->IsFrontEngine()) { + if (t->IsEngine()) t->vehstatus |= VS_STOPPED; + /* cur_speed is now relevant for non-front parts - nonzero breaks + * moving-wagons-inside-depot- and autoreplace- code */ + t->cur_speed = 0; + } + } + /* trains weren't stopping gradually in old OTTD versions (and TTO/TTD) + * other vehicle types didn't have zero speed while stopped (even in 'recent' OTTD versions) */ + if ((v->vehstatus & VS_STOPPED) && (v->type != VEH_TRAIN || IsSavegameVersionBefore(SLV_2, 1))) { + v->cur_speed = 0; + } + } + } + + for (Vehicle *v : Vehicle::Iterate()) { + si_v = v; + switch (v->type) { + case VEH_ROAD: + case VEH_TRAIN: + case VEH_SHIP: + v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq); + v->UpdateSpriteSeqBound(); + break; + + case VEH_AIRCRAFT: + if (Aircraft::From(v)->IsNormalAircraft()) { + v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq); + v->UpdateSpriteSeqBound(); + + /* The aircraft's shadow will have the same image as the aircraft, but no colour */ + Vehicle *shadow = v->Next(); + if (shadow == nullptr) SlErrorCorrupt("Missing shadow for aircraft"); + shadow->sprite_seq.CopyWithoutPalette(v->sprite_seq); + shadow->sprite_seq_bounds = v->sprite_seq_bounds; + + /* In the case of a helicopter we will update the rotor sprites */ + if (v->subtype == AIR_HELICOPTER) { + Vehicle *rotor = shadow->Next(); + if (rotor == nullptr) SlErrorCorrupt("Missing rotor for helicopter"); + GetRotorImage(Aircraft::From(v), EIT_ON_MAP, &rotor->sprite_seq); + rotor->UpdateSpriteSeqBound(); + } + + UpdateAircraftCache(Aircraft::From(v), true); + } + break; + default: break; + } + + v->UpdateDeltaXY(); + v->coord.left = INVALID_COORD; + v->UpdatePosition(); + v->UpdateViewport(false); + v->cargo.AssertCountConsistency(); + } +} + +void AfterLoadVehiclesRemoveAnyFoundInvalid() +{ + if (!_load_invalid_vehicles_to_delete.empty()) { + DEBUG(sl, 0, "Removing %u vehicles found to be uncorrectably invalid during load", (uint)_load_invalid_vehicles_to_delete.size()); + SetDParam(0, (uint)_load_invalid_vehicles_to_delete.size()); + ShowErrorMessage(STR_WARNING_LOADGAME_REMOVED_UNCORRECTABLE_VEHICLES, INVALID_STRING_ID, WL_CRITICAL); + GroupStatistics::UpdateAfterLoad(); + } + + for (Vehicle *v : _load_invalid_vehicles_to_delete) { + delete v; + } + _load_invalid_vehicles_to_delete.clear(); +} + +bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // From train_cmd.cpp +void ReverseTrainDirection(Train *v); +void ReverseTrainSwapVeh(Train *v, int l, int r); + +/** Fixup old train spacing. */ +void FixupTrainLengths() +{ + /* Vehicle center was moved from 4 units behind the front to half the length + * behind the front. Move vehicles so they end up on the same spot. */ + for (Vehicle *v : Vehicle::Iterate()) { + if (v->type == VEH_TRAIN && v->IsPrimaryVehicle()) { + /* The vehicle center is now more to the front depending on vehicle length, + * so we need to move all vehicles forward to cover the difference to the + * old center, otherwise wagon spacing in trains would be broken upon load. */ + for (Train *u = Train::From(v); u != nullptr; u = u->Next()) { + if (u->track == TRACK_BIT_DEPOT || (u->vehstatus & VS_CRASHED)) continue; + + Train *next = u->Next(); + + /* Try to pull the vehicle half its length forward. */ + int diff = (VEHICLE_LENGTH - u->gcache.cached_veh_length) / 2; + int done; + for (done = 0; done < diff; done++) { + if (!TrainController(u, next, false)) break; + } + + if (next != nullptr && done < diff && u->IsFrontEngine()) { + /* Pulling the front vehicle forwards failed, we either encountered a dead-end + * or a red signal. To fix this, we try to move the whole train the required + * space backwards and re-do the fix up of the front vehicle. */ + + /* Ignore any signals when backtracking. */ + TrainForceProceeding old_tfp = u->force_proceed; + u->force_proceed = TFP_SIGNAL; + + /* Swap start<>end, start+1<>end-1, ... */ + int r = CountVehiclesInChain(u) - 1; // number of vehicles - 1 + int l = 0; + do ReverseTrainSwapVeh(u, l++, r--); while (l <= r); + + /* We moved the first vehicle which is now the last. Move it back to the + * original position as we will fix up the last vehicle later in the loop. */ + for (int i = 0; i < done; i++) TrainController(u->Last(), nullptr); + + /* Move the train backwards to get space for the first vehicle. As the stopping + * distance from a line end is rounded up, move the train one unit more to cater + * for front vehicles with odd lengths. */ + int moved; + for (moved = 0; moved < diff + 1; moved++) { + if (!TrainController(u, nullptr, false)) break; + } + + /* Swap start<>end, start+1<>end-1, ... again. */ + r = CountVehiclesInChain(u) - 1; // number of vehicles - 1 + l = 0; + do ReverseTrainSwapVeh(u, l++, r--); while (l <= r); + + u->force_proceed = old_tfp; + + /* Tracks are too short to fix the train length. The player has to fix the + * train in a depot. Bail out so we don't damage the vehicle chain any more. */ + if (moved < diff + 1) break; + + /* Re-do the correction for the first vehicle. */ + for (done = 0; done < diff; done++) TrainController(u, next, false); + + /* We moved one unit more backwards than needed for even-length front vehicles, + * try to move that unit forward again. We don't care if this step fails. */ + TrainController(u, nullptr, false); + } + + /* If the next wagon is still in a depot, check if it shouldn't be outside already. */ + if (next != nullptr && next->track == TRACK_BIT_DEPOT) { + int d = TicksToLeaveDepot(u); + if (d <= 0) { + /* Next vehicle should have left the depot already, show it and pull forward. */ + next->vehstatus &= ~VS_HIDDEN; + next->track = TrackToTrackBits(GetRailDepotTrack(next->tile)); + for (int i = 0; i >= d; i--) TrainController(next, nullptr); + } + } + } + + /* Update all cached properties after moving the vehicle chain around. */ + Train::From(v)->ConsistChanged(CCF_TRACK); + } + } +} + +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; +CargoPacketList _cpp_packets; +std::map _veh_cpp_packets; + +static uint32 _old_ahead_separation; + +/** + * 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 + */ +SaveLoadTable 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_CNAME, SL_MIN_VERSION, SLV_84), + SLE_CONDSTR(Vehicle, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_8), + SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, SLV_8, SL_MAX_VERSION), + SLE_VAR(Vehicle, owner, SLE_UINT8), + SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)), + SLE_CONDVAR_X(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)), + SLE_VAR(Vehicle, direction, SLE_UINT8), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_58), + SLE_VAR(Vehicle, spritenum, SLE_UINT8), + SLE_CONDNULL(5, SL_MIN_VERSION, SLV_58), + SLE_VAR(Vehicle, engine_type, SLE_UINT16), + + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_152), + SLE_VAR(Vehicle, cur_speed, SLE_UINT16), + SLE_VAR(Vehicle, subspeed, SLE_UINT8), + SLE_VAR(Vehicle, acceleration, SLE_UINT8), + SLE_CONDVAR(Vehicle, motion_counter, SLE_UINT32, SLV_VEH_MOTION_COUNTER, SL_MAX_VERSION), + SLE_VAR(Vehicle, progress, SLE_UINT8), + + SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, last_loading_station, SLE_UINT16, SLV_182, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP, SL_CHILLPP_232)), + + SLE_VAR(Vehicle, cargo_type, SLE_UINT8), + SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, SLV_35, SL_MAX_VERSION), + SLEG_CONDVAR( _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68), + SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), + SLEG_CONDVAR( _cargo_source, SLE_UINT16, SLV_7, SLV_68), + SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), + SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), + SLE_CONDVAR(Vehicle, refit_cap, SLE_UINT16, SLV_182, SL_MAX_VERSION), + SLEG_CONDVAR( _cargo_count, SLE_UINT16, SL_MIN_VERSION, SLV_68), + SLE_CONDPTRDEQ(Vehicle, cargo.packets, REF_CARGO_PACKET, SLV_68, SL_MAX_VERSION), + SLEG_CONDPTRDEQ_X( _cpp_packets, REF_CARGO_PACKET, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP)), + SLE_CONDARR(Vehicle, cargo.action_counts, SLE_UINT, VehicleCargoList::NUM_MOVE_TO_ACTION, SLV_181, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, cargo_age_counter, SLE_UINT16, SLV_162, SL_MAX_VERSION), + + SLE_VAR(Vehicle, day_counter, SLE_UINT8), + SLE_VAR(Vehicle, tick_counter, SLE_UINT8), + SLE_CONDVAR_X(Vehicle, running_ticks, SLE_FILE_U8 | SLE_VAR_U16, SLV_88, SL_MAX_VERSION, SlXvFeatureTest([](uint16 version, bool version_in_range, const std::array &feature_versions) -> bool { + return version_in_range && !(SlXvIsFeaturePresent(feature_versions, XSLFI_SPRINGPP, 3) || SlXvIsFeaturePresent(feature_versions, XSLFI_JOKERPP) || SlXvIsFeaturePresent(feature_versions, XSLFI_CHILLPP) || SlXvIsFeaturePresent(feature_versions, XSLFI_VARIABLE_DAY_LENGTH, 2)); + })), + SLE_CONDVAR_X(Vehicle, running_ticks, SLE_UINT16, SLV_88, SL_MAX_VERSION, SlXvFeatureTest([](uint16 version, bool version_in_range, const std::array &feature_versions) -> bool { + return version_in_range && (SlXvIsFeaturePresent(feature_versions, XSLFI_SPRINGPP, 2) || SlXvIsFeaturePresent(feature_versions, XSLFI_JOKERPP) || SlXvIsFeaturePresent(feature_versions, XSLFI_CHILLPP) || SlXvIsFeaturePresent(feature_versions, XSLFI_VARIABLE_DAY_LENGTH, 2)); + })), + + SLE_VAR(Vehicle, cur_implicit_order_index, SLE_VEHORDERID), + SLE_CONDVAR(Vehicle, cur_real_order_index, SLE_VEHORDERID, SLV_158, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, cur_timetable_order_index, SLE_VEHORDERID, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)), + /* num_orders is now part of OrderList and is not saved but counted */ + SLE_CONDNULL(1, SL_MIN_VERSION, SLV_105), + + /* 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_CONDVAR(Vehicle, current_order.type, SLE_UINT8, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), + + /* Orders for version 5 and on */ + SLE_CONDVAR(Vehicle, current_order.type, SLE_UINT8, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, current_order.flags, SLE_FILE_U8 | SLE_VAR_U16, SLV_5, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 0, 0)), + SLE_CONDVAR_X(Vehicle, current_order.flags, SLE_UINT16, SLV_5, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 1)), + SLE_CONDVAR(Vehicle, current_order.dest, SLE_UINT16, SLV_5, SL_MAX_VERSION), + + /* Refit in current order */ + SLE_CONDVAR(Vehicle, current_order.refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION), + SLE_CONDNULL(1, SLV_36, SLV_182), // refit_subtype + + /* Timetable in current order */ + SLE_CONDVAR_X(Vehicle, current_order.wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), + SLE_CONDVAR_X(Vehicle, current_order.wait_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), + SLE_CONDVAR_X(Vehicle, current_order.travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), + SLE_CONDVAR_X(Vehicle, current_order.travel_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), + SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, SLV_174, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, timetable_start, SLE_INT32, SLV_129, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, timetable_start_subticks, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)), + + SLE_CONDREF(Vehicle, orders, REF_ORDER, SL_MIN_VERSION, SLV_105), + SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, SLV_105, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, max_age, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SLV_31, SLV_180), + SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SLV_180, 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_X(Vehicle, breakdown_chance_factor, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 3)), + SLE_CONDVAR_X(Vehicle, breakdown_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS)), + SLE_CONDVAR_X(Vehicle, breakdown_severity, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS)), + SLE_CONDVAR(Vehicle, build_year, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, build_year, SLE_INT32, SLV_31, SL_MAX_VERSION), + + SLE_VAR(Vehicle, load_unload_ticks, SLE_UINT16), + SLEG_CONDVAR( _cargo_paid_for, SLE_UINT16, SLV_45, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_40, SLV_180), + SLE_CONDVAR_X(Vehicle, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 0, 0)), + SLE_CONDVAR_X(Vehicle, vehicle_flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 1)), + + SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, SLV_65, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, SLV_65, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle,profit_lifetime, SLE_INT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEH_LIFETIME_PROFIT)), + SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64, SLV_51, SLV_65), + SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), + SLEG_CONDVAR( _cargo_loaded_at_xy, SLE_UINT32, SLV_51, SLV_68), + SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65), + SLE_CONDVAR(Vehicle, value, SLE_INT64, SLV_65, SL_MAX_VERSION), + SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_REPAIR_COST, 1, 1)), + + SLE_CONDVAR_X(Vehicle, random_bits, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTEND_VEHICLE_RANDOM, 0, 0)), + SLE_CONDVAR_X(Vehicle, random_bits, SLE_UINT16, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTEND_VEHICLE_RANDOM, 1)), + SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, SLV_2, SL_MAX_VERSION), + + SLEG_CONDVAR_X(_old_ahead_separation, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 1, 4)), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 1, 4)), + + SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, SLV_2, SL_MAX_VERSION), + SLE_CONDNULL(2, SLV_2, SLV_69), + SLE_CONDNULL(4, SLV_69, SLV_101), + + SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, SLV_60, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, current_order_time, SLE_UINT32, SLV_67, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, current_loading_time, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE)), + SLE_CONDVAR_X(Vehicle, current_loading_time, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_23)), + SLE_CONDVAR_X(Vehicle, last_loading_tick, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LAST_LOADING_TICK)), + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_CONDVAR(Vehicle, lateness_counter, SLE_INT32, SLV_67, SL_MAX_VERSION), + + SLE_CONDNULL(10, SLV_2, SLV_144), // old reserved space + + SLE_CONDNULL_X((8 + 8 + 2 + 2 + 4 + 4 + 1 + 1) * 30, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_CONDNULL_X((8 + 8 + 2 + 2 + 4 + 4 + 1 + 1) * 70, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + + SLE_CONDNULL_X(160, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), + }; + + static const SaveLoad _train_desc[] = { + SLE_WRITEBYTE(Vehicle, type), + SLE_VEH_INCLUDE(), + SLE_VAR(Train, crash_anim_pos, SLE_UINT16), + SLE_VAR(Train, force_proceed, SLE_UINT8), + SLE_VAR(Train, railtype, SLE_UINT8), + SLE_VAR(Train, track, SLE_UINT8), + + SLE_CONDVAR(Train, flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_2, SLV_100), + SLE_CONDVAR_X(Train, flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_100, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_FLAGS_EXTRA, 0, 0)), + SLE_CONDVAR_X(Train, flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_FLAGS_EXTRA, 1)), + SLE_CONDNULL(2, SLV_2, SLV_60), + + SLE_CONDVAR(Train, wait_counter, SLE_UINT16, SLV_136, SL_MAX_VERSION), + SLE_CONDVAR_X(Train, tunnel_bridge_signal_num, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SIG_TUNNEL_BRIDGE, 5)), + + SLE_CONDNULL(2, SLV_2, SLV_20), + SLE_CONDVAR(Train, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), + SLE_CONDNULL_X(2 , SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), + SLE_CONDNULL(11, SLV_2, SLV_144), // old reserved space + SLE_CONDVAR_X(Train, reverse_distance, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REVERSE_AT_WAYPOINT)), + SLE_CONDVAR_X(Train, speed_restriction, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPEED_RESTRICTION)), + SLE_CONDVAR_X(Train, signal_speed_restriction, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_SPEED_ADAPTATION)), + SLE_CONDVAR_X(Train, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 2)), + }; + + static const SaveLoad _roadveh_desc[] = { + SLE_WRITEBYTE(Vehicle, type), + SLE_VEH_INCLUDE(), + SLE_VAR(RoadVehicle, state, SLE_UINT8), + SLE_VAR(RoadVehicle, frame, SLE_UINT8), + SLE_VAR(RoadVehicle, blocked_ctr, SLE_UINT16), + SLE_VAR(RoadVehicle, overtaking, SLE_UINT8), + SLE_VAR(RoadVehicle, overtaking_ctr, SLE_UINT8), + SLE_VAR(RoadVehicle, crashed_ctr, SLE_UINT16), + SLE_VAR(RoadVehicle, reverse_ctr, SLE_UINT8), + SLE_CONDDEQUE(RoadVehicle, path.td, SLE_UINT8, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION), + SLE_CONDDEQUE(RoadVehicle, path.tile, SLE_UINT32, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION), + SLE_CONDVAR_X(RoadVehicle, path.layout_ctr, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), + + SLE_CONDNULL(2, SLV_6, SLV_69), + SLE_CONDVAR(RoadVehicle, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), + SLE_CONDNULL(4, SLV_69, SLV_131), + SLE_CONDNULL(2, SLV_6, SLV_131), + SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space + SLE_CONDVAR_X(RoadVehicle, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 6)), + }; + + static const SaveLoad _ship_desc[] = { + SLE_WRITEBYTE(Vehicle, type), + SLE_VEH_INCLUDE(), + SLE_VAR(Ship, state, SLE_UINT8), + SLE_CONDDEQUE(Ship, path, SLE_UINT8, SLV_SHIP_PATH_CACHE, SL_MAX_VERSION), + SLE_CONDVAR(Ship, rotation, SLE_UINT8, SLV_SHIP_ROTATION, SL_MAX_VERSION), + SLE_CONDVAR_X(Ship, lost_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SHIP_LOST_COUNTER)), + SLE_CONDVAR_X(Ship, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 8)), + + SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space + }; + + static const SaveLoad _aircraft_desc[] = { + SLE_WRITEBYTE(Vehicle, type), + SLE_VEH_INCLUDE(), + SLE_VAR(Aircraft, crashed_counter, SLE_UINT16), + SLE_VAR(Aircraft, pos, SLE_UINT8), + + SLE_CONDVAR(Aircraft, targetairport, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Aircraft, targetairport, SLE_UINT16, SLV_5, SL_MAX_VERSION), + + SLE_VAR(Aircraft, state, SLE_UINT8), + + SLE_CONDVAR(Aircraft, previous_pos, SLE_UINT8, SLV_2, SL_MAX_VERSION), + SLE_CONDVAR(Aircraft, last_direction, SLE_UINT8, SLV_2, SL_MAX_VERSION), + SLE_CONDVAR(Aircraft, number_consecutive_turns, SLE_UINT8, SLV_2, SL_MAX_VERSION), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP)), + + SLE_CONDVAR(Aircraft, turn_counter, SLE_UINT8, SLV_136, SL_MAX_VERSION), + SLE_CONDVAR(Aircraft, flags, SLE_UINT8, SLV_167, SL_MAX_VERSION), + + SLE_CONDNULL(13, SLV_2, SLV_144), // old reserved space + }; + + static const SaveLoad _special_desc[] = { + SLE_WRITEBYTE(Vehicle, type), + + SLE_VAR(Vehicle, subtype, SLE_UINT8), + + SLE_CONDNULL_X(5, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + + SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)), + SLE_CONDVAR_X(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)), + + SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), + SLE_CONDNULL(5, SL_MIN_VERSION, SLV_59), + SLE_VAR(Vehicle, progress, SLE_UINT8), + SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + + SLE_VAR(EffectVehicle, animation_state, SLE_UINT16), + SLE_VAR(EffectVehicle, animation_substate, SLE_UINT8), + + SLE_CONDVAR(Vehicle, spritenum, SLE_UINT8, SLV_2, SL_MAX_VERSION), + + SLE_CONDNULL(15, SLV_2, SLV_144), // old reserved space + }; + + static const SaveLoad _disaster_desc[] = { + SLE_WRITEBYTE(Vehicle, type), + + SLE_REF(Vehicle, next, REF_VEHICLE_OLD), + + SLE_VAR(Vehicle, subtype, SLE_UINT8), + SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + + SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6), + SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, SLV_6, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)), + SLE_CONDVAR_X(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)), + SLE_VAR(Vehicle, direction, SLE_UINT8), + + SLE_CONDNULL(5, SL_MIN_VERSION, SLV_58), + SLE_VAR(Vehicle, owner, SLE_UINT8), + SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + SLE_CONDVAR_X(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5, SlXvFeatureTest(XSLFTO_AND, XSLFI_DISASTER_VEH_STATE, 0, 0)), + SLE_CONDVAR_X(Vehicle, current_order.dest, SLE_UINT16, SLV_5, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_DISASTER_VEH_STATE, 0, 0)), + SLE_CONDVAR_X(DisasterVehicle, state, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_DISASTER_VEH_STATE, 1)), + + SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), + SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), + SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_VAR(Vehicle, tick_counter, SLE_UINT8), + + SLE_CONDVAR(DisasterVehicle, image_override, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_191), + SLE_CONDVAR(DisasterVehicle, image_override, SLE_UINT32, SLV_191, SL_MAX_VERSION), + SLE_CONDVAR(DisasterVehicle, big_ufo_destroyer_target, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_191), + SLE_CONDVAR(DisasterVehicle, big_ufo_destroyer_target, SLE_UINT32, SLV_191, SL_MAX_VERSION), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP)), + SLE_CONDVAR(DisasterVehicle, flags, SLE_UINT8, SLV_194, SL_MAX_VERSION), + + SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space + }; + + + static const SaveLoadTable _veh_descs[] = { + _train_desc, + _roadveh_desc, + _ship_desc, + _aircraft_desc, + _special_desc, + _disaster_desc, + _common_veh_desc, + }; + + return _veh_descs[vt]; +} + +static std::vector _filtered_train_desc; +static std::vector _filtered_roadveh_desc; +static std::vector _filtered_ship_desc; +static std::vector _filtered_aircraft_desc; +static std::vector _filtered_special_desc; +static std::vector _filtered_disaster_desc; + +static std::vector * const _filtered_veh_descs[] = { + &_filtered_train_desc, + &_filtered_roadveh_desc, + &_filtered_ship_desc, + &_filtered_aircraft_desc, + &_filtered_special_desc, + &_filtered_disaster_desc, +}; + +const SaveLoadTable GetVehicleDescriptionFiltered(VehicleType vt) +{ + return *(_filtered_veh_descs[vt]); +} + +static void SetupDescs_VEHS() +{ + for (size_t i = 0; i < lengthof(_filtered_veh_descs); i++) { + *(_filtered_veh_descs[i]) = SlFilterObject(GetVehicleDescription((VehicleType) i)); + } +} + +/** Will be called when the vehicles need to be saved. */ +static void Save_VEHS() +{ + SetupDescs_VEHS(); + /* Write the vehicles */ + for (Vehicle *v : Vehicle::Iterate()) { + SlSetArrayIndex(v->index); + SlObjectSaveFiltered(v, GetVehicleDescriptionFiltered(v->type)); + } +} + +/** Will be called when vehicles need to be loaded. */ +void Load_VEHS() +{ + SetupDescs_VEHS(); + + int index; + + _cargo_count = 0; + + _cpp_packets.clear(); + _veh_cpp_packets.clear(); + + 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: // Savegame shouldn't contain invalid vehicles + default: SlErrorCorrupt("Invalid vehicle type"); + } + + SlObjectLoadFiltered(v, GetVehicleDescriptionFiltered(vtype)); + + if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v) && CargoPacket::CanAllocateItem()) { + /* Don't construct the packet with station here, because that'll fail with old savegames */ + CargoPacket *cp = new CargoPacket(_cargo_count, _cargo_days, _cargo_source, _cargo_source_xy, _cargo_loaded_at_xy, _cargo_feeder_share); + v->cargo.Append(cp); + } + + /* Old savegames used 'last_station_visited = 0xFF' */ + if (IsSavegameVersionBefore(SLV_5) && v->last_station_visited == 0xFF) { + v->last_station_visited = INVALID_STATION; + } + + if (IsSavegameVersionBefore(SLV_182) && !SlXvIsFeaturePresent(XSLFI_CHILLPP)) v->last_loading_station = INVALID_STATION; + + if (IsSavegameVersionBefore(SLV_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 (IsSavegameVersionBefore(SLV_60)) v->group_id = DEFAULT_GROUP; + + if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { + _veh_cpp_packets[index] = std::move(_cpp_packets); + _cpp_packets.clear(); + } + + if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 4)) { + SB(v->vehicle_flags, VF_SEPARATION_ACTIVE, 1, _old_ahead_separation ? 1 : 0); + } + } +} + +static void Ptrs_VEHS() +{ + SetupDescs_VEHS(); + + for (Vehicle *v : Vehicle::Iterate()) { + if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) _cpp_packets = std::move(_veh_cpp_packets[v->index]); + SlObjectPtrOrNullFiltered(v, GetVehicleDescriptionFiltered(v->type)); + if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) _veh_cpp_packets[v->index] = std::move(_cpp_packets); + } +} + +const SaveLoadTable GetOrderExtraInfoDescription(); + +void Save_VEOX() +{ + /* save extended order info for vehicle current order */ + for (Vehicle *v : Vehicle::Iterate()) { + if (v->current_order.extra) { + SlSetArrayIndex(v->index); + SlObject(v->current_order.extra.get(), GetOrderExtraInfoDescription()); + } + } +} + +void Load_VEOX() +{ + /* load extended order info for vehicle current order */ + int index; + while ((index = SlIterateArray()) != -1) { + Vehicle *v = Vehicle::GetIfValid(index); + assert(v != nullptr); + v->current_order.AllocExtraInfo(); + SlObject(v->current_order.extra.get(), GetOrderExtraInfoDescription()); + } +} + +const SaveLoadTable GetVehicleSpeedRestrictionDescription() +{ + static const SaveLoad _vehicle_speed_restriction_desc[] = { + SLE_VAR(PendingSpeedRestrictionChange, distance, SLE_UINT16), + SLE_VAR(PendingSpeedRestrictionChange, new_speed, SLE_UINT16), + SLE_VAR(PendingSpeedRestrictionChange, prev_speed, SLE_UINT16), + SLE_VAR(PendingSpeedRestrictionChange, flags, SLE_UINT16), + }; + + return _vehicle_speed_restriction_desc; +} + +void Save_VESR() +{ + for (auto &it : _pending_speed_restriction_change_map) { + SlSetArrayIndex(it.first); + PendingSpeedRestrictionChange *ptr = &(it.second); + SlObject(ptr, GetVehicleSpeedRestrictionDescription()); + } +} + +void Load_VESR() +{ + int index; + while ((index = SlIterateArray()) != -1) { + auto iter = _pending_speed_restriction_change_map.insert({ static_cast(index), {} }); + PendingSpeedRestrictionChange *ptr = &(iter->second); + SlObject(ptr, GetVehicleSpeedRestrictionDescription()); + } +} + +struct vehicle_venc { + VehicleID id; + VehicleCache vcache; +}; + +struct train_venc { + VehicleID id; + GroundVehicleCache gvcache; + int cached_curve_speed_mod; + uint8 cached_tflags; + uint8 cached_num_engines; + uint16 cached_centre_mass; + uint16 cached_braking_length; + uint16 cached_veh_weight; + uint16 cached_uncapped_decel; + uint8 cached_deceleration; + byte user_def_data; + int cached_max_curve_speed; +}; + +struct roadvehicle_venc { + VehicleID id; + GroundVehicleCache gvcache; +}; + +struct aircraft_venc { + VehicleID id; + uint16 cached_max_range; +}; + +static std::vector _vehicle_vencs; +static std::vector _train_vencs; +static std::vector _roadvehicle_vencs; +static std::vector _aircraft_vencs; + +void Save_VENC() +{ + assert(_sl_xv_feature_versions[XSLFI_VENC_CHUNK] != 0); + + if (!IsNetworkServerSave()) { + SlSetLength(0); + return; + } + + SlAutolength([](void *) { + int types[4] = {}; + int total = 0; + for (Vehicle *v : Vehicle::Iterate()) { + total++; + if (v->type < VEH_COMPANY_END) types[v->type]++; + } + + /* vehicle cache */ + SlWriteUint32(total); + for (Vehicle *v : Vehicle::Iterate()) { + SlWriteUint32(v->index); + SlWriteUint16(v->vcache.cached_max_speed); + SlWriteUint16(v->vcache.cached_cargo_age_period); + SlWriteByte(v->vcache.cached_vis_effect); + SlWriteByte(v->vcache.cached_veh_flags); + } + + auto write_gv_cache = [&](const GroundVehicleCache &cache) { + SlWriteUint32(cache.cached_weight); + SlWriteUint32(cache.cached_slope_resistance); + SlWriteUint32(cache.cached_max_te); + SlWriteUint32(cache.cached_axle_resistance); + SlWriteUint32(cache.cached_max_track_speed); + SlWriteUint32(cache.cached_power); + SlWriteUint32(cache.cached_air_drag); + SlWriteUint16(cache.cached_total_length); + SlWriteUint16(cache.first_engine); + SlWriteByte(cache.cached_veh_length); + }; + + /* train */ + SlWriteUint32(types[VEH_TRAIN]); + for (Train *t : Train::Iterate()) { + SlWriteUint32(t->index); + write_gv_cache(t->gcache); + SlWriteUint32(t->tcache.cached_curve_speed_mod); + SlWriteByte(t->tcache.cached_tflags); + SlWriteByte(t->tcache.cached_num_engines); + SlWriteUint16(t->tcache.cached_centre_mass); + SlWriteUint16(t->tcache.cached_braking_length); + SlWriteUint16(t->tcache.cached_veh_weight); + SlWriteUint16(t->tcache.cached_uncapped_decel); + SlWriteByte(t->tcache.cached_deceleration); + SlWriteByte(t->tcache.user_def_data); + SlWriteUint32(t->tcache.cached_max_curve_speed); + } + + /* road vehicle */ + SlWriteUint32(types[VEH_ROAD]); + for (RoadVehicle *rv : RoadVehicle::Iterate()) { + SlWriteUint32(rv->index); + write_gv_cache(rv->gcache); + } + + /* aircraft */ + SlWriteUint32(types[VEH_AIRCRAFT]); + for (Aircraft *a : Aircraft::Iterate()) { + SlWriteUint32(a->index); + SlWriteUint16(a->acache.cached_max_range); + } + }, nullptr); +} + +void Load_VENC() +{ + if (SlGetFieldLength() == 0) return; + + if (!_networking || _network_server) { + SlSkipBytes(SlGetFieldLength()); + return; + } + + _vehicle_vencs.resize(SlReadUint32()); + for (vehicle_venc &venc : _vehicle_vencs) { + venc.id = SlReadUint32(); + venc.vcache.cached_max_speed = SlReadUint16(); + venc.vcache.cached_cargo_age_period = SlReadUint16(); + venc.vcache.cached_vis_effect = SlReadByte(); + venc.vcache.cached_veh_flags = SlReadByte(); + } + + auto read_gv_cache = [&](GroundVehicleCache &cache) { + cache.cached_weight = SlReadUint32(); + cache.cached_slope_resistance = SlReadUint32(); + cache.cached_max_te = SlReadUint32(); + cache.cached_axle_resistance = SlReadUint32(); + cache.cached_max_track_speed = SlReadUint32(); + cache.cached_power = SlReadUint32(); + cache.cached_air_drag = SlReadUint32(); + cache.cached_total_length = SlReadUint16(); + cache.first_engine = SlReadUint16(); + cache.cached_veh_length = SlReadByte(); + }; + + _train_vencs.resize(SlReadUint32()); + for (train_venc &venc : _train_vencs) { + venc.id = SlReadUint32(); + read_gv_cache(venc.gvcache); + venc.cached_curve_speed_mod = SlReadUint32(); + venc.cached_tflags = SlReadByte(); + venc.cached_num_engines = SlReadByte(); + venc.cached_centre_mass = SlReadUint16(); + venc.cached_braking_length = SlReadUint16(); + venc.cached_veh_weight = SlReadUint16(); + venc.cached_uncapped_decel = SlReadUint16(); + venc.cached_deceleration = SlReadByte(); + venc.user_def_data = SlReadByte(); + venc.cached_max_curve_speed = SlReadUint32(); + } + + _roadvehicle_vencs.resize(SlReadUint32()); + for (roadvehicle_venc &venc : _roadvehicle_vencs) { + venc.id = SlReadUint32(); + read_gv_cache(venc.gvcache); + } + + _aircraft_vencs.resize(SlReadUint32()); + for (aircraft_venc &venc : _aircraft_vencs) { + venc.id = SlReadUint32(); + venc.cached_max_range = SlReadUint16(); + } +} + +void SlResetVENC() +{ + _vehicle_vencs.clear(); + _train_vencs.clear(); + _roadvehicle_vencs.clear(); + _aircraft_vencs.clear(); +} + +static void LogVehicleVENCMessage(const Vehicle *v, const char *var) +{ + char log_buffer[1024]; + + char *p = log_buffer + seprintf(log_buffer, lastof(log_buffer), "[load]: vehicle cache mismatch: %s", var); + + extern void WriteVehicleInfo(char *&p, const char *last, const Vehicle *u, const Vehicle *v, uint length); + uint length = 0; + for (const Vehicle *u = v->First(); u != v; u = u->Next()) { + length++; + } + WriteVehicleInfo(p, lastof(log_buffer), v, v->First(), length); + DEBUG(desync, 0, "%s", log_buffer); + LogDesyncMsg(log_buffer); +} + +template +void CheckVehicleVENCProp(T &v_prop, T venc_prop, const Vehicle *v, const char *var) +{ + if (v_prop != venc_prop) { + std::string data = fmt::format("{} [{:X} != {:X}]", var, v_prop, venc_prop); + v_prop = venc_prop; + LogVehicleVENCMessage(v, data.c_str()); + } +} + +void SlProcessVENC() +{ + for (const vehicle_venc &venc : _vehicle_vencs) { + Vehicle *v = Vehicle::GetIfValid(venc.id); + if (v == nullptr) continue; + CheckVehicleVENCProp(v->vcache.cached_max_speed, venc.vcache.cached_max_speed, v, "cached_max_speed"); + CheckVehicleVENCProp(v->vcache.cached_cargo_age_period, venc.vcache.cached_cargo_age_period, v, "cached_cargo_age_period"); + CheckVehicleVENCProp(v->vcache.cached_vis_effect, venc.vcache.cached_vis_effect, v, "cached_vis_effect"); + if (HasBit(v->vcache.cached_veh_flags ^ venc.vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT)) { + SB(v->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT, 1, HasBit(venc.vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT) ? 1 : 0); + LogVehicleVENCMessage(v, "VCF_LAST_VISUAL_EFFECT"); + } + } + + auto check_gv_cache = [&](GroundVehicleCache &v_gvcache, const GroundVehicleCache &venc_gvcache, const Vehicle *v) { + CheckVehicleVENCProp(v_gvcache.cached_weight, venc_gvcache.cached_weight, v, "cached_weight"); + CheckVehicleVENCProp(v_gvcache.cached_slope_resistance, venc_gvcache.cached_slope_resistance, v, "cached_slope_resistance"); + CheckVehicleVENCProp(v_gvcache.cached_max_te, venc_gvcache.cached_max_te, v, "cached_max_te"); + CheckVehicleVENCProp(v_gvcache.cached_axle_resistance, venc_gvcache.cached_axle_resistance, v, "cached_axle_resistance"); + CheckVehicleVENCProp(v_gvcache.cached_max_track_speed, venc_gvcache.cached_max_track_speed, v, "cached_max_track_speed"); + CheckVehicleVENCProp(v_gvcache.cached_power, venc_gvcache.cached_power, v, "cached_power"); + CheckVehicleVENCProp(v_gvcache.cached_air_drag, venc_gvcache.cached_air_drag, v, "cached_air_drag"); + CheckVehicleVENCProp(v_gvcache.cached_total_length, venc_gvcache.cached_total_length, v, "cached_total_length"); + CheckVehicleVENCProp(v_gvcache.first_engine, venc_gvcache.first_engine, v, "first_engine"); + CheckVehicleVENCProp(v_gvcache.cached_veh_length, venc_gvcache.cached_veh_length, v, "cached_veh_length"); + }; + + for (const train_venc &venc : _train_vencs) { + Train *t = Train::GetIfValid(venc.id); + if (t == nullptr) continue; + check_gv_cache(t->gcache, venc.gvcache, t); + CheckVehicleVENCProp(t->tcache.cached_curve_speed_mod, venc.cached_curve_speed_mod, t, "cached_curve_speed_mod"); + CheckVehicleVENCProp(t->tcache.cached_tflags, (TrainCacheFlags)venc.cached_tflags, t, "cached_tflags"); + CheckVehicleVENCProp(t->tcache.cached_num_engines, venc.cached_num_engines, t, "cached_num_engines"); + CheckVehicleVENCProp(t->tcache.cached_centre_mass, venc.cached_centre_mass, t, "cached_centre_mass"); + CheckVehicleVENCProp(t->tcache.cached_braking_length, venc.cached_braking_length, t, "cached_braking_length"); + CheckVehicleVENCProp(t->tcache.cached_veh_weight, venc.cached_veh_weight, t, "cached_veh_weight"); + CheckVehicleVENCProp(t->tcache.cached_uncapped_decel, venc.cached_uncapped_decel, t, "cached_uncapped_decel"); + CheckVehicleVENCProp(t->tcache.cached_deceleration, venc.cached_deceleration, t, "cached_deceleration"); + CheckVehicleVENCProp(t->tcache.user_def_data, venc.user_def_data, t, "user_def_data"); + CheckVehicleVENCProp(t->tcache.cached_max_curve_speed, venc.cached_max_curve_speed, t, "cached_max_curve_speed"); + } + + for (const roadvehicle_venc &venc : _roadvehicle_vencs) { + RoadVehicle *rv = RoadVehicle::GetIfValid(venc.id); + if (rv == nullptr) continue; + check_gv_cache(rv->gcache, venc.gvcache, rv); + } + + for (const aircraft_venc &venc : _aircraft_vencs) { + Aircraft *a = Aircraft::GetIfValid(venc.id); + if (a == nullptr) continue; + if (a->acache.cached_max_range != venc.cached_max_range) { + a->acache.cached_max_range = venc.cached_max_range; + a->acache.cached_max_range_sqr = venc.cached_max_range * venc.cached_max_range; + LogVehicleVENCMessage(a, "cached_max_range"); + } + } +} + +static ChunkSaveLoadSpecialOpResult Special_VENC(uint32 chunk_id, ChunkSaveLoadSpecialOp op) +{ + switch (op) { + case CSLSO_SHOULD_SAVE_CHUNK: + if (_sl_xv_feature_versions[XSLFI_VENC_CHUNK] == 0) return CSLSOR_DONT_SAVE_CHUNK; + break; + + default: + break; + } + return CSLSOR_NONE; +} + +const SaveLoadTable GetVehicleLookAheadDescription() +{ + static const SaveLoad _vehicle_look_ahead_desc[] = { + SLE_VAR(TrainReservationLookAhead, reservation_end_tile, SLE_UINT32), + SLE_VAR(TrainReservationLookAhead, reservation_end_trackdir, SLE_UINT8), + SLE_VAR(TrainReservationLookAhead, current_position, SLE_INT32), + SLE_VAR(TrainReservationLookAhead, reservation_end_position, SLE_INT32), + SLE_CONDVAR_X(TrainReservationLookAhead, lookahead_end_position, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 9)), + SLE_VAR(TrainReservationLookAhead, reservation_end_z, SLE_INT16), + SLE_VAR(TrainReservationLookAhead, tunnel_bridge_reserved_tiles, SLE_INT16), + SLE_VAR(TrainReservationLookAhead, flags, SLE_UINT16), + SLE_VAR(TrainReservationLookAhead, speed_restriction, SLE_UINT16), + SLE_CONDVAR_X(TrainReservationLookAhead, next_extend_position, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 5)), + SLE_CONDVAR_X(TrainReservationLookAhead, cached_zpos, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 6)), + SLE_CONDVAR_X(TrainReservationLookAhead, zpos_refresh_remaining, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 6)), + }; + + return _vehicle_look_ahead_desc; +} + +const SaveLoadTable GetVehicleLookAheadItemDescription() +{ + static const SaveLoad _vehicle_look_ahead_item_desc[] = { + SLE_VAR(TrainReservationLookAheadItem, start, SLE_INT32), + SLE_VAR(TrainReservationLookAheadItem, end, SLE_INT32), + SLE_VAR(TrainReservationLookAheadItem, z_pos, SLE_INT16), + SLE_VAR(TrainReservationLookAheadItem, data_id, SLE_UINT16), + SLE_CONDVAR_X(TrainReservationLookAheadItem, data_aux, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 9)), + SLE_VAR(TrainReservationLookAheadItem, type, SLE_UINT8), + }; + + return _vehicle_look_ahead_item_desc; +} + +const SaveLoadTable GetVehicleLookAheadCurveDescription() +{ + static const SaveLoad _vehicle_look_ahead_curve_desc[] = { + SLE_VAR(TrainReservationLookAheadCurve, position, SLE_INT32), + SLE_VAR(TrainReservationLookAheadCurve, dir_diff, SLE_UINT8), + }; + + return _vehicle_look_ahead_curve_desc; +} + +static void RealSave_VLKA(TrainReservationLookAhead *lookahead) +{ + SlObject(lookahead, GetVehicleLookAheadDescription()); + SlWriteUint32((uint32)lookahead->items.size()); + for (TrainReservationLookAheadItem &item : lookahead->items) { + SlObject(&item, GetVehicleLookAheadItemDescription()); + } + SlWriteUint32((uint32)lookahead->curves.size()); + for (TrainReservationLookAheadCurve &curve : lookahead->curves) { + SlObject(&curve, GetVehicleLookAheadCurveDescription()); + } +} + +void Save_VLKA() +{ + for (Train *t : Train::Iterate()) { + if (t->lookahead != nullptr) { + SlSetArrayIndex(t->index); + SlAutolength((AutolengthProc*) RealSave_VLKA, t->lookahead.get()); + } + } +} + +void Load_VLKA() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Train *t = Train::GetIfValid(index); + assert(t != nullptr); + t->lookahead.reset(new TrainReservationLookAhead()); + SlObject(t->lookahead.get(), GetVehicleLookAheadDescription()); + uint32 items = SlReadUint32(); + t->lookahead->items.resize(items); + for (uint i = 0; i < items; i++) { + SlObject(&t->lookahead->items[i], GetVehicleLookAheadItemDescription()); + } + uint32 curves = SlReadUint32(); + t->lookahead->curves.resize(curves); + for (uint i = 0; i < curves; i++) { + SlObject(&t->lookahead->curves[i], GetVehicleLookAheadCurveDescription()); + } + } +} + +static const ChunkHandler veh_chunk_handlers[] = { + { 'VEHS', Save_VEHS, Load_VEHS, Ptrs_VEHS, nullptr, CH_SPARSE_ARRAY }, + { 'VEOX', Save_VEOX, Load_VEOX, nullptr, nullptr, CH_SPARSE_ARRAY }, + { 'VESR', Save_VESR, Load_VESR, nullptr, nullptr, CH_SPARSE_ARRAY }, + { 'VENC', Save_VENC, Load_VENC, nullptr, nullptr, CH_RIFF, Special_VENC }, + { 'VLKA', Save_VLKA, Load_VLKA, nullptr, nullptr, CH_SPARSE_ARRAY }, +}; + +extern const ChunkHandlerTable _veh_chunk_handlers(veh_chunk_handlers); diff --git a/src/saveload/waypoint_sl.cpp b/src/sl/waypoint_sl.cpp similarity index 100% rename from src/saveload/waypoint_sl.cpp rename to src/sl/waypoint_sl.cpp diff --git a/src/statusbar_gui.cpp b/src/statusbar_gui.cpp index dbae86083c..5a2dff8fce 100644 --- a/src/statusbar_gui.cpp +++ b/src/statusbar_gui.cpp @@ -20,7 +20,7 @@ #include "news_gui.h" #include "company_gui.h" #include "window_gui.h" -#include "saveload/saveload.h" +#include "sl/saveload.h" #include "window_func.h" #include "statusbar_gui.h" #include "toolbar_gui.h" diff --git a/src/tbtr_template_vehicle.h b/src/tbtr_template_vehicle.h index c7b6ab7e45..41f2d4541d 100644 --- a/src/tbtr_template_vehicle.h +++ b/src/tbtr_template_vehicle.h @@ -27,7 +27,7 @@ #include "sortlist_type.h" -#include "saveload/saveload_common.h" +#include "sl/saveload_common.h" #include "zoom_func.h" diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 9c82c61ba0..4e14b3f295 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -25,7 +25,7 @@ #include "newgrf_cache_check.h" #include "landscape.h" #include "network/network.h" -#include "saveload/saveload_common.h" +#include "sl/saveload_common.h" #include #include diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index 5639570941..2d2feeefe2 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -19,7 +19,7 @@ #include "../blitter/factory.hpp" #include "../company_func.h" #include "../core/random_func.hpp" -#include "../saveload/saveload.h" +#include "../sl/saveload.h" #include "../thread.h" #include "../window_func.h" #include "dedicated_v.h" diff --git a/src/video/null_v.cpp b/src/video/null_v.cpp index bec6f6a9e1..576738dfb2 100644 --- a/src/video/null_v.cpp +++ b/src/video/null_v.cpp @@ -10,7 +10,7 @@ #include "../stdafx.h" #include "../gfx_func.h" #include "../blitter/factory.hpp" -#include "../saveload/saveload.h" +#include "../sl/saveload.h" #include "../window_func.h" #include "../thread.h" #include "null_v.h"