summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatric Stout <truebrain@openttd.org>2021-06-14 10:05:30 +0200
committerPatric Stout <github@truebrain.nl>2021-07-02 22:21:58 +0200
commit7dd5fd6ed497e1da40c13075d6e37b54ab12a082 (patch)
tree19f2e49e0e9ad714cec2fcf917dca954d33d8c0a
parent513641f9baaa732ab8000bc452a26284ae601f32 (diff)
downloadopenttd-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.
-rw-r--r--src/core/span_type.hpp2
-rw-r--r--src/saveload/ai_sl.cpp8
-rw-r--r--src/saveload/animated_tile_sl.cpp2
-rw-r--r--src/saveload/cheat_sl.cpp1
-rw-r--r--src/saveload/company_sl.cpp12
-rw-r--r--src/saveload/depot_sl.cpp2
-rw-r--r--src/saveload/game_sl.cpp14
-rw-r--r--src/saveload/gamelog_sl.cpp26
-rw-r--r--src/saveload/industry_sl.cpp4
-rw-r--r--src/saveload/linkgraph_sl.cpp8
-rw-r--r--src/saveload/map_sl.cpp4
-rw-r--r--src/saveload/misc_sl.cpp44
-rw-r--r--src/saveload/saveload.cpp421
-rw-r--r--src/saveload/saveload.h154
-rw-r--r--src/saveload/station_sl.cpp42
-rw-r--r--src/saveload/town_sl.cpp6
-rw-r--r--src/saveload/vehicle_sl.cpp40
-rw-r--r--src/script/script_instance.cpp2
-rw-r--r--src/settings.cpp2
-rw-r--r--src/table/settings.h.preamble12
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))