From ec3f6639b721912ca447ff9202406167f6849e02 Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Sat, 11 Apr 2026 14:42:51 -0700 Subject: [PATCH] ccj: fix locale issues with carding in (#628) ## Link to GitHub Issue or related Pull Request, if one exists #624 ## Description of change See bug. Add locale hooks specifically for CCJ. also, fix y axis direction for XInput, it was flipped because XInput uses opposite values from what RawInput does ## Testing Tested card in / play one round with Windows regional format set to French (France) and other language set to English. --- src/spice2x/hooks/lang.cpp | 52 +++++++++++++++++++++++++++++++++ src/spice2x/rawinput/xinput.cpp | 4 +-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/spice2x/hooks/lang.cpp b/src/spice2x/hooks/lang.cpp index 24d8045..fb94aba 100644 --- a/src/spice2x/hooks/lang.cpp +++ b/src/spice2x/hooks/lang.cpp @@ -30,6 +30,8 @@ static decltype(GetLocaleInfoEx) *GetLocaleInfoEx_orig = nullptr; static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr; static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr; static decltype(WideCharToMultiByte) *WideCharToMultiByte_orig = nullptr; +static decltype(GetLocaleInfoA) *GetLocaleInfoA_orig = nullptr; +static decltype(GetThreadLocale) *GetThreadLocale_orig = nullptr; #endif static NTSTATUS NTAPI RtlMultiByteToUnicodeN_hook( @@ -94,6 +96,10 @@ static LCID WINAPI GetSystemDefaultLCID_hook() { return 0x411; } +static LCID WINAPI GetThreadLocale_hook() { + return 0x411; +} + #endif static int WINAPI MultiByteToWideChar_hook( @@ -214,6 +220,31 @@ WideCharToMultiByte_hook( lpUsedDefaultChar); } +int +WINAPI +GetLocaleInfoA_hook( + LCID Locale, + LCTYPE LCType, + LPSTR lpLCData, + int cchData) { + + if (LCType == LOCALE_SISO639LANGNAME && lpLCData != NULL && cchData >= 3) { + log_misc("hooks::lang", "GetLocaleInfoA_hook hit ({:#x}, LOCALE_SISO639LANGNAME), return `ja`", Locale); + strcpy(lpLCData, "ja"); + return 3; + } + + if (LCType == LOCALE_SISO3166CTRYNAME && lpLCData != NULL && cchData >= 3) { + log_misc("hooks::lang", + "GetLocaleInfoA_hook hit ({:#x}, LOCALE_SISO3166CTRYNAME), return `JP`", Locale); + strcpy(lpLCData, "JP"); + return 3; + } + + log_misc("hooks::lang", "GetLocaleInfoA_hook hit, {:#x}, {:#x}", Locale, LCType); + return GetLocaleInfoA_orig(Locale, LCType, lpLCData, cchData); +} + #endif void hooks::lang::early_init() { @@ -244,6 +275,27 @@ void hooks::lang::early_init() { } #ifdef SPICE64 + + if (avs::game::is_model("UJK")) { + // CCJ build of Unity calls GetThreadLocale, + // and then GetLocaleInfoA to figure out the locale; eventually this is + // used to figure out the decimal separator, which is then used in parsing + // Double values - which fails carding in if it isn't "." + log_info("hooks::lang", "hooking GetThreadLocale"); + detour::trampoline_try( + "kernel32.dll", + "GetThreadLocale", + GetThreadLocale_hook, + &GetThreadLocale_orig); + + log_info("hooks::lang", "hooking GetLocaleInfoA"); + detour::trampoline_try( + "kernel32.dll", + "GetLocaleInfoA", + GetLocaleInfoA_hook, + &GetLocaleInfoA_orig); + } + // for TDJ subscreen search keyboard if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) { log_info("hooks::lang", "hooking IsDBCSLeadByte"); diff --git a/src/spice2x/rawinput/xinput.cpp b/src/spice2x/rawinput/xinput.cpp index a05296e..f42a589 100644 --- a/src/spice2x/rawinput/xinput.cpp +++ b/src/spice2x/rawinput/xinput.cpp @@ -245,7 +245,7 @@ XInputSetState( // left stick circular dead zone { const auto x_raw = x.Gamepad.sThumbLX; - const auto y_raw = x.Gamepad.sThumbLY; + const auto y_raw = -x.Gamepad.sThumbLY; // flip to make [down = positive] const float magnitude = sqrtf(x_raw * x_raw + y_raw * y_raw); if (magnitude > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { const float scaled = @@ -268,7 +268,7 @@ XInputSetState( // right stick circular dead zone { const auto x_raw = x.Gamepad.sThumbRX; - const auto y_raw = x.Gamepad.sThumbRY; + const auto y_raw = -x.Gamepad.sThumbRY; // flip to make [down = positive] const float magnitude = sqrtf(x_raw * x_raw + y_raw * y_raw); if (magnitude > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { const float scaled =