From f539fdd79c5a24bffee08d5014a57c18be94186f Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 22 Jul 2019 22:01:49 +0100 Subject: [PATCH] Convert user strings to RCT2 encoding when exporting to S6 --- src/openrct2/rct1/S4Importer.cpp | 5 ++-- src/openrct2/rct2/S6Exporter.cpp | 41 ++++++++++++++++++++++++++------ src/openrct2/rct2/S6Importer.cpp | 5 ++-- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index f90ae26fdf..7f00ce6acd 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -2904,8 +2904,9 @@ private: std::string GetUserString(rct_string_id stringId) { - const char* originalString = _s4.string_table[(stringId - USER_STRING_START) % 1024]; - return rct2_to_utf8(originalString, RCT2_LANGUAGE_ID_ENGLISH_UK); + const auto originalString = _s4.string_table[(stringId - USER_STRING_START) % 1024]; + std::string_view originalStringView(originalString, USER_STRING_MAX_LENGTH); + return rct2_to_utf8(originalStringView, RCT2_LANGUAGE_ID_ENGLISH_UK); } void FixLandOwnership() diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index ba80a874f9..6b2ab83280 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -1278,6 +1278,35 @@ opt::optional S6Exporter::AllocateUserString(const std::string_view& v return {}; } +static std::string GetTruncatedRCT2String(const std::string_view& src) +{ + auto rct2encoded = utf8_to_rct2(src); + if (rct2encoded.size() > RCT12_USER_STRING_MAX_LENGTH - 1) + { + log_warning("The user string '%s' is too long for the S6 file format and has therefore been truncated.", std::string(src).c_str()); + + rct2encoded.resize(RCT12_USER_STRING_MAX_LENGTH - 1); + for (size_t i = 0; i < rct2encoded.size(); i++) + { + if (rct2encoded[i] == (char)(uint8_t)0xFF) + { + if (i > RCT12_USER_STRING_MAX_LENGTH - 4) + { + // This codepoint was truncated, remove codepoint altogether + rct2encoded.resize(i); + break; + } + else + { + // Skip the next two bytes which represent the unicode character + i += 2; + } + } + } + } + return rct2encoded; +} + void S6Exporter::ExportUserStrings() { auto numUserStrings = std::min(_userStrings.size(), RCT12_MAX_USER_STRINGS); @@ -1285,12 +1314,9 @@ void S6Exporter::ExportUserStrings() { auto dst = _s6.custom_strings[i]; const auto& src = _userStrings[i]; - auto stringLen = std::min(src.size(), RCT12_USER_STRING_MAX_LENGTH - 1); - std::memcpy(dst, src.data(), stringLen); - if (stringLen < src.size()) - { - log_warning("A user string '%s' was truncated to the S6 user string length limit.", src.c_str()); - } + auto encodedSrc = GetTruncatedRCT2String(src); + auto stringLen = std::min(encodedSrc.size(), RCT12_USER_STRING_MAX_LENGTH - 1); + std::memcpy(dst, encodedSrc.data(), stringLen); } } @@ -1346,8 +1372,9 @@ int32_t scenario_save(const utf8* path, int32_t flags) } result = true; } - catch (const std::exception&) + catch (const std::exception& e) { + log_error("Unable to save park: '%s'", e.what()); } delete s6exporter; diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 57ebe48ce6..906349d2a5 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -1522,8 +1522,9 @@ public: std::string GetUserString(rct_string_id stringId) { - const char* originalString = _s6.custom_strings[(stringId - USER_STRING_START) % 1024]; - return rct2_to_utf8(originalString, RCT2_LANGUAGE_ID_ENGLISH_UK); + const auto originalString = _s6.custom_strings[(stringId - USER_STRING_START) % 1024]; + std::string_view originalStringView(originalString, USER_STRING_MAX_LENGTH); + return rct2_to_utf8(originalStringView, RCT2_LANGUAGE_ID_ENGLISH_UK); } };