From 6e52462319adbe3698f0675dee770c2bf919e5d6 Mon Sep 17 00:00:00 2001 From: Alex Duchesne Date: Fri, 7 Nov 2025 15:34:31 -0500 Subject: [PATCH] Enable UTF-8 support in FATFS for all targets 9e53e09c113a0dad9cb1fae5029bac667a60b76e --- components/retro-go/rg_gui.c | 6 +-- components/retro-go/rg_utils.c | 38 +++++++++++++++++-- components/retro-go/rg_utils.h | 7 +++- .../retro-go/targets/byteboi-rev1/sdkconfig | 3 ++ .../retro-go/targets/crokpocket/sdkconfig | 3 ++ .../retro-go/targets/esplay-micro/sdkconfig | 3 ++ .../retro-go/targets/fri3d-2024/sdkconfig | 3 ++ .../retro-go/targets/mrgc-g32/sdkconfig | 3 ++ .../retro-go/targets/mrgc-gbm/sdkconfig | 3 ++ .../retro-go/targets/odroid-go/sdkconfig | 3 ++ .../retro-go/targets/rachel-esp32/sdkconfig | 3 ++ .../retro-go/targets/retro-esp32/sdkconfig | 3 ++ .../retro-go/targets/retro-ruler-V1/sdkconfig | 3 ++ .../retro-go/targets/t-deck-plus/sdkconfig | 3 ++ components/retro-go/targets/vmu/sdkconfig | 3 ++ retro-core/main/main_sms.c | 2 +- 16 files changed, 80 insertions(+), 9 deletions(-) diff --git a/components/retro-go/rg_gui.c b/components/retro-go/rg_gui.c index 40102e84..a8ccc267 100644 --- a/components/retro-go/rg_gui.c +++ b/components/retro-go/rg_gui.c @@ -364,7 +364,7 @@ rg_rect_t rg_gui_draw_text(int x_pos, int y_pos, int width, const char *text, // int line_width = padding * 2; for (const char *ptr = text; *ptr;) { - int chr = rg_utf8_get_codepoint(&ptr); + int chr = rg_utf8_decode(&ptr); line_width += monospace ?: get_glyph(NULL, font, font_height, chr); if (chr == '\n' || *ptr == 0) @@ -398,7 +398,7 @@ rg_rect_t rg_gui_draw_text(int x_pos, int y_pos, int width, const char *text, // const char *line = ptr; while (x_offset < draw_width && *line && *line != '\n') { - int chr = rg_utf8_get_codepoint(&line); + int chr = rg_utf8_decode(&line); int width = monospace ?: get_glyph(NULL, font, font_height, chr); if (draw_width - x_offset < width) // Do not truncate glyphs break; @@ -419,7 +419,7 @@ rg_rect_t rg_gui_draw_text(int x_pos, int y_pos, int width, const char *text, // { uint32_t bitmap[font_height]; const char *prev_ptr = ptr; - int glyph_width = get_glyph(bitmap, font, font_height, rg_utf8_get_codepoint(&ptr)); + int glyph_width = get_glyph(bitmap, font, font_height, rg_utf8_decode(&ptr)); int width = monospace ?: glyph_width; if (draw_width - x_offset < width) // Do not truncate glyphs diff --git a/components/retro-go/rg_utils.c b/components/retro-go/rg_utils.c index 4f27ca7f..8e842ef1 100644 --- a/components/retro-go/rg_utils.c +++ b/components/retro-go/rg_utils.c @@ -48,7 +48,7 @@ char *rg_json_fixup(char *json) return json; } -int rg_utf8_get_codepoint(const char **ptr) +int rg_utf8_decode(const char **ptr) { // This implementation is based solely on https://en.wikipedia.org/wiki/UTF-8#Description // It's probably wrong in many ways but I'm sure it'll be good enough for us :) @@ -102,6 +102,38 @@ int rg_utf8_get_codepoint(const char **ptr) return codepoint; } +size_t rg_utf8_encode(char *output, int codepoint) +{ + if (codepoint <= 0x7F) // 1 byte + { + output[0] = codepoint & 0xFF; + return 1; + } + else if (codepoint <= 0x7FF) // 2 bytes + { + output[0] = 0xC0 | ((codepoint >> 6) & 0x1F); + output[1] = 0x80 | (codepoint & 0x3F); + return 2; + } + else if (codepoint <= 0xFFFF) // 3 bytes + { + output[0] = 0xE0 | ((codepoint >> 12) & 0x0F); + output[1] = 0x80 | ((codepoint >> 6) & 0x3F); + output[2] = 0x80 | (codepoint & 0x3F); + return 3; + } + else if (codepoint <= 0x10FFFF) // 4 bytes + { + output[0] = 0xF0 | ((codepoint >> 18) & 0x07); + output[1] = 0x80 | ((codepoint >> 12) & 0x3F); + output[2] = 0x80 | ((codepoint >> 6) & 0x3F); + output[3] = 0x80 | (codepoint & 0x3F); + return 4; + } + RG_LOGD("Out of range codepoint 0x%X", codepoint); + return 0; +} + size_t rg_utf8_strlen(const char *str) { if (!str) @@ -110,8 +142,8 @@ size_t rg_utf8_strlen(const char *str) size_t length = 0; while (*str) { - rg_utf8_get_codepoint(&str); - length++; + if (rg_utf8_decode(&str) > 0) + length++; } return length; } diff --git a/components/retro-go/rg_utils.h b/components/retro-go/rg_utils.h index 221a9385..8d7b5b25 100644 --- a/components/retro-go/rg_utils.h +++ b/components/retro-go/rg_utils.h @@ -55,9 +55,12 @@ char *rg_json_fixup(char *json); /* UTF-8 */ // Parse the next codepoint and advance ptr -int rg_utf8_get_codepoint(const char **ptr); -// Count the codepoints in a string +int rg_utf8_decode(const char **ptr); +// size_t rg_utf8_encode(char **ptr, int codepoint); +size_t rg_utf8_encode(char *ptr, int codepoint); size_t rg_utf8_strlen(const char *str); +// size_t rg_utf8_get_codepoint(const char *str, int *codepoint); +// size_t rg_utf8_put_codepoint(char *str, int codepoint); /* Paths functions */ const char *rg_dirname(const char *path); diff --git a/components/retro-go/targets/byteboi-rev1/sdkconfig b/components/retro-go/targets/byteboi-rev1/sdkconfig index fb931087..0ac18260 100644 --- a/components/retro-go/targets/byteboi-rev1/sdkconfig +++ b/components/retro-go/targets/byteboi-rev1/sdkconfig @@ -100,6 +100,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/crokpocket/sdkconfig b/components/retro-go/targets/crokpocket/sdkconfig index efedb444..eafcfd9c 100644 --- a/components/retro-go/targets/crokpocket/sdkconfig +++ b/components/retro-go/targets/crokpocket/sdkconfig @@ -96,6 +96,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/esplay-micro/sdkconfig b/components/retro-go/targets/esplay-micro/sdkconfig index 3d1b0144..a0c329d6 100644 --- a/components/retro-go/targets/esplay-micro/sdkconfig +++ b/components/retro-go/targets/esplay-micro/sdkconfig @@ -102,6 +102,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/fri3d-2024/sdkconfig b/components/retro-go/targets/fri3d-2024/sdkconfig index 28000ee2..d14594b2 100644 --- a/components/retro-go/targets/fri3d-2024/sdkconfig +++ b/components/retro-go/targets/fri3d-2024/sdkconfig @@ -101,6 +101,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/mrgc-g32/sdkconfig b/components/retro-go/targets/mrgc-g32/sdkconfig index 3d1b0144..a0c329d6 100644 --- a/components/retro-go/targets/mrgc-g32/sdkconfig +++ b/components/retro-go/targets/mrgc-g32/sdkconfig @@ -102,6 +102,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/mrgc-gbm/sdkconfig b/components/retro-go/targets/mrgc-gbm/sdkconfig index 3d1b0144..a0c329d6 100644 --- a/components/retro-go/targets/mrgc-gbm/sdkconfig +++ b/components/retro-go/targets/mrgc-gbm/sdkconfig @@ -102,6 +102,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/odroid-go/sdkconfig b/components/retro-go/targets/odroid-go/sdkconfig index 462a5536..72c27f21 100644 --- a/components/retro-go/targets/odroid-go/sdkconfig +++ b/components/retro-go/targets/odroid-go/sdkconfig @@ -100,6 +100,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/rachel-esp32/sdkconfig b/components/retro-go/targets/rachel-esp32/sdkconfig index e8b79bab..adbba1e0 100644 --- a/components/retro-go/targets/rachel-esp32/sdkconfig +++ b/components/retro-go/targets/rachel-esp32/sdkconfig @@ -116,6 +116,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/retro-esp32/sdkconfig b/components/retro-go/targets/retro-esp32/sdkconfig index befa10d1..700b54dc 100644 --- a/components/retro-go/targets/retro-esp32/sdkconfig +++ b/components/retro-go/targets/retro-esp32/sdkconfig @@ -102,6 +102,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/retro-ruler-V1/sdkconfig b/components/retro-go/targets/retro-ruler-V1/sdkconfig index f806fa3b..1e210f2c 100644 --- a/components/retro-go/targets/retro-ruler-V1/sdkconfig +++ b/components/retro-go/targets/retro-ruler-V1/sdkconfig @@ -102,6 +102,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/t-deck-plus/sdkconfig b/components/retro-go/targets/t-deck-plus/sdkconfig index 27cff363..b77213f3 100644 --- a/components/retro-go/targets/t-deck-plus/sdkconfig +++ b/components/retro-go/targets/t-deck-plus/sdkconfig @@ -96,6 +96,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/components/retro-go/targets/vmu/sdkconfig b/components/retro-go/targets/vmu/sdkconfig index 99e1ec94..f7ecfce4 100644 --- a/components/retro-go/targets/vmu/sdkconfig +++ b/components/retro-go/targets/vmu/sdkconfig @@ -100,6 +100,9 @@ CONFIG_FATFS_LFN_NONE=n CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_LFN_STACK=n CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=n +CONFIG_FATFS_API_ENCODING_UTF_16=n +CONFIG_FATFS_API_ENCODING_UTF_8=y CONFIG_FATFS_USE_FASTSEEK=y # diff --git a/retro-core/main/main_sms.c b/retro-core/main/main_sms.c index d24382f9..2a7d9fc1 100644 --- a/retro-core/main/main_sms.c +++ b/retro-core/main/main_sms.c @@ -48,7 +48,7 @@ static bool save_state_handler(const char *filename) static bool load_state_handler(const char *filename) { - FILE* f = fopen(filename, "r"); + FILE* f = fopen(filename, "rb"); if (f) { system_load_state(f);