From c082f570cebd237c35de9caa08fcc84a9c44e95d Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 May 2020 23:31:51 +0200 Subject: Codechange: Use std::string when translating TTDP NewGRF string codes. --- src/newgrf.cpp | 11 +- src/newgrf_text.cpp | 332 +++++++++++++++++++++++++--------------------------- src/newgrf_text.h | 2 +- src/string.cpp | 15 ++- src/string_func.h | 10 ++ 5 files changed, 188 insertions(+), 182 deletions(-) (limited to 'src') diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 4ada53a0a..3f621e5c9 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -6895,7 +6895,7 @@ static void GRFLoadError(ByteReader *buf) if (buf->HasData()) { const char *message = buf->ReadString(); - error->custom_message = TranslateTTDPatchCodes(_cur.grffile->grfid, lang, true, message, nullptr, SCC_RAW_STRING_POINTER); + error->custom_message = stredup(TranslateTTDPatchCodes(_cur.grffile->grfid, lang, true, message, SCC_RAW_STRING_POINTER).c_str()); } else { grfmsg(7, "GRFLoadError: No custom message supplied."); error->custom_message = stredup(""); @@ -6907,7 +6907,7 @@ static void GRFLoadError(ByteReader *buf) if (buf->HasData()) { const char *data = buf->ReadString(); - error->data = TranslateTTDPatchCodes(_cur.grffile->grfid, lang, true, data); + error->data = stredup(TranslateTTDPatchCodes(_cur.grffile->grfid, lang, true, data).c_str()); } else { grfmsg(7, "GRFLoadError: No message data supplied."); error->data = stredup(""); @@ -7456,9 +7456,8 @@ static void FeatureTownName(ByteReader *buf) const char *name = buf->ReadString(); - char *lang_name = TranslateTTDPatchCodes(grfid, lang, false, name); - grfmsg(6, "FeatureTownName: lang 0x%X -> '%s'", lang, lang_name); - free(lang_name); + std::string lang_name = TranslateTTDPatchCodes(grfid, lang, false, name); + grfmsg(6, "FeatureTownName: lang 0x%X -> '%s'", lang, lang_name.c_str()); townname->name[nb_gen] = AddGRFString(grfid, id, lang, new_scheme, false, name, STR_UNDEFINED); @@ -7500,7 +7499,7 @@ static void FeatureTownName(ByteReader *buf) townname->partlist[id][i].parts[j].data.id = ref_id; } else { const char *text = buf->ReadString(); - townname->partlist[id][i].parts[j].data.text = TranslateTTDPatchCodes(grfid, 0, false, text); + townname->partlist[id][i].parts[j].data.text = stredup(TranslateTTDPatchCodes(grfid, 0, false, text).c_str()); grfmsg(6, "FeatureTownName: part %d, text %d, '%s' (with probability %d)", i, j, townname->partlist[id][i].parts[j].data.text, prob); } townname->partlist[id][i].parts[j].prob = prob; diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index 4f1a6ffd4..b6a0e4891 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -31,6 +31,8 @@ #include "core/alloc_type.hpp" #include "core/smallmap_type.hpp" #include "language.h" +#include +#include #include "table/strings.h" #include "table/control_codes.h" @@ -109,58 +111,48 @@ int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const } /** Helper structure for mapping choice lists. */ -struct UnmappedChoiceList : ZeroedMemoryAllocator { - /** Clean everything up. */ - ~UnmappedChoiceList() - { - for (std::pair p : this->strings) { - free(p.second); - } - } - +struct UnmappedChoiceList { /** * Initialise the mapping. * @param type The type of mapping. - * @param old_d The old begin of the string, i.e. from where to start writing again. * @param offset The offset to get the plural/gender from. */ - UnmappedChoiceList(StringControlCode type, char *old_d, int offset) : - type(type), old_d(old_d), offset(offset) + UnmappedChoiceList(StringControlCode type, int offset) : + type(type), offset(offset) { } StringControlCode type; ///< The type of choice list. - char *old_d; ///< The old/original location of the "d" local variable. int offset; ///< The offset for the plural/gender form. /** Mapping of NewGRF supplied ID to the different strings in the choice list. */ - SmallMap strings; + std::map strings; /** - * Flush this choice list into the old d variable. - * @param lm The current language mapping. - * @return The new location of the output string. + * Flush this choice list into the destination string. + * @param lm The current language mapping. + * @param dest Target to write to. */ - char *Flush(const LanguageMap *lm) + void Flush(const LanguageMap *lm, std::ostringstream &dest) { - if (!this->strings.Contains(0)) { + if (this->strings.find(0) == this->strings.end()) { /* In case of a (broken) NewGRF without a default, * assume an empty string. */ grfmsg(1, "choice list misses default value"); - this->strings[0] = stredup(""); + this->strings[0] = std::stringstream(); } - char *d = old_d; + std::ostreambuf_iterator d(dest); + if (lm == nullptr) { /* In case there is no mapping, just ignore everything but the default. * A probable cause for this happening is when the language file has * been removed by the user and as such no mapping could be made. */ - size_t len = strlen(this->strings[0]); - memcpy(d, this->strings[0], len); - return d + len; + dest << this->strings[0].rdbuf(); + return; } - d += Utf8Encode(d, this->type); + Utf8Encode(d, this->type); if (this->type == SCC_SWITCH_CASE) { /* @@ -173,33 +165,31 @@ struct UnmappedChoiceList : ZeroedMemoryAllocator { int count = 0; for (uint8 i = 0; i < _current_language->num_cases; i++) { /* Count the ones we have a mapped string for. */ - if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++; + if (this->strings.find(lm->GetReverseMapping(i, false)) != this->strings.end()) count++; } *d++ = count; for (uint8 i = 0; i < _current_language->num_cases; i++) { /* Resolve the string we're looking for. */ int idx = lm->GetReverseMapping(i, false); - if (!this->strings.Contains(idx)) continue; - char *str = this->strings[idx]; + if (this->strings.find(idx) == this->strings.end()) continue; + auto str = this->strings[idx].str(); /* "" */ *d++ = i + 1; - /* "" */ - size_t len = strlen(str) + 1; - *d++ = GB(len, 8, 8); - *d++ = GB(len, 0, 8); + /* "": Limit the length of the string to 0xFFFE to leave space for the '\0'. */ + size_t len = min(0xFFFE, str.size()); + *d++ = GB(len + 1, 8, 8); + *d++ = GB(len + 1, 0, 8); /* "" */ - memcpy(d, str, len); - d += len; + dest.write(str.c_str(), len); + *d++ = '\0'; } /* "" */ - size_t len = strlen(this->strings[0]) + 1; - memcpy(d, this->strings[0], len); - d += len; + dest << this->strings[0].rdbuf() << '\0'; } else { if (this->type == SCC_PLURAL_LIST) { *d++ = lm->plural_form; @@ -220,8 +210,8 @@ struct UnmappedChoiceList : ZeroedMemoryAllocator { /* "" */ for (int i = 0; i < count; i++) { int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1); - const char *str = this->strings[this->strings.Contains(idx) ? idx : 0]; - size_t len = strlen(str) + 1; + const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0].str(); + size_t len = str.size() + 1; if (len > 0xFF) grfmsg(1, "choice list string is too long"); *d++ = GB(len, 0, 8); } @@ -229,16 +219,14 @@ struct UnmappedChoiceList : ZeroedMemoryAllocator { /* "" */ for (int i = 0; i < count; i++) { int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1); - const char *str = this->strings[this->strings.Contains(idx) ? idx : 0]; + const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0].str(); /* Limit the length of the string we copy to 0xFE. The length is written above * as a byte and we need room for the final '\0'. */ - size_t len = min(0xFE, strlen(str)); - memcpy(d, str, len); - d += len; + size_t len = min(0xFE, str.size()); + dest.write(str.c_str(), len); *d++ = '\0'; } } - return d; } }; @@ -248,47 +236,55 @@ struct UnmappedChoiceList : ZeroedMemoryAllocator { * @param language_id The (NewGRF) language ID associated with this string. * @param allow_newlines Whether newlines are allowed in the string or not. * @param str The string to translate. - * @param[out] olen The length of the final string. * @param byte80 The control code to use as replacement for the 0x80-value. * @return The translated string. */ -char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80) +std::string TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const std::string &str, StringControlCode byte80) { - char *tmp = MallocT(strlen(str) * 10 + 1); // Allocate space to allow for expansion - char *d = tmp; + /* Empty input string? Nothing to do here. */ + if (str.empty()) return str; + + std::string::const_iterator src = str.cbegin(); + + /* Is this an unicode string? */ bool unicode = false; - WChar c; - size_t len = Utf8Decode(&c, str); + WChar marker; + size_t len = Utf8Decode(&marker, &*src); + + if (marker == NFO_UTF8_IDENTIFIER) { + unicode = true; + src += len; + } /* Helper variable for a possible (string) mapping. */ UnmappedChoiceList *mapping = nullptr; - if (c == NFO_UTF8_IDENTIFIER) { - unicode = true; - str += len; - } + std::ostringstream dest; + std::ostreambuf_iterator d(dest); + while (src != str.cend()) { + WChar c; - for (;;) { - if (unicode && Utf8EncodedCharLen(*str) != 0) { - c = Utf8Consume(&str); + if (unicode && Utf8EncodedCharLen(*src) != 0) { + c = Utf8Consume(src); /* 'Magic' range of control codes. */ if (GB(c, 8, 8) == 0xE0) { c = GB(c, 0, 8); } else if (c >= 0x20) { if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?'; - d += Utf8Encode(d, c); + Utf8Encode(d, c); continue; } } else { - c = (byte)*str++; + c = (byte)*src++; } + if (c == '\0') break; switch (c) { case 0x01: - if (str[0] == '\0') goto string_end; - d += Utf8Encode(d, ' '); - str++; + if (*src == '\0') goto string_end; + Utf8Encode(d, ' '); + src++; break; case 0x0A: break; case 0x0D: @@ -298,92 +294,96 @@ char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newline grfmsg(1, "Detected newline in string that does not allow one"); } break; - case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break; - case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break; + case 0x0E: Utf8Encode(d, SCC_TINYFONT); break; + case 0x0F: Utf8Encode(d, SCC_BIGFONT); break; case 0x1F: - if (str[0] == '\0' || str[1] == '\0') goto string_end; - d += Utf8Encode(d, ' '); - str += 2; + if (src[0] == '\0' || src[1] == '\0') goto string_end; + Utf8Encode(d, ' '); + src += 2; break; case 0x7B: case 0x7C: case 0x7D: case 0x7E: - case 0x7F: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break; - case 0x80: d += Utf8Encode(d, byte80); break; - case 0x81: { - if (str[0] == '\0' || str[1] == '\0') goto string_end; + case 0x7F: Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break; + case 0x80: Utf8Encode(d, byte80); break; + case 0x81: + { + if (src[0] == '\0' || src[1] == '\0') goto string_end; StringID string; - string = ((uint8)*str++); - string |= ((uint8)*str++) << 8; - d += Utf8Encode(d, SCC_NEWGRF_STRINL); - d += Utf8Encode(d, MapGRFStringID(grfid, string)); + string = ((uint8)* src++); + string |= ((uint8)* src++) << 8; + Utf8Encode(d, SCC_NEWGRF_STRINL); + Utf8Encode(d, MapGRFStringID(grfid, string)); break; } case 0x82: case 0x83: - case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break; - case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break; - case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break; - case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG); break; - case 0x88: d += Utf8Encode(d, SCC_BLUE); break; - case 0x89: d += Utf8Encode(d, SCC_SILVER); break; - case 0x8A: d += Utf8Encode(d, SCC_GOLD); break; - case 0x8B: d += Utf8Encode(d, SCC_RED); break; - case 0x8C: d += Utf8Encode(d, SCC_PURPLE); break; - case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break; - case 0x8E: d += Utf8Encode(d, SCC_ORANGE); break; - case 0x8F: d += Utf8Encode(d, SCC_GREEN); break; - case 0x90: d += Utf8Encode(d, SCC_YELLOW); break; - case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break; - case 0x92: d += Utf8Encode(d, SCC_CREAM); break; - case 0x93: d += Utf8Encode(d, SCC_BROWN); break; - case 0x94: d += Utf8Encode(d, SCC_WHITE); break; - case 0x95: d += Utf8Encode(d, SCC_LTBLUE); break; - case 0x96: d += Utf8Encode(d, SCC_GRAY); break; - case 0x97: d += Utf8Encode(d, SCC_DKBLUE); break; - case 0x98: d += Utf8Encode(d, SCC_BLACK); break; - case 0x9A: { - int code = *str++; + case 0x84: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break; + case 0x85: Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break; + case 0x86: Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break; + case 0x87: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG); break; + case 0x88: Utf8Encode(d, SCC_BLUE); break; + case 0x89: Utf8Encode(d, SCC_SILVER); break; + case 0x8A: Utf8Encode(d, SCC_GOLD); break; + case 0x8B: Utf8Encode(d, SCC_RED); break; + case 0x8C: Utf8Encode(d, SCC_PURPLE); break; + case 0x8D: Utf8Encode(d, SCC_LTBROWN); break; + case 0x8E: Utf8Encode(d, SCC_ORANGE); break; + case 0x8F: Utf8Encode(d, SCC_GREEN); break; + case 0x90: Utf8Encode(d, SCC_YELLOW); break; + case 0x91: Utf8Encode(d, SCC_DKGREEN); break; + case 0x92: Utf8Encode(d, SCC_CREAM); break; + case 0x93: Utf8Encode(d, SCC_BROWN); break; + case 0x94: Utf8Encode(d, SCC_WHITE); break; + case 0x95: Utf8Encode(d, SCC_LTBLUE); break; + case 0x96: Utf8Encode(d, SCC_GRAY); break; + case 0x97: Utf8Encode(d, SCC_DKBLUE); break; + case 0x98: Utf8Encode(d, SCC_BLACK); break; + case 0x9A: + { + int code = *src++; switch (code) { case 0x00: goto string_end; - case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break; - /* 0x02: ignore next colour byte is not supported. It works on the final - * string and as such hooks into the string drawing routine. At that - * point many things already happened, such as splitting up of strings - * when drawn over multiple lines or right-to-left translations, which - * make the behaviour peculiar, e.g. only happening at specific width - * of windows. Or we need to add another pass over the string to just - * support this. As such it is not implemented in OpenTTD. */ - case 0x03: { - if (str[0] == '\0' || str[1] == '\0') goto string_end; - uint16 tmp = ((uint8)*str++); - tmp |= ((uint8)*str++) << 8; - d += Utf8Encode(d, SCC_NEWGRF_PUSH_WORD); - d += Utf8Encode(d, tmp); + case 0x01: Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break; + /* 0x02: ignore next colour byte is not supported. It works on the final + * string and as such hooks into the string drawing routine. At that + * point many things already happened, such as splitting up of strings + * when drawn over multiple lines or right-to-left translations, which + * make the behaviour peculiar, e.g. only happening at specific width + * of windows. Or we need to add another pass over the string to just + * support this. As such it is not implemented in OpenTTD. */ + case 0x03: + { + if (src[0] == '\0' || src[1] == '\0') goto string_end; + uint16 tmp = ((uint8)* src++); + tmp |= ((uint8)* src++) << 8; + Utf8Encode(d, SCC_NEWGRF_PUSH_WORD); + Utf8Encode(d, tmp); break; } case 0x04: - if (str[0] == '\0') goto string_end; - d += Utf8Encode(d, SCC_NEWGRF_UNPRINT); - d += Utf8Encode(d, *str++); + if (src[0] == '\0') goto string_end; + Utf8Encode(d, SCC_NEWGRF_UNPRINT); + Utf8Encode(d, *src++); break; - case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break; - case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break; - case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break; + case 0x06: Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break; + case 0x07: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break; + case 0x08: Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break; /* 0x09, 0x0A are TTDPatch internal use only string codes. */ - case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX); break; - case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break; - case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG); break; + case 0x0B: Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX); break; + case 0x0C: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break; + case 0x0D: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG); break; case 0x0E: - case 0x0F: { + case 0x0F: + { if (str[0] == '\0') goto string_end; const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id); - int index = *str++; + int index = *src++; int mapped = lm != nullptr ? lm->GetMapping(index, code == 0x0E) : -1; if (mapped >= 0) { - d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE); - d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1); + Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE); + Utf8Encode(d, code == 0x0E ? mapped : mapped + 1); } break; } @@ -392,18 +392,16 @@ char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newline case 0x11: if (str[0] == '\0') goto string_end; if (mapping == nullptr) { - if (code == 0x10) str++; // Skip the index + if (code == 0x10) src++; // Skip the index grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default"); break; } else { - /* Terminate the previous string. */ - *d = '\0'; - int index = (code == 0x10 ? *str++ : 0); - if (mapping->strings.Contains(index)) { + int index = (code == 0x10 ? *src++ : 0); + if (mapping->strings.find(index) != mapping->strings.end()) { grfmsg(1, "duplicate choice list string, ignoring"); d++; } else { - d = mapping->strings[index] = MallocT(strlen(str) * 10 + 1); + d = std::ostreambuf_iterator(mapping->strings[index]); } } break; @@ -412,26 +410,25 @@ char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newline if (mapping == nullptr) { grfmsg(1, "choice list end marker found when not expected"); } else { - /* Terminate the previous string. */ - *d = '\0'; - /* Now we can start flushing everything and clean everything up. */ - d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id)); + mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id), dest); delete mapping; mapping = nullptr; + + d = std::ostreambuf_iterator(dest); } break; case 0x13: case 0x14: case 0x15: - if (str[0] == '\0') goto string_end; + if (src[0] == '\0') goto string_end; if (mapping != nullptr) { grfmsg(1, "choice lists can't be stacked, it's going to get messy now..."); - if (code != 0x14) str++; + if (code != 0x14) src++; } else { static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST }; - mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++); + mapping = new UnmappedChoiceList(mp[code - 0x13], code == 0x14 ? 0 : *src++); } break; @@ -444,11 +441,11 @@ char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newline case 0x1C: case 0x1D: case 0x1E: - d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16); + Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16); break; - case 0x1F: d += Utf8Encode(d, SCC_PUSH_COLOUR); break; - case 0x20: d += Utf8Encode(d, SCC_POP_COLOUR); break; + case 0x1F: Utf8Encode(d, SCC_PUSH_COLOUR); break; + case 0x20: Utf8Encode(d, SCC_POP_COLOUR); break; default: grfmsg(1, "missing handler for extended format code"); @@ -457,25 +454,25 @@ char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newline break; } - case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro - case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis - case 0xA0: d += Utf8Encode(d, SCC_UP_ARROW); break; - case 0xAA: d += Utf8Encode(d, SCC_DOWN_ARROW); break; - case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break; - case 0xAD: d += Utf8Encode(d, SCC_CROSS); break; - case 0xAF: d += Utf8Encode(d, SCC_RIGHT_ARROW); break; - case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break; - case 0xB5: d += Utf8Encode(d, SCC_LORRY); break; - case 0xB6: d += Utf8Encode(d, SCC_BUS); break; - case 0xB7: d += Utf8Encode(d, SCC_PLANE); break; - case 0xB8: d += Utf8Encode(d, SCC_SHIP); break; - case 0xB9: d += Utf8Encode(d, SCC_SUPERSCRIPT_M1); break; - case 0xBC: d += Utf8Encode(d, SCC_SMALL_UP_ARROW); break; - case 0xBD: d += Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break; + case 0x9E: Utf8Encode(d, 0x20AC); break; // Euro + case 0x9F: Utf8Encode(d, 0x0178); break; // Y with diaeresis + case 0xA0: Utf8Encode(d, SCC_UP_ARROW); break; + case 0xAA: Utf8Encode(d, SCC_DOWN_ARROW); break; + case 0xAC: Utf8Encode(d, SCC_CHECKMARK); break; + case 0xAD: Utf8Encode(d, SCC_CROSS); break; + case 0xAF: Utf8Encode(d, SCC_RIGHT_ARROW); break; + case 0xB4: Utf8Encode(d, SCC_TRAIN); break; + case 0xB5: Utf8Encode(d, SCC_LORRY); break; + case 0xB6: Utf8Encode(d, SCC_BUS); break; + case 0xB7: Utf8Encode(d, SCC_PLANE); break; + case 0xB8: Utf8Encode(d, SCC_SHIP); break; + case 0xB9: Utf8Encode(d, SCC_SUPERSCRIPT_M1); break; + case 0xBC: Utf8Encode(d, SCC_SMALL_UP_ARROW); break; + case 0xBD: Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break; default: /* Validate any unhandled character */ if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?'; - d += Utf8Encode(d, c); + Utf8Encode(d, c); break; } } @@ -486,10 +483,7 @@ string_end: delete mapping; } - *d = '\0'; - if (olen != nullptr) *olen = d - tmp + 1; - tmp = ReallocT(tmp, d - tmp + 1); - return tmp; + return dest.str(); } /** @@ -523,12 +517,7 @@ static void AddGRFTextToList(GRFTextList &list, byte langid, const std::string & */ void AddGRFTextToList(GRFTextList &list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add) { - int len; - char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len); - std::string newtext(translatedtext, len); - free(translatedtext); - - AddGRFTextToList(list, langid, newtext); + AddGRFTextToList(list, langid, TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add)); } /** @@ -563,9 +552,6 @@ void AddGRFTextToList(GRFTextWrapper &list, const char *text_to_add) */ StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, bool allow_newlines, const char *text_to_add, StringID def_string) { - char *translatedtext; - uint id; - /* When working with the old language scheme (grf_version is less than 7) and * English or American is among the set bits, simply add it as English in * the new scheme, i.e. as langid = 1. @@ -584,6 +570,7 @@ StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool ne } } + uint id; for (id = 0; id < _num_grf_texts; id++) { if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) { break; @@ -593,10 +580,7 @@ StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool ne /* Too many strings allocated, return empty */ if (id == lengthof(_grf_text)) return STR_EMPTY; - int len; - translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len); - std::string newtext(translatedtext, len); - free(translatedtext); + std::string newtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add); /* If we didn't find our stringid and grfid in the list, allocate a new id */ if (id == _num_grf_texts) _num_grf_texts++; diff --git a/src/newgrf_text.h b/src/newgrf_text.h index bdd1f1bc9..2c7eb731d 100644 --- a/src/newgrf_text.h +++ b/src/newgrf_text.h @@ -39,7 +39,7 @@ const char *GetGRFStringFromGRFText(const GRFTextWrapper &text); const char *GetGRFStringPtr(uint16 stringid); void CleanUpStrings(); void SetCurrentGrfLangID(byte language_id); -char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen = nullptr, StringControlCode byte80 = SCC_NEWGRF_PRINT_WORD_STRING_ID); +std::string TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const std::string &str, StringControlCode byte80 = SCC_NEWGRF_PRINT_WORD_STRING_ID); void AddGRFTextToList(GRFTextList &list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add); void AddGRFTextToList(GRFTextWrapper &list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add); void AddGRFTextToList(GRFTextWrapper &list, const char *text_to_add); diff --git a/src/string.cpp b/src/string.cpp index dea369037..90d287c9e 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -18,6 +18,7 @@ #include #include /* required for tolower() */ +#include #ifdef _MSC_VER #include // required by vsnprintf implementation for MSVC @@ -479,11 +480,13 @@ size_t Utf8Decode(WChar *c, const char *s) /** * Encode a unicode character and place it in the buffer. + * @tparam T Type of the buffer. * @param buf Buffer to place character. * @param c Unicode character to encode. * @return Number of characters in the encoded sequence. */ -size_t Utf8Encode(char *buf, WChar c) +template +inline size_t Utf8Encode(T buf, WChar c) { if (c < 0x80) { *buf = c; @@ -510,6 +513,16 @@ size_t Utf8Encode(char *buf, WChar c) return 1; } +size_t Utf8Encode(char *buf, WChar c) +{ + return Utf8Encode(buf, c); +} + +size_t Utf8Encode(std::ostreambuf_iterator &buf, WChar c) +{ + return Utf8Encode &>(buf, c); +} + /** * Properly terminate an UTF8 string to some maximum length * @param s string to check if it needs additional trimming diff --git a/src/string_func.h b/src/string_func.h index 9942e9a6a..002b8f217 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -25,6 +25,7 @@ #define STRING_FUNC_H #include +#include #include "core/bitmath_func.hpp" #include "string_type.h" @@ -79,6 +80,7 @@ bool IsValidChar(WChar key, CharSetFilter afilter); size_t Utf8Decode(WChar *c, const char *s); size_t Utf8Encode(char *buf, WChar c); +size_t Utf8Encode(std::ostreambuf_iterator &buf, WChar c); size_t Utf8TrimString(char *s, size_t maxlen); @@ -89,6 +91,14 @@ static inline WChar Utf8Consume(const char **s) return c; } +template +static inline WChar Utf8Consume(Titr &s) +{ + WChar c; + s += Utf8Decode(&c, &*s); + return c; +} + /** * Return the length of a UTF-8 encoded character. * @param c Unicode character. -- cgit v1.2.3-54-g00ecf