1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-06 06:32:56 +01:00

Fix #13842: News is imported incorrectly

This commit is contained in:
Ted John
2021-01-20 11:35:11 +00:00
committed by GitHub
parent 9d666700f9
commit 19f0d8dfe9
14 changed files with 206 additions and 71 deletions

View File

@@ -562,7 +562,7 @@ static void window_game_bottom_toolbar_draw_news_item(rct_drawpixelinfo* dpi, rc
INSET_RECT_F_30);
// Text
utf8* newsItemText = newsItem->Text;
const auto* newsItemText = newsItem->Text.c_str();
auto screenCoords = w->windowPos + ScreenCoordsXY{ middleOutsetWidget->midX(), middleOutsetWidget->top + 11 };
width = middleOutsetWidget->width() - 62;
gfx_draw_string_centred_wrapped_partial(

View File

@@ -248,7 +248,7 @@ static void window_news_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32
// Item text
{
auto ft = Formatter();
ft.Add<utf8*>(newsItem.Text);
ft.Add<const char*>(newsItem.Text.c_str());
gfx_draw_string_left_wrapped(
dpi, ft.Data(), { 2, y + lineHeight }, 325, STR_BOTTOM_TOOLBAR_NEWS_TEXT, COLOUR_BRIGHT_GREEN);
}

View File

@@ -380,22 +380,6 @@ void game_convert_strings_to_utf8()
gScenarioCompletedBy = rct2_to_utf8(gScenarioCompletedBy, RCT2LanguageId::EnglishUK);
gScenarioName = rct2_to_utf8(gScenarioName, RCT2LanguageId::EnglishUK);
gScenarioDetails = rct2_to_utf8(gScenarioDetails, RCT2LanguageId::EnglishUK);
// News items
game_convert_news_items_to_utf8();
}
void game_convert_news_items_to_utf8()
{
for (int32_t i = 0; i < News::MaxItems; i++)
{
News::Item* newsItem = News::GetItem(i);
if (!str_is_null_or_empty(newsItem->Text))
{
rct2_to_utf8_self(newsItem->Text, sizeof(newsItem->Text));
}
}
}
/**
@@ -416,15 +400,6 @@ void game_convert_strings_to_rct2(rct_s6_data* s6)
utf8_to_rct2_self(userString, RCT12_USER_STRING_MAX_LENGTH);
}
}
// News items
for (auto& newsItem : s6->news_items)
{
if (!str_is_null_or_empty(newsItem.Text))
{
utf8_to_rct2_self(newsItem.Text, sizeof(newsItem.Text));
}
}
}
// OpenRCT2 workaround to recalculate some values which are saved redundantly in the save to fix corrupted files.

View File

@@ -173,7 +173,6 @@ void save_game_cmd(const utf8* name = nullptr);
void save_game_with_name(const utf8* name);
void game_autosave();
void game_convert_strings_to_utf8();
void game_convert_news_items_to_utf8();
void game_convert_strings_to_rct2(rct_s6_data* s6);
void utf8_to_rct2_self(char* buffer, size_t length);
void rct2_to_utf8_self(char* buffer, size_t length);

View File

@@ -65,7 +65,7 @@ static const std::unordered_map<std::string_view, FormatToken> FormatTokenMap =
};
// clang-format on
static std::string_view GetFormatTokenStringWithBraces(FormatToken token)
std::string_view GetFormatTokenStringWithBraces(FormatToken token)
{
// Ensure cache is thread safe
static std::mutex mutex;

View File

@@ -76,6 +76,7 @@ enum class FormatToken
OutlineDisable,
};
std::string_view GetFormatTokenStringWithBraces(FormatToken token);
FormatToken FormatTokenFromString(std::string_view token);
std::string_view FormatTokenToString(FormatToken token, bool withBraces = false);
bool FormatTokenTakesArgument(FormatToken token);

View File

@@ -319,7 +319,7 @@ News::Item* News::AddItemToQueue(News::ItemType type, const utf8* text, uint32_t
newsItem->Ticks = 0;
newsItem->MonthYear = static_cast<uint16_t>(gDateMonthsElapsed);
newsItem->Day = ((days_in_month[date_get_month(newsItem->MonthYear)] * gDateMonthTicks) >> 16) + 1;
safe_strcpy(newsItem->Text, text, sizeof(newsItem->Text));
newsItem->Text = text;
return newsItem;
}

View File

@@ -15,6 +15,7 @@
#include <array>
#include <iterator>
#include <optional>
#include <string>
struct CoordsXYZ;
class Formatter;
@@ -60,7 +61,7 @@ namespace News
uint16_t Ticks;
uint16_t MonthYear;
uint8_t Day;
utf8 Text[256];
std::string Text;
constexpr bool IsEmpty() const noexcept
{

View File

@@ -208,7 +208,6 @@ public:
SetDefaultNames();
determine_ride_entrance_and_exit_locations();
game_convert_news_items_to_utf8();
map_count_remaining_land_rights();
research_determine_first_of_type();
}
@@ -2613,7 +2612,7 @@ private:
dst->Ticks = src->Ticks;
dst->MonthYear = src->MonthYear;
dst->Day = src->Day;
std::copy(std::begin(src->Text), std::end(src->Text), dst->Text);
dst->Text = ConvertFormattedStringToOpenRCT2(std::string_view(src->Text, sizeof(src->Text)));
if (dst->Type == News::ItemType::Research)
{

View File

@@ -10,6 +10,7 @@
#include "RCT12.h"
#include "../core/String.hpp"
#include "../localisation/Formatting.h"
#include "../localisation/Localisation.h"
#include "../ride/Track.h"
#include "../world/Banner.h"
@@ -20,6 +21,8 @@
#include "../world/TileElement.h"
#include "../world/Wall.h"
using namespace OpenRCT2;
uint8_t RCT12TileElementBase::GetType() const
{
return this->type & TILE_ELEMENT_TYPE_MASK;
@@ -1101,3 +1104,188 @@ std::string RCT12RemoveFormattingUTF8(std::string_view s)
result.shrink_to_fit();
return result;
}
namespace RCT12FormatCode
{
constexpr codepoint_t Newline = 5;
constexpr codepoint_t NewlineSmall = 6;
constexpr codepoint_t ColourBlack = 142;
constexpr codepoint_t ColourGrey = 143;
constexpr codepoint_t ColourWhite = 144;
constexpr codepoint_t ColourRed = 145;
constexpr codepoint_t ColourGreen = 146;
constexpr codepoint_t ColourYellow = 147;
constexpr codepoint_t ColourTopaz = 148;
constexpr codepoint_t ColourCeladon = 149;
constexpr codepoint_t ColourBabyBlue = 150;
constexpr codepoint_t ColourPaleLavender = 151;
constexpr codepoint_t ColourPaleGold = 152;
constexpr codepoint_t ColourLightPink = 153;
constexpr codepoint_t ColourPearlAqua = 154;
constexpr codepoint_t ColourPaleSilver = 155;
} // namespace RCT12FormatCode
static FormatToken GetFormatTokenFromRCT12Code(codepoint_t codepoint)
{
switch (codepoint)
{
case RCT12FormatCode::Newline:
return FormatToken::Newline;
case RCT12FormatCode::NewlineSmall:
return FormatToken::NewlineSmall;
case RCT12FormatCode::ColourBlack:
return FormatToken::ColourBlack;
case RCT12FormatCode::ColourGrey:
return FormatToken::ColourGrey;
case RCT12FormatCode::ColourWhite:
return FormatToken::ColourWhite;
case RCT12FormatCode::ColourRed:
return FormatToken::ColourRed;
case RCT12FormatCode::ColourGreen:
return FormatToken::ColourGreen;
case RCT12FormatCode::ColourYellow:
return FormatToken::ColourYellow;
case RCT12FormatCode::ColourTopaz:
return FormatToken::ColourTopaz;
case RCT12FormatCode::ColourCeladon:
return FormatToken::ColourCeladon;
case RCT12FormatCode::ColourBabyBlue:
return FormatToken::ColourBabyBlue;
case RCT12FormatCode::ColourPaleLavender:
return FormatToken::ColourPaleLavender;
case RCT12FormatCode::ColourPaleGold:
return FormatToken::ColourPaleGold;
case RCT12FormatCode::ColourLightPink:
return FormatToken::ColourLightPink;
case RCT12FormatCode::ColourPearlAqua:
return FormatToken::ColourPearlAqua;
case RCT12FormatCode::ColourPaleSilver:
return FormatToken::ColourPaleSilver;
default:
return FormatToken::Unknown;
}
}
static codepoint_t GetRCT12CodeFromFormatToken(FormatToken token)
{
switch (token)
{
case FormatToken::Newline:
return RCT12FormatCode::Newline;
case FormatToken::NewlineSmall:
return RCT12FormatCode::NewlineSmall;
case FormatToken::ColourBlack:
return RCT12FormatCode::ColourBlack;
case FormatToken::ColourGrey:
return RCT12FormatCode::ColourGrey;
case FormatToken::ColourWhite:
return RCT12FormatCode::ColourWhite;
case FormatToken::ColourRed:
return RCT12FormatCode::ColourRed;
case FormatToken::ColourGreen:
return RCT12FormatCode::ColourGreen;
case FormatToken::ColourYellow:
return RCT12FormatCode::ColourYellow;
case FormatToken::ColourTopaz:
return RCT12FormatCode::ColourTopaz;
case FormatToken::ColourCeladon:
return RCT12FormatCode::ColourCeladon;
case FormatToken::ColourBabyBlue:
return RCT12FormatCode::ColourBabyBlue;
case FormatToken::ColourPaleLavender:
return RCT12FormatCode::ColourPaleLavender;
case FormatToken::ColourPaleGold:
return RCT12FormatCode::ColourPaleGold;
case FormatToken::ColourLightPink:
return RCT12FormatCode::ColourLightPink;
case FormatToken::ColourPearlAqua:
return RCT12FormatCode::ColourPearlAqua;
case FormatToken::ColourPaleSilver:
return RCT12FormatCode::ColourPaleSilver;
default:
return 0;
}
}
std::string ConvertFormattedStringToOpenRCT2(std::string_view buffer)
{
auto nullTerminator = buffer.find('\0');
if (nullTerminator != std::string::npos)
{
buffer = buffer.substr(0, nullTerminator);
}
auto asUtf8 = rct2_to_utf8(buffer, RCT2LanguageId::EnglishUK);
std::string result;
CodepointView codepoints(asUtf8);
for (auto codepoint : codepoints)
{
auto token = GetFormatTokenFromRCT12Code(codepoint);
if (token != FormatToken::Unknown)
{
result += GetFormatTokenStringWithBraces(token);
}
else
{
String::AppendCodepoint(result, codepoint);
}
}
return result;
}
std::string ConvertFormattedStringToRCT2(std::string_view buffer, size_t maxLength)
{
std::string result;
FmtString fmt(buffer);
for (const auto& token : fmt)
{
if (token.IsLiteral())
{
result += token.text;
}
else
{
auto codepoint = GetRCT12CodeFromFormatToken(token.kind);
if (codepoint == 0)
{
result += token.text;
}
else
{
String::AppendCodepoint(result, codepoint);
}
}
}
return GetTruncatedRCT2String(result, maxLength);
}
std::string GetTruncatedRCT2String(std::string_view src, size_t maxLength)
{
auto rct2encoded = utf8_to_rct2(src);
if (rct2encoded.size() > maxLength - 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(maxLength - 1);
for (size_t i = 0; i < rct2encoded.size(); i++)
{
if (rct2encoded[i] == static_cast<char>(static_cast<uint8_t>(0xFF)))
{
if (i > maxLength - 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;
}

View File

@@ -876,3 +876,6 @@ 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);
std::string ConvertFormattedStringToOpenRCT2(std::string_view buffer);
std::string ConvertFormattedStringToRCT2(std::string_view buffer, size_t maxLength);
std::string GetTruncatedRCT2String(std::string_view src, size_t maxLength);

View File

@@ -412,7 +412,9 @@ void S6Exporter::Export()
dst->Ticks = src->Ticks;
dst->MonthYear = src->MonthYear;
dst->Day = src->Day;
std::memcpy(dst->Text, src->Text, sizeof(dst->Text));
auto rct2text = ConvertFormattedStringToRCT2(src->Text, sizeof(dst->Text));
std::memcpy(dst->Text, rct2text.c_str(), std::min(sizeof(dst->Text), rct2text.size()));
}
// pad_13CE730
@@ -1646,37 +1648,6 @@ std::optional<uint16_t> S6Exporter::AllocateUserString(std::string_view value)
return std::nullopt;
}
static std::string GetTruncatedRCT2String(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] == static_cast<char>(static_cast<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<size_t>(_userStrings.size(), RCT12_MAX_USER_STRINGS);
@@ -1684,7 +1655,7 @@ void S6Exporter::ExportUserStrings()
{
auto dst = _s6.custom_strings[i];
const auto& src = _userStrings[i];
auto encodedSrc = GetTruncatedRCT2String(src);
auto encodedSrc = GetTruncatedRCT2String(src, RCT12_USER_STRING_MAX_LENGTH);
auto stringLen = std::min<size_t>(encodedSrc.size(), RCT12_USER_STRING_MAX_LENGTH - 1);
std::memcpy(dst, encodedSrc.data(), stringLen);
}

View File

@@ -433,7 +433,7 @@ public:
dst->Ticks = src->Ticks;
dst->MonthYear = src->MonthYear;
dst->Day = src->Day;
std::memcpy(dst->Text, src->Text, sizeof(src->Text));
dst->Text = ConvertFormattedStringToOpenRCT2(std::string_view(src->Text, sizeof(src->Text)));
}
else
{

View File

@@ -63,9 +63,7 @@ namespace OpenRCT2::Scripting
result.Ticks = value["tickCount"].as_int();
result.MonthYear = value["month"].as_int();
result.Day = value["day"].as_int();
auto text = value["text"].as_string();
String::Set(result.Text, sizeof(result.Text), text.c_str());
result.Text = value["text"].as_string();
return result;
}
@@ -219,7 +217,7 @@ namespace OpenRCT2::Scripting
auto msg = GetMessage();
if (msg != nullptr)
{
String::Set(msg->Text, sizeof(msg->Text), value.c_str());
msg->Text = value;
}
}