From 157c984d4cf87291b583238550bd91cebe93ce94 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 15 Oct 2020 22:12:41 +0100 Subject: [PATCH] Fix more string functions --- src/openrct2/drawing/Drawing.String.cpp | 127 ++++++++++++----------- src/openrct2/drawing/Drawing.h | 2 +- src/openrct2/localisation/Formatting.cpp | 16 ++- 3 files changed, 79 insertions(+), 66 deletions(-) diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index c5a26966e2..df2ca8b904 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -41,30 +41,34 @@ static int32_t ttf_get_string_width(std::string_view text); * * rct2: 0x006C23B1 */ -int32_t gfx_get_string_width_new_lined(utf8* text) +int32_t gfx_get_string_width_new_lined(std::string_view text) { - utf8* ch = text; - utf8* firstCh = text; - utf8* nextCh; - utf8 backup; - int32_t codepoint; + thread_local std::string buffer; + buffer.clear(); - int32_t maxWidth = 0; - while ((codepoint = utf8_get_next(ch, const_cast(&nextCh))) != 0) + std::optional maxWidth; + FmtString fmt(text); + for (const auto& token : fmt) { - if (codepoint == FORMAT_NEWLINE || codepoint == FORMAT_NEWLINE_SMALLER) + if (token.kind == FORMAT_NEWLINE || token.kind == FORMAT_NEWLINE_SMALLER) { - backup = *nextCh; - *nextCh = 0; - maxWidth = std::max(maxWidth, gfx_get_string_width(firstCh)); - *nextCh = backup; - firstCh = nextCh; + auto width = gfx_get_string_width(buffer); + if (!maxWidth || maxWidth > width) + { + maxWidth = width; + } + buffer.clear(); + } + else + { + buffer.append(token.text); } - ch = nextCh; } - maxWidth = std::max(maxWidth, gfx_get_string_width(firstCh)); - - return maxWidth; + if (!maxWidth) + { + maxWidth = gfx_get_string_width(buffer); + } + return *maxWidth; } /** @@ -87,65 +91,62 @@ int32_t gfx_get_string_width(std::string_view text) */ int32_t gfx_clip_string(utf8* text, int32_t width) { - int32_t clippedWidth; - if (width < 6) { *text = 0; return 0; } - clippedWidth = gfx_get_string_width(text); + // If width of the full string is less than allowed width then we don't need to clip + auto clippedWidth = gfx_get_string_width(text); if (clippedWidth <= width) { return clippedWidth; } - utf8 backup[4]; - utf8* ch = text; - utf8* nextCh = text; - utf8* clipCh = text; - int32_t codepoint; - while ((codepoint = utf8_get_next(ch, const_cast(&nextCh))) != 0) + // Append each character 1 by 1 with an ellipsis on the end until width is exceeded + thread_local std::string buffer; + buffer.clear(); + + size_t bestLength = 0; + int32_t bestWidth = 0; + + FmtString fmt(text); + for (const auto& token : fmt) { - if (utf8_is_format_code(codepoint)) + CodepointView codepoints(token.text); + for (auto codepoint : codepoints) { - ch = nextCh; - ch += utf8_get_format_code_arg_length(codepoint); - continue; - } + // Add the ellipsis before checking the width + buffer.append("..."); - for (int32_t i = 0; i < 4; i++) - { - backup[i] = nextCh[i]; - }; - for (int32_t i = 0; i < 3; i++) - { - nextCh[i] = '.'; - } - nextCh[3] = 0; - - int32_t queryWidth = gfx_get_string_width(text); - if (queryWidth < width) - { - clipCh = nextCh; - clippedWidth = queryWidth; - } - else - { - for (int32_t i = 0; i < 3; i++) + auto currentWidth = gfx_get_string_width(text); + if (currentWidth < width) { - clipCh[i] = '.'; - } - clipCh[3] = 0; - return clippedWidth; - } + bestLength = buffer.size(); + bestWidth = currentWidth; - for (int32_t i = 0; i < 4; i++) - { - nextCh[i] = backup[i]; - }; - ch = nextCh; + // Trim the ellipsis + buffer.resize(bestLength - 3); + } + else + { + // Width exceeded, rollback to best length and put ellipsis back + buffer.resize(bestLength); + for (auto i = static_cast(bestLength) - 1; i >= 0 && i >= bestLength - 3; i--) + { + buffer[bestLength - i] = '.'; + } + + // Copy buffer back to input text buffer + std::strcpy(text, buffer.c_str()); + return bestWidth; + } + + char cb[8]{}; + utf8_write_codepoint(cb, codepoint); + buffer.append(cb); + } } return gfx_get_string_width(text); } @@ -182,7 +183,9 @@ int32_t gfx_wrap_string(utf8* text, int32_t width, int32_t* outNumLines, int32_t CodepointView codepoints(token.text); for (auto codepoint : codepoints) { - buffer.push_back(codepoint); + char cb[8]{}; + utf8_write_codepoint(cb, codepoint); + buffer.append(cb); auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]); if (lineWidth <= width || splitIndex == NULL_INDEX) diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index 6ad406022d..7ccb38eeb5 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -758,7 +758,7 @@ void gfx_draw_string_with_y_offsets( int32_t gfx_wrap_string(char* buffer, int32_t width, int32_t* num_lines, int32_t* font_height); int32_t gfx_get_string_width(std::string_view text); -int32_t gfx_get_string_width_new_lined(char* buffer); +int32_t gfx_get_string_width_new_lined(std::string_view text); int32_t string_get_height_raw(char* buffer); int32_t gfx_clip_string(char* buffer, int32_t width); void shorten_path(utf8* buffer, size_t bufferSize, const utf8* path, int32_t availableWidth); diff --git a/src/openrct2/localisation/Formatting.cpp b/src/openrct2/localisation/Formatting.cpp index abfe1f8f12..2438062382 100644 --- a/src/openrct2/localisation/Formatting.cpp +++ b/src/openrct2/localisation/Formatting.cpp @@ -79,7 +79,11 @@ namespace OpenRCT2 return; } - if (str[i] == '{' && i + 1 < str.size() && str[i + 1] != '{') + if (str[i] == '\n' || str[i] == '\r') + { + i++; + } + else if (str[i] == '{' && i + 1 < str.size() && str[i + 1] != '{') { // Move to end brace auto startIndex = i; @@ -127,7 +131,7 @@ namespace OpenRCT2 do { i++; - } while (i < str.size() && str[i] != '{'); + } while (i < str.size() && str[i] != '{' && str[i] != '\n' && str[i] != '\r'); } current = CreateToken(i - index); } @@ -150,6 +154,10 @@ namespace OpenRCT2 auto kind = format_get_code(sztoken.substr(1, len - 2)); return token(kind, sztoken); } + else if (sztoken == "\n" || sztoken == "\r") + { + return token(FORMAT_NEWLINE, sztoken); + } return token(0, sztoken); } @@ -566,6 +574,8 @@ namespace OpenRCT2 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); } @@ -656,7 +666,7 @@ namespace OpenRCT2 } argIndex++; } - else + else if (token.kind != FORMAT_PUSH16 && token.kind != FORMAT_POP16) { ss << token.text; }