diff options
author | Patric Stout <truebrain@openttd.org> | 2021-06-14 10:05:30 +0200 |
---|---|---|
committer | Patric Stout <github@truebrain.nl> | 2021-07-02 22:21:58 +0200 |
commit | 7dd5fd6ed497e1da40c13075d6e37b54ab12a082 (patch) | |
tree | 19f2e49e0e9ad714cec2fcf917dca954d33d8c0a /src | |
parent | 513641f9baaa732ab8000bc452a26284ae601f32 (diff) | |
download | openttd-7dd5fd6ed497e1da40c13075d6e37b54ab12a082.tar.xz |
Feature: framework to make savegames self-descriptive
We won't be able to make it fully self-descriptive (looking at you
MAP-chunks), but anything else can. With this framework, we can
add headers for each chunk explaining how each chunk looks like
in detail.
They also will all be tables, making it a lot easier to read in
external tooling, and opening the way to consider a database
(like SQLite) to use as savegame format.
Lastly, with the headers in the savegame, you can freely add
fields without needing a savegame version bump; older versions
of OpenTTD will simply ignore the new field. This also means
we can remove all the SLE_CONDNULL, as they are irrelevant.
The next few commits will start using this framework.
Diffstat (limited to 'src')
-rw-r--r-- | src/core/span_type.hpp | 2 | ||||
-rw-r--r-- | src/saveload/ai_sl.cpp | 8 | ||||
-rw-r--r-- | src/saveload/animated_tile_sl.cpp | 2 | ||||
-rw-r--r-- | src/saveload/cheat_sl.cpp | 1 | ||||
-rw-r--r-- | src/saveload/company_sl.cpp | 12 | ||||
-rw-r--r-- | src/saveload/depot_sl.cpp | 2 | ||||
-rw-r--r-- | src/saveload/game_sl.cpp | 14 | ||||
-rw-r--r-- | src/saveload/gamelog_sl.cpp | 26 | ||||
-rw-r--r-- | src/saveload/industry_sl.cpp | 4 | ||||
-rw-r--r-- | src/saveload/linkgraph_sl.cpp | 8 | ||||
-rw-r--r-- | src/saveload/map_sl.cpp | 4 | ||||
-rw-r--r-- | src/saveload/misc_sl.cpp | 44 | ||||
-rw-r--r-- | src/saveload/saveload.cpp | 421 | ||||
-rw-r--r-- | src/saveload/saveload.h | 154 | ||||
-rw-r--r-- | src/saveload/station_sl.cpp | 42 | ||||
-rw-r--r-- | src/saveload/town_sl.cpp | 6 | ||||
-rw-r--r-- | src/saveload/vehicle_sl.cpp | 40 | ||||
-rw-r--r-- | src/script/script_instance.cpp | 2 | ||||
-rw-r--r-- | src/settings.cpp | 2 | ||||
-rw-r--r-- | src/table/settings.h.preamble | 12 |
20 files changed, 619 insertions, 187 deletions
diff --git a/src/core/span_type.hpp b/src/core/span_type.hpp index 394b9ef38..03bc678b7 100644 --- a/src/core/span_type.hpp +++ b/src/core/span_type.hpp @@ -73,6 +73,8 @@ public: typedef size_t size_type; typedef std::ptrdiff_t difference_type; + constexpr span() noexcept : first(nullptr), last(nullptr) {} + constexpr span(pointer data_in, size_t size_in) : first(data_in), last(data_in + size_in) {} template<class Container, typename std::enable_if<(is_compatible_container<Container, element_type>::value), int>::type = 0> diff --git a/src/saveload/ai_sl.cpp b/src/saveload/ai_sl.cpp index 49221ef30..1c44ef732 100644 --- a/src/saveload/ai_sl.cpp +++ b/src/saveload/ai_sl.cpp @@ -26,10 +26,10 @@ 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), + 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) diff --git a/src/saveload/animated_tile_sl.cpp b/src/saveload/animated_tile_sl.cpp index 7f05eaeeb..2e0666d4a 100644 --- a/src/saveload/animated_tile_sl.cpp +++ b/src/saveload/animated_tile_sl.cpp @@ -19,7 +19,7 @@ extern std::vector<TileIndex> _animated_tiles; static const SaveLoad _animated_tile_desc[] = { - SLEG_VECTOR(_animated_tiles, SLE_UINT32), + SLEG_VECTOR("tiles", _animated_tiles, SLE_UINT32), }; /** diff --git a/src/saveload/cheat_sl.cpp b/src/saveload/cheat_sl.cpp index 51df50314..6c6bab4dd 100644 --- a/src/saveload/cheat_sl.cpp +++ b/src/saveload/cheat_sl.cpp @@ -74,7 +74,6 @@ static void Load_CHTS() if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many CHTS entries"); } -/** Chunk handlers related to cheats. */ static const ChunkHandler cheat_chunk_handlers[] = { { 'CHTS', Save_CHTS, Load_CHTS, nullptr, nullptr, CH_ARRAY }, }; diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 880fc9e89..7abec8041 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -294,7 +294,7 @@ public: SLE_CONDNULL(32, SL_MIN_VERSION, SLV_107), SLE_CONDNULL(64, SLV_2, SLV_107), - SLEG_STRUCTLIST(SlCompanyOldAIBuildRec), + SLEG_STRUCTLIST("build_rec", SlCompanyOldAIBuildRec), }; void GenericSaveLoad(CompanyProperties *c) const @@ -513,11 +513,11 @@ static const SaveLoad _company_desc[] = { 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(SlCompanySettings), - SLEG_CONDSTRUCT(SlCompanyOldAI, SL_MIN_VERSION, SLV_107), - SLEG_STRUCT(SlCompanyEconomy), - SLEG_STRUCTLIST(SlCompanyOldEconomy), - SLEG_CONDSTRUCTLIST(SlCompanyLiveries, SLV_34, 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), }; static void Save_PLYR() diff --git a/src/saveload/depot_sl.cpp b/src/saveload/depot_sl.cpp index da195416b..6af08e718 100644 --- a/src/saveload/depot_sl.cpp +++ b/src/saveload/depot_sl.cpp @@ -20,7 +20,7 @@ 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_CONDSSTR(Depot, name, SLE_STR, SLV_141, SL_MAX_VERSION), diff --git a/src/saveload/game_sl.cpp b/src/saveload/game_sl.cpp index 48281ed66..1ef513c63 100644 --- a/src/saveload/game_sl.cpp +++ b/src/saveload/game_sl.cpp @@ -26,10 +26,10 @@ 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), + 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) @@ -116,7 +116,7 @@ static uint32 _game_saveload_strings; class SlGameLanguageString : public DefaultSaveLoadHandler<SlGameLanguageString, LanguageStrings> { public: inline static const SaveLoad description[] = { - SLEG_SSTR(_game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL), + SLEG_SSTR("string", _game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL), }; void Save(LanguageStrings *ls) const override @@ -142,8 +142,8 @@ public: static const SaveLoad _game_language_desc[] = { SLE_SSTR(LanguageStrings, language, SLE_STR), - SLEG_CONDVAR(_game_saveload_strings, SLE_UINT32, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), - SLEG_STRUCTLIST(SlGameLanguageString), + SLEG_CONDVAR("count", _game_saveload_strings, SLE_UINT32, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), + SLEG_STRUCTLIST("strings", SlGameLanguageString), }; static void Load_GSTR() diff --git a/src/saveload/gamelog_sl.cpp b/src/saveload/gamelog_sl.cpp index 9f7f4f140..f76718d42 100644 --- a/src/saveload/gamelog_sl.cpp +++ b/src/saveload/gamelog_sl.cpp @@ -204,7 +204,7 @@ class SlGamelogEmergency : public DefaultSaveLoadHandler<SlGamelogEmergency, Log public: /* We need to store something, so store a "true" value. */ inline static const SaveLoad description[] = { - SLEG_CONDVAR(_is_emergency_save, SLE_BOOL, SLV_RIFF_TO_ARRAY, SL_MAX_VERSION), + SLEG_CONDVAR("is_emergency_save", _is_emergency_save, SLE_BOOL, SLV_RIFF_TO_ARRAY, SL_MAX_VERSION), }; void GenericSaveLoad(LoggedChange *lc) const @@ -224,17 +224,17 @@ class SlGamelogAction : public DefaultSaveLoadHandler<SlGamelogAction, LoggedAct public: inline static const SaveLoad description[] = { SLE_SAVEBYTE(LoggedChange, ct), - SLEG_STRUCT(SlGamelogMode), - SLEG_STRUCT(SlGamelogRevision), - SLEG_STRUCT(SlGamelogOldver), - SLEG_STRUCT(SlGamelogSetting), - SLEG_STRUCT(SlGamelogGrfadd), - SLEG_STRUCT(SlGamelogGrfrem), - SLEG_STRUCT(SlGamelogGrfcompat), - SLEG_STRUCT(SlGamelogGrfparam), - SLEG_STRUCT(SlGamelogGrfmove), - SLEG_STRUCT(SlGamelogGrfbug), - SLEG_STRUCT(SlGamelogEmergency), + 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), }; void Save(LoggedAction *la) const override @@ -285,7 +285,7 @@ public: static const SaveLoad _gamelog_desc[] = { SLE_CONDVAR(LoggedAction, at, SLE_UINT8, SLV_RIFF_TO_ARRAY, SL_MAX_VERSION), SLE_VAR(LoggedAction, tick, SLE_UINT16), - SLEG_STRUCTLIST(SlGamelogAction), + SLEG_STRUCTLIST("action", SlGamelogAction), }; static void Load_GLOG_common(LoggedAction *&gamelog_action, uint &gamelog_actions) diff --git a/src/saveload/industry_sl.cpp b/src/saveload/industry_sl.cpp index 1043023b1..139a5c1af 100644 --- a/src/saveload/industry_sl.cpp +++ b/src/saveload/industry_sl.cpp @@ -67,7 +67,7 @@ static const SaveLoad _industry_desc[] = { 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 @@ -136,7 +136,7 @@ static void Ptrs_INDY() /** 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), }; /** Save industry builder. */ diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp index 7000946da..5456fb33e 100644 --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -83,7 +83,7 @@ public: SLE_VAR(Node, demand, SLE_UINT32), SLE_VAR(Node, station, SLE_UINT16), SLE_VAR(Node, last_update, SLE_INT32), - SLEG_STRUCTLIST(SlLinkgraphEdge), + SLEG_STRUCTLIST("edges", SlLinkgraphEdge), }; void Save(LinkGraph *lg) const override @@ -118,9 +118,9 @@ SaveLoadTable GetLinkGraphDesc() { static const SaveLoad link_graph_desc[] = { SLE_VAR(LinkGraph, last_compression, SLE_INT32), - SLEG_CONDVAR(_num_nodes, SLE_UINT16, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), + SLEG_CONDVAR("num_nodes", _num_nodes, SLE_UINT16, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), SLE_VAR(LinkGraph, cargo, SLE_UINT8), - SLEG_STRUCTLIST(SlLinkgraphNode), + SLEG_STRUCTLIST("nodes", SlLinkgraphNode), }; return link_graph_desc; } @@ -165,7 +165,7 @@ SaveLoadTable GetLinkGraphJobDesc() static const SaveLoad job_desc[] = { SLE_VAR(LinkGraphJob, join_date, SLE_INT32), SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16), - SLEG_STRUCT(SlLinkgraphJobProxy), + SLEG_STRUCT("linkgraph", SlLinkgraphJobProxy), }; /* The member offset arithmetic below is only valid if the types in question diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp index de043d513..476a9a89a 100644 --- a/src/saveload/map_sl.cpp +++ b/src/saveload/map_sl.cpp @@ -21,8 +21,8 @@ static uint32 _map_dim_x; static uint32 _map_dim_y; static const SaveLoad _map_desc[] = { - SLEG_CONDVAR(_map_dim_x, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLEG_CONDVAR(_map_dim_y, SLE_UINT32, SLV_6, SL_MAX_VERSION), + 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() diff --git a/src/saveload/misc_sl.cpp b/src/saveload/misc_sl.cpp index 4c66f4e26..eb227e336 100644 --- a/src/saveload/misc_sl.cpp +++ b/src/saveload/misc_sl.cpp @@ -69,32 +69,32 @@ void ResetViewportAfterLoadGame() byte _age_cargo_skip_counter; ///< Skip aging of cargo? Used before savegame version 162. 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_VAR(_tick_counter, SLE_UINT16), + 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_VAR("tick_counter", _tick_counter, SLE_UINT16), 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), + SLEG_CONDVAR("age_cargo_skip_counter", _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), + 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), SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120), - SLEG_VAR(_random.state[0], SLE_UINT32), - SLEG_VAR(_random.state[1], SLE_UINT32), + SLEG_VAR("random_state[0]", _random.state[0], SLE_UINT32), + SLEG_VAR("random_state[1]", _random.state[1], SLE_UINT32), 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(_next_competitor_start, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109), - SLEG_CONDVAR(_next_competitor_start, SLE_UINT32, SLV_109, SL_MAX_VERSION), - SLEG_VAR(_trees_tick_ctr, SLE_UINT8), - SLEG_CONDVAR(_pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION), + SLEG_VAR("company_tick_counter", _cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32), + SLEG_CONDVAR("next_competitor_start", _next_competitor_start, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109), + SLEG_CONDVAR("next_competitor_start", _next_competitor_start, SLE_UINT32, SLV_109, SL_MAX_VERSION), + SLEG_VAR("trees_tick_counter", _trees_tick_ctr, SLE_UINT8), + SLEG_CONDVAR("pause_mode", _pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION), SLE_CONDNULL(4, SLV_11, SLV_120), }; 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), + 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), SLE_NULL(2), // _date_fract SLE_NULL(2), // _tick_counter SLE_CONDNULL(2, SL_MIN_VERSION, SLV_157), // _vehicle_id_ctr_day @@ -147,11 +147,11 @@ static void Check_DATE() 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), + 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 void Save_VIEW() diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 2184ef85b..82e96080e 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -199,6 +199,7 @@ struct SaveLoadParams { 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. MemoryDumper *dumper; ///< Memory dumper to write the savegame to. SaveFilter *sf; ///< Filter to write the savegame to. @@ -580,6 +581,39 @@ static inline uint SlGetArrayLength(size_t 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: + 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 @@ -610,7 +644,7 @@ static inline uint SlCalcConvMemLen(VarType conv) */ static inline byte SlCalcConvFileLen(VarType conv) { - static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2}; + 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)); @@ -646,6 +680,7 @@ int SlIterateArray() for (;;) { uint length = SlReadArrayLength(); if (length == 0) { + assert(!_sl.expect_table_header); _next_offs = 0; return -1; } @@ -653,8 +688,15 @@ int SlIterateArray() _sl.obj_len = --length; _next_offs = _sl.reader->GetSize() + 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"); @@ -687,6 +729,12 @@ 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 @@ -695,6 +743,7 @@ void SlSetLength(size_t length) 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) { @@ -702,6 +751,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); @@ -1142,7 +1192,15 @@ static void SlArray(void *array, size_t length, VarType conv) case SLA_LOAD: { if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { size_t sv_length = SlReadArrayLength(); - if (sv_length != length) SlErrorCorrupt("Fixed-length array is of wrong length"); + 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); @@ -1502,6 +1560,34 @@ static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld) } /** + * 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. @@ -1765,6 +1851,233 @@ void SlObject(void *object, const SaveLoadTable &slt) } /** + * 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<SaveLoad> 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<SaveLoad> saveloads; + + /* Build a key lookup mapping based on the available fields. */ + std::map<std::string, const SaveLoad *> 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()) { + Debug(sl, 2, "Field '{}' of type 0x{:02x} not found, skipping", key, type); + + std::shared_ptr<SaveLoadHandler> handler = nullptr; + SaveLoadType slt; + 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. */ + slt = 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. */ + slt = SL_STRUCTLIST; + handler = std::make_shared<SlSkipHandler>(); + break; + + default: + slt = (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, slt, ((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 '{}' was expected to be 0x{:02x} but 0x{:02x} was found", key, 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<std::string *>(&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<SaveLoad>(); +} + +/** + * 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<SaveLoad> 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<SaveLoad> saveloads; + + /* Build a key lookup mapping based on the available fields. */ + std::map<std::string, std::vector<const SaveLoad *>> 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 '{}' not found", slc.name); + 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. */ @@ -1811,33 +2124,43 @@ static void SlLoadChunk(const ChunkHandler &ch) 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); + + /* The header should always be at the start. Read the length; the + * load_proc() 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(); if (_next_offs != 0) SlErrorCorrupt("Invalid array length"); break; + case CH_SPARSE_TABLE: case CH_SPARSE_ARRAY: ch.load_proc(); 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 = _sl.reader->GetSize() + len; + ch.load_proc(); + if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size"); + break; default: - if ((m & 0xF) == CH_RIFF) { - /* Read length */ - len = (SlReadByte() << 16) | ((m >> 4) << 24); - len += SlReadUint16(); - _sl.obj_len = len; - endoffs = _sl.reader->GetSize() + len; - ch.load_proc(); - if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size"); - } else { - SlErrorCorrupt("Invalid chunk type"); - } + SlErrorCorrupt("Invalid chunk type"); break; } + + if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); } /** @@ -1851,43 +2174,54 @@ static void SlLoadCheckChunk(const ChunkHandler &ch) 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); + + /* The header should always be at the start. Read the length; the + * load_check_proc() should as first action process the header. */ + if (_sl.expect_table_header && ch.load_check_proc != nullptr) { + /* If load_check_proc() is nullptr, SlSkipArray() will already skip the header. */ + SlIterateArray(); + } - switch (m) { + switch (_sl.block_mode) { + case CH_TABLE: case CH_ARRAY: _sl.array_index = 0; - if (ch.load_check_proc) { + if (ch.load_check_proc != nullptr) { ch.load_check_proc(); } else { SlSkipArray(); } break; + case CH_SPARSE_TABLE: case CH_SPARSE_ARRAY: - if (ch.load_check_proc) { + if (ch.load_check_proc != nullptr) { ch.load_check_proc(); } else { SlSkipArray(); } break; - default: - if ((m & 0xF) == CH_RIFF) { - /* Read length */ - len = (SlReadByte() << 16) | ((m >> 4) << 24); - len += SlReadUint16(); - _sl.obj_len = len; - endoffs = _sl.reader->GetSize() + len; - if (ch.load_check_proc) { - ch.load_check_proc(); - } else { - SlSkipBytes(len); - } - if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size"); + case CH_RIFF: + /* Read length */ + len = (SlReadByte() << 16) | ((m >> 4) << 24); + len += SlReadUint16(); + _sl.obj_len = len; + endoffs = _sl.reader->GetSize() + len; + if (ch.load_check_proc) { + ch.load_check_proc(); } else { - SlErrorCorrupt("Invalid chunk type"); + SlSkipBytes(len); } + if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size"); + break; + default: + SlErrorCorrupt("Invalid chunk type"); break; } + + if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); } /** @@ -1906,24 +2240,31 @@ static void SlSaveChunk(const ChunkHandler &ch) 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; - switch (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: - _sl.need_length = NL_WANTLENGTH; proc(); break; + case CH_TABLE: case CH_ARRAY: _sl.last_array_index = 0; - SlWriteByte(CH_ARRAY); + SlWriteByte(_sl.block_mode); proc(); SlWriteArrayLength(0); // Terminate arrays break; + case CH_SPARSE_TABLE: case CH_SPARSE_ARRAY: - SlWriteByte(CH_SPARSE_ARRAY); + SlWriteByte(_sl.block_mode); proc(); SlWriteArrayLength(0); // Terminate arrays break; default: NOT_REACHED(); } + + if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); } /** Save all chunks */ @@ -3068,3 +3409,9 @@ void FileToSaveLoad::SetTitle(const char *title) { strecpy(this->title, title, lastof(this->title)); } + +SaveLoadTable SaveLoadHandler::GetLoadDescription() const +{ + assert(this->load_description.has_value()); + return *this->load_description; +} diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 0ce46cbe9..b6e843c99 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -13,6 +13,7 @@ #include "../fileio_type.h" #include "../strings_type.h" #include "../core/span_type.hpp" +#include <optional> #include <string> #include <vector> @@ -333,6 +334,8 @@ enum SaveLoadVersion : uint16 { SLV_SAVELOAD_LIST_LENGTH, ///< 293 PR#9374 Consistency in list length with SL_STRUCT / SL_STRUCTLIST / SL_DEQUE / SL_REFLIST. SLV_RIFF_TO_ARRAY, ///< 294 PR#9375 Changed many CH_RIFF chunks to CH_ARRAY chunks. + SLV_TABLE_CHUNKS, ///< 295 PR#9322 Introduction of CH_TABLE and CH_SPARSE_TABLE. + SL_MAX_VERSION, ///< Highest possible saveload version }; @@ -388,6 +391,10 @@ 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. }; @@ -407,9 +414,14 @@ using ChunkHandlerTable = span<const ChunkHandler>; /** A table of SaveLoad entries. */ using SaveLoadTable = span<const struct SaveLoad>; +/** A table of SaveLoadCompat entries. */ +using SaveLoadCompatTable = span<const struct SaveLoadCompat>; + /** Handler for saving/loading an object to/from disk. */ class SaveLoadHandler { public: + std::optional<std::vector<SaveLoad>> load_description; + virtual ~SaveLoadHandler() {} /** @@ -440,6 +452,18 @@ public: * 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 { return {}; } + + /** + * 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; }; /** @@ -496,18 +520,24 @@ enum SLRefType { * Bits 8-15 are reserved for various flags as explained below */ enum VarTypes { - /* 4 bits allocated a maximum of 16 types for NumberType */ - SLE_FILE_I8 = 0, - SLE_FILE_U8 = 1, - SLE_FILE_I16 = 2, - SLE_FILE_U16 = 3, - SLE_FILE_I32 = 4, - SLE_FILE_U32 = 5, - SLE_FILE_I64 = 6, - SLE_FILE_U64 = 7, - SLE_FILE_STRINGID = 8, ///< StringID offset into strings-array - SLE_FILE_STRING = 9, - /* 6 more possible file-primitives */ + /* 4 bits allocated a maximum of 16 types for NumberType. + * 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, @@ -586,6 +616,7 @@ 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, int uint16 length; ///< (conditional) length of the variable (eg. arrays) (max array size is 65536 elements) @@ -594,7 +625,22 @@ struct SaveLoad { 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 - SaveLoadHandler *handler; ///< Custom handler for Save/Load procs. + std::shared_ptr<SaveLoadHandler> 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 until this savegame version. }; /** @@ -608,7 +654,7 @@ struct SaveLoad { * @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) SaveLoad {cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast<void *>(static_cast<const void *>(std::addressof(static_cast<base *>(b)->variable))); }, extra, nullptr} +#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) SaveLoad {#variable, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast<void *>(static_cast<const void *>(std::addressof(static_cast<base *>(b)->variable))); }, extra, nullptr} /** * Storage of a variable in some savegame versions. @@ -744,7 +790,7 @@ struct SaveLoad { * @param from First savegame version that has the empty space. * @param to Last savegame version that has the empty space. */ -#define SLE_CONDNULL(length, from, to) SaveLoad {SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr} +#define SLE_CONDNULL(length, from, to) SaveLoad {"", SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr} /** * Only write byte during saving; never read it during loading. @@ -760,6 +806,7 @@ struct SaveLoad { /** * 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. @@ -768,149 +815,167 @@ struct SaveLoad { * @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(cmd, variable, type, length, from, to, extra) SaveLoad {cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast<void *>(std::addressof(variable)); }, extra, nullptr} +#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<void *>(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(variable, type, from, to) SLEG_GENERAL(SL_VAR, variable, type, 0, from, to, 0) +#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(variable, type, from, to) SLEG_GENERAL(SL_REF, variable, type, 0, from, to, 0) +#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(variable, type, length, from, to) SLEG_GENERAL(SL_ARR, variable, type, length, from, to, 0) +#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(variable, type, length, from, to) SLEG_GENERAL(SL_STR, variable, type, length, from, to, 0) +#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(variable, type, from, to) SLEG_GENERAL(SL_STDSTR, variable, type, 0, from, to, 0) +#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(handler, from, to) SaveLoad {SL_STRUCT, 0, 0, from, to, 0, nullptr, 0, new handler()} +#define SLEG_CONDSTRUCT(name, handler, from, to) SaveLoad {name, SL_STRUCT, 0, 0, from, to, 0, nullptr, 0, std::make_shared<handler>()} /** * 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(variable, type, from, to) SLEG_GENERAL(SL_REFLIST, variable, type, 0, from, to, 0) +#define SLEG_CONDREFLIST(name, variable, type, from, to) SLEG_GENERAL(name, SL_REFLIST, 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(variable, type, from, to) SLEG_GENERAL(SL_VECTOR, variable, type, 0, from, to, 0) +#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(handler, from, to) SaveLoad {SL_STRUCTLIST, 0, 0, from, to, 0, nullptr, 0, new handler()} +#define SLEG_CONDSTRUCTLIST(name, handler, from, to) SaveLoad {name, SL_STRUCTLIST, 0, 0, from, to, 0, nullptr, 0, std::make_shared<handler>()} /** * 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(handler) SLEG_CONDSTRUCT(handler, SL_MIN_VERSION, SL_MAX_VERSION) +#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 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(variable, type) SLEG_CONDVECTOR(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) /** * 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(handler) SLEG_CONDSTRUCTLIST(handler, SL_MIN_VERSION, SL_MAX_VERSION) +#define SLEG_STRUCTLIST(name, handler) SLEG_CONDSTRUCTLIST(name, handler, SL_MIN_VERSION, SL_MAX_VERSION) /** * Empty global space in some savegame versions. @@ -918,7 +983,24 @@ struct SaveLoad { * @param from First savegame version that has the empty space. * @param to Last savegame version that has the empty space. */ -#define SLEG_CONDNULL(length, from, to) SaveLoad {SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr} +#define SLEG_CONDNULL(length, from, to) SaveLoad {"", SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr} + +/** + * 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. @@ -1029,6 +1111,8 @@ void SlWriteByte(byte b); void SlGlobList(const SaveLoadTable &slt); void SlCopy(void *object, size_t length, VarType conv); +std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt); +std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct); void SlObject(void *object, const SaveLoadTable &slt); void NORETURN SlError(StringID string, const char *extra_msg = nullptr); void NORETURN SlErrorCorrupt(const char *msg); diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index a536c02db..5b99b09f7 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -331,29 +331,29 @@ public: inline #endif static const SaveLoad description[] = { - SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68), + 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_CONDNULL(2, SLV_51, SLV_68), SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8), 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), + 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, SLE_FILE_U32 | SLE_VAR_I64, SLV_14, SLV_65), - SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), + 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_CONDREFLIST( _packets, REF_CARGO_PACKET, SLV_68, SLV_183), - SLEG_CONDVAR( _old_num_dests, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), + SLEG_CONDREFLIST("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, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), + 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(SlStationFlow, SLV_183, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST(SlStationCargo, 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; @@ -497,8 +497,8 @@ static const SaveLoad _old_station_desc[] = { /* reserve extra space in savegame here. (currently 32 bytes) */ SLE_CONDNULL(32, SLV_2, SL_MAX_VERSION), - SLEG_STRUCTLIST(SlStationGoods), - SLEG_CONDSTRUCTLIST(SlStationSpecList, SLV_27, SL_MAX_VERSION), + SLEG_STRUCTLIST("goods", SlStationGoods), + SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList, SLV_27, SL_MAX_VERSION), }; static void Load_STNS() @@ -566,7 +566,7 @@ public: class SlStationNormal : public DefaultSaveLoadHandler<SlStationNormal, BaseStation> { public: inline static const SaveLoad description[] = { - SLEG_STRUCT(SlStationBase), + 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), @@ -587,7 +587,7 @@ public: 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(_old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161), + 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), @@ -599,7 +599,7 @@ public: SLE_REFLIST(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_STRUCTLIST(SlStationGoods), + SLEG_STRUCTLIST("goods", SlStationGoods), }; void GenericSaveLoad(BaseStation *bst) const @@ -616,7 +616,7 @@ public: class SlStationWaypoint : public DefaultSaveLoadHandler<SlStationWaypoint, BaseStation> { public: inline static const SaveLoad description[] = { - SLEG_STRUCT(SlStationBase), + SLEG_STRUCT("base", SlStationBase), SLE_VAR(Waypoint, town_cn, SLE_UINT16), SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION), @@ -637,9 +637,9 @@ public: static const SaveLoad _station_desc[] = { SLE_SAVEBYTE(BaseStation, facilities), - SLEG_STRUCT(SlStationNormal), - SLEG_STRUCT(SlStationWaypoint), - SLEG_CONDSTRUCTLIST(SlStationSpecList, SLV_27, SL_MAX_VERSION), + SLEG_STRUCT("normal", SlStationNormal), + SLEG_STRUCT("waypoint", SlStationWaypoint), + SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList, SLV_27, SL_MAX_VERSION), }; static void Save_STNN() diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp index 38c000e3b..c653db53a 100644 --- a/src/saveload/town_sl.cpp +++ b/src/saveload/town_sl.cpp @@ -278,9 +278,9 @@ static const SaveLoad _town_desc[] = { 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 - SLEG_CONDSTRUCTLIST(SlTownSupplied, SLV_165, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST(SlTownReceived, SLV_165, SL_MAX_VERSION), - SLEG_CONDSTRUCTLIST(SlTownAcceptanceMatrix, SLV_166, SLV_REMOVE_TOWN_CARGO_CACHE), + 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 void Save_HIDS() diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index ffa020677..b2d9972fe 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -626,14 +626,14 @@ public: 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), + 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, SLE_UINT16, SL_MIN_VERSION, SLV_68), - SLE_CONDREFLIST(Vehicle, cargo.packets, REF_CARGO_PACKET, SLV_68, SL_MAX_VERSION), + SLEG_CONDVAR("cargo_count", _cargo_count, SLE_UINT16, SL_MIN_VERSION, SLV_68), + SLE_CONDREFLIST(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), @@ -689,7 +689,7 @@ public: 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), + 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_U16, SLV_40, SLV_180), SLE_CONDVAR(Vehicle, vehicle_flags, SLE_UINT16, SLV_180, SL_MAX_VERSION), @@ -697,9 +697,9 @@ public: 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, 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), + 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), @@ -735,7 +735,7 @@ public: class SlVehicleTrain : public DefaultSaveLoadHandler<SlVehicleTrain, Vehicle> { public: inline static const SaveLoad description[] = { - SLEG_STRUCT(SlVehicleCommon), + 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), @@ -766,7 +766,7 @@ public: class SlVehicleRoadVeh : public DefaultSaveLoadHandler<SlVehicleRoadVeh, Vehicle> { public: inline static const SaveLoad description[] = { - SLEG_STRUCT(SlVehicleCommon), + SLEG_STRUCT("common", SlVehicleCommon), SLE_VAR(RoadVehicle, state, SLE_UINT8), SLE_VAR(RoadVehicle, frame, SLE_UINT8), SLE_VAR(RoadVehicle, blocked_ctr, SLE_UINT16), @@ -798,7 +798,7 @@ public: class SlVehicleShip : public DefaultSaveLoadHandler<SlVehicleShip, Vehicle> { public: inline static const SaveLoad description[] = { - SLEG_STRUCT(SlVehicleCommon), + 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), @@ -820,7 +820,7 @@ public: class SlVehicleAircraft : public DefaultSaveLoadHandler<SlVehicleAircraft, Vehicle> { public: inline static const SaveLoad description[] = { - SLEG_STRUCT(SlVehicleCommon), + SLEG_STRUCT("common", SlVehicleCommon), SLE_VAR(Aircraft, crashed_counter, SLE_UINT16), SLE_VAR(Aircraft, pos, SLE_UINT8), @@ -955,12 +955,12 @@ public: const static SaveLoad _vehicle_desc[] = { SLE_SAVEBYTE(Vehicle, type), - SLEG_STRUCT(SlVehicleTrain), - SLEG_STRUCT(SlVehicleRoadVeh), - SLEG_STRUCT(SlVehicleShip), - SLEG_STRUCT(SlVehicleAircraft), - SLEG_STRUCT(SlVehicleEffect), - SLEG_STRUCT(SlVehicleDisaster), + SLEG_STRUCT("train", SlVehicleTrain), + SLEG_STRUCT("roadveh", SlVehicleRoadVeh), + SLEG_STRUCT("ship", SlVehicleShip), + SLEG_STRUCT("aircraft", SlVehicleAircraft), + SLEG_STRUCT("effect", SlVehicleEffect), + SLEG_STRUCT("disaster", SlVehicleDisaster), }; /** Will be called when the vehicles need to be saved. */ diff --git a/src/script/script_instance.cpp b/src/script/script_instance.cpp index 2665a8684..1d57383a9 100644 --- a/src/script/script_instance.cpp +++ b/src/script/script_instance.cpp @@ -345,7 +345,7 @@ static byte _script_sl_byte; ///< Used as source/target by the script saveload c /** SaveLoad array that saves/loads exactly one byte. */ static const SaveLoad _script_byte[] = { - SLEG_VAR(_script_sl_byte, SLE_UINT8), + SLEG_VAR("type", _script_sl_byte, SLE_UINT8), }; /* static */ bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test) diff --git a/src/settings.cpp b/src/settings.cpp index 9847a0ff9..6368dea11 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2194,7 +2194,7 @@ static std::vector<SaveLoad> GetSettingsDesc(const SettingTable &settings, bool if (is_loading && (sd->flags & SF_NO_NETWORK_SYNC) && _networking && !_network_server) { /* We don't want to read this setting, so we do need to skip over it. */ - saveloads.push_back({sd->save.cmd, GetVarFileType(sd->save.conv) | SLE_VAR_NULL, sd->save.length, sd->save.version_from, sd->save.version_to, 0, nullptr, 0, nullptr}); + saveloads.push_back({sd->name, sd->save.cmd, GetVarFileType(sd->save.conv) | SLE_VAR_NULL, sd->save.length, sd->save.version_from, sd->save.version_to, 0, nullptr, 0, nullptr}); continue; } diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index 5fd1a9bac..9641c24ce 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -59,22 +59,22 @@ static size_t ConvertLandscape(const char *value); /* Macros for various objects to go in the configuration file. * This section is for global variables */ #define SDTG_VAR(name, type, flags, var, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(Int, SLEG_GENERAL(SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, min, max, interval, str, strhelp, strval, cat, pre_check, post_callback) + NSD(Int, SLEG_GENERAL(#var, SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, min, max, interval, str, strhelp, strval, cat, pre_check, post_callback) #define SDTG_BOOL(name, flags, var, def, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(Bool, SLEG_GENERAL(SL_VAR, var, SLE_BOOL, 1, from, to, extra), name, flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback) + NSD(Bool, SLEG_GENERAL(#var, SL_VAR, var, SLE_BOOL, 1, from, to, extra), name, flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback) #define SDTG_LIST(name, type, flags, var, def, length, from, to, cat, extra, startup)\ - NSD(List, SLEG_GENERAL(SL_ARR, var, type, length, from, to, extra), name, flags, startup, def) + NSD(List, SLEG_GENERAL(#var, SL_ARR, var, type, length, from, to, extra), name, flags, startup, def) #define SDTG_SSTR(name, type, flags, var, def, max_length, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(String, SLEG_GENERAL(SL_STDSTR, var, type, sizeof(var), from, to, extra), name, flags, startup, def, max_length, pre_check, post_callback) + NSD(String, SLEG_GENERAL(#var, SL_STDSTR, var, type, sizeof(var), from, to, extra), name, flags, startup, def, max_length, pre_check, post_callback) #define SDTG_OMANY(name, type, flags, var, def, max, full, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(OneOfMany, SLEG_GENERAL(SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, max, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) + NSD(OneOfMany, SLEG_GENERAL(#var, SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, max, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) #define SDTG_MMANY(name, type, flags, var, def, full, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(ManyOfMany, SLEG_GENERAL(SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) + NSD(ManyOfMany, SLEG_GENERAL(#var, SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) #define SDTG_NULL(length, from, to)\ NSD(Null, SLEG_NULL(length, from, to)) |