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:
Jonathan G Rennison
2015-07-31 17:34:27 +01:00
parent 67366cf03d
commit 17e8693e62
14 changed files with 369 additions and 117 deletions

View File

@@ -14,6 +14,7 @@
#include "../fileio_type.h"
#include "../strings_type.h"
#include "extended_ver_sl.h"
/** Save or load result codes. */
enum SaveOrLoadResult {
@@ -213,6 +214,7 @@ struct SaveLoad {
* 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)
size_t size; ///< the sizeof size.
ExtendedSaveLoadFeatureTest ext_feature_test; ///< extended feature test
};
/** Same as #SaveLoad but global variables are used (for better readability); */
@@ -226,9 +228,11 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param type Storage of the data in memory and in the savegame.
* @param from First savegame version that has the field.
* @param to Last savegame version that has the field.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
* @note In general, it is better to use one of the SLE_* macros below.
*/
#define SLE_GENERAL(cmd, base, variable, type, length, from, to) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable), cpp_sizeof(base, variable)}
#define SLE_GENERAL_X(cmd, base, variable, type, length, from, to, extver) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable), cpp_sizeof(base, variable), extver}
#define SLE_GENERAL(cmd, base, variable, type, length, from, to) SLE_GENERAL_X(cmd, base, variable, type, length, from, to, {})
/**
* Storage of a variable in some savegame versions.
@@ -237,8 +241,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param type Storage of the data in memory and in the savegame.
* @param from First savegame version that has the field.
* @param to Last savegame version that has the field.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to)
#define SLE_CONDVAR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VAR, base, variable, type, 0, from, to, extver)
#define SLE_CONDVAR(base, variable, type, from, to) SLE_CONDVAR_X(base, variable, type, from, to, {})
/**
* Storage of a reference in some savegame versions.
@@ -247,8 +253,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param type Type of the reference, a value from #SLRefType.
* @param from First savegame version that has the field.
* @param to Last savegame version that has the field.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to)
#define SLE_CONDREF_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REF, base, variable, type, 0, from, to, extver)
#define SLE_CONDREF(base, variable, type, from, to) SLE_CONDREF_X(base, variable, type, from, to, {})
/**
* Storage of an array in some savegame versions.
@@ -258,8 +266,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param length Number of elements in the array.
* @param from First savegame version that has the array.
* @param to Last savegame version that has the array.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to)
#define SLE_CONDARR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_ARR, base, variable, type, length, from, to, extver)
#define SLE_CONDARR(base, variable, type, length, from, to) SLE_CONDARR_X(base, variable, type, length, from, to, {})
/**
* Storage of a string in some savegame versions.
@@ -269,8 +279,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param length Number of elements in the string (only used for fixed size buffers).
* @param from First savegame version that has the string.
* @param to Last savegame version that has the string.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to)
#define SLE_CONDSTR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_STR, base, variable, type, length, from, to, extver)
#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_CONDSTR_X(base, variable, type, length, from, to, {})
/**
* Storage of a list in some savegame versions.
@@ -279,8 +291,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param type Storage of the data in memory and in the savegame.
* @param from First savegame version that has the list.
* @param to Last savegame version that has the list.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDLST(base, variable, type, from, to) SLE_GENERAL(SL_LST, base, variable, type, 0, from, to)
#define SLE_CONDLST_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_LST, base, variable, type, 0, from, to, extver)
#define SLE_CONDLST(base, variable, type, from, to) SLE_CONDLST_X(base, variable, type, from, to, {})
/**
* Storage of a variable in every version of a savegame.
@@ -335,17 +349,19 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param length Length of the empty space.
* @param from First savegame version that has the empty space.
* @param to Last savegame version that has the empty space.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have empty space
*/
#define SLE_CONDNULL(length, from, to) SLE_CONDARR(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL | SLF_NOT_IN_CONFIG, length, from, to)
#define SLE_CONDNULL_X(length, from, to, extver) SLE_CONDARR_X(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL | SLF_NOT_IN_CONFIG, length, from, to, extver)
#define SLE_CONDNULL(length, from, to) SLE_CONDNULL_X(length, from, to, {})
/** Translate values ingame to different values in the savegame and vv. */
#define SLE_WRITEBYTE(base, variable, value) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, value, value)
#define SLE_VEH_INCLUDE() {false, SL_VEH_INCLUDE, 0, 0, 0, SL_MAX_VERSION, NULL, 0}
#define SLE_ST_INCLUDE() {false, SL_ST_INCLUDE, 0, 0, 0, SL_MAX_VERSION, NULL, 0}
#define SLE_VEH_INCLUDE() {false, SL_VEH_INCLUDE, 0, 0, 0, SL_MAX_VERSION, NULL, 0, {}}
#define SLE_ST_INCLUDE() {false, SL_ST_INCLUDE, 0, 0, 0, SL_MAX_VERSION, NULL, 0, {}}
/** End marker of a struct/class save or load. */
#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL, 0}
#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL, 0, {}}
/**
* Storage of global simple variables, references (pointers), and arrays.
@@ -354,9 +370,11 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param type Storage of the data in memory and in the savegame.
* @param from First savegame version that has the field.
* @param to Last savegame version that has the field.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
* @note In general, it is better to use one of the SLEG_* macros below.
*/
#define SLEG_GENERAL(cmd, variable, type, length, from, to) {true, cmd, type, length, from, to, (void*)&variable, sizeof(variable)}
#define SLEG_GENERAL_X(cmd, variable, type, length, from, to, extver) {true, cmd, type, length, from, to, (void*)&variable, sizeof(variable), extver}
#define SLEG_GENERAL(cmd, variable, type, length, from, to) SLEG_GENERAL_X(cmd, variable, type, length, from, to, {})
/**
* Storage of a global variable in some savegame versions.
@@ -364,8 +382,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param type Storage of the data in memory and in the savegame.
* @param from First savegame version that has the field.
* @param to Last savegame version that has the field.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDVAR(variable, type, from, to) SLEG_GENERAL(SL_VAR, variable, type, 0, from, to)
#define SLEG_CONDVAR_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VAR, variable, type, 0, from, to, extver)
#define SLEG_CONDVAR(variable, type, from, to) SLEG_CONDVAR_X(variable, type, from, to, {})
/**
* Storage of a global reference in some savegame versions.
@@ -373,8 +393,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param type Storage of the data in memory and in the savegame.
* @param from First savegame version that has the field.
* @param to Last savegame version that has the field.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDREF(variable, type, from, to) SLEG_GENERAL(SL_REF, variable, type, 0, from, to)
#define SLEG_CONDREF_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_REF, variable, type, 0, from, to, extver)
#define SLEG_CONDREF(variable, type, from, to) SLEG_CONDREF_X(variable, type, from, to, {})
/**
* Storage of a global array in some savegame versions.
@@ -383,8 +405,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param length Number of elements in the array.
* @param from First savegame version that has the array.
* @param to Last savegame version that has the array.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDARR(variable, type, length, from, to) SLEG_GENERAL(SL_ARR, variable, type, length, from, to)
#define SLEG_CONDARR_X(variable, type, length, from, to, extver) SLEG_GENERAL_X(SL_ARR, variable, type, length, from, to, extver)
#define SLEG_CONDARR(variable, type, length, from, to) SLEG_CONDARR_X(variable, type, length, from, to, {})
/**
* Storage of a global string in some savegame versions.
@@ -393,8 +417,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param length Number of elements in the string (only used for fixed size buffers).
* @param from First savegame version that has the string.
* @param to Last savegame version that has the string.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_GENERAL(SL_STR, variable, type, length, from, to)
#define SLEG_CONDSTR_X(variable, type, length, from, to, extver) SLEG_GENERAL_X(SL_STR, variable, type, length, from, to, extver)
#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_CONDSTR_X(variable, type, length, from, to, {})
/**
* Storage of a global list in some savegame versions.
@@ -402,8 +428,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param type Storage of the data in memory and in the savegame.
* @param from First savegame version that has the list.
* @param to Last savegame version that has the list.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDLST(variable, type, from, to) SLEG_GENERAL(SL_LST, variable, type, 0, from, to)
#define SLEG_CONDLST_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_LST, variable, type, 0, from, to, extver)
#define SLEG_CONDLST(variable, type, from, to) SLEG_CONDLST_X(variable, type, from, to, {})
/**
* Storage of a global variable in every savegame version.
@@ -445,11 +473,12 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param length Length of the empty space.
* @param from First savegame version that has the empty space.
* @param to Last savegame version that has the empty space.
* @param extver ExtendedSaveLoadFeatureTest to test (along with from and to) which savegames have empty space
*/
#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_NOT_IN_CONFIG, length, from, to, (void*)NULL}
#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_NOT_IN_CONFIG, length, from, to, (void*)NULL, {}}
/** End marker of global variables save or load. */
#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL, 0}
#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL, 0, {}}
/**
* Checks whether the savegame is below \a major.\a minor.
@@ -471,10 +500,10 @@ static inline bool IsSavegameVersionBefore(uint16 major, byte minor = 0)
* @param version_to Highest version number that falls within the range.
* @return Active savegame version falls within the given range.
*/
static inline bool SlIsObjectCurrentlyValid(uint16 version_from, uint16 version_to)
static inline bool SlIsObjectCurrentlyValid(uint16 version_from, uint16 version_to, ExtendedSaveLoadFeatureTest ext_feature_test)
{
extern const uint16 SAVEGAME_VERSION;
if (SAVEGAME_VERSION < version_from || SAVEGAME_VERSION > version_to) return false;
if (!ext_feature_test.IsFeaturePresent(SAVEGAME_VERSION, version_from, version_to)) return false;
return true;
}