diff --git a/src/openrct2/core/String.hpp b/src/openrct2/core/String.hpp index b1c6a9f4dd..866a453de0 100644 --- a/src/openrct2/core/String.hpp +++ b/src/openrct2/core/String.hpp @@ -42,7 +42,7 @@ namespace String bool IsNullOrEmpty(const utf8* str); int32_t Compare(const std::string& a, const std::string& b, bool ignoreCase = false); int32_t Compare(const utf8* a, const utf8* b, bool ignoreCase = false); - bool Equals(const std::string_view& a, const std::string_view& b, bool ignoreCase); + bool Equals(const std::string_view& a, const std::string_view& b, bool ignoreCase = false); bool Equals(const std::string& a, const std::string& b, bool ignoreCase = false); bool Equals(const utf8* a, const utf8* b, bool ignoreCase = false); bool StartsWith(const utf8* str, const utf8* match, bool ignoreCase = false); diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index 2dadbbaca6..9d5c046c51 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -11,6 +11,7 @@ #include "../config/Config.h" #include "../drawing/Drawing.h" #include "../interface/Viewport.h" +#include "../localisation/Formatting.h" #include "../localisation/Localisation.h" #include "../localisation/LocalisationService.h" #include "../platform/platform.h" @@ -20,6 +21,83 @@ #include +using namespace OpenRCT2; + +class CodepointView +{ +private: + std::string_view _str; + +public: + class iterator + { + private: + std::string_view _str; + size_t _index; + + public: + iterator(std::string_view str, size_t index) + : _str(str) + , _index(index) + { + } + + bool operator==(const iterator& rhs) const + { + return _index == rhs._index; + } + bool operator!=(const iterator& rhs) const + { + return _index != rhs._index; + } + const char32_t operator*() const + { + return utf8_get_next(&_str[_index], nullptr); + } + iterator& operator++() + { + if (_index < _str.size()) + { + const utf8* nextch; + utf8_get_next(&_str[_index], &nextch); + _index = nextch - _str.data(); + } + return *this; + } + iterator operator++(int) + { + auto result = *this; + if (_index < _str.size()) + { + const utf8* nextch; + utf8_get_next(&_str[_index], &nextch); + _index = nextch - _str.data(); + } + return result; + } + + size_t GetIndex() const + { + return _index; + } + }; + + CodepointView(std::string_view str) + : _str(str) + { + } + + iterator begin() const + { + return iterator(_str, 0); + } + + iterator end() const + { + return iterator(_str, _str.size()); + } +}; + enum : uint32_t { TEXT_DRAW_FLAG_INSET = 1 << 0, @@ -31,7 +109,7 @@ enum : uint32_t TEXT_DRAW_FLAG_NO_DRAW = 1u << 31 }; -static int32_t ttf_get_string_width(const utf8* text); +static int32_t ttf_get_string_width(std::string_view text); /** * @@ -69,9 +147,9 @@ int32_t gfx_get_string_width_new_lined(utf8* text) * rct2: 0x006C2321 * buffer (esi) */ -int32_t gfx_get_string_width(const utf8* buffer) +int32_t gfx_get_string_width(std::string_view text) { - return ttf_get_string_width(buffer); + return ttf_get_string_width(text); } /** @@ -161,84 +239,84 @@ int32_t gfx_clip_string(utf8* text, int32_t width) */ int32_t gfx_wrap_string(utf8* text, int32_t width, int32_t* outNumLines, int32_t* outFontHeight) { - int32_t lineWidth = 0; + constexpr size_t NULL_INDEX = std::numeric_limits::max(); + thread_local std::string buffer; + buffer.resize(0); + + size_t currentLineIndex = 0; + size_t splitIndex = NULL_INDEX; + size_t numLines = 0; int32_t maxWidth = 0; - *outNumLines = 0; - // Pointer to the start of the current word - utf8* currentWord = nullptr; - - // Width of line up to current word - int32_t currentWidth = 0; - - utf8* ch = text; - utf8* firstCh = text; - utf8* nextCh; - int32_t codepoint; - int32_t numCharactersOnLine = 0; - while ((codepoint = utf8_get_next(ch, const_cast(&nextCh))) != 0) + FmtString fmt = text; + for (const auto& token : fmt) { - if (codepoint == ' ') + if (token.IsLiteral()) { - currentWord = ch; - currentWidth = lineWidth; - numCharactersOnLine++; - } - else if (codepoint == FORMAT_NEWLINE) - { - *ch++ = 0; - maxWidth = std::max(maxWidth, lineWidth); - (*outNumLines)++; - lineWidth = 0; - currentWord = nullptr; - firstCh = ch; - numCharactersOnLine = 0; - continue; - } - else if (utf8_is_format_code(codepoint)) - { - ch = nextCh; - ch += utf8_get_format_code_arg_length(codepoint); - continue; - } + CodepointView codepoints(token.text); + for (auto codepoint : codepoints) + { + buffer.push_back(codepoint); - uint8_t saveCh = *nextCh; - *nextCh = 0; - lineWidth = gfx_get_string_width(firstCh); - *nextCh = saveCh; + auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]); + if (lineWidth <= width || splitIndex == NULL_INDEX) + { + if (codepoint == ' ') + { + // Mark line split here + splitIndex = buffer.size() - 1; + } + else if (splitIndex == NULL_INDEX) + { + // Mark line split here (this is after first character of line) + splitIndex = buffer.size(); + } + } + else + { + // Insert new line before current word + buffer.insert(buffer.begin() + splitIndex, '\0'); - if (lineWidth <= width || numCharactersOnLine == 0) - { - ch = nextCh; - numCharactersOnLine++; + // Recalculate the line length after splitting + lineWidth = gfx_get_string_width(&buffer[currentLineIndex]); + maxWidth = std::max(maxWidth, lineWidth); + numLines++; + + currentLineIndex = splitIndex + 1; + splitIndex = NULL_INDEX; + + // Trim the beginning of the new line + while (buffer[currentLineIndex] == ' ') + { + buffer.erase(buffer.begin() + currentLineIndex); + } + } + } } - else if (currentWord == nullptr) + else if (token.kind == FORMAT_NEWLINE) { - // Single word is longer than line, insert null terminator - ch += utf8_insert_codepoint(ch, 0); + buffer.push_back('\0'); + + auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]); maxWidth = std::max(maxWidth, lineWidth); - (*outNumLines)++; - lineWidth = 0; - currentWord = nullptr; - firstCh = ch; - numCharactersOnLine = 0; + numLines++; + splitIndex = buffer.size(); } else { - ch = currentWord; - *ch++ = 0; - - maxWidth = std::max(maxWidth, currentWidth); - (*outNumLines)++; - lineWidth = 0; - currentWord = nullptr; - firstCh = ch; - numCharactersOnLine = 0; + buffer.append(token.text); } } - maxWidth = std::max(maxWidth, lineWidth); + { + // Final line width calculation + auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]); + maxWidth = std::max(maxWidth, lineWidth); + } + + std::memcpy(text, buffer.data(), buffer.size() + 1); + *outNumLines = static_cast(numLines); *outFontHeight = gCurrentFontSpriteBase; - return maxWidth == 0 ? lineWidth : maxWidth; + return maxWidth; } /** @@ -509,20 +587,18 @@ static void ttf_draw_character_sprite(rct_drawpixelinfo* dpi, int32_t codepoint, info->x += characterWidth; } -static void ttf_draw_string_raw_sprite(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info) +static void ttf_draw_string_raw_sprite(rct_drawpixelinfo* dpi, const std::string_view text, text_draw_info* info) { - const utf8* ch = text; - int32_t codepoint; - - while (!utf8_is_format_code(codepoint = utf8_get_next(ch, &ch))) + CodepointView codepoints(text); + for (auto codepoint : codepoints) { ttf_draw_character_sprite(dpi, codepoint, info); - }; + } } #ifndef NO_TTF -static void ttf_draw_string_raw_ttf(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info) +static void ttf_draw_string_raw_ttf(rct_drawpixelinfo* dpi, std::string_view text, text_draw_info* info) { if (!ttf_initialise()) return; @@ -670,58 +746,38 @@ static void ttf_draw_string_raw_ttf(rct_drawpixelinfo* dpi, const utf8* text, te #endif // NO_TTF -static void ttf_draw_string_raw(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info) +static void ttf_process_format_code(rct_drawpixelinfo* dpi, const FmtString::token& token, text_draw_info* info) { -#ifndef NO_TTF - if (info->flags & TEXT_DRAW_FLAG_TTF) - { - ttf_draw_string_raw_ttf(dpi, text, info); - } - else - { -#endif // NO_TTF - ttf_draw_string_raw_sprite(dpi, text, info); -#ifndef NO_TTF - } -#endif // NO_TTF -} - -static const utf8* ttf_process_format_code(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info) -{ - const utf8* nextCh; - int32_t codepoint; - - codepoint = utf8_get_next(text, &nextCh); - switch (codepoint) + switch (token.kind) { case FORMAT_MOVE_X: - info->x = info->startX + static_cast(*nextCh++); + // info->x = info->startX + static_cast(*nextCh++); break; case FORMAT_ADJUST_PALETTE: { - auto paletteMapId = static_cast(*nextCh++); - auto paletteMap = GetPaletteMapForColour(paletteMapId); - if (paletteMap) - { - uint32_t c = (*paletteMap)[249] + 256; - if (!(info->flags & TEXT_DRAW_FLAG_OUTLINE)) - { - c &= 0xFF; - } - info->palette[1] = c & 0xFF; - info->palette[2] = (c >> 8) & 0xFF; - - // Adjust the text palette - info->palette[3] = (*paletteMap)[247]; - info->palette[4] = (*paletteMap)[248]; - info->palette[5] = (*paletteMap)[250]; - info->palette[6] = (*paletteMap)[251]; - } + // auto paletteMapId = static_cast(*nextCh++); + // auto paletteMap = GetPaletteMapForColour(paletteMapId); + // if (paletteMap) + // { + // uint32_t c = (*paletteMap)[249] + 256; + // if (!(info->flags & TEXT_DRAW_FLAG_OUTLINE)) + // { + // c &= 0xFF; + // } + // info->palette[1] = c & 0xFF; + // info->palette[2] = (c >> 8) & 0xFF; + // + // // Adjust the text palette + // info->palette[3] = (*paletteMap)[247]; + // info->palette[4] = (*paletteMap)[248]; + // info->palette[5] = (*paletteMap)[250]; + // info->palette[6] = (*paletteMap)[251]; + // } break; } case FORMAT_3: case FORMAT_4: - nextCh++; + // nextCh++; break; case FORMAT_NEWLINE: info->x = info->startX; @@ -766,102 +822,89 @@ static const utf8* ttf_process_format_code(rct_drawpixelinfo* dpi, const utf8* t } case FORMAT_16: break; - case FORMAT_INLINE_SPRITE: - { - uint32_t imageId; - std::memcpy(&imageId, nextCh, sizeof(uint32_t)); - const rct_g1_element* g1 = gfx_get_g1_element(imageId & 0x7FFFF); - if (g1 != nullptr) - { - if (!(info->flags & TEXT_DRAW_FLAG_NO_DRAW)) - { - gfx_draw_sprite(dpi, imageId, { info->x, info->y }, 0); - } - info->x += g1->width; - } - nextCh += 4; - break; - } + // case FORMAT_INLINE_SPRITE: + // { + // uint32_t imageId; + // std::memcpy(&imageId, nextCh, sizeof(uint32_t)); + // const rct_g1_element* g1 = gfx_get_g1_element(imageId & 0x7FFFF); + // if (g1 != nullptr) + // { + // if (!(info->flags & TEXT_DRAW_FLAG_NO_DRAW)) + // { + // gfx_draw_sprite(dpi, imageId, { info->x, info->y }, 0); + // } + // info->x += g1->width; + // } + // nextCh += 4; + // break; + // } default: - if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END) + if (token.kind >= FORMAT_COLOUR_CODE_START && token.kind <= FORMAT_COLOUR_CODE_END) { uint16_t flags = info->flags; - colour_char(codepoint - FORMAT_COLOUR_CODE_START, &flags, info->palette); + colour_char(token.kind - FORMAT_COLOUR_CODE_START, &flags, info->palette); } - else if (codepoint <= 0x16) + else if (token.kind <= 0x16) { // case 0x11? FORMAT_NEW_LINE_X_Y - nextCh += 2; + // nextCh += 2; } else { - nextCh += 4; // never happens? + // nextCh += 4; // never happens? } break; } - return nextCh; } -static const utf8* ttf_process_glyph_run(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info) +static void ttf_process_string_literal(rct_drawpixelinfo* dpi, const std::string_view text, text_draw_info* info) { - utf8 buffer[512]; - const utf8* ch = text; - const utf8* lastCh; - int32_t codepoint; - #ifndef NO_TTF bool isTTF = info->flags & TEXT_DRAW_FLAG_TTF; #else bool isTTF = false; #endif // NO_TTF - while (!utf8_is_format_code(codepoint = utf8_get_next(ch, &lastCh))) + + if (!isTTF) { - if (isTTF && utf8_should_use_sprite_for_codepoint(codepoint)) - { - break; - } - ch = lastCh; - } - if (codepoint == 0) - { - ttf_draw_string_raw(dpi, text, info); - return ch; + ttf_draw_string_raw_sprite(dpi, text, info); } else { - size_t length = static_cast(ch - text); - std::memcpy(buffer, text, length); - buffer[length] = 0; - ttf_draw_string_raw(dpi, buffer, info); - return ch; + CodepointView codepoints(text); + size_t ttfRunIndex = 0; + for (auto it = codepoints.begin(); it != codepoints.end(); it++) + { + auto codepoint = *it; + if (utf8_should_use_sprite_for_codepoint(codepoint)) + { + auto index = it.GetIndex(); + auto ttfLen = index - ttfRunIndex; + if (ttfLen > 0) + { + // Draw the TTF run + ttf_draw_string_raw_ttf(dpi, text.substr(ttfRunIndex, ttfLen), info); + } + ttfRunIndex = index; + + // Draw the sprite font glyph + ttf_draw_character_sprite(dpi, codepoint, info); + } + } } } -static void ttf_process_string(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info) +static void ttf_process_string(rct_drawpixelinfo* dpi, std::string_view text, text_draw_info* info) { - const utf8* ch = text; - const utf8* nextCh; - int32_t codepoint; - -#ifndef NO_TTF - bool isTTF = info->flags & TEXT_DRAW_FLAG_TTF; -#else - bool isTTF = false; -#endif // NO_TTF - - while ((codepoint = utf8_get_next(ch, &nextCh)) != 0) + FmtString fmt(text); + for (const auto& token : fmt) { - if (utf8_is_format_code(codepoint)) + if (token.IsLiteral()) { - ch = ttf_process_format_code(dpi, ch, info); - } - else if (isTTF && utf8_should_use_sprite_for_codepoint(codepoint)) - { - ttf_draw_character_sprite(dpi, codepoint, info); - ch = nextCh; + ttf_process_string_literal(dpi, token.text, info); } else { - ch = ttf_process_glyph_run(dpi, ch, info); + ttf_process_format_code(dpi, token, info); } info->maxX = std::max(info->maxX, info->x); info->maxY = std::max(info->maxY, info->y); @@ -963,7 +1006,7 @@ void ttf_draw_string(rct_drawpixelinfo* dpi, const_utf8string text, int32_t colo gLastDrawStringY = info.y; } -static int32_t ttf_get_string_width(const utf8* text) +static int32_t ttf_get_string_width(std::string_view text) { text_draw_info info; info.font_sprite_base = gCurrentFontSpriteBase; diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index e8fa9b95ef..6ad406022d 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -757,7 +757,7 @@ void gfx_draw_string_with_y_offsets( bool forceSpriteFont); int32_t gfx_wrap_string(char* buffer, int32_t width, int32_t* num_lines, int32_t* font_height); -int32_t gfx_get_string_width(const utf8* buffer); +int32_t gfx_get_string_width(std::string_view text); int32_t gfx_get_string_width_new_lined(char* buffer); int32_t string_get_height_raw(char* buffer); int32_t gfx_clip_string(char* buffer, int32_t width); diff --git a/src/openrct2/drawing/TTF.cpp b/src/openrct2/drawing/TTF.cpp index 225612b402..ec4bc6973e 100644 --- a/src/openrct2/drawing/TTF.cpp +++ b/src/openrct2/drawing/TTF.cpp @@ -19,6 +19,7 @@ # include "../OpenRCT2.h" # include "../config/Config.h" +# include "../core/String.hpp" # include "../localisation/Localisation.h" # include "../localisation/LocalisationService.h" # include "../platform/platform.h" @@ -63,9 +64,9 @@ static uint32_t ttf_surface_cache_hash(TTF_Font* font, const utf8* text); static void ttf_surface_cache_dispose(ttf_cache_entry* entry); static void ttf_surface_cache_dispose_all(); static void ttf_getwidth_cache_dispose_all(); -static bool ttf_get_size(TTF_Font* font, const utf8* text, int32_t* width, int32_t* height); +static bool ttf_get_size(TTF_Font* font, std::string_view text, int32_t* outWidth, int32_t* outHeight); static void ttf_toggle_hinting(bool); -static TTFSurface* ttf_render(TTF_Font* font, const utf8* text); +static TTFSurface* ttf_render(TTF_Font* font, std::string_view text); template class FontLockHelper { @@ -181,12 +182,12 @@ static void ttf_close_font(TTF_Font* font) TTF_CloseFont(font); } -static uint32_t ttf_surface_cache_hash(TTF_Font* font, const utf8* text) +static uint32_t ttf_surface_cache_hash(TTF_Font* font, std::string_view text) { uint32_t hash = static_cast(((reinterpret_cast(font) * 23) ^ 0xAAAAAAAA) & 0xFFFFFFFF); - for (const utf8* ch = text; *ch != 0; ch++) + for (size_t i = 0; i < text.size(); i++) { - hash = ror32(hash, 3) ^ (*ch * 13); + hash = ror32(hash, 3) ^ (text[i] * 13); } return hash; } @@ -219,7 +220,7 @@ void ttf_toggle_hinting() ttf_toggle_hinting(true); } -TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text) +TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, std::string_view text) { ttf_cache_entry* entry; @@ -235,7 +236,7 @@ TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text) // Check if entry is a hit if (entry->surface == nullptr) break; - if (entry->font == font && strcmp(entry->text, text) == 0) + if (entry->font == font && String::Equals(entry->text, text)) { _ttfSurfaceCacheHitCount++; entry->lastUseTick = gCurrentDrawCount; @@ -269,7 +270,7 @@ TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text) _ttfSurfaceCacheCount++; entry->surface = surface; entry->font = font; - entry->text = _strdup(text); + entry->text = strndup(text.data(), text.size()); entry->lastUseTick = gCurrentDrawCount; return entry->surface; } @@ -295,7 +296,7 @@ static void ttf_getwidth_cache_dispose_all() } } -uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text) +uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, std::string_view text) { ttf_getwidth_cache_entry* entry; @@ -311,7 +312,7 @@ uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text) // Check if entry is a hit if (entry->text == nullptr) break; - if (entry->font == font && strcmp(entry->text, text) == 0) + if (entry->font == font && String::Equals(entry->text, text)) { _ttfGetWidthCacheHitCount++; entry->lastUseTick = gCurrentDrawCount; @@ -341,7 +342,7 @@ uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text) _ttfGetWidthCacheCount++; entry->width = width; entry->font = font; - entry->text = _strdup(text); + entry->text = strndup(text.data(), text.size()); entry->lastUseTick = gCurrentDrawCount; return entry->width; } @@ -357,20 +358,24 @@ bool ttf_provides_glyph(const TTF_Font* font, codepoint_t codepoint) return TTF_GlyphIsProvided(font, codepoint); } -static bool ttf_get_size(TTF_Font* font, const utf8* text, int32_t* outWidth, int32_t* outHeight) +static bool ttf_get_size(TTF_Font* font, std::string_view text, int32_t* outWidth, int32_t* outHeight) { - return TTF_SizeUTF8(font, text, outWidth, outHeight); + thread_local std::string buffer; + buffer.assign(text); + return TTF_SizeUTF8(font, buffer.c_str(), outWidth, outHeight); } -static TTFSurface* ttf_render(TTF_Font* font, const utf8* text) +static TTFSurface* ttf_render(TTF_Font* font, std::string_view text) { + thread_local std::string buffer; + buffer.assign(text); if (TTF_GetFontHinting(font) != 0) { - return TTF_RenderUTF8_Shaded(font, text, 0x000000FF, 0x000000FF); + return TTF_RenderUTF8_Shaded(font, buffer.c_str(), 0x000000FF, 0x000000FF); } else { - return TTF_RenderUTF8_Solid(font, text, 0x000000FF); + return TTF_RenderUTF8_Solid(font, buffer.c_str(), 0x000000FF); } } diff --git a/src/openrct2/drawing/TTF.h b/src/openrct2/drawing/TTF.h index f5b2a424b3..57c6b9c77e 100644 --- a/src/openrct2/drawing/TTF.h +++ b/src/openrct2/drawing/TTF.h @@ -11,6 +11,8 @@ #include "Font.h" +#include + bool ttf_initialise(); void ttf_dispose(); @@ -26,8 +28,8 @@ struct TTFSurface TTFFontDescriptor* ttf_get_font_from_sprite_base(uint16_t spriteBase); void ttf_toggle_hinting(); -TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text); -uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text); +TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, std::string_view text); +uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, std::string_view text); bool ttf_provides_glyph(const TTF_Font* font, codepoint_t codepoint); void ttf_free_surface(TTFSurface* surface); diff --git a/src/openrct2/localisation/Formatting.cpp b/src/openrct2/localisation/Formatting.cpp index 051bf4bcea..d4deb1b0d0 100644 --- a/src/openrct2/localisation/Formatting.cpp +++ b/src/openrct2/localisation/Formatting.cpp @@ -136,7 +136,7 @@ namespace OpenRCT2 } FmtString::FmtString(const char* s) - : FmtString(std::string_view(s)) + : FmtString(s == nullptr ? std::string_view() : std::string_view(s)) { } @@ -176,6 +176,18 @@ namespace OpenRCT2 return sz != nullptr ? sz : std::string_view(); } + void FormatRealName(std::stringstream& ss, rct_string_id id) + { + if (IsRealNameStringId(id)) + { + auto realNameIndex = id - REAL_NAME_START; + ss << real_names[realNameIndex % std::size(real_names)]; + ss << ' '; + ss << real_name_initials[(realNameIndex >> 10) % std::size(real_name_initials)]; + ss << '.'; + } + } + template static void AppendSeperator(char (&buffer)[TSize], TIndex& i, std::string_view sep) { if (i < TSize) @@ -468,13 +480,6 @@ namespace OpenRCT2 ss << arg.c_str(); } break; - case FORMAT_STRINGID: - case FORMAT_STRINGID2: - if constexpr (std::is_integral()) - { - ss << language_get_string(arg); - } - break; } } @@ -488,11 +493,15 @@ namespace OpenRCT2 return t == FORMAT_COMMA1DP16 || (t >= FORMAT_COMMA32 && t <= FORMAT_LENGTH); } + bool IsRealNameStringId(rct_string_id id) + { + return id >= REAL_NAME_START && id <= REAL_NAME_END; + } + FmtString GetFmtStringById(rct_string_id id) { - auto lang = language_get_string(id); - auto fmtc = language_convert_string_to_tokens(lang); - return FmtString(std::move(fmtc)); + auto fmtc = language_get_string(id); + return FmtString(fmtc); } std::stringstream& GetThreadFormatStream() @@ -547,8 +556,15 @@ namespace OpenRCT2 const auto& arg = args[argIndex++]; if (auto stringid = std::get_if(&arg)) { - auto subfmt = GetFmtStringById(*stringid); - FormatStringAny(ss, subfmt, args, argIndex); + if (IsRealNameStringId(*stringid)) + { + FormatRealName(ss, *stringid); + } + else + { + auto subfmt = GetFmtStringById(*stringid); + FormatStringAny(ss, subfmt, args, argIndex); + } } } else @@ -630,7 +646,6 @@ namespace OpenRCT2 { auto sz = ReadFromArgs(args); anyArgs.push_back(sz); - BuildAnyArgListFromLegacyArgBuffer(sz, anyArgs, args); break; } case FORMAT_POP16: @@ -651,3 +666,8 @@ namespace OpenRCT2 return FormatStringAny(buffer, bufferLen, fmt, anyArgs); } } // namespace OpenRCT2 + +void format_string(utf8* dest, size_t size, rct_string_id format, const void* args) +{ + OpenRCT2::FormatStringLegacy(dest, size, format, args); +} diff --git a/src/openrct2/localisation/Formatting.h b/src/openrct2/localisation/Formatting.h index 19b760c7bf..27c4e3bdb0 100644 --- a/src/openrct2/localisation/Formatting.h +++ b/src/openrct2/localisation/Formatting.h @@ -64,6 +64,7 @@ namespace OpenRCT2 bool eol() const; }; + FmtString() = default; FmtString(std::string&& s); FmtString(std::string_view s); FmtString(const char* s); @@ -76,6 +77,8 @@ 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); std::stringstream& GetThreadFormatStream(); size_t CopyStringStreamToBuffer(char* buffer, size_t bufferLen, std::stringstream& ss); @@ -110,9 +113,17 @@ namespace OpenRCT2 { if constexpr (std::is_integral()) { - auto subfmt = GetFmtStringById(static_cast(arg0)); - auto subit = subfmt.begin(); - stack.push(&subit); + auto stringId = static_cast(arg0); + if (IsRealNameStringId(stringId)) + { + FormatRealName(ss, stringId); + } + else + { + auto subfmt = GetFmtStringById(stringId); + auto subit = subfmt.begin(); + stack.push(&subit); + } FormatString(ss, stack, argN...); } } @@ -145,6 +156,14 @@ namespace OpenRCT2 return ss.str(); } + template + size_t FormatStringToBuffer(char* buffer, size_t bufferLen, const FmtString& fmt, TArgs&&... argN) + { + auto& ss = GetThreadFormatStream(); + FormatString(ss, fmt, argN...); + return CopyStringStreamToBuffer(buffer, bufferLen, ss); + } + template static void FormatStringId(std::stringstream& ss, rct_string_id id, TArgs&&... argN) { auto fmt = GetFmtStringById(id); diff --git a/src/openrct2/localisation/Language.cpp b/src/openrct2/localisation/Language.cpp index 341a9c71ea..14f546935a 100644 --- a/src/openrct2/localisation/Language.cpp +++ b/src/openrct2/localisation/Language.cpp @@ -51,14 +51,6 @@ const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] = }; // clang-format on -// clang-format off -const utf8 BlackUpArrowString[] = { static_cast(static_cast(0xC2)), static_cast(static_cast(0x8E)), static_cast(static_cast(0xE2)), static_cast(static_cast(0x96)), static_cast(static_cast(0xB2)), static_cast(static_cast(0x00)) }; -const utf8 BlackDownArrowString[] = { static_cast(static_cast(0xC2)), static_cast(static_cast(0x8E)), static_cast(static_cast(0xE2)), static_cast(static_cast(0x96)), static_cast(static_cast(0xBC)), static_cast(static_cast(0x00)) }; -const utf8 BlackLeftArrowString[] = { static_cast(static_cast(0xC2)), static_cast(static_cast(0x8E)), static_cast(static_cast(0xE2)), static_cast(static_cast(0x97)), static_cast(static_cast(0x80)), static_cast(static_cast(0x00)) }; -const utf8 BlackRightArrowString[] = { static_cast(static_cast(0xC2)), static_cast(static_cast(0x8E)), static_cast(static_cast(0xE2)), static_cast(static_cast(0x96)), static_cast(static_cast(0xB6)), static_cast(static_cast(0x00)) }; -const utf8 CheckBoxMarkString[] = { static_cast(static_cast(0xE2)), static_cast(static_cast(0x9C)), static_cast(static_cast(0x93)), static_cast(static_cast(0x00)) }; -// clang-format on - void utf8_remove_format_codes(utf8* text, bool allowcolours) { const utf8* ch = text; diff --git a/src/openrct2/localisation/Language.h b/src/openrct2/localisation/Language.h index 062f13b2a3..9eb43c5de4 100644 --- a/src/openrct2/localisation/Language.h +++ b/src/openrct2/localisation/Language.h @@ -85,11 +85,11 @@ struct language_descriptor extern const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT]; -extern const utf8 BlackUpArrowString[]; -extern const utf8 BlackDownArrowString[]; -extern const utf8 BlackLeftArrowString[]; -extern const utf8 BlackRightArrowString[]; -extern const utf8 CheckBoxMarkString[]; +constexpr const char* BlackUpArrowString = u8"{BLACK}▲"; +constexpr const char* BlackDownArrowString = u8"{BLACK}▼"; +constexpr const char* BlackLeftArrowString = u8"{BLACK}◀"; +constexpr const char* BlackRightArrowString = u8"{BLACK}▶"; +constexpr const char* CheckBoxMarkString = u8"✓"; uint8_t language_get_id_from_locale(const char* locale); const char* language_get_string(rct_string_id id); diff --git a/src/openrct2/localisation/LanguagePack.cpp b/src/openrct2/localisation/LanguagePack.cpp index 0ac8201244..e1a593453c 100644 --- a/src/openrct2/localisation/LanguagePack.cpp +++ b/src/openrct2/localisation/LanguagePack.cpp @@ -539,28 +539,28 @@ 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 + // 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); diff --git a/src/openrct2/localisation/Localisation.cpp b/src/openrct2/localisation/Localisation.cpp index 22a22a301f..f4258ebbbf 100644 --- a/src/openrct2/localisation/Localisation.cpp +++ b/src/openrct2/localisation/Localisation.cpp @@ -1299,7 +1299,7 @@ std::string format_string(rct_string_id format, const void* args) * format (ax) * args (ecx) */ -void format_string(utf8* dest, size_t size, rct_string_id format, const void* args) +void format_string_old(utf8* dest, size_t size, rct_string_id format, const void* args) { #ifdef DEBUG if (gDebugStringFormatting) diff --git a/src/openrct2/paint/Painter.cpp b/src/openrct2/paint/Painter.cpp index fa3e21f77c..2783591fcb 100644 --- a/src/openrct2/paint/Painter.cpp +++ b/src/openrct2/paint/Painter.cpp @@ -19,6 +19,7 @@ #include "../interface/Chat.h" #include "../interface/InteractiveConsole.h" #include "../localisation/FormatCodes.h" +#include "../localisation/Formatting.h" #include "../localisation/Language.h" #include "../paint/Paint.h" #include "../title/TitleScreen.h" @@ -83,16 +84,10 @@ void Painter::PaintReplayNotice(rct_drawpixelinfo* dpi, const char* text) { ScreenCoordsXY screenCoords(_uiContext->GetWidth() / 2, _uiContext->GetHeight() - 44); - // Format string - utf8 buffer[64] = { 0 }; - utf8* ch = buffer; - ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT); - ch = utf8_write_codepoint(ch, FORMAT_OUTLINE); - ch = utf8_write_codepoint(ch, FORMAT_RED); + char buffer[64]{}; + FormatStringToBuffer(buffer, sizeof(buffer), "{MEDIUMFONT}{OUTLINE}{RED}{STRING}", text); - snprintf(ch, 64 - (ch - buffer), "%s", text); - - int32_t stringWidth = gfx_get_string_width(buffer); + auto stringWidth = gfx_get_string_width(buffer); screenCoords.x = screenCoords.x - stringWidth; if (((gCurrentTicks >> 1) & 0xF) > 4) @@ -106,17 +101,10 @@ void Painter::PaintFPS(rct_drawpixelinfo* dpi) { ScreenCoordsXY screenCoords(_uiContext->GetWidth() / 2, 2); - // Measure FPS MeasureFPS(); - // Format string - utf8 buffer[64] = { 0 }; - utf8* ch = buffer; - ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT); - ch = utf8_write_codepoint(ch, FORMAT_OUTLINE); - ch = utf8_write_codepoint(ch, FORMAT_WHITE); - - snprintf(ch, 64 - (ch - buffer), "%d", _currentFPS); + char buffer[64]{}; + FormatStringToBuffer(buffer, sizeof(buffer), "{MEDIUMFONT}{OUTLINE}{WHITE}{INT32}", _currentFPS); // Draw Text int32_t stringWidth = gfx_get_string_width(buffer);