Saveload: Add support for using upstream save/load for specific chunks

This commit is contained in:
Jonathan G Rennison
2022-12-03 12:53:35 +00:00
parent a608a2592d
commit 690ef6cc22
3 changed files with 171 additions and 0 deletions

View File

@@ -95,8 +95,12 @@ extern bool _sl_upstream_mode;
namespace upstream_sl { namespace upstream_sl {
void SlNullPointers(); void SlNullPointers();
void SlLoadChunks(); void SlLoadChunks();
void SlLoadChunkByID(uint32 id);
void SlLoadCheckChunks(); void SlLoadCheckChunks();
void SlLoadCheckChunkByID(uint32 id);
void SlFixPointers(); void SlFixPointers();
void SlFixPointerChunkByID(uint32 id);
void SlSaveChunkChunkByID(uint32 id);
} }
/** What are we currently doing? */ /** What are we currently doing? */
@@ -2132,6 +2136,10 @@ inline void SlRIFFSpringPPCheck(size_t len)
*/ */
static void SlLoadChunk(const ChunkHandler &ch) static void SlLoadChunk(const ChunkHandler &ch)
{ {
if (ch.special_proc != nullptr) {
if (ch.special_proc(ch.id, CSLSO_PRE_LOADCHECK)) return;
}
byte m = SlReadByte(); byte m = SlReadByte();
size_t len; size_t len;
size_t endoffs; size_t endoffs;
@@ -2195,6 +2203,10 @@ static void SlLoadChunk(const ChunkHandler &ch)
*/ */
static void SlLoadCheckChunk(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(); byte m = SlReadByte();
size_t len; size_t len;
size_t endoffs; size_t endoffs;
@@ -2281,6 +2293,14 @@ static void SlLoadCheckChunk(const ChunkHandler *ch)
*/ */
static void SlSaveChunk(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; ChunkSaveLoadProc *proc = ch.save_proc;
/* Don't save any chunk information if there is no save handler. */ /* 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; return _file_to_saveload.abstract_ftype == FT_SCENARIO;
} }
void SlUnreachablePlaceholder()
{
NOT_REACHED();
}

View File

@@ -77,12 +77,22 @@ bool IsNetworkServerSave();
typedef void ChunkSaveLoadProc(); typedef void ChunkSaveLoadProc();
typedef void AutolengthProc(void *arg); typedef void AutolengthProc(void *arg);
void SlUnreachablePlaceholder();
enum ChunkSaveLoadSpecialOp {
CSLSO_PRE_LOAD,
CSLSO_PRE_LOADCHECK,
};
typedef bool ChunkSaveLoadSpecialProc(uint32, ChunkSaveLoadSpecialOp);
/** Type of a chunk. */ /** Type of a chunk. */
enum ChunkType { enum ChunkType {
CH_RIFF = 0, CH_RIFF = 0,
CH_ARRAY = 1, CH_ARRAY = 1,
CH_SPARSE_ARRAY = 2, CH_SPARSE_ARRAY = 2,
CH_EXT_HDR = 15, ///< Extended chunk header CH_EXT_HDR = 15, ///< Extended chunk header
CH_UPSTREAM_SAVE = 0x80,
}; };
/** Handlers and description of chunk. */ /** Handlers and description of chunk. */
@@ -93,8 +103,62 @@ struct ChunkHandler {
ChunkSaveLoadProc *ptrs_proc; ///< Manipulate pointers in the chunk. ChunkSaveLoadProc *ptrs_proc; ///< Manipulate pointers in the chunk.
ChunkSaveLoadProc *load_check_proc; ///< Load procedure for game preview. ChunkSaveLoadProc *load_check_proc; ///< Load procedure for game preview.
ChunkType type; ///< Type of the chunk. @see ChunkType ChunkType type; ///< Type of the chunk. @see ChunkType
ChunkSaveLoadSpecialProc *special_proc = nullptr;
}; };
template <typename F>
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 <uint32 id, typename F>
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 { struct NullStruct {
byte null; byte null;
}; };

View File

@@ -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 */ /** Load all chunks for savegame checking */
void SlLoadCheckChunks() 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) */ /** Fix all pointers (convert index -> pointer) */
void SlFixPointers() void SlFixPointers()
{ {
@@ -2044,6 +2068,64 @@ void SlFixPointers()
assert(_sl.action == SLA_PTRS); 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 SaveLoadTable SaveLoadHandler::GetLoadDescription() const
{ {
assert(this->load_description.has_value()); assert(this->load_description.has_value());