From 8216d0052200904328379103d418146652e06285 Mon Sep 17 00:00:00 2001 From: Silent Date: Mon, 13 Feb 2023 18:48:36 +0100 Subject: [PATCH 1/2] Set the container type in LanguagePack::FromFile to std::string The code interpreting fileData assumes the string has a valid null terminator, which is not guaranteed by std::vector, but is guaranteed by std::string. --- src/openrct2/localisation/LanguagePack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openrct2/localisation/LanguagePack.cpp b/src/openrct2/localisation/LanguagePack.cpp index 7f46cb93da..51efc6209c 100644 --- a/src/openrct2/localisation/LanguagePack.cpp +++ b/src/openrct2/localisation/LanguagePack.cpp @@ -70,7 +70,7 @@ public: Guard::ArgumentNotNull(path); // Load file directly into memory - std::vector fileData; + u8string fileData; try { From 0763fd5a784d8a54254cac1db40cb9d73341510a Mon Sep 17 00:00:00 2001 From: Silent Date: Mon, 13 Feb 2023 22:02:40 +0100 Subject: [PATCH 2/2] Fix GfxWrapString potentially corrupting the in/out buffer Fixes a consistent assertion when displaying "Cannot start construction" in Japanese, but it has potential fix countless other crashes related to string wrapping/display. --- distribution/changelog.txt | 1 + src/openrct2-ui/interface/Widget.cpp | 36 +++++++++---------- src/openrct2-ui/windows/Error.cpp | 6 ++-- src/openrct2-ui/windows/GameBottomToolbar.cpp | 4 +-- src/openrct2-ui/windows/Multiplayer.cpp | 13 ++++--- src/openrct2-ui/windows/TextInput.cpp | 30 +++++++--------- src/openrct2-ui/windows/Tooltip.cpp | 15 ++++---- src/openrct2/drawing/Drawing.String.cpp | 31 +++++++++------- src/openrct2/drawing/Drawing.h | 7 ++-- src/openrct2/drawing/Text.cpp | 16 ++++----- src/openrct2/interface/Chat.cpp | 35 ++++++------------ src/openrct2/interface/Chat.h | 3 +- 12 files changed, 88 insertions(+), 109 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 5a4a42a476..cdd7a7e13e 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -43,6 +43,7 @@ - Fix: [#19292] Overflow in totalRideValue. - Fix: [#19379] "No platforms" station style shows platforms on the Junior Roller Coaster. - Fix: [#19380] Startup crash when no sequences are installed and random sequences are enabled. +- Fix: [#19391] String corruption caused by an improper buffer handling in GfxWrapString. 0.4.3 (2022-12-14) ------------------------------------------------------------------------ diff --git a/src/openrct2-ui/interface/Widget.cpp b/src/openrct2-ui/interface/Widget.cpp index 4d3e29e020..b88b021f87 100644 --- a/src/openrct2-ui/interface/Widget.cpp +++ b/src/openrct2-ui/interface/Widget.cpp @@ -1123,9 +1123,6 @@ void WidgetSetCheckboxValue(WindowBase& w, WidgetIndex widgetIndex, bool value) static void WidgetTextBoxDraw(DrawPixelInfo* dpi, WindowBase& w, WidgetIndex widgetIndex) { - int32_t no_lines = 0; - char wrapped_string[TEXT_INPUT_SIZE]; - // Get the widget const auto& widget = w.widgets[widgetIndex]; @@ -1149,43 +1146,42 @@ static void WidgetTextBoxDraw(DrawPixelInfo* dpi, WindowBase& w, WidgetIndex wid { if (widget.text != 0) { - SafeStrCpy(wrapped_string, widget.string, 512); - GfxWrapString(wrapped_string, bottomRight.x - topLeft.x - 5, FontStyle::Medium, &no_lines); - GfxDrawStringNoFormatting(dpi, { topLeft.x + 2, topLeft.y }, wrapped_string, { w.colours[1], FontStyle::Medium }); + u8string wrappedString; + GfxWrapString(widget.string, bottomRight.x - topLeft.x - 5, FontStyle::Medium, &wrappedString, nullptr); + GfxDrawStringNoFormatting( + dpi, { topLeft.x + 2, topLeft.y }, wrappedString.c_str(), { w.colours[1], FontStyle::Medium }); } return; } - SafeStrCpy(wrapped_string, gTextBoxInput, TEXT_INPUT_SIZE); - // String length needs to add 12 either side of box // +13 for cursor when max length. - GfxWrapString(wrapped_string, bottomRight.x - topLeft.x - 5 - 6, FontStyle::Medium, &no_lines); + u8string wrappedString; + GfxWrapString(gTextBoxInput, bottomRight.x - topLeft.x - 5 - 6, FontStyle::Medium, &wrappedString, nullptr); - GfxDrawStringNoFormatting(dpi, { topLeft.x + 2, topLeft.y }, wrapped_string, { w.colours[1], FontStyle::Medium }); + GfxDrawStringNoFormatting(dpi, { topLeft.x + 2, topLeft.y }, wrappedString.c_str(), { w.colours[1], FontStyle::Medium }); - size_t string_length = GetStringSize(wrapped_string) - 1; - - // Make a copy of the string for measuring the width. - char temp_string[TEXT_INPUT_SIZE] = { 0 }; - std::memcpy(temp_string, wrapped_string, std::min(string_length, gTextInput->SelectionStart)); - int32_t cur_x = topLeft.x + GfxGetStringWidthNoFormatting(temp_string, FontStyle::Medium) + 3; + // Make a trimmed view of the string for measuring the width. + int32_t curX = topLeft.x + + GfxGetStringWidthNoFormatting( + u8string_view{ wrappedString.c_str(), std::min(wrappedString.length(), gTextInput->SelectionStart) }, + FontStyle::Medium) + + 3; int32_t width = 6; if (static_cast(gTextInput->SelectionStart) < strlen(gTextBoxInput)) { // Make a new 1 character wide string for measuring the width // of the character that the cursor is under. - temp_string[1] = '\0'; - temp_string[0] = gTextBoxInput[gTextInput->SelectionStart]; - width = std::max(GfxGetStringWidthNoFormatting(temp_string, FontStyle::Medium) - 2, 4); + width = std::max( + GfxGetStringWidthNoFormatting(u8string{ gTextBoxInput[gTextInput->SelectionStart] }, FontStyle::Medium) - 2, 4); } if (gTextBoxFrameNo <= 15) { colour = ColourMapA[w.colours[1]].mid_light; auto y = topLeft.y + (widget.height() - 1); - GfxFillRect(dpi, { { cur_x, y }, { cur_x + width, y } }, colour + 5); + GfxFillRect(dpi, { { curX, y }, { curX + width, y } }, colour + 5); } } diff --git a/src/openrct2-ui/windows/Error.cpp b/src/openrct2-ui/windows/Error.cpp index 38527f967d..ccf30e943b 100644 --- a/src/openrct2-ui/windows/Error.cpp +++ b/src/openrct2-ui/windows/Error.cpp @@ -37,7 +37,7 @@ private: public: ErrorWindow(std::string text, uint16_t numLines) - : _text(text) + : _text(std::move(text)) , _numLines(numLines) { } @@ -139,7 +139,7 @@ WindowBase* WindowErrorOpen(std::string_view title, std::string_view message) width = std::clamp(width, 64, 196); int32_t numLines{}; - GfxWrapString(buffer.data(), width + 1, FontStyle::Medium, &numLines); + GfxWrapString(buffer, width + 1, FontStyle::Medium, &buffer, &numLines); width = width + 3; int32_t height = (numLines + 1) * FontGetLineHeight(FontStyle::Medium) + 4; @@ -155,7 +155,7 @@ WindowBase* WindowErrorOpen(std::string_view title, std::string_view message) windowPosition.y = std::min(windowPosition.y - height - 40, maxY); } - auto errorWindow = std::make_unique(buffer, numLines); + auto errorWindow = std::make_unique(std::move(buffer), numLines); return WindowCreate( std::move(errorWindow), WindowClass::Error, windowPosition, width, height, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_RESIZABLE); diff --git a/src/openrct2-ui/windows/GameBottomToolbar.cpp b/src/openrct2-ui/windows/GameBottomToolbar.cpp index 9553845b17..88678d0f43 100644 --- a/src/openrct2-ui/windows/GameBottomToolbar.cpp +++ b/src/openrct2-ui/windows/GameBottomToolbar.cpp @@ -567,10 +567,10 @@ static void WindowGameBottomToolbarDrawNewsItem(DrawPixelInfo* dpi, WindowBase* w->colours[2], INSET_RECT_F_30); // Text - const auto* newsItemText = newsItem->Text.c_str(); auto screenCoords = w->windowPos + ScreenCoordsXY{ middleOutsetWidget->midX(), middleOutsetWidget->top + 11 }; width = middleOutsetWidget->width() - 62; - DrawNewsTicker(dpi, screenCoords, width, COLOUR_BRIGHT_GREEN, STR_BOTTOM_TOOLBAR_NEWS_TEXT, &newsItemText, newsItem->Ticks); + DrawNewsTicker( + dpi, screenCoords, width, COLOUR_BRIGHT_GREEN, STR_BOTTOM_TOOLBAR_NEWS_TEXT, newsItem->Text, newsItem->Ticks); screenCoords = w->windowPos + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].left, diff --git a/src/openrct2-ui/windows/Multiplayer.cpp b/src/openrct2-ui/windows/Multiplayer.cpp index 4b2eab106f..b432c0db02 100644 --- a/src/openrct2-ui/windows/Multiplayer.cpp +++ b/src/openrct2-ui/windows/Multiplayer.cpp @@ -339,22 +339,21 @@ static ScreenCoordsXY WindowMultiplayerInformationGetSize() // Base dimensions. const int32_t width = 450; int32_t height = 55; - int32_t numLines; // Server name is displayed word-wrapped, so figure out how high it will be. { - u8string buffer = NetworkGetServerName(); - GfxWrapString(buffer.data(), width, FontStyle::Medium, &numLines); - height += ++numLines * lineHeight + (LIST_ROW_HEIGHT / 2); + int32_t numLines; + GfxWrapString(NetworkGetServerName(), width, FontStyle::Medium, nullptr, &numLines); + height += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); } // Likewise, for the optional server description -- which can be a little longer. const auto& descString = NetworkGetServerDescription(); if (!descString.empty()) { - u8string buffer = descString; - GfxWrapString(buffer.data(), width, FontStyle::Medium, &numLines); - height += ++numLines * lineHeight + (LIST_ROW_HEIGHT / 2); + int32_t numLines; + GfxWrapString(descString, width, FontStyle::Medium, nullptr, &numLines); + height += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); } // Finally, account for provider info, if present. diff --git a/src/openrct2-ui/windows/TextInput.cpp b/src/openrct2-ui/windows/TextInput.cpp index c45709fd9c..c35ee81397 100644 --- a/src/openrct2-ui/windows/TextInput.cpp +++ b/src/openrct2-ui/windows/TextInput.cpp @@ -218,12 +218,11 @@ public: screenCoords.y += 25; - char wrapped_string[TEXT_INPUT_SIZE]; - SafeStrCpy(wrapped_string, _buffer.data(), _buffer.size()); - // String length needs to add 12 either side of box // +13 for cursor when max length. - GfxWrapString(wrapped_string, WW - (24 + 13), FontStyle::Medium, &no_lines); + u8string wrappedString; + GfxWrapString( + u8string_view{ _buffer.data(), _buffer.size() }, WW - (24 + 13), FontStyle::Medium, &wrappedString, &no_lines); GfxFillRectInset( &dpi, { { windowPos.x + 10, screenCoords.y }, { windowPos.x + WW - 10, screenCoords.y + 10 * (no_lines + 1) + 3 } }, @@ -231,7 +230,7 @@ public: screenCoords.y += 1; - char* wrap_pointer = wrapped_string; + const utf8* wrapPointer = wrappedString.data(); size_t char_count = 0; uint8_t cur_drawn = 0; @@ -240,16 +239,16 @@ public: for (int32_t line = 0; line <= no_lines; line++) { screenCoords.x = windowPos.x + 12; - GfxDrawStringNoFormatting(&dpi, screenCoords, wrap_pointer, { colours[1], FontStyle::Medium }); + GfxDrawStringNoFormatting(&dpi, screenCoords, wrapPointer, { colours[1], FontStyle::Medium }); - size_t string_length = GetStringSize(wrap_pointer) - 1; + size_t string_length = GetStringSize(wrapPointer) - 1; if (!cur_drawn && (gTextInput->SelectionStart <= char_count + string_length)) { - // Make a copy of the string for measuring the width. - char temp_string[TEXT_INPUT_SIZE] = { 0 }; - std::memcpy(temp_string, wrap_pointer, gTextInput->SelectionStart - char_count); - cursorX = windowPos.x + 13 + GfxGetStringWidthNoFormatting(temp_string, FontStyle::Medium); + // Make a view of the string for measuring the width. + cursorX = windowPos.x + 13 + + GfxGetStringWidthNoFormatting( + u8string_view{ wrapPointer, gTextInput->SelectionStart - char_count }, FontStyle::Medium); cursorY = screenCoords.y; int32_t textWidth = 6; @@ -257,7 +256,7 @@ public: { // Make a 1 utf8-character wide string for measuring the width // of the currently selected character. - utf8 tmp[5] = { 0 }; // This is easier than setting temp_string[0..5] + utf8 tmp[5] = {}; // This is easier than setting temp_string[0..5] uint32_t codepoint = UTF8GetNext(_buffer.data() + gTextInput->SelectionStart, nullptr); UTF8WriteCodepoint(tmp, codepoint); textWidth = std::max(GfxGetStringWidthNoFormatting(tmp, FontStyle::Medium) - 2, 4); @@ -274,7 +273,7 @@ public: cur_drawn++; } - wrap_pointer += string_length + 1; + wrapPointer += string_length + 1; if (_buffer[char_count + string_length] == ' ') char_count++; @@ -305,12 +304,9 @@ public: static int32_t CalculateWindowHeight(std::string_view text) { - std::string wrappedString(text); - wrappedString.resize(TEXT_INPUT_SIZE); - // String length needs to add 12 either side of box +13 for cursor when max length. int32_t numLines{}; - GfxWrapString(wrappedString.data(), WW - (24 + 13), FontStyle::Medium, &numLines); + GfxWrapString(text, WW - (24 + 13), FontStyle::Medium, nullptr, &numLines); return numLines * 10 + WH; } diff --git a/src/openrct2-ui/windows/Tooltip.cpp b/src/openrct2-ui/windows/Tooltip.cpp index 63b5d98991..d8a25fb131 100644 --- a/src/openrct2-ui/windows/Tooltip.cpp +++ b/src/openrct2-ui/windows/Tooltip.cpp @@ -32,7 +32,7 @@ static Widget window_tooltip_widgets[] = { class TooltipWindow final : public Window { private: - utf8 _tooltipText[CommonTextBufferSize]{}; + u8string _tooltipText; int16_t _tooltipNumLines = 1; public: @@ -100,25 +100,24 @@ public: // Text left = windowPos.x + ((width + 1) / 2) - 1; top = windowPos.y + 1; - DrawStringCentredRaw(&dpi, { left, top }, _tooltipNumLines, _tooltipText, FontStyle::Small); + DrawStringCentredRaw(&dpi, { left, top }, _tooltipNumLines, _tooltipText.data(), FontStyle::Small); } private: // Returns the width of the new tooltip text int32_t FormatTextForTooltip(const OpenRCT2String& message) { - utf8 tempBuffer[CommonTextBufferSize]; - OpenRCT2::FormatStringLegacy(tempBuffer, sizeof(tempBuffer), message.str, message.args.Data()); + const u8string tempString = FormatStringID(message.str, message.args.Data()); OpenRCT2String formattedMessage{ STR_STRING_TOOLTIP, Formatter() }; - formattedMessage.args.Add(tempBuffer); - OpenRCT2::FormatStringLegacy(_tooltipText, sizeof(_tooltipText), formattedMessage.str, formattedMessage.args.Data()); + formattedMessage.args.Add(tempString.c_str()); + const u8string tooltipTextUnwrapped = FormatStringID(formattedMessage.str, formattedMessage.args.Data()); - auto textWidth = GfxGetStringWidthNewLined(_tooltipText, FontStyle::Small); + auto textWidth = GfxGetStringWidthNewLined(tooltipTextUnwrapped, FontStyle::Small); textWidth = std::min(textWidth, 196); int32_t numLines; - textWidth = GfxWrapString(_tooltipText, textWidth + 1, FontStyle::Small, &numLines); + textWidth = GfxWrapString(tooltipTextUnwrapped, textWidth + 1, FontStyle::Small, &_tooltipText, &numLines); _tooltipNumLines = numLines; return textWidth; } diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index 146b745f7f..df491c909c 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -48,8 +48,7 @@ static int32_t TTFGetStringWidth(std::string_view text, FontStyle fontStyle, boo */ int32_t GfxGetStringWidthNewLined(std::string_view text, FontStyle fontStyle) { - thread_local std::string buffer; - buffer.clear(); + u8string buffer; std::optional maxWidth; FmtString fmt(text); @@ -174,11 +173,10 @@ int32_t GfxClipString(utf8* text, int32_t width, FontStyle fontStyle) * num_lines (edi) - out * font_height (ebx) - out */ -int32_t GfxWrapString(utf8* text, int32_t width, FontStyle fontStyle, int32_t* outNumLines) +int32_t GfxWrapString(u8string_view text, int32_t width, FontStyle fontStyle, u8string* outWrappedText, int32_t* outNumLines) { constexpr size_t NULL_INDEX = std::numeric_limits::max(); - thread_local std::string buffer; - buffer.resize(0); + u8string buffer; size_t currentLineIndex = 0; size_t splitIndex = NULL_INDEX; @@ -186,7 +184,7 @@ int32_t GfxWrapString(utf8* text, int32_t width, FontStyle fontStyle, int32_t* o size_t numLines = 0; int32_t maxWidth = 0; - FmtString fmt = text; + FmtString fmt(text); for (const auto& token : fmt) { if (token.IsLiteral()) @@ -261,8 +259,14 @@ int32_t GfxWrapString(utf8* text, int32_t width, FontStyle fontStyle, int32_t* o maxWidth = std::max(maxWidth, lineWidth); } - std::memcpy(text, buffer.data(), buffer.size() + 1); - *outNumLines = static_cast(numLines); + if (outWrappedText != nullptr) + { + *outWrappedText = std::move(buffer); + } + if (outNumLines != nullptr) + { + *outNumLines = static_cast(numLines); + } return maxWidth; } @@ -333,7 +337,8 @@ static void ColourCharacterWindow(uint8_t colour, const uint16_t* current_font_f * text : esi * dpi : edi */ -void DrawStringCentredRaw(DrawPixelInfo* dpi, const ScreenCoordsXY& coords, int32_t numLines, char* text, FontStyle fontStyle) +void DrawStringCentredRaw( + DrawPixelInfo* dpi, const ScreenCoordsXY& coords, int32_t numLines, const utf8* text, FontStyle fontStyle) { ScreenCoordsXY screenCoords(dpi->x, dpi->y); GfxDrawString(dpi, screenCoords, "", { COLOUR_BLACK, fontStyle }); @@ -428,22 +433,22 @@ int32_t StringGetHeightRaw(std::string_view text, FontStyle fontStyle) * ticks : ebp >> 16 */ void DrawNewsTicker( - DrawPixelInfo* dpi, const ScreenCoordsXY& coords, int32_t width, colour_t colour, StringId format, void* args, + DrawPixelInfo* dpi, const ScreenCoordsXY& coords, int32_t width, colour_t colour, StringId format, u8string_view args, int32_t ticks) { int32_t numLines, lineHeight, lineY; - utf8* buffer = gCommonStringFormatBuffer; ScreenCoordsXY screenCoords(dpi->x, dpi->y); GfxDrawString(dpi, screenCoords, "", { colour }); - FormatStringLegacy(buffer, 256, format, args); - GfxWrapString(buffer, width, FontStyle::Small, &numLines); + u8string wrappedString; + GfxWrapString(FormatStringID(format, args), width, FontStyle::Small, &wrappedString, &numLines); lineHeight = FontGetLineHeight(FontStyle::Small); int32_t numCharactersDrawn = 0; int32_t numCharactersToDraw = ticks; + const utf8* buffer = wrappedString.data(); lineY = coords.y - ((numLines * lineHeight) / 2); for (int32_t line = 0; line <= numLines; line++) { diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index c07e458a49..511b914684 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -524,15 +524,16 @@ void GfxDrawString(DrawPixelInfo* dpi, const ScreenCoordsXY& coords, const_utf8s void GfxDrawStringNoFormatting(DrawPixelInfo* dpi, const ScreenCoordsXY& coords, const_utf8string buffer, TextPaint textPaint); void GfxDrawStringLeftCentred(DrawPixelInfo* dpi, StringId format, void* args, colour_t colour, const ScreenCoordsXY& coords); -void DrawStringCentredRaw(DrawPixelInfo* dpi, const ScreenCoordsXY& coords, int32_t numLines, char* text, FontStyle fontStyle); +void DrawStringCentredRaw( + DrawPixelInfo* dpi, const ScreenCoordsXY& coords, int32_t numLines, const utf8* text, FontStyle fontStyle); void DrawNewsTicker( - DrawPixelInfo* dpi, const ScreenCoordsXY& coords, int32_t width, colour_t colour, StringId format, void* args, + DrawPixelInfo* dpi, const ScreenCoordsXY& coords, int32_t width, colour_t colour, StringId format, u8string_view args, int32_t ticks); void GfxDrawStringWithYOffsets( DrawPixelInfo* dpi, const utf8* text, int32_t colour, const ScreenCoordsXY& coords, const int8_t* yOffsets, bool forceSpriteFont, FontStyle fontStyle); -int32_t GfxWrapString(char* buffer, int32_t width, FontStyle fontStyle, int32_t* num_lines); +int32_t GfxWrapString(u8string_view text, int32_t width, FontStyle fontStyle, u8string* outWrappedText, int32_t* outNumLines); int32_t GfxGetStringWidth(std::string_view text, FontStyle fontStyle); int32_t GfxGetStringWidthNewLined(std::string_view text, FontStyle fontStyle); int32_t GfxGetStringWidthNoFormatting(std::string_view text, FontStyle fontStyle); diff --git a/src/openrct2/drawing/Text.cpp b/src/openrct2/drawing/Text.cpp index 881f4fe1fd..8493279f52 100644 --- a/src/openrct2/drawing/Text.cpp +++ b/src/openrct2/drawing/Text.cpp @@ -22,19 +22,17 @@ static void DrawText( class StaticLayout { private: - utf8string Buffer; + u8string Buffer; TextPaint Paint; int32_t LineCount = 0; int32_t LineHeight; int32_t MaxWidth; public: - StaticLayout(utf8string source, const TextPaint& paint, int32_t width) + StaticLayout(u8string_view source, const TextPaint& paint, int32_t width) + : Paint(paint) { - Buffer = source; - Paint = paint; - - MaxWidth = GfxWrapString(Buffer, width, paint.FontStyle, &LineCount); + MaxWidth = GfxWrapString(source, width, paint.FontStyle, &Buffer, &LineCount); LineCount += 1; LineHeight = FontGetLineHeight(paint.FontStyle); } @@ -55,7 +53,7 @@ public: lineCoords.x += MaxWidth; break; } - utf8* buffer = Buffer; + const utf8* buffer = Buffer.data(); for (int32_t line = 0; line < LineCount; ++line) { DrawText(dpi, lineCoords, tempPaint, buffer); @@ -175,9 +173,7 @@ int32_t DrawTextWrapped( { const void* args = ft.Data(); - // TODO: Refactor StaticLayout to take a std::string_view instead. It shouldn't have to write to the buffer. - const std::string buffer = FormatStringID(format, args); - StaticLayout layout(const_cast(buffer.c_str()), textPaint, width); + StaticLayout layout(FormatStringID(format, args), textPaint, width); if (textPaint.Alignment == TextAlignment::CENTRE) { diff --git a/src/openrct2/interface/Chat.cpp b/src/openrct2/interface/Chat.cpp index 569a8b1570..79542ac142 100644 --- a/src/openrct2/interface/Chat.cpp +++ b/src/openrct2/interface/Chat.cpp @@ -109,7 +109,7 @@ void ChatDraw(DrawPixelInfo* dpi, uint8_t chatBackgroundColor) // Draw chat window if (gChatOpen) { - inputLineHeight = ChatStringWrappedGetHeight(static_cast(&inputLine), _chatWidth - 10); + inputLineHeight = ChatStringWrappedGetHeight(inputLine, _chatWidth - 10); _chatTop -= inputLineHeight; for (int32_t i = 0; i < CHAT_HISTORY_SIZE; i++) @@ -120,8 +120,7 @@ void ChatDraw(DrawPixelInfo* dpi, uint8_t chatBackgroundColor) } lineBuffer.assign(ChatGetHistory(i)); - auto lineCh = lineBuffer.c_str(); - int32_t lineHeight = ChatStringWrappedGetHeight(static_cast(&lineCh), _chatWidth - 10); + int32_t lineHeight = ChatStringWrappedGetHeight(lineBuffer, _chatWidth - 10); _chatTop -= (lineHeight + 5); } @@ -272,12 +271,9 @@ static void ChatClearInput() // But this adjusts the initial Y coordinate depending of the number of lines. static int32_t ChatHistoryDrawString(DrawPixelInfo* dpi, const char* text, const ScreenCoordsXY& screenCoords, int32_t width) { - char buffer[CommonTextBufferSize]; - auto bufferPtr = buffer; - FormatStringToBuffer(buffer, sizeof(buffer), "{OUTLINE}{WHITE}{STRING}", text); - int32_t numLines; - GfxWrapString(bufferPtr, width, FontStyle::Medium, &numLines); + u8string wrappedString; + GfxWrapString(FormatString("{OUTLINE}{WHITE}{STRING}", text), width, FontStyle::Medium, &wrappedString, &numLines); auto lineHeight = FontGetLineHeight(FontStyle::Medium); int32_t expectedY = screenCoords.y - (numLines * lineHeight); @@ -286,7 +282,8 @@ static int32_t ChatHistoryDrawString(DrawPixelInfo* dpi, const char* text, const return (numLines * lineHeight); // Skip drawing, return total height. } - auto lineY = screenCoords.y; + const utf8* bufferPtr = wrappedString.data(); + int32_t lineY = screenCoords.y; for (int32_t line = 0; line <= numLines; ++line) { GfxDrawString(dpi, { screenCoords.x, lineY - (numLines * lineHeight) }, bufferPtr, { TEXT_COLOUR_254 }); @@ -298,22 +295,10 @@ static int32_t ChatHistoryDrawString(DrawPixelInfo* dpi, const char* text, const // Wrap string without drawing, useful to get the height of a wrapped string. // Almost the same as gfx_draw_string_left_wrapped -int32_t ChatStringWrappedGetHeight(void* args, int32_t width) +int32_t ChatStringWrappedGetHeight(u8string_view args, int32_t width) { - char buffer[CommonTextBufferSize]; - auto bufferPtr = buffer; - FormatStringLegacy(bufferPtr, 256, STR_STRING, args); - int32_t numLines; - GfxWrapString(bufferPtr, width, FontStyle::Medium, &numLines); - int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - - int32_t lineY = 0; - for (int32_t line = 0; line <= numLines; ++line) - { - bufferPtr = GetStringEnd(bufferPtr) + 1; - lineY += lineHeight; - } - - return lineY; + GfxWrapString(FormatStringID(STR_STRING, args), width, FontStyle::Medium, nullptr, &numLines); + const int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); + return lineHeight * numLines; } diff --git a/src/openrct2/interface/Chat.h b/src/openrct2/interface/Chat.h index 2b9da9b97e..54ba3a6a25 100644 --- a/src/openrct2/interface/Chat.h +++ b/src/openrct2/interface/Chat.h @@ -10,6 +10,7 @@ #pragma once #include "../common.h" +#include "../core/String.hpp" #include @@ -42,4 +43,4 @@ void ChatDraw(DrawPixelInfo* dpi, uint8_t chatBackgroundColour); void ChatAddHistory(std::string_view s); void ChatInput(ChatInput input); -int32_t ChatStringWrappedGetHeight(void* args, int32_t width); +int32_t ChatStringWrappedGetHeight(u8string_view args, int32_t width);