Refactor format tokens

This commit is contained in:
Ted John 2020-10-16 00:13:52 +01:00
parent 157c984d4c
commit b6a688e540
38 changed files with 445 additions and 1749 deletions

View File

@ -89,7 +89,6 @@ void TextComposition::HandleMessage(const SDL_Event* e)
}
utf8* newText = String::Duplicate(e->text.text);
utf8_remove_formatting(newText, false);
Insert(newText);
Memory::Free(newText);
@ -186,7 +185,6 @@ void TextComposition::HandleMessage(const SDL_Event* e)
if ((modifier & KEYBOARD_PRIMARY_MODIFIER) && SDL_HasClipboardText())
{
utf8* text = SDL_GetClipboardText();
utf8_remove_formatting(text, false);
Insert(text);
SDL_free(text);
window_update_textbox();

View File

@ -199,13 +199,13 @@ void InGameConsole::Toggle()
}
}
void InGameConsole::WriteLine(const std::string& input, uint32_t colourFormat)
void InGameConsole::WriteLine(const std::string& input, FormatToken colourFormat)
{
// Include text colour format only for special cases
// The draw function handles the default text colour differently
utf8 colourCodepoint[4]{};
if (colourFormat != FORMAT_WINDOW_COLOUR_2)
utf8_write_codepoint(colourCodepoint, colourFormat);
auto colourCodepoint = "";
if (colourFormat != FormatToken::ColourWindow2)
colourCodepoint = "{";
std::string line;
std::size_t splitPos = 0;
@ -254,9 +254,6 @@ void InGameConsole::Update()
}
}
}
// Remove unwanted characters in console input
utf8_remove_format_codes(_consoleCurrentLine, false);
}
// Flash the caret

View File

@ -10,6 +10,7 @@
#pragma once
#include <openrct2/interface/InteractiveConsole.h>
#include <openrct2/localisation/FormatCodes.h>
#include <openrct2/world/Location.hpp>
namespace OpenRCT2::Ui
@ -52,7 +53,7 @@ namespace OpenRCT2::Ui
void Close() override;
void Hide() override;
void Toggle();
void WriteLine(const std::string& s, uint32_t colourFormat) override;
void WriteLine(const std::string& s, FormatToken colourFormat) override;
void Input(ConsoleInput input);
void RefreshCaret(size_t position = 0);

View File

@ -270,7 +270,7 @@ namespace OpenRCT2::Ui::Windows
result.MaxWidth = GetOptionalInt(desc["maxWidth"]);
result.MinHeight = GetOptionalInt(desc["minHeight"]);
result.MaxHeight = GetOptionalInt(desc["maxHeight"]);
result.Title = language_convert_string(desc["title"].as_string());
result.Title = desc["title"].as_string();
result.Id = GetOptionalInt(desc["id"]);
result.TabIndex = GetOptionalInt(desc["tabIndex"]);
@ -1077,7 +1077,7 @@ namespace OpenRCT2::Ui::Windows
auto customWidgetInfo = customInfo.GetCustomWidgetDesc(w, widgetIndex);
if (customWidgetInfo != nullptr)
{
customWidgetInfo->Text = language_convert_string(value);
customWidgetInfo->Text = value;
w->widgets[widgetIndex].string = customWidgetInfo->Text.data();
widget_invalidate(w, widgetIndex);
}

View File

@ -314,7 +314,7 @@ namespace OpenRCT2::Scripting
auto widget = GetWidget();
if (widget != nullptr && (widget->flags & WIDGET_FLAGS::TEXT_IS_STRING) && widget->string != nullptr)
{
return language_convert_string_to_tokens(widget->string);
return widget->string;
}
}
return "";

View File

@ -243,7 +243,7 @@ namespace OpenRCT2::Scripting
auto w = GetWindow();
if (w != nullptr && w->classification == WC_CUSTOM)
{
return language_convert_string_to_tokens(GetWindowTitle(w));
return GetWindowTitle(w);
}
return {};
}
@ -252,7 +252,7 @@ namespace OpenRCT2::Scripting
auto w = GetWindow();
if (w != nullptr && w->classification == WC_CUSTOM)
{
UpdateWindowTitle(w, language_convert_string(value));
UpdateWindowTitle(w, value);
}
}

View File

@ -236,13 +236,6 @@ static void window_changelog_process_changelog_text(const std::string& text)
while ((pos = text.find("\n", prev)) != std::string::npos)
{
std::string line = text.substr(prev, pos - prev);
for (char* ch = line.data(); *ch != '\0'; ch++)
{
if (utf8_is_format_code(*ch))
{
*ch = FORMAT_OUTLINE_OFF;
}
}
_changelogLines.push_back(line);
prev = pos + 1;
}

View File

@ -329,13 +329,12 @@ void window_player_overview_paint(rct_window* w, rct_drawpixelinfo* dpi)
if (groupindex != -1)
{
rct_widget* widget = &window_player_overview_widgets[WIDX_GROUP];
char buffer[300];
char* lineCh;
lineCh = buffer;
lineCh = utf8_write_codepoint(lineCh, FORMAT_WINDOW_COLOUR_2);
safe_strcpy(lineCh, network_get_group_name(groupindex), sizeof(buffer) - (lineCh - buffer));
thread_local std::string buffer;
buffer.assign("{WINDOW_COLOUR_2}");
buffer += network_get_group_name(groupindex);
auto ft = Formatter();
ft.Add<const char*>(buffer);
ft.Add<const char*>(buffer.c_str());
DrawTextEllipsised(
dpi, w->windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft,

View File

@ -85,7 +85,6 @@ void window_text_input_open(
if (existing_text != STR_NONE)
format_string(buffer, maxLength, existing_text, &existing_args);
utf8_remove_format_codes(buffer, false);
window_text_input_raw_open(call_w, call_widget, title, description, buffer, maxLength);
}

View File

@ -771,18 +771,6 @@ namespace String
return res;
#endif
}
bool ContainsColourCode(const std::string& string)
{
for (unsigned char c : string)
{
if (c >= FORMAT_COLOUR_CODE_START && c <= FORMAT_COLOUR_CODE_END)
{
return true;
}
}
return false;
}
} // namespace String
char32_t CodepointView::iterator::GetNextCodepoint(const char* ch, const char** next)

View File

@ -111,12 +111,6 @@ namespace String
* Returns an uppercased version of a UTF-8 string.
*/
std::string ToUpper(const std::string_view& src);
/**
* Returns true if the string contains an RCT2 colour code.
*/
bool ContainsColourCode(const std::string& string);
} // namespace String
class CodepointView

View File

@ -50,7 +50,7 @@ int32_t gfx_get_string_width_new_lined(std::string_view text)
FmtString fmt(text);
for (const auto& token : fmt)
{
if (token.kind == FORMAT_NEWLINE || token.kind == FORMAT_NEWLINE_SMALLER)
if (token.kind == FormatToken::Newline || token.kind == FormatToken::NewlineSmall)
{
auto width = gfx_get_string_width(buffer);
if (!maxWidth || maxWidth > width)
@ -222,7 +222,7 @@ int32_t gfx_wrap_string(utf8* text, int32_t width, int32_t* outNumLines, int32_t
}
}
}
else if (token.kind == FORMAT_NEWLINE)
else if (token.kind == FormatToken::Newline)
{
buffer.push_back('\0');
@ -352,13 +352,12 @@ int32_t string_get_height_raw(char* buffer)
else if (fontBase == FONT_SPRITE_BASE_TINY)
height += 6;
char* ch = buffer;
while (*ch != 0)
FmtString fmt(buffer);
for (const auto& token : fmt)
{
char c = *ch++;
switch (c)
switch (token.kind)
{
case FORMAT_NEWLINE:
case FormatToken::Newline:
if (fontBase == FONT_SPRITE_BASE_SMALL || fontBase == FONT_SPRITE_BASE_MEDIUM)
{
height += 10;
@ -371,7 +370,7 @@ int32_t string_get_height_raw(char* buffer)
}
height += 18;
break;
case FORMAT_NEWLINE_SMALLER:
case FormatToken::NewlineSmall:
if (fontBase == FONT_SPRITE_BASE_SMALL || fontBase == FONT_SPRITE_BASE_MEDIUM)
{
height += 5;
@ -384,33 +383,17 @@ int32_t string_get_height_raw(char* buffer)
}
height += 9;
break;
case FORMAT_TINYFONT:
case FormatToken::FontTiny:
fontBase = FONT_SPRITE_BASE_TINY;
break;
case FORMAT_MEDIUMFONT:
case FormatToken::FontMedium:
fontBase = FONT_SPRITE_BASE_MEDIUM;
break;
case FORMAT_SMALLFONT:
case FormatToken::FontSmall:
fontBase = FONT_SPRITE_BASE_SMALL;
break;
default:
if (c >= 32)
continue;
if (c <= 4)
{
ch++;
continue;
}
if (c <= 16)
continue;
ch += 2;
if (c <= 22)
continue;
ch += 2;
break;
}
}
return height;
}
@ -452,21 +435,27 @@ void gfx_draw_string_centred_wrapped_partial(
{
int32_t halfWidth = gfx_get_string_width(buffer) / 2;
utf8* ch = buffer;
utf8* nextCh;
int32_t codepoint;
while ((codepoint = utf8_get_next(ch, const_cast<const utf8**>(&nextCh))) != 0)
FmtString fmt(buffer);
for (const auto& token : fmt)
{
if (!utf8_is_format_code(codepoint))
bool doubleBreak = false;
if (token.IsLiteral())
{
numCharactersDrawn++;
if (numCharactersDrawn > numCharactersToDraw)
CodepointView codepoints(token.text);
for (auto it = codepoints.begin(); it != codepoints.end(); it++)
{
*ch = 0;
break;
numCharactersDrawn++;
if (numCharactersDrawn > numCharactersToDraw)
{
auto ch = const_cast<char*>(&token.text[it.GetIndex()]);
*ch = '\0';
doubleBreak = true;
break;
}
}
}
ch = nextCh;
if (doubleBreak)
break;
}
screenCoords = { coords.x - halfWidth, lineY };
@ -679,51 +668,51 @@ static void ttf_process_format_code(rct_drawpixelinfo* dpi, const FmtString::tok
{
switch (token.kind)
{
case FORMAT_MOVE_X:
case FormatToken::Move:
info->x = info->startX + token.parameter;
break;
case FORMAT_NEWLINE:
case FormatToken::Newline:
info->x = info->startX;
info->y += font_get_line_height(info->font_sprite_base);
break;
case FORMAT_NEWLINE_SMALLER:
case FormatToken::NewlineSmall:
info->x = info->startX;
info->y += font_get_line_height_small(info->font_sprite_base);
break;
case FORMAT_TINYFONT:
case FormatToken::FontTiny:
info->font_sprite_base = FONT_SPRITE_BASE_TINY;
break;
case FORMAT_SMALLFONT:
case FormatToken::FontSmall:
info->font_sprite_base = FONT_SPRITE_BASE_SMALL;
break;
case FORMAT_MEDIUMFONT:
case FormatToken::FontMedium:
info->font_sprite_base = FONT_SPRITE_BASE_MEDIUM;
break;
case FORMAT_OUTLINE:
case FormatToken::OutlineEnable:
info->flags |= TEXT_DRAW_FLAG_OUTLINE;
break;
case FORMAT_OUTLINE_OFF:
case FormatToken::OutlineDisable:
info->flags &= ~TEXT_DRAW_FLAG_OUTLINE;
break;
case FORMAT_WINDOW_COLOUR_1:
case FormatToken::ColourWindow1:
{
uint16_t flags = info->flags;
colour_char_window(gCurrentWindowColours[0], &flags, info->palette);
break;
}
case FORMAT_WINDOW_COLOUR_2:
case FormatToken::ColourWindow2:
{
uint16_t flags = info->flags;
colour_char_window(gCurrentWindowColours[1], &flags, info->palette);
break;
}
case FORMAT_WINDOW_COLOUR_3:
case FormatToken::ColourWindow3:
{
uint16_t flags = info->flags;
colour_char_window(gCurrentWindowColours[2], &flags, info->palette);
break;
}
case FORMAT_INLINE_SPRITE:
case FormatToken::InlineSprite:
{
auto g1 = gfx_get_g1_element(token.parameter & 0x7FFFF);
if (g1 != nullptr)
@ -737,10 +726,11 @@ static void ttf_process_format_code(rct_drawpixelinfo* dpi, const FmtString::tok
break;
}
default:
if (token.kind >= FORMAT_COLOUR_CODE_START && token.kind <= FORMAT_COLOUR_CODE_END)
if (FormatTokenIsColour(token.kind))
{
uint16_t flags = info->flags;
colour_char(token.kind - FORMAT_COLOUR_CODE_START, &flags, info->palette);
auto colourIndex = FormatTokenGetTextColourIndex(token.kind);
colour_char(static_cast<uint8_t>(colourIndex), &flags, info->palette);
}
break;
}

View File

@ -240,7 +240,7 @@ void font_sprite_initialise_characters()
int32_t width = 0;
if (g1 != nullptr)
{
if (glyphIndex < (FORMAT_ARGUMENT_CODE_START - 32) || glyphIndex >= (FORMAT_COLOUR_CODE_END - 32))
if (glyphIndex < 91 || glyphIndex >= 109)
{
width = (g1->width + 2 * g1->x_offset) - 1;
}

View File

@ -1540,12 +1540,12 @@ static void scrolling_text_set_bitmap_for_sprite(
}
}
}
else if (token.kind <= FORMAT_COLOUR_CODE_END && token.kind >= FORMAT_COLOUR_CODE_START)
else if (FormatTokenIsColour(token.kind))
{
auto g1 = gfx_get_g1_element(SPR_TEXT_PALETTE);
if (g1 != nullptr)
{
auto colourIndex = token.kind - FORMAT_COLOUR_CODE_START;
auto colourIndex = FormatTokenGetTextColourIndex(token.kind);
characterColour = g1->offset[colourIndex * 4];
}
}
@ -1574,12 +1574,12 @@ static void scrolling_text_set_bitmap_for_ttf(
{
ttfBuffer.append(token.text);
}
else if (token.kind >= FORMAT_COLOUR_CODE_START && token.kind <= FORMAT_COLOUR_CODE_END)
else if (FormatTokenIsColour(token.kind))
{
auto g1 = gfx_get_g1_element(SPR_TEXT_PALETTE);
if (g1 != nullptr)
{
auto colourIndex = token.kind - FORMAT_COLOUR_CODE_START;
auto colourIndex = FormatTokenGetTextColourIndex(token.kind);
colour = g1->offset[colourIndex * 4];
}
}

View File

@ -1931,17 +1931,17 @@ void InteractiveConsole::Execute(const std::string& s)
void InteractiveConsole::WriteLine(const std::string& s)
{
WriteLine(s, FORMAT_WINDOW_COLOUR_2);
WriteLine(s, FormatToken::ColourWindow2);
}
void InteractiveConsole::WriteLineError(const std::string& s)
{
WriteLine(s, FORMAT_RED);
WriteLine(s, FormatToken::ColourRed);
}
void InteractiveConsole::WriteLineWarning(const std::string& s)
{
WriteLine(s, FORMAT_YELLOW);
WriteLine(s, FormatToken::ColourYellow);
}
void InteractiveConsole::WriteFormatLine(const char* format, ...)

View File

@ -46,7 +46,7 @@ public:
virtual void Clear() abstract;
virtual void Close() abstract;
virtual void Hide() abstract;
virtual void WriteLine(const std::string& s, uint32_t colourFormat) abstract;
virtual void WriteLine(const std::string& s, FormatToken colourFormat) abstract;
};
class StdInOutConsole final : public InteractiveConsole
@ -68,5 +68,5 @@ public:
{
InteractiveConsole::WriteLine(s);
}
void WriteLine(const std::string& s, uint32_t colourFormat) override;
void WriteLine(const std::string& s, FormatToken colourFormat) override;
};

View File

@ -108,17 +108,17 @@ void StdInOutConsole::Close()
openrct2_finish();
}
void StdInOutConsole::WriteLine(const std::string& s, uint32_t colourFormat)
void StdInOutConsole::WriteLine(const std::string& s, FormatToken colourFormat)
{
std::string formatBegin;
if (colourFormat != FORMAT_WINDOW_COLOUR_2)
if (colourFormat != FormatToken::ColourWindow2)
{
switch (colourFormat)
{
case FORMAT_RED:
case FormatToken::ColourRed:
formatBegin = "\033[31m";
break;
case FORMAT_YELLOW:
case FormatToken::ColourYellow:
formatBegin = "\033[33m";
break;
}

View File

@ -17,54 +17,54 @@
// clang-format off
const encoding_convert_entry RCT2ToUnicodeTable[] =
{
{ 1, FORMAT_MOVE_X },
{ 2, FORMAT_ADJUST_PALETTE },
{ 5, FORMAT_NEWLINE },
{ 6, FORMAT_NEWLINE_SMALLER },
{ 7, FORMAT_TINYFONT },
{ 8, FORMAT_BIGFONT },
{ 9, FORMAT_MEDIUMFONT },
{ 10, FORMAT_SMALLFONT },
{ 11, FORMAT_OUTLINE },
{ 12, FORMAT_OUTLINE_OFF },
{ 13, FORMAT_WINDOW_COLOUR_1 },
{ 14, FORMAT_WINDOW_COLOUR_2 },
{ 15, FORMAT_WINDOW_COLOUR_3 },
{ 17, FORMAT_NEWLINE_X_Y },
{ 23, FORMAT_INLINE_SPRITE },
{ 123, FORMAT_COMMA32 },
{ 124, FORMAT_INT32 },
{ 125, FORMAT_COMMA2DP32 },
{ 126, FORMAT_COMMA16 },
{ 127, FORMAT_UINT16 },
{ 128, FORMAT_CURRENCY2DP },
{ 129, FORMAT_CURRENCY },
{ 130, FORMAT_STRINGID },
{ 131, FORMAT_STRINGID2 },
{ 132, FORMAT_STRING },
{ 133, FORMAT_MONTHYEAR },
{ 134, FORMAT_MONTH },
{ 135, FORMAT_VELOCITY },
{ 136, FORMAT_POP16 },
{ 137, FORMAT_PUSH16 },
{ 138, FORMAT_DURATION },
{ 139, FORMAT_REALTIME },
{ 140, FORMAT_LENGTH },
{ 141, FORMAT_SPRITE },
{ 142, FORMAT_BLACK },
{ 143, FORMAT_GREY },
{ 144, FORMAT_WHITE },
{ 145, FORMAT_RED },
{ 146, FORMAT_GREEN },
{ 147, FORMAT_YELLOW },
{ 148, FORMAT_TOPAZ },
{ 149, FORMAT_CELADON },
{ 150, FORMAT_BABYBLUE },
{ 151, FORMAT_PALELAVENDER },
{ 152, FORMAT_PALEGOLD },
{ 153, FORMAT_LIGHTPINK },
{ 154, FORMAT_PEARLAQUA },
{ 155, FORMAT_PALESILVER },
// { 1, FORMAT_MOVE_X },
// { 2, FORMAT_ADJUST_PALETTE },
// { 5, FORMAT_NEWLINE },
// { 6, FORMAT_NEWLINE_SMALLER },
// { 7, FORMAT_TINYFONT },
// { 8, FORMAT_BIGFONT },
// { 9, FORMAT_MEDIUMFONT },
// { 10, FORMAT_SMALLFONT },
// { 11, FORMAT_OUTLINE },
// { 12, FORMAT_OUTLINE_OFF },
// { 13, FORMAT_WINDOW_COLOUR_1 },
// { 14, FORMAT_WINDOW_COLOUR_2 },
// { 15, FORMAT_WINDOW_COLOUR_3 },
// { 17, FORMAT_NEWLINE_X_Y },
// { 23, FORMAT_INLINE_SPRITE },
// { 123, FORMAT_COMMA32 },
// { 124, FORMAT_INT32 },
// { 125, FORMAT_COMMA2DP32 },
// { 126, FORMAT_COMMA16 },
// { 127, FORMAT_UINT16 },
// { 128, FORMAT_CURRENCY2DP },
// { 129, FORMAT_CURRENCY },
// { 130, FORMAT_STRINGID },
// { 131, FORMAT_STRINGID2 },
// { 132, FORMAT_STRING },
// { 133, FORMAT_MONTHYEAR },
// { 134, FORMAT_MONTH },
// { 135, FORMAT_VELOCITY },
// { 136, FORMAT_POP16 },
// { 137, FORMAT_PUSH16 },
// { 138, FORMAT_DURATION },
// { 139, FORMAT_REALTIME },
// { 140, FORMAT_LENGTH },
// { 141, FORMAT_SPRITE },
// { 142, FORMAT_BLACK },
// { 143, FORMAT_GREY },
// { 144, FORMAT_WHITE },
// { 145, FORMAT_RED },
// { 146, FORMAT_GREEN },
// { 147, FORMAT_YELLOW },
// { 148, FORMAT_TOPAZ },
// { 149, FORMAT_CELADON },
// { 150, FORMAT_BABYBLUE },
// { 151, FORMAT_PALELAVENDER },
// { 152, FORMAT_PALEGOLD },
// { 153, FORMAT_LIGHTPINK },
// { 154, FORMAT_PEARLAQUA },
// { 155, FORMAT_PALESILVER },
{ CSChar::a_ogonek_uc, UnicodeChar::a_ogonek_uc },
{ CSChar::up, UnicodeChar::up },
{ CSChar::c_acute_uc, UnicodeChar::c_acute_uc },

View File

@ -9,90 +9,174 @@
#include "FormatCodes.h"
#include "../common.h"
#include "../core/String.hpp"
#include "Localisation.h"
#include <iterator>
#pragma region Format codes
struct format_code_token
{
uint32_t code;
const char* token;
};
#include <unordered_map>
// clang-format off
static constexpr const format_code_token format_code_tokens[] = {
{ FORMAT_MOVE_X, "MOVE_X" },
{ FORMAT_ADJUST_PALETTE, "ADJUST_PALETTE" },
{ FORMAT_NEWLINE, "NEWLINE" },
{ FORMAT_NEWLINE_SMALLER, "NEWLINE_SMALLER" },
{ FORMAT_TINYFONT, "TINYFONT" },
{ FORMAT_BIGFONT, "BIGFONT" },
{ FORMAT_MEDIUMFONT, "MEDIUMFONT" },
{ FORMAT_SMALLFONT, "SMALLFONT" },
{ FORMAT_OUTLINE, "OUTLINE" },
{ FORMAT_OUTLINE_OFF, "OUTLINE_OFF" },
{ FORMAT_WINDOW_COLOUR_1, "WINDOW_COLOUR_1" },
{ FORMAT_WINDOW_COLOUR_2, "WINDOW_COLOUR_2" },
{ FORMAT_WINDOW_COLOUR_3, "WINDOW_COLOUR_3" },
{ FORMAT_NEWLINE_X_Y, "NEWLINE_X_Y" },
{ FORMAT_INLINE_SPRITE, "INLINE_SPRITE" },
{ FORMAT_COMMA32, "COMMA32" },
{ FORMAT_INT32, "INT32" },
{ FORMAT_COMMA2DP32, "COMMA2DP32" },
{ FORMAT_COMMA16, "COMMA16" },
{ FORMAT_UINT16, "UINT16" },
{ FORMAT_CURRENCY2DP, "CURRENCY2DP" },
{ FORMAT_CURRENCY, "CURRENCY" },
{ FORMAT_STRINGID, "STRINGID" },
{ FORMAT_STRINGID2, "STRINGID2" },
{ FORMAT_STRING, "STRING" },
{ FORMAT_MONTHYEAR, "MONTHYEAR" },
{ FORMAT_MONTH, "MONTH" },
{ FORMAT_VELOCITY, "VELOCITY" },
{ FORMAT_POP16, "POP16" },
{ FORMAT_PUSH16, "PUSH16" },
{ FORMAT_DURATION, "DURATION" },
{ FORMAT_REALTIME, "REALTIME" },
{ FORMAT_LENGTH, "LENGTH" },
{ FORMAT_SPRITE, "SPRITE" },
{ FORMAT_BLACK, "BLACK" },
{ FORMAT_GREY, "GREY" },
{ FORMAT_WHITE, "WHITE" },
{ FORMAT_RED, "RED" },
{ FORMAT_GREEN, "GREEN" },
{ FORMAT_YELLOW, "YELLOW" },
{ FORMAT_TOPAZ, "TOPAZ" },
{ FORMAT_CELADON, "CELADON" },
{ FORMAT_BABYBLUE, "BABYBLUE" },
{ FORMAT_PALELAVENDER, "PALELAVENDER" },
{ FORMAT_PALEGOLD, "PALEGOLD" },
{ FORMAT_LIGHTPINK, "LIGHTPINK" },
{ FORMAT_PEARLAQUA, "PEARLAQUA" },
{ FORMAT_PALESILVER, "PALESILVER" },
{ FORMAT_COMMA1DP16, "COMMA1DP16" }
static const std::unordered_map<std::string_view, FormatToken> FormatTokenMap = {
{ "MOVE_X", FormatToken::Move, },
{ "NEWLINE", FormatToken::Newline, },
{ "NEWLINE_SMALLER", FormatToken::NewlineSmall, },
{ "TINYFONT", FormatToken::FontTiny, },
{ "BIGFONT", FormatToken::FontBig, },
{ "MEDIUMFONT", FormatToken::FontMedium, },
{ "SMALLFONT", FormatToken::FontSmall, },
{ "OUTLINE", FormatToken::OutlineEnable, },
{ "OUTLINE_OFF", FormatToken::OutlineDisable, },
{ "WINDOW_COLOUR_1", FormatToken::ColourWindow1, },
{ "WINDOW_COLOUR_2", FormatToken::ColourWindow2, },
{ "WINDOW_COLOUR_3", FormatToken::ColourWindow3, },
{ "INLINE_SPRITE", FormatToken::InlineSprite, },
{ "COMMA32", FormatToken::Comma32, },
{ "INT32", FormatToken::Int32, },
{ "COMMA1DP16", FormatToken::Comma1dp16, },
{ "COMMA2DP32", FormatToken::Comma2dp32, },
{ "COMMA16", FormatToken::Comma16, },
{ "UINT16", FormatToken::Uint16, },
{ "CURRENCY2DP", FormatToken::Currency2dp, },
{ "CURRENCY", FormatToken::Currency, },
{ "STRINGID", FormatToken::StringId, },
{ "STRING", FormatToken::String, },
{ "MONTHYEAR", FormatToken::MonthYear, },
{ "MONTH", FormatToken::Month, },
{ "VELOCITY", FormatToken::Velocity, },
{ "POP16", FormatToken::Pop16, },
{ "PUSH16", FormatToken::Push16, },
{ "DURATION", FormatToken::DurationShort, },
{ "REALTIME", FormatToken::DurationLong, },
{ "LENGTH", FormatToken::Length, },
{ "SPRITE", FormatToken::Sprite, },
{ "BLACK", FormatToken::ColourBlack, },
{ "GREY", FormatToken::ColourGrey, },
{ "WHITE", FormatToken::ColourWhite, },
{ "RED", FormatToken::ColourRed, },
{ "GREEN", FormatToken::ColourGreen, },
{ "YELLOW", FormatToken::ColourYellow, },
{ "TOPAZ", FormatToken::ColourTopaz, },
{ "CELADON", FormatToken::ColourCeladon, },
{ "BABYBLUE", FormatToken::ColourBabyBlue, },
{ "PALELAVENDER", FormatToken::ColourPaleLavender, },
{ "PALEGOLD", FormatToken::ColourPaleGold, },
{ "LIGHTPINK", FormatToken::ColourLightPink, },
{ "PEARLAQUA", FormatToken::ColourPearlAqua, },
{ "PALESILVER", FormatToken::ColourPaleSilver, },
};
// clang-format on
uint32_t format_get_code(std::string_view token)
FormatToken FormatTokenFromString(std::string_view token)
{
auto result = std::find_if(std::begin(format_code_tokens), std::end(format_code_tokens), [token](auto& fct) {
return String::Equals(token, fct.token, true);
});
return result != std::end(format_code_tokens) ? result->code : 0;
auto result = FormatTokenMap.find(token);
return result != std::end(FormatTokenMap) ? result->second : FormatToken::Unknown;
}
const char* format_get_token(uint32_t code)
std::string_view FormatTokenToString(FormatToken token)
{
for (uint32_t i = 0; i < std::size(format_code_tokens); i++)
for (const auto& t : FormatTokenMap)
{
if (code == format_code_tokens[i].code)
return format_code_tokens[i].token;
if (t.second == token)
{
return t.first;
}
}
return nullptr;
return {};
}
bool FormatTokenTakesArgument(FormatToken token)
{
switch (token)
{
case FormatToken::Comma32:
case FormatToken::Int32:
case FormatToken::Comma1dp16:
case FormatToken::Comma2dp32:
case FormatToken::Comma16:
case FormatToken::Uint16:
case FormatToken::Currency2dp:
case FormatToken::Currency:
case FormatToken::StringId:
case FormatToken::String:
case FormatToken::MonthYear:
case FormatToken::Month:
case FormatToken::Velocity:
case FormatToken::DurationShort:
case FormatToken::DurationLong:
case FormatToken::Length:
case FormatToken::Sprite:
return true;
}
return false;
}
bool FormatTokenIsColour(FormatToken token)
{
switch (token)
{
case FormatToken::ColourBlack:
case FormatToken::ColourGrey:
case FormatToken::ColourWhite:
case FormatToken::ColourRed:
case FormatToken::ColourGreen:
case FormatToken::ColourYellow:
case FormatToken::ColourTopaz:
case FormatToken::ColourCeladon:
case FormatToken::ColourBabyBlue:
case FormatToken::ColourPaleLavender:
case FormatToken::ColourPaleGold:
case FormatToken::ColourLightPink:
case FormatToken::ColourPearlAqua:
case FormatToken::ColourPaleSilver:
return true;
}
return false;
}
size_t FormatTokenGetTextColourIndex(FormatToken token)
{
switch (token)
{
case FormatToken::ColourBlack:
return 0;
case FormatToken::ColourGrey:
return 1;
case FormatToken::ColourWhite:
return 2;
case FormatToken::ColourRed:
return 3;
case FormatToken::ColourGreen:
return 4;
case FormatToken::ColourYellow:
return 5;
case FormatToken::ColourTopaz:
return 6;
case FormatToken::ColourCeladon:
return 7;
case FormatToken::ColourBabyBlue:
return 8;
case FormatToken::ColourPaleLavender:
return 9;
case FormatToken::ColourPaleGold:
return 10;
case FormatToken::ColourLightPink:
return 11;
case FormatToken::ColourPearlAqua:
return 12;
case FormatToken::ColourPaleSilver:
return 13;
}
return 0;
}
FormatToken FormatTokenFromTextColour(size_t textColour)
{
static constexpr const FormatToken tokens[] = {
FormatToken::ColourBlack, FormatToken::ColourGrey, FormatToken::ColourWhite,
FormatToken::ColourRed, FormatToken::ColourGreen, FormatToken::ColourYellow,
FormatToken::ColourTopaz, FormatToken::ColourCeladon, FormatToken::ColourBabyBlue,
FormatToken::ColourPaleLavender, FormatToken::ColourPaleGold, FormatToken::ColourLightPink,
FormatToken::ColourPearlAqua, FormatToken::ColourPaleSilver,
};
if (textColour > std::size(tokens))
return FormatToken::ColourBlack;
return tokens[textColour];
}
bool utf8_should_use_sprite_for_codepoint(char32_t codepoint)
@ -119,5 +203,3 @@ bool utf8_should_use_sprite_for_codepoint(char32_t codepoint)
return false;
}
}
#pragma endregion

View File

@ -13,92 +13,75 @@
#include <string_view>
uint32_t format_get_code(std::string_view token);
const char* format_get_token(uint32_t code);
enum
enum class FormatToken
{
// Font format codes
Unknown,
Literal,
// The next byte specifies the X coordinate
FORMAT_MOVE_X = 1,
// The next byte specifies the palette
FORMAT_ADJUST_PALETTE,
Newline,
NewlineSmall,
FORMAT_3,
FORMAT_4,
// With parameters
Move,
InlineSprite,
// Moves to the next line
FORMAT_NEWLINE = 5,
// Moves less than NEWLINE
FORMAT_NEWLINE_SMALLER,
// With arguments
Comma32,
Int32,
Comma1dp16,
Comma2dp32,
Comma16,
Uint16,
Currency2dp,
Currency,
StringId,
String,
MonthYear,
Month,
Velocity,
DurationShort,
DurationLong,
Length,
Sprite,
Pop16,
Push16,
FORMAT_TINYFONT,
FORMAT_BIGFONT,
FORMAT_MEDIUMFONT,
FORMAT_SMALLFONT,
// Colours
ColourWindow1,
ColourWindow2,
ColourWindow3,
ColourBlack,
ColourGrey,
ColourWhite,
ColourRed,
ColourGreen,
ColourYellow,
ColourTopaz,
ColourCeladon,
ColourBabyBlue,
ColourPaleLavender,
ColourPaleGold,
ColourLightPink,
ColourPearlAqua,
ColourPaleSilver,
FORMAT_OUTLINE,
FORMAT_OUTLINE_OFF,
// Fonts
FontTiny,
FontSmall,
FontMedium,
FontBig,
// Changes the colour of the text to a predefined window colour.
FORMAT_WINDOW_COLOUR_1,
FORMAT_WINDOW_COLOUR_2,
FORMAT_WINDOW_COLOUR_3,
FORMAT_16,
// The next 2 bytes specify the X and Y coordinates
FORMAT_NEWLINE_X_Y = 17,
// The next 4 bytes specify the sprite
FORMAT_INLINE_SPRITE = 23,
// Argument format codes
FORMAT_ARGUMENT_CODE_START = 123, // 'z' == 122 or 0x7A
FORMAT_COMMA32 = 123,
FORMAT_INT32,
FORMAT_COMMA2DP32,
FORMAT_COMMA16,
FORMAT_UINT16,
FORMAT_CURRENCY2DP,
FORMAT_CURRENCY,
FORMAT_STRINGID,
FORMAT_STRINGID2,
FORMAT_STRING,
FORMAT_MONTHYEAR,
FORMAT_MONTH,
FORMAT_VELOCITY,
FORMAT_POP16,
FORMAT_PUSH16,
FORMAT_DURATION,
FORMAT_REALTIME,
FORMAT_LENGTH,
FORMAT_SPRITE,
FORMAT_ARGUMENT_CODE_END = FORMAT_SPRITE,
// Colour format codes
FORMAT_COLOUR_CODE_START = 142,
FORMAT_BLACK = 142,
FORMAT_GREY,
FORMAT_WHITE,
FORMAT_RED,
FORMAT_GREEN,
FORMAT_YELLOW,
FORMAT_TOPAZ,
FORMAT_CELADON,
FORMAT_BABYBLUE,
FORMAT_PALELAVENDER,
FORMAT_PALEGOLD,
FORMAT_LIGHTPINK,
FORMAT_PEARLAQUA,
FORMAT_PALESILVER,
FORMAT_COLOUR_CODE_END = FORMAT_PALESILVER,
// Format codes that need suitable Unicode allocations
FORMAT_COMMA1DP16 = 20004
OutlineEnable,
OutlineDisable,
};
FormatToken FormatTokenFromString(std::string_view token);
std::string_view FormatTokenToString(FormatToken token);
bool FormatTokenTakesArgument(FormatToken token);
bool FormatTokenIsColour(FormatToken token);
size_t FormatTokenGetTextColourIndex(FormatToken token);
FormatToken FormatTokenFromTextColour(size_t textColour);
constexpr uint8_t CS_SPRITE_FONT_OFFSET = 32;
namespace CSChar

View File

@ -60,7 +60,7 @@ namespace OpenRCT2
bool FmtString::token::IsLiteral() const
{
return kind == 0;
return kind == FormatToken::Literal;
}
FmtString::iterator::iterator(std::string_view s, size_t i)
@ -104,7 +104,7 @@ namespace OpenRCT2
{
p = *p0;
}
current = token(FORMAT_MOVE_X, str.substr(startIndex, i - startIndex), p);
current = token(FormatToken::Move, str.substr(startIndex, i - startIndex), p);
return;
}
else if (inner == "INLINE_SPRITE")
@ -121,7 +121,7 @@ namespace OpenRCT2
p |= (*p2) << 16;
p |= (*p3) << 24;
}
current = token(FORMAT_INLINE_SPRITE, str.substr(startIndex, i - startIndex), p);
current = token(FormatToken::InlineSprite, str.substr(startIndex, i - startIndex), p);
return;
}
}
@ -151,14 +151,14 @@ namespace OpenRCT2
std::string_view sztoken = str.substr(index, len);
if (sztoken.size() >= 2 && sztoken[0] == '{' && sztoken[1] != '{')
{
auto kind = format_get_code(sztoken.substr(1, len - 2));
auto kind = FormatTokenFromString(sztoken.substr(1, len - 2));
return token(kind, sztoken);
}
else if (sztoken == "\n" || sztoken == "\r")
{
return token(FORMAT_NEWLINE, sztoken);
return token(FormatToken::Newline, sztoken);
}
return token(0, sztoken);
return token(FormatToken::Literal, sztoken);
}
const FmtString::token* FmtString::iterator::operator->() const
@ -433,21 +433,21 @@ namespace OpenRCT2
{
switch (token)
{
case FORMAT_UINT16:
case FORMAT_INT32:
case FormatToken::Uint16:
case FormatToken::Int32:
if constexpr (std::is_integral<T>())
{
FormatNumber<0, false>(ss, arg);
}
break;
case FORMAT_COMMA16:
case FORMAT_COMMA32:
case FormatToken::Comma16:
case FormatToken::Comma32:
if constexpr (std::is_integral<T>())
{
FormatNumber<0, true>(ss, arg);
}
break;
case FORMAT_COMMA1DP16:
case FormatToken::Comma1dp16:
if constexpr (std::is_integral<T>())
{
FormatNumber<1, true>(ss, arg);
@ -457,7 +457,7 @@ namespace OpenRCT2
FormatNumber<1, true>(ss, std::round(arg * 10));
}
break;
case FORMAT_COMMA2DP32:
case FormatToken::Comma2dp32:
if constexpr (std::is_integral<T>())
{
FormatNumber<2, true>(ss, arg);
@ -467,19 +467,19 @@ namespace OpenRCT2
FormatNumber<2, true>(ss, std::round(arg * 100));
}
break;
case FORMAT_CURRENCY2DP:
case FormatToken::Currency2dp:
if constexpr (std::is_integral<T>())
{
FormatCurrency<2, true>(ss, arg);
}
break;
case FORMAT_CURRENCY:
case FormatToken::Currency:
if constexpr (std::is_integral<T>())
{
FormatCurrency<0, true>(ss, arg);
}
break;
case FORMAT_VELOCITY:
case FormatToken::Velocity:
if constexpr (std::is_integral<T>())
{
switch (gConfigGeneral.measurement_format)
@ -497,19 +497,19 @@ namespace OpenRCT2
}
}
break;
case FORMAT_DURATION:
case FormatToken::DurationShort:
if constexpr (std::is_integral<T>())
{
FormatMinutesSeconds(ss, arg);
}
break;
case FORMAT_REALTIME:
case FormatToken::DurationLong:
if constexpr (std::is_integral<T>())
{
FormatHoursMinutes(ss, arg);
}
break;
case FORMAT_LENGTH:
case FormatToken::Length:
if constexpr (std::is_integral<T>())
{
switch (gConfigGeneral.measurement_format)
@ -525,7 +525,7 @@ namespace OpenRCT2
}
}
break;
case FORMAT_MONTHYEAR:
case FormatToken::MonthYear:
if constexpr (std::is_integral<T>())
{
auto month = date_get_month(arg);
@ -533,7 +533,7 @@ namespace OpenRCT2
FormatStringId(ss, STR_DATE_FORMAT_MY, month, year);
}
break;
case FORMAT_MONTH:
case FormatToken::Month:
if constexpr (std::is_integral<T>())
{
auto szMonth = language_get_string(DateGameMonthNames[date_get_month(arg)]);
@ -543,7 +543,7 @@ namespace OpenRCT2
}
}
break;
case FORMAT_STRING:
case FormatToken::String:
if constexpr (std::is_same<T, const char*>())
{
ss << arg;
@ -553,7 +553,7 @@ namespace OpenRCT2
ss << arg.c_str();
}
break;
case FORMAT_SPRITE:
case FormatToken::Sprite:
if constexpr (std::is_integral<T>())
{
auto idx = static_cast<uint32_t>(arg);
@ -567,17 +567,10 @@ namespace OpenRCT2
}
}
template void FormatArgument(std::stringstream&, uint32_t, uint16_t);
template void FormatArgument(std::stringstream&, uint32_t, int16_t);
template void FormatArgument(std::stringstream&, uint32_t, int32_t);
template void FormatArgument(std::stringstream&, uint32_t, const char*);
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);
}
template void FormatArgument(std::stringstream&, FormatToken, uint16_t);
template void FormatArgument(std::stringstream&, FormatToken, int16_t);
template void FormatArgument(std::stringstream&, FormatToken, int32_t);
template void FormatArgument(std::stringstream&, FormatToken, const char*);
bool IsRealNameStringId(rct_string_id id)
{
@ -635,7 +628,7 @@ namespace OpenRCT2
{
for (const auto& token : fmt)
{
if (token.kind == FORMAT_STRINGID || token.kind == FORMAT_STRINGID2)
if (token.kind == FormatToken::StringId)
{
if (argIndex < args.size())
{
@ -658,7 +651,7 @@ namespace OpenRCT2
argIndex++;
}
}
else if (CanFormatToken(token.kind))
else if (FormatTokenTakesArgument(token.kind))
{
if (argIndex < args.size())
{
@ -666,7 +659,7 @@ namespace OpenRCT2
}
argIndex++;
}
else if (token.kind != FORMAT_PUSH16 && token.kind != FORMAT_POP16)
else if (token.kind != FormatToken::Push16 && token.kind != FormatToken::Pop16)
{
ss << token.text;
}
@ -703,42 +696,41 @@ namespace OpenRCT2
{
switch (t.kind)
{
case FORMAT_COMMA32:
case FORMAT_INT32:
case FORMAT_COMMA2DP32:
case FORMAT_CURRENCY2DP:
case FORMAT_CURRENCY:
case FORMAT_SPRITE:
case FormatToken::Comma32:
case FormatToken::Int32:
case FormatToken::Comma2dp32:
case FormatToken::Currency2dp:
case FormatToken::Currency:
case FormatToken::Sprite:
anyArgs.push_back(ReadFromArgs<int32_t>(args));
break;
case FORMAT_COMMA16:
case FORMAT_UINT16:
case FORMAT_MONTHYEAR:
case FORMAT_MONTH:
case FORMAT_VELOCITY:
case FORMAT_DURATION:
case FORMAT_REALTIME:
case FORMAT_LENGTH:
case FormatToken::Comma16:
case FormatToken::Uint16:
case FormatToken::MonthYear:
case FormatToken::Month:
case FormatToken::Velocity:
case FormatToken::DurationShort:
case FormatToken::DurationLong:
case FormatToken::Length:
anyArgs.push_back(ReadFromArgs<uint16_t>(args));
break;
case FORMAT_STRINGID:
case FORMAT_STRINGID2:
case FormatToken::StringId:
{
auto stringId = ReadFromArgs<rct_string_id>(args);
anyArgs.push_back(stringId);
BuildAnyArgListFromLegacyArgBuffer(GetFmtStringById(stringId), anyArgs, args);
break;
}
case FORMAT_STRING:
case FormatToken::String:
{
auto sz = ReadFromArgs<const char*>(args);
anyArgs.push_back(sz);
break;
}
case FORMAT_POP16:
case FormatToken::Pop16:
args = reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(args) + 2);
break;
case FORMAT_PUSH16:
case FormatToken::Push16:
args = reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(args) - 2);
break;
}

View File

@ -23,7 +23,6 @@
namespace OpenRCT2
{
using FormatToken = uint32_t;
using FormatArg_t = std::variant<uint16_t, int32_t, const char*>;
class FmtString
@ -77,7 +76,6 @@ namespace OpenRCT2
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg);
bool CanFormatToken(FormatToken t);
bool IsRealNameStringId(rct_string_id id);
void FormatRealName(std::stringstream& ss, rct_string_id id);
FmtString GetFmtStringById(rct_string_id id);
@ -92,7 +90,7 @@ namespace OpenRCT2
while (!it.eol())
{
const auto& token = *it++;
if (!CanFormatToken(token.kind))
if (!FormatTokenTakesArgument(token.kind))
{
ss << token.text;
}
@ -110,7 +108,7 @@ namespace OpenRCT2
while (!it.eol())
{
const auto& token = *it++;
if (token.kind == FORMAT_STRINGID || token.kind == FORMAT_STRINGID2)
if (token.kind == FormatToken::StringId)
{
if constexpr (std::is_integral<TArg0>())
{
@ -128,7 +126,7 @@ namespace OpenRCT2
FormatString(ss, stack, argN...);
}
}
else if (CanFormatToken(token.kind))
else if (FormatTokenTakesArgument(token.kind))
{
FormatArgument(ss, token.kind, arg0);
return FormatString(ss, stack, argN...);

View File

@ -51,21 +51,6 @@ const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] =
};
// clang-format on
void utf8_remove_format_codes(utf8* text, bool allowcolours)
{
const utf8* ch = text;
utf8* dstCh = text;
int32_t codepoint;
while ((codepoint = String::GetNextCodepoint(ch, &ch)) != 0)
{
if (!utf8_is_format_code(codepoint) || (allowcolours && utf8_is_colour_code(codepoint)))
{
dstCh = String::WriteCodepoint(dstCh, codepoint);
}
}
*dstCh = 0;
}
uint8_t language_get_id_from_locale(const char* locale)
{
uint8_t i = 0;
@ -125,113 +110,3 @@ rct_string_id language_allocate_object_string(const std::string& target)
auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService();
return localisationService.AllocateObjectString(target);
}
std::string language_convert_string_to_tokens(const std::string_view& s)
{
std::string result;
result.reserve(s.size() * 4);
std::string input = std::string(s);
auto readPtr = input.c_str();
while (true)
{
char32_t code = utf8_get_next(readPtr, const_cast<const utf8**>(&readPtr));
if (code == 0)
{
break;
}
else if (code == '\n')
{
result.push_back('\n');
}
else if (utf8_is_format_code(code))
{
auto token = format_get_token(code);
result.push_back('{');
result.append(token);
result.push_back('}');
}
else
{
char buffer[8]{};
utf8_write_codepoint(buffer, code);
result.append(buffer);
}
}
result.shrink_to_fit();
return result;
}
std::string language_convert_string(const std::string_view& s)
{
enum class PARSE_STATE
{
DEFAULT,
CR,
TOKEN,
};
std::string result;
std::string token;
PARSE_STATE state{};
token.reserve(64);
result.reserve(s.size() * 2);
for (char c : s)
{
switch (state)
{
case PARSE_STATE::CR:
result.push_back(FORMAT_NEWLINE);
state = PARSE_STATE::DEFAULT;
[[fallthrough]];
case PARSE_STATE::DEFAULT:
switch (c)
{
case '\r':
state = PARSE_STATE::CR;
break;
case '\n':
result.push_back(FORMAT_NEWLINE);
break;
case '{':
token.clear();
state = PARSE_STATE::TOKEN;
break;
default:
if (static_cast<uint8_t>(c) >= 32)
{
result.push_back(c);
}
break;
}
break;
case PARSE_STATE::TOKEN:
if (c == '}')
{
auto code = format_get_code(token.c_str());
if (code == 0)
{
int32_t number{};
if (sscanf(token.c_str(), "%d", &number) == 1)
{
auto b = static_cast<uint8_t>(std::clamp(number, 0, 255));
token.push_back(b);
}
}
else
{
char buffer[8]{};
utf8_write_codepoint(buffer, code);
result.append(buffer);
}
state = PARSE_STATE::DEFAULT;
}
else
{
token.push_back(c);
}
break;
}
}
result.shrink_to_fit();
return result;
}

View File

@ -98,7 +98,6 @@ bool language_open(int32_t id);
uint32_t utf8_get_next(const utf8* char_ptr, const utf8** nextchar_ptr);
int32_t utf8_insert_codepoint(utf8* dst, uint32_t codepoint);
bool utf8_is_codepoint_start(const utf8* text);
void utf8_remove_format_codes(utf8* text, bool allowcolours);
int32_t utf8_get_codepoint_length(char32_t codepoint);
int32_t utf8_length(const utf8* text);
@ -107,8 +106,6 @@ std::string utf8_to_rct2(const std::string_view& src);
bool language_get_localised_scenario_strings(const utf8* scenarioFilename, rct_string_id* outStringIds);
void language_free_object_string(rct_string_id stringId);
rct_string_id language_allocate_object_string(const std::string& target);
std::string language_convert_string_to_tokens(const std::string_view& s);
std::string language_convert_string(const std::string_view& s);
constexpr utf8* utf8_write_codepoint(utf8* dst, uint32_t codepoint)
{

View File

@ -539,32 +539,8 @@ private:
sb.Clear();
while (reader->TryPeek(&codepoint) && !IsNewLine(codepoint))
{
// if (codepoint == '{')
// {
// uint32_t token;
// bool isByte;
// if (ParseToken(reader, &token, &isByte))
// {
// if (isByte)
// {
// sb.Append(reinterpret_cast<const utf8*>(&token), 1);
// }
// else
// {
// sb.Append(static_cast<int32_t>(token));
// }
// }
// else
// {
// // Syntax error or unknown token, ignore line entirely
// return;
// }
// }
// else
{
reader->Skip();
sb.Append(codepoint);
}
reader->Skip();
sb.Append(codepoint);
}
std::string s;
@ -599,47 +575,6 @@ private:
}
}
}
bool ParseToken(IStringReader* reader, uint32_t* token, bool* isByte)
{
auto sb = StringBuilder();
codepoint_t codepoint;
// Skip open brace
reader->Skip();
while (reader->TryPeek(&codepoint))
{
if (IsNewLine(codepoint))
return false;
if (IsWhitespace(codepoint))
return false;
reader->Skip();
if (codepoint == '}')
break;
sb.Append(codepoint);
}
const utf8* tokenName = sb.GetBuffer();
*token = format_get_code(tokenName);
*isByte = false;
// Handle explicit byte values
if (*token == 0)
{
int32_t number;
if (sscanf(tokenName, "%d", &number) == 1)
{
*token = std::clamp(number, 0, 255);
*isByte = true;
}
}
return true;
}
};
namespace LanguagePackFactory

File diff suppressed because it is too large Load Diff

View File

@ -19,15 +19,10 @@
#include <string>
bool utf8_is_format_code(char32_t codepoint);
bool utf8_is_colour_code(char32_t codepoint);
bool utf8_should_use_sprite_for_codepoint(char32_t codepoint);
int32_t utf8_get_format_code_arg_length(char32_t codepoint);
void utf8_remove_formatting(utf8* string, bool allowColours);
std::string format_string(rct_string_id format, const void* args);
void format_string(char* dest, size_t size, rct_string_id format, const void* args);
void format_string_raw(char* dest, size_t size, const char* src, const void* args);
void format_string_to_upper(char* dest, size_t size, rct_string_id format, const void* args);
void generate_string_file();
@ -43,7 +38,6 @@ void format_readable_speed(char* buf, size_t bufSize, uint64_t sizeBytesPerSec);
utf8* get_string_end(const utf8* text);
size_t get_string_size(const utf8* text);
int32_t get_string_length(const utf8* text);
// The maximum number of characters allowed for string/money conversions (anything above will risk integer overflow issues)
#define MONEY_STRING_MAXLENGTH 14

View File

@ -112,15 +112,7 @@ int32_t utf8_length(const utf8* text)
*/
utf8* get_string_end(const utf8* text)
{
int32_t codepoint;
const utf8* ch = text;
while ((codepoint = utf8_get_next(ch, &ch)) != 0)
{
int32_t argLength = utf8_get_format_code_arg_length(codepoint);
ch += argLength;
}
return const_cast<utf8*>(ch - 1);
return const_cast<char*>(std::strchr(text, 0));
}
/**
@ -130,83 +122,3 @@ size_t get_string_size(const utf8* text)
{
return get_string_end(text) - text + 1;
}
/**
* Return the number of visible characters (excludes format codes) in the given UTF-8 string.
*/
int32_t get_string_length(const utf8* text)
{
char32_t codepoint;
const utf8* ch = text;
int32_t count = 0;
while ((codepoint = utf8_get_next(ch, &ch)) != 0)
{
if (utf8_is_format_code(codepoint))
{
ch += utf8_get_format_code_arg_length(codepoint);
}
else
{
count++;
}
}
return count;
}
int32_t utf8_get_format_code_arg_length(char32_t codepoint)
{
switch (codepoint)
{
case FORMAT_MOVE_X:
case FORMAT_ADJUST_PALETTE:
case FORMAT_3:
case FORMAT_4:
return 1;
case FORMAT_NEWLINE_X_Y:
return 2;
case FORMAT_INLINE_SPRITE:
return 4;
default:
return 0;
}
}
void utf8_remove_formatting(utf8* string, bool allowColours)
{
utf8* readPtr = string;
utf8* writePtr = string;
while (true)
{
char32_t code = utf8_get_next(readPtr, const_cast<const utf8**>(&readPtr));
if (code == 0)
{
*writePtr = 0;
break;
}
else if (!utf8_is_format_code(code) || (allowColours && utf8_is_colour_code(code)))
{
writePtr = utf8_write_codepoint(writePtr, code);
}
}
}
bool utf8_is_format_code(char32_t codepoint)
{
if (codepoint < 32)
return true;
if (codepoint >= FORMAT_ARGUMENT_CODE_START && codepoint <= FORMAT_ARGUMENT_CODE_END)
return true;
if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END)
return true;
if (codepoint == FORMAT_COMMA1DP16)
return true;
return false;
}
bool utf8_is_colour_code(char32_t codepoint)
{
return codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END;
}

View File

@ -695,23 +695,18 @@ NetworkGroup* NetworkBase::GetGroupByID(uint8_t id)
const char* NetworkBase::FormatChat(NetworkPlayer* fromplayer, const char* text)
{
static char formatted[1024];
char* lineCh = formatted;
formatted[0] = 0;
static std::string formatted;
formatted.clear();
formatted += "{OUTLINE}";
if (fromplayer)
{
lineCh = utf8_write_codepoint(lineCh, FORMAT_OUTLINE);
lineCh = utf8_write_codepoint(lineCh, FORMAT_BABYBLUE);
safe_strcpy(lineCh, static_cast<const char*>(fromplayer->Name.c_str()), sizeof(formatted) - (lineCh - formatted));
safe_strcat(lineCh, ": ", sizeof(formatted) - (lineCh - formatted));
lineCh = strchr(lineCh, '\0');
formatted += "{BABYBLUE}";
formatted += fromplayer->Name;
formatted += ": ";
}
lineCh = utf8_write_codepoint(lineCh, FORMAT_OUTLINE);
lineCh = utf8_write_codepoint(lineCh, FORMAT_WHITE);
char* ptrtext = lineCh;
safe_strcpy(lineCh, text, 800);
utf8_remove_format_codes(ptrtext, true);
return formatted;
formatted += "{WHITE}";
formatted += text;
return formatted.c_str();
}
void NetworkBase::SendPacketToClients(const NetworkPacket& packet, bool front, bool gameCmd)
@ -1092,7 +1087,6 @@ void NetworkBase::AppendLog(std::ostream& fs, const std::string& s)
if (strftime(buffer, sizeof(buffer), "[%Y/%m/%d %H:%M:%S] ", tmInfo) != 0)
{
String::Append(buffer, sizeof(buffer), s.c_str());
utf8_remove_formatting(buffer, false);
String::Append(buffer, sizeof(buffer), PLATFORM_NEWLINE);
fs.write(buffer, strlen(buffer));

View File

@ -19,7 +19,6 @@ void NetworkPlayer::SetName(const std::string& name)
{
// 36 == 31 + strlen(" #255");
Name = name.substr(0, 36);
utf8_remove_format_codes(static_cast<utf8*>(Name.data()), false);
}
void NetworkPlayer::Read(NetworkPacket& packet)

View File

@ -3006,7 +3006,6 @@ 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);
utf8_remove_format_codes(asUtf8.data(), /*allow colour*/ false);
return asUtf8.data();
}

View File

@ -1371,7 +1371,7 @@ void S6Exporter::ExportBanner(RCT12Banner& dst, const Banner& src)
if (!(src.flags & BANNER_FLAG_IS_WALL) && !(src.flags & BANNER_FLAG_IS_LARGE_SCENERY))
{
char codeBuffer[32]{};
utf8_write_codepoint(codeBuffer, FORMAT_COLOUR_CODE_START + src.text_colour);
utf8_write_codepoint(codeBuffer, 142 + src.text_colour);
bannerText = codeBuffer + bannerText;
}

View File

@ -187,6 +187,18 @@ public:
return false;
}
static bool IsLikelyUtf8(const std::string_view s)
{
for (auto c : s)
{
if (static_cast<uint8_t>(c) >= 128)
{
return true;
}
}
return false;
}
void Import() override
{
Initialise();
@ -195,15 +207,7 @@ public:
gS6Info = _s6.info;
// Some scenarios have their scenario details in UTF-8, due to earlier bugs in OpenRCT2.
// This is hard to detect. Therefore, consider invalid characters like colour codes as a sign the text is in UTF-8.
bool alreadyInUTF8 = false;
if (String::ContainsColourCode(_s6.info.name) || String::ContainsColourCode(_s6.info.details))
{
alreadyInUTF8 = true;
}
if (!alreadyInUTF8)
if (!IsLikelyUtf8(_s6.info.name) && !IsLikelyUtf8(_s6.info.details))
{
auto temp = rct2_to_utf8(_s6.info.name, RCT2_LANGUAGE_ID_ENGLISH_UK);
safe_strcpy(gS6Info.name, temp.data(), sizeof(gS6Info.name));
@ -1668,7 +1672,6 @@ 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);
utf8_remove_format_codes(asUtf8.data(), /*allow colour*/ false);
return asUtf8.data();
}

View File

@ -261,7 +261,7 @@ private:
rct_s6_info info = chunkReader.ReadChunkAs<rct_s6_info>();
// If the name or the details contain a colour code, they might be in UTF-8 already.
// This is caused by a bug that was in OpenRCT2 for 3 years.
if (!String::ContainsColourCode(info.name) && !String::ContainsColourCode(info.details))
if (!IsLikelyUtf8(info.name) && !IsLikelyUtf8(info.details))
{
rct2_to_utf8_self(info.name, sizeof(info.name));
rct2_to_utf8_self(info.details, sizeof(info.details));
@ -283,6 +283,18 @@ private:
return false;
}
static bool IsLikelyUtf8(const std::string_view s)
{
for (auto c : s)
{
if (static_cast<uint8_t>(c) >= 128)
{
return true;
}
}
return false;
}
static scenario_index_entry CreateNewScenarioEntry(const std::string& path, uint64_t timestamp, rct_s6_info* s6Info)
{
scenario_index_entry entry = {};

View File

@ -63,7 +63,7 @@ namespace OpenRCT2::Scripting
result.MonthYear = value["month"].as_int();
result.Day = value["day"].as_int();
auto text = language_convert_string(value["text"].as_string());
auto text = value["text"].as_string();
String::Set(result.Text, sizeof(result.Text), text.c_str());
return result;
}
@ -207,7 +207,7 @@ namespace OpenRCT2::Scripting
auto msg = GetMessage();
if (msg != nullptr)
{
return language_convert_string_to_tokens(msg->Text);
return msg->Text;
}
return 0;
}
@ -218,8 +218,7 @@ namespace OpenRCT2::Scripting
auto msg = GetMessage();
if (msg != nullptr)
{
auto text = language_convert_string(value);
String::Set(msg->Text, sizeof(msg->Text), text.c_str());
String::Set(msg->Text, sizeof(msg->Text), value.c_str());
}
}
@ -395,12 +394,12 @@ namespace OpenRCT2::Scripting
std::string text;
if (message.type() == DukValue::Type::STRING)
{
text = language_convert_string(message.as_string());
text = message.as_string();
}
else
{
type = GetParkMessageType(message["type"].as_string());
text = language_convert_string(message["text"].as_string());
text = message["text"].as_string();
if (type == News::ItemType::Blank)
{
assoc = static_cast<uint32_t>(((COORDS_NULL & 0xFFFF) << 16) | (COORDS_NULL & 0xFFFF));

View File

@ -1267,7 +1267,7 @@ std::string OpenRCT2::Scripting::Stringify(const DukValue& val)
std::string OpenRCT2::Scripting::ProcessString(const DukValue& value)
{
if (value.type() == DukValue::Type::STRING)
return language_convert_string(value.as_string());
return value.as_string();
return {};
}

View File

@ -429,17 +429,13 @@ bool title_is_previewing_sequence()
void DrawOpenRCT2(rct_drawpixelinfo* dpi, const ScreenCoordsXY& screenCoords)
{
utf8 buffer[256];
// Write format codes
utf8* ch = buffer;
ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT);
ch = utf8_write_codepoint(ch, FORMAT_OUTLINE);
ch = utf8_write_codepoint(ch, FORMAT_WHITE);
thread_local std::string buffer;
buffer.clear();
buffer.assign("{MEDIUMFONT}{OUTLINE}{WHITE}");
// Write name and version information
openrct2_write_full_version_info(ch, sizeof(buffer) - (ch - buffer));
gfx_draw_string(dpi, buffer, COLOUR_BLACK, screenCoords + ScreenCoordsXY(5, 5 - 13));
buffer += gVersionInfoFull;
gfx_draw_string(dpi, buffer.c_str(), COLOUR_BLACK, screenCoords + ScreenCoordsXY(5, 5 - 13));
// Invalidate screen area
int16_t width = static_cast<int16_t>(gfx_get_string_width(buffer));
@ -447,6 +443,9 @@ void DrawOpenRCT2(rct_drawpixelinfo* dpi, const ScreenCoordsXY& screenCoords)
{ screenCoords, screenCoords + ScreenCoordsXY{ width, 30 } }); // 30 is an arbitrary height to catch both strings
// Write platform information
snprintf(ch, 256 - (ch - buffer), "%s (%s)", OPENRCT2_PLATFORM, OPENRCT2_ARCHITECTURE);
gfx_draw_string(dpi, buffer, COLOUR_BLACK, screenCoords + ScreenCoordsXY(5, 5));
buffer.assign(OPENRCT2_PLATFORM);
buffer.append(" (");
buffer.append(OPENRCT2_ARCHITECTURE);
buffer.append(")");
gfx_draw_string(dpi, buffer.c_str(), COLOUR_BLACK, screenCoords + ScreenCoordsXY(5, 5));
}

View File

@ -33,30 +33,6 @@
static Banner _banners[MAX_BANNERS];
namespace
{
template<uint32_t TFrom, uint32_t TTo> struct CodePointToUtf8
{
constexpr CodePointToUtf8()
{
for (uint32_t i = TFrom; i <= TTo; ++i)
{
utf8_write_codepoint(m_colors[i - TFrom], i);
}
}
constexpr auto operator()(uint8_t colourId) const
{
return m_colors[colourId];
}
using Utf8Colour = utf8[5]; // A 32bit codepoint uses at most 4 bytes in utf8
Utf8Colour m_colors[TTo - TFrom + 1]{};
};
} // namespace
static constexpr CodePointToUtf8<FORMAT_COLOUR_CODE_START, FORMAT_COLOUR_CODE_END> colourToUtf8;
std::string Banner::GetText() const
{
Formatter ft;
@ -68,7 +44,10 @@ void Banner::FormatTextTo(Formatter& ft, bool addColour) const
{
if (addColour)
{
ft.Add<rct_string_id>(STR_STRING_STRINGID).Add<const char*>(colourToUtf8(text_colour));
auto formatToken = FormatTokenFromTextColour(text_colour);
auto tokenText = FormatTokenToString(formatToken);
ft.Add<rct_string_id>(STR_STRING_STRINGID);
ft.Add<const char*>(tokenText.data());
}
FormatTextTo(ft);