From 024a3f62593fd783285c5ff09b8c85ee1fc34e2d Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 6 Dec 2020 21:11:48 +0100 Subject: Codechange: Use automatic memory management for language pack reading. --- src/fileio.cpp | 29 ++++++------- src/fileio_func.h | 2 +- src/language.h | 2 +- src/string.cpp | 2 +- src/strings.cpp | 102 ++++++++++++++++++++------------------------ src/table/misc_settings.ini | 6 +-- 6 files changed, 66 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/fileio.cpp b/src/fileio.cpp index 5b8005929..5431df814 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -1236,28 +1236,27 @@ void SanitizeFilename(char *filename) * @return Pointer to new memory containing the loaded data, or \c nullptr if loading failed. * @note If \a maxsize less than the length of the file, loading fails. */ -void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize) +std::unique_ptr ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize) { - FILE *in = fopen(filename, "rb"); + FILE *in = fopen(filename.c_str(), "rb"); if (in == nullptr) return nullptr; + FileCloser fc(in); + fseek(in, 0, SEEK_END); size_t len = ftell(in); fseek(in, 0, SEEK_SET); - if (len > maxsize) { - fclose(in); - return nullptr; - } - byte *mem = MallocT(len + 1); - mem[len] = 0; - if (fread(mem, len, 1, in) != 1) { - fclose(in); - free(mem); - return nullptr; - } - fclose(in); + if (len > maxsize) return nullptr; + + /* std::unique_ptr assumes new/delete unless a custom deleter is supplied. + * As we don't want to have to carry that deleter all over the place, use + * new directly to allocate the memory instead of malloc. */ + std::unique_ptr mem(static_cast(::operator new(len + 1))); + + mem.get()[len] = 0; + if (fread(mem.get(), len, 1, in) != 1) return nullptr; - *lenp = len; + lenp = len; return mem; } diff --git a/src/fileio_func.h b/src/fileio_func.h index a2da91063..72d461863 100644 --- a/src/fileio_func.h +++ b/src/fileio_func.h @@ -49,7 +49,7 @@ const char *FiosGetScreenshotDir(); void SanitizeFilename(char *filename); void AppendPathSeparator(std::string &buf); void DeterminePaths(const char *exe); -void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize); +std::unique_ptr ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize); bool FileExists(const std::string &filename); bool ExtractTar(const std::string &tar_filename, Subdirectory subdir); diff --git a/src/language.h b/src/language.h index 269b22c24..faac59561 100644 --- a/src/language.h +++ b/src/language.h @@ -103,7 +103,7 @@ extern LanguageList _languages; extern const LanguageMetadata *_current_language; #ifdef WITH_ICU_I18N -extern icu::Collator *_current_collator; +extern std::unique_ptr _current_collator; #endif /* WITH_ICU_I18N */ bool ReadLanguagePack(const LanguageMetadata *lang); diff --git a/src/string.cpp b/src/string.cpp index 14bc926c1..91c1aa147 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -630,7 +630,7 @@ int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front) } #ifdef WITH_ICU_I18N - if (_current_collator != nullptr) { + if (_current_collator) { UErrorCode status = U_ZERO_ERROR; int result = _current_collator->compareUTF8(s1, s2, status); if (U_SUCCESS(status)) return result; diff --git a/src/strings.cpp b/src/strings.cpp index 9f61ea5fd..adb874ba1 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -41,14 +41,14 @@ #include "safeguards.h" -char _config_language_file[MAX_PATH]; ///< The file (name) stored in the configuration. +std::string _config_language_file; ///< The file (name) stored in the configuration. LanguageList _languages; ///< The actual list of language meta data. const LanguageMetadata *_current_language = nullptr; ///< The currently loaded language. TextDirection _current_text_dir; ///< Text direction of the currently selected language. #ifdef WITH_ICU_I18N -icu::Collator *_current_collator = nullptr; ///< Collator for the language currently in use. +std::unique_ptr _current_collator; ///< Collator for the language currently in use. #endif /* WITH_ICU_I18N */ static uint64 _global_string_params_data[20]; ///< Global array of string parameters. To access, use #SetDParam. @@ -185,10 +185,17 @@ struct LanguagePack : public LanguagePackHeader { char data[]; // list of strings }; -static char **_langpack_offs; -static LanguagePack *_langpack; -static uint _langtab_num[TEXT_TAB_END]; ///< Offset into langpack offs -static uint _langtab_start[TEXT_TAB_END]; ///< Offset into langpack offs +struct LoadedLanguagePack { + std::unique_ptr langpack; + + std::vector offsets; + + std::array langtab_num; ///< Offset into langpack offs + std::array langtab_start; ///< Offset into langpack offs +}; + +static LoadedLanguagePack _langpack; + static bool _scan_for_gender_data = false; ///< Are we scanning for the gender of the current string? (instead of formatting it) @@ -199,7 +206,7 @@ const char *GetStringPtr(StringID string) /* 0xD0xx and 0xD4xx IDs have been converted earlier. */ case TEXT_TAB_OLD_NEWGRF: NOT_REACHED(); case TEXT_TAB_NEWGRF_START: return GetGRFStringPtr(GetStringIndex(string)); - default: return _langpack_offs[_langtab_start[GetStringTab(string)] + GetStringIndex(string)]; + default: return _langpack.offsets[_langpack.langtab_start[GetStringTab(string)] + GetStringIndex(string)]; } } @@ -253,7 +260,7 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, co break; } - if (index >= _langtab_num[tab]) { + if (index >= _langpack.langtab_num[tab]) { if (game_script) { return GetStringWithArgs(buffr, STR_UNDEFINED, args, last); } @@ -318,7 +325,7 @@ static char *FormatNumber(char *buff, int64 number, const char *last, const char for (int i = 0; i < max_digits; i++) { if (i == max_digits - fractional_digits) { const char *decimal_separator = _settings_game.locale.digit_decimal_separator; - if (decimal_separator == nullptr) decimal_separator = _langpack->digit_decimal_separator; + if (decimal_separator == nullptr) decimal_separator = _langpack.langpack->digit_decimal_separator; buff += seprintf(buff, last, "%s", decimal_separator); } @@ -343,7 +350,7 @@ static char *FormatNumber(char *buff, int64 number, const char *last, const char static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0) { const char *separator = _settings_game.locale.digit_group_separator; - if (separator == nullptr) separator = _langpack->digit_group_separator; + if (separator == nullptr) separator = _langpack.langpack->digit_group_separator; return FormatNumber(buff, number, last, separator, 1, fractional_digits); } @@ -382,7 +389,7 @@ static char *FormatBytes(char *buff, int64 number, const char *last) } const char *decimal_separator = _settings_game.locale.digit_decimal_separator; - if (decimal_separator == nullptr) decimal_separator = _langpack->digit_decimal_separator; + if (decimal_separator == nullptr) decimal_separator = _langpack.langpack->digit_decimal_separator; if (number < 1024) { id = 0; @@ -477,7 +484,7 @@ static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money n const char *separator = _settings_game.locale.digit_group_separator_currency; if (separator == nullptr && !StrEmpty(_currency->separator)) separator = _currency->separator; - if (separator == nullptr) separator = _langpack->digit_group_separator_currency; + if (separator == nullptr) separator = _langpack.langpack->digit_group_separator_currency; buff = FormatNumber(buff, number, last, separator); buff = strecpy(buff, multiplier, last); @@ -1721,16 +1728,15 @@ bool LanguagePackHeader::IsValid() const bool ReadLanguagePack(const LanguageMetadata *lang) { /* Current language pack */ - size_t len; - LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20); - if (lang_pack == nullptr) return false; + size_t len = 0; + std::unique_ptr lang_pack(reinterpret_cast(ReadFileToMem(lang->file, len, 1U << 20).release())); + if (!lang_pack) return false; /* End of read data (+ terminating zero added in ReadFileToMem()) */ - const char *end = (char *)lang_pack + len + 1; + const char *end = (char *)lang_pack.get() + len + 1; /* We need at least one byte of lang_pack->data */ if (end <= lang_pack->data || !lang_pack->IsValid()) { - free(lang_pack); return false; } @@ -1740,55 +1746,46 @@ bool ReadLanguagePack(const LanguageMetadata *lang) } #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */ + std::array tab_start, tab_num; + uint count = 0; for (uint i = 0; i < TEXT_TAB_END; i++) { uint16 num = lang_pack->offsets[i]; - if (num > TAB_SIZE) { - free(lang_pack); - return false; - } + if (num > TAB_SIZE) return false; - _langtab_start[i] = count; - _langtab_num[i] = num; + tab_start[i] = count; + tab_num[i] = num; count += num; } /* Allocate offsets */ - char **langpack_offs = MallocT(count); + std::vector offs(count); /* Fill offsets */ char *s = lang_pack->data; len = (byte)*s++; for (uint i = 0; i < count; i++) { - if (s + len >= end) { - free(lang_pack); - free(langpack_offs); - return false; - } + if (s + len >= end) return false; + if (len >= 0xC0) { len = ((len & 0x3F) << 8) + (byte)*s++; - if (s + len >= end) { - free(lang_pack); - free(langpack_offs); - return false; - } + if (s + len >= end) return false; } - langpack_offs[i] = s; + offs[i] = s; s += len; len = (byte)*s; *s++ = '\0'; // zero terminate the string } - free(_langpack); - _langpack = lang_pack; - - free(_langpack_offs); - _langpack_offs = langpack_offs; + _langpack.langpack = std::move(lang_pack); + _langpack.offsets = std::move(offs); + _langpack.langtab_num = tab_num; + _langpack.langtab_start = tab_start; _current_language = lang; _current_text_dir = (TextDirection)_current_language->text_dir; const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1; - strecpy(_config_language_file, c_file, lastof(_config_language_file)); + _config_language_file = c_file; SetCurrentGrfLangID(_current_language->newgrflangid); #ifdef _WIN32 @@ -1802,21 +1799,14 @@ bool ReadLanguagePack(const LanguageMetadata *lang) #endif #ifdef WITH_ICU_I18N - /* Delete previous collator. */ - if (_current_collator != nullptr) { - delete _current_collator; - _current_collator = nullptr; - } - /* Create a collator instance for our current locale. */ UErrorCode status = U_ZERO_ERROR; - _current_collator = icu::Collator::createInstance(icu::Locale(_current_language->isocode), status); + _current_collator.reset(icu::Collator::createInstance(icu::Locale(_current_language->isocode), status)); /* Sort number substrings by their numerical value. */ - if (_current_collator != nullptr) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status); + if (_current_collator) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status); /* Avoid using the collator if it is not correctly set. */ if (U_FAILURE(status)) { - delete _current_collator; - _current_collator = nullptr; + _current_collator.reset(); } #endif /* WITH_ICU_I18N */ @@ -1978,7 +1968,7 @@ void InitializeLanguagePacks() * configuration file, local environment and last, if nothing found, * English. */ const char *lang_file = strrchr(lng.file, PATHSEPCHAR) + 1; - if (strcmp(lang_file, _config_language_file) == 0) { + if (_config_language_file == lang_file) { chosen_language = &lng; break; } @@ -2003,7 +1993,7 @@ void InitializeLanguagePacks() */ const char *GetCurrentLanguageIsoCode() { - return _langpack->isocode; + return _langpack.langpack->isocode; } /** @@ -2057,10 +2047,10 @@ class LanguagePackGlyphSearcher : public MissingGlyphSearcher { { if (this->i >= TEXT_TAB_END) return nullptr; - const char *ret = _langpack_offs[_langtab_start[this->i] + this->j]; + const char *ret = _langpack.offsets[_langpack.langtab_start[this->i] + this->j]; this->j++; - while (this->i < TEXT_TAB_END && this->j >= _langtab_num[this->i]) { + while (this->i < TEXT_TAB_END && this->j >= _langpack.langtab_num[this->i]) { this->i++; this->j = 0; } @@ -2116,7 +2106,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) _freetype.mono.os_handle = nullptr; _freetype.medium.os_handle = nullptr; - bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher); + bad_font = !SetFallbackFont(&_freetype, _langpack.langpack->isocode, _langpack.langpack->winlangid, searcher); free(_freetype.mono.os_handle); free(_freetype.medium.os_handle); diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini index 60799a436..edad84816 100644 --- a/src/table/misc_settings.ini +++ b/src/table/misc_settings.ini @@ -5,7 +5,7 @@ ; [pre-amble] -extern char _config_language_file[MAX_PATH]; +extern std::string _config_language_file; static const char *_support8bppmodes = "no|system|hardware"; @@ -107,9 +107,9 @@ type = SLE_STRQ var = _ini_blitter def = nullptr -[SDTG_STR] +[SDTG_SSTR] name = ""language"" -type = SLE_STRB +type = SLE_STR var = _config_language_file def = nullptr cat = SC_BASIC -- cgit v1.2.3-70-g09d2