diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 83b5ddd960..7d598ff8f5 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -95,8 +95,12 @@ extern bool _sl_upstream_mode; namespace upstream_sl { void SlNullPointers(); void SlLoadChunks(); + void SlLoadChunkByID(uint32 id); void SlLoadCheckChunks(); + void SlLoadCheckChunkByID(uint32 id); void SlFixPointers(); + void SlFixPointerChunkByID(uint32 id); + void SlSaveChunkChunkByID(uint32 id); } /** What are we currently doing? */ @@ -2132,6 +2136,10 @@ inline void SlRIFFSpringPPCheck(size_t len) */ static void SlLoadChunk(const ChunkHandler &ch) { + if (ch.special_proc != nullptr) { + if (ch.special_proc(ch.id, CSLSO_PRE_LOADCHECK)) return; + } + byte m = SlReadByte(); size_t len; size_t endoffs; @@ -2195,6 +2203,10 @@ static void SlLoadChunk(const ChunkHandler &ch) */ static void SlLoadCheckChunk(const ChunkHandler *ch) { + if (ch && ch->special_proc != nullptr) { + if (ch->special_proc(ch->id, CSLSO_PRE_LOAD)) return; + } + byte m = SlReadByte(); size_t len; size_t endoffs; @@ -2281,6 +2293,14 @@ static void SlLoadCheckChunk(const ChunkHandler *ch) */ static void SlSaveChunk(const ChunkHandler &ch) { + if (ch.type == CH_UPSTREAM_SAVE) { + SaveLoadVersion old_ver = _sl_version; + _sl_version = MAX_LOAD_SAVEGAME_VERSION; + upstream_sl::SlSaveChunkChunkByID(ch.id); + _sl_version = old_ver; + return; + } + ChunkSaveLoadProc *proc = ch.save_proc; /* Don't save any chunk information if there is no save handler. */ @@ -3804,3 +3824,8 @@ bool SaveLoadFileTypeIsScenario() { return _file_to_saveload.abstract_ftype == FT_SCENARIO; } + +void SlUnreachablePlaceholder() +{ + NOT_REACHED(); +} diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 3480619932..9abcca02ab 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -77,12 +77,22 @@ bool IsNetworkServerSave(); typedef void ChunkSaveLoadProc(); typedef void AutolengthProc(void *arg); +void SlUnreachablePlaceholder(); + +enum ChunkSaveLoadSpecialOp { + CSLSO_PRE_LOAD, + CSLSO_PRE_LOADCHECK, +}; +typedef bool ChunkSaveLoadSpecialProc(uint32, ChunkSaveLoadSpecialOp); + /** Type of a chunk. */ enum ChunkType { CH_RIFF = 0, CH_ARRAY = 1, CH_SPARSE_ARRAY = 2, CH_EXT_HDR = 15, ///< Extended chunk header + + CH_UPSTREAM_SAVE = 0x80, }; /** Handlers and description of chunk. */ @@ -93,8 +103,62 @@ struct ChunkHandler { ChunkSaveLoadProc *ptrs_proc; ///< Manipulate pointers in the chunk. ChunkSaveLoadProc *load_check_proc; ///< Load procedure for game preview. ChunkType type; ///< Type of the chunk. @see ChunkType + ChunkSaveLoadSpecialProc *special_proc = nullptr; }; +template +void SlExecWithSlVersion(SaveLoadVersion use_version, F proc) +{ + extern SaveLoadVersion _sl_version; + SaveLoadVersion old_ver = _sl_version; + _sl_version = use_version; + proc(); + _sl_version = old_ver; +} + +namespace upstream_sl { + template + ChunkHandler MakeUpstreamChunkHandler() + { + extern void SlLoadChunkByID(uint32); + extern void SlLoadCheckChunkByID(uint32); + extern void SlFixPointerChunkByID(uint32); + + ChunkHandler ch = { + id, + nullptr, + SlUnreachablePlaceholder, + []() { + SlExecWithSlVersion(F::GetVersion(), []() { + SlFixPointerChunkByID(id); + }); + }, + SlUnreachablePlaceholder, + CH_UPSTREAM_SAVE + }; + ch.special_proc = [](uint32 chunk_id, ChunkSaveLoadSpecialOp op) -> bool { + assert(id == chunk_id); + switch (op) { + case CSLSO_PRE_LOAD: + SlExecWithSlVersion(F::GetVersion(), []() { + SlLoadChunkByID(id); + }); + break; + case CSLSO_PRE_LOADCHECK: + SlExecWithSlVersion(F::GetVersion(), []() { + SlLoadCheckChunkByID(id); + }); + break; + } + + return true; // chunk has been consumed + }; + return ch; + } +} + +using upstream_sl::MakeUpstreamChunkHandler; + struct NullStruct { byte null; }; diff --git a/src/saveload/upstream/saveload.cpp b/src/saveload/upstream/saveload.cpp index a4cef44d66..b3124f6c00 100644 --- a/src/saveload/upstream/saveload.cpp +++ b/src/saveload/upstream/saveload.cpp @@ -2014,6 +2014,18 @@ void SlLoadChunks() } } +/** Load a chunk */ +void SlLoadChunkByID(uint32 id) +{ + _sl.action = SLA_LOAD; + + DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); + + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + SlLoadChunk(*ch); +} + /** Load all chunks for savegame checking */ void SlLoadCheckChunks() { @@ -2031,6 +2043,18 @@ void SlLoadCheckChunks() } } +/** Load a chunk for savegame checking */ +void SlLoadCheckChunkByID(uint32 id) +{ + _sl.action = SLA_LOAD_CHECK; + + DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); + + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + SlLoadCheckChunk(*ch); +} + /** Fix all pointers (convert index -> pointer) */ void SlFixPointers() { @@ -2044,6 +2068,64 @@ void SlFixPointers() assert(_sl.action == SLA_PTRS); } +void SlFixPointerChunkByID(uint32 id) +{ + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + DEBUG(sl, 3, "Fixing pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id); + ch->FixPointers(); +} + +/** + * Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is + * prefixed by an ID identifying it, followed by data, and terminator where appropriate + * @param ch The chunkhandler that will be used for the operation + */ +static void SlSaveChunk(const ChunkHandler &ch) +{ + if (ch.type == CH_READONLY) return; + + SlWriteUint32(ch.id); + DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id); + + _sl.block_mode = ch.type; + _sl.expect_table_header = (_sl.block_mode == CH_TABLE || _sl.block_mode == CH_SPARSE_TABLE); + + _sl.need_length = (_sl.expect_table_header || _sl.block_mode == CH_RIFF) ? NL_WANTLENGTH : NL_NONE; + + switch (_sl.block_mode) { + case CH_RIFF: + ch.Save(); + break; + case CH_TABLE: + case CH_ARRAY: + _sl.last_array_index = 0; + SlWriteByte(_sl.block_mode); + ch.Save(); + SlWriteArrayLength(0); // Terminate arrays + break; + case CH_SPARSE_TABLE: + case CH_SPARSE_ARRAY: + SlWriteByte(_sl.block_mode); + ch.Save(); + SlWriteArrayLength(0); // Terminate arrays + break; + default: NOT_REACHED(); + } + + if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header"); +} + +/** Save a chunk of data */ +void SlSaveChunkChunkByID(uint32 id) +{ + const ChunkHandler *ch = SlFindChunkHandler(id); + if (ch == nullptr) SlErrorCorrupt("Unknown chunk type"); + + _sl.action = SLA_SAVE; + SlSaveChunk(*ch); +} + SaveLoadTable SaveLoadHandler::GetLoadDescription() const { assert(this->load_description.has_value());