diff --git a/src/network/network.cpp b/src/network/network.cpp index de11f48eef..321e47ffb7 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -37,6 +37,7 @@ #include "../gfx_func.h" #include "../error.h" #include "../misc_cmd.h" +#include "../core/string_builder.hpp" #ifdef DEBUG_DUMP_COMMANDS # include "../fileio_func.h" #endif @@ -231,49 +232,48 @@ uint8_t NetworkSpectatorCount() * If 'self_send' is true, this is the client who is sending the message */ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const std::string &name, const std::string &str, StringParameter &&data) { - std::string string; - switch (action) { - case NETWORK_ACTION_SERVER_MESSAGE: - /* Ignore invalid messages */ - string = GetString(STR_NETWORK_SERVER_MESSAGE, str); - colour = CC_DEFAULT; - break; - case NETWORK_ACTION_COMPANY_SPECTATOR: - colour = CC_DEFAULT; - string = GetString(STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE, name); - break; - case NETWORK_ACTION_COMPANY_JOIN: - colour = CC_DEFAULT; - string = GetString(STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN, name, str); - break; - case NETWORK_ACTION_COMPANY_NEW: - colour = CC_DEFAULT; - string = GetString(STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW, name, std::move(data)); - break; - case NETWORK_ACTION_JOIN: - /* Show the Client ID for the server but not for the client. */ - string = _network_server ? - GetString(STR_NETWORK_MESSAGE_CLIENT_JOINED_ID, name, std::move(data)) : - GetString(STR_NETWORK_MESSAGE_CLIENT_JOINED, name); - break; - case NETWORK_ACTION_LEAVE: string = GetString(STR_NETWORK_MESSAGE_CLIENT_LEFT, name, std::move(data)); break; - case NETWORK_ACTION_NAME_CHANGE: string = GetString(STR_NETWORK_MESSAGE_NAME_CHANGE, name, str); break; - case NETWORK_ACTION_GIVE_MONEY: string = GetString(STR_NETWORK_MESSAGE_GIVE_MONEY, name, std::move(data), str); break; - case NETWORK_ACTION_KICKED: string = GetString(STR_NETWORK_MESSAGE_KICKED, name, str); break; - case NETWORK_ACTION_CHAT_COMPANY: string = GetString(self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY, name, str); break; - case NETWORK_ACTION_CHAT_CLIENT: string = GetString(self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT, name, str); break; - case NETWORK_ACTION_EXTERNAL_CHAT: string = GetString(STR_NETWORK_CHAT_EXTERNAL, std::move(data), name, str); break; - default: string = GetString(STR_NETWORK_CHAT_ALL, name, str); break; - } + std::string message; + StringBuilder builder(message); /* All of these strings start with "***". These characters are interpreted as both left-to-right and * right-to-left characters depending on the context. As the next text might be an user's name, the * user name's characters will influence the direction of the "***" instead of the language setting * of the game. Manually set the direction of the "***" by inserting a text-direction marker. */ - std::ostringstream stream; - std::ostreambuf_iterator iterator(stream); - Utf8Encode(iterator, _current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM); - std::string message = stream.str() + string; + builder.PutUtf8(_current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM); + + switch (action) { + case NETWORK_ACTION_SERVER_MESSAGE: + /* Ignore invalid messages */ + builder += GetString(STR_NETWORK_SERVER_MESSAGE, str); + colour = CC_DEFAULT; + break; + case NETWORK_ACTION_COMPANY_SPECTATOR: + colour = CC_DEFAULT; + builder += GetString(STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE, name); + break; + case NETWORK_ACTION_COMPANY_JOIN: + colour = CC_DEFAULT; + builder += GetString(STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN, name, str); + break; + case NETWORK_ACTION_COMPANY_NEW: + colour = CC_DEFAULT; + builder += GetString(STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW, name, std::move(data)); + break; + case NETWORK_ACTION_JOIN: + /* Show the Client ID for the server but not for the client. */ + builder += _network_server ? + GetString(STR_NETWORK_MESSAGE_CLIENT_JOINED_ID, name, std::move(data)) : + GetString(STR_NETWORK_MESSAGE_CLIENT_JOINED, name); + break; + case NETWORK_ACTION_LEAVE: builder += GetString(STR_NETWORK_MESSAGE_CLIENT_LEFT, name, std::move(data)); break; + case NETWORK_ACTION_NAME_CHANGE: builder += GetString(STR_NETWORK_MESSAGE_NAME_CHANGE, name, str); break; + case NETWORK_ACTION_GIVE_MONEY: builder += GetString(STR_NETWORK_MESSAGE_GIVE_MONEY, name, std::move(data), str); break; + case NETWORK_ACTION_KICKED: builder += GetString(STR_NETWORK_MESSAGE_KICKED, name, str); break; + case NETWORK_ACTION_CHAT_COMPANY: builder += GetString(self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY, name, str); break; + case NETWORK_ACTION_CHAT_CLIENT: builder += GetString(self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT, name, str); break; + case NETWORK_ACTION_EXTERNAL_CHAT: builder += GetString(STR_NETWORK_CHAT_EXTERNAL, std::move(data), name, str); break; + default: builder += GetString(STR_NETWORK_CHAT_ALL, name, str); break; + } Debug(desync, 1, "msg: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, message); IConsolePrint(colour, message); diff --git a/src/saveload/strings_sl.cpp b/src/saveload/strings_sl.cpp index 2be7fe0f4a..94caa07b20 100644 --- a/src/saveload/strings_sl.cpp +++ b/src/saveload/strings_sl.cpp @@ -10,6 +10,7 @@ #include "../stdafx.h" #include "../string_func.h" #include "../strings_func.h" +#include "../core/string_builder.hpp" #include "saveload_internal.h" #include "table/strings.h" @@ -63,8 +64,8 @@ std::string CopyFromOldName(StringID id) if (IsSavegameVersionBefore(SLV_37)) { const std::string &strfrom = _old_name_array[GB(id, 0, 9)]; - std::ostringstream tmp; - std::ostreambuf_iterator strto(tmp); + std::string result; + StringBuilder builder(result); for (char s : strfrom) { if (s == '\0') break; char32_t c = static_cast(s); // cast to unsigned before integer promotion @@ -82,10 +83,10 @@ std::string CopyFromOldName(StringID id) default: break; } - if (IsPrintable(c)) Utf8Encode(strto, c); + if (IsPrintable(c)) builder.PutUtf8(c); } - return tmp.str(); + return result; } else { /* Name will already be in UTF-8. */ return StrMakeValid(_old_name_array[GB(id, 0, 9)]); diff --git a/src/strings.cpp b/src/strings.cpp index e6ab3fc047..68a59c0c9b 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -2371,13 +2371,12 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) if (!bad_font && any_font_configured) { /* If the user configured a bad font, and we found a better one, * show that we loaded the better font instead of the configured one. - * The colour 'character' might change in the - * future, so for safety we just Utf8 Encode it into the string, - * which takes exactly three characters, so it replaces the "XXX" - * with the colour marker. */ - static std::string err_str("XXXThe current font is missing some of the characters used in the texts for this language. Using system fallback font instead."); - Utf8Encode(err_str.data(), SCC_YELLOW); - ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_WARNING); + */ + std::string err_str; + StringBuilder builder(err_str); + builder.PutUtf8(SCC_YELLOW); + builder.Put("The current font is missing some of the characters used in the texts for this language. Using system fallback font instead."); + ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING); } if (bad_font && base_font) { @@ -2393,11 +2392,12 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) /* All attempts have failed. Display an error. As we do not want the string to be translated by * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts. - * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into - * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */ - static std::string err_str("XXXThe current font is missing some of the characters used in the texts for this language. Go to Help & Manuals > Fonts, or read the file docs/fonts.md in your OpenTTD directory, to see how to solve this."); - Utf8Encode(err_str.data(), SCC_YELLOW); - ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_WARNING); + */ + std::string err_str; + StringBuilder builder(err_str); + builder.PutUtf8(SCC_YELLOW); + builder.Put("The current font is missing some of the characters used in the texts for this language. Go to Help & Manuals > Fonts, or read the file docs/fonts.md in your OpenTTD directory, to see how to solve this."); + ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING); /* Reset the font width */ LoadStringWidthTable(searcher->Monospace()); @@ -2415,16 +2415,14 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) * be translated by the translators, we 'force' it into the * binary and 'load' it via a BindCString. To do this * properly we have to set the colour of the string, - * otherwise we end up with a lot of artifacts. The colour - * 'character' might change in the future, so for safety - * we just Utf8 Encode it into the string, which takes - * exactly three characters, so it replaces the "XXX" with - * the colour marker. + * otherwise we end up with a lot of artifacts. */ if (_current_text_dir != TD_LTR) { - static std::string err_str("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with ICU + Harfbuzz enabled."); - Utf8Encode(err_str.data(), SCC_YELLOW); - ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_ERROR); + std::string err_str; + StringBuilder builder(err_str); + builder.PutUtf8(SCC_YELLOW); + builder.Put("This version of OpenTTD does not support right-to-left languages. Recompile with ICU + Harfbuzz enabled."); + ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_ERROR); } #endif /* !(WITH_ICU_I18N && WITH_HARFBUZZ) && !WITH_UNISCRIBE && !WITH_COCOA */ } diff --git a/src/tests/string_func.cpp b/src/tests/string_func.cpp index 93853ed795..1b05786a9e 100644 --- a/src/tests/string_func.cpp +++ b/src/tests/string_func.cpp @@ -13,6 +13,7 @@ #include "../string_func.h" #include "../strings_func.h" +#include "../core/string_builder.hpp" #include "../table/control_codes.h" #include "table/strings.h" @@ -423,15 +424,15 @@ static std::string FixSCCEncodedWrapper(const std::string &str, bool fix_code) } /* Helper to compose a string part from a unicode character */ -static void ComposePart(std::back_insert_iterator &output, char32_t c) +static void ComposePart(StringBuilder &builder, char32_t c) { - Utf8Encode(output, c); + builder.PutUtf8(c); } /* Helper to compose a string part from a string. */ -static void ComposePart(std::back_insert_iterator &output, const std::string &value) +static void ComposePart(StringBuilder &builder, const std::string &value) { - for (const auto &c : value) *output = c; + builder += value; } /* Helper to compose a string from unicode or string parts. */ @@ -439,8 +440,8 @@ template static std::string Compose(Args &&... args) { std::string result; - auto output = std::back_inserter(result); - (ComposePart(output, args), ...); + StringBuilder builder(result); + (ComposePart(builder, args), ...); return result; } diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp index 82e06a02b2..0ac1f3a3da 100644 --- a/src/textfile_gui.cpp +++ b/src/textfile_gui.cpp @@ -14,6 +14,7 @@ #include "gfx_type.h" #include "gfx_func.h" #include "string_func.h" +#include "core/string_builder.hpp" #include "textfile_gui.h" #include "dropdown_type.h" #include "dropdown_func.h" @@ -245,7 +246,7 @@ void TextfileWindow::FindHyperlinksInMarkdown(Line &line, size_t line_index) { std::string::const_iterator last_match_end = line.text.cbegin(); std::string fixed_line; - char ccbuf[5]; + StringBuilder builder(fixed_line); std::sregex_iterator matcher{ line.text.cbegin(), line.text.cend(), _markdown_link_regex}; while (matcher != std::sregex_iterator()) { @@ -273,13 +274,13 @@ void TextfileWindow::FindHyperlinksInMarkdown(Line &line, size_t line_index) if (link_colour != SCC_CONTROL_END) { /* Format the link to look like a link. */ - fixed_line += std::string(last_match_end, match[0].first); + builder += std::string_view(last_match_end, match[0].first); link.begin = fixed_line.length(); - fixed_line += std::string(ccbuf, Utf8Encode(ccbuf, SCC_PUSH_COLOUR)); - fixed_line += std::string(ccbuf, Utf8Encode(ccbuf, link_colour)); - fixed_line += match[1].str(); + builder.PutUtf8(SCC_PUSH_COLOUR); + builder.PutUtf8(link_colour); + builder += match[1].str(); link.end = fixed_line.length(); - fixed_line += std::string(ccbuf, Utf8Encode(ccbuf, SCC_POP_COLOUR)); + builder.PutUtf8(SCC_POP_COLOUR); last_match_end = match[0].second; }