diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj
index 3108569a3c..0a6c0becc1 100644
--- a/projects/openrct2.vcxproj
+++ b/projects/openrct2.vcxproj
@@ -67,6 +67,7 @@
+
diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters
index a714ad89d1..cfc5b0b556 100644
--- a/projects/openrct2.vcxproj.filters
+++ b/projects/openrct2.vcxproj.filters
@@ -504,6 +504,9 @@
Source\Drawing
+
+ Source\Localisation
+
diff --git a/src/drawing/scrolling_text.c b/src/drawing/scrolling_text.c
index 378eca1b4c..e04d40fca4 100644
--- a/src/drawing/scrolling_text.c
+++ b/src/drawing/scrolling_text.c
@@ -210,7 +210,7 @@ void scrolling_text_set_bitmap_for_ttf(utf8 *text, int scroll, uint8 *bitmap, si
int codepoint;
while ((codepoint = utf8_get_next(ch, &ch)) != 0) {
if (utf8_is_format_code(codepoint)) {
- if (colour == 0 && codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END) {
+ if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END) {
colour = (uint8)codepoint;
}
} else {
diff --git a/src/drawing/string.c b/src/drawing/string.c
index 30770addfa..fb1337b814 100644
--- a/src/drawing/string.c
+++ b/src/drawing/string.c
@@ -200,10 +200,7 @@ int gfx_wrap_string(utf8 *text, int width, int *outNumLines, int *outFontHeight)
ch = lastCh;
} else if (currentWord == NULL) {
// Single word is longer than line, insert null terminator
- utf8 *end = get_string_end(ch);
- memmove(ch + 1, ch, end - ch + 1);
- *ch++ = 0;
-
+ ch += utf8_insert_codepoint(ch, 0);
maxWidth = max(maxWidth, lineWidth);
(*outNumLines)++;
lineWidth = 0;
diff --git a/src/localisation/language.c b/src/localisation/language.c
index 022c93ee18..f933eef302 100644
--- a/src/localisation/language.c
+++ b/src/localisation/language.c
@@ -86,64 +86,6 @@ const utf8 CheckBoxMarkString[] = { 0xE2, 0x9C, 0x93, 0x00 };
static int language_open_file(const char *filename, language_data *language);
static void language_close(language_data *language);
-uint32 utf8_get_next(const utf8 *char_ptr, const utf8 **nextchar_ptr)
-{
- int result;
- int numBytes;
-
- if (!(char_ptr[0] & 0x80)) {
- result = char_ptr[0];
- numBytes = 1;
- } else if ((char_ptr[0] & 0xE0) == 0xC0) {
- result = ((char_ptr[0] & 0x1F) << 6) | (char_ptr[1] & 0x3F);
- numBytes = 2;
- } else if ((char_ptr[0] & 0xF0) == 0xE0) {
- result = ((char_ptr[0] & 0x0F) << 12) | ((char_ptr[1] & 0x3F) << 6) | (char_ptr[2] & 0x3F);
- numBytes = 3;
- } else if ((char_ptr[0] & 0xF8) == 0xF0) {
- result = ((char_ptr[0] & 0x07) << 18) | ((char_ptr[1] & 0x3F) << 12) | ((char_ptr[1] & 0x3F) << 6) | (char_ptr[2] & 0x3F);
- numBytes = 4;
- } else {
- // TODO 4 bytes
- result = ' ';
- numBytes = 1;
- }
-
- if (nextchar_ptr != NULL)
- *nextchar_ptr = char_ptr + numBytes;
- return result;
-}
-
-utf8 *utf8_write_codepoint(utf8 *dst, uint32 codepoint)
-{
- if (codepoint <= 0x7F) {
- dst[0] = (utf8)codepoint;
- return dst + 1;
- } else if (codepoint <= 0x7FF) {
- dst[0] = 0xC0 | ((codepoint >> 6) & 0x1F);
- dst[1] = 0x80 | (codepoint & 0x3F);
- return dst + 2;
- } else if (codepoint <= 0xFFFF) {
- dst[0] = 0xE0 | ((codepoint >> 12) & 0x0F);
- dst[1] = 0x80 | ((codepoint >> 6) & 0x3F);
- dst[2] = 0x80 | (codepoint & 0x3F);
- return dst + 3;
- } else {
- dst[0] = 0xF0 | ((codepoint >> 18) & 0x07);
- dst[1] = 0x80 | ((codepoint >> 12) & 0x3F);
- dst[2] = 0x80 | ((codepoint >> 6) & 0x3F);
- dst[3] = 0x80 | (codepoint & 0x3F);
- return dst + 4;
- }
-}
-
-bool utf8_is_codepoint_start(utf8 *text)
-{
- if ((text[0] & 0x80) == 0) return true;
- if ((text[0] & 0xC0) == 0xC0) return true;
- return false;
-}
-
void utf8_remove_format_codes(utf8 *text)
{
utf8 *dstCh = text;
@@ -157,19 +99,6 @@ void utf8_remove_format_codes(utf8 *text)
*dstCh = 0;
}
-int utf8_get_codepoint_length(int codepoint)
-{
- if (codepoint <= 0x7F) {
- return 1;
- } else if (codepoint <= 0x7FF) {
- return 2;
- } else if (codepoint <= 0xFFFF) {
- return 3;
- } else {
- return 4;
- }
-}
-
const char *language_get_string(rct_string_id id)
{
const char *openrctString = NULL;
diff --git a/src/localisation/language.h b/src/localisation/language.h
index 69358349bd..6f84cf6471 100644
--- a/src/localisation/language.h
+++ b/src/localisation/language.h
@@ -72,6 +72,7 @@ rct_string_id object_get_localised_text(uint8_t** pStringTable/*ebp*/, int type/
uint32 utf8_get_next(const utf8 *char_ptr, const utf8 **nextchar_ptr);
utf8 *utf8_write_codepoint(utf8 *dst, uint32 codepoint);
+int utf8_insert_codepoint(utf8 *dst, uint32 codepoint);
bool utf8_is_codepoint_start(utf8 *text);
void utf8_remove_format_codes(utf8 *text);
int utf8_get_codepoint_length(int codepoint);
diff --git a/src/localisation/utf8.c b/src/localisation/utf8.c
new file mode 100644
index 0000000000..fc9e1ce60a
--- /dev/null
+++ b/src/localisation/utf8.c
@@ -0,0 +1,85 @@
+#include "localisation.h"
+
+uint32 utf8_get_next(const utf8 *char_ptr, const utf8 **nextchar_ptr)
+{
+ int result;
+ int numBytes;
+
+ if (!(char_ptr[0] & 0x80)) {
+ result = char_ptr[0];
+ numBytes = 1;
+ } else if ((char_ptr[0] & 0xE0) == 0xC0) {
+ result = ((char_ptr[0] & 0x1F) << 6) | (char_ptr[1] & 0x3F);
+ numBytes = 2;
+ } else if ((char_ptr[0] & 0xF0) == 0xE0) {
+ result = ((char_ptr[0] & 0x0F) << 12) | ((char_ptr[1] & 0x3F) << 6) | (char_ptr[2] & 0x3F);
+ numBytes = 3;
+ } else if ((char_ptr[0] & 0xF8) == 0xF0) {
+ result = ((char_ptr[0] & 0x07) << 18) | ((char_ptr[1] & 0x3F) << 12) | ((char_ptr[1] & 0x3F) << 6) | (char_ptr[2] & 0x3F);
+ numBytes = 4;
+ } else {
+ // TODO 4 bytes
+ result = ' ';
+ numBytes = 1;
+ }
+
+ if (nextchar_ptr != NULL)
+ *nextchar_ptr = char_ptr + numBytes;
+ return result;
+}
+
+utf8 *utf8_write_codepoint(utf8 *dst, uint32 codepoint)
+{
+ if (codepoint <= 0x7F) {
+ dst[0] = (utf8)codepoint;
+ return dst + 1;
+ } else if (codepoint <= 0x7FF) {
+ dst[0] = 0xC0 | ((codepoint >> 6) & 0x1F);
+ dst[1] = 0x80 | (codepoint & 0x3F);
+ return dst + 2;
+ } else if (codepoint <= 0xFFFF) {
+ dst[0] = 0xE0 | ((codepoint >> 12) & 0x0F);
+ dst[1] = 0x80 | ((codepoint >> 6) & 0x3F);
+ dst[2] = 0x80 | (codepoint & 0x3F);
+ return dst + 3;
+ } else {
+ dst[0] = 0xF0 | ((codepoint >> 18) & 0x07);
+ dst[1] = 0x80 | ((codepoint >> 12) & 0x3F);
+ dst[2] = 0x80 | ((codepoint >> 6) & 0x3F);
+ dst[3] = 0x80 | (codepoint & 0x3F);
+ return dst + 4;
+ }
+}
+
+/**
+ * Inserts the given codepoint at the given address, shifting all characters after along.
+ * @returns the size of the inserted codepoint.
+ */
+int utf8_insert_codepoint(utf8 *dst, uint32 codepoint)
+{
+ int shift = utf8_get_codepoint_length(codepoint);
+ utf8 *endPoint = get_string_end(dst);
+ memmove(dst + shift, dst, endPoint - dst + 1);
+ utf8_write_codepoint(dst, codepoint);
+ return shift;
+}
+
+bool utf8_is_codepoint_start(utf8 *text)
+{
+ if ((text[0] & 0x80) == 0) return true;
+ if ((text[0] & 0xC0) == 0xC0) return true;
+ return false;
+}
+
+int utf8_get_codepoint_length(int codepoint)
+{
+ if (codepoint <= 0x7F) {
+ return 1;
+ } else if (codepoint <= 0x7FF) {
+ return 2;
+ } else if (codepoint <= 0xFFFF) {
+ return 3;
+ } else {
+ return 4;
+ }
+}
diff --git a/src/openrct2.c b/src/openrct2.c
index 04a2babcc9..8b8f21ee83 100644
--- a/src/openrct2.c
+++ b/src/openrct2.c
@@ -195,6 +195,7 @@ bool openrct2_initialise()
addhook(0x006BB76E, (int)sound_play_panned, 0, (int[]){EAX, EBX, ECX, EDX, EBP, END}, EAX, 0); // remove when all callers are decompiled
addhook(0x006C42D9, (int)scrolling_text_setup, 0, (int[]){EAX, ECX, EBP, END}, 0, EBX); // remove when all callers are decompiled
addhook(0x006C2321, (int)gfx_get_string_width, 0, (int[]){ESI, END}, 0, ECX); // remove when all callers are decompiled
+ addhook(0x006C2321, (int)format_string, 0, (int[]){EDI, EAX, ECX, END}, 0, 0); // remove when all callers are decompiled
if (!rct2_init())
return false;
diff --git a/src/windows/banner.c b/src/windows/banner.c
index 251d0dd9c8..9926914055 100644
--- a/src/windows/banner.c
+++ b/src/windows/banner.c
@@ -265,17 +265,14 @@ static void window_banner_dropdown(rct_window *w, int widgetIndex, int dropdownI
int colourCodepoint = FORMAT_COLOUR_CODE_START + banner->text_colour;
- // Can be replaced with a buffer 34 chars wide ( 32 character + 1 colour_format + 1 '\0')
- uint8* buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, uint8);
+ uint8 buffer[256];
format_string(buffer, banner->string_idx, 0);
-
int firstCodepoint = utf8_get_next(buffer, NULL);
- if (!(firstCodepoint >= FORMAT_COLOUR_CODE_START && firstCodepoint <= FORMAT_COLOUR_CODE_END)) {
- utf8 *endPoint = get_string_end(buffer) + utf8_get_codepoint_length(colourCodepoint);
- memmove(buffer + utf8_get_codepoint_length(colourCodepoint), buffer, endPoint - buffer);
- *endPoint = 0;
+ if (firstCodepoint >= FORMAT_COLOUR_CODE_START && firstCodepoint <= FORMAT_COLOUR_CODE_END) {
+ utf8_write_codepoint(buffer, colourCodepoint);
+ } else {
+ utf8_insert_codepoint(buffer, colourCodepoint);
}
- utf8_write_codepoint(buffer, colourCodepoint);
rct_string_id stringId = user_string_allocate(128, buffer);
if (stringId != 0) {