diff --git a/src/sl/saveload.h b/src/sl/saveload.h index f61ff44d84..2961721ea8 100644 --- a/src/sl/saveload.h +++ b/src/sl/saveload.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -275,6 +276,24 @@ static inline constexpr VarType GetVarFileType(VarType type) return type & 0xF; // GB(type, 0, 4); } +template class> +struct sl_is_instance : public std::false_type {}; + +template class U> +struct sl_is_instance, U> : public std::true_type {}; + +template class T, class U> +struct sl_is_derived_from_array +{ +private: + template + static decltype(static_cast>(std::declval()), std::true_type{}) test(const T&); + static std::false_type test(...); + +public: + static constexpr bool value = decltype(sl_is_derived_from_array::test(std::declval()))::value; +}; + /** * Return expect size in bytes of a VarType * @param type VarType to get size of. @@ -304,11 +323,47 @@ static inline constexpr size_t SlVarSize(VarType type) } } -template class> -struct sl_is_instance : public std::false_type {}; +/** + * Check whether the variable size/type of the variable in the saveload configuration + * matches with the actual variable size, for primitive types. + */ +template +static inline constexpr bool SlCheckPrimitiveTypeVar(VarType type) +{ + using T = typename std::remove_reference::type; -template class U> -struct sl_is_instance, U> : public std::true_type {}; + if (GetVarMemType(type) == SLE_VAR_NAME) { + return std::is_same_v; + } + if (GetVarMemType(type) == SLE_VAR_CNAME) { + return std::is_same_v || std::is_same_v || std::is_same_v; + } + if (!std::is_integral_v && !std::is_enum_v && !sl_is_instance{}) return false; + return sizeof(T) == SlVarSize(type); +} + +/** + * Check whether the variable size/type of the variable in the saveload configuration + * matches with the actual variable size, for array types. + */ +template +static inline constexpr bool SlCheckArrayTypeVar(VarType type, size_t length, bool top_level) +{ + using T = typename std::remove_reference::type; + + if constexpr (std::is_array_v) { + return SlCheckPrimitiveTypeVar>(type); + } + if constexpr (sl_is_derived_from_array::value) { + return SlCheckArrayTypeVar(type, length, false); + } + if constexpr (std::is_class_v) { + /* If T is class/struct, assume that the array is writing all its members, so check that the total size matches. + * It's impractical to check the actual struct/class fields. */ + if (top_level && sizeof(T) == length) return true; + } + return SlCheckPrimitiveTypeVar(type); +} /** * Check whether the variable size/type of the variable in the saveload configuration @@ -321,7 +376,7 @@ static inline constexpr bool SlCheckVar(SaveLoadType cmd, VarType type, size_t l switch (cmd) { case SL_VAR: - return sizeof(T) == SlVarSize(type); + return SlCheckPrimitiveTypeVar(type); case SL_REF: /* These should all be pointer sized. */ @@ -340,7 +395,7 @@ static inline constexpr bool SlCheckVar(SaveLoadType cmd, VarType type, size_t l case SL_ARR: /* Partial load of array is permitted. */ - return sizeof(T) >= SlVarSize(type) * length; + return SlCheckArrayTypeVar(type, SlVarSize(type) * length, true) && sizeof(T) >= SlVarSize(type) * length; case SL_REFLIST: if constexpr (sl_is_instance{}) { @@ -362,13 +417,13 @@ static inline constexpr bool SlCheckVar(SaveLoadType cmd, VarType type, size_t l case SL_RING: if constexpr (sl_is_instance{}) { - return sizeof(typename T::value_type) == SlVarSize(type); + return SlCheckPrimitiveTypeVar(type); } return false; case SL_VARVEC: if constexpr (sl_is_instance{}) { - return sizeof(typename T::value_type) == SlVarSize(type); + return SlCheckPrimitiveTypeVar(type); } return false;