summaryrefslogtreecommitdiff
path: root/src/saveload
diff options
context:
space:
mode:
Diffstat (limited to 'src/saveload')
-rw-r--r--src/saveload/saveload.cpp55
-rw-r--r--src/saveload/saveload.h119
2 files changed, 156 insertions, 18 deletions
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index f9abac3ba..6be86a7ac 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -1450,6 +1450,27 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
case SL_WRITEBYTE: return 1; // a byte 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: {
+ if (!SlIsObjectValidInSavegame(sld)) break;
+
+ NeedLength old_need_length = _sl.need_length;
+ size_t old_obj_len = _sl.obj_len;
+
+ _sl.need_length = NL_CALCLENGTH;
+ _sl.obj_len = 0;
+
+ /* Pretend that we are saving to collect the object size. Other
+ * means are difficult, as we don't know the length of the list we
+ * are about to store. */
+ sld.handler->Save(const_cast<void *>(object));
+ size_t length = _sl.obj_len;
+
+ _sl.obj_len = old_obj_len;
+ _sl.need_length = old_need_length;
+
+ return length;
+ }
default: NOT_REACHED();
}
return 0;
@@ -1505,8 +1526,6 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld)
static bool SlObjectMember(void *object, const SaveLoad &sld)
{
- void *ptr = GetVariableAddress(object, sld);
-
assert(IsVariableSizeRight(sld));
VarType conv = GB(sld.conv, 0, 8);
@@ -1517,10 +1536,12 @@ static bool SlObjectMember(void *object, const SaveLoad &sld)
case SL_STR:
case SL_REFLIST:
case SL_DEQUE:
- case SL_STDSTR:
+ case SL_STDSTR: {
/* CONDITIONAL saveload types depend on the savegame version */
if (!SlIsObjectValidInSavegame(sld)) return false;
+ void *ptr = GetVariableAddress(object, sld);
+
switch (sld.cmd) {
case SL_VAR: SlSaveLoadConv(ptr, conv); break;
case SL_REF: SlSaveLoadRef(ptr, conv); break;
@@ -1532,11 +1553,14 @@ static bool SlObjectMember(void *object, const SaveLoad &sld)
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. */
- case SL_WRITEBYTE:
+ case SL_WRITEBYTE: {
+ void *ptr = GetVariableAddress(object, sld);
+
switch (_sl.action) {
case SLA_SAVE: SlWriteByte(*(uint8 *)ptr); break;
case SLA_LOAD_CHECK:
@@ -1546,15 +1570,34 @@ static bool SlObjectMember(void *object, const SaveLoad &sld)
default: NOT_REACHED();
}
break;
+ }
/* SL_VEH_INCLUDE loads common code for vehicles */
- case SL_VEH_INCLUDE:
+ case SL_VEH_INCLUDE: {
+ void *ptr = GetVariableAddress(object, sld);
SlObject(ptr, GetVehicleDescription(VEH_END));
break;
+ }
- case SL_ST_INCLUDE:
+ case SL_ST_INCLUDE: {
+ void *ptr = GetVariableAddress(object, sld);
SlObject(ptr, GetBaseStationDescription());
break;
+ }
+
+ case SL_STRUCT:
+ case SL_STRUCTLIST:
+ if (!SlIsObjectValidInSavegame(sld)) return false;
+
+ switch (_sl.action) {
+ case SLA_SAVE: sld.handler->Save(object); break;
+ case SLA_LOAD_CHECK: sld.handler->LoadCheck(object); break;
+ case SLA_LOAD: sld.handler->Load(object); break;
+ case SLA_PTRS: sld.handler->FixPointers(object); break;
+ case SLA_NULL: break;
+ default: NOT_REACHED();
+ }
+ break;
default: NOT_REACHED();
}
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 28af1fd37..ff0982569 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -400,6 +400,73 @@ struct ChunkHandler {
/** A table of ChunkHandler entries. */
using ChunkHandlerTable = span<const ChunkHandler>;
+/** A table of SaveLoad entries. */
+using SaveLoadTable = span<const struct SaveLoad>;
+
+/** Handler for saving/loading an object to/from disk. */
+class SaveLoadHandler {
+public:
+ virtual ~SaveLoadHandler() {}
+
+ /**
+ * Save the object to disk.
+ * @param object The object to store.
+ */
+ virtual void Save(void *object) const {}
+
+ /**
+ * Load the object from disk.
+ * @param object The object to load.
+ */
+ virtual void Load(void *object) const {}
+
+ /**
+ * Similar to load, but used only to validate savegames.
+ * @param object The object to load.
+ */
+ virtual void LoadCheck(void *object) const {}
+
+ /**
+ * A post-load callback to fix #SL_REF integers into pointers.
+ * @param object The object to fix.
+ */
+ virtual void FixPointers(void *object) const {}
+
+ /**
+ * Get the description of the fields in the savegame.
+ */
+ virtual SaveLoadTable GetDescription() const = 0;
+};
+
+/**
+ * Default handler for saving/loading an object to/from disk.
+ *
+ * This handles a few common things for handlers, meaning the actual handler
+ * needs less code.
+ *
+ * Usage: class SlMine : public DefaultSaveLoadHandler<SlMine, MyObject> {}
+ *
+ * @tparam TImpl The class initializing this template.
+ * @tparam TObject The class of the object using this SaveLoadHandler.
+ */
+template <class TImpl, class TObject>
+class DefaultSaveLoadHandler : public SaveLoadHandler {
+public:
+ SaveLoadTable GetDescription() const override { return static_cast<const TImpl *>(this)->description; }
+
+ virtual void Save(TObject *object) const {}
+ void Save(void *object) const override { this->Save(static_cast<TObject *>(object)); }
+
+ virtual void Load(TObject *object) const {}
+ void Load(void *object) const override { this->Load(static_cast<TObject *>(object)); }
+
+ virtual void LoadCheck(TObject *object) const {}
+ void LoadCheck(void *object) const override { this->LoadCheck(static_cast<TObject *>(object)); }
+
+ virtual void FixPointers(TObject *object) const {}
+ void FixPointers(void *object) const override { this->FixPointers(static_cast<TObject *>(object)); }
+};
+
/** Type of reference (#SLE_REF, #SLE_CONDREF). */
enum SLRefType {
REF_ORDER = 0, ///< Load/save a reference to an order.
@@ -501,10 +568,12 @@ enum SaveLoadType : byte {
SL_REFLIST = 4, ///< Save/load a list of #SL_REF elements.
SL_DEQUE = 5, ///< Save/load a deque of #SL_VAR elements.
SL_STDSTR = 6, ///< Save/load a \c std::string.
+ SL_STRUCT = 7, ///< Save/load a struct.
+ SL_STRUCTLIST = 8, ///< Save/load a list of structs.
/* non-normal save-load types */
- SL_WRITEBYTE = 8,
- SL_VEH_INCLUDE = 9,
- SL_ST_INCLUDE = 10,
+ SL_WRITEBYTE = 9,
+ SL_VEH_INCLUDE = 10,
+ SL_ST_INCLUDE = 11,
};
typedef void *SaveLoadAddrProc(void *base, size_t extra);
@@ -519,11 +588,9 @@ struct SaveLoad {
size_t size; ///< the sizeof size.
SaveLoadAddrProc *address_proc; ///< callback proc the get the actual variable address in memory
size_t extra_data; ///< extra data for the callback proc
+ SaveLoadHandler *handler; ///< Custom handler for Save/Load procs.
};
-/** A table of SaveLoad entries. */
-using SaveLoadTable = span<const SaveLoad>;
-
/**
* Storage of simple variables, references (pointers), and arrays.
* @param cmd Load/save type. @see SaveLoadType
@@ -535,7 +602,7 @@ using SaveLoadTable = span<const SaveLoad>;
* @param extra Extra data to pass to the address callback function.
* @note In general, it is better to use one of the SLE_* macros below.
*/
-#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) {cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast<void *>(static_cast<const void *>(std::addressof(static_cast<base *>(b)->variable))); }, extra}
+#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) {cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast<void *>(static_cast<const void *>(std::addressof(static_cast<base *>(b)->variable))); }, extra, nullptr}
/**
* Storage of a variable in some savegame versions.
@@ -671,13 +738,13 @@ using SaveLoadTable = span<const SaveLoad>;
* @param from First savegame version that has the empty space.
* @param to Last savegame version that has the empty space.
*/
-#define SLE_CONDNULL(length, from, to) {SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0}
+#define SLE_CONDNULL(length, from, to) {SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr}
/** Translate values ingame to different values in the savegame and vv. */
#define SLE_WRITEBYTE(base, variable) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, 0)
-#define SLE_VEH_INCLUDE() {SL_VEH_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, 0, [] (void *b, size_t) { return b; }, 0}
-#define SLE_ST_INCLUDE() {SL_ST_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, 0, [] (void *b, size_t) { return b; }, 0}
+#define SLE_VEH_INCLUDE() {SL_VEH_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, 0, [] (void *b, size_t) { return b; }, 0, nullptr}
+#define SLE_ST_INCLUDE() {SL_ST_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, 0, [] (void *b, size_t) { return b; }, 0, nullptr}
/**
* Storage of global simple variables, references (pointers), and arrays.
@@ -689,7 +756,7 @@ using SaveLoadTable = span<const SaveLoad>;
* @param extra Extra data to pass to the address callback function.
* @note In general, it is better to use one of the SLEG_* macros below.
*/
-#define SLEG_GENERAL(cmd, variable, type, length, from, to, extra) {cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast<void *>(std::addressof(variable)); }, extra}
+#define SLEG_GENERAL(cmd, variable, type, length, from, to, extra) {cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast<void *>(std::addressof(variable)); }, extra, nullptr}
/**
* Storage of a global variable in some savegame versions.
@@ -739,6 +806,14 @@ using SaveLoadTable = span<const SaveLoad>;
#define SLEG_CONDSSTR(variable, type, from, to) SLEG_GENERAL(SL_STDSTR, variable, type, 0, from, to, 0)
/**
+ * Storage of a structs in some savegame versions.
+ * @param handler SaveLoadHandler for the structs.
+ * @param from First savegame version that has the struct.
+ * @param to Last savegame version that has the struct.
+ */
+#define SLEG_CONDSTRUCT(handler, from, to) {SL_STRUCT, 0, 0, from, to, 0, nullptr, 0, new handler()}
+
+/**
* Storage of a global reference list in some savegame versions.
* @param variable Name of the global variable.
* @param type Storage of the data in memory and in the savegame.
@@ -748,6 +823,14 @@ using SaveLoadTable = span<const SaveLoad>;
#define SLEG_CONDREFLIST(variable, type, from, to) SLEG_GENERAL(SL_REFLIST, variable, type, 0, from, to, 0)
/**
+ * Storage of a list of structs in some savegame versions.
+ * @param handler SaveLoadHandler for the list of structs.
+ * @param from First savegame version that has the list.
+ * @param to Last savegame version that has the list.
+ */
+#define SLEG_CONDSTRUCTLIST(handler, from, to) {SL_STRUCTLIST, 0, 0, from, to, 0, nullptr, 0, new handler()}
+
+/**
* Storage of a global variable in every savegame version.
* @param variable Name of the global variable.
* @param type Storage of the data in memory and in the savegame.
@@ -783,6 +866,12 @@ using SaveLoadTable = span<const SaveLoad>;
#define SLEG_SSTR(variable, type) SLEG_CONDSSTR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
+ * Storage of a structs in every savegame version.
+ * @param handler SaveLoadHandler for the structs.
+ */
+#define SLEG_STRUCT(handler) SLEG_CONDSTRUCT(handler, SL_MIN_VERSION, SL_MAX_VERSION)
+
+/**
* Storage of a global reference list in every savegame version.
* @param variable Name of the global variable.
* @param type Storage of the data in memory and in the savegame.
@@ -790,12 +879,18 @@ using SaveLoadTable = span<const SaveLoad>;
#define SLEG_REFLIST(variable, type) SLEG_CONDREFLIST(variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
+ * Storage of a list of structs in every savegame version.
+ * @param handler SaveLoadHandler for the list of structs.
+ */
+#define SLEG_STRUCTLIST(handler) SLEG_CONDSTRUCTLIST(handler, SL_MIN_VERSION, SL_MAX_VERSION)
+
+/**
* Empty global space in some savegame versions.
* @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.
*/
-#define SLEG_CONDNULL(length, from, to) {SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0}
+#define SLEG_CONDNULL(length, from, to) {SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, 0, nullptr, 0, nullptr}
/**
* Checks whether the savegame is below \a major.\a minor.