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.
This commit is contained in:
Patric Stout 2021-06-14 10:05:30 +02:00 committed by Patric Stout
parent 513641f9ba
commit 7dd5fd6ed4
20 changed files with 620 additions and 188 deletions

View File

@ -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>

View File

@ -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)

View File

@ -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),
};
/**

View File

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

View File

@ -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()

View File

@ -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),

View File

@ -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()

View File

@ -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)

View File

@ -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. */

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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.
@ -579,6 +580,39 @@ static inline uint SlGetArrayLength(size_t length)
return SlGetGammaLength(length);
}
/**
* Return the type as saved/loaded inside the savegame.
*/
static uint8 GetSavegameFileType(const SaveLoad &sld)
{
switch (sld.cmd) {
case SL_VAR:
return GetVarFileType(sld.conv); break;
case SL_STR:
case SL_STDSTR:
case SL_ARR:
case SL_VECTOR:
case SL_DEQUE:
return GetVarFileType(sld.conv) | SLE_FILE_HAS_LENGTH_FIELD; break;
case SL_REF:
return IsSavegameVersionBefore(SLV_69) ? SLE_FILE_U16 : SLE_FILE_U32;
case SL_REFLIST:
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
@ -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);
@ -1501,6 +1559,34 @@ static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
}
/**
* Calculate the size of the table header.
* @param slt The SaveLoad table with objects to save/load.
* @return size of given object.
*/
static size_t SlCalcTableHeader(const SaveLoadTable &slt)
{
size_t length = 0;
for (auto &sld : slt) {
if (!SlIsObjectValidInSavegame(sld)) continue;
length += SlCalcConvFileLen(SLE_UINT8);
length += SlCalcStdStringLen(&sld.name);
}
length += SlCalcConvFileLen(SLE_UINT8); // End-of-list entry.
for (auto &sld : slt) {
if (!SlIsObjectValidInSavegame(sld)) continue;
if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
length += SlCalcTableHeader(sld.handler->GetDescription());
}
}
return length;
}
/**
* Calculate the size of an object.
* @param object to be measured.
@ -1764,6 +1850,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);
switch (m) {
/* 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 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);
switch (m) {
/* 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) {
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;
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:
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");
} else {
SlErrorCorrupt("Invalid chunk type");
}
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;
}

View File

@ -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);

View File

@ -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()

View File

@ -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()

View File

@ -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. */

View File

@ -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)

View File

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

View File

@ -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))