mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-11 13:10:45 +00:00
Saveload: Table format sub-struct support
This commit is contained in:
parent
99c6cc5bdb
commit
11eebdc5dd
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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[] = {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
|
@ -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; }
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user