From 699103fe869bd5e30c7b97faa85b00cae8a639be Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 28 Nov 2020 16:52:19 +0000 Subject: [PATCH] Fix banner text formatting, import / export --- src/openrct2/core/String.cpp | 7 +++++ src/openrct2/core/String.hpp | 1 + src/openrct2/rct1/S4Importer.cpp | 3 ++- src/openrct2/rct12/RCT12.cpp | 44 +++++++++++++++++++++++++++----- src/openrct2/rct12/RCT12.h | 1 + src/openrct2/rct2/S6Exporter.cpp | 7 ++--- src/openrct2/rct2/S6Importer.cpp | 3 ++- src/openrct2/world/Banner.cpp | 9 ++++++- 8 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/openrct2/core/String.cpp b/src/openrct2/core/String.cpp index 08349e67cf..1ac4e7aa09 100644 --- a/src/openrct2/core/String.cpp +++ b/src/openrct2/core/String.cpp @@ -509,6 +509,13 @@ namespace String return utf8_write_codepoint(dst, codepoint); } + void AppendCodepoint(std::string& str, codepoint_t codepoint) + { + char buffer[8]{}; + utf8_write_codepoint(buffer, codepoint); + str.append(buffer); + } + bool IsWhiteSpace(codepoint_t codepoint) { // 0x3000 is the 'ideographic space', a 'fullwidth' character used in CJK languages. diff --git a/src/openrct2/core/String.hpp b/src/openrct2/core/String.hpp index ebb4643c41..62fbadb9a6 100644 --- a/src/openrct2/core/String.hpp +++ b/src/openrct2/core/String.hpp @@ -94,6 +94,7 @@ namespace String codepoint_t GetNextCodepoint(utf8* ptr, utf8** nextPtr = nullptr); codepoint_t GetNextCodepoint(const utf8* ptr, const utf8** nextPtr = nullptr); utf8* WriteCodepoint(utf8* dst, codepoint_t codepoint); + void AppendCodepoint(std::string& str, codepoint_t codepoint); bool IsWhiteSpace(codepoint_t codepoint); utf8* Trim(utf8* str); diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index d58f5dcfd4..2fb3a72d84 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -3006,7 +3006,8 @@ 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); - return asUtf8.data(); + auto justText = RCT12RemoveFormattingUTF8(asUtf8); + return justText.data(); } void FixLandOwnership() diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp index 1d6f4aafd7..c72f8be87a 100644 --- a/src/openrct2/rct12/RCT12.cpp +++ b/src/openrct2/rct12/RCT12.cpp @@ -9,6 +9,7 @@ #include "RCT12.h" +#include "../core/String.hpp" #include "../localisation/Localisation.h" #include "../ride/Track.h" #include "../world/Banner.h" @@ -1041,6 +1042,24 @@ RCT12RideId OpenRCT2RideIdToRCT12RideId(const ride_id_t rideId) return rideId; } +static bool RCT12IsFormatChar(codepoint_t c) +{ + if (c >= RCT2_STRING_FORMAT_ARG_START && c <= RCT2_STRING_FORMAT_ARG_END) + { + return true; + } + if (c >= RCT2_STRING_FORMAT_COLOUR_START && c <= RCT2_STRING_FORMAT_COLOUR_END) + { + return true; + } + return false; +} + +static bool RCT12IsFormatChar(char c) +{ + return RCT12IsFormatChar(static_cast(c)); +} + bool IsLikelyUTF8(std::string_view s) { // RCT2 uses CP-1252 so some characters may be >= 128. However we don't expect any @@ -1048,15 +1067,28 @@ bool IsLikelyUTF8(std::string_view s) // that the string is UTF-8. for (auto c : s) { - auto chr = static_cast(c); - if (chr >= RCT2_STRING_FORMAT_ARG_START && chr <= RCT2_STRING_FORMAT_ARG_END) - { - return true; - } - if (chr >= RCT2_STRING_FORMAT_COLOUR_START && chr <= RCT2_STRING_FORMAT_COLOUR_END) + if (RCT12IsFormatChar(c)) { return true; } } return false; } + +std::string RCT12RemoveFormattingUTF8(std::string_view s) +{ + std::string result; + result.reserve(s.size() * 2); + + CodepointView codepoints(s); + for (auto codepoint : codepoints) + { + if (!RCT12IsFormatChar(codepoint)) + { + String::AppendCodepoint(result, codepoint); + } + } + + result.shrink_to_fit(); + return result; +} diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index 6047ea6312..a4faf68c3f 100644 --- a/src/openrct2/rct12/RCT12.h +++ b/src/openrct2/rct12/RCT12.h @@ -868,3 +868,4 @@ RCT12ObjectEntryIndex OpenRCT2EntryIndexToRCTEntryIndex(const ObjectEntryIndex i ride_id_t RCT12RideIdToOpenRCT2RideId(const RCT12RideId rideId); RCT12RideId OpenRCT2RideIdToRCT12RideId(const ride_id_t rideId); bool IsLikelyUTF8(std::string_view s); +std::string RCT12RemoveFormattingUTF8(std::string_view s); diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index aae0242794..56391f40f0 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -1367,12 +1367,13 @@ void S6Exporter::ExportBanner(RCT12Banner& dst, const Banner& src) dst.string_idx = STR_DEFAULT_SIGN; - auto bannerText = src.text; + std::string bannerText; if (!(src.flags & BANNER_FLAG_IS_WALL) && !(src.flags & BANNER_FLAG_IS_LARGE_SCENERY)) { - auto formatCode = static_cast(RCT2_STRING_FORMAT_COLOUR_START + src.text_colour); - bannerText.insert(bannerText.begin(), formatCode); + auto formatCode = static_cast(RCT2_STRING_FORMAT_COLOUR_START + src.text_colour); + String::AppendCodepoint(bannerText, formatCode); } + bannerText.append(src.text); auto stringId = AllocateUserString(bannerText); if (stringId != std::nullopt) diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 7956bec291..2002d85c70 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -1661,7 +1661,8 @@ 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); - return asUtf8.data(); + auto justText = RCT12RemoveFormattingUTF8(asUtf8); + return justText.data(); } std::vector GetRequiredObjects() diff --git a/src/openrct2/world/Banner.cpp b/src/openrct2/world/Banner.cpp index 554dd43549..008ff53a63 100644 --- a/src/openrct2/world/Banner.cpp +++ b/src/openrct2/world/Banner.cpp @@ -46,8 +46,15 @@ void Banner::FormatTextTo(Formatter& ft, bool addColour) const { auto formatToken = FormatTokenFromTextColour(text_colour); auto tokenText = FormatTokenToString(formatToken); + + thread_local std::string tokenTextColourBuffer; + tokenTextColourBuffer.clear(); + tokenTextColourBuffer.push_back('{'); + tokenTextColourBuffer.append(tokenText); + tokenTextColourBuffer.push_back('}'); + ft.Add(STR_STRING_STRINGID); - ft.Add(tokenText.data()); + ft.Add(tokenTextColourBuffer.c_str()); } FormatTextTo(ft);