Changeset - r25751:3154638283de
[Not reviewed]
master
0 20 0
Patric Stout - 3 years ago 2021-06-14 08:05:30
truebrain@openttd.org
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.
20 files changed with 627 insertions and 195 deletions:
0 comments (0 inline, 0 general)
src/core/span_type.hpp
Show inline comments
 
@@ -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>
src/saveload/ai_sl.cpp
Show inline comments
 
@@ -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)
src/saveload/animated_tile_sl.cpp
Show inline comments
 
@@ -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),
 
};
 

	
 
/**
src/saveload/cheat_sl.cpp
Show inline comments
 
@@ -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 },
 
};
src/saveload/company_sl.cpp
Show inline comments
 
@@ -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()
src/saveload/depot_sl.cpp
Show inline comments
 
@@ -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),
src/saveload/game_sl.cpp
Show inline comments
 
@@ -26,10 +26,10 @@ static std::string _game_saveload_settin
 
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()
src/saveload/gamelog_sl.cpp
Show inline comments
 
@@ -204,7 +204,7 @@ class SlGamelogEmergency : public Defaul
 
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 DefaultSa
 
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)
src/saveload/industry_sl.cpp
Show inline comments
 
@@ -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. */
src/saveload/linkgraph_sl.cpp
Show inline comments
 
@@ -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
src/saveload/map_sl.cpp
Show inline comments
 
@@ -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()
src/saveload/misc_sl.cpp
Show inline comments
 
@@ -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()
src/saveload/saveload.cpp
Show inline comments
 
@@ -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
 
}
 

	
 
/**
 
 * 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(VarT
 
 */
 
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 
 
		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 SlIsObjectValidInSave
 
}
 

	
 
/**
 
 * 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 SaveLo
 
}
 

	
 
/**
 
 * 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 ChunkHandl
 
	size_t len;
 
	size_t endoffs;
 

	
 
	_sl.block_mode = m;
 
	_sl.block_mode = m & CH_TYPE_MASK;
 
	_sl.obj_len = 0;
 

	
 
	switch (m) {
 
	_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 (_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 Chunk
 
	size_t len;
 
	size_t endoffs;
 

	
 
	_sl.block_mode = m;
 
	_sl.block_mode = m & CH_TYPE_MASK;
 
	_sl.obj_len = 0;
 

	
 
	switch (m) {
 
	_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 (_sl.block_mode) {
 
		case CH_TABLE:
 
		case CH_ARRAY:
 
			_sl.array_index = 0;
 
			if (ch.load_check_proc) {
 
				ch.load_check_proc();
 
			} else {
 
				SlSkipArray();
 
			}
 
			break;
 
		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_SPARSE_TABLE:
 
		case CH_SPARSE_ARRAY:
 
			if (ch.load_check_proc != nullptr) {
 
				ch.load_check_proc();
 
			} else {
 
				SlErrorCorrupt("Invalid chunk type");
 
				SlSkipArray();
 
			}
 
			break;
 
		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 {
 
				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 ChunkHandl
 
	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
 
{
 
	strecpy(this->title, title, lastof(this->title));
 
}
 

	
 
SaveLoadTable SaveLoadHandler::GetLoadDescription() const
 
{
 
	assert(this->load_description.has_value());
 
	return *this->load_description;
 
}
src/saveload/saveload.h
Show inline comments
 
@@ -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 Chu
 
/** 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 *bas
 

	
 
/** 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);
src/saveload/station_sl.cpp
Show inline comments
 
@@ -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()
src/saveload/town_sl.cpp
Show inline comments
 
@@ -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()
src/saveload/vehicle_sl.cpp
Show inline comments
 
@@ -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. */
src/script/script_instance.cpp
Show inline comments
 
@@ -345,7 +345,7 @@ static byte _script_sl_byte; ///< Used a
 

	
 
/** 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)
src/settings.cpp
Show inline comments
 
@@ -2194,7 +2194,7 @@ static std::vector<SaveLoad> GetSettings
 

	
 
		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;
 
		}
 

	
src/table/settings.h.preamble
Show inline comments
 
@@ -59,22 +59,22 @@ static size_t ConvertLandscape(const cha
 
/* 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))
0 comments (0 inline, 0 general)