From 11eebdc5dd3f92f2fbb0aecaf7b1c7619d7366e9 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 14 Jul 2024 21:50:52 +0100 Subject: [PATCH] Saveload: Table format sub-struct support --- src/sl/bridge_signal_sl.cpp | 4 +- src/sl/cargopacket_sl.cpp | 4 +- src/sl/cheat_sl.cpp | 6 +- src/sl/debug_sl.cpp | 4 +- src/sl/industry_sl.cpp | 4 +- src/sl/newgrf_sl.cpp | 8 +- src/sl/newsignals_sl.cpp | 4 +- src/sl/saveload.cpp | 202 +++++++++++++++++++++++++----- src/sl/saveload.h | 17 ++- src/sl/saveload_buffer.h | 7 ++ src/sl/saveload_types.h | 139 +++++++++++++++++--- src/sl/tracerestrict_sl.cpp | 16 +-- src/sl/train_speed_adaptation.cpp | 4 +- src/sl/tunnel_sl.cpp | 4 +- 14 files changed, 340 insertions(+), 83 deletions(-) diff --git a/src/sl/bridge_signal_sl.cpp b/src/sl/bridge_signal_sl.cpp index 0a06688fb7..fab8e93da4 100644 --- a/src/sl/bridge_signal_sl.cpp +++ b/src/sl/bridge_signal_sl.cpp @@ -18,7 +18,7 @@ static const NamedSaveLoad _long_bridge_signal_storage_desc[] = { static void Load_XBSS() { - std::vector 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 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; diff --git a/src/sl/cargopacket_sl.cpp b/src/sl/cargopacket_sl.cpp index 542ba1e897..c2fc2e6636 100644 --- a/src/sl/cargopacket_sl.cpp +++ b/src/sl/cargopacket_sl.cpp @@ -181,7 +181,7 @@ NamedSaveLoadTable GetCargoPacketDesc() */ static void Save_CAPA() { - std::vector 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 slt = SlTableHeaderOrRiff(GetCargoPacketDesc()); + SaveLoadTableData slt = SlTableHeaderOrRiff(GetCargoPacketDesc()); int index; while ((index = SlIterateArray()) != -1) { diff --git a/src/sl/cheat_sl.cpp b/src/sl/cheat_sl.cpp index 1aa78bb343..3b5df34315 100644 --- a/src/sl/cheat_sl.cpp +++ b/src/sl/cheat_sl.cpp @@ -80,7 +80,7 @@ std::vector GetCheatsDesc(bool save) { */ static void Save_CHTS() { - std::vector 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 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 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 diff --git a/src/sl/debug_sl.cpp b/src/sl/debug_sl.cpp index 75e26bdf55..45179e8e26 100644 --- a/src/sl/debug_sl.cpp +++ b/src/sl/debug_sl.cpp @@ -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[] = { diff --git a/src/sl/industry_sl.cpp b/src/sl/industry_sl.cpp index ada384acee..a64e01a1ee 100644 --- a/src/sl/industry_sl.cpp +++ b/src/sl/industry_sl.cpp @@ -167,7 +167,7 @@ static const NamedSaveLoad _industrytype_builder_desc[] = { /** Save industry-type build data. */ static void Save_ITBL() { - std::vector 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 sld = SlTableHeaderOrRiff(_industrytype_builder_desc); + SaveLoadTableData sld = SlTableHeaderOrRiff(_industrytype_builder_desc); for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) { _industry_builder.builddata[it].Reset(); diff --git a/src/sl/newgrf_sl.cpp b/src/sl/newgrf_sl.cpp index 97dd6fb857..7c6cd96ba5 100644 --- a/src/sl/newgrf_sl.cpp +++ b/src/sl/newgrf_sl.cpp @@ -36,7 +36,7 @@ static const NamedSaveLoad _newgrf_mapping_desc_new[] = { */ void Save_NewGRFMapping(const OverrideManagerBase &mapping) { - std::vector 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 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 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 sld = SlTableHeaderOrRiff(_grfconfig_desc); + SaveLoadTableData sld = SlTableHeaderOrRiff(_grfconfig_desc); ClearGRFConfigList(&grfconfig); while (SlIterateArray() != -1) { GRFConfig *c = new GRFConfig(); diff --git a/src/sl/newsignals_sl.cpp b/src/sl/newsignals_sl.cpp index bfaef5bc2b..1c983de524 100644 --- a/src/sl/newsignals_sl.cpp +++ b/src/sl/newsignals_sl.cpp @@ -21,7 +21,7 @@ static const NamedSaveLoad _new_signal_style_mapping_desc[] = { static void Save_NSID() { - std::vector 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 slt = SlTableHeader(_new_signal_style_mapping_desc); + SaveLoadTableData slt = SlTableHeader(_new_signal_style_mapping_desc); int index; while ((index = SlIterateArray()) != -1) { diff --git a/src/sl/saveload.cpp b/src/sl/saveload.cpp index 1ad1459931..1f77e122e8 100644 --- a/src/sl/saveload.cpp +++ b/src/sl/saveload.cpp @@ -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 &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 &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 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 saveloads; + SaveLoadTableData saveloads; switch (_sl.action) { case SLA_LOAD_CHECK: @@ -2261,15 +2345,20 @@ std::vector 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(); + 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 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 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 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 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 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 SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp return saveloads; } -std::vector SlTableHeaderOrRiff(const NamedSaveLoadTable &slt) +SaveLoadTableData SlTableHeaderOrRiff(const NamedSaveLoadTable &slt) { if (SlIsTableChunk()) return SlTableHeader(slt); - std::vector 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(); diff --git a/src/sl/saveload.h b/src/sl/saveload.h index 18c3ae9097..1fb1d74373 100644 --- a/src/sl/saveload.h +++ b/src/sl/saveload.h @@ -1092,12 +1092,15 @@ struct TableHeaderSpecialHandler { bool SlIsTableChunk(); void SlSkipTableHeader(); -std::vector SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSpecialHandler *special_handler = nullptr); -std::vector 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)); diff --git a/src/sl/saveload_buffer.h b/src/sl/saveload_buffer.h index c45eb6ae67..98b2ea6769 100644 --- a/src/sl/saveload_buffer.h +++ b/src/sl/saveload_buffer.h @@ -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 StopAutoLength(); bool IsAutoLengthActive() const { return this->saved_buf != nullptr; } diff --git a/src/sl/saveload_types.h b/src/sl/saveload_types.h index 3235dc3818..93a7a9774d 100644 --- a/src/sl/saveload_types.h +++ b/src/sl/saveload_types.h @@ -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 (*)(); + /** 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 +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 { + return std::make_unique(); + }; + 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 +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 { + return std::make_unique(); + }; + return NSLT_STRUCTLIST(name, factory, from, to, extver); +} + +struct SaveLoadTableData : public std::vector { + std::vector> 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 TypedSaveLoadStructHandler : public SaveLoadStructHandler { +public: + void Save([[maybe_unused]] TObject *object) const {} + void Save(void *object) const override { static_cast(this)->Save(static_cast(object)); } + + void Load([[maybe_unused]] TObject *object) const {} + void Load(void *object) const override { static_cast(this)->Load(static_cast(object)); } + + void LoadCheck([[maybe_unused]] TObject *object) const {} + void LoadCheck(void *object) const override { static_cast(this)->LoadCheck(static_cast(object)); } + + void FixPointers([[maybe_unused]] TObject *object) const {} + void FixPointers(void *object) const override { static_cast(this)->FixPointers(static_cast(object)); } +}; + #endif /* SL_SAVELOAD_TYPES_H */ diff --git a/src/sl/tracerestrict_sl.cpp b/src/sl/tracerestrict_sl.cpp index 756c2540b7..a4ec6f6980 100644 --- a/src/sl/tracerestrict_sl.cpp +++ b/src/sl/tracerestrict_sl.cpp @@ -23,7 +23,7 @@ static const NamedSaveLoad _trace_restrict_mapping_desc[] = { */ static void Load_TRRM() { - std::vector 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 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 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 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 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 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 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 slt = SlTableHeader(_trace_restrict_counter_desc); + SaveLoadTableData slt = SlTableHeader(_trace_restrict_counter_desc); for (TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) { SlSetArrayIndex(ctr->index); diff --git a/src/sl/train_speed_adaptation.cpp b/src/sl/train_speed_adaptation.cpp index 93817351ed..4be27b13e9 100644 --- a/src/sl/train_speed_adaptation.cpp +++ b/src/sl/train_speed_adaptation.cpp @@ -25,7 +25,7 @@ static const NamedSaveLoad _train_speed_adaptation_map_desc[] = { static void Load_TSAS() { const bool table_mode = SlIsTableChunk(); - std::vector 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 slt = SlTableHeader(_train_speed_adaptation_map_desc); + SaveLoadTableData slt = SlTableHeader(_train_speed_adaptation_map_desc); int index = 0; for (auto &it : _signal_speeds) { diff --git a/src/sl/tunnel_sl.cpp b/src/sl/tunnel_sl.cpp index 43461b8e55..2b740aa867 100644 --- a/src/sl/tunnel_sl.cpp +++ b/src/sl/tunnel_sl.cpp @@ -25,7 +25,7 @@ static const NamedSaveLoad _tunnel_desc[] = { static void Save_TUNN() { - std::vector 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 slt = SlTableHeaderOrRiff(_tunnel_desc); + SaveLoadTableData slt = SlTableHeaderOrRiff(_tunnel_desc); int index; while ((index = SlIterateArray()) != -1) {