From 3b29227dbc424bb05e6957d0a24fef056424473f Mon Sep 17 00:00:00 2001
From: James Liu <17558260+JamesLewisLiu@users.noreply.github.com>
Date: Wed, 12 Aug 2026 15:08:05 +0800
Subject: [PATCH] gitadora: fix XG2 gibberish text showing (#863)
> [!NOTE]
> Before submitting code changes...
> * Please do note that this is a GPL v3.0 open source project.
> * Please read the
[CONTRIBUTING](https://github.com/spice2x/spice2x.github.io/blob/main/CONTRIBUTING.md)
guide.
> * Maintainers reserve the right to reject or modify your submission
without reason.
> * No new compiler warnings must be introduced. Check the CI build
results.
>
> Feel free to remove this section after you have read it.
## Link to GitHub Issue or related Pull Request, if one exists
## Description of change
Fix gibberish in dynamic Japanese text in GuitarFreaks/DrumMania XG2
(K32/K33) when Windows uses a non-Japanese system code page.
XG2 converts UTF-8 property strings through WideCharToMultiByte(CP_ACP)
before rendering. On systems where the active code page is not
Shift-JIS, these strings are converted using the system code page and
become corrupted or turn into question marks.
Spice2x already hooks WideCharToMultiByte and redirects these
conversions to CP932 for newer 64-bit Gitadora models. This change makes
the existing hook available to 32-bit builds and enables it specifically
for XG2 models K32 and K33.
The existing 64-bit Gitadora Arena and T44 conditions remain unchanged.
## Testing
Before
After
- Tested GuitarFreaks XG2 on a non-Japanese (English US) Windows.
- Verified that Community Log preset comments render correctly in
Japanese.
- Verified that Cooperation Challenge descriptions, rewards, and
progress text render correctly.
- Confirmed that the previous mojibake and question marks no longer
appear.
- GitHub Actions build completed successfully for both architectures.
---
src/spice2x/hooks/lang.cpp | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/src/spice2x/hooks/lang.cpp b/src/spice2x/hooks/lang.cpp
index 553b966..0567dc2 100644
--- a/src/spice2x/hooks/lang.cpp
+++ b/src/spice2x/hooks/lang.cpp
@@ -26,13 +26,13 @@ constexpr UINT CODEPAGE_SHIFT_JIS = 932;
static decltype(GetACP) *GetACP_orig = nullptr;
static decltype(GetOEMCP) *GetOEMCP_orig = nullptr;
static decltype(MultiByteToWideChar) *MultiByteToWideChar_orig = nullptr;
+static decltype(WideCharToMultiByte) *WideCharToMultiByte_orig = nullptr;
static decltype(GetLocaleInfoEx) *GetLocaleInfoEx_orig = nullptr;
#ifdef SPICE64
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr;
static decltype(IsDBCSLeadByteEx) *IsDBCSLeadByteEx_orig = nullptr;
-static decltype(WideCharToMultiByte) *WideCharToMultiByte_orig = nullptr;
static decltype(GetLocaleInfoA) *GetLocaleInfoA_orig = nullptr;
static decltype(GetThreadLocale) *GetThreadLocale_orig = nullptr;
#endif
@@ -209,6 +209,8 @@ static BOOL WINAPI IsDBCSLeadByteEx_hook(
return IsDBCSLeadByteEx_orig(CodePage, TestChar);
}
+#endif
+
static
int
WINAPI
@@ -244,6 +246,8 @@ WideCharToMultiByte_hook(
lpUsedDefaultChar);
}
+#ifdef SPICE64
+
int
WINAPI
GetLocaleInfoA_hook(
@@ -343,15 +347,6 @@ void hooks::lang::early_init() {
&IsDBCSLeadByte_orig);
}
- if (games::gitadora::is_arena_model() || avs::game::is_model("T44")) {
- log_info("hooks::lang", "hooking WideCharToMultiByte");
- detour::trampoline_try(
- "kernel32.dll",
- "WideCharToMultiByte",
- WideCharToMultiByte_hook,
- &WideCharToMultiByte_orig);
- }
-
if (games::popn::is_pikapika_model() && native_code_page == CP_UTF8) {
detour::trampoline_try(
"kernel32.dll",
@@ -362,6 +357,23 @@ void hooks::lang::early_init() {
#endif
+#ifdef SPICE64
+ const auto hook_wide_char_to_multi_byte =
+ games::gitadora::is_arena_model() || avs::game::is_model("T44");
+#else
+ // XG2 converts UTF-8 property strings through CP_ACP before rendering.
+ const auto hook_wide_char_to_multi_byte = avs::game::is_model({ "K32", "K33" });
+#endif
+
+ if (hook_wide_char_to_multi_byte) {
+ log_info("hooks::lang", "hooking WideCharToMultiByte");
+ detour::trampoline_try(
+ "kernel32.dll",
+ "WideCharToMultiByte",
+ WideCharToMultiByte_hook,
+ &WideCharToMultiByte_orig);
+ }
+
}
void hooks::lang::init() {