diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 6f49cda7ed..6ac242cb70 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -741,6 +741,54 @@ static inline constexpr bool IsNumericType(VarType conv) return GetVarMemType(conv) <= SLE_VAR_U64; } +/** + * Return expect size in bytes of a VarType + * @param type VarType to get size of. + * @return size of type in bytes. + */ +static inline constexpr size_t SlVarSize(VarType type) +{ + switch (GetVarMemType(type)) { + case SLE_VAR_BL: return sizeof(bool); + case SLE_VAR_I8: return sizeof(int8_t); + case SLE_VAR_U8: return sizeof(uint8_t); + case SLE_VAR_I16: return sizeof(int16_t); + case SLE_VAR_U16: return sizeof(uint16_t); + case SLE_VAR_I32: return sizeof(int32_t); + case SLE_VAR_U32: return sizeof(uint32_t); + case SLE_VAR_I64: return sizeof(int64_t); + case SLE_VAR_U64: return sizeof(uint64_t); + case SLE_VAR_NULL: return sizeof(void *); + case SLE_VAR_STR: return sizeof(std::string); + case SLE_VAR_STRQ: return sizeof(std::string); + case SLE_VAR_NAME: return sizeof(std::string); + default: NOT_REACHED(); + } +} + +/** + * Check if a saveload cmd/type/length entry matches the size of the variable. + * @param cmd SaveLoadType of entry. + * @param type VarType of entry. + * @param length Array length of entry. + * @param size Actual size of variable. + * @return true iff the sizes match. + */ +static inline constexpr bool SlCheckVarSize(SaveLoadType cmd, VarType type, size_t length, size_t size) +{ + switch (cmd) { + case SL_VAR: return SlVarSize(type) == size; + case SL_REF: return sizeof(void *) == size; + case SL_STDSTR: return SlVarSize(type) == size; + case SL_ARR: return SlVarSize(type) * length <= size; // Partial load of array is permitted. + case SL_DEQUE: return sizeof(std::deque) == size; + case SL_VECTOR: return sizeof(std::vector) == size; + case SL_REFLIST: return sizeof(std::list) == size; + case SL_SAVEBYTE: return true; + default: NOT_REACHED(); + } +} + /** * Storage of simple variables, references (pointers), and arrays. * @param cmd Load/save type. @see SaveLoadType @@ -748,12 +796,18 @@ static inline constexpr bool IsNumericType(VarType conv) * @param base Name of the class or struct containing the variable. * @param variable Name of the variable in the class or struct referenced by \a base. * @param type Storage of the data in memory and in the savegame. + * @param length Number of elements in the array. * @param from First savegame version that has the field. * @param to Last savegame version that has the field. * @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_NAME(cmd, name, base, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast(static_cast(std::addressof(static_cast(b)->variable))); }, extra, nullptr} +#define SLE_GENERAL_NAME(cmd, name, base, variable, type, length, from, to, extra) \ + SaveLoad {name, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { \ + static_assert(SlCheckVarSize(cmd, type, length, sizeof(static_cast(b)->variable))); \ + assert(b != nullptr); \ + return const_cast(static_cast(std::addressof(static_cast(b)->variable))); \ + }, extra, nullptr} /** * Storage of simple variables, references (pointers), and arrays with a custom name. @@ -761,6 +815,7 @@ static inline constexpr bool IsNumericType(VarType conv) * @param base Name of the class or struct containing the variable. * @param variable Name of the variable in the class or struct referenced by \a base. * @param type Storage of the data in memory and in the savegame. + * @param length Number of elements in the array. * @param from First savegame version that has the field. * @param to Last savegame version that has the field. * @param extra Extra data to pass to the address callback function. @@ -966,7 +1021,10 @@ static inline constexpr bool IsNumericType(VarType conv) * @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(name, cmd, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast(std::addressof(variable)); }, extra, nullptr} +#define SLEG_GENERAL(name, cmd, variable, type, length, from, to, extra) \ + SaveLoad {name, cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { \ + static_assert(SlCheckVarSize(cmd, type, length, sizeof(variable))); \ + return static_cast(std::addressof(variable)); }, extra, nullptr} /** * Storage of a global variable in some savegame versions.