diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj
index efe3e09bcc..495391fc30 100644
--- a/projects/openrct2.vcxproj
+++ b/projects/openrct2.vcxproj
@@ -36,6 +36,7 @@
+
diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters
index 5a8f3f4179..0c74f0bb5e 100644
--- a/projects/openrct2.vcxproj.filters
+++ b/projects/openrct2.vcxproj.filters
@@ -498,6 +498,9 @@
Source\Localisation
+
+ Source\Drawing
+
diff --git a/src/drawing/drawing.h b/src/drawing/drawing.h
index 9ed88c96f1..cf0450d3d2 100644
--- a/src/drawing/drawing.h
+++ b/src/drawing/drawing.h
@@ -144,4 +144,8 @@ void redraw_rain();
// unknown
void sub_681DE2(rct_drawpixelinfo *dpi, int x, int y, int image1, int image2);
+// scrolling text
+void scrolling_text_initialise_bitmaps();
+int scrolling_text_setup(rct_string_id stringId, uint16 scroll, uint16 scrollingMode);
+
#endif
diff --git a/src/drawing/scrolling_text.c b/src/drawing/scrolling_text.c
new file mode 100644
index 0000000000..188defc574
--- /dev/null
+++ b/src/drawing/scrolling_text.c
@@ -0,0 +1,161 @@
+#include "../addresses.h"
+#include "../config.h"
+#include "../localisation/localisation.h"
+#include "drawing.h"
+
+/* size: 0xA12 */
+typedef struct {
+ rct_string_id string_id; // 0x00
+ uint32 string_args_0; // 0x02
+ uint32 string_args_1; // 0x06
+ uint16 position; // 0x0A
+ uint16 mode; // 0x0C
+ uint32 id; // 0x0E
+ uint8 bitmap[64 * 8 * 5]; // 0x12
+} rct_draw_scroll_text;
+
+rct_draw_scroll_text *gDrawScrollTextList = RCT2_ADDRESS(RCT2_ADDRESS_DRAW_SCROLL_LIST, rct_draw_scroll_text);
+
+void scrolling_text_initialise_bitmaps()
+{
+ uint8 drawingSurface[64];
+ rct_drawpixelinfo dpi = {
+ .bits = (char*)&drawingSurface,
+ .x = 0,
+ .y = 0,
+ .width = 8,
+ .height = 8,
+ .pitch = 0,
+ .zoom_level = 0
+ };
+
+
+ for (int i = 0; i < 224; i++) {
+ memset(drawingSurface, 0, sizeof(drawingSurface));
+ gfx_draw_sprite(&dpi, i + 0x10D5, -1, 0, 0);
+
+ for (int x = 0; x < 8; x++) {
+ uint8 val = 0;
+ for (int y = 0; y < 8; y++) {
+ val >>= 1;
+ if (dpi.bits[x + y * 8] == 1) {
+ val |= 0x80;
+ }
+ }
+ RCT2_ADDRESS(RCT2_ADDRESS_CHARACTER_BITMAP, uint8)[i * 8 + x] = val;
+ }
+
+ }
+}
+
+/**
+ *
+ * rct2: 0x006C42D9
+ */
+int scrolling_text_setup(rct_string_id stringId, uint16 scroll, uint16 scrollingMode)
+{
+ rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*);
+
+ if (dpi->zoom_level != 0) return 0x626;
+
+ RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32)++;
+
+ // Find the oldest scroll for use as the newest
+ uint32 oldestId = 0xFFFFFFFF;
+ uint8 scrollIndex = 0xFF;
+ rct_draw_scroll_text* oldestScroll = NULL;
+ for (int i = 0; i < 32; i++) {
+ rct_draw_scroll_text *scrollText = &gDrawScrollTextList[i];
+ if (oldestId >= scrollText->id) {
+ oldestId = scrollText->id;
+ scrollIndex = i;
+ oldestScroll = scrollText;
+ }
+
+ // If exact match return the matching index
+ if (
+ scrollText->string_id == stringId &&
+ scrollText->string_args_0 == RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32) &&
+ scrollText->string_args_1 == RCT2_GLOBAL(0x13CE956, uint32) &&
+ scrollText->position == scroll &&
+ scrollText->mode == scrollingMode
+ ) {
+ scrollText->id = RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32);
+ return i + 0x606;
+ }
+ }
+
+ // Setup scrolling text
+ rct_draw_scroll_text* scrollText = oldestScroll;
+ scrollText->string_id = stringId;
+ scrollText->string_args_0 = RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32);
+ scrollText->string_args_1 = RCT2_GLOBAL(0x13CE956, uint32);
+ scrollText->position = scroll;
+ scrollText->mode = scrollingMode;
+ scrollText->id = RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32_t);
+
+ uint8* scrollPixelPointer = scrollText->bitmap;
+ memset(scrollPixelPointer, 0, 320 * 8);
+
+ // Convert string id back into a string for processing
+ utf8 scrollString[MAX_PATH];
+ if (gConfigGeneral.upper_case_banners)
+ format_string_to_upper(scrollString, stringId, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
+ else
+ format_string(scrollString, stringId, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
+
+ // Setup character colour from ???
+ uint32 character = RCT2_GLOBAL(0x13CE959, uint8);
+ int edi = character & 0x7F;
+ int offs = 0;
+ if (character >= 0x80) offs = 2;
+ uint8 characterColour = RCT2_ADDRESS(0x0141FC47, uint8)[offs + edi * 8];
+
+ sint16* scrollingModePositions = RCT2_ADDRESS(RCT2_ADDRESS_SCROLLING_MODE_POSITIONS, uint16_t*)[scrollingMode];
+ uint8* formatResult = scrollString;
+ while (true) {
+ character = utf8_get_next(formatResult, &formatResult);
+
+ // If at the end of the string loop back to the start
+ if (character == 0) {
+ formatResult = scrollString;
+ continue;
+ }
+
+ // Set any change in colour
+ if (character <= FORMAT_COLOUR_CODE_END && character >= FORMAT_COLOUR_CODE_START){
+ character -= FORMAT_COLOUR_CODE_START;
+ characterColour = RCT2_GLOBAL(0x009FF048, uint8*)[character * 4];
+ continue;
+ }
+
+ // If another type of control character ignore
+ if (character < 32) continue;
+
+ // Convert to an indexable character
+ character = utf8_get_sprite_offset_for_codepoint(character);
+
+ uint8 characterWidth = RCT2_ADDRESS(RCT2_ADDRESS_FONT_CHAR_WIDTH + 448, uint8)[character];
+ uint8* characterBitmap = &(RCT2_ADDRESS(RCT2_ADDRESS_CHARACTER_BITMAP, uint8)[character * 8]);
+ for (; characterWidth != 0; characterWidth--, characterBitmap++) {
+ // Skip any none displayed columns
+ if (scroll != 0){
+ scroll--;
+ continue;
+ }
+
+ sint16 scrollPosition = *scrollingModePositions;
+ if (scrollPosition == -1) return scrollIndex + 0x606;
+ if (scrollPosition > -1) {
+ uint8* dst = &scrollPixelPointer[scrollPosition];
+ for (uint8 char_bitmap = *characterBitmap; char_bitmap != 0; char_bitmap >>= 1){
+ if (char_bitmap & 1) *dst = characterColour;
+
+ // Jump to next row
+ dst += 64;
+ }
+ }
+ scrollingModePositions++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/drawing/string.c b/src/drawing/string.c
index 8c5a1f78e9..46c0e56b46 100644
--- a/src/drawing/string.c
+++ b/src/drawing/string.c
@@ -35,6 +35,16 @@ static int _ttfFontOffsetY = 0;
static const int TTFFontSizes[] = { 7, 9, 11, 13 };
enum {
+ FONT_SIZE_TINY = 2,
+ FONT_SIZE_SMALL = 0,
+ FONT_SIZE_MEDIUM = 1,
+ FONT_SIZE_BIG = 3,
+ FONT_SIZE_COUNT = 4
+};
+
+enum {
+ FONT_SPRITE_GLYPH_COUNT = 224,
+
FONT_SPRITE_BASE_TINY = 448,
FONT_SPRITE_BASE_SMALL = 0,
FONT_SPRITE_BASE_MEDIUM = 224,
@@ -47,59 +57,30 @@ static uint8 *_spriteFontCharacterWidths = (uint8*)RCT2_ADDRESS_FONT_CHAR_WIDTH;
*
* rct2: 0x006C19AC
*/
-void gfx_load_character_widths(){
-
- uint8* char_width_pointer = _spriteFontCharacterWidths;
- for (int char_set_offset = 0; char_set_offset < 4*0xE0; char_set_offset+=0xE0){
- for (uint8 c = 0; c < 0xE0; c++, char_width_pointer++){
- rct_g1_element g1 = g1Elements[c + SPR_CHAR_START + char_set_offset];
- int width;
-
- if (char_set_offset == 0xE0*3) width = g1.width + 1;
- else width = g1.width - 1;
-
- if (c >= (FORMAT_ARGUMENT_CODE_START - 0x20) && c < (FORMAT_COLOUR_CODE_END - 0x20)){
+void gfx_load_character_widths()
+{
+ uint8* pCharacterWidth = _spriteFontCharacterWidths;
+ for (int fontSize = 0; fontSize < FONT_SIZE_COUNT; fontSize++) {
+ int glyphOffset = fontSize * FONT_SPRITE_GLYPH_COUNT;
+ for (uint8 glyphIndex = 0; glyphIndex < FONT_SPRITE_GLYPH_COUNT; glyphIndex++) {
+ rct_g1_element g1 = g1Elements[glyphIndex + SPR_CHAR_START + glyphOffset];
+
+ int width = fontSize == FONT_SIZE_BIG ? g1.width + 1 : g1.width - 1;
+ if (glyphIndex >= (FORMAT_ARGUMENT_CODE_START - 32) && glyphIndex < (FORMAT_COLOUR_CODE_END - 32)) {
width = 0;
}
- *char_width_pointer = (uint8)width;
+ *pCharacterWidth++ = (uint8)width;
}
-
}
- uint8 drawing_surface[0x40];
- rct_drawpixelinfo dpi = {
- .bits = (char*)&drawing_surface,
- .width = 8,
- .height = 8,
- .x = 0,
- .y = 0,
- .pitch = 0,
- .zoom_level = 0};
+ scrolling_text_initialise_bitmaps();
-
- for (int i = 0; i < 0xE0; ++i){
- memset(drawing_surface, 0, sizeof(drawing_surface));
- gfx_draw_sprite(&dpi, i + 0x10D5, -1, 0, 0);
-
- for (int x = 0; x < 8; ++x){
- uint8 val = 0;
- for (int y = 0; y < 8; ++y){
- val >>= 1;
- if (dpi.bits[x + y * 8]==1){
- val |= 0x80;
- }
- }
- RCT2_ADDRESS(RCT2_ADDRESS_CHARACTER_BITMAP, uint8)[i * 8 + x] = val;
- }
-
- }
-
- for (int i = 0; i < 0x20; ++i){
+ for (int i = 0; i < 32; i++) {
rct_g1_element* g1 = &g1Elements[0x606 + i];
- uint8* unknown_pointer = RCT2_ADDRESS(0x9C3852, uint8) + 0xa12 * i;
+ uint8* unknown_pointer = RCT2_ADDRESS(0x009C3852, uint8) + 0xA12 * i;
g1->offset = unknown_pointer;
- g1->width = 0x40;
- g1->height = 0x28;
+ g1->width = 64;
+ g1->height = 40;
*((uint16*)unknown_pointer) = 0xFFFF;
*((uint32*)(unknown_pointer + 0x0E)) = 0;
}
@@ -782,7 +763,7 @@ bool ttf_initialise()
}
_ttfFontOffsetX = 1;
- _ttfFontOffsetY = -3;
+ _ttfFontOffsetY = -2;
_ttfInitialised = true;
}
return true;
@@ -805,6 +786,7 @@ void ttf_dispose()
}
enum {
+ TEXT_DRAW_FLAG_INSET = 1 << 0,
TEXT_DRAW_FLAG_OUTLINE = 1 << 1,
TEXT_DRAW_FLAG_TTF = 1 << 30,
TEXT_DRAW_FLAG_NO_DRAW = 1 << 31
@@ -920,8 +902,10 @@ static void ttf_draw_string_raw_ttf(rct_drawpixelinfo *dpi, const utf8 *text, te
for (int xx = 0; xx < width; xx++) {
if (*src != 0) {
*dst = colour;
- if (info->flags & TEXT_DRAW_FLAG_OUTLINE) {
- *(dst + width + dstScanSkip + 1) = 0;
+ if (info->flags & TEXT_DRAW_FLAG_INSET) {
+ *(dst + width + dstScanSkip + 1) = info->palette[3];
+ } else if (info->flags & TEXT_DRAW_FLAG_OUTLINE) {
+ *(dst + width + dstScanSkip + 1) = info->palette[3];
}
}
src++;
@@ -953,7 +937,7 @@ static const utf8 *ttf_process_format_code(rct_drawpixelinfo *dpi, const utf8 *t
codepoint = utf8_get_next(text, &nextCh);
switch (codepoint) {
case FORMAT_MOVE_X:
- info->x = info->startX + *nextCh++;
+ info->x = info->startX + (uint8)(*nextCh++);
break;
case FORMAT_ADJUST_PALETTE:
{
diff --git a/src/interface/viewport.c b/src/interface/viewport.c
index 90771a2bb2..ef6475f160 100644
--- a/src/interface/viewport.c
+++ b/src/interface/viewport.c
@@ -990,130 +990,6 @@ void sprite_paint_setup(uint16 eax, uint16 ecx){
}
}
-/* size: 0xA12 */
-typedef struct{
- rct_string_id string_id; // 0x00
- uint32 string_args_0; // 0x02
- uint32 string_args_1; // 0x06
- uint16 position; // 0x0A
- uint16 mode; // 0x0C
- uint32 id; // 0x0E
- uint8 bitmap[64 * 8 * 5];// 0x12
-}rct_draw_scroll_text;
-
-/*rct2: 0x006C42D9*/
-int scrolling_text_setup(rct_string_id string_id, uint16 scroll, uint16 scrolling_mode)
-{
- rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*);
-
- if (dpi->zoom_level != 0) return 0x626;
-
- RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32)++;
-
- uint32 oldest_id = 0xFFFFFFFF;
- uint8 scroll_index = 0xFF;
- rct_draw_scroll_text* oldest_scroll = NULL;
-
- // Find the oldest scroll for use as the newest
- for (int i = 0; i < 32; i++)
- {
- rct_draw_scroll_text* scroll_text = &RCT2_ADDRESS(RCT2_ADDRESS_DRAW_SCROLL_LIST, rct_draw_scroll_text)[i];
- if (oldest_id >= scroll_text->id){
- oldest_id = scroll_text->id;
- scroll_index = i;
- oldest_scroll = scroll_text;
- }
-
- // If exact match return the matching index
- if (scroll_text->string_id == string_id &&
- scroll_text->string_args_0 == RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32) &&
- scroll_text->string_args_1 == RCT2_GLOBAL(0x13CE956, uint32) &&
- scroll_text->position == scroll &&
- scroll_text->mode == scrolling_mode){
-
- scroll_text->id = RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32);
- return i + 0x606;
- }
- }
-
- // Setup scrolling text
-
- rct_draw_scroll_text* scroll_text = oldest_scroll;
- scroll_text->string_id = string_id;
- scroll_text->string_args_0 = RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32);
- scroll_text->string_args_1 = RCT2_GLOBAL(0x13CE956, uint32);
- scroll_text->position = scroll;
- scroll_text->mode = scrolling_mode;
- scroll_text->id = RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32_t);
-
- uint8* scroll_pixel_pointer = scroll_text->bitmap;
- memset(scroll_pixel_pointer, 0, 320 * 8);
-
- // Convert string id back into a string for processing
- uint8 scroll_string[MAX_PATH];
- if(gConfigGeneral.upper_case_banners)
- format_string_to_upper(scroll_string, string_id, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
- else
- format_string(scroll_string, string_id, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
-
- // Setup character colour from ???
- uint8 character = RCT2_GLOBAL(0x13CE959, uint8);
- int edi = character & 0x7F;
- int offs = 0;
- if (character >= 0x80) offs = 2;
- uint8 character_colour = RCT2_ADDRESS(0x0141FC47, uint8)[offs + edi * 8];
-
- sint16* scrolling_mode_positions = RCT2_ADDRESS(RCT2_ADDRESS_SCROLLING_MODE_POSITIONS, uint16_t*)[scrolling_mode];
- uint8* format_result = scroll_string;
- while (true)
- {
- character = *format_result++;
-
- // If at the end of the string loop back to the start
- if (character == 0){
- format_result = scroll_string;
- continue;
- }
-
- // Set any change in colour
- if (character <= FORMAT_COLOUR_CODE_END && character >= FORMAT_COLOUR_CODE_START){
- character -= FORMAT_COLOUR_CODE_START;
- character_colour = RCT2_GLOBAL(0x9FF048, uint8*)[character * 4];
- continue;
- }
-
- // If another type of control character ignore
- if (character < 0x20) continue;
-
- // Convert to an indexable character
- character -= 0x20;
-
- uint8 character_width = RCT2_ADDRESS(RCT2_ADDRESS_FONT_CHAR_WIDTH + 0x1C0, uint8)[character];
- uint8* character_bitmap = &(RCT2_ADDRESS(RCT2_ADDRESS_CHARACTER_BITMAP, uint8)[character * 8]);
- for (; character_width != 0;character_width--,character_bitmap++)
- {
- // Skip any none displayed columns
- if (scroll != 0){
- scroll--;
- continue;
- }
-
- sint16 scroll_position = *scrolling_mode_positions;
- if (scroll_position == -1) return scroll_index + 0x606;
- if (scroll_position > -1)
- {
- uint8* dst = &scroll_pixel_pointer[scroll_position];
- for (uint8 char_bitmap = *character_bitmap; char_bitmap != 0; char_bitmap >>= 1){
- if (char_bitmap & 1) *dst = character_colour;
- // Jump to next row
- dst += 64;
- }
- }
- scrolling_mode_positions++;
- }
- }
-}
-
/* rct2: 0x006629BC
* returns al
* ebp : image_id
diff --git a/src/ride/track.c b/src/ride/track.c
index d8fe740f24..acb55157ec 100644
--- a/src/ride/track.c
+++ b/src/ride/track.c
@@ -3111,20 +3111,24 @@ rct_track_design *temp_track_get_info(char* path, uint8** preview)
return trackDesign;
}
-void window_track_list_format_name(char *dst, const char *src, char colour, char quotes)
+void window_track_list_format_name(utf8 *dst, const utf8 *src, int colour, bool quotes)
{
- if (colour != 0)
- *dst++ = colour;
+ const utf8 *ch;
+ int codepoint;
- if (quotes != 0)
- *dst++ = FORMAT_OPENQUOTES;
-
- while (*src != '.' && *src != 0) {
- *dst++ = *src++;
+ if (colour != 0) {
+ dst = utf8_write_codepoint(dst, colour);
}
- if (quotes != 0)
- *dst++ = FORMAT_ENDQUOTES;
+ if (quotes) dst = utf8_write_codepoint(dst, FORMAT_OPENQUOTES);
+
+ ch = src;
+ while ((codepoint = utf8_get_next(ch, &ch)) != 0) {
+ if (codepoint == '.') break;
+ dst = utf8_write_codepoint(dst, codepoint);
+ }
+
+ if (quotes) dst = utf8_write_codepoint(dst, FORMAT_ENDQUOTES);
*dst = 0;
}
diff --git a/src/ride/track.h b/src/ride/track.h
index 241fad4a65..622aca98b3 100644
--- a/src/ride/track.h
+++ b/src/ride/track.h
@@ -512,7 +512,7 @@ int track_is_connected_by_shape(rct_map_element *a, rct_map_element *b);
int sub_6D01B3(uint8 bl, uint8 rideIndex, int x, int y, int z);
int save_track_design(uint8 rideIndex);
int install_track(char* source_path, char* dest_name);
-void window_track_list_format_name(char *dst, const char *src, char colour, char quotes);
+void window_track_list_format_name(utf8 *dst, const utf8 *src, int colour, bool quotes);
void game_command_place_track_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
void track_save_reset_scenery();
diff --git a/src/windows/error.c b/src/windows/error.c
index 3a1b232952..90576f6b61 100644
--- a/src/windows/error.c
+++ b/src/windows/error.c
@@ -80,7 +80,8 @@ static uint16 _window_error_num_lines;
*/
void window_error_open(rct_string_id title, rct_string_id message)
{
- char *dst, *args;
+ utf8 *dst;
+ char *args;
int numLines, fontHeight, x, y, width, height, maxY;
rct_window *w;
@@ -89,17 +90,17 @@ void window_error_open(rct_string_id title, rct_string_id message)
args = (char*)0x0013CE952;
// Format the title
- *dst++ = FORMAT_BLACK;
+ dst = utf8_write_codepoint(dst, FORMAT_BLACK);
if (title != (rct_string_id)STR_NONE) {
format_string(dst, title, args);
- dst += get_string_size(dst) - 1;
+ dst = get_string_end(dst);
}
// Format the message
if (message != (rct_string_id)STR_NONE) {
- *dst++ = FORMAT_NEWLINE;
+ dst = utf8_write_codepoint(dst, FORMAT_NEWLINE);
format_string(dst, message, args);
- dst += get_string_size(dst) - 1;
+ dst = get_string_end(dst);
}
log_verbose("show error, %s", _window_error_text + 1);