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() static void Load_XBSS()
{ {
std::vector<SaveLoad> slt = SlTableHeaderOrRiff(_long_bridge_signal_storage_desc); SaveLoadTableData slt = SlTableHeaderOrRiff(_long_bridge_signal_storage_desc);
int index; int index;
while ((index = SlIterateArray()) != -1) { while ((index = SlIterateArray()) != -1) {
@@ -29,7 +29,7 @@ static void Load_XBSS()
static void Save_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) { for (auto &it : _long_bridge_signal_sim_map) {
LongBridgeSignalStorage &lbss = it.second; LongBridgeSignalStorage &lbss = it.second;

View File

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

View File

@@ -80,7 +80,7 @@ std::vector<NamedSaveLoad> GetCheatsDesc(bool save) {
*/ */
static void Save_CHTS() static void Save_CHTS()
{ {
std::vector<SaveLoad> slt = SlTableHeader(GetCheatsDesc(true)); SaveLoadTableData slt = SlTableHeader(GetCheatsDesc(true));
SlSetArrayIndex(0); SlSetArrayIndex(0);
SlObjectSaveFiltered(&_cheats, slt); SlObjectSaveFiltered(&_cheats, slt);
@@ -105,7 +105,7 @@ static void Load_CHTS()
}; };
UnknownCheatHandler uch{}; UnknownCheatHandler uch{};
std::vector<SaveLoad> slt = SlTableHeader(GetCheatsDesc(false), &uch); SaveLoadTableData slt = SlTableHeader(GetCheatsDesc(false), &uch);
if (SlIterateArray() == -1) return; if (SlIterateArray() == -1) return;
SlObjectLoadFiltered(&_cheats, slt); SlObjectLoadFiltered(&_cheats, slt);
@@ -114,7 +114,7 @@ static void Load_CHTS()
} }
} else { } else {
size_t count = SlGetFieldLength(); 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 /* 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 * 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("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)), NSLT("log", SLEG_SSTR(_loadgame_DBGL_data, SLE_STR | SLF_ALLOW_CONTROL | SLF_ALLOW_NEWLINE)),
}; };
SlLoadTableOrRiffFiltered(nsl); SlLoadTableObjectChunk(nsl);
} }
static void Check_DBGD() 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("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)), 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[] = { extern const ChunkHandler debug_chunk_handlers[] = {

View File

@@ -167,7 +167,7 @@ static const NamedSaveLoad _industrytype_builder_desc[] = {
/** Save industry-type build data. */ /** Save industry-type build data. */
static void Save_ITBL() 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++) { for (int i = 0; i < NUM_INDUSTRYTYPES; i++) {
SlSetArrayIndex(i); SlSetArrayIndex(i);
@@ -178,7 +178,7 @@ static void Save_ITBL()
/** Load industry-type build data. */ /** Load industry-type build data. */
static void Load_ITBL() 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++) { for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
_industry_builder.builddata[it].Reset(); _industry_builder.builddata[it].Reset();

View File

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

View File

@@ -21,7 +21,7 @@ static const NamedSaveLoad _new_signal_style_mapping_desc[] = {
static void Save_NSID() 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; int index = 0;
for (NewSignalStyleMapping &it : _new_signal_style_mapping) { for (NewSignalStyleMapping &it : _new_signal_style_mapping) {
@@ -35,7 +35,7 @@ static void Load_NSID()
_new_signal_style_mapping.fill({}); _new_signal_style_mapping.fill({});
if (SlIsTableChunk()) { if (SlIsTableChunk()) {
std::vector<SaveLoad> slt = SlTableHeader(_new_signal_style_mapping_desc); SaveLoadTableData slt = SlTableHeader(_new_signal_style_mapping_desc);
int index; int index;
while ((index = SlIterateArray()) != -1) { 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); 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 { 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 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_WRITEBYTE: return 1; // a uint8_t is logically of size 1
case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END)); case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END));
case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription()); 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(); default: NOT_REACHED();
} }
return 0; return 0;
@@ -1906,6 +1924,8 @@ static void SlFilterObjectMember(const SaveLoad &sld, std::vector<SaveLoad> &sav
case SL_RING: case SL_RING:
case SL_STDSTR: case SL_STDSTR:
case SL_VARVEC: case SL_VARVEC:
case SL_STRUCT:
case SL_STRUCTLIST:
/* CONDITIONAL saveload types depend on the savegame version */ /* CONDITIONAL saveload types depend on the savegame version */
if (!SlIsObjectValidInSavegame(sld)) return; if (!SlIsObjectValidInSavegame(sld)) return;
@@ -1921,6 +1941,8 @@ static void SlFilterObjectMember(const SaveLoad &sld, std::vector<SaveLoad> &sav
case SL_REFLIST: case SL_REFLIST:
case SL_PTRRING: case SL_PTRRING:
case SL_VEC: case SL_VEC:
case SL_STRUCT:
case SL_STRUCTLIST:
break; break;
/* non-ptr types do not require SLA_PTRS or SLA_NULL actions */ /* non-ptr types do not require SLA_PTRS or SLA_NULL actions */
@@ -2032,6 +2054,51 @@ bool SlObjectMemberGeneric(void *object, const SaveLoad &sld)
} }
break; 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. /* 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 * When loading, the value is read explicitly with SlReadByte() to determine which
* object description to use. */ * object description to use. */
@@ -2141,34 +2208,18 @@ bool SlIsTableChunk()
void SlSkipTableHeader() void SlSkipTableHeader()
{ {
uint sub_tables = 0;
while (true) { while (true) {
uint8_t type = SlReadByte(); uint8_t type = SlReadByte();
if (type == SLE_FILE_END) break; 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); SlString(nullptr, 0, SLE_FILE_STRING | SLE_VAR_NULL);
} }
} for (uint i = 0; i < sub_tables; i++) {
SlSkipTableHeader();
/**
* 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);
} }
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: case SL_WRITEBYTE:
return SLE_FILE_U8; return SLE_FILE_U8;
case SL_STRUCT:
case SL_STRUCTLIST:
return SLE_FILE_STRUCT | SLE_FILE_HAS_LENGTH_FIELD;
default: NOT_REACHED(); 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. * Save or Load a table header.
* @note a table-header can never contain more than 65535 fields. * @note a table-header can never contain more than 65535 fields.
* @param slt The NamedSaveLoad table with objects to save/load. * @param slt The NamedSaveLoad table with objects to save/load.
* @return The ordered SaveLoad array to use. * @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. */ /* You can only use SlTableHeader if you are a CH_TABLE. */
assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); assert(_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE);
std::vector<SaveLoad> saveloads; SaveLoadTableData saveloads;
switch (_sl.action) { switch (_sl.action) {
case SLA_LOAD_CHECK: 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); DEBUG(sl, _sl.action == SLA_LOAD ? 2 : 6, "Field '%s' of type 0x%02X not found, skipping", key.c_str(), type);
SaveLoadType saveload_type; SaveLoadType saveload_type;
SaveLoadStructHandler *struct_handler = nullptr;
switch (type & SLE_FILE_TYPE_MASK) { switch (type & SLE_FILE_TYPE_MASK) {
case SLE_FILE_STRING: case SLE_FILE_STRING:
/* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */ /* Strings are always marked with SLE_FILE_HAS_LENGTH_FIELD, as they are a list of chars. */
saveload_type = SL_STDSTR; saveload_type = SL_STDSTR;
break; break;
case SLE_FILE_STRUCT: case SLE_FILE_STRUCT: {
SlErrorCorrupt("SLE_FILE_STRUCT not supported yet"); saveload_type = SL_STRUCTLIST;
auto handler = std::make_unique<SaveLoadSkipStructHandler>();
struct_handler = handler.get();
saveloads.struct_handlers.push_back(std::move(handler));
break; break;
}
default: default:
saveload_type = (type & SLE_FILE_HAS_LENGTH_FIELD) ? SL_ARR : SL_VAR; 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. */ /* 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; continue;
} }
@@ -2292,17 +2381,28 @@ std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp
SlErrorCorrupt("Field type is different than expected"); SlErrorCorrupt("Field type is different than expected");
} }
saveloads.push_back(*sld_it->save_load); 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; break;
} }
case SLA_SAVE: { case SLA_SAVE: {
/* Automatically calculate the length? */ const NeedLength orig_need_length = _sl.need_length;
if (_sl.need_length != NL_NONE) { if (orig_need_length != NL_NONE) {
SlSetLength(SlCalcTableHeader(slt)); _sl.need_length = NL_NONE;
_sl.dumper->StartAutoLength();
} }
for (auto &nsld : slt) { for (auto &nsld : slt) {
@@ -2319,7 +2419,21 @@ std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp
/* Add an end-of-header marker. */ /* Add an end-of-header marker. */
SlWriteByte(SLE_FILE_END); 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; break;
} }
@@ -2330,11 +2444,11 @@ std::vector<SaveLoad> SlTableHeader(const NamedSaveLoadTable &slt, TableHeaderSp
return saveloads; return saveloads;
} }
std::vector<SaveLoad> SlTableHeaderOrRiff(const NamedSaveLoadTable &slt) SaveLoadTableData SlTableHeaderOrRiff(const NamedSaveLoadTable &slt)
{ {
if (SlIsTableChunk()) return SlTableHeader(slt); if (SlIsTableChunk()) return SlTableHeader(slt);
std::vector<SaveLoad> saveloads; SaveLoadTableData saveloads;
for (auto &nsld : slt) { for (auto &nsld : slt) {
if ((nsld.nsl_flags & NSLF_TABLE_ONLY) != 0) continue; if ((nsld.nsl_flags & NSLF_TABLE_ONLY) != 0) continue;
SlFilterObjectMember(nsld.save_load, saveloads); SlFilterObjectMember(nsld.save_load, saveloads);
@@ -2363,6 +2477,28 @@ void SlLoadTableWithArrayLengthPrefixesMissing()
SetBit(_sl.block_flags, SLBF_TABLE_ARRAY_LENGTH_PREFIX_MISSING); 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() void SlSkipChunkContents()
{ {
if (SlIsTableChunk()) SlSkipTableHeader(); if (SlIsTableChunk()) SlSkipTableHeader();

View File

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

View File

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

View File

@@ -103,26 +103,30 @@ typedef uint32_t VarType;
/** Type of data saved. */ /** Type of data saved. */
enum SaveLoadTypes { enum SaveLoadTypes {
SL_VAR = 0, ///< Save/load a variable. SL_VAR = 0, ///< Save/load a variable.
SL_REF = 1, ///< Save/load a reference. SL_REF, ///< Save/load a reference.
SL_ARR = 2, ///< Save/load a fixed-size array of #SL_VAR elements. SL_ARR, ///< Save/load a fixed-size array of #SL_VAR elements.
SL_STR = 3, ///< Save/load a string. SL_STR, ///< Save/load a string.
SL_REFLIST = 4, ///< Save/load a list of #SL_REF elements. SL_REFLIST, ///< Save/load a list of #SL_REF elements.
SL_RING = 5, ///< Save/load a ring of #SL_VAR elements. SL_RING, ///< Save/load a ring of #SL_VAR elements.
SL_VEC = 6, ///< Save/load a vector of #SL_REF elements. SL_VEC, ///< Save/load a vector of #SL_REF elements.
SL_STDSTR = 7, ///< Save/load a std::string. 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 */ /* non-normal save-load types */
SL_WRITEBYTE = 8, SL_WRITEBYTE,
SL_VEH_INCLUDE = 9, SL_VEH_INCLUDE,
SL_ST_INCLUDE = 10, SL_ST_INCLUDE,
SL_PTRRING = 13, ///< Save/load a ring of #SL_REF elements.
SL_VARVEC = 14, ///< Save/load a primitive type vector.
}; };
typedef uint8_t SaveLoadType; ///< Save/load type. @see SaveLoadTypes 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! */ /** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */
struct SaveLoad { struct SaveLoad {
bool global; ///< should we load a global variable or a non-global one 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_from; ///< save/load the variable starting from this savegame version
SaveLoadVersion version_to; ///< save/load the variable until this savegame version SaveLoadVersion version_to; ///< save/load the variable until this savegame version
uint16_t label_tag; ///< for labelling purposes 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 union {
* during runtime. Decision on which one to use is controlled by the function /* NOTE: This element either denotes the address of the variable for a global
* that is called to save it. address: global=true, offset: global=false */ * variable, or the offset within a struct which is then bound to a variable
void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536) * 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 SlXvFeatureTest ext_feature_test; ///< extended feature test
SaveLoadStructHandler *struct_handler = nullptr;
}; };
inline constexpr SaveLoad SLTAG(uint16_t label_tag, SaveLoad save_load) 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 }; 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 */ #endif /* SL_SAVELOAD_TYPES_H */

View File

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

View File

@@ -25,7 +25,7 @@ static const NamedSaveLoad _train_speed_adaptation_map_desc[] = {
static void Load_TSAS() static void Load_TSAS()
{ {
const bool table_mode = SlIsTableChunk(); 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; int index;
SignalSpeedType data; SignalSpeedType data;
@@ -40,7 +40,7 @@ static void Load_TSAS()
static void Save_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; int index = 0;
for (auto &it : _signal_speeds) { for (auto &it : _signal_speeds) {

View File

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