diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index fbaf207eaf..a94bbf785f 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -1078,6 +1078,18 @@ static inline size_t SlCalcNetStringLen(const char *ptr, size_t length) return min(strlen(ptr), length - 1); } +/** + * Calculate the gross length of the std::string that it + * will occupy in the savegame. This includes the real length, + * and the length that the index will occupy. + * @param str reference to the std::string + * @return return the gross length of the string + */ +static inline size_t SlCalcStdStrLen(const std::string &str) +{ + return str.size() + SlGetArrayLength(str.size()); // also include the length of the index +} + /** * Calculate the gross length of the string that it * will occupy in the savegame. This includes the real length, returned @@ -1189,6 +1201,41 @@ static void SlString(void *ptr, size_t length, VarType conv) } } +/** + * Save/Load a std::string. + * @param ptr the std::string being manipulated + * @param conv must be SLE_FILE_STRING + */ +static void SlStdString(std::string &str, VarType conv) +{ + switch (_sl.action) { + case SLA_SAVE: { + SlWriteArrayLength(str.size()); + SlCopyBytes(const_cast(str.data()), str.size()); + break; + } + case SLA_LOAD_CHECK: + case SLA_LOAD: { + size_t len = SlReadArrayLength(); + str.resize(len); + SlCopyBytes(const_cast(str.c_str()), len); + + StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK; + if ((conv & SLF_ALLOW_CONTROL) != 0) { + settings = settings | SVS_ALLOW_CONTROL_CODE; + } + if ((conv & SLF_ALLOW_NEWLINE) != 0) { + settings = settings | SVS_ALLOW_NEWLINE; + } + str_validate(str, settings); + break; + } + case SLA_PTRS: break; + case SLA_NULL: break; + default: NOT_REACHED(); + } +} + /** * Return the size in bytes of a certain type of atomic array * @param length The length of the array counted in elements @@ -1491,6 +1538,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_LST: case SL_DEQ: case SL_VEC: + case SL_STDSTR: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) break; @@ -1502,6 +1550,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_LST: return SlCalcListLen>(GetVariableAddress(object, sld)); case SL_DEQ: return SlCalcListLen>(GetVariableAddress(object, sld)); case SL_VEC: return SlCalcListLen>(GetVariableAddress(object, sld)); + case SL_STDSTR: return SlCalcStdStrLen(*static_cast(GetVariableAddress(object, sld))); default: NOT_REACHED(); } break; @@ -1550,6 +1599,9 @@ static bool IsVariableSizeRight(const SaveLoad *sld) /* These should be pointer sized, or fixed array. */ return sld->size == sizeof(void *) || sld->size == sld->length; + case SL_STDSTR: + return sld->size == sizeof(std::string); + default: return true; } @@ -1572,6 +1624,7 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_LST: case SL_DEQ: case SL_VEC: + case SL_STDSTR: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) return false; if (SlSkipVariableOnLoad(sld)) return false; @@ -1601,6 +1654,7 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_LST: SlList>(ptr, (SLRefType)conv); break; case SL_DEQ: SlList>(ptr, (SLRefType)conv); break; case SL_VEC: SlList>(ptr, (SLRefType)conv); break; + case SL_STDSTR: SlStdString(*static_cast(ptr), sld->conv); break; default: NOT_REACHED(); } break; diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index a4e6a69a7d..45968bf7ff 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -207,6 +207,7 @@ enum SaveLoadTypes { SL_LST = 4, ///< Save/load a list. SL_DEQ = 5, ///< Save/load a deque. SL_VEC = 6, ///< Save/load a vector. + SL_STDSTR = 7, ///< Save/load a std::string. /* non-normal save-load types */ SL_WRITEBYTE = 8, SL_VEH_INCLUDE = 9, @@ -300,6 +301,18 @@ typedef SaveLoad SaveLoadGlobVarList; #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, SlXvFeatureTest()) +/** + * Storage of a std::string in some savegame versions. + * @param base Name of the class or struct containing the string. + * @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 from First savegame version that has the string. + * @param to Last savegame version that has the string. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDSTDSTR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_STDSTR, base, variable, type, 0, from, to, extver) +#define SLE_CONDSTDSTR(base, variable, type, from, to) SLE_CONDSTDSTR_X(base, variable, type, from, to, SlXvFeatureTest()) + /** * Storage of a list in some savegame versions. * @param base Name of the class or struct containing the list. @@ -370,6 +383,14 @@ typedef SaveLoad SaveLoadGlobVarList; */ #define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, 0, SL_MAX_VERSION) +/** + * Storage of a std::string in every savegame version. + * @param base Name of the class or struct containing the string. + * @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. + */ +#define SLE_STDSTR(base, variable, type) SLE_CONDSTDSTR(base, variable, type, 0, SL_MAX_VERSION) + /** * Storage of a list in every savegame version. * @param base Name of the class or struct containing the list. diff --git a/src/string.cpp b/src/string.cpp index 1ceae6061b..343250a979 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -180,14 +180,7 @@ void str_fix_scc_encoded(char *str, const char *last) } -/** - * Scans the string for valid characters and if it finds invalid ones, - * replaces them with a question mark '?' (if not ignored) - * @param str the string to validate - * @param last the last valid character of str - * @param settings the settings for the string validation. - */ -void str_validate(char *str, const char *last, StringValidationSettings settings) +char *str_validate_intl(char *str, const char *last, StringValidationSettings settings) { /* Assume the ABSOLUTE WORST to be in str as it comes from the outside. */ @@ -228,7 +221,7 @@ void str_validate(char *str, const char *last, StringValidationSettings settings } } - *dst = '\0'; + return dst; } /** diff --git a/src/string_func.h b/src/string_func.h index dd9b42600d..70214ff8d1 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -27,6 +27,7 @@ #define STRING_FUNC_H #include +#include #include "core/bitmath_func.hpp" #include "string_type.h" @@ -41,7 +42,27 @@ int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap) char *CDECL str_fmt(const char *str, ...) WARN_FORMAT(1, 2); char *str_vfmt(const char *str, va_list ap); -void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); +char *str_validate_intl(char *str, const char *last, StringValidationSettings settings); + +/** + * Scans the string for valid characters and if it finds invalid ones, + * replaces them with a question mark '?' (if not ignored) + * @param str the string to validate + * @param last the last valid character of str + * @param settings the settings for the string validation. + */ +static inline void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK) +{ + *str_validate_intl(str, last, settings) = '\0'; +} + +static inline void str_validate(std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK) +{ + if (str.empty()) return; + char *buf = const_cast(str.c_str()); + str.resize(str_validate_intl(buf, buf + str.size(), settings) - buf); +} + void ValidateString(const char *str); void str_fix_scc_encoded(char *str, const char *last);