From b6a688e5400d48dc228d05a8d876fbf70f66cb21 Mon Sep 17 00:00:00 2001 From: Ted John Date: Fri, 16 Oct 2020 00:13:52 +0100 Subject: [PATCH] Refactor format tokens --- src/openrct2-ui/TextComposition.cpp | 2 - src/openrct2-ui/interface/InGameConsole.cpp | 11 +- src/openrct2-ui/interface/InGameConsole.h | 3 +- src/openrct2-ui/scripting/CustomWindow.cpp | 4 +- src/openrct2-ui/scripting/ScWidget.hpp | 2 +- src/openrct2-ui/scripting/ScWindow.hpp | 4 +- src/openrct2-ui/windows/Changelog.cpp | 7 - src/openrct2-ui/windows/Player.cpp | 11 +- src/openrct2-ui/windows/TextInput.cpp | 1 - src/openrct2/core/String.cpp | 12 - src/openrct2/core/String.hpp | 6 - src/openrct2/drawing/Drawing.String.cpp | 92 +- src/openrct2/drawing/Font.cpp | 2 +- src/openrct2/drawing/ScrollingText.cpp | 8 +- src/openrct2/interface/InteractiveConsole.cpp | 6 +- src/openrct2/interface/InteractiveConsole.h | 4 +- src/openrct2/interface/StdInOutConsole.cpp | 8 +- .../localisation/ConversionTables.cpp | 96 +- src/openrct2/localisation/FormatCodes.cpp | 232 ++-- src/openrct2/localisation/FormatCodes.h | 137 +-- src/openrct2/localisation/Formatting.cpp | 102 +- src/openrct2/localisation/Formatting.h | 8 +- src/openrct2/localisation/Language.cpp | 125 -- src/openrct2/localisation/Language.h | 3 - src/openrct2/localisation/LanguagePack.cpp | 69 +- src/openrct2/localisation/Localisation.cpp | 1015 ----------------- src/openrct2/localisation/Localisation.h | 6 - src/openrct2/localisation/UTF8.cpp | 90 +- src/openrct2/network/NetworkBase.cpp | 24 +- src/openrct2/network/NetworkPlayer.cpp | 1 - src/openrct2/rct1/S4Importer.cpp | 1 - src/openrct2/rct2/S6Exporter.cpp | 2 +- src/openrct2/rct2/S6Importer.cpp | 23 +- src/openrct2/scenario/ScenarioRepository.cpp | 14 +- src/openrct2/scripting/ScPark.hpp | 11 +- src/openrct2/scripting/ScriptEngine.cpp | 2 +- src/openrct2/title/TitleScreen.cpp | 21 +- src/openrct2/world/Banner.cpp | 29 +- 38 files changed, 445 insertions(+), 1749 deletions(-) diff --git a/src/openrct2-ui/TextComposition.cpp b/src/openrct2-ui/TextComposition.cpp index b28958580f..227f8b262e 100644 --- a/src/openrct2-ui/TextComposition.cpp +++ b/src/openrct2-ui/TextComposition.cpp @@ -89,7 +89,6 @@ void TextComposition::HandleMessage(const SDL_Event* e) } utf8* newText = String::Duplicate(e->text.text); - utf8_remove_formatting(newText, false); Insert(newText); Memory::Free(newText); @@ -186,7 +185,6 @@ void TextComposition::HandleMessage(const SDL_Event* e) if ((modifier & KEYBOARD_PRIMARY_MODIFIER) && SDL_HasClipboardText()) { utf8* text = SDL_GetClipboardText(); - utf8_remove_formatting(text, false); Insert(text); SDL_free(text); window_update_textbox(); diff --git a/src/openrct2-ui/interface/InGameConsole.cpp b/src/openrct2-ui/interface/InGameConsole.cpp index 8bf82cdaba..99e13a5b2a 100644 --- a/src/openrct2-ui/interface/InGameConsole.cpp +++ b/src/openrct2-ui/interface/InGameConsole.cpp @@ -199,13 +199,13 @@ void InGameConsole::Toggle() } } -void InGameConsole::WriteLine(const std::string& input, uint32_t colourFormat) +void InGameConsole::WriteLine(const std::string& input, FormatToken colourFormat) { // Include text colour format only for special cases // The draw function handles the default text colour differently - utf8 colourCodepoint[4]{}; - if (colourFormat != FORMAT_WINDOW_COLOUR_2) - utf8_write_codepoint(colourCodepoint, colourFormat); + auto colourCodepoint = ""; + if (colourFormat != FormatToken::ColourWindow2) + colourCodepoint = "{"; std::string line; std::size_t splitPos = 0; @@ -254,9 +254,6 @@ void InGameConsole::Update() } } } - - // Remove unwanted characters in console input - utf8_remove_format_codes(_consoleCurrentLine, false); } // Flash the caret diff --git a/src/openrct2-ui/interface/InGameConsole.h b/src/openrct2-ui/interface/InGameConsole.h index f6e492e4ec..359089f7c3 100644 --- a/src/openrct2-ui/interface/InGameConsole.h +++ b/src/openrct2-ui/interface/InGameConsole.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include namespace OpenRCT2::Ui @@ -52,7 +53,7 @@ namespace OpenRCT2::Ui void Close() override; void Hide() override; void Toggle(); - void WriteLine(const std::string& s, uint32_t colourFormat) override; + void WriteLine(const std::string& s, FormatToken colourFormat) override; void Input(ConsoleInput input); void RefreshCaret(size_t position = 0); diff --git a/src/openrct2-ui/scripting/CustomWindow.cpp b/src/openrct2-ui/scripting/CustomWindow.cpp index ec0f237a6d..3b24fc15b7 100644 --- a/src/openrct2-ui/scripting/CustomWindow.cpp +++ b/src/openrct2-ui/scripting/CustomWindow.cpp @@ -270,7 +270,7 @@ namespace OpenRCT2::Ui::Windows result.MaxWidth = GetOptionalInt(desc["maxWidth"]); result.MinHeight = GetOptionalInt(desc["minHeight"]); result.MaxHeight = GetOptionalInt(desc["maxHeight"]); - result.Title = language_convert_string(desc["title"].as_string()); + result.Title = desc["title"].as_string(); result.Id = GetOptionalInt(desc["id"]); result.TabIndex = GetOptionalInt(desc["tabIndex"]); @@ -1077,7 +1077,7 @@ namespace OpenRCT2::Ui::Windows auto customWidgetInfo = customInfo.GetCustomWidgetDesc(w, widgetIndex); if (customWidgetInfo != nullptr) { - customWidgetInfo->Text = language_convert_string(value); + customWidgetInfo->Text = value; w->widgets[widgetIndex].string = customWidgetInfo->Text.data(); widget_invalidate(w, widgetIndex); } diff --git a/src/openrct2-ui/scripting/ScWidget.hpp b/src/openrct2-ui/scripting/ScWidget.hpp index 3c8e671671..50a875b02c 100644 --- a/src/openrct2-ui/scripting/ScWidget.hpp +++ b/src/openrct2-ui/scripting/ScWidget.hpp @@ -314,7 +314,7 @@ namespace OpenRCT2::Scripting auto widget = GetWidget(); if (widget != nullptr && (widget->flags & WIDGET_FLAGS::TEXT_IS_STRING) && widget->string != nullptr) { - return language_convert_string_to_tokens(widget->string); + return widget->string; } } return ""; diff --git a/src/openrct2-ui/scripting/ScWindow.hpp b/src/openrct2-ui/scripting/ScWindow.hpp index f91f754a2b..8601841a96 100644 --- a/src/openrct2-ui/scripting/ScWindow.hpp +++ b/src/openrct2-ui/scripting/ScWindow.hpp @@ -243,7 +243,7 @@ namespace OpenRCT2::Scripting auto w = GetWindow(); if (w != nullptr && w->classification == WC_CUSTOM) { - return language_convert_string_to_tokens(GetWindowTitle(w)); + return GetWindowTitle(w); } return {}; } @@ -252,7 +252,7 @@ namespace OpenRCT2::Scripting auto w = GetWindow(); if (w != nullptr && w->classification == WC_CUSTOM) { - UpdateWindowTitle(w, language_convert_string(value)); + UpdateWindowTitle(w, value); } } diff --git a/src/openrct2-ui/windows/Changelog.cpp b/src/openrct2-ui/windows/Changelog.cpp index e3bcf8efe4..dbb0f0bf40 100644 --- a/src/openrct2-ui/windows/Changelog.cpp +++ b/src/openrct2-ui/windows/Changelog.cpp @@ -236,13 +236,6 @@ static void window_changelog_process_changelog_text(const std::string& text) while ((pos = text.find("\n", prev)) != std::string::npos) { std::string line = text.substr(prev, pos - prev); - for (char* ch = line.data(); *ch != '\0'; ch++) - { - if (utf8_is_format_code(*ch)) - { - *ch = FORMAT_OUTLINE_OFF; - } - } _changelogLines.push_back(line); prev = pos + 1; } diff --git a/src/openrct2-ui/windows/Player.cpp b/src/openrct2-ui/windows/Player.cpp index 104020e563..13b473b5d3 100644 --- a/src/openrct2-ui/windows/Player.cpp +++ b/src/openrct2-ui/windows/Player.cpp @@ -329,13 +329,12 @@ void window_player_overview_paint(rct_window* w, rct_drawpixelinfo* dpi) if (groupindex != -1) { rct_widget* widget = &window_player_overview_widgets[WIDX_GROUP]; - char buffer[300]; - char* lineCh; - lineCh = buffer; - lineCh = utf8_write_codepoint(lineCh, FORMAT_WINDOW_COLOUR_2); - safe_strcpy(lineCh, network_get_group_name(groupindex), sizeof(buffer) - (lineCh - buffer)); + + thread_local std::string buffer; + buffer.assign("{WINDOW_COLOUR_2}"); + buffer += network_get_group_name(groupindex); auto ft = Formatter(); - ft.Add(buffer); + ft.Add(buffer.c_str()); DrawTextEllipsised( dpi, w->windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, diff --git a/src/openrct2-ui/windows/TextInput.cpp b/src/openrct2-ui/windows/TextInput.cpp index 6a340cb392..db80b5873c 100644 --- a/src/openrct2-ui/windows/TextInput.cpp +++ b/src/openrct2-ui/windows/TextInput.cpp @@ -85,7 +85,6 @@ void window_text_input_open( if (existing_text != STR_NONE) format_string(buffer, maxLength, existing_text, &existing_args); - utf8_remove_format_codes(buffer, false); window_text_input_raw_open(call_w, call_widget, title, description, buffer, maxLength); } diff --git a/src/openrct2/core/String.cpp b/src/openrct2/core/String.cpp index 3b2b9d7764..1fc0b59bc2 100644 --- a/src/openrct2/core/String.cpp +++ b/src/openrct2/core/String.cpp @@ -771,18 +771,6 @@ namespace String return res; #endif } - - bool ContainsColourCode(const std::string& string) - { - for (unsigned char c : string) - { - if (c >= FORMAT_COLOUR_CODE_START && c <= FORMAT_COLOUR_CODE_END) - { - return true; - } - } - return false; - } } // namespace String char32_t CodepointView::iterator::GetNextCodepoint(const char* ch, const char** next) diff --git a/src/openrct2/core/String.hpp b/src/openrct2/core/String.hpp index 69d237469b..7e7c373a0c 100644 --- a/src/openrct2/core/String.hpp +++ b/src/openrct2/core/String.hpp @@ -111,12 +111,6 @@ namespace String * Returns an uppercased version of a UTF-8 string. */ std::string ToUpper(const std::string_view& src); - - /** - * Returns true if the string contains an RCT2 colour code. - */ - bool ContainsColourCode(const std::string& string); - } // namespace String class CodepointView diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index df2ca8b904..4ae0a79816 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -50,7 +50,7 @@ int32_t gfx_get_string_width_new_lined(std::string_view text) FmtString fmt(text); for (const auto& token : fmt) { - if (token.kind == FORMAT_NEWLINE || token.kind == FORMAT_NEWLINE_SMALLER) + if (token.kind == FormatToken::Newline || token.kind == FormatToken::NewlineSmall) { auto width = gfx_get_string_width(buffer); if (!maxWidth || maxWidth > width) @@ -222,7 +222,7 @@ int32_t gfx_wrap_string(utf8* text, int32_t width, int32_t* outNumLines, int32_t } } } - else if (token.kind == FORMAT_NEWLINE) + else if (token.kind == FormatToken::Newline) { buffer.push_back('\0'); @@ -352,13 +352,12 @@ int32_t string_get_height_raw(char* buffer) else if (fontBase == FONT_SPRITE_BASE_TINY) height += 6; - char* ch = buffer; - while (*ch != 0) + FmtString fmt(buffer); + for (const auto& token : fmt) { - char c = *ch++; - switch (c) + switch (token.kind) { - case FORMAT_NEWLINE: + case FormatToken::Newline: if (fontBase == FONT_SPRITE_BASE_SMALL || fontBase == FONT_SPRITE_BASE_MEDIUM) { height += 10; @@ -371,7 +370,7 @@ int32_t string_get_height_raw(char* buffer) } height += 18; break; - case FORMAT_NEWLINE_SMALLER: + case FormatToken::NewlineSmall: if (fontBase == FONT_SPRITE_BASE_SMALL || fontBase == FONT_SPRITE_BASE_MEDIUM) { height += 5; @@ -384,33 +383,17 @@ int32_t string_get_height_raw(char* buffer) } height += 9; break; - case FORMAT_TINYFONT: + case FormatToken::FontTiny: fontBase = FONT_SPRITE_BASE_TINY; break; - case FORMAT_MEDIUMFONT: + case FormatToken::FontMedium: fontBase = FONT_SPRITE_BASE_MEDIUM; break; - case FORMAT_SMALLFONT: + case FormatToken::FontSmall: fontBase = FONT_SPRITE_BASE_SMALL; break; - default: - if (c >= 32) - continue; - if (c <= 4) - { - ch++; - continue; - } - if (c <= 16) - continue; - ch += 2; - if (c <= 22) - continue; - ch += 2; - break; } } - return height; } @@ -452,21 +435,27 @@ void gfx_draw_string_centred_wrapped_partial( { int32_t halfWidth = gfx_get_string_width(buffer) / 2; - utf8* ch = buffer; - utf8* nextCh; - int32_t codepoint; - while ((codepoint = utf8_get_next(ch, const_cast(&nextCh))) != 0) + FmtString fmt(buffer); + for (const auto& token : fmt) { - if (!utf8_is_format_code(codepoint)) + bool doubleBreak = false; + if (token.IsLiteral()) { - numCharactersDrawn++; - if (numCharactersDrawn > numCharactersToDraw) + CodepointView codepoints(token.text); + for (auto it = codepoints.begin(); it != codepoints.end(); it++) { - *ch = 0; - break; + numCharactersDrawn++; + if (numCharactersDrawn > numCharactersToDraw) + { + auto ch = const_cast(&token.text[it.GetIndex()]); + *ch = '\0'; + doubleBreak = true; + break; + } } } - ch = nextCh; + if (doubleBreak) + break; } screenCoords = { coords.x - halfWidth, lineY }; @@ -679,51 +668,51 @@ static void ttf_process_format_code(rct_drawpixelinfo* dpi, const FmtString::tok { switch (token.kind) { - case FORMAT_MOVE_X: + case FormatToken::Move: info->x = info->startX + token.parameter; break; - case FORMAT_NEWLINE: + case FormatToken::Newline: info->x = info->startX; info->y += font_get_line_height(info->font_sprite_base); break; - case FORMAT_NEWLINE_SMALLER: + case FormatToken::NewlineSmall: info->x = info->startX; info->y += font_get_line_height_small(info->font_sprite_base); break; - case FORMAT_TINYFONT: + case FormatToken::FontTiny: info->font_sprite_base = FONT_SPRITE_BASE_TINY; break; - case FORMAT_SMALLFONT: + case FormatToken::FontSmall: info->font_sprite_base = FONT_SPRITE_BASE_SMALL; break; - case FORMAT_MEDIUMFONT: + case FormatToken::FontMedium: info->font_sprite_base = FONT_SPRITE_BASE_MEDIUM; break; - case FORMAT_OUTLINE: + case FormatToken::OutlineEnable: info->flags |= TEXT_DRAW_FLAG_OUTLINE; break; - case FORMAT_OUTLINE_OFF: + case FormatToken::OutlineDisable: info->flags &= ~TEXT_DRAW_FLAG_OUTLINE; break; - case FORMAT_WINDOW_COLOUR_1: + case FormatToken::ColourWindow1: { uint16_t flags = info->flags; colour_char_window(gCurrentWindowColours[0], &flags, info->palette); break; } - case FORMAT_WINDOW_COLOUR_2: + case FormatToken::ColourWindow2: { uint16_t flags = info->flags; colour_char_window(gCurrentWindowColours[1], &flags, info->palette); break; } - case FORMAT_WINDOW_COLOUR_3: + case FormatToken::ColourWindow3: { uint16_t flags = info->flags; colour_char_window(gCurrentWindowColours[2], &flags, info->palette); break; } - case FORMAT_INLINE_SPRITE: + case FormatToken::InlineSprite: { auto g1 = gfx_get_g1_element(token.parameter & 0x7FFFF); if (g1 != nullptr) @@ -737,10 +726,11 @@ static void ttf_process_format_code(rct_drawpixelinfo* dpi, const FmtString::tok break; } default: - if (token.kind >= FORMAT_COLOUR_CODE_START && token.kind <= FORMAT_COLOUR_CODE_END) + if (FormatTokenIsColour(token.kind)) { uint16_t flags = info->flags; - colour_char(token.kind - FORMAT_COLOUR_CODE_START, &flags, info->palette); + auto colourIndex = FormatTokenGetTextColourIndex(token.kind); + colour_char(static_cast(colourIndex), &flags, info->palette); } break; } diff --git a/src/openrct2/drawing/Font.cpp b/src/openrct2/drawing/Font.cpp index 4c111a1544..cba10d38bf 100644 --- a/src/openrct2/drawing/Font.cpp +++ b/src/openrct2/drawing/Font.cpp @@ -240,7 +240,7 @@ void font_sprite_initialise_characters() int32_t width = 0; if (g1 != nullptr) { - if (glyphIndex < (FORMAT_ARGUMENT_CODE_START - 32) || glyphIndex >= (FORMAT_COLOUR_CODE_END - 32)) + if (glyphIndex < 91 || glyphIndex >= 109) { width = (g1->width + 2 * g1->x_offset) - 1; } diff --git a/src/openrct2/drawing/ScrollingText.cpp b/src/openrct2/drawing/ScrollingText.cpp index e47eec2919..4c6cc7f029 100644 --- a/src/openrct2/drawing/ScrollingText.cpp +++ b/src/openrct2/drawing/ScrollingText.cpp @@ -1540,12 +1540,12 @@ static void scrolling_text_set_bitmap_for_sprite( } } } - else if (token.kind <= FORMAT_COLOUR_CODE_END && token.kind >= FORMAT_COLOUR_CODE_START) + else if (FormatTokenIsColour(token.kind)) { auto g1 = gfx_get_g1_element(SPR_TEXT_PALETTE); if (g1 != nullptr) { - auto colourIndex = token.kind - FORMAT_COLOUR_CODE_START; + auto colourIndex = FormatTokenGetTextColourIndex(token.kind); characterColour = g1->offset[colourIndex * 4]; } } @@ -1574,12 +1574,12 @@ static void scrolling_text_set_bitmap_for_ttf( { ttfBuffer.append(token.text); } - else if (token.kind >= FORMAT_COLOUR_CODE_START && token.kind <= FORMAT_COLOUR_CODE_END) + else if (FormatTokenIsColour(token.kind)) { auto g1 = gfx_get_g1_element(SPR_TEXT_PALETTE); if (g1 != nullptr) { - auto colourIndex = token.kind - FORMAT_COLOUR_CODE_START; + auto colourIndex = FormatTokenGetTextColourIndex(token.kind); colour = g1->offset[colourIndex * 4]; } } diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index 8d43edc083..6526bcebc4 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1931,17 +1931,17 @@ void InteractiveConsole::Execute(const std::string& s) void InteractiveConsole::WriteLine(const std::string& s) { - WriteLine(s, FORMAT_WINDOW_COLOUR_2); + WriteLine(s, FormatToken::ColourWindow2); } void InteractiveConsole::WriteLineError(const std::string& s) { - WriteLine(s, FORMAT_RED); + WriteLine(s, FormatToken::ColourRed); } void InteractiveConsole::WriteLineWarning(const std::string& s) { - WriteLine(s, FORMAT_YELLOW); + WriteLine(s, FormatToken::ColourYellow); } void InteractiveConsole::WriteFormatLine(const char* format, ...) diff --git a/src/openrct2/interface/InteractiveConsole.h b/src/openrct2/interface/InteractiveConsole.h index 2cb845d282..0f24115de6 100644 --- a/src/openrct2/interface/InteractiveConsole.h +++ b/src/openrct2/interface/InteractiveConsole.h @@ -46,7 +46,7 @@ public: virtual void Clear() abstract; virtual void Close() abstract; virtual void Hide() abstract; - virtual void WriteLine(const std::string& s, uint32_t colourFormat) abstract; + virtual void WriteLine(const std::string& s, FormatToken colourFormat) abstract; }; class StdInOutConsole final : public InteractiveConsole @@ -68,5 +68,5 @@ public: { InteractiveConsole::WriteLine(s); } - void WriteLine(const std::string& s, uint32_t colourFormat) override; + void WriteLine(const std::string& s, FormatToken colourFormat) override; }; diff --git a/src/openrct2/interface/StdInOutConsole.cpp b/src/openrct2/interface/StdInOutConsole.cpp index cf64e785cf..4570a9dfd5 100644 --- a/src/openrct2/interface/StdInOutConsole.cpp +++ b/src/openrct2/interface/StdInOutConsole.cpp @@ -108,17 +108,17 @@ void StdInOutConsole::Close() openrct2_finish(); } -void StdInOutConsole::WriteLine(const std::string& s, uint32_t colourFormat) +void StdInOutConsole::WriteLine(const std::string& s, FormatToken colourFormat) { std::string formatBegin; - if (colourFormat != FORMAT_WINDOW_COLOUR_2) + if (colourFormat != FormatToken::ColourWindow2) { switch (colourFormat) { - case FORMAT_RED: + case FormatToken::ColourRed: formatBegin = "\033[31m"; break; - case FORMAT_YELLOW: + case FormatToken::ColourYellow: formatBegin = "\033[33m"; break; } diff --git a/src/openrct2/localisation/ConversionTables.cpp b/src/openrct2/localisation/ConversionTables.cpp index e4c87fa261..a4e745cb97 100644 --- a/src/openrct2/localisation/ConversionTables.cpp +++ b/src/openrct2/localisation/ConversionTables.cpp @@ -17,54 +17,54 @@ // clang-format off const encoding_convert_entry RCT2ToUnicodeTable[] = { - { 1, FORMAT_MOVE_X }, - { 2, FORMAT_ADJUST_PALETTE }, - { 5, FORMAT_NEWLINE }, - { 6, FORMAT_NEWLINE_SMALLER }, - { 7, FORMAT_TINYFONT }, - { 8, FORMAT_BIGFONT }, - { 9, FORMAT_MEDIUMFONT }, - { 10, FORMAT_SMALLFONT }, - { 11, FORMAT_OUTLINE }, - { 12, FORMAT_OUTLINE_OFF }, - { 13, FORMAT_WINDOW_COLOUR_1 }, - { 14, FORMAT_WINDOW_COLOUR_2 }, - { 15, FORMAT_WINDOW_COLOUR_3 }, - { 17, FORMAT_NEWLINE_X_Y }, - { 23, FORMAT_INLINE_SPRITE }, - { 123, FORMAT_COMMA32 }, - { 124, FORMAT_INT32 }, - { 125, FORMAT_COMMA2DP32 }, - { 126, FORMAT_COMMA16 }, - { 127, FORMAT_UINT16 }, - { 128, FORMAT_CURRENCY2DP }, - { 129, FORMAT_CURRENCY }, - { 130, FORMAT_STRINGID }, - { 131, FORMAT_STRINGID2 }, - { 132, FORMAT_STRING }, - { 133, FORMAT_MONTHYEAR }, - { 134, FORMAT_MONTH }, - { 135, FORMAT_VELOCITY }, - { 136, FORMAT_POP16 }, - { 137, FORMAT_PUSH16 }, - { 138, FORMAT_DURATION }, - { 139, FORMAT_REALTIME }, - { 140, FORMAT_LENGTH }, - { 141, FORMAT_SPRITE }, - { 142, FORMAT_BLACK }, - { 143, FORMAT_GREY }, - { 144, FORMAT_WHITE }, - { 145, FORMAT_RED }, - { 146, FORMAT_GREEN }, - { 147, FORMAT_YELLOW }, - { 148, FORMAT_TOPAZ }, - { 149, FORMAT_CELADON }, - { 150, FORMAT_BABYBLUE }, - { 151, FORMAT_PALELAVENDER }, - { 152, FORMAT_PALEGOLD }, - { 153, FORMAT_LIGHTPINK }, - { 154, FORMAT_PEARLAQUA }, - { 155, FORMAT_PALESILVER }, + // { 1, FORMAT_MOVE_X }, + // { 2, FORMAT_ADJUST_PALETTE }, + // { 5, FORMAT_NEWLINE }, + // { 6, FORMAT_NEWLINE_SMALLER }, + // { 7, FORMAT_TINYFONT }, + // { 8, FORMAT_BIGFONT }, + // { 9, FORMAT_MEDIUMFONT }, + // { 10, FORMAT_SMALLFONT }, + // { 11, FORMAT_OUTLINE }, + // { 12, FORMAT_OUTLINE_OFF }, + // { 13, FORMAT_WINDOW_COLOUR_1 }, + // { 14, FORMAT_WINDOW_COLOUR_2 }, + // { 15, FORMAT_WINDOW_COLOUR_3 }, + // { 17, FORMAT_NEWLINE_X_Y }, + // { 23, FORMAT_INLINE_SPRITE }, + // { 123, FORMAT_COMMA32 }, + // { 124, FORMAT_INT32 }, + // { 125, FORMAT_COMMA2DP32 }, + // { 126, FORMAT_COMMA16 }, + // { 127, FORMAT_UINT16 }, + // { 128, FORMAT_CURRENCY2DP }, + // { 129, FORMAT_CURRENCY }, + // { 130, FORMAT_STRINGID }, + // { 131, FORMAT_STRINGID2 }, + // { 132, FORMAT_STRING }, + // { 133, FORMAT_MONTHYEAR }, + // { 134, FORMAT_MONTH }, + // { 135, FORMAT_VELOCITY }, + // { 136, FORMAT_POP16 }, + // { 137, FORMAT_PUSH16 }, + // { 138, FORMAT_DURATION }, + // { 139, FORMAT_REALTIME }, + // { 140, FORMAT_LENGTH }, + // { 141, FORMAT_SPRITE }, + // { 142, FORMAT_BLACK }, + // { 143, FORMAT_GREY }, + // { 144, FORMAT_WHITE }, + // { 145, FORMAT_RED }, + // { 146, FORMAT_GREEN }, + // { 147, FORMAT_YELLOW }, + // { 148, FORMAT_TOPAZ }, + // { 149, FORMAT_CELADON }, + // { 150, FORMAT_BABYBLUE }, + // { 151, FORMAT_PALELAVENDER }, + // { 152, FORMAT_PALEGOLD }, + // { 153, FORMAT_LIGHTPINK }, + // { 154, FORMAT_PEARLAQUA }, + // { 155, FORMAT_PALESILVER }, { CSChar::a_ogonek_uc, UnicodeChar::a_ogonek_uc }, { CSChar::up, UnicodeChar::up }, { CSChar::c_acute_uc, UnicodeChar::c_acute_uc }, diff --git a/src/openrct2/localisation/FormatCodes.cpp b/src/openrct2/localisation/FormatCodes.cpp index 4fba694267..38013c70c0 100644 --- a/src/openrct2/localisation/FormatCodes.cpp +++ b/src/openrct2/localisation/FormatCodes.cpp @@ -9,90 +9,174 @@ #include "FormatCodes.h" -#include "../common.h" -#include "../core/String.hpp" -#include "Localisation.h" - -#include - -#pragma region Format codes - -struct format_code_token -{ - uint32_t code; - const char* token; -}; +#include // clang-format off -static constexpr const format_code_token format_code_tokens[] = { - { FORMAT_MOVE_X, "MOVE_X" }, - { FORMAT_ADJUST_PALETTE, "ADJUST_PALETTE" }, - { FORMAT_NEWLINE, "NEWLINE" }, - { FORMAT_NEWLINE_SMALLER, "NEWLINE_SMALLER" }, - { FORMAT_TINYFONT, "TINYFONT" }, - { FORMAT_BIGFONT, "BIGFONT" }, - { FORMAT_MEDIUMFONT, "MEDIUMFONT" }, - { FORMAT_SMALLFONT, "SMALLFONT" }, - { FORMAT_OUTLINE, "OUTLINE" }, - { FORMAT_OUTLINE_OFF, "OUTLINE_OFF" }, - { FORMAT_WINDOW_COLOUR_1, "WINDOW_COLOUR_1" }, - { FORMAT_WINDOW_COLOUR_2, "WINDOW_COLOUR_2" }, - { FORMAT_WINDOW_COLOUR_3, "WINDOW_COLOUR_3" }, - { FORMAT_NEWLINE_X_Y, "NEWLINE_X_Y" }, - { FORMAT_INLINE_SPRITE, "INLINE_SPRITE" }, - { FORMAT_COMMA32, "COMMA32" }, - { FORMAT_INT32, "INT32" }, - { FORMAT_COMMA2DP32, "COMMA2DP32" }, - { FORMAT_COMMA16, "COMMA16" }, - { FORMAT_UINT16, "UINT16" }, - { FORMAT_CURRENCY2DP, "CURRENCY2DP" }, - { FORMAT_CURRENCY, "CURRENCY" }, - { FORMAT_STRINGID, "STRINGID" }, - { FORMAT_STRINGID2, "STRINGID2" }, - { FORMAT_STRING, "STRING" }, - { FORMAT_MONTHYEAR, "MONTHYEAR" }, - { FORMAT_MONTH, "MONTH" }, - { FORMAT_VELOCITY, "VELOCITY" }, - { FORMAT_POP16, "POP16" }, - { FORMAT_PUSH16, "PUSH16" }, - { FORMAT_DURATION, "DURATION" }, - { FORMAT_REALTIME, "REALTIME" }, - { FORMAT_LENGTH, "LENGTH" }, - { FORMAT_SPRITE, "SPRITE" }, - { FORMAT_BLACK, "BLACK" }, - { FORMAT_GREY, "GREY" }, - { FORMAT_WHITE, "WHITE" }, - { FORMAT_RED, "RED" }, - { FORMAT_GREEN, "GREEN" }, - { FORMAT_YELLOW, "YELLOW" }, - { FORMAT_TOPAZ, "TOPAZ" }, - { FORMAT_CELADON, "CELADON" }, - { FORMAT_BABYBLUE, "BABYBLUE" }, - { FORMAT_PALELAVENDER, "PALELAVENDER" }, - { FORMAT_PALEGOLD, "PALEGOLD" }, - { FORMAT_LIGHTPINK, "LIGHTPINK" }, - { FORMAT_PEARLAQUA, "PEARLAQUA" }, - { FORMAT_PALESILVER, "PALESILVER" }, - { FORMAT_COMMA1DP16, "COMMA1DP16" } +static const std::unordered_map FormatTokenMap = { + { "MOVE_X", FormatToken::Move, }, + { "NEWLINE", FormatToken::Newline, }, + { "NEWLINE_SMALLER", FormatToken::NewlineSmall, }, + { "TINYFONT", FormatToken::FontTiny, }, + { "BIGFONT", FormatToken::FontBig, }, + { "MEDIUMFONT", FormatToken::FontMedium, }, + { "SMALLFONT", FormatToken::FontSmall, }, + { "OUTLINE", FormatToken::OutlineEnable, }, + { "OUTLINE_OFF", FormatToken::OutlineDisable, }, + { "WINDOW_COLOUR_1", FormatToken::ColourWindow1, }, + { "WINDOW_COLOUR_2", FormatToken::ColourWindow2, }, + { "WINDOW_COLOUR_3", FormatToken::ColourWindow3, }, + { "INLINE_SPRITE", FormatToken::InlineSprite, }, + { "COMMA32", FormatToken::Comma32, }, + { "INT32", FormatToken::Int32, }, + { "COMMA1DP16", FormatToken::Comma1dp16, }, + { "COMMA2DP32", FormatToken::Comma2dp32, }, + { "COMMA16", FormatToken::Comma16, }, + { "UINT16", FormatToken::Uint16, }, + { "CURRENCY2DP", FormatToken::Currency2dp, }, + { "CURRENCY", FormatToken::Currency, }, + { "STRINGID", FormatToken::StringId, }, + { "STRING", FormatToken::String, }, + { "MONTHYEAR", FormatToken::MonthYear, }, + { "MONTH", FormatToken::Month, }, + { "VELOCITY", FormatToken::Velocity, }, + { "POP16", FormatToken::Pop16, }, + { "PUSH16", FormatToken::Push16, }, + { "DURATION", FormatToken::DurationShort, }, + { "REALTIME", FormatToken::DurationLong, }, + { "LENGTH", FormatToken::Length, }, + { "SPRITE", FormatToken::Sprite, }, + { "BLACK", FormatToken::ColourBlack, }, + { "GREY", FormatToken::ColourGrey, }, + { "WHITE", FormatToken::ColourWhite, }, + { "RED", FormatToken::ColourRed, }, + { "GREEN", FormatToken::ColourGreen, }, + { "YELLOW", FormatToken::ColourYellow, }, + { "TOPAZ", FormatToken::ColourTopaz, }, + { "CELADON", FormatToken::ColourCeladon, }, + { "BABYBLUE", FormatToken::ColourBabyBlue, }, + { "PALELAVENDER", FormatToken::ColourPaleLavender, }, + { "PALEGOLD", FormatToken::ColourPaleGold, }, + { "LIGHTPINK", FormatToken::ColourLightPink, }, + { "PEARLAQUA", FormatToken::ColourPearlAqua, }, + { "PALESILVER", FormatToken::ColourPaleSilver, }, }; // clang-format on -uint32_t format_get_code(std::string_view token) +FormatToken FormatTokenFromString(std::string_view token) { - auto result = std::find_if(std::begin(format_code_tokens), std::end(format_code_tokens), [token](auto& fct) { - return String::Equals(token, fct.token, true); - }); - return result != std::end(format_code_tokens) ? result->code : 0; + auto result = FormatTokenMap.find(token); + return result != std::end(FormatTokenMap) ? result->second : FormatToken::Unknown; } -const char* format_get_token(uint32_t code) +std::string_view FormatTokenToString(FormatToken token) { - for (uint32_t i = 0; i < std::size(format_code_tokens); i++) + for (const auto& t : FormatTokenMap) { - if (code == format_code_tokens[i].code) - return format_code_tokens[i].token; + if (t.second == token) + { + return t.first; + } } - return nullptr; + return {}; +} + +bool FormatTokenTakesArgument(FormatToken token) +{ + switch (token) + { + case FormatToken::Comma32: + case FormatToken::Int32: + case FormatToken::Comma1dp16: + case FormatToken::Comma2dp32: + case FormatToken::Comma16: + case FormatToken::Uint16: + case FormatToken::Currency2dp: + case FormatToken::Currency: + case FormatToken::StringId: + case FormatToken::String: + case FormatToken::MonthYear: + case FormatToken::Month: + case FormatToken::Velocity: + case FormatToken::DurationShort: + case FormatToken::DurationLong: + case FormatToken::Length: + case FormatToken::Sprite: + return true; + } + return false; +} + +bool FormatTokenIsColour(FormatToken token) +{ + switch (token) + { + case FormatToken::ColourBlack: + case FormatToken::ColourGrey: + case FormatToken::ColourWhite: + case FormatToken::ColourRed: + case FormatToken::ColourGreen: + case FormatToken::ColourYellow: + case FormatToken::ColourTopaz: + case FormatToken::ColourCeladon: + case FormatToken::ColourBabyBlue: + case FormatToken::ColourPaleLavender: + case FormatToken::ColourPaleGold: + case FormatToken::ColourLightPink: + case FormatToken::ColourPearlAqua: + case FormatToken::ColourPaleSilver: + return true; + } + return false; +} + +size_t FormatTokenGetTextColourIndex(FormatToken token) +{ + switch (token) + { + case FormatToken::ColourBlack: + return 0; + case FormatToken::ColourGrey: + return 1; + case FormatToken::ColourWhite: + return 2; + case FormatToken::ColourRed: + return 3; + case FormatToken::ColourGreen: + return 4; + case FormatToken::ColourYellow: + return 5; + case FormatToken::ColourTopaz: + return 6; + case FormatToken::ColourCeladon: + return 7; + case FormatToken::ColourBabyBlue: + return 8; + case FormatToken::ColourPaleLavender: + return 9; + case FormatToken::ColourPaleGold: + return 10; + case FormatToken::ColourLightPink: + return 11; + case FormatToken::ColourPearlAqua: + return 12; + case FormatToken::ColourPaleSilver: + return 13; + } + return 0; +} + +FormatToken FormatTokenFromTextColour(size_t textColour) +{ + static constexpr const FormatToken tokens[] = { + FormatToken::ColourBlack, FormatToken::ColourGrey, FormatToken::ColourWhite, + FormatToken::ColourRed, FormatToken::ColourGreen, FormatToken::ColourYellow, + FormatToken::ColourTopaz, FormatToken::ColourCeladon, FormatToken::ColourBabyBlue, + FormatToken::ColourPaleLavender, FormatToken::ColourPaleGold, FormatToken::ColourLightPink, + FormatToken::ColourPearlAqua, FormatToken::ColourPaleSilver, + }; + if (textColour > std::size(tokens)) + return FormatToken::ColourBlack; + return tokens[textColour]; } bool utf8_should_use_sprite_for_codepoint(char32_t codepoint) @@ -119,5 +203,3 @@ bool utf8_should_use_sprite_for_codepoint(char32_t codepoint) return false; } } - -#pragma endregion diff --git a/src/openrct2/localisation/FormatCodes.h b/src/openrct2/localisation/FormatCodes.h index 7e2ed2f43e..4e80c5b542 100644 --- a/src/openrct2/localisation/FormatCodes.h +++ b/src/openrct2/localisation/FormatCodes.h @@ -13,92 +13,75 @@ #include -uint32_t format_get_code(std::string_view token); -const char* format_get_token(uint32_t code); - -enum +enum class FormatToken { - // Font format codes + Unknown, + Literal, - // The next byte specifies the X coordinate - FORMAT_MOVE_X = 1, - // The next byte specifies the palette - FORMAT_ADJUST_PALETTE, + Newline, + NewlineSmall, - FORMAT_3, - FORMAT_4, + // With parameters + Move, + InlineSprite, - // Moves to the next line - FORMAT_NEWLINE = 5, - // Moves less than NEWLINE - FORMAT_NEWLINE_SMALLER, + // With arguments + Comma32, + Int32, + Comma1dp16, + Comma2dp32, + Comma16, + Uint16, + Currency2dp, + Currency, + StringId, + String, + MonthYear, + Month, + Velocity, + DurationShort, + DurationLong, + Length, + Sprite, + Pop16, + Push16, - FORMAT_TINYFONT, - FORMAT_BIGFONT, - FORMAT_MEDIUMFONT, - FORMAT_SMALLFONT, + // Colours + ColourWindow1, + ColourWindow2, + ColourWindow3, + ColourBlack, + ColourGrey, + ColourWhite, + ColourRed, + ColourGreen, + ColourYellow, + ColourTopaz, + ColourCeladon, + ColourBabyBlue, + ColourPaleLavender, + ColourPaleGold, + ColourLightPink, + ColourPearlAqua, + ColourPaleSilver, - FORMAT_OUTLINE, - FORMAT_OUTLINE_OFF, + // Fonts + FontTiny, + FontSmall, + FontMedium, + FontBig, - // Changes the colour of the text to a predefined window colour. - FORMAT_WINDOW_COLOUR_1, - FORMAT_WINDOW_COLOUR_2, - FORMAT_WINDOW_COLOUR_3, - - FORMAT_16, - - // The next 2 bytes specify the X and Y coordinates - FORMAT_NEWLINE_X_Y = 17, - - // The next 4 bytes specify the sprite - FORMAT_INLINE_SPRITE = 23, - - // Argument format codes - FORMAT_ARGUMENT_CODE_START = 123, // 'z' == 122 or 0x7A - FORMAT_COMMA32 = 123, - FORMAT_INT32, - FORMAT_COMMA2DP32, - FORMAT_COMMA16, - FORMAT_UINT16, - FORMAT_CURRENCY2DP, - FORMAT_CURRENCY, - FORMAT_STRINGID, - FORMAT_STRINGID2, - FORMAT_STRING, - FORMAT_MONTHYEAR, - FORMAT_MONTH, - FORMAT_VELOCITY, - FORMAT_POP16, - FORMAT_PUSH16, - FORMAT_DURATION, - FORMAT_REALTIME, - FORMAT_LENGTH, - FORMAT_SPRITE, - FORMAT_ARGUMENT_CODE_END = FORMAT_SPRITE, - - // Colour format codes - FORMAT_COLOUR_CODE_START = 142, - FORMAT_BLACK = 142, - FORMAT_GREY, - FORMAT_WHITE, - FORMAT_RED, - FORMAT_GREEN, - FORMAT_YELLOW, - FORMAT_TOPAZ, - FORMAT_CELADON, - FORMAT_BABYBLUE, - FORMAT_PALELAVENDER, - FORMAT_PALEGOLD, - FORMAT_LIGHTPINK, - FORMAT_PEARLAQUA, - FORMAT_PALESILVER, - FORMAT_COLOUR_CODE_END = FORMAT_PALESILVER, - - // Format codes that need suitable Unicode allocations - FORMAT_COMMA1DP16 = 20004 + OutlineEnable, + OutlineDisable, }; +FormatToken FormatTokenFromString(std::string_view token); +std::string_view FormatTokenToString(FormatToken token); +bool FormatTokenTakesArgument(FormatToken token); +bool FormatTokenIsColour(FormatToken token); +size_t FormatTokenGetTextColourIndex(FormatToken token); +FormatToken FormatTokenFromTextColour(size_t textColour); + constexpr uint8_t CS_SPRITE_FONT_OFFSET = 32; namespace CSChar diff --git a/src/openrct2/localisation/Formatting.cpp b/src/openrct2/localisation/Formatting.cpp index 2438062382..b9f2950419 100644 --- a/src/openrct2/localisation/Formatting.cpp +++ b/src/openrct2/localisation/Formatting.cpp @@ -60,7 +60,7 @@ namespace OpenRCT2 bool FmtString::token::IsLiteral() const { - return kind == 0; + return kind == FormatToken::Literal; } FmtString::iterator::iterator(std::string_view s, size_t i) @@ -104,7 +104,7 @@ namespace OpenRCT2 { p = *p0; } - current = token(FORMAT_MOVE_X, str.substr(startIndex, i - startIndex), p); + current = token(FormatToken::Move, str.substr(startIndex, i - startIndex), p); return; } else if (inner == "INLINE_SPRITE") @@ -121,7 +121,7 @@ namespace OpenRCT2 p |= (*p2) << 16; p |= (*p3) << 24; } - current = token(FORMAT_INLINE_SPRITE, str.substr(startIndex, i - startIndex), p); + current = token(FormatToken::InlineSprite, str.substr(startIndex, i - startIndex), p); return; } } @@ -151,14 +151,14 @@ namespace OpenRCT2 std::string_view sztoken = str.substr(index, len); if (sztoken.size() >= 2 && sztoken[0] == '{' && sztoken[1] != '{') { - auto kind = format_get_code(sztoken.substr(1, len - 2)); + auto kind = FormatTokenFromString(sztoken.substr(1, len - 2)); return token(kind, sztoken); } else if (sztoken == "\n" || sztoken == "\r") { - return token(FORMAT_NEWLINE, sztoken); + return token(FormatToken::Newline, sztoken); } - return token(0, sztoken); + return token(FormatToken::Literal, sztoken); } const FmtString::token* FmtString::iterator::operator->() const @@ -433,21 +433,21 @@ namespace OpenRCT2 { switch (token) { - case FORMAT_UINT16: - case FORMAT_INT32: + case FormatToken::Uint16: + case FormatToken::Int32: if constexpr (std::is_integral()) { FormatNumber<0, false>(ss, arg); } break; - case FORMAT_COMMA16: - case FORMAT_COMMA32: + case FormatToken::Comma16: + case FormatToken::Comma32: if constexpr (std::is_integral()) { FormatNumber<0, true>(ss, arg); } break; - case FORMAT_COMMA1DP16: + case FormatToken::Comma1dp16: if constexpr (std::is_integral()) { FormatNumber<1, true>(ss, arg); @@ -457,7 +457,7 @@ namespace OpenRCT2 FormatNumber<1, true>(ss, std::round(arg * 10)); } break; - case FORMAT_COMMA2DP32: + case FormatToken::Comma2dp32: if constexpr (std::is_integral()) { FormatNumber<2, true>(ss, arg); @@ -467,19 +467,19 @@ namespace OpenRCT2 FormatNumber<2, true>(ss, std::round(arg * 100)); } break; - case FORMAT_CURRENCY2DP: + case FormatToken::Currency2dp: if constexpr (std::is_integral()) { FormatCurrency<2, true>(ss, arg); } break; - case FORMAT_CURRENCY: + case FormatToken::Currency: if constexpr (std::is_integral()) { FormatCurrency<0, true>(ss, arg); } break; - case FORMAT_VELOCITY: + case FormatToken::Velocity: if constexpr (std::is_integral()) { switch (gConfigGeneral.measurement_format) @@ -497,19 +497,19 @@ namespace OpenRCT2 } } break; - case FORMAT_DURATION: + case FormatToken::DurationShort: if constexpr (std::is_integral()) { FormatMinutesSeconds(ss, arg); } break; - case FORMAT_REALTIME: + case FormatToken::DurationLong: if constexpr (std::is_integral()) { FormatHoursMinutes(ss, arg); } break; - case FORMAT_LENGTH: + case FormatToken::Length: if constexpr (std::is_integral()) { switch (gConfigGeneral.measurement_format) @@ -525,7 +525,7 @@ namespace OpenRCT2 } } break; - case FORMAT_MONTHYEAR: + case FormatToken::MonthYear: if constexpr (std::is_integral()) { auto month = date_get_month(arg); @@ -533,7 +533,7 @@ namespace OpenRCT2 FormatStringId(ss, STR_DATE_FORMAT_MY, month, year); } break; - case FORMAT_MONTH: + case FormatToken::Month: if constexpr (std::is_integral()) { auto szMonth = language_get_string(DateGameMonthNames[date_get_month(arg)]); @@ -543,7 +543,7 @@ namespace OpenRCT2 } } break; - case FORMAT_STRING: + case FormatToken::String: if constexpr (std::is_same()) { ss << arg; @@ -553,7 +553,7 @@ namespace OpenRCT2 ss << arg.c_str(); } break; - case FORMAT_SPRITE: + case FormatToken::Sprite: if constexpr (std::is_integral()) { auto idx = static_cast(arg); @@ -567,17 +567,10 @@ namespace OpenRCT2 } } - template void FormatArgument(std::stringstream&, uint32_t, uint16_t); - template void FormatArgument(std::stringstream&, uint32_t, int16_t); - template void FormatArgument(std::stringstream&, uint32_t, int32_t); - template void FormatArgument(std::stringstream&, uint32_t, const char*); - - bool CanFormatToken(FormatToken t) - { - if (t == FORMAT_PUSH16 || t == FORMAT_POP16) - return false; - return t == FORMAT_COMMA1DP16 || (t >= FORMAT_ARGUMENT_CODE_START && t <= FORMAT_ARGUMENT_CODE_END); - } + template void FormatArgument(std::stringstream&, FormatToken, uint16_t); + template void FormatArgument(std::stringstream&, FormatToken, int16_t); + template void FormatArgument(std::stringstream&, FormatToken, int32_t); + template void FormatArgument(std::stringstream&, FormatToken, const char*); bool IsRealNameStringId(rct_string_id id) { @@ -635,7 +628,7 @@ namespace OpenRCT2 { for (const auto& token : fmt) { - if (token.kind == FORMAT_STRINGID || token.kind == FORMAT_STRINGID2) + if (token.kind == FormatToken::StringId) { if (argIndex < args.size()) { @@ -658,7 +651,7 @@ namespace OpenRCT2 argIndex++; } } - else if (CanFormatToken(token.kind)) + else if (FormatTokenTakesArgument(token.kind)) { if (argIndex < args.size()) { @@ -666,7 +659,7 @@ namespace OpenRCT2 } argIndex++; } - else if (token.kind != FORMAT_PUSH16 && token.kind != FORMAT_POP16) + else if (token.kind != FormatToken::Push16 && token.kind != FormatToken::Pop16) { ss << token.text; } @@ -703,42 +696,41 @@ namespace OpenRCT2 { switch (t.kind) { - case FORMAT_COMMA32: - case FORMAT_INT32: - case FORMAT_COMMA2DP32: - case FORMAT_CURRENCY2DP: - case FORMAT_CURRENCY: - case FORMAT_SPRITE: + case FormatToken::Comma32: + case FormatToken::Int32: + case FormatToken::Comma2dp32: + case FormatToken::Currency2dp: + case FormatToken::Currency: + case FormatToken::Sprite: anyArgs.push_back(ReadFromArgs(args)); break; - case FORMAT_COMMA16: - case FORMAT_UINT16: - case FORMAT_MONTHYEAR: - case FORMAT_MONTH: - case FORMAT_VELOCITY: - case FORMAT_DURATION: - case FORMAT_REALTIME: - case FORMAT_LENGTH: + case FormatToken::Comma16: + case FormatToken::Uint16: + case FormatToken::MonthYear: + case FormatToken::Month: + case FormatToken::Velocity: + case FormatToken::DurationShort: + case FormatToken::DurationLong: + case FormatToken::Length: anyArgs.push_back(ReadFromArgs(args)); break; - case FORMAT_STRINGID: - case FORMAT_STRINGID2: + case FormatToken::StringId: { auto stringId = ReadFromArgs(args); anyArgs.push_back(stringId); BuildAnyArgListFromLegacyArgBuffer(GetFmtStringById(stringId), anyArgs, args); break; } - case FORMAT_STRING: + case FormatToken::String: { auto sz = ReadFromArgs(args); anyArgs.push_back(sz); break; } - case FORMAT_POP16: + case FormatToken::Pop16: args = reinterpret_cast(reinterpret_cast(args) + 2); break; - case FORMAT_PUSH16: + case FormatToken::Push16: args = reinterpret_cast(reinterpret_cast(args) - 2); break; } diff --git a/src/openrct2/localisation/Formatting.h b/src/openrct2/localisation/Formatting.h index 747c4677a8..210e11594a 100644 --- a/src/openrct2/localisation/Formatting.h +++ b/src/openrct2/localisation/Formatting.h @@ -23,7 +23,6 @@ namespace OpenRCT2 { - using FormatToken = uint32_t; using FormatArg_t = std::variant; class FmtString @@ -77,7 +76,6 @@ namespace OpenRCT2 template void FormatArgument(std::stringstream& ss, FormatToken token, T arg); - bool CanFormatToken(FormatToken t); bool IsRealNameStringId(rct_string_id id); void FormatRealName(std::stringstream& ss, rct_string_id id); FmtString GetFmtStringById(rct_string_id id); @@ -92,7 +90,7 @@ namespace OpenRCT2 while (!it.eol()) { const auto& token = *it++; - if (!CanFormatToken(token.kind)) + if (!FormatTokenTakesArgument(token.kind)) { ss << token.text; } @@ -110,7 +108,7 @@ namespace OpenRCT2 while (!it.eol()) { const auto& token = *it++; - if (token.kind == FORMAT_STRINGID || token.kind == FORMAT_STRINGID2) + if (token.kind == FormatToken::StringId) { if constexpr (std::is_integral()) { @@ -128,7 +126,7 @@ namespace OpenRCT2 FormatString(ss, stack, argN...); } } - else if (CanFormatToken(token.kind)) + else if (FormatTokenTakesArgument(token.kind)) { FormatArgument(ss, token.kind, arg0); return FormatString(ss, stack, argN...); diff --git a/src/openrct2/localisation/Language.cpp b/src/openrct2/localisation/Language.cpp index 14f546935a..c7c8630da6 100644 --- a/src/openrct2/localisation/Language.cpp +++ b/src/openrct2/localisation/Language.cpp @@ -51,21 +51,6 @@ const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] = }; // clang-format on -void utf8_remove_format_codes(utf8* text, bool allowcolours) -{ - const utf8* ch = text; - utf8* dstCh = text; - int32_t codepoint; - while ((codepoint = String::GetNextCodepoint(ch, &ch)) != 0) - { - if (!utf8_is_format_code(codepoint) || (allowcolours && utf8_is_colour_code(codepoint))) - { - dstCh = String::WriteCodepoint(dstCh, codepoint); - } - } - *dstCh = 0; -} - uint8_t language_get_id_from_locale(const char* locale) { uint8_t i = 0; @@ -125,113 +110,3 @@ rct_string_id language_allocate_object_string(const std::string& target) auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService(); return localisationService.AllocateObjectString(target); } - -std::string language_convert_string_to_tokens(const std::string_view& s) -{ - std::string result; - result.reserve(s.size() * 4); - std::string input = std::string(s); - auto readPtr = input.c_str(); - while (true) - { - char32_t code = utf8_get_next(readPtr, const_cast(&readPtr)); - if (code == 0) - { - break; - } - else if (code == '\n') - { - result.push_back('\n'); - } - else if (utf8_is_format_code(code)) - { - auto token = format_get_token(code); - result.push_back('{'); - result.append(token); - result.push_back('}'); - } - else - { - char buffer[8]{}; - utf8_write_codepoint(buffer, code); - result.append(buffer); - } - } - result.shrink_to_fit(); - return result; -} - -std::string language_convert_string(const std::string_view& s) -{ - enum class PARSE_STATE - { - DEFAULT, - CR, - TOKEN, - }; - - std::string result; - std::string token; - PARSE_STATE state{}; - token.reserve(64); - result.reserve(s.size() * 2); - for (char c : s) - { - switch (state) - { - case PARSE_STATE::CR: - result.push_back(FORMAT_NEWLINE); - state = PARSE_STATE::DEFAULT; - [[fallthrough]]; - case PARSE_STATE::DEFAULT: - switch (c) - { - case '\r': - state = PARSE_STATE::CR; - break; - case '\n': - result.push_back(FORMAT_NEWLINE); - break; - case '{': - token.clear(); - state = PARSE_STATE::TOKEN; - break; - default: - if (static_cast(c) >= 32) - { - result.push_back(c); - } - break; - } - break; - case PARSE_STATE::TOKEN: - if (c == '}') - { - auto code = format_get_code(token.c_str()); - if (code == 0) - { - int32_t number{}; - if (sscanf(token.c_str(), "%d", &number) == 1) - { - auto b = static_cast(std::clamp(number, 0, 255)); - token.push_back(b); - } - } - else - { - char buffer[8]{}; - utf8_write_codepoint(buffer, code); - result.append(buffer); - } - state = PARSE_STATE::DEFAULT; - } - else - { - token.push_back(c); - } - break; - } - } - result.shrink_to_fit(); - return result; -} diff --git a/src/openrct2/localisation/Language.h b/src/openrct2/localisation/Language.h index 9eb43c5de4..b9114c92de 100644 --- a/src/openrct2/localisation/Language.h +++ b/src/openrct2/localisation/Language.h @@ -98,7 +98,6 @@ bool language_open(int32_t id); uint32_t utf8_get_next(const utf8* char_ptr, const utf8** nextchar_ptr); int32_t utf8_insert_codepoint(utf8* dst, uint32_t codepoint); bool utf8_is_codepoint_start(const utf8* text); -void utf8_remove_format_codes(utf8* text, bool allowcolours); int32_t utf8_get_codepoint_length(char32_t codepoint); int32_t utf8_length(const utf8* text); @@ -107,8 +106,6 @@ std::string utf8_to_rct2(const std::string_view& src); bool language_get_localised_scenario_strings(const utf8* scenarioFilename, rct_string_id* outStringIds); void language_free_object_string(rct_string_id stringId); rct_string_id language_allocate_object_string(const std::string& target); -std::string language_convert_string_to_tokens(const std::string_view& s); -std::string language_convert_string(const std::string_view& s); constexpr utf8* utf8_write_codepoint(utf8* dst, uint32_t codepoint) { diff --git a/src/openrct2/localisation/LanguagePack.cpp b/src/openrct2/localisation/LanguagePack.cpp index e1a593453c..ca0dfe7a31 100644 --- a/src/openrct2/localisation/LanguagePack.cpp +++ b/src/openrct2/localisation/LanguagePack.cpp @@ -539,32 +539,8 @@ private: sb.Clear(); while (reader->TryPeek(&codepoint) && !IsNewLine(codepoint)) { - // if (codepoint == '{') - // { - // uint32_t token; - // bool isByte; - // if (ParseToken(reader, &token, &isByte)) - // { - // if (isByte) - // { - // sb.Append(reinterpret_cast(&token), 1); - // } - // else - // { - // sb.Append(static_cast(token)); - // } - // } - // else - // { - // // Syntax error or unknown token, ignore line entirely - // return; - // } - // } - // else - { - reader->Skip(); - sb.Append(codepoint); - } + reader->Skip(); + sb.Append(codepoint); } std::string s; @@ -599,47 +575,6 @@ private: } } } - - bool ParseToken(IStringReader* reader, uint32_t* token, bool* isByte) - { - auto sb = StringBuilder(); - codepoint_t codepoint; - - // Skip open brace - reader->Skip(); - - while (reader->TryPeek(&codepoint)) - { - if (IsNewLine(codepoint)) - return false; - if (IsWhitespace(codepoint)) - return false; - - reader->Skip(); - - if (codepoint == '}') - break; - - sb.Append(codepoint); - } - - const utf8* tokenName = sb.GetBuffer(); - *token = format_get_code(tokenName); - *isByte = false; - - // Handle explicit byte values - if (*token == 0) - { - int32_t number; - if (sscanf(tokenName, "%d", &number) == 1) - { - *token = std::clamp(number, 0, 255); - *isByte = true; - } - } - - return true; - } }; namespace LanguagePackFactory diff --git a/src/openrct2/localisation/Localisation.cpp b/src/openrct2/localisation/Localisation.cpp index f4258ebbbf..48728c7844 100644 --- a/src/openrct2/localisation/Localisation.cpp +++ b/src/openrct2/localisation/Localisation.cpp @@ -328,944 +328,6 @@ const rct_string_id DateGameShortMonthNames[MONTH_COUNT] = { }; // clang-format on -#define format_push_char_safe(C) \ - { \ - *(*dest)++ = (C); \ - --(*size); \ - } -#define format_handle_overflow(X) \ - if ((*size) <= (X)) \ - { \ - *(*dest) = '\0'; \ - (*size) = 0; \ - return; \ - } -#define format_push_char(C) \ - { \ - format_handle_overflow(1); \ - format_push_char_safe(C); \ - } - -#define format_push_wrap(C) \ - { \ - *ncur = (C); \ - if (ncur == (*dest)) \ - ncur = nbegin; \ - } -#define reverse_string() \ - while (nbegin < nend) \ - { \ - tmp = *nbegin; \ - *nbegin++ = *nend; \ - *nend-- = tmp; \ - } - -static void format_string_part_from_raw(char** dest, size_t* size, const char* src, char** args); -static void format_string_part(char** dest, size_t* size, rct_string_id format, char** args); - -static void format_append_string(char** dest, size_t* size, const utf8* string) -{ - if ((*size) == 0) - return; - if (string == nullptr) - return; - size_t length = strnlen(string, *size); - if (length < (*size)) - { - std::memcpy((*dest), string, length); - (*dest) += length; - (*size) -= length; - } - else - { - std::memcpy((*dest), string, (*size) - 1); - (*dest) += (*size) - 1; - *(*dest)++ = '\0'; - (*size) = 0; - } -} - -static void format_integer(char** dest, size_t* size, int64_t value) -{ - int32_t digit; - char *nbegin, *nend, *ncur; - char tmp; - - if ((*size) == 0) - return; - - // Negative sign - if (value < 0) - { - format_push_char('-'); - value = -value; - } - - if (value == 0) - { - format_push_char('0'); - return; - } - - nbegin = (*dest); - - // Right to left - while (value > 0 && (*size) > 1) - { - digit = value % 10; - value /= 10; - - format_push_char_safe('0' + digit); - } - - if (value > 0) - { - ncur = nbegin; - - while (value > 0) - { - digit = value % 10; - value /= 10; - - format_push_wrap('0' + digit); - } - - // Reverse first half of string - nend = ncur - 1; - reverse_string(); - - // Reverse second half of string - nbegin = ncur; - nend = (*dest) - 1; - reverse_string(); - - format_push_char_safe('\0'); // Truncate overflowed string - } - else - { - // Reverse string - nend = (*dest) - 1; - reverse_string(); - } -} - -static void format_comma_separated_integer(char** dest, size_t* size, int64_t value) -{ - int32_t digit, groupIndex; - char *nbegin, *nend, *ncur; - char tmp; - const char* commaMark = language_get_string(STR_LOCALE_THOUSANDS_SEPARATOR); - const char* ch = nullptr; - - if ((*size) == 0) - return; - - // Negative sign - if (value < 0) - { - format_push_char('-'); - value = -value; - } - - if (value == 0) - { - format_push_char('0'); - return; - } - - nbegin = *dest; - - // Groups of three digits, right to left - groupIndex = 0; - while (value > 0 && (*size) > 1) - { - // Append group separator - if (groupIndex == 3) - { - groupIndex = 0; - ch = commaMark; - } - - if (ch != nullptr) - { - format_push_char_safe(*ch++); - if (*ch == '\0') - ch = nullptr; - } - else - { - digit = value % 10; - value /= 10; - - format_push_char_safe('0' + digit); - groupIndex++; - } - } - - if (value > 0) - { - ncur = nbegin; - - while (value > 0) - { - // Append group separator - if (groupIndex == 3) - { - groupIndex = 0; - ch = commaMark; - } - - if (ch != nullptr) - { - format_push_wrap(*ch++); - if (*ch == '\0') - ch = nullptr; - } - else - { - digit = value % 10; - value /= 10; - - format_push_wrap('0' + digit); - groupIndex++; - } - } - - // Reverse first half of string - nend = ncur - 1; - reverse_string(); - - // Reverse second half of string - nbegin = ncur; - nend = (*dest) - 1; - reverse_string(); - - format_push_char_safe('\0'); // Truncate overflowed string - } - else - { - // Reverse string - nend = *dest - 1; - reverse_string(); - } -} - -static void format_comma_separated_fixed_1dp(char** dest, size_t* size, int64_t value) -{ - int32_t digit, groupIndex; - char *nbegin, *nend, *ncur; - char tmp; - const char* commaMark = language_get_string(STR_LOCALE_THOUSANDS_SEPARATOR); - const char* decimalMark = language_get_string(STR_LOCALE_DECIMAL_POINT); - const char* ch = nullptr; - int32_t zeroNeeded = 1; - - if ((*size) == 0) - return; - - // Negative sign - if (value < 0) - { - format_push_char('-'); - value = -value; - } - - nbegin = (*dest); - - // In the case of buffers this small, - // all of this would be truncated anyways. - format_handle_overflow(1); - if ((*size) > 2) - { - // One decimal place - digit = value % 10; - format_push_char_safe('0' + digit); - - ch = decimalMark; - } - value /= 10; - - groupIndex = 0; - while ((zeroNeeded || value > 0) && (*size) > 1) - { - // Append group separator - if (groupIndex == 3) - { - groupIndex = 0; - ch = commaMark; - } - - if (ch != nullptr) - { - format_push_char_safe(*ch++); - if (*ch == '\0') - ch = nullptr; - } - else - { - zeroNeeded = 0; - digit = value % 10; - value /= 10; - - format_push_char_safe('0' + digit); - groupIndex++; - } - } - - if (zeroNeeded || value > 0) - { - ncur = nbegin; - - while (zeroNeeded || value > 0) - { - // Append group separator - if (groupIndex == 3) - { - groupIndex = 0; - ch = commaMark; - } - - if (ch != nullptr) - { - format_push_wrap(*ch++); - if (*ch == '\0') - ch = nullptr; - } - else - { - zeroNeeded = 0; - digit = value % 10; - value /= 10; - - format_push_wrap('0' + digit); - groupIndex++; - } - } - - // Reverse first half of string - nend = ncur - 1; - reverse_string(); - - // Reverse second half of string - nbegin = ncur; - nend = (*dest) - 1; - reverse_string(); - - format_push_char_safe('\0'); // Truncate overflowed string - } - else - { - // Reverse string - nend = *dest - 1; - reverse_string(); - } -} - -static void format_comma_separated_fixed_2dp(char** dest, size_t* size, int64_t value) -{ - int32_t digit, groupIndex; - char *nbegin, *nend, *ncur; - char tmp; - const char* commaMark = language_get_string(STR_LOCALE_THOUSANDS_SEPARATOR); - const char* decimalMark = language_get_string(STR_LOCALE_DECIMAL_POINT); - const char* ch = nullptr; - int32_t zeroNeeded = 1; - - if ((*size) == 0) - return; - - // Negative sign - if (value < 0) - { - format_push_char('-'); - value = -value; - } - - nbegin = (*dest); - - // In the case of buffers this small, - // all of this would be truncated anyways. - format_handle_overflow(1); - if ((*size) < 3) - { - value /= 100; - } - else - { - // Two decimal places - digit = value % 10; - value /= 10; - format_push_char_safe('0' + digit); - - digit = value % 10; - value /= 10; - format_push_char_safe('0' + digit); - - ch = decimalMark; - } - - groupIndex = 0; - while ((zeroNeeded || value > 0) && (*size) > 1) - { - // Append group separator - if (groupIndex == 3) - { - groupIndex = 0; - ch = commaMark; - } - - if (ch != nullptr) - { - format_push_char_safe(*ch++); - if (*ch == '\0') - ch = nullptr; - } - else - { - zeroNeeded = 0; - digit = value % 10; - value /= 10; - - format_push_char_safe('0' + digit); - groupIndex++; - } - } - - if (zeroNeeded || value > 0) - { - ncur = nbegin; - - while (zeroNeeded || value > 0) - { - // Append group separator - if (groupIndex == 3) - { - groupIndex = 0; - ch = commaMark; - } - - if (ch != nullptr) - { - format_push_wrap(*ch++); - if (*ch == '\0') - ch = nullptr; - } - else - { - zeroNeeded = 0; - digit = value % 10; - value /= 10; - - format_push_wrap('0' + digit); - groupIndex++; - } - } - - // Reverse first half of string - nend = ncur - 1; - reverse_string(); - - // Reverse second half of string - nbegin = ncur; - nend = (*dest) - 1; - reverse_string(); - - format_push_char_safe('\0'); // Truncate overflowed string - } - else - { - // Reverse string - nend = *dest - 1; - reverse_string(); - } -} - -static void format_currency(char** dest, size_t* size, int64_t value) -{ - if ((*size) == 0) - return; - - const currency_descriptor* currencyDesc = &CurrencyDescriptors[EnumValue(gConfigGeneral.currency_format)]; - - value *= currencyDesc->rate; - - // Negative sign - if (value < 0) - { - format_push_char('-'); - value = -value; - } - - // Round the value away from zero - value = (value + 99) / 100; - - // Currency symbol - const utf8* symbol = currencyDesc->symbol_unicode; - CurrencyAffix affix = currencyDesc->affix_unicode; - if (!font_supports_string(symbol, FONT_SIZE_MEDIUM)) - { - symbol = currencyDesc->symbol_ascii; - affix = currencyDesc->affix_ascii; - } - - // Prefix - if (affix == CurrencyAffix::Prefix) - format_append_string(dest, size, symbol); - if ((*size) == 0) - return; - - format_comma_separated_integer(dest, size, value); - if ((*size) == 0) - return; - - // Currency symbol suffix - if (affix == CurrencyAffix::Suffix) - format_append_string(dest, size, symbol); -} - -static void format_currency_2dp(char** dest, size_t* size, int64_t value) -{ - if ((*size) == 0) - return; - - const currency_descriptor* currencyDesc = &CurrencyDescriptors[EnumValue(gConfigGeneral.currency_format)]; - - int32_t rate = currencyDesc->rate; - value *= rate; - - // Negative sign - if (value < 0) - { - format_push_char('-'); - value = -value; - } - - // Currency symbol - const utf8* symbol = currencyDesc->symbol_unicode; - CurrencyAffix affix = currencyDesc->affix_unicode; - if (!font_supports_string(symbol, FONT_SIZE_MEDIUM)) - { - symbol = currencyDesc->symbol_ascii; - affix = currencyDesc->affix_ascii; - } - - // Prefix - if (affix == CurrencyAffix::Prefix) - format_append_string(dest, size, symbol); - if ((*size) == 0) - return; - - // Drop the pennies for "large" currencies - if (rate >= 100) - { - format_comma_separated_integer(dest, size, value / 100); - } - else - { - format_comma_separated_fixed_2dp(dest, size, value); - } - if ((*size) == 0) - return; - - // Currency symbol suffix - if (affix == CurrencyAffix::Suffix) - format_append_string(dest, size, symbol); -} - -static void format_date(char** dest, size_t* size, uint16_t value) -{ - uint16_t args[] = { static_cast(date_get_month(value)), static_cast(date_get_year(value) + 1) }; - uint16_t* argsRef = args; - format_string_part(dest, size, STR_DATE_FORMAT_MY, reinterpret_cast(&argsRef)); -} - -static void format_length(char** dest, size_t* size, int32_t value) -{ - rct_string_id stringId = STR_UNIT_SUFFIX_METRES; - - if (gConfigGeneral.measurement_format == MeasurementFormat::Imperial) - { - value = metres_to_feet(value); - stringId = STR_UNIT_SUFFIX_FEET; - } - - int32_t* argRef = &value; - format_string_part(dest, size, stringId, reinterpret_cast(&argRef)); -} - -static void format_velocity(char** dest, size_t* size, uint16_t value) -{ - rct_string_id stringId = STR_NONE; - - switch (gConfigGeneral.measurement_format) - { - case MeasurementFormat::Imperial: - stringId = STR_UNIT_SUFFIX_MILES_PER_HOUR; - break; - case MeasurementFormat::Metric: - value = mph_to_kmph(value); - stringId = STR_UNIT_SUFFIX_KILOMETRES_PER_HOUR; - break; - case MeasurementFormat::SI: - value = mph_to_dmps(value); - stringId = STR_UNIT_SUFFIX_METRES_PER_SECOND; - break; - } - - uint16_t* argRef = &value; - format_string_part(dest, size, stringId, reinterpret_cast(&argRef)); -} - -static constexpr const rct_string_id DurationFormats[][2] = { - { STR_DURATION_SEC, STR_DURATION_SECS }, - { STR_DURATION_MIN_SEC, STR_DURATION_MIN_SECS }, - { STR_DURATION_MINS_SEC, STR_DURATION_MINS_SECS }, -}; - -static void format_duration(char** dest, size_t* size, uint16_t value) -{ - uint16_t minutes = value / 60; - uint16_t seconds = value % 60; - uint16_t args[] = { minutes, seconds }; - uint16_t* argsRef = &args[1]; - - int32_t minuteIndex = 0; - if (minutes > 0) - { - minuteIndex = 1; - if (minutes != 1) - { - minuteIndex = 2; - } - - argsRef--; - } - - int32_t secondsIndex = 0; - if (seconds != 1) - { - secondsIndex = 1; - } - - rct_string_id stringId = DurationFormats[minuteIndex][secondsIndex]; - - format_string_part(dest, size, stringId, reinterpret_cast(&argsRef)); -} - -static constexpr const rct_string_id RealtimeFormats[][2] = { - { STR_REALTIME_MIN, STR_REALTIME_MINS }, - { STR_REALTIME_HOUR_MIN, STR_REALTIME_HOUR_MINS }, - { STR_REALTIME_HOURS_MIN, STR_REALTIME_HOURS_MINS }, -}; - -static void format_realtime(char** dest, size_t* size, uint16_t value) -{ - uint16_t hours = value / 60; - uint16_t minutes = value % 60; - uint16_t args[] = { hours, minutes }; - uint16_t* argsRef = &args[1]; - - int32_t hourIndex = 0; - if (hours > 0) - { - hourIndex = 1; - if (hours != 1) - { - hourIndex = 2; - } - - argsRef--; - } - - int32_t minuteIndex = 0; - if (minutes != 1) - { - minuteIndex = 1; - } - - rct_string_id stringId = RealtimeFormats[hourIndex][minuteIndex]; - - format_string_part(dest, size, stringId, reinterpret_cast(&argsRef)); -} - -static void format_string_code(uint32_t format_code, char** dest, size_t* size, char** args) -{ - intptr_t value = 0; - - if ((*size) == 0) - return; - -#ifdef DEBUG - if (gDebugStringFormatting) - { - printf("format_string_code(\"%s\")\n", format_get_token(format_code)); - } -#endif - - // Use the temporaries below to enforce sign extension, which does not happen with simple memcpy. See - // https://github.com/OpenRCT2/OpenRCT2/issues/8674. This is only required for signed values. - [[maybe_unused]] int32_t temp32; - [[maybe_unused]] int16_t temp16; - switch (format_code) - { - case FORMAT_COMMA32: - // Pop argument - std::memcpy(&temp32, *args, sizeof(int32_t)); - value = temp32; - *args += 4; - - format_comma_separated_integer(dest, size, value); - break; - case FORMAT_INT32: - // Pop argument - std::memcpy(&temp32, *args, sizeof(int32_t)); - value = temp32; - *args += 4; - - format_integer(dest, size, value); - break; - case FORMAT_COMMA2DP32: - // Pop argument - std::memcpy(&temp32, *args, sizeof(int32_t)); - value = temp32; - *args += 4; - - format_comma_separated_fixed_2dp(dest, size, value); - break; - case FORMAT_COMMA1DP16: - // Pop argument - std::memcpy(&temp16, *args, sizeof(int16_t)); - value = temp16; - *args += 2; - - format_comma_separated_fixed_1dp(dest, size, value); - break; - case FORMAT_COMMA16: - // Pop argument - std::memcpy(&temp16, *args, sizeof(int16_t)); - value = temp16; - *args += 2; - - format_comma_separated_integer(dest, size, value); - break; - case FORMAT_UINT16: - // Pop argument - std::memcpy(&value, *args, sizeof(uint16_t)); - *args += 2; - - format_integer(dest, size, value); - break; - case FORMAT_CURRENCY2DP: - // Pop argument - std::memcpy(&temp32, *args, sizeof(int32_t)); - value = temp32; - *args += 4; - - format_currency_2dp(dest, size, value); - break; - case FORMAT_CURRENCY: - // Pop argument - std::memcpy(&temp32, *args, sizeof(int32_t)); - value = temp32; - *args += 4; - - format_currency(dest, size, value); - break; - case FORMAT_STRINGID: - case FORMAT_STRINGID2: - // Pop argument - std::memcpy(&value, *args, sizeof(uint16_t)); - *args += 2; - - format_string_part(dest, size, static_cast(value), args); - break; - case FORMAT_STRING: - // Pop argument - std::memcpy(&value, *args, sizeof(uintptr_t)); - *args += sizeof(uintptr_t); - - if (value != 0) - format_append_string(dest, size, reinterpret_cast(value)); - break; - case FORMAT_MONTHYEAR: - // Pop argument - std::memcpy(&value, *args, sizeof(uint16_t)); - *args += 2; - - format_date(dest, size, static_cast(value)); - break; - case FORMAT_MONTH: - // Pop argument - std::memcpy(&value, *args, sizeof(uint16_t)); - *args += 2; - - format_append_string( - dest, size, language_get_string(DateGameMonthNames[date_get_month(static_cast(value))])); - break; - case FORMAT_VELOCITY: - // Pop argument - std::memcpy(&temp16, *args, sizeof(int16_t)); - value = temp16; - *args += 2; - - format_velocity(dest, size, static_cast(value)); - break; - case FORMAT_POP16: - *args += 2; - break; - case FORMAT_PUSH16: - *args -= 2; - break; - case FORMAT_DURATION: - // Pop argument - std::memcpy(&value, *args, sizeof(uint16_t)); - *args += 2; - - format_duration(dest, size, static_cast(value)); - break; - case FORMAT_REALTIME: - // Pop argument - std::memcpy(&value, *args, sizeof(uint16_t)); - *args += 2; - - format_realtime(dest, size, static_cast(value)); - break; - case FORMAT_LENGTH: - // Pop argument - std::memcpy(&value, *args, sizeof(uint16_t)); - *args += 2; - - format_length(dest, size, static_cast(value)); - break; - case FORMAT_SPRITE: - // Pop argument - std::memcpy(&value, *args, sizeof(uint32_t)); - *args += 4; - - format_handle_overflow(1 + sizeof(uint32_t)); - - format_push_char_safe('\x17'); - std::memcpy(*dest, &value, sizeof(uint32_t)); - (*dest) += sizeof(uint32_t); - (*size) -= sizeof(uint32_t); - break; - } -} - -static void format_string_part_from_raw(utf8** dest, size_t* size, const utf8* src, char** args) -{ -#ifdef DEBUG - if (gDebugStringFormatting) - { - printf("format_string_part_from_raw(\"%s\")\n", src); - } -#endif - - while (*size > 1) - { - uint32_t code = utf8_get_next(src, &src); - if (code < ' ') - { - if (code == 0) - { - break; - } - else if (code <= 4) - { - format_handle_overflow(2); - format_push_char_safe(code); - format_push_char_safe(*src++); - } - else if (code <= 16) - { - format_handle_overflow(1); - format_push_char_safe(code); - } - else if (code <= 22) - { - format_handle_overflow(3); - format_push_char_safe(code); - format_push_char_safe(*src++); - format_push_char_safe(*src++); - } - else - { - format_handle_overflow(5); - format_push_char_safe(code); - format_push_char_safe(*src++); - format_push_char_safe(*src++); - format_push_char_safe(*src++); - format_push_char_safe(*src++); - } - } - else if (code <= 'z') - { - format_push_char(code); - } - else if (code < FORMAT_COLOUR_CODE_START || code == FORMAT_COMMA1DP16) - { - format_string_code(code, dest, size, args); - } - else - { - size_t codepointLength = static_cast(utf8_get_codepoint_length(code)); - format_handle_overflow(codepointLength); - if (*size > codepointLength) - { - *dest = utf8_write_codepoint(*dest, code); - *size -= codepointLength; - } - } - } -} - -static void format_string_part(utf8** dest, size_t* size, rct_string_id format, char** args) -{ - if (format == STR_NONE) - { - if (*size > 0) - { - *(*dest) = '\0'; - } - } - else if (format < USER_STRING_START) - { - // Language string - const utf8* rawString = language_get_string(format); - format_string_part_from_raw(dest, size, rawString, args); - } - else if (format <= USER_STRING_END) - { - // User strings should no longer be used - assert(false); - - // Custom string - format -= 0x8000; - - // Bits 10, 11 represent number of bytes to pop off arguments - *args += (format & 0xC00) >> 9; - } - else if (format <= REAL_NAME_END) - { - // Real name - auto realNameIndex = format - REAL_NAME_START; - - format_append_string(dest, size, real_names[realNameIndex % std::size(real_names)]); - if ((*size) == 0) - return; - format_push_char(' '); - format_push_char(real_name_initials[(realNameIndex >> 10) % std::size(real_name_initials)]); - format_push_char('.'); - *(*dest) = '\0'; - } - else - { - // ? - log_error("Localisation CALLPROC reached. Please contact a dev"); - assert(false); - } -} - std::string format_string(rct_string_id format, const void* args) { std::string buffer(256, 0); @@ -1292,83 +354,6 @@ std::string format_string(rct_string_id format, const void* args) return buffer; } -/** - * Writes a formatted string to a buffer. - * rct2: 0x006C2555 - * dest (edi) - * format (ax) - * args (ecx) - */ -void format_string_old(utf8* dest, size_t size, rct_string_id format, const void* args) -{ -#ifdef DEBUG - if (gDebugStringFormatting) - { - printf("format_string(%hu)\n", format); - } -#endif - - if (size == 0) - { - return; - } - - utf8* end = dest; - size_t left = size; - format_string_part(&end, &left, format, reinterpret_cast(const_cast(&args))); - if (left == 0) - { - // Replace last character with null terminator - *(end - 1) = '\0'; - log_warning("Truncating formatted string \"%s\" to %d bytes.", dest, size); - } - else - { - // Null terminate - *end = '\0'; - } - -#ifdef DEBUG - // Check if characters were written past the end of the buffer - assert(end <= dest + size); -#endif -} - -void format_string_raw(utf8* dest, size_t size, const utf8* src, const void* args) -{ -#ifdef DEBUG - if (gDebugStringFormatting) - { - printf("format_string_raw(\"%s\")\n", src); - } -#endif - - if (size == 0) - { - return; - } - - utf8* end = dest; - size_t left = size; - format_string_part_from_raw(&end, &left, src, reinterpret_cast(const_cast(&args))); - if (left == 0) - { - // Replace last character with null terminator - *(end - 1) = '\0'; - log_warning("Truncating formatted string \"%s\" to %d bytes.", dest, size); - } - else - { - // Null terminate - *end = '\0'; - } - -#ifdef DEBUG - // Check if characters were written past the end of the buffer - assert(end <= dest + size); -#endif -} - /** * Writes a formatted string to a buffer and converts it to upper case. * rct2: 0x006C2538 diff --git a/src/openrct2/localisation/Localisation.h b/src/openrct2/localisation/Localisation.h index 5b325242c5..1e1230f9a1 100644 --- a/src/openrct2/localisation/Localisation.h +++ b/src/openrct2/localisation/Localisation.h @@ -19,15 +19,10 @@ #include -bool utf8_is_format_code(char32_t codepoint); -bool utf8_is_colour_code(char32_t codepoint); bool utf8_should_use_sprite_for_codepoint(char32_t codepoint); -int32_t utf8_get_format_code_arg_length(char32_t codepoint); -void utf8_remove_formatting(utf8* string, bool allowColours); std::string format_string(rct_string_id format, const void* args); void format_string(char* dest, size_t size, rct_string_id format, const void* args); -void format_string_raw(char* dest, size_t size, const char* src, const void* args); void format_string_to_upper(char* dest, size_t size, rct_string_id format, const void* args); void generate_string_file(); @@ -43,7 +38,6 @@ void format_readable_speed(char* buf, size_t bufSize, uint64_t sizeBytesPerSec); utf8* get_string_end(const utf8* text); size_t get_string_size(const utf8* text); -int32_t get_string_length(const utf8* text); // The maximum number of characters allowed for string/money conversions (anything above will risk integer overflow issues) #define MONEY_STRING_MAXLENGTH 14 diff --git a/src/openrct2/localisation/UTF8.cpp b/src/openrct2/localisation/UTF8.cpp index d3accc856b..6854c2bab3 100644 --- a/src/openrct2/localisation/UTF8.cpp +++ b/src/openrct2/localisation/UTF8.cpp @@ -112,15 +112,7 @@ int32_t utf8_length(const utf8* text) */ utf8* get_string_end(const utf8* text) { - int32_t codepoint; - const utf8* ch = text; - - while ((codepoint = utf8_get_next(ch, &ch)) != 0) - { - int32_t argLength = utf8_get_format_code_arg_length(codepoint); - ch += argLength; - } - return const_cast(ch - 1); + return const_cast(std::strchr(text, 0)); } /** @@ -130,83 +122,3 @@ size_t get_string_size(const utf8* text) { return get_string_end(text) - text + 1; } - -/** - * Return the number of visible characters (excludes format codes) in the given UTF-8 string. - */ -int32_t get_string_length(const utf8* text) -{ - char32_t codepoint; - const utf8* ch = text; - - int32_t count = 0; - while ((codepoint = utf8_get_next(ch, &ch)) != 0) - { - if (utf8_is_format_code(codepoint)) - { - ch += utf8_get_format_code_arg_length(codepoint); - } - else - { - count++; - } - } - return count; -} - -int32_t utf8_get_format_code_arg_length(char32_t codepoint) -{ - switch (codepoint) - { - case FORMAT_MOVE_X: - case FORMAT_ADJUST_PALETTE: - case FORMAT_3: - case FORMAT_4: - return 1; - case FORMAT_NEWLINE_X_Y: - return 2; - case FORMAT_INLINE_SPRITE: - return 4; - default: - return 0; - } -} - -void utf8_remove_formatting(utf8* string, bool allowColours) -{ - utf8* readPtr = string; - utf8* writePtr = string; - - while (true) - { - char32_t code = utf8_get_next(readPtr, const_cast(&readPtr)); - - if (code == 0) - { - *writePtr = 0; - break; - } - else if (!utf8_is_format_code(code) || (allowColours && utf8_is_colour_code(code))) - { - writePtr = utf8_write_codepoint(writePtr, code); - } - } -} - -bool utf8_is_format_code(char32_t codepoint) -{ - if (codepoint < 32) - return true; - if (codepoint >= FORMAT_ARGUMENT_CODE_START && codepoint <= FORMAT_ARGUMENT_CODE_END) - return true; - if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END) - return true; - if (codepoint == FORMAT_COMMA1DP16) - return true; - return false; -} - -bool utf8_is_colour_code(char32_t codepoint) -{ - return codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END; -} diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 40bb009181..e548e8e6d7 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -695,23 +695,18 @@ NetworkGroup* NetworkBase::GetGroupByID(uint8_t id) const char* NetworkBase::FormatChat(NetworkPlayer* fromplayer, const char* text) { - static char formatted[1024]; - char* lineCh = formatted; - formatted[0] = 0; + static std::string formatted; + formatted.clear(); + formatted += "{OUTLINE}"; if (fromplayer) { - lineCh = utf8_write_codepoint(lineCh, FORMAT_OUTLINE); - lineCh = utf8_write_codepoint(lineCh, FORMAT_BABYBLUE); - safe_strcpy(lineCh, static_cast(fromplayer->Name.c_str()), sizeof(formatted) - (lineCh - formatted)); - safe_strcat(lineCh, ": ", sizeof(formatted) - (lineCh - formatted)); - lineCh = strchr(lineCh, '\0'); + formatted += "{BABYBLUE}"; + formatted += fromplayer->Name; + formatted += ": "; } - lineCh = utf8_write_codepoint(lineCh, FORMAT_OUTLINE); - lineCh = utf8_write_codepoint(lineCh, FORMAT_WHITE); - char* ptrtext = lineCh; - safe_strcpy(lineCh, text, 800); - utf8_remove_format_codes(ptrtext, true); - return formatted; + formatted += "{WHITE}"; + formatted += text; + return formatted.c_str(); } void NetworkBase::SendPacketToClients(const NetworkPacket& packet, bool front, bool gameCmd) @@ -1092,7 +1087,6 @@ void NetworkBase::AppendLog(std::ostream& fs, const std::string& s) if (strftime(buffer, sizeof(buffer), "[%Y/%m/%d %H:%M:%S] ", tmInfo) != 0) { String::Append(buffer, sizeof(buffer), s.c_str()); - utf8_remove_formatting(buffer, false); String::Append(buffer, sizeof(buffer), PLATFORM_NEWLINE); fs.write(buffer, strlen(buffer)); diff --git a/src/openrct2/network/NetworkPlayer.cpp b/src/openrct2/network/NetworkPlayer.cpp index 0153753c19..09cdd51ee6 100644 --- a/src/openrct2/network/NetworkPlayer.cpp +++ b/src/openrct2/network/NetworkPlayer.cpp @@ -19,7 +19,6 @@ void NetworkPlayer::SetName(const std::string& name) { // 36 == 31 + strlen(" #255"); Name = name.substr(0, 36); - utf8_remove_format_codes(static_cast(Name.data()), false); } void NetworkPlayer::Read(NetworkPacket& packet) diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 70ff51cfcb..d58f5dcfd4 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -3006,7 +3006,6 @@ private: const auto originalString = _s4.string_table[(stringId - USER_STRING_START) % 1024]; std::string_view originalStringView(originalString, USER_STRING_MAX_LENGTH); auto asUtf8 = rct2_to_utf8(originalStringView, RCT2_LANGUAGE_ID_ENGLISH_UK); - utf8_remove_format_codes(asUtf8.data(), /*allow colour*/ false); return asUtf8.data(); } diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 8834cdf59f..7522bfea0b 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -1371,7 +1371,7 @@ void S6Exporter::ExportBanner(RCT12Banner& dst, const Banner& src) if (!(src.flags & BANNER_FLAG_IS_WALL) && !(src.flags & BANNER_FLAG_IS_LARGE_SCENERY)) { char codeBuffer[32]{}; - utf8_write_codepoint(codeBuffer, FORMAT_COLOUR_CODE_START + src.text_colour); + utf8_write_codepoint(codeBuffer, 142 + src.text_colour); bannerText = codeBuffer + bannerText; } diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index ed0bfcbf86..4d8bb2c8ac 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -187,6 +187,18 @@ public: return false; } + static bool IsLikelyUtf8(const std::string_view s) + { + for (auto c : s) + { + if (static_cast(c) >= 128) + { + return true; + } + } + return false; + } + void Import() override { Initialise(); @@ -195,15 +207,7 @@ public: gS6Info = _s6.info; // Some scenarios have their scenario details in UTF-8, due to earlier bugs in OpenRCT2. - // This is hard to detect. Therefore, consider invalid characters like colour codes as a sign the text is in UTF-8. - bool alreadyInUTF8 = false; - - if (String::ContainsColourCode(_s6.info.name) || String::ContainsColourCode(_s6.info.details)) - { - alreadyInUTF8 = true; - } - - if (!alreadyInUTF8) + if (!IsLikelyUtf8(_s6.info.name) && !IsLikelyUtf8(_s6.info.details)) { auto temp = rct2_to_utf8(_s6.info.name, RCT2_LANGUAGE_ID_ENGLISH_UK); safe_strcpy(gS6Info.name, temp.data(), sizeof(gS6Info.name)); @@ -1668,7 +1672,6 @@ public: const auto originalString = _s6.custom_strings[(stringId - USER_STRING_START) % 1024]; std::string_view originalStringView(originalString, USER_STRING_MAX_LENGTH); auto asUtf8 = rct2_to_utf8(originalStringView, RCT2_LANGUAGE_ID_ENGLISH_UK); - utf8_remove_format_codes(asUtf8.data(), /*allow colour*/ false); return asUtf8.data(); } diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index c296f7bc85..2122259b50 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -261,7 +261,7 @@ private: rct_s6_info info = chunkReader.ReadChunkAs(); // If the name or the details contain a colour code, they might be in UTF-8 already. // This is caused by a bug that was in OpenRCT2 for 3 years. - if (!String::ContainsColourCode(info.name) && !String::ContainsColourCode(info.details)) + if (!IsLikelyUtf8(info.name) && !IsLikelyUtf8(info.details)) { rct2_to_utf8_self(info.name, sizeof(info.name)); rct2_to_utf8_self(info.details, sizeof(info.details)); @@ -283,6 +283,18 @@ private: return false; } + static bool IsLikelyUtf8(const std::string_view s) + { + for (auto c : s) + { + if (static_cast(c) >= 128) + { + return true; + } + } + return false; + } + static scenario_index_entry CreateNewScenarioEntry(const std::string& path, uint64_t timestamp, rct_s6_info* s6Info) { scenario_index_entry entry = {}; diff --git a/src/openrct2/scripting/ScPark.hpp b/src/openrct2/scripting/ScPark.hpp index fd122e9b30..d4ec7dcaec 100644 --- a/src/openrct2/scripting/ScPark.hpp +++ b/src/openrct2/scripting/ScPark.hpp @@ -63,7 +63,7 @@ namespace OpenRCT2::Scripting result.MonthYear = value["month"].as_int(); result.Day = value["day"].as_int(); - auto text = language_convert_string(value["text"].as_string()); + auto text = value["text"].as_string(); String::Set(result.Text, sizeof(result.Text), text.c_str()); return result; } @@ -207,7 +207,7 @@ namespace OpenRCT2::Scripting auto msg = GetMessage(); if (msg != nullptr) { - return language_convert_string_to_tokens(msg->Text); + return msg->Text; } return 0; } @@ -218,8 +218,7 @@ namespace OpenRCT2::Scripting auto msg = GetMessage(); if (msg != nullptr) { - auto text = language_convert_string(value); - String::Set(msg->Text, sizeof(msg->Text), text.c_str()); + String::Set(msg->Text, sizeof(msg->Text), value.c_str()); } } @@ -395,12 +394,12 @@ namespace OpenRCT2::Scripting std::string text; if (message.type() == DukValue::Type::STRING) { - text = language_convert_string(message.as_string()); + text = message.as_string(); } else { type = GetParkMessageType(message["type"].as_string()); - text = language_convert_string(message["text"].as_string()); + text = message["text"].as_string(); if (type == News::ItemType::Blank) { assoc = static_cast(((COORDS_NULL & 0xFFFF) << 16) | (COORDS_NULL & 0xFFFF)); diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 16d9c86680..e139c1d9e4 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -1267,7 +1267,7 @@ std::string OpenRCT2::Scripting::Stringify(const DukValue& val) std::string OpenRCT2::Scripting::ProcessString(const DukValue& value) { if (value.type() == DukValue::Type::STRING) - return language_convert_string(value.as_string()); + return value.as_string(); return {}; } diff --git a/src/openrct2/title/TitleScreen.cpp b/src/openrct2/title/TitleScreen.cpp index 20be3b9ecf..3af59eca3f 100644 --- a/src/openrct2/title/TitleScreen.cpp +++ b/src/openrct2/title/TitleScreen.cpp @@ -429,17 +429,13 @@ bool title_is_previewing_sequence() void DrawOpenRCT2(rct_drawpixelinfo* dpi, const ScreenCoordsXY& screenCoords) { - utf8 buffer[256]; - - // Write format codes - utf8* ch = buffer; - ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT); - ch = utf8_write_codepoint(ch, FORMAT_OUTLINE); - ch = utf8_write_codepoint(ch, FORMAT_WHITE); + thread_local std::string buffer; + buffer.clear(); + buffer.assign("{MEDIUMFONT}{OUTLINE}{WHITE}"); // Write name and version information - openrct2_write_full_version_info(ch, sizeof(buffer) - (ch - buffer)); - gfx_draw_string(dpi, buffer, COLOUR_BLACK, screenCoords + ScreenCoordsXY(5, 5 - 13)); + buffer += gVersionInfoFull; + gfx_draw_string(dpi, buffer.c_str(), COLOUR_BLACK, screenCoords + ScreenCoordsXY(5, 5 - 13)); // Invalidate screen area int16_t width = static_cast(gfx_get_string_width(buffer)); @@ -447,6 +443,9 @@ void DrawOpenRCT2(rct_drawpixelinfo* dpi, const ScreenCoordsXY& screenCoords) { screenCoords, screenCoords + ScreenCoordsXY{ width, 30 } }); // 30 is an arbitrary height to catch both strings // Write platform information - snprintf(ch, 256 - (ch - buffer), "%s (%s)", OPENRCT2_PLATFORM, OPENRCT2_ARCHITECTURE); - gfx_draw_string(dpi, buffer, COLOUR_BLACK, screenCoords + ScreenCoordsXY(5, 5)); + buffer.assign(OPENRCT2_PLATFORM); + buffer.append(" ("); + buffer.append(OPENRCT2_ARCHITECTURE); + buffer.append(")"); + gfx_draw_string(dpi, buffer.c_str(), COLOUR_BLACK, screenCoords + ScreenCoordsXY(5, 5)); } diff --git a/src/openrct2/world/Banner.cpp b/src/openrct2/world/Banner.cpp index fd78e3ac0d..554dd43549 100644 --- a/src/openrct2/world/Banner.cpp +++ b/src/openrct2/world/Banner.cpp @@ -33,30 +33,6 @@ static Banner _banners[MAX_BANNERS]; -namespace -{ - template struct CodePointToUtf8 - { - constexpr CodePointToUtf8() - { - for (uint32_t i = TFrom; i <= TTo; ++i) - { - utf8_write_codepoint(m_colors[i - TFrom], i); - } - } - - constexpr auto operator()(uint8_t colourId) const - { - return m_colors[colourId]; - } - - using Utf8Colour = utf8[5]; // A 32bit codepoint uses at most 4 bytes in utf8 - Utf8Colour m_colors[TTo - TFrom + 1]{}; - }; -} // namespace - -static constexpr CodePointToUtf8 colourToUtf8; - std::string Banner::GetText() const { Formatter ft; @@ -68,7 +44,10 @@ void Banner::FormatTextTo(Formatter& ft, bool addColour) const { if (addColour) { - ft.Add(STR_STRING_STRINGID).Add(colourToUtf8(text_colour)); + auto formatToken = FormatTokenFromTextColour(text_colour); + auto tokenText = FormatTokenToString(formatToken); + ft.Add(STR_STRING_STRINGID); + ft.Add(tokenText.data()); } FormatTextTo(ft);