diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/game/game_text.cpp | 120 | ||||
-rw-r--r-- | src/game/game_text.hpp | 29 | ||||
-rw-r--r-- | src/saveload/game_sl.cpp | 22 | ||||
-rw-r--r-- | src/saveload/saveload.cpp | 71 | ||||
-rw-r--r-- | src/saveload/saveload.h | 35 |
5 files changed, 184 insertions, 93 deletions
diff --git a/src/game/game_text.cpp b/src/game/game_text.cpp index 64281c60c..39732f789 100644 --- a/src/game/game_text.cpp +++ b/src/game/game_text.cpp @@ -59,69 +59,46 @@ void NORETURN CDECL strgen_fatal(const char *s, ...) } /** - * Create a new container for language strings. - * @param language The language name. - * @param end If not nullptr, terminate \a language at this position. - */ -LanguageStrings::LanguageStrings(const char *language, const char *end) -{ - this->language = stredup(language, end != nullptr ? end - 1 : nullptr); -} - -/** Free everything. */ -LanguageStrings::~LanguageStrings() -{ - free(this->language); -} - -/** * Read all the raw language strings from the given file. * @param file The file to read from. * @return The raw strings, or nullptr upon error. */ -std::unique_ptr<LanguageStrings> ReadRawLanguageStrings(const char *file) +LanguageStrings ReadRawLanguageStrings(const std::string &file) { - try { - size_t to_read; - FILE *fh = FioFOpenFile(file, "rb", GAME_DIR, &to_read); - if (fh == nullptr) return nullptr; + size_t to_read; + FILE *fh = FioFOpenFile(file.c_str(), "rb", GAME_DIR, &to_read); + if (fh == nullptr) return LanguageStrings(); - FileCloser fhClose(fh); + FileCloser fhClose(fh); - const char *langname = strrchr(file, PATHSEPCHAR); - if (langname == nullptr) { - langname = file; - } else { - langname++; - } + auto pos = file.rfind(PATHSEPCHAR); + if (pos == std::string::npos) return LanguageStrings(); + std::string langname = file.substr(pos + 1); - /* Check for invalid empty filename */ - if (*langname == '.' || *langname == 0) return nullptr; + /* Check for invalid empty filename */ + if (langname.empty() || langname.front() == '.') return LanguageStrings(); - std::unique_ptr<LanguageStrings> ret(new LanguageStrings(langname, strchr(langname, '.'))); + LanguageStrings ret(langname.substr(0, langname.find('.'))); - char buffer[2048]; - while (to_read != 0 && fgets(buffer, sizeof(buffer), fh) != nullptr) { - size_t len = strlen(buffer); + char buffer[2048]; + while (to_read != 0 && fgets(buffer, sizeof(buffer), fh) != nullptr) { + size_t len = strlen(buffer); - /* Remove trailing spaces/newlines from the string. */ - size_t i = len; - while (i > 0 && (buffer[i - 1] == '\r' || buffer[i - 1] == '\n' || buffer[i - 1] == ' ')) i--; - buffer[i] = '\0'; + /* Remove trailing spaces/newlines from the string. */ + size_t i = len; + while (i > 0 && (buffer[i - 1] == '\r' || buffer[i - 1] == '\n' || buffer[i - 1] == ' ')) i--; + buffer[i] = '\0'; - ret->lines.emplace_back(buffer, i); + ret.lines.emplace_back(buffer, i); - if (len > to_read) { - to_read = 0; - } else { - to_read -= len; - } + if (len > to_read) { + to_read = 0; + } else { + to_read -= len; } - - return ret; - } catch (...) { - return nullptr; } + + return ret; } @@ -138,7 +115,7 @@ struct StringListReader : StringReader { * @param translation Are we reading a translation? */ StringListReader(StringData &data, const LanguageStrings &strings, bool master, bool translation) : - StringReader(data, strings.language, master, translation), p(strings.lines.begin()), end(strings.lines.end()) + StringReader(data, strings.language.c_str(), master, translation), p(strings.lines.begin()), end(strings.lines.end()) { } @@ -215,12 +192,11 @@ struct StringNameWriter : HeaderWriter { class LanguageScanner : protected FileScanner { private: GameStrings *gs; - char *exclude; + std::string exclude; public: /** Initialise */ - LanguageScanner(GameStrings *gs, const char *exclude) : gs(gs), exclude(stredup(exclude)) {} - ~LanguageScanner() { free(exclude); } + LanguageScanner(GameStrings *gs, const std::string &exclude) : gs(gs), exclude(exclude) {} /** * Scan. @@ -232,10 +208,10 @@ public: bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename) override { - if (strcmp(filename, exclude) == 0) return true; + if (exclude == filename) return true; auto ls = ReadRawLanguageStrings(filename); - if (ls == nullptr) return false; + if (!ls.IsValid()) return false; gs->raw_strings.push_back(std::move(ls)); return true; @@ -249,17 +225,16 @@ public: GameStrings *LoadTranslations() { const GameInfo *info = Game::GetInfo(); - char filename[512]; - strecpy(filename, info->GetMainScript(), lastof(filename)); - char *e = strrchr(filename, PATHSEPCHAR); - if (e == nullptr) return nullptr; - e++; // Make 'e' point after the PATHSEPCHAR + std::string basename(info->GetMainScript()); + auto e = basename.rfind(PATHSEPCHAR); + if (e == std::string::npos) return nullptr; + basename.erase(e + 1); - strecpy(e, "lang" PATHSEP "english.txt", lastof(filename)); - if (!FioCheckFileExists(filename, GAME_DIR)) return nullptr; + std::string filename = basename + "lang" PATHSEP "english.txt"; + if (!FioCheckFileExists(filename.c_str() , GAME_DIR)) return nullptr; auto ls = ReadRawLanguageStrings(filename); - if (ls == nullptr) return nullptr; + if (!ls.IsValid()) return nullptr; GameStrings *gs = new GameStrings(); try { @@ -267,8 +242,7 @@ GameStrings *LoadTranslations() /* Scan for other language files */ LanguageScanner scanner(gs, filename); - strecpy(e, "lang" PATHSEP, lastof(filename)); - size_t len = strlen(filename); + std::string ldir = basename + "lang" PATHSEP; const char *tar_filename = info->GetTarFile(); TarList::iterator iter; @@ -281,14 +255,14 @@ GameStrings *LoadTranslations() if (tar->second.tar_filename != iter->first) continue; /* Check the path and extension. */ - if (tar->first.size() <= len || tar->first.compare(0, len, filename) != 0) continue; + if (tar->first.size() <= ldir.size() || tar->first.compare(0, ldir.size(), ldir) != 0) continue; if (tar->first.compare(tar->first.size() - 4, 4, ".txt") != 0) continue; scanner.AddFile(tar->first.c_str(), 0, tar_filename); } } else { /* Scan filesystem */ - scanner.Scan(filename); + scanner.Scan(ldir.c_str()); } gs->Compile(); @@ -303,7 +277,7 @@ GameStrings *LoadTranslations() void GameStrings::Compile() { StringData data(32); - StringListReader master_reader(data, *this->raw_strings[0], true, false); + StringListReader master_reader(data, this->raw_strings[0], true, false); master_reader.ParseFile(); if (_errors != 0) throw std::exception(); @@ -314,12 +288,12 @@ void GameStrings::Compile() for (const auto &p : this->raw_strings) { data.FreeTranslation(); - StringListReader translation_reader(data, *p, false, strcmp(p->language, "english") != 0); + StringListReader translation_reader(data, p, false, p.language != "english"); translation_reader.ParseFile(); if (_errors != 0) throw std::exception(); - this->compiled_strings.emplace_back(new LanguageStrings(p->language)); - TranslationWriter writer(this->compiled_strings.back()->lines); + this->compiled_strings.emplace_back(p.language); + TranslationWriter writer(this->compiled_strings.back().lines); writer.WriteLang(data); } } @@ -387,11 +361,11 @@ void ReconsiderGameScriptLanguage() language++; for (auto &p : _current_data->compiled_strings) { - if (strcmp(p->language, language) == 0) { - _current_data->cur_language = p; + if (p.language == language) { + _current_data->cur_language = &p; return; } } - _current_data->cur_language = _current_data->compiled_strings[0]; + _current_data->cur_language = &_current_data->compiled_strings[0]; } diff --git a/src/game/game_text.hpp b/src/game/game_text.hpp index 20cccff54..91d85847d 100644 --- a/src/game/game_text.hpp +++ b/src/game/game_text.hpp @@ -18,23 +18,34 @@ void ReconsiderGameScriptLanguage(); /** Container for the raw (unencoded) language strings of a language. */ struct LanguageStrings { - const char *language; ///< Name of the language (base filename). - StringList lines; ///< The lines of the file to pass into the parser/encoder. + std::string language; ///< Name of the language (base filename). Empty string if invalid. + StringList lines; ///< The lines of the file to pass into the parser/encoder. - LanguageStrings(const char *language, const char *end = nullptr); - ~LanguageStrings(); + LanguageStrings() {} + LanguageStrings(const std::string &lang) : language(lang) {} + LanguageStrings(const LanguageStrings &other) : language(other.language), lines(other.lines) {} + LanguageStrings(LanguageStrings &&other) : language(std::move(other.language)), lines(std::move(other.lines)) {} + + bool IsValid() const { return !this->language.empty(); } }; /** Container for all the game strings. */ struct GameStrings { - uint version; ///< The version of the language strings. - std::shared_ptr<LanguageStrings> cur_language; ///< The current (compiled) language. + uint version; ///< The version of the language strings. + LanguageStrings *cur_language; ///< The current (compiled) language. - std::vector<std::unique_ptr<LanguageStrings>> raw_strings; ///< The raw strings per language, first must be English/the master language!. - std::vector<std::shared_ptr<LanguageStrings>> compiled_strings; ///< The compiled strings per language, first must be English/the master language!. - StringList string_names; ///< The names of the compiled strings. + std::vector<LanguageStrings> raw_strings; ///< The raw strings per language, first must be English/the master language!. + std::vector<LanguageStrings> compiled_strings; ///< The compiled strings per language, first must be English/the master language!. + StringList string_names; ///< The names of the compiled strings. void Compile(); + + GameStrings() = default; + + GameStrings(const GameStrings &) = delete; + GameStrings(GameStrings &&) = delete; + GameStrings &operator=(const GameStrings &) = delete; + GameStrings &operator=(GameStrings &&) = delete; }; #endif /* GAME_TEXT_HPP */ diff --git a/src/saveload/game_sl.cpp b/src/saveload/game_sl.cpp index 28a6c6c11..e13484385 100644 --- a/src/saveload/game_sl.cpp +++ b/src/saveload/game_sl.cpp @@ -113,23 +113,23 @@ static void Save_GSDT() extern GameStrings *_current_data; -static const char *_game_saveload_string; +static std::string _game_saveload_string; static uint _game_saveload_strings; static const SaveLoad _game_language_header[] = { - SLEG_STR(_game_saveload_string, SLE_STR), - SLEG_VAR(_game_saveload_strings, SLE_UINT32), - SLE_END() + SLEG_SSTR(_game_saveload_string, SLE_STR), + SLEG_VAR(_game_saveload_strings, SLE_UINT32), + SLE_END() }; static const SaveLoad _game_language_string[] = { - SLEG_STR(_game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL), - SLE_END() + SLEG_SSTR(_game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL), + SLE_END() }; static void SaveReal_GSTR(const LanguageStrings *ls) { - _game_saveload_string = ls->language; + _game_saveload_string = ls->language.c_str(); _game_saveload_strings = (uint)ls->lines.size(); SlObject(nullptr, _game_language_header); @@ -145,13 +145,13 @@ static void Load_GSTR() _current_data = new GameStrings(); while (SlIterateArray() != -1) { - _game_saveload_string = nullptr; + _game_saveload_string.clear(); SlObject(nullptr, _game_language_header); - std::unique_ptr<LanguageStrings> ls(new LanguageStrings(_game_saveload_string != nullptr ? _game_saveload_string : "")); + LanguageStrings ls(_game_saveload_string); for (uint i = 0; i < _game_saveload_strings; i++) { SlObject(nullptr, _game_language_string); - ls->lines.emplace_back(_game_saveload_string != nullptr ? _game_saveload_string : ""); + ls.lines.emplace_back(_game_saveload_string); } _current_data->raw_strings.push_back(std::move(ls)); @@ -174,7 +174,7 @@ static void Save_GSTR() for (uint i = 0; i < _current_data->raw_strings.size(); i++) { SlSetArrayIndex(i); - SlAutolength((AutolengthProc *)SaveReal_GSTR, _current_data->raw_strings[i].get()); + SlAutolength((AutolengthProc *)SaveReal_GSTR, &_current_data->raw_strings[i]); } } diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index f9eebed46..7a41f4c40 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -44,6 +44,7 @@ #include "../fios.h" #include "../error.h" #include <atomic> +#include <string> #include "table/strings.h" @@ -902,6 +903,21 @@ static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType con } /** + * Calculate the gross length of the string that it + * will occupy in the savegame. This includes the real length, returned + * by SlCalcNetStringLen and the length that the index will occupy. + * @param ptr Pointer to the \c std::string. + * @return The gross length of the string. + */ +static inline size_t SlCalcStdStringLen(const void *ptr) +{ + const std::string *str = reinterpret_cast<const std::string *>(ptr); + + size_t len = str->length(); + return len + SlGetArrayLength(len); // also include the length of the index +} + +/** * Save/Load a string. * @param ptr the string being manipulated * @param length of the string (full length) @@ -981,6 +997,53 @@ static void SlString(void *ptr, size_t length, VarType conv) } /** + * Save/Load a \c std::string. + * @param ptr the string being manipulated + * @param conv must be SLE_FILE_STRING + */ +static void SlStdString(void *ptr, VarType conv) +{ + std::string *str = reinterpret_cast<std::string *>(ptr); + + switch (_sl.action) { + case SLA_SAVE: { + size_t len = str->length(); + SlWriteArrayLength(len); + SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->c_str())), len); + break; + } + + case SLA_LOAD_CHECK: + case SLA_LOAD: { + size_t len = SlReadArrayLength(); + char *buf = AllocaM(char, len + 1); + + SlCopyBytes(buf, len); + buf[len] = '\0'; // properly terminate the string + + StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK; + if ((conv & SLF_ALLOW_CONTROL) != 0) { + settings = settings | SVS_ALLOW_CONTROL_CODE; + if (IsSavegameVersionBefore(SLV_169)) { + str_fix_scc_encoded(buf, buf + len); + } + } + if ((conv & SLF_ALLOW_NEWLINE) != 0) { + settings = settings | SVS_ALLOW_NEWLINE; + } + str_validate(buf, buf + len, settings); + + // Store sanitized string. + str->assign(buf); + } + + 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 * @param conv VarType type of the variable that is used in calculating the size @@ -1403,6 +1466,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_STR: case SL_LST: case SL_DEQUE: + case SL_STDSTR: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) break; @@ -1413,6 +1477,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv); case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld)); case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld->conv); + case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld)); default: NOT_REACHED(); } break; @@ -1461,6 +1526,10 @@ 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: + /* These should be all pointers to std::string. */ + return sld->size == sizeof(std::string); + default: return true; } @@ -1482,6 +1551,7 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_STR: case SL_LST: case SL_DEQUE: + case SL_STDSTR: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) return false; if (SlSkipVariableOnLoad(sld)) return false; @@ -1510,6 +1580,7 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_STR: SlString(ptr, sld->length, sld->conv); break; case SL_LST: SlList(ptr, (SLRefType)conv); break; case SL_DEQUE: SlDeque(ptr, conv); break; + case SL_STDSTR: SlStdString(ptr, sld->conv); break; default: NOT_REACHED(); } break; diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 5065e568b..01a074bd8 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -485,6 +485,7 @@ enum SaveLoadTypes { SL_STR = 3, ///< Save/load a string. SL_LST = 4, ///< Save/load a list. SL_DEQUE = 5, ///< Save/load a deque. + SL_STDSTR = 6, ///< Save/load a \c std::string. /* non-normal save-load types */ SL_WRITEBYTE = 8, SL_VEH_INCLUDE = 9, @@ -568,6 +569,16 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to) /** + * Storage of a \c 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. + */ +#define SLE_CONDSSTR(base, variable, type, from, to) SLE_GENERAL(SL_STDSTR, base, variable, type, 0, from, to) + +/** * Storage of a list in some savegame versions. * @param base Name of the class or struct containing the list. * @param variable Name of the variable in the class or struct referenced by \a base. @@ -622,6 +633,14 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, SL_MIN_VERSION, SL_MAX_VERSION) /** + * Storage of a \c 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_SSTR(base, variable, type) SLE_CONDSSTR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** * Storage of a list in every savegame version. * @param base Name of the class or struct containing the list. * @param variable Name of the variable in the class or struct referenced by \a base. @@ -702,6 +721,15 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLEG_CONDSTR(variable, type, length, from, to) SLEG_GENERAL(SL_STR, variable, type, length, from, to) /** + * Storage of a global \c std::string in some savegame versions. + * @param variable Name of the global variable. + * @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. + */ +#define SLEG_CONDSSTR(variable, type, from, to) SLEG_GENERAL(SL_STDSTR, variable, type, 0, from, to) + +/** * Storage of a global list in some savegame versions. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. @@ -739,6 +767,13 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, sizeof(variable), SL_MIN_VERSION, SL_MAX_VERSION) /** + * Storage of a global \c std::string in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_SSTR(variable, type) SLEG_CONDSSTR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + +/** * Storage of a global list in every savegame version. * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. |