Saveload: Table format sub-struct support

This commit is contained in:
Jonathan G Rennison 2024-07-14 21:50:52 +01:00
parent 99c6cc5bdb
commit 11eebdc5dd
14 changed files with 340 additions and 83 deletions

View File

@ -18,7 +18,7 @@ static const NamedSaveLoad _long_bridge_signal_storage_desc[] = {
static void Load_XBSS()
{
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(_long_bridge_signal_storage_desc);
SaveLoadTableData slt = SlTableHeaderOrRiff(_long_bridge_signal_storage_desc);
int index;
while ((index = SlIterateArray()) != -1) {
@ -29,7 +29,7 @@ static void Load_XBSS()
static void Save_XBSS()
{
std::vector<SaveLoad> slt = SlTableHeader(_long_bridge_signal_storage_desc);
SaveLoadTableData slt = SlTableHeader(_long_bridge_signal_storage_desc);
for (auto &it : _long_bridge_signal_sim_map) {
LongBridgeSignalStorage &lbss = it.second;

View File

@ -181,7 +181,7 @@ NamedSaveLoadTable GetCargoPacketDesc()
*/
static void Save_CAPA()
{
std::vector<SaveLoad> slt = SlTableHeader(GetCargoPacketDesc());
SaveLoadTableData slt = SlTableHeader(GetCargoPacketDesc());
for (CargoPacket *cp : CargoPacket::Iterate()) {
SlSetArrayIndex(cp->index);
@ -194,7 +194,7 @@ static void Save_CAPA()
*/
static void Load_CAPA()
{
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(GetCargoPacketDesc());
SaveLoadTableData slt = SlTableHeaderOrRiff(GetCargoPacketDesc());
int index;
while ((index = SlIterateArray()) != -1) {

View File

@ -80,7 +80,7 @@ std::vector<NamedSaveLoad> GetCheatsDesc(bool save) {
*/
static void Save_CHTS()
{
std::vector<SaveLoad> slt = SlTableHeader(GetCheatsDesc(true));
SaveLoadTableData slt = SlTableHeader(GetCheatsDesc(true));
SlSetArrayIndex(0);
SlObjectSaveFiltered(&_cheats, slt);
@ -105,7 +105,7 @@ static void Load_CHTS()
};
UnknownCheatHandler uch{};
std::vector<SaveLoad> slt = SlTableHeader(GetCheatsDesc(false), &uch);
SaveLoadTableData slt = SlTableHeader(GetCheatsDesc(false), &uch);
if (SlIterateArray() == -1) return;
SlObjectLoadFiltered(&_cheats, slt);
@ -114,7 +114,7 @@ static void Load_CHTS()
}
} else {
size_t count = SlGetFieldLength();
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(GetCheatsDesc(false));
SaveLoadTableData slt = SlTableHeaderOrRiff(GetCheatsDesc(false));
/* Cheats were added over the years without a savegame bump. They are
* stored as 2 SLE_BOOLs per entry. "count" indicates how many SLE_BOOLs

View File

@ -85,7 +85,7 @@ static void Load_DBGD()
NSLT("config", SLEG_SSTR(_loadgame_DBGC_data, SLE_STR | SLF_ALLOW_CONTROL | SLF_ALLOW_NEWLINE)),
NSLT("log", SLEG_SSTR(_loadgame_DBGL_data, SLE_STR | SLF_ALLOW_CONTROL | SLF_ALLOW_NEWLINE)),
};
SlLoadTableOrRiffFiltered(nsl);
SlLoadTableObjectChunk(nsl);
}
static void Check_DBGD()
@ -99,7 +99,7 @@ static void Check_DBGD()
NSLT("config", SLEG_SSTR(_load_check_data.debug_config_data, SLE_STR | SLF_ALLOW_CONTROL | SLF_ALLOW_NEWLINE)),
NSLT("log", SLEG_SSTR(_load_check_data.debug_log_data, SLE_STR | SLF_ALLOW_CONTROL | SLF_ALLOW_NEWLINE)),
};
SlLoadTableOrRiffFiltered(nsl);
SlLoadTableObjectChunk(nsl);
}
extern const ChunkHandler debug_chunk_handlers[] = {

View File

@ -167,7 +167,7 @@ static const NamedSaveLoad _industrytype_builder_desc[] = {
/** Save industry-type build data. */
static void Save_ITBL()
{
std::vector<SaveLoad> sld = SlTableHeader(_industrytype_builder_desc);
SaveLoadTableData sld = SlTableHeader(_industrytype_builder_desc);
for (int i = 0; i < NUM_INDUSTRYTYPES; i++) {
SlSetArrayIndex(i);
@ -178,7 +178,7 @@ static void Save_ITBL()
/** Load industry-type build data. */
static void Load_ITBL()
{
std::vector<SaveLoad> sld = SlTableHeaderOrRiff(_industrytype_builder_desc);
SaveLoadTableData sld = SlTableHeaderOrRiff(_industrytype_builder_desc);
for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
_industry_builder.builddata[it].Reset();

View File

@ -36,7 +36,7 @@ static const NamedSaveLoad _newgrf_mapping_desc_new[] = {
*/
void Save_NewGRFMapping(const OverrideManagerBase &mapping)
{
std::vector<SaveLoad> sld = SlTableHeader(_newgrf_mapping_desc_new);
SaveLoadTableData sld = SlTableHeader(_newgrf_mapping_desc_new);
for (uint i = 0; i < mapping.GetMaxMapping(); i++) {
if (mapping.mappings[i].grfid == 0 &&
@ -60,7 +60,7 @@ void Load_NewGRFMapping(OverrideManagerBase &mapping)
uint max_id = mapping.GetMaxMapping();
SaveLoadTable slt;
std::vector<SaveLoad> sld;
SaveLoadTableData sld;
if (SlXvIsFeaturePresent(XSLFI_NEWGRF_ENTITY_EXTRA) || SlIsTableChunk()) {
sld = SlTableHeaderOrRiff(_newgrf_mapping_desc_new);
@ -91,7 +91,7 @@ static const NamedSaveLoad _grfconfig_desc[] = {
static void Save_NGRF()
{
std::vector<SaveLoad> sld = SlTableHeader(_grfconfig_desc);
SaveLoadTableData sld = SlTableHeader(_grfconfig_desc);
int index = 0;
for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
@ -108,7 +108,7 @@ static void Load_NGRF_common(GRFConfig *&grfconfig)
if (SlXvIsFeaturePresent(XSLFI_TABLE_NEWGRF_SL, 1, 1)) {
SlLoadTableWithArrayLengthPrefixesMissing();
}
std::vector<SaveLoad> sld = SlTableHeaderOrRiff(_grfconfig_desc);
SaveLoadTableData sld = SlTableHeaderOrRiff(_grfconfig_desc);
ClearGRFConfigList(&grfconfig);
while (SlIterateArray() != -1) {
GRFConfig *c = new GRFConfig();

View File

@ -21,7 +21,7 @@ static const NamedSaveLoad _new_signal_style_mapping_desc[] = {
static void Save_NSID()
{
std::vector<SaveLoad> slt = SlTableHeader(_new_signal_style_mapping_desc);
SaveLoadTableData slt = SlTableHeader(_new_signal_style_mapping_desc);
int index = 0;
for (NewSignalStyleMapping &it : _new_signal_style_mapping) {
@ -35,7 +35,7 @@ static void Load_NSID()
_new_signal_style_mapping.fill({});
if (SlIsTableChunk()) {
std::vector<SaveLoad> slt = SlTableHeader(_new_signal_style_mapping_desc);
SaveLoadTableData slt = SlTableHeader(_new_signal_style_mapping_desc);
int index;
while ((index = SlIterateArray()) != -1) {

View File

@ -230,6 +230,19 @@ size_t MemoryDumper::GetSize() const
return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0);
}
/**
* Get the size of the memory dump made so far.
* @return The size.
*/
size_t MemoryDumper::GetWriteOffsetGeneric() const
{
if (this->saved_buf != nullptr) {
return this->buf - this->autolen_buf;
} else {
return this->GetSize();
}
}
enum SaveLoadBlockFlags {
SLBF_TABLE_ARRAY_LENGTH_PREFIX_MISSING, ///< Table chunk arrays were incorrectly saved without the length prefix, skip reading the length prefix on load
};
@ -1886,6 +1899,11 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
case SL_WRITEBYTE: return 1; // a uint8_t is logically of size 1
case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END));
case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription());
case SL_STRUCT:
case SL_STRUCTLIST:
NOT_REACHED(); // SlAutolength or similar should be used for sub-structs
default: NOT_REACHED();
}
return 0;
@ -1906,6 +1924,8 @@ static void SlFilterObjectMember(const SaveLoad &sld, std::vector<SaveLoad> &sav
case SL_RING:
case SL_STDSTR:
case SL_VARVEC:
case SL_STRUCT:
case SL_STRUCTLIST:
/* CONDITIONAL saveload types depend on the savegame version */
if (!SlIsObjectValidInSavegame(sld)) return;
@ -1921,6 +1941,8 @@ static void SlFilterObjectMember(const SaveLoad &sld, std::vector<SaveLoad> &sav
case SL_REFLIST:
case SL_PTRRING:
case SL_VEC:
case SL_STRUCT:
case SL_STRUCTLIST:
break;
/* non-ptr types do not require SLA_PTRS or SLA_NULL actions */
@ -2032,6 +2054,51 @@ bool SlObjectMemberGeneric(void *object, const SaveLoad &sld)
}
break;
case SL_STRUCT:
case SL_STRUCTLIST:
switch (action) {
case SLA_SAVE: {
if (sld.cmd == SL_STRUCT) {
/* Number of structs written in the savegame: write a value of 1, change to zero later if nothing after this was written */
_sl.dumper->WriteByte(1);
size_t offset = _sl.dumper->GetWriteOffsetGeneric();
sld.struct_handler->Save(object);
if (offset == _sl.dumper->GetWriteOffsetGeneric()) {
/* Nothing was actaully written, so it's safe to change the 1 above to 0 */
_sl.dumper->UnWriteByte(); // This is fine iff nothing has been written since the WriteByte(1)
_sl.dumper->RawWriteByte(0);
}
} else {
sld.struct_handler->Save(object);
}
break;
}
case SLA_LOAD_CHECK: {
if (sld.cmd == SL_STRUCT && SlIsTableChunk()) {
if (SlGetStructListLength(1) == 0) break;
}
sld.struct_handler->LoadCheck(object);
break;
}
case SLA_LOAD: {
if (sld.cmd == SL_STRUCT && SlIsTableChunk()) {
if (SlGetStructListLength(1) == 0) break;
}
sld.struct_handler->Load(object);
break;
}
case SLA_PTRS:
sld.struct_handler->FixPointers(object);
break;
case SLA_NULL: break;
default: NOT_REACHED();
}
break;
/* SL_WRITEBYTE writes a value to the savegame to identify the type of an object.
* When loading, the value is read explicitly with SlReadByte() to determine which
* object description to use. */
@ -2141,34 +2208,18 @@ bool SlIsTableChunk()
void SlSkipTableHeader()
{
uint sub_tables = 0;
while (true) {
uint8_t type = SlReadByte();
if (type == SLE_FILE_END) break;
if ((type & SLE_FILE_TYPE_MASK) == SLE_FILE_STRUCT) sub_tables++;
SlString(nullptr, 0, SLE_FILE_STRING | SLE_VAR_NULL);
}
}
/**
* 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 NamedSaveLoadTable &slt)
{
size_t length = 0;
for (auto &nsld : slt) {
if (StrEmpty(nsld.name) || !SlIsObjectValidInSavegame(nsld.save_load)) continue;
length += 1 + SlCalcStringLen(&nsld.name, 0, SLE_STR);
for (uint i = 0; i < sub_tables; i++) {
SlSkipTableHeader();
}
length++; // End-of-list entry.
/* SL_STRUCTLIST, SL_STRUCT not currently implemented */
return length;
}
/**
@ -2198,22 +2249,55 @@ static uint8_t GetSavegameTableFileType(const SaveLoad &sld)
case SL_WRITEBYTE:
return SLE_FILE_U8;
case SL_STRUCT:
case SL_STRUCTLIST:
return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD;
default: NOT_REACHED();
}
}
/**
* 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 SaveLoadSkipStructHandler : public SaveLoadStructHandler {
void Save(void *) const override
{
NOT_REACHED();
}
void Load(void *object) const override
{
size_t length = SlGetStructListLength(UINT32_MAX);
for (; length > 0; length--) {
SlObjectLoadFiltered(object, this->GetLoadDescription());
}
}
void LoadCheck(void *object) const override
{
this->Load(object);
}
NamedSaveLoadTable GetDescription() const override
{
return {};
}
};
/**
* Save or Load a table header.
* @note a table-header can never contain more than 65535 fields.
* @param slt The NamedSaveLoad table with objects to save/load.
* @return The ordered SaveLoad array to use.
*/
std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSpecialHandler *special_handler)
SaveLoadTableData SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSpecialHandler *special_handler)
{
/* You can only use SlTableHeader if you are a CH_TABLE. */
assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
std::vector<SaveLoad> saveloads;
SaveLoadTableData saveloads;
switch (_sl.action) {
case SLA_LOAD_CHECK:
@ -2261,15 +2345,20 @@ std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp
DEBUG(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '%s' of type 0x%02X not found, skipping", key.c_str(), type);
SaveLoadType saveload_type;
SaveLoadStructHandler *struct_handler = nullptr;
switch (type & SLE_FILE_TYPE_MASK) {
case SLE_FILE_STRING:
/* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */
saveload_type = SL_STDSTR;
break;
case SLE_FILE_STRUCT:
SlErrorCorrupt("SLE_FILE_STRUCT not supported yet");
case SLE_FILE_STRUCT: {
saveload_type = SL_STRUCTLIST;
auto handler = std::make_unique<SaveLoadSkipStructHandler>();
struct_handler = handler.get();
saveloads.struct_handlers.push_back(std::move(handler));
break;
}
default:
saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR;
@ -2277,7 +2366,7 @@ std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp
}
/* We don't know this field, so read to nothing. */
saveloads.push_back({ true, saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, SLTAG_TABLE_UNKNOWN, nullptr, SlXvFeatureTest() });
saveloads.push_back({ true, saveload_type, ((VarType)type & SLE_FILE_TYPE_MASK) | SLE_VAR_NULL, 1, SL_MIN_VERSION, SL_MAX_VERSION, SLTAG_TABLE_UNKNOWN, nullptr, SlXvFeatureTest(), struct_handler });
continue;
}
@ -2292,17 +2381,28 @@ std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp
SlErrorCorrupt("Field type is different than expected");
}
saveloads.push_back(*sld_it->save_load);
if ((type & SLE_FILE_TYPE_MASK) == SLE_FILE_STRUCT) {
std::unique_ptr<SaveLoadStructHandler> handler = saveloads.back().struct_handler_factory();
saveloads.back().struct_handler = handler.get();
saveloads.struct_handlers.push_back(std::move(handler));
}
}
/* SL_STRUCTLIST, SL_STRUCT not currently implemented */
for (auto &sld : saveloads) {
if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
sld.struct_handler->table_data = SlTableHeader(sld.struct_handler->GetDescription());
}
}
break;
}
case SLA_SAVE: {
/* Automatically calculate the length? */
if (_sl.need_length != NL_NONE) {
SlSetLength(SlCalcTableHeader(slt));
const NeedLength orig_need_length = _sl.need_length;
if (orig_need_length != NL_NONE) {
_sl.need_length = NL_NONE;
_sl.dumper->StartAutoLength();
}
for (auto &nsld : slt) {
@ -2319,7 +2419,21 @@ std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp
/* Add an end-of-header marker. */
SlWriteByte(SLE_FILE_END);
/* SL_STRUCTLIST, SL_STRUCT not currently implemented */
for (auto &sld : saveloads) {
if (sld.cmd == SL_STRUCTLIST || sld.cmd == SL_STRUCT) {
std::unique_ptr<SaveLoadStructHandler> handler = sld.struct_handler_factory();
sld.struct_handler = handler.get();
sld.struct_handler->table_data = SlTableHeader(sld.struct_handler->GetDescription());
saveloads.struct_handlers.push_back(std::move(handler));
}
}
if (orig_need_length != NL_NONE) {
auto result = _sl.dumper->StopAutoLength();
_sl.need_length = orig_need_length;
SlSetLength(result.size());
_sl.dumper->CopyBytes(result);
}
break;
}
@ -2330,11 +2444,11 @@ std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp
return saveloads;
}
std::vector<SaveLoad> SlTableHeaderOrRiff(const NamedSaveLoadTable &slt)
SaveLoadTableData SlTableHeaderOrRiff(const NamedSaveLoadTable &slt)
{
if (SlIsTableChunk()) return SlTableHeader(slt);
std::vector<SaveLoad> saveloads;
SaveLoadTableData saveloads;
for (auto &nsld : slt) {
if ((nsld.nsl_flags & NSLF_TABLE_ONLY) != 0) continue;
SlFilterObjectMember(nsld.save_load, saveloads);
@ -2363,6 +2477,28 @@ void SlLoadTableWithArrayLengthPrefixesMissing()
SetBit(_sl.block_flags, SLBF_TABLE_ARRAY_LENGTH_PREFIX_MISSING);
}
/**
* Set the length of this list.
* @param The length of the list.
*/
void SlSetStructListLength(size_t length)
{
SlWriteArrayLength(length);
}
/**
* Get the length of this list; if it exceeds the limit, error out.
* @param limit The maximum size the list can be.
* @return The length of the list.
*/
size_t SlGetStructListLength(size_t limit)
{
size_t length = SlReadArrayLength();
if (length > limit) SlErrorCorrupt("List exceeds storage size");
return length;
}
void SlSkipChunkContents()
{
if (SlIsTableChunk()) SlSkipTableHeader();

View File

@ -1092,12 +1092,15 @@ struct TableHeaderSpecialHandler {
bool SlIsTableChunk();
void SlSkipTableHeader();
std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSpecialHandler *special_handler = nullptr);
std::vector<SaveLoad> SlTableHeaderOrRiff(const NamedSaveLoadTable &slt);
SaveLoadTableData SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSpecialHandler *special_handler = nullptr);
SaveLoadTableData SlTableHeaderOrRiff(const NamedSaveLoadTable &slt);
void SlSaveTableObjectChunk(const SaveLoadTable &slt);
void SlLoadTableOrRiffFiltered(const SaveLoadTable &slt);
void SlLoadTableWithArrayLengthPrefixesMissing();
void SlSetStructListLength(size_t length);
size_t SlGetStructListLength(size_t limit);
void SlSkipChunkContents();
inline void SlSaveTableObjectChunk(const NamedSaveLoadTable &slt)
@ -1105,6 +1108,16 @@ inline void SlSaveTableObjectChunk(const NamedSaveLoadTable &slt)
SlSaveTableObjectChunk(SlTableHeader(slt));
}
inline void SlLoadTableObjectChunk(const NamedSaveLoadTable &slt)
{
SlLoadTableOrRiffFiltered(SlTableHeader(slt));
}
inline void SlLoadTableObjectChunk(const SaveLoadTable &slt)
{
SlLoadTableOrRiffFiltered(slt);
}
inline void SlLoadTableOrRiffFiltered(const NamedSaveLoadTable &slt)
{
SlLoadTableOrRiffFiltered(SlTableHeaderOrRiff(slt));

View File

@ -256,6 +256,12 @@ struct MemoryDumper {
this->CopyBytes(buffer.data(), buffer.size());
}
/** For limited/special purposes only */
inline void UnWriteByte()
{
this->buf--;
}
inline void RawWriteByte(uint8_t b)
{
*this->buf++ = b;
@ -331,6 +337,7 @@ struct MemoryDumper {
void Flush(SaveFilter &writer);
size_t GetSize() const;
size_t GetWriteOffsetGeneric() const;
void StartAutoLength();
std::span<uint8_t> StopAutoLength();
bool IsAutoLengthActive() const { return this->saved_buf != nullptr; }

View File

@ -103,26 +103,30 @@ typedef uint32_t VarType;
/** Type of data saved. */
enum SaveLoadTypes {
SL_VAR = 0, ///< Save/load a variable.
SL_REF = 1, ///< Save/load a reference.
SL_ARR = 2, ///< Save/load a fixed-size array of #SL_VAR elements.
SL_STR = 3, ///< Save/load a string.
SL_REFLIST = 4, ///< Save/load a list of #SL_REF elements.
SL_RING = 5, ///< Save/load a ring of #SL_VAR elements.
SL_VEC = 6, ///< Save/load a vector of #SL_REF elements.
SL_STDSTR = 7, ///< Save/load a std::string.
SL_VAR = 0, ///< Save/load a variable.
SL_REF, ///< Save/load a reference.
SL_ARR, ///< Save/load a fixed-size array of #SL_VAR elements.
SL_STR, ///< Save/load a string.
SL_REFLIST, ///< Save/load a list of #SL_REF elements.
SL_RING, ///< Save/load a ring of #SL_VAR elements.
SL_VEC, ///< Save/load a vector of #SL_REF elements.
SL_STDSTR, ///< Save/load a std::string.
SL_PTRRING, ///< Save/load a ring of #SL_REF elements.
SL_VARVEC, ///< Save/load a primitive type vector.
SL_STRUCT, ///< Save/load a struct.
SL_STRUCTLIST, ///< Save/load a list of structs.
/* non-normal save-load types */
SL_WRITEBYTE = 8,
SL_VEH_INCLUDE = 9,
SL_ST_INCLUDE = 10,
SL_PTRRING = 13, ///< Save/load a ring of #SL_REF elements.
SL_VARVEC = 14, ///< Save/load a primitive type vector.
SL_WRITEBYTE,
SL_VEH_INCLUDE,
SL_ST_INCLUDE,
};
typedef uint8_t SaveLoadType; ///< Save/load type. @see SaveLoadTypes
using SaveLoadStructHandlerFactory = std::unique_ptr<struct SaveLoadStructHandler> (*)();
/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */
struct SaveLoad {
bool global; ///< should we load a global variable or a non-global one
@ -132,12 +136,18 @@ struct SaveLoad {
SaveLoadVersion version_from; ///< save/load the variable starting from this savegame version
SaveLoadVersion version_to; ///< save/load the variable until this savegame version
uint16_t label_tag; ///< for labelling purposes
/* NOTE: This element either denotes the address of the variable for a global
* variable, or the offset within a struct which is then bound to a variable
* during runtime. Decision on which one to use is controlled by the function
* that is called to save it. address: global=true, offset: global=false */
void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536)
union {
/* NOTE: This element either denotes the address of the variable for a global
* variable, or the offset within a struct which is then bound to a variable
* during runtime. Decision on which one to use is controlled by the function
* that is called to save it. address: global=true, offset: global=false */
void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536)
SaveLoadStructHandlerFactory struct_handler_factory; ///< factory function pointer for SaveLoadStructHandler
};
SlXvFeatureTest ext_feature_test; ///< extended feature test
SaveLoadStructHandler *struct_handler = nullptr;
};
inline constexpr SaveLoad SLTAG(uint16_t label_tag, SaveLoad save_load)
@ -175,4 +185,95 @@ inline constexpr NamedSaveLoad NSLT(const char *name, SaveLoad save_load)
return { name, save_load, NSLF_TABLE_ONLY };
}
inline constexpr NamedSaveLoad NSLT_STRUCT(const char *name, SaveLoadStructHandlerFactory factory, SaveLoadVersion from = SL_MIN_VERSION, SaveLoadVersion to = SL_MAX_VERSION, SlXvFeatureTest extver = {})
{
return { name, SaveLoad { true, SL_STRUCT, SLE_FILE_STRUCT, 0, from, to, SLTAG_DEFAULT, { .struct_handler_factory = factory }, extver }, NSLF_TABLE_ONLY };
}
template <typename T>
inline constexpr NamedSaveLoad NSLT_STRUCT(const char *name, SaveLoadVersion from = SL_MIN_VERSION, SaveLoadVersion to = SL_MAX_VERSION, SlXvFeatureTest extver = {})
{
SaveLoadStructHandlerFactory factory = []() -> std::unique_ptr<struct SaveLoadStructHandler> {
return std::make_unique<T>();
};
return NSLT_STRUCT(name, factory, from, to, extver);
}
inline constexpr NamedSaveLoad NSLT_STRUCTLIST(const char *name, SaveLoadStructHandlerFactory factory, SaveLoadVersion from = SL_MIN_VERSION, SaveLoadVersion to = SL_MAX_VERSION, SlXvFeatureTest extver = {})
{
return { name, SaveLoad { true, SL_STRUCTLIST, SLE_FILE_STRUCT, 0, from, to, SLTAG_DEFAULT, { .struct_handler_factory = factory }, extver }, NSLF_TABLE_ONLY };
}
template <typename T>
inline constexpr NamedSaveLoad NSLT_STRUCTLIST(const char *name, SaveLoadVersion from = SL_MIN_VERSION, SaveLoadVersion to = SL_MAX_VERSION, SlXvFeatureTest extver = {})
{
SaveLoadStructHandlerFactory factory = []() -> std::unique_ptr<struct SaveLoadStructHandler> {
return std::make_unique<T>();
};
return NSLT_STRUCTLIST(name, factory, from, to, extver);
}
struct SaveLoadTableData : public std::vector<SaveLoad> {
std::vector<std::unique_ptr<struct SaveLoadStructHandler>> struct_handlers;
};
/** Handler for saving/loading a SL_STRUCT/SL_STRUCTLIST. */
class SaveLoadStructHandler {
public:
SaveLoadTableData table_data;
virtual ~SaveLoadStructHandler() = default;
/**
* Get the (static) description of the fields in the savegame.
*/
virtual NamedSaveLoadTable GetDescription() const = 0;
/**
* Get the (current) description of the fields in the savegame.
*/
SaveLoadTable GetLoadDescription() const { return this->table_data; }
/**
* Save the object to disk.
* @param object The object to store.
*/
virtual void Save([[maybe_unused]] void *object) const {}
/**
* Load the object from disk.
* @param object The object to load.
*/
virtual void Load([[maybe_unused]] void *object) const {}
/**
* Similar to load, but used only to validate savegames.
* @param object The object to load.
*/
virtual void LoadCheck([[maybe_unused]] void *object) const {}
/**
* A post-load callback to fix #SL_REF integers into pointers.
* @param object The object to fix.
*/
virtual void FixPointers([[maybe_unused]] void *object) const {}
};
template <class TImpl, class TObject>
class TypedSaveLoadStructHandler : public SaveLoadStructHandler {
public:
void Save([[maybe_unused]] TObject *object) const {}
void Save(void *object) const override { static_cast<const TImpl *>(this)->Save(static_cast<TObject *>(object)); }
void Load([[maybe_unused]] TObject *object) const {}
void Load(void *object) const override { static_cast<const TImpl *>(this)->Load(static_cast<TObject *>(object)); }
void LoadCheck([[maybe_unused]] TObject *object) const {}
void LoadCheck(void *object) const override { static_cast<const TImpl *>(this)->LoadCheck(static_cast<TObject *>(object)); }
void FixPointers([[maybe_unused]] TObject *object) const {}
void FixPointers(void *object) const override { static_cast<const TImpl *>(this)->FixPointers(static_cast<TObject *>(object)); }
};
#endif /* SL_SAVELOAD_TYPES_H */

View File

@ -23,7 +23,7 @@ static const NamedSaveLoad _trace_restrict_mapping_desc[] = {
*/
static void Load_TRRM()
{
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(_trace_restrict_mapping_desc);
SaveLoadTableData slt = SlTableHeaderOrRiff(_trace_restrict_mapping_desc);
int index;
while ((index = SlIterateArray()) != -1) {
@ -37,7 +37,7 @@ static void Load_TRRM()
*/
static void Save_TRRM()
{
std::vector<SaveLoad> slt = SlTableHeader(_trace_restrict_mapping_desc);
SaveLoadTableData slt = SlTableHeader(_trace_restrict_mapping_desc);
for (TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.begin();
iter != _tracerestrictprogram_mapping.end(); ++iter) {
@ -55,7 +55,7 @@ static const NamedSaveLoad _trace_restrict_program_desc[] = {
*/
static void Load_TRRP()
{
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(_trace_restrict_program_desc);
SaveLoadTableData slt = SlTableHeaderOrRiff(_trace_restrict_program_desc);
int index;
while ((index = SlIterateArray()) != -1) {
@ -111,7 +111,7 @@ static void Load_TRRP()
*/
static void Save_TRRP()
{
std::vector<SaveLoad> slt = SlTableHeader(_trace_restrict_program_desc);
SaveLoadTableData slt = SlTableHeader(_trace_restrict_program_desc);
for (TraceRestrictProgram *prog : TraceRestrictProgram::Iterate()) {
SlSetArrayIndex(prog->index);
@ -132,7 +132,7 @@ static const NamedSaveLoad _trace_restrict_slot_desc[] = {
*/
static void Load_TRRS()
{
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(_trace_restrict_slot_desc);
SaveLoadTableData slt = SlTableHeaderOrRiff(_trace_restrict_slot_desc);
int index;
while ((index = SlIterateArray()) != -1) {
@ -147,7 +147,7 @@ static void Load_TRRS()
*/
static void Save_TRRS()
{
std::vector<SaveLoad> slt = SlTableHeader(_trace_restrict_slot_desc);
SaveLoadTableData slt = SlTableHeader(_trace_restrict_slot_desc);
for (TraceRestrictSlot *slot : TraceRestrictSlot::Iterate()) {
SlSetArrayIndex(slot->index);
@ -166,7 +166,7 @@ static const NamedSaveLoad _trace_restrict_counter_desc[] = {
*/
static void Load_TRRC()
{
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(_trace_restrict_counter_desc);
SaveLoadTableData slt = SlTableHeaderOrRiff(_trace_restrict_counter_desc);
int index;
while ((index = SlIterateArray()) != -1) {
@ -180,7 +180,7 @@ static void Load_TRRC()
*/
static void Save_TRRC()
{
std::vector<SaveLoad> slt = SlTableHeader(_trace_restrict_counter_desc);
SaveLoadTableData slt = SlTableHeader(_trace_restrict_counter_desc);
for (TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) {
SlSetArrayIndex(ctr->index);

View File

@ -25,7 +25,7 @@ static const NamedSaveLoad _train_speed_adaptation_map_desc[] = {
static void Load_TSAS()
{
const bool table_mode = SlIsTableChunk();
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(_train_speed_adaptation_map_desc);
SaveLoadTableData slt = SlTableHeaderOrRiff(_train_speed_adaptation_map_desc);
int index;
SignalSpeedType data;
@ -40,7 +40,7 @@ static void Load_TSAS()
static void Save_TSAS()
{
std::vector<SaveLoad> slt = SlTableHeader(_train_speed_adaptation_map_desc);
SaveLoadTableData slt = SlTableHeader(_train_speed_adaptation_map_desc);
int index = 0;
for (auto &it : _signal_speeds) {

View File

@ -25,7 +25,7 @@ static const NamedSaveLoad _tunnel_desc[] = {
static void Save_TUNN()
{
std::vector<SaveLoad> slt = SlTableHeader(_tunnel_desc);
SaveLoadTableData slt = SlTableHeader(_tunnel_desc);
for (Tunnel *tunnel : Tunnel::Iterate()) {
SlSetArrayIndex(tunnel->index);
@ -35,7 +35,7 @@ static void Save_TUNN()
static void Load_TUNN()
{
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(_tunnel_desc);
SaveLoadTableData slt = SlTableHeaderOrRiff(_tunnel_desc);
int index;
while ((index = SlIterateArray()) != -1) {