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 image image After image image - 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() {