Initial stubs for extending the save/load version checking/upgrade code,
with support for tests on individually versioned features as well as savegame versions. This is mainly for improved handling of features added to patchpacks. The current status-quo of picking a random number for the savegame version when releasing a patch breaks loading of savegames from newer trunk savegame versions, with or without the same patch applied, as savegame upgrade code is not run, which can also lead to crashes on load. Instead don't change the savegame version (but set the high bit to keep trunk versions away), but instead include the versions of individual features using a separate mechanism.
This commit is contained in:
@@ -48,9 +48,12 @@
|
||||
|
||||
#include "saveload_internal.h"
|
||||
#include "saveload_filter.h"
|
||||
#include "extended_ver_sl.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
* Previous savegame versions, the trunk revision where they were
|
||||
* introduced and the released version that had that particular
|
||||
@@ -264,12 +267,14 @@
|
||||
* 194 26881 1.5.x
|
||||
*/
|
||||
extern const uint16 SAVEGAME_VERSION = 194; ///< Current savegame version of OpenTTD.
|
||||
const uint16 SAVEGAME_VERSION_EXT = 0x8000; ///< Savegame extension indicator mask
|
||||
|
||||
SavegameType _savegame_type; ///< type of savegame we are loading
|
||||
|
||||
uint32 _ttdp_version; ///< version of TTDP savegame (if applicable)
|
||||
uint16 _sl_version; ///< the major savegame version identifier
|
||||
byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
|
||||
bool _sl_is_ext_version; ///< is this an extended savegame version, with more info in the SLXI chunk?
|
||||
char _savegame_format[8]; ///< how to compress savegames
|
||||
bool _do_autosave; ///< are we doing an autosave at the moment?
|
||||
|
||||
@@ -414,6 +419,7 @@ struct SaveLoadParams {
|
||||
static SaveLoadParams _sl; ///< Parameters used for/at saveload.
|
||||
|
||||
/* these define the chunks */
|
||||
//extern const ChunkHandler _version_ext_chunk_handlers[];
|
||||
extern const ChunkHandler _gamelog_chunk_handlers[];
|
||||
extern const ChunkHandler _map_chunk_handlers[];
|
||||
extern const ChunkHandler _misc_chunk_handlers[];
|
||||
@@ -450,6 +456,7 @@ extern const ChunkHandler _persistent_storage_chunk_handlers[];
|
||||
|
||||
/** Array of all chunks in a savegame, \c NULL terminated. */
|
||||
static const ChunkHandler * const _chunk_handlers[] = {
|
||||
// _version_ext_chunk_handlers, // this should be first, such that it is saved first, as when loading it affects the loading of subsequent chunks
|
||||
_gamelog_chunk_handlers,
|
||||
_map_chunk_handlers,
|
||||
_misc_chunk_handlers,
|
||||
@@ -503,6 +510,7 @@ static void SlNullPointers()
|
||||
* during NULLing; especially those that try to get
|
||||
* pointers from other pools. */
|
||||
_sl_version = SAVEGAME_VERSION;
|
||||
SlXvSetCurrentState();
|
||||
|
||||
DEBUG(sl, 1, "Nulling pointers");
|
||||
|
||||
@@ -1414,7 +1422,7 @@ static void SlList(void *list, SLRefType conv)
|
||||
/** Are we going to save this object or not? */
|
||||
static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
|
||||
{
|
||||
if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false;
|
||||
if (!sld->ext_feature_test.IsFeaturePresent(_sl_version, sld->version_from, sld->version_to)) return false;
|
||||
if (sld->conv & SLF_NOT_IN_SAVE) return false;
|
||||
|
||||
return true;
|
||||
@@ -1692,7 +1700,7 @@ static void SlLoadChunk(const ChunkHandler *ch)
|
||||
/**
|
||||
* Load a chunk of data for checking savegames.
|
||||
* If the chunkhandler is NULL, the chunk is skipped.
|
||||
* @param ch The chunkhandler that will be used for the operation
|
||||
* @param ch The chunkhandler that will be used for the operation, this may be NULL
|
||||
*/
|
||||
static void SlLoadCheckChunk(const ChunkHandler *ch)
|
||||
{
|
||||
@@ -1706,14 +1714,14 @@ static void SlLoadCheckChunk(const ChunkHandler *ch)
|
||||
switch (m) {
|
||||
case CH_ARRAY:
|
||||
_sl.array_index = 0;
|
||||
if (ch->load_check_proc) {
|
||||
if (ch && ch->load_check_proc) {
|
||||
ch->load_check_proc();
|
||||
} else {
|
||||
SlSkipArray();
|
||||
}
|
||||
break;
|
||||
case CH_SPARSE_ARRAY:
|
||||
if (ch->load_check_proc) {
|
||||
if (ch && ch->load_check_proc) {
|
||||
ch->load_check_proc();
|
||||
} else {
|
||||
SlSkipArray();
|
||||
@@ -1726,7 +1734,7 @@ static void SlLoadCheckChunk(const ChunkHandler *ch)
|
||||
len += SlReadUint16();
|
||||
_sl.obj_len = len;
|
||||
endoffs = _sl.reader->GetSize() + len;
|
||||
if (ch->load_check_proc) {
|
||||
if (ch && ch->load_check_proc) {
|
||||
ch->load_check_proc();
|
||||
} else {
|
||||
SlSkipBytes(len);
|
||||
@@ -1840,8 +1848,16 @@ static void SlLoadChunks()
|
||||
DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
|
||||
|
||||
ch = SlFindChunkHandler(id);
|
||||
if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
|
||||
SlLoadChunk(ch);
|
||||
if (ch == NULL) {
|
||||
if (SlXvIsChunkDiscardable(id)) {
|
||||
DEBUG(sl, 1, "Discarding chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
|
||||
SlLoadCheckChunk(NULL);
|
||||
} else {
|
||||
SlErrorCorrupt("Unknown chunk type");
|
||||
}
|
||||
} else {
|
||||
SlLoadChunk(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1855,7 +1871,7 @@ static void SlLoadCheckChunks()
|
||||
DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
|
||||
|
||||
ch = SlFindChunkHandler(id);
|
||||
if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
|
||||
if (ch == NULL && !SlXvIsChunkDiscardable(id)) SlErrorCorrupt("Unknown chunk type");
|
||||
SlLoadCheckChunk(ch);
|
||||
}
|
||||
}
|
||||
@@ -2511,7 +2527,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded)
|
||||
const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
|
||||
|
||||
/* We have written our stuff to memory, now write it to file! */
|
||||
uint32 hdr[2] = { fmt->tag, TO_BE32(SAVEGAME_VERSION << 16) };
|
||||
uint32 hdr[2] = { fmt->tag, TO_BE32((SAVEGAME_VERSION | SAVEGAME_VERSION_EXT) << 16) };
|
||||
_sl.sf->Write((byte*)hdr, sizeof(hdr));
|
||||
|
||||
_sl.sf = fmt->init_write(_sl.sf, compression);
|
||||
@@ -2578,6 +2594,7 @@ static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded)
|
||||
_sl.sf = writer;
|
||||
|
||||
_sl_version = SAVEGAME_VERSION;
|
||||
SlXvSetCurrentState();
|
||||
|
||||
SaveViewportBeforeSaveGame();
|
||||
SlSaveChunks();
|
||||
@@ -2629,6 +2646,8 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
|
||||
_load_check_data.checkable = true;
|
||||
}
|
||||
|
||||
SlXvResetState();
|
||||
|
||||
uint32 hdr[2];
|
||||
if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
|
||||
|
||||
@@ -2641,6 +2660,8 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
|
||||
_sl.lf->Reset();
|
||||
_sl_version = 0;
|
||||
_sl_minor_version = 0;
|
||||
_sl_is_ext_version = false;
|
||||
SlXvResetState();
|
||||
|
||||
/* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
|
||||
fmt = _saveload_formats;
|
||||
@@ -2663,7 +2684,14 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
|
||||
* Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
|
||||
_sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
|
||||
|
||||
DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
|
||||
if (_sl_version & SAVEGAME_VERSION_EXT) {
|
||||
_sl_version &= ~SAVEGAME_VERSION_EXT;
|
||||
_sl_is_ext_version = true;
|
||||
} else {
|
||||
SlXvCheckSpecialSavegameVersions();
|
||||
}
|
||||
|
||||
DEBUG(sl, 1, "Loading savegame version %d%s", _sl_version, _sl_is_ext_version ? " (extended)" : "");
|
||||
|
||||
/* Is the version higher than the current? */
|
||||
if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
|
||||
@@ -2800,6 +2828,8 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo
|
||||
if (!LoadOldSaveGame(filename)) return SL_REINIT;
|
||||
_sl_version = 0;
|
||||
_sl_minor_version = 0;
|
||||
_sl_is_ext_version = false;
|
||||
SlXvResetState();
|
||||
GamelogStartAction(GLAT_LOAD);
|
||||
if (!AfterLoadGame()) {
|
||||
GamelogStopAction();
|
||||
|
Reference in New Issue
Block a user