From 32512d0dbc9bf2fcadbcf3d047f31e06b453b315 Mon Sep 17 00:00:00 2001 From: ptmaster Date: Sat, 21 Dec 2024 06:03:09 +0800 Subject: [PATCH] Language patch | Fix boot lag | Fix axis input --- CMakeLists.txt | 1 + dist/config.toml | 5 - src/bnusio.cpp | 29 +- src/constants.h | 5 + src/dllmain.cpp | 53 +-- src/helpers.cpp | 18 + src/helpers.h | 12 +- src/patches/language.cpp | 626 +++++++++++++++++++++++++++++++++ src/patches/layeredfs.cpp | 33 +- src/patches/patches.h | 9 +- src/patches/scanner.cpp | 18 +- src/patches/testmode.cpp | 134 ++----- src/patches/versions/JPN39.cpp | 406 ++------------------- src/pollasync.cpp | 159 +++++++-- 14 files changed, 905 insertions(+), 603 deletions(-) create mode 100644 src/patches/language.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index eaaad31..8a6e9db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,7 @@ set(SOURCES src/patches/layeredfs.cpp src/patches/testmode.cpp src/patches/timer.cpp + src/patches/language.cpp src/patches/versions/JPN00.cpp src/patches/versions/JPN08.cpp src/patches/versions/JPN39.cpp diff --git a/dist/config.toml b/dist/config.toml index fba0b8a..484baaa 100644 --- a/dist/config.toml +++ b/dist/config.toml @@ -24,11 +24,6 @@ mode_collabo025 = false # Enable one piece collab mode mode_collabo026 = false # Enable ai soshina mode -[patches.jpn39] # These patches are only available for version JPN39 -chs_patch = false # Use Chinese font and Simplified Chinese values from the wordlist - # More options are available in the ModManager, in the TestMode menu (Default key is F1) - - [emulation] usio = true # Disable this if you want to use an original Namco USIO board. you need to place bnusio_original.dll (unmodified bnusio.dll) in the executable folder. card_reader = true # Disable this if you want to use an original Namco card reader diff --git a/src/bnusio.cpp b/src/bnusio.cpp index 0f82475..459d0fb 100644 --- a/src/bnusio.cpp +++ b/src/bnusio.cpp @@ -137,16 +137,10 @@ bool valueStates[] = {false, false, false, false, false, false, false, false}; Keybindings *analogButtons[] = {&P1_LEFT_BLUE, &P1_LEFT_RED, &P1_RIGHT_RED, &P1_RIGHT_BLUE, &P2_LEFT_BLUE, &P2_LEFT_RED, &P2_RIGHT_RED, &P2_RIGHT_BLUE}; -// u16 cooldown[8] = { 0 }; +u8 cooldown[8] = { 0 }; u16 buttonWaitPeriod[] = { 0, 0 }; std::queue buttonQueue[] = { {}, {} }; -// u16 buttonWaitPeriodP1 = 0; -// u16 buttonWaitPeriodP2 = 0; -// std::queue buttonQueueP1; -// std::queue buttonQueueP2; - - SDLAxis analogBindings[] = { SDL_AXIS_LEFT_LEFT, SDL_AXIS_LEFT_RIGHT, SDL_AXIS_LEFT_DOWN, SDL_AXIS_LEFT_UP, // P1: LB, LR, RR, RB SDL_AXIS_RIGHT_LEFT, SDL_AXIS_RIGHT_RIGHT, SDL_AXIS_RIGHT_DOWN, SDL_AXIS_RIGHT_UP, // P2: LB, LR, RR, RB @@ -248,6 +242,22 @@ AnalogInputWaitPeriod (const u8 which) { return analogValue; } +u8 blueCooldown = 1, redCooldown = 1; +u16 __fastcall +AnalogInputCooldown (const u8 which) { + const auto button = analogButtons[which]; + const bool blue = !(which % 4 % 3); + + if (cooldown[which] > 0) cooldown[which]--; + if (IsButtonTapped (*button)) { + if (cooldown[which] > 0) return 0; + cooldown[which] = blue ? blueCooldown : redCooldown; + const u16 hitValue = !valueStates[which] ? 50 : 51; + valueStates[which] = !valueStates[which]; + return (hitValue << 15) / 100 + 1; + } else return 0; +} + u16 __fastcall AnalogInputSimple (const u8 which) { const auto button = analogButtons[which]; @@ -299,7 +309,7 @@ UpdateLoop () { if (IsButtonTapped (TEST)) testEnabled = !testEnabled; if (IsButtonTapped (EXIT)) { LogMessage (LogLevel::INFO, "Exit By Press Exit Button!"); - exited += 1; testEnabled = 1; patches::Plugins::Exit (); + exited += 1; testEnabled = 1; std::thread([](){patches::Plugins::Exit ();}).detach(); } if (GameVersion::CHN00 == gameVersion) { if (IsButtonTapped (CARD_INSERT_1)) patches::Scanner::Qr::CommitLogin (accessCode1); @@ -344,6 +354,9 @@ Init () { } else if (drumWaitPeriod > 0) { LogMessage (LogLevel::WARN, "[Analog Type] WaitPeriod: Fast input might be queued"); analogMethod = AnalogInputWaitPeriod; + } else if (drumWaitPeriod == 0) { + LogMessage (LogLevel::INFO, "[Analog Type] Cooldown: Fastest and original input"); + analogMethod = AnalogInputCooldown; } else { LogMessage (LogLevel::INFO, "[Analog Type] Simple: Fastest and original input"); analogMethod = AnalogInputSimple; diff --git a/src/constants.h b/src/constants.h index 33222f1..4bbf46d 100644 --- a/src/constants.h +++ b/src/constants.h @@ -7,4 +7,9 @@ enum class GameVersion : XXH64_hash_t { JPN08 = 0x67C0F3042746D488, JPN39 = 0x49F643ADB6B18705, CHN00 = 0xA7EE39F2CC2C57C8, +}; + +enum StatusType { + CardStatus = 1, + QrStatus = 2 }; \ No newline at end of file diff --git a/src/dllmain.cpp b/src/dllmain.cpp index 1bec855..af4440a 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -140,51 +140,6 @@ CreateCard () { WritePrivateProfileStringA ("card", "chipId2", buf, ".\\card.ini"); } -void -ElevateProcess () { - HANDLE hToken; - TOKEN_PRIVILEGES tp; - LUID luid; - - // 获取当前进程的访问令牌 - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { - LogMessage (LogLevel::ERROR, "OpenProcessToken failed: {}", GetLastError()); - return; - } - - // 获取 SE_DEBUG_NAME 权限的 LUID - if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { - LogMessage (LogLevel::ERROR, "LookupPrivilegeValue failed: {}", GetLastError()); - CloseHandle(hToken); - return; - } - - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = luid; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - // 调整权限 - if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL)) { - LogMessage (LogLevel::ERROR, "AdjustTokenPrivileges failed: {}", GetLastError()); - CloseHandle(hToken); - return; - } - - LogMessage (LogLevel::INFO, "Privileges adjusted successfully!"); - CloseHandle(hToken); -} - -bool DisableDEP() { - if (SetProcessDEPPolicy(PROCESS_DEP_ENABLE)) { - // std::cout << "DEP disabled successfully.\n"; - return true; - } else { - LogMessage (LogLevel::ERROR, "Failed to disable DEP: {}", GetLastError()); - // std::cerr << "Failed to disable DEP: " << GetLastError() << std::endl; - return false; - } -} - BOOL DllMain (HMODULE module, const DWORD reason, LPVOID reserved) { if (reason == DLL_PROCESS_ATTACH) { @@ -192,14 +147,11 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) { // I/O in DllMain can easily cause a deadlock // Init logger for loading config - auto start = std::chrono::high_resolution_clock::now(); InitializeLogger (GetLogLevel (logLevelStr), logToFile); - // ElevateProcess (); - // DisableDEP (); patches::Timer::Init (); + #ifdef ASYNC_IO - LogMessage (LogLevel::WARN, "(experimental) Using Async IO!"); InitializeKeyboard (); #endif @@ -314,8 +266,9 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) { patches::Audio::Init (); patches::Dxgi::Init (); patches::AmAuth::Init (); - patches::LayeredFs::Init (); + patches::Language::Init (); patches::TestMode::Init (); + patches::LayeredFs::Init (); std::chrono::duration duration = std::chrono::high_resolution_clock::now() - start; LogMessage (LogLevel::INFO, "==== Finished Loading patches! using: {:.2f}ms", duration.count () * 1000); diff --git a/src/helpers.cpp b/src/helpers.cpp index 8cd2968..8e5ec58 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -182,4 +182,22 @@ AreAllBytesZero (const u8 *array, const size_t offset, const size_t length) { for (size_t i = 0; i < length; ++i) if (array[offset + i] != 0x00) return false; return true; +} + +void +withFile (const char *fileName, std::function callback) { + FILE *file = fopen (fileName, "rb"); + if (file == nullptr) return; + + fseek (file, 0, SEEK_END); + long size = ftell (file); + fseek (file, 0, SEEK_SET); + + u8 *buffer = (u8 *)malloc(size); + fread (buffer, 1, size, file); + + callback (buffer); + + free (buffer); + fclose (file); } \ No newline at end of file diff --git a/src/helpers.h b/src/helpers.h index ef3b708..7ed853a 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "constants.h" #include "logger.h" @@ -123,15 +125,8 @@ const HMODULE MODULE_HANDLE = GetModuleHandle (nullptr); #define INSTALL_FAST_HOOK_DYNAMIC(functionName, location) \ { \ LogMessage (LogLevel::DEBUG, std::string ("Installing fast hook dynamic for ") + #functionName); \ - if (auto hook = safetyhook::InlineHook::create((void *)location, (void*)implOf##functionName)) {\ - original##functionName = std::move(*hook); \ - } else { \ - LogMessage (LogLevel::ERROR, std::string ("Error Installing fast hook dynamic for ") + #functionName);\ - ExitProcess (1); \ - } \ + original##functionName = safetyhook::create_inline ((void *)location, (void*)implOf##functionName); \ } - // original##functionName = safetyhook::create_inline ((void *)location, (void*)implOf##functionName); \ - // } inline bool sendFlag = false; #define SCENE_RESULT_HOOK(functionName, location) \ @@ -228,3 +223,4 @@ const char *GameVersionToString (GameVersion version); const char *languageStr (int language); std::string ConvertWideToUtf8 (const std::wstring &wstr); bool AreAllBytesZero (const u8 *array, size_t offset, size_t length); +void withFile (const char *fileName, std::function callback); diff --git a/src/patches/language.cpp b/src/patches/language.cpp new file mode 100644 index 0000000..abf9f3d --- /dev/null +++ b/src/patches/language.cpp @@ -0,0 +1,626 @@ +#include "constants.h" +#include "helpers.h" +#include "patches.h" +#include + +extern GameVersion gameVersion; + +namespace patches::Language { +const std::vector languages = { "jpn", "en_us", "cn_tw", "kor", "cn_cn" }; +std::vector language_patch = { }; +int language = 0; + +FUNCTION_PTR (void, lua_settop, PROC_ADDRESS ("lua51.dll", "lua_settop"), i64, i32); +FUNCTION_PTR (void, lua_replace, PROC_ADDRESS ("lua51.dll", "lua_replace"), i64, i32); +FUNCTION_PTR (const char *, lua_pushstring, PROC_ADDRESS ("lua51.dll", "lua_pushstring"), i64, const char *); +FUNCTION_PTR (const char *, lua_tolstring, PROC_ADDRESS ("lua51.dll", "lua_tolstring"), u64, i32, size_t *); +FUNCTION_PTR (void, lua_pushnumber, PROC_ADDRESS ("lua51.dll", "lua_pushnumber"), i64, double); + +typedef void *(*Taiko_string) (void *address, const char *src); +typedef void *(*Taiko_assign) (void *address, const char *src, size_t size); +typedef int (*LanguageFix) (int language); + +Taiko_string taiko_string = nullptr; +Taiko_assign taiko_assign = nullptr; +Taiko_assign std_assign = nullptr; +Taiko_assign std_append = nullptr; +LanguageFix languageFix = [](int lang){ return lang; }; + +namespace Font { +typedef u64 *(*Taiko_mapping) (u64 *a1, u8 *a2); +Taiko_mapping taiko_mapping = nullptr; + +typedef struct { + const char *font_name; + const char *font_shape; + const char *font_file; +} LanguageItem; + +void *languageData = nullptr; +const size_t languageOffset = languages.size () * 560; +// jp_64, jp_30, jp_32, en_64, en_30, en_32, tw_64, tw_30, tw_32, ko_64, ko_30, ko_32, cn_64, cn_30, cn_32 +const float upA[15] = { -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; +const float upB[15] = { -0.265625f, 0.15f, 0.078125f, -0.21875f, 0.2329999f, 0.140625f, -0.0625f, 0.333f, 0.28125f, -0.109375f, 0.333f, 0.28125f, -0.09375f, 0.30000001f, 0.25f }; +const float downA[15] = { -0.25f, 0.167f, 0.125f, -0.21875f, 0.233f, 0.140625f, -0.0625f, 0.333f, 0.25f, -0.125f, 0.333f, 0.28125f, -0.09375f, 0.30000001f, 0.25f }; +const float downB[15] = { 50.5f, 10.0f, 13.0f, 46.0f, 8.0f, 11.5f, 36.0f, 5.0f, 7.0f, 39.0f, 5.0f, 7.0f, 38.0f, 6.0f, 8.0f }; +// xx_64, 00_30, xx_32 +const float downC[3] = { 64.0f, 30.0f, 32.0f }; +const float shapes[3] = { 20.0f, 12.0f, 16.0f }; +const LanguageItem languageItems[5][5] = { + {{"jp", "64", "jp_64"},{"jp", "30", "jp_30"},{"jp", "32_EB", "jp_32_EB"},{"jp", "32_B", "jp_32_B"},{"jp", "32_DB", "jp_32_DB"}}, + {{"en", "64", "en_64"},{"en", "30", "en_30"},{"en", "32_EB", "en_32" },{"en", "32_B", "en_32" },{"en", "32_DB", "en_32" }}, + {{"tw", "64", "tw_64"},{"tw", "30", "tw_30"},{"tw", "32_EB", "tw_32" },{"tw", "32_B", "tw_32" },{"tw", "32_DB", "tw_32" }}, + {{"kr", "64", "kr_64"},{"kr", "30", "kr_30"},{"kr", "32_EB", "kr_32" },{"kr", "32_B", "kr_32" },{"kr", "32_DB", "kr_32" }}, + {{"cn", "64", "cn_64"},{"cn", "30", "cn_30"},{"cn", "32_EB", "cn_32" },{"cn", "32_B", "cn_32" },{"cn", "32_DB", "cn_32" }}, +}; + +FAST_HOOK_DYNAMIC (int, initial_load_setting) { + for (int repeat = 0; repeat < languages.size (); repeat ++) { + u64 offsetR = repeat * languages.size () * 0x5 * 0x70; + for (int lang = 0; lang < languages.size (); lang ++) { + u64 offsetI = lang * 0x5 * 0x70; + const LanguageItem *group = languageItems[lang]; + for (int shape = 0; shape < 0x5; shape ++) { + LanguageItem item = group[shape]; + u64 basicOffset = (u64)languageData + offsetR + offsetI + shape * 0x70; + u32 shapeIndex = shape > 2 ? 2 : shape; + + std::string *str = new std::string (item.font_name); + memcpy ((void *)(basicOffset + 0x00), str, 0x20); + str = new std::string (item.font_shape); + memcpy ((void *)(basicOffset + 0x20), str, 0x20); + str = new std::string (item.font_file); + memcpy ((void *)(basicOffset + 0x40), str, 0x20); + *(float *)(basicOffset + 0x60) = shapes[shapeIndex]; + *(u32 *)(basicOffset + 0x64) = lang == 0 ? 0 : shapeIndex + 1; + *(u32 *)(basicOffset + 0x68) = lang == 0 ? 0 : (3 * lang + shapeIndex + 1); + } + } + } + return 0; +} + +FAST_HOOK_DYNAMIC (double, GetCorrectionOffsetY, u64 a1, u32 a2) { + if (!a2) return 0.0; + double result; + u8 type = *(u8 *)(a1 + 572); + float varA = *(float *)(a1 + 532); + if (type) { + *(float *)&result = upA[a2 - 1] + upB[a2 - 1] * varA; + } else { + float varB = *(float *)(a1 + 564); + float varC = downC[(a2 - 1) % 3]; + float val = (varC - varA) * downA[a2 - 1] + (varB - varC) * 0.5f + downB[a2 - 1] - varB * 0.5f; + *(u64 *)&result = *(u64 *)&val ^ 0x8000000080000000ui64; + } + return result; +} + +void +Init () { + bool fontExistAll = true; + const char *fontToCheck[]{"cn_30.nutexb", "cn_30.xml", "cn_32.nutexb", "cn_32.xml", "cn_64.nutexb", "cn_64.xml"}; + for (int i = 0; i < 6; i++) { + if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\font\\") + fontToCheck[i])) continue; + fontExistAll = false; + } + if (fontExistAll == false) return; + LogMessage (LogLevel::INFO, "Detected cn_xx files, Install Font patches"); + languageData = calloc (languages.size () * languages.size () * 0x70 * 0x5, sizeof (u8)); + + switch (gameVersion) { + default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + case GameVersion::JPN39: { + taiko_mapping = (Taiko_mapping)ASLR (0x140369B60); + TestMode::RegisterModify ( + L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='LanguageItem']", + [](pugi::xml_node &node) { + node.attribute (L"max").set_value (L"4"); + node.attribute (L"default").set_value (L"4"); + node.attribute (L"replace-text").set_value (L"0:JPN, 1:ENG, 2:zh-tw, 3:KOR, 4:zh-cn"); + }, [&](){ + // load_setting + INSTALL_FAST_HOOK_DYNAMIC (initial_load_setting, ASLR (0x14001B010)); + // SceneBoot::LoadFont + language_patch.push_back (safetyhook::create_mid (ASLR (0x140464235), [](SafetyHookContext &ctx){ + ctx.rbp = ctx.rbx * languageOffset; + ctx.rax = (uintptr_t)languageData; + ctx.rip = ASLR (0x140464243); + })); + language_patch.push_back (safetyhook::create_mid (ASLR (0x14046426C), [](SafetyHookContext &ctx){ + ctx.r15 = ctx.rbp + languageOffset; + ctx.rip = ASLR (0x140464273); + })); + // SceneBoot::SetFontShift + language_patch.push_back (safetyhook::create_mid (ASLR (0x1404644AF), [](SafetyHookContext &ctx){ + ctx.rdi = ctx.rcx * languageOffset; + ctx.rax = (uintptr_t)languageData; + ctx.rdi += ctx.rax; + ctx.rsi = ctx.rdi + languageOffset; + ctx.rip = ASLR (0x1404644C7); + })); + // AppLumenRenderer + language_patch.push_back (safetyhook::create_mid (ASLR (0x140368961), [](SafetyHookContext &ctx){ + *(u64 *)(ctx.rsp + 0x20) = 4; + void *buf = taiko_mapping ((u64 *)(ctx.r14), (u8 *)(ctx.rsp + 0x20)); + taiko_assign (buf, "cn", 2); + })); + // CorrectionOffsetY + INSTALL_FAST_HOOK_DYNAMIC (GetCorrectionOffsetY, ASLR (0x14049FAD0)); + // user_name_entry + language_patch.push_back (safetyhook::create_mid (ASLR (0x14016128D), [](SafetyHookContext &ctx) + { *(int *)(ctx.rsp + 0xD0) = (int)(ctx.rcx % languages.size ()); ctx.rip = ASLR (0x1401612C7); })); + // user_name_p + language_patch.push_back (safetyhook::create_mid (ASLR (0x140160F63), [](SafetyHookContext &ctx) + { *(int *)(ctx.rsp + 0xD0) = (int)(ctx.rdi % languages.size ()); ctx.rip = ASLR (0x140160F9D); })); + // user_name_ai + language_patch.push_back (safetyhook::create_mid (ASLR (0x14016146D), [](SafetyHookContext &ctx) + { *(int *)(ctx.rsp + 0xD0) = (int)(ctx.rcx % languages.size ()); ctx.rip = ASLR (0x1401614A7); })); + // user_name_soshina + language_patch.push_back (safetyhook::create_mid (ASLR (0x14016164D), [](SafetyHookContext &ctx) + { *(int *)(ctx.rsp + 0xD0) = (int)(ctx.rcx % languages.size ()); ctx.rip = ASLR (0x140161687); })); + } + ); + } break; + } +} +} + +namespace Datatable { +const std::vector languageTexts = {"japaneseText", "englishUsText", "chineseTText", "koreanText", "chineseSText"}; +const std::vector languageFontTypes = {"japaneseFontType", "englishUsFontType", "chineseTFontType", "koreanFontType", "chineseSFontType"}; +const std::vector languageFlas = {"taikoSelectFla", "taikoSelectFlaENUS", "taikoSelectFlaCNTW", "taikoSelectFlaKOR", "taikoSelectFlaZHCN"}; + +typedef bool (*JsonHasMember) (u64, const char *); +typedef u64 *(*JsonGetMember) (u64, const char *); +typedef void (*VectorIterator) (char *, u64, u64, u64); +JsonHasMember jsonHasMember = nullptr; +JsonGetMember jsonGetMember = nullptr; +VectorIterator vectorIterator = nullptr; + +uintptr_t WordlistLanguageTypeRIP = 0; +MID_HOOK_DYNAMIC (WordlistLanguageType, SafetyHookContext &ctx) { + int language = (int)ctx.r12, offset = (int)ctx.rdi; + u64 wordlist = ctx.rbx; + + int fallback = -1; + language = language > 0 && language < languages.size () ? language : 0; + u64 json = 0xFFFFFFFFFFFFi64 & wordlist + offset; + if (jsonHasMember (json, languageTexts[language].c_str ())) { + u64 *data = jsonGetMember (json, languageTexts[language].c_str ()); + if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF); + taiko_assign ((void *)(ctx.rsp + 0x60), (char *)data, strlen ((char *)data)); + } else if (language == 4 && jsonHasMember (json, languageTexts[2].c_str ())) { + fallback = 2; + u64 *data = jsonGetMember (json, languageTexts[2].c_str ()); + if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF); + taiko_assign ((void *)(ctx.rsp + 0x60), (char *)data, strlen ((char *)data)); + } else if (language != 0 && jsonHasMember (json, languageTexts[0].c_str ())) { + fallback = 0; + u64 *data = jsonGetMember (json, languageTexts[0].c_str ()); + if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF); + taiko_assign ((void *)(ctx.rsp + 0x60), (char *)data, strlen ((char *)data)); + } else taiko_assign ((void *)(ctx.rsp + 0x60), "", 0); + + if (fallback != -1) { + if (jsonHasMember (json, languageFontTypes[fallback].c_str ())) { + ctx.rcx = *jsonGetMember (json, languageFontTypes[fallback].c_str ()); + } else ctx.rcx = 0; + } else if (jsonHasMember (json, languageFontTypes[language].c_str ())) { + ctx.rcx = *jsonGetMember (json, languageFontTypes[language].c_str ()); + } else ctx.rcx = 0; + + ctx.rip = WordlistLanguageTypeRIP; +} + +char *languageFla = nullptr; +uintptr_t QrcodeInfoResourceTypeRIP = 0; +MID_HOOK_DYNAMIC (QrcodeInfoResourceType, SafetyHookContext &ctx) { + languageFla = (char *)(ctx.rsp + 0x130); + u64 json = ctx.rbx; + for (int i = 0; i < 4; i ++) { + int lang = language == 4 ? (i == 2 ? 4 : i) : i; + if (jsonHasMember (json, languageFlas[lang].c_str ())) { + u64 *data = jsonGetMember (json, languageFlas[lang].c_str ()); + if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF); + std_assign (languageFla + i * 0x20, (char *)data, strlen ((char *)data)); + } else if (language == 4 && jsonHasMember (json, languageFlas[2].c_str ())) { + u64 *data = jsonGetMember (json, languageFlas[2].c_str ()); + if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF); + std_assign (languageFla + i * 0x20, (char *)data, strlen ((char *)data)); + } else if (language != 0 && jsonHasMember (json, languageFlas[0].c_str ())) { + u64 *data = jsonGetMember (json, languageFlas[0].c_str ()); + if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF); + std_assign (languageFla + i * 0x20, (char *)data, strlen ((char *)data)); + } else std_assign (languageFla + i * 0x20, "", 0); + } + ctx.rip = QrcodeInfoResourceTypeRIP; +} + +std::string CHSGaidenTitle = ""; +MID_HOOK_DYNAMIC (GaidenTitleExtren, SafetyHookContext &ctx) { + LogMessage (LogLevel::DEBUG, "GaidenTitle: {}", (char *)ctx.rcx); + std::string gaidenTitle = std::string ((char *)ctx.rcx); + if (gaidenTitle.find ("[CHS]") != std::string::npos) { + CHSGaidenTitle = gaidenTitle; + } +} +uintptr_t JumpInjectGaidenTitleRIP = 0; +MID_HOOK_DYNAMIC (GaidenTitleInject, SafetyHookContext &ctx) { + // only handle ctx.rdx > 2 2 => lang4 => zh-cn etc. + if (ctx.rdx == 2) { + ctx.rdx = (uintptr_t)CHSGaidenTitle.c_str (); + ctx.r8 = CHSGaidenTitle.size (); + LogMessage (LogLevel::DEBUG, "GaidenTitleInjectCHS title={} length={}", CHSGaidenTitle, CHSGaidenTitle.size ()); + ctx.rip = JumpInjectGaidenTitleRIP; + } +} +MID_HOOK_DYNAMIC (GaidenTitleClean, SafetyHookContext &ctx) { + CHSGaidenTitle = ""; +} +uintptr_t JumpInjectGaidenLanguageRIP = 0; +MID_HOOK_DYNAMIC (GaidenLanguageInject, SafetyHookContext &ctx) { + if (ctx.rbx == 2) { + *(int *)(ctx.rsp + 0x2D8) = 4; + ctx.rip = JumpInjectGaidenLanguageRIP; + } +} + +void +Init () { + switch (gameVersion) { + default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + case GameVersion::JPN39: { + jsonHasMember = (JsonHasMember)ASLR (0x1400B5A30); + jsonGetMember = (JsonGetMember)ASLR (0x1400B6080); + vectorIterator = (VectorIterator)ASLR (0x14079C430); + // App::DataTableHolder::Serialize + WordlistLanguageTypeRIP = ASLR (0x1400B20B8); + INSTALL_MID_HOOK_DYNAMIC (WordlistLanguageType, ASLR (0x1400B1E47)); + // App::DataTableHolder::Serialize + QrcodeInfoResourceTypeRIP = ASLR (0x1400B2959); + INSTALL_MID_HOOK_DYNAMIC (QrcodeInfoResourceType, ASLR (0x1400B276E)); + INSTALL_MID_HOOK_DYNAMIC (GaidenTitleExtren, ASLR (0x1403E2224)); + JumpInjectGaidenTitleRIP = ASLR (0x1403E2416); + INSTALL_MID_HOOK_DYNAMIC (GaidenTitleInject, ASLR (0x1403E238F)); + INSTALL_MID_HOOK_DYNAMIC (GaidenTitleClean, ASLR (0x1403E2980)); + JumpInjectGaidenLanguageRIP = ASLR (0x1403E2721); + INSTALL_MID_HOOK_DYNAMIC (GaidenLanguageInject, ASLR (0x1403E26E2)); + } break; + } +} +} + +namespace Script { +FAST_HOOK_DYNAMIC (i64, RefPlayDataManager, i64 a1) { + LogMessage (LogLevel::HOOKS, "RefPlayDataManager was called"); + const auto result = originalRefPlayDataManager.fastcall (a1); + language = *reinterpret_cast (result); + return result; +} +FAST_HOOK_DYNAMIC (i64, GetLanguage, u64 *a1, i64 a2) { + LogMessage (LogLevel::HOOKS, "GetLanguage was called"); + lua_settop (a2, 0); + int langFix = languageFix (language); + const char* type = languageStr (langFix); + lua_pushstring (a2, type); + return 1; +} +FAST_HOOK_DYNAMIC (i64, GetRegionLanguage, i64 a1) { + LogMessage (LogLevel::HOOKS, "GetRegionLanguage was called"); + lua_settop (a1, 0); + int langFix = languageFix (language); + lua_pushstring (a1, languageStr (langFix)); + return 1; +} +std::thread::id *LoadMovieThreadId = nullptr; +FAST_HOOK_DYNAMIC (i64, GetCabinetLanguage, u64 *a1, i64 a2) { + LogMessage (LogLevel::HOOKS, "GetCabinetLanguage was called"); + i64 result = originalGetCabinetLanguage.fastcall (a1, a2); + if (language == 4 && (LoadMovieThreadId == nullptr || *LoadMovieThreadId != std::this_thread::get_id())) { + std_assign ((void *)result, languageStr (2), 5); + } else std_assign ((void *)result, languageStr (language), (language == 0 || language == 3) ? 3 : 5); + return result; +} +FAST_HOOK_DYNAMIC (i64, GetLanguageType, i64 a1) { + LogMessage (LogLevel::HOOKS, "GetLanguageType was called"); + lua_settop (a1, 0); + lua_pushnumber (a1, languageFix (language)); + return 1; +} +void +Init () { + switch (gameVersion) { + default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + case GameVersion::JPN39: { + INSTALL_FAST_HOOK_DYNAMIC (RefPlayDataManager, ASLR (0x140024AC0)); + // Export methods for lua invoke + INSTALL_FAST_HOOK_DYNAMIC (GetLanguage, ASLR (0x1401D1930)); + INSTALL_FAST_HOOK_DYNAMIC (GetRegionLanguage, ASLR (0x1401CE9B0)); + INSTALL_FAST_HOOK_DYNAMIC (GetCabinetLanguage, ASLR (0x14014DB80)); + INSTALL_FAST_HOOK_DYNAMIC (GetLanguageType, ASLR (0x1401CECE0)); + } break; + } +} +} + +namespace Lumen { +FAST_HOOK_DYNAMIC (i64, LoadDemoMovie, i64 a1, i64 a2, i64 a3) { + std::thread::id this_id = std::this_thread::get_id(); + Script::LoadMovieThreadId = &this_id; + i64 result = originalLoadDemoMovie.fastcall (a1, a2, a3); + Script::LoadMovieThreadId = nullptr; + return result; +} +std::string onpCn = "textures/onpu_cn/onp_all.nutexb"; +MID_HOOK_DYNAMIC (ChangeOnpFile, SafetyHookContext &ctx) { + if (language != 4) return; + ctx.rdx = (uintptr_t) onpCn.c_str (); + ctx.r8 = 0x1F; +} +void * +GetLanguageType (Taiko_assign assign, int language, void *buf) { + int langFix = languageFix (language); + return assign (buf, languageStr (langFix), (langFix == 0 || langFix == 3) ? 3 : 5); +} +void +InjectLanguageType (uintptr_t language, uintptr_t ®) { + int langFix = languageFix ((int)language); + reg = (uintptr_t)languageStr (langFix); +} + +FAST_HOOK_DYNAMIC (u64, GraphicBaseGetLanguageType, u64 a1, void *a2) { + int language = *(int *)(a1 + 56); + return (u64)GetLanguageType (taiko_assign, language, a2); +} + +void +Init () { + switch (gameVersion) { + default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + case GameVersion::JPN39: { + bool demoMovieExistAll = true; + const char *movieToCheck[]{"movie\\attractdemo_cn_cn.wmv", "sound\\attractdemo_cn_cn.nus3bank"}; + for (int i = 0; i < 2; i++) { + if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\") + movieToCheck[i])) continue; + demoMovieExistAll = false; + } + if (demoMovieExistAll) { + LogMessage (LogLevel::INFO, "Detected attractdemo_cn files, install attractdemo patches!"); + INSTALL_FAST_HOOK_DYNAMIC (LoadDemoMovie, ASLR (0x1404313F0)); + } + + bool onpCnExist = false; + if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\textures\\onpu_cn\\onp_all.nutexb"))) onpCnExist = true; + if (onpCnExist) { + LogMessage (LogLevel::INFO, "Detected onpu_cn files, install onp patches!"); + INSTALL_MID_HOOK_DYNAMIC (ChangeOnpFile, ASLR (0x140134D16)); + } + language_patch.push_back (safetyhook::create_mid (ASLR (0x140134E22), [](SafetyHookContext &ctx){ + if (ctx.rcx == 4) *(int *)(ctx.r15 + 0x3100) = 2; + else *(int *)(ctx.r15 + 0x3100) = (int)ctx.rcx; + ctx.rip = ASLR (0x140134E29); + })); + + INSTALL_FAST_HOOK_DYNAMIC (GraphicBaseGetLanguageType, ASLR (0x1400E8740)); + language_patch.push_back (safetyhook::create_mid (ASLR (0x1400E8791), [](SafetyHookContext &ctx) + { InjectLanguageType (ctx.rsp + 0x58 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x1400E8796); })); // HitEffectGetLanguageType + language_patch.push_back (safetyhook::create_mid (ASLR (0x14011F8E4), [](SafetyHookContext &ctx) + { InjectLanguageType (ctx.rbp + 0x57 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x14011F8E9); })); // KusudamaGetLanguageType + language_patch.push_back (safetyhook::create_mid (ASLR (0x140122404), [](SafetyHookContext &ctx) + { InjectLanguageType (ctx.rbp + 0x140 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x14012240C); })); // NoteJumpGetLanguageType + language_patch.push_back (safetyhook::create_mid (ASLR (0x14012425E), [](SafetyHookContext &ctx) + { InjectLanguageType (ctx.rbp + 0x57 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x140124263); })); // RendaEffectGetLanguageType + language_patch.push_back (safetyhook::create_mid (ASLR (0x140129450), [](SafetyHookContext &ctx) + { InjectLanguageType (ctx.rbp + 0x57 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x140129455); })); // RendaEffectGetLanguageType + language_patch.push_back (safetyhook::create_mid (ASLR (0x14012B8C8), [](SafetyHookContext &ctx) + { InjectLanguageType (ctx.rbp + 0x180 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x14012B8D0); })); // ScoreGetLanguageType + LayeredFs::RegisterBefore ([&] (const std::string &originalFileName, const std::string ¤tFileName) -> std::string { + if (language != 4 || currentFileName.find ("\\lumen\\") == std::string::npos) return ""; + std::string fileName = currentFileName; + fileName = replace (fileName, "\\lumen\\", "\\lumen_cn\\"); + if (std::filesystem::exists (fileName)) return fileName; + return ""; + }); + + } break; + } +} +} + +namespace Voice { +std::map voiceCnExist; +std::map nus3bankMap; +bool enableSwitchVoice = false; +int nus3bankIdCounter = 0; +std::mutex nus3bankMtx; + +int +get_bank_id (const std::string &bankName) { + if (!nus3bankMap.contains (bankName)) { + nus3bankMap[bankName] = nus3bankIdCounter; + nus3bankIdCounter++; + } + LogMessage (LogLevel::DEBUG, "LoadBank {} id={}", bankName, nus3bankMap[bankName]); + return nus3bankMap[bankName]; +} + +void +check_voice_tail (const std::string &bankName, u8 *pBinfBlock, std::map &voiceExist, const std::string &tail) { + // check if any voice_xxx.nus3bank has xxx_cn audio inside while loading + if (bankName.starts_with ("voice_")) { + const int binfLength = *reinterpret_cast (pBinfBlock + 4); + u8 *pGrpBlock = pBinfBlock + 8 + binfLength; + const int grpLength = *reinterpret_cast (pGrpBlock + 4); + u8 *pDtonBlock = pGrpBlock + 8 + grpLength; + const int dtonLength = *reinterpret_cast (pDtonBlock + 4); + u8 *pToneBlock = pDtonBlock + 8 + dtonLength; + const int toneSize = *reinterpret_cast (pToneBlock + 8); + u8 *pToneBase = pToneBlock + 12; + for (int i = 0; i < toneSize; i++) { + if (*reinterpret_cast (pToneBase + i * 8 + 4) <= 0x0C) continue; // skip empty space + u8 *currToneBase = pToneBase + *reinterpret_cast (pToneBase + i * 8); + int titleOffset = -1; + switch (*currToneBase) { + case 0xFF: titleOffset = 9; break; // audio mark + case 0x7F: titleOffset = 5; break; // randomizer mark + default: continue; // unknown mark skip + } + if (titleOffset > 0) { + std::string title (reinterpret_cast (currToneBase + titleOffset)); + if (title.ends_with (tail)) { + if (!voiceExist.contains (bankName) || !voiceExist[bankName]) { + voiceExist[bankName] = true; + enableSwitchVoice = true; + } + return; + } + } + } + if (!voiceExist.contains (bankName) || voiceExist[bankName]) voiceExist[bankName] = false; + } +} + +void +checkVoiceFile (const char *fileName) { + withFile (fileName, [](u8 *bankData){ + if (bankData[0] == 'N' && bankData[1] == 'U' && bankData[2] == 'S' && bankData[3] == '3') { + const int tocLength = *reinterpret_cast (bankData + 16); + u8 *pPropBlock = bankData + 20 + tocLength; + const int propLength = *reinterpret_cast (pPropBlock + 4); + u8 *pBinfBlock = pPropBlock + 8 + propLength; + const std::string bankName (reinterpret_cast (pBinfBlock + 0x11)); + check_voice_tail (bankName, pBinfBlock, voiceCnExist, "_cn"); + } + }); +} + +MID_HOOK_DYNAMIC (GenNus3bankId, SafetyHookContext &ctx) { + LogMessage (LogLevel::HOOKS, "GenNus3bankId was called"); + std::lock_guard lock (nus3bankMtx); + if (reinterpret_cast (ctx.rcx + 8) != nullptr) { + u8 *pNus3bankFile = *reinterpret_cast (ctx.rcx + 8); + if (pNus3bankFile[0] == 'N' && pNus3bankFile[1] == 'U' && pNus3bankFile[2] == 'S' && pNus3bankFile[3] == '3') { + const int tocLength = *reinterpret_cast (pNus3bankFile + 16); + u8 *pPropBlock = pNus3bankFile + 20 + tocLength; + const int propLength = *reinterpret_cast (pPropBlock + 4); + u8 *pBinfBlock = pPropBlock + 8 + propLength; + const std::string bankName (reinterpret_cast (pBinfBlock + 0x11)); + ctx.rax = get_bank_id (bankName); + } + } +} + +std::string +FixToneName (const std::string &bankName, std::string toneName, int voiceLang) { + if (voiceLang == 1) { + if (voiceCnExist.contains (bankName) && voiceCnExist[bankName]) return toneName + "_cn"; + } + return toneName; +} + +size_t commonSize = 0; +FAST_HOOK_DYNAMIC (i64, PlaySoundMain, i64 a1) { + LogMessage (LogLevel::HOOKS, "PlaySoundMain was called"); + int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem"); + if (lang > 0) { + const std::string bankName (lua_tolstring (a1, -3, &commonSize)); + if (bankName[0] == 'v') { + lua_pushstring (a1, FixToneName (bankName, lua_tolstring (a1, -2, &commonSize), lang).c_str ()); + lua_replace (a1, -3); + } + } + return originalPlaySoundMain.fastcall (a1); +} + +FAST_HOOK_DYNAMIC (i64, PlaySoundMulti, i64 a1) { + LogMessage (LogLevel::HOOKS, "PlaySoundMulti was called"); + int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem"); + if (lang > 0) { + const std::string bankName (const_cast (lua_tolstring (a1, -3, &commonSize))); + if (bankName[0] == 'v') { + lua_pushstring (a1, FixToneName (bankName, lua_tolstring (a1, -2, &commonSize), lang).c_str ()); + lua_replace (a1, -3); + } + } + return originalPlaySoundMulti.fastcall (a1); +} + +u64 * +FixToneNameEnso (u64 *Src, const std::string &bankName, int voiceLang) { + if (voiceLang == 1) { + if (voiceCnExist.contains (bankName) && voiceCnExist[bankName]) Src = (u64 *)std_append (Src, "_cn", 3); + } + return Src; +} + +FAST_HOOK_DYNAMIC (bool, PlaySoundEnso, u64 *a1, u64 *a2, i64 a3) { + LogMessage (LogLevel::HOOKS, "PlaySoundEnso was called"); + int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem"); + if (lang > 0) { + const std::string bankName = a1[3] > 0x10 ? std::string (*reinterpret_cast (a1)) : std::string (reinterpret_cast (a1)); + if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName, lang); + } + return originalPlaySoundEnso.fastcall (a1, a2, a3); +} + +FAST_HOOK_DYNAMIC (bool, PlaySoundSpecial, u64 *a1, u64 *a2) { + LogMessage (LogLevel::HOOKS, "PlaySoundSpecial was called"); + int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem"); + if (lang > 0) { + const std::string bankName = a1[3] > 0x10 ? std::string (*reinterpret_cast (a1)) : std::string (reinterpret_cast (a1)); + if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName, lang); + } + return originalPlaySoundSpecial.fastcall (a1, a2); +} + +void +Init () { + switch (gameVersion) { + default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + case GameVersion::JPN39: { + INSTALL_MID_HOOK_DYNAMIC (GenNus3bankId, ASLR (0x1407B97BD)); + std::vector voiceFiles = { + "voice_aienso_v12g.nus3bank", "voice_airesult_v12g.nus3bank", "voice_aistory_v12g.nus3bank", + "voice_aoharu_v1238.nus3bank", "voice_common_v12a.nus3bank", "voice_daniodai_v12e.nus3bank", + "voice_daniresult_v12e.nus3bank", "voice_enso_v12a.nus3bank", "voice_entry_v12a.nus3bank", + "voice_howtoplay_v12a.nus3bank", "voice_result_v12a.nus3bank", "voice_senkuoku_v12a.nus3bank" + }; + for (std::string voiceFile : voiceFiles) checkVoiceFile (("..\\..\\Data\\x64\\sound\\" + voiceFile).c_str ()); + if (enableSwitchVoice) { + TestMode::RegisterItemAfter( + L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='LanguageItem']", + L"", + [&](){ + INSTALL_FAST_HOOK_DYNAMIC (PlaySoundMain, ASLR (0x1404C6DC0)); + INSTALL_FAST_HOOK_DYNAMIC (PlaySoundMulti, ASLR (0x1404C6D60)); + INSTALL_FAST_HOOK_DYNAMIC (PlaySoundEnso, ASLR (0x1404ED590)); + INSTALL_FAST_HOOK_DYNAMIC (PlaySoundSpecial, ASLR (0x1404ED230)); + } + ); + } + } break; + } +} +} + +void +Init () { + switch (gameVersion) { + default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + case GameVersion::JPN39: { + languageFix = [](int lang){ return lang == 4 ? 2 : lang; }; + taiko_string = (Taiko_string)ASLR (0x1400203B0); + taiko_assign = (Taiko_assign)ASLR (0x140020850); + std_assign = (Taiko_assign)ASLR (0x1400209E0); + std_append = (Taiko_assign)ASLR (0x140028DA0); + Language::Font::Init (); + Language::Datatable::Init (); + Language::Script::Init (); + Language::Lumen::Init (); + Language::Voice::Init (); + } break; + } +} +} \ No newline at end of file diff --git a/src/patches/layeredfs.cpp b/src/patches/layeredfs.cpp index 434dbbe..54dfe29 100644 --- a/src/patches/layeredfs.cpp +++ b/src/patches/layeredfs.cpp @@ -251,51 +251,40 @@ LayeredFsHandler (const std::string &originalFileName, const std::string ¤ return ""; // we return an empty string, causing the rest of CreateFileAHook to not update the returned value } -HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, - LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { +FAST_HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { bool changed = false; const auto originalFileName = std::string (lpFileName); std::string currentFileName = originalFileName; - LogMessage (LogLevel::HOOKS, ("CreateFileA: " + originalFileName).c_str ()); + LogMessage (LogLevel::HOOKS, "CreateFileA: {}", originalFileName); if (!beforeHandlers.empty ()) { for (const auto handler : beforeHandlers) { std::string result = handler->handlerMethod (originalFileName, currentFileName); - if (result != "") { - currentFileName = result; - changed = true; - } + if (result != "") { currentFileName = result; changed = true; } } } if (useLayeredFs) { const std::string result = LayeredFsHandler (originalFileName, currentFileName); - if (result != "") { - currentFileName = result; - changed = true; - } + if (result != "") { currentFileName = result; changed = true; } } if (!afterHandlers.empty ()) { for (const auto handler : afterHandlers) { std::string result = handler->handlerMethod (originalFileName, currentFileName); - if (result != "") { - currentFileName = result; - changed = true; - } + if (result != "") { currentFileName = result; changed = true; } } } LPCSTR pFinalFileName = changed ? currentFileName.c_str () : lpFileName; - if (changed) LogMessage (LogLevel::DEBUG, "Final Redirect: {}", currentFileName); - return originalCreateFileAHook (pFinalFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, - dwFlagsAndAttributes, hTemplateFile); + if (changed) LogMessage (LogLevel::HOOKS, "Final Redirect: {}", currentFileName); + return originalCreateFileAHook.stdcall (pFinalFileName, dwDesiredAccess, dwShareMode, + lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); } void Init () { - // LogMessage (LogLevel::INFO, "Init LayeredFs patches"); - const auto configPath = std::filesystem::current_path () / "config.toml"; const std::unique_ptr config_ptr (openConfig (configPath), toml_free); if (config_ptr) { @@ -306,17 +295,19 @@ Init () { if (useLayeredFs || !beforeHandlers.empty () || !afterHandlers.empty ()) { LogMessage (LogLevel::INFO, "using LayeredFs! Data_mods={} beforHandlers={} afterHandlers={}", useLayeredFs ? "enabled" : "disabled", beforeHandlers.size (), afterHandlers.size ()); - INSTALL_HOOK (CreateFileAHook); + INSTALL_FAST_HOOK (CreateFileAHook); } } void RegisterBefore (const std::function &fileHandler) { + LogMessage (LogLevel::DEBUG, "Registered Before"); beforeHandlers.push_back (new RegisteredHandler (fileHandler)); } void RegisterAfter (const std::function &fileHandler) { + LogMessage (LogLevel::DEBUG, "Registered After"); afterHandlers.push_back (new RegisteredHandler (fileHandler)); } diff --git a/src/patches/patches.h b/src/patches/patches.h index 4e95601..2bbb60c 100644 --- a/src/patches/patches.h +++ b/src/patches/patches.h @@ -38,10 +38,8 @@ void Init (); } // namespace AmAuth namespace LayeredFs { void Init (); -void RegisterBeforeA (const std::function &fileHandler); -void RegisterAfterA (const std::function &fileHandler); -void RegisterBeforeW (const std::function &fileHandler); -void RegisterAfterW (const std::function &fileHandler); +void RegisterBefore (const std::function &fileHandler); +void RegisterAfter (const std::function &fileHandler); } // namespace LayeredFs namespace TestMode { class Applicable { @@ -121,4 +119,7 @@ std::vector &ReadQRImage (std::vector &buffer); namespace Timer { void Init (); } // namespace timer +namespace Language { +void Init (); +} } // namespace patches diff --git a/src/patches/scanner.cpp b/src/patches/scanner.cpp index 5a574b8..8f8ad2c 100644 --- a/src/patches/scanner.cpp +++ b/src/patches/scanner.cpp @@ -53,7 +53,7 @@ namespace Card { InsertCard (uint8_t cardData[168], bool internalInvoke) { if (callbackTouch) { state = internalInvoke ? State::Disable : State::CopyWait; - patches::Plugins::UpdateStatus (1, false); + patches::Plugins::UpdateStatus (StatusType::CardStatus, false); if (!internalInvoke) { LogMessage (LogLevel::DEBUG, "[Card] Call CallbackTouch!"); for (int i = 0; i < 8; i++) { @@ -73,7 +73,7 @@ namespace Card { InsertError (CardErrorCode errorCode) { if (callbackTouch) { state = State::CopyWait; - patches::Plugins::UpdateStatus (1, false); + patches::Plugins::UpdateStatus (StatusType::CardStatus, false); callbackTouch (0, errorCode, cardData, touchData); } } @@ -82,7 +82,7 @@ namespace Card { AgentCallbackTouch (int32_t a1, int32_t a2, uint8_t *a3, uint64_t a4) { if (callbackTouch) { state = State::CopyWait; - patches::Plugins::UpdateStatus (1, false); + patches::Plugins::UpdateStatus (StatusType::CardStatus, false); callbackTouch (a1, a2, a3, a4); } } @@ -91,7 +91,7 @@ namespace Card { AgentCallbackTouchOfficial (int32_t a1, int32_t a2, uint8_t *a3, uint64_t a4) { if (callbackTouch) { state = State::CopyWait; - patches::Plugins::UpdateStatus (1, false); + patches::Plugins::UpdateStatus (StatusType::CardStatus, false); LogMessage (LogLevel::DEBUG, "Official CallbackTouch a1={} a2={}", a1, a2); if (acceptInvalidCards && !a3[0]) { char AccessId[21] = "00000000000000000001"; @@ -154,7 +154,7 @@ namespace Card { } FAST_HOOK (u64, bngrw_ReqWaitTouch, PROC_ADDRESS ("bngrw.dll", "BngRwReqWaitTouch"), u32 a1, i32 a2, u32 a3, CallbackTouch callback, u64 a5) { state = State::Ready; - patches::Plugins::UpdateStatus (1, true); + patches::Plugins::UpdateStatus (StatusType::CardStatus, true); patches::Plugins::WaitTouch (Internal::AgentCallbackTouch, a5); callbackTouch = callback; touchData = a5; @@ -164,13 +164,13 @@ namespace Card { HOOK (i64, bngrw_ReqCancelOfficial, PROC_ADDRESS ("bngrw.dll", "BngRwReqCancel"), u32 a1) { if (state != State::Disable) { state = State::Disable; - patches::Plugins::UpdateStatus (1, false); + patches::Plugins::UpdateStatus (StatusType::CardStatus, false); } return originalbngrw_ReqCancelOfficial (a1); } HOOK (u64, bngrw_ReqWaitTouchOfficial, PROC_ADDRESS ("bngrw.dll", "BngRwReqWaitTouch"), u32 a1, i32 a2, u32 a3, CallbackTouch callback, u64 a5) { state = State::Ready; - patches::Plugins::UpdateStatus (1, true); + patches::Plugins::UpdateStatus (StatusType::CardStatus, true); callbackTouch = callback; touchData = a5; return originalbngrw_ReqWaitTouchOfficial (a1, a2, a3, Internal::AgentCallbackTouchOfficial, a5); @@ -291,7 +291,7 @@ namespace Qr { scanQueue.pop (); } state = State::Ready; - patches::Plugins::UpdateStatus (2, true); + patches::Plugins::UpdateStatus (StatusType::QrStatus, true); } return 0; } @@ -339,7 +339,7 @@ namespace Qr { if (state != State::Disable) { if ((lastScan + 200) < std::chrono::duration_cast(std::chrono::system_clock::now ().time_since_epoch ()).count ()) { state = State::Disable; - patches::Plugins::UpdateStatus (2, false); + patches::Plugins::UpdateStatus (StatusType::QrStatus, false); } else { void *plugin = patches::Plugins::CheckQr (); if (plugin) { diff --git a/src/patches/testmode.cpp b/src/patches/testmode.cpp index dede594..d00a7e4 100644 --- a/src/patches/testmode.cpp +++ b/src/patches/testmode.cpp @@ -17,25 +17,6 @@ std::thread *hookInstall; std::mutex hooksMutex; std::condition_variable hooksCV; -// void -// AddHookInLoop (std::function installMethod) { -// // hooks.push (installMethod); -// hooksCV.notify_all (); -// } - -// void -// HookInstallLoop () { -// std::unique_lock hooksLock(hooksMutex); -// while (!hookInstallFinished) { -// while (!hooks.empty ()) { -// auto installMethod = hooks.front (); -// hooks.pop (); -// if (installMethod) installMethod (); -// } -// if (!hookInstallFinished) hooksCV.wait (hooksLock); -// } -// } - std::wstring mergeCondition (std::wstring original, std::wstring addition, std::wstring value) { size_t pos = original.find (L"/"); @@ -56,18 +37,6 @@ addConditionValue (pugi::xml_node *node, std::wstring key, std::wstring addition attr.set_value (mergeCondition(attr.value (), addition, value).c_str ()); } else node->append_attribute (key.c_str ()) = std::format(L"True/{}:{}", addition, value).c_str (); } -class RegisteredHook : public Applicable { -public: - std::function registerInit; - RegisteredHook (const std::function &initMethod) { - this->registerInit = initMethod; - } - pugi::xml_node * - Apply (pugi::xml_node *doc, pugi::xml_node *node) override { - // AddHookInLoop (this->registerInit); - return node; - } -}; class RegisteredItem : public Applicable { public: std::wstring selectItem; @@ -237,26 +206,27 @@ Menu *modManager = new RegisteredMenu (L"MOD MANAGER", L"ModManagerMenu"); // std::vector registeredItems = {}; std::vector registeredSingleItems = {}; std::vector registeredModifies = {}; -std::vector registeredHooks = {}; std::wstring moddedInitial = L""; std::wstring modded = L""; +std::wstring originalDeviceInitialize = L""; +std::wstring usingDeviceInitialize = L""; +std::wstring moddedDeviceInitialize = L"DeviceInitialize_mod.xml"; +std::wstring originalTestMode = L""; +std::wstring usingTestMode = L""; +std::wstring moddedTestMode = L"TestMode_mod.xml"; + u64 appAccessor = 0; RefTestModeMain refTestMode = nullptr; -std::wstring -ReadXMLFileSwitcher (std::wstring &fileName) { - const std::size_t pos = fileName.rfind (L"/"); - const std::wstring file = fileName.substr (pos + 1, fileName.size ()); - std::wstring base = fileName.substr (0, pos + 1); +FAST_HOOK_DYNAMIC (char, SceneTestModeLoading, u64 a1, u64 a2, u64 a3) { + char result = originalSceneTestModeLoading.fastcall (a1, a2, a3); + return result; +} - if (gameVersion == GameVersion::JPN39 && chsPatch) { - if (file.starts_with (L"DeviceInitialize")) base.append (L"DeviceInitialize_china.xml"); - if (file.starts_with (L"TestMode")) base.append (L"TestMode_china.xml"); - if (std::filesystem::exists (base)) return base; - } - - return fileName; +FAST_HOOK_DYNAMIC (char, SceneTestModeFinalize, u64 a1, u64 a2, u64 a3) { + char result = originalSceneTestModeFinalize.fastcall (a1, a2, a3); + return result; } FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t *lFileName) { @@ -264,17 +234,15 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t const auto originalFileName = std::wstring (lFileName); LogMessage (LogLevel::DEBUG, L"Begin Loading {}", originalFileName); std::wstring fileName = originalFileName; - if (fileName.ends_with (L"DeviceInitialize.xml") || fileName.ends_with (L"DeviceInitialize_asia.xml") - || fileName.ends_with (L"DeviceInitialize_china.xml")) { + if (fileName.ends_with (originalDeviceInitialize)) { if (moddedInitial == L"") { - fileName = ReadXMLFileSwitcher (fileName); + fileName = replace (originalFileName, originalDeviceInitialize, usingDeviceInitialize); if (pugi::xml_document doc; !doc.load_file (fileName.c_str ())) { LogMessage (LogLevel::ERROR, L"Loading DeviceInitialize structure failed! path: " + fileName); - moddedInitial = fileName; + moddedInitial = originalFileName; + fileName = originalFileName; } else { - const std::wstring modFileName - = replace (replace (replace (fileName, L"lize_asia.xml", L"lize_mod.xml"), L"lize_china.xml", L"lize_mod.xml"), L"lize.xml", - L"lize_mod.xml"); + const std::wstring modFileName = replace (originalFileName, originalDeviceInitialize, moddedDeviceInitialize); const pugi::xpath_query dongleQuery = pugi::xpath_query (L"/root/menu[@id='TopMenu']/layout[@type='Center']/select-item[@id='DongleItem']"); const pugi::xml_node dongleItem = doc.select_node (dongleQuery).node (); pugi::xml_node talItem = dongleItem.parent ().append_copy (dongleItem); @@ -288,17 +256,16 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t fileName = modFileName; } } else fileName = moddedInitial; - } else if (fileName.ends_with (L"TestMode.xml") || fileName.ends_with (L"TestMode_asia.xml") || fileName.ends_with (L"TestMode_china.xml")) { + } else if (fileName.ends_with (originalTestMode)) { if (modded == L"") { if (!((RegisteredMenu*)modManager)->items.empty () || registeredSingleItems.empty () || !registeredModifies.empty ()) { - fileName = ReadXMLFileSwitcher (fileName); + fileName = replace (originalFileName, originalTestMode, usingTestMode); if (pugi::xml_document doc; !doc.load_file (fileName.c_str ())) { LogMessage (LogLevel::ERROR, L"Loading TestMode structure failed! path: " + fileName); - modded = fileName; + modded = originalFileName; + fileName = originalFileName; } else { - const std::wstring modFileName - = replace (replace (replace (fileName, L"Mode_asia.xml", L"Mode_mod.xml"), L"Mode_china.xml", L"Mode_mod.xml"), L"Mode.xml", - L"Mode_mod.xml"); + const std::wstring modFileName = replace (originalFileName, originalTestMode, moddedTestMode); if (!((RegisteredMenu*)modManager)->items.empty ()) { LogMessage (LogLevel::DEBUG, "Begin generate Mod Manager menu"); pugi::xpath_query menuQuery @@ -320,13 +287,6 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t LogMessage (LogLevel::DEBUG, "End Apply Modify Items"); } - if (!registeredHooks.empty ()) { - LogMessage (LogLevel::DEBUG, "Begin Apply Hooks"); - for (RegisteredHook *hook : registeredHooks) hook->Apply (&doc, &doc); - LogMessage (LogLevel::DEBUG, "End Apply Hooks"); - } - - // AddHookInLoop ([&](){ hookInstallFinished = true; }); [[maybe_unused]] auto _ = doc.save_file (modFileName.c_str ()); modded = modFileName; fileName = modFileName; @@ -340,16 +300,6 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t originalTestModeSetMenuHook.fastcall (testModeLibrary, fileName.c_str ()); } -// HOOK_DYNAMIC (void, TestModeGetValueHook, u64 *reader, const wchar_t *lItemName, int *value) { -// originalTestModeGetValueHook (reader, lItemName, value); -// LogMessage (LogLevel::DEBUG, L"TestMode get Item: {} Value: {}", lItemName, *value); -// } - -// HOOK_DYNAMIC (void, TestModeSetValueHook, u64 *reader, const wchar_t *lItemName, int value) { -// LogMessage (LogLevel::DEBUG, L"TestMode set Item: {} Value: {}", lItemName, value); -// originalTestModeSetValueHook (reader, lItemName, value); -// } - void CommonModify () { // Default off Close time @@ -482,28 +432,29 @@ void Init () { LogMessage (LogLevel::INFO, "Init TestMode patches"); - const auto configPath = std::filesystem::current_path () / "config.toml"; - const std::unique_ptr config_ptr (openConfig (configPath), toml_free); - const u64 testModeLibrary = (u64)GetModuleHandle ("TestModeLibrary.dll"); const u64 testModeSetMenu = testModeLibrary + 0x99D0; - // const u64 testModeGetValue = testModeLibrary + 0x6FB18; - // const u64 testModeSetValue = testModeLibrary + 0x6FAC8; - LogMessage (LogLevel::DEBUG, "TestModeLibrary: {}", testModeLibrary); - // LogMessage (LogLevel::DEBUG, "TestModeLibrary set: {}", testModeSetValue); - // LogMessage (LogLevel::DEBUG, "TestModeLibrary get: {}", testModeGetValue); switch (gameVersion) { case GameVersion::UNKNOWN: break; case GameVersion::JPN00: break; case GameVersion::JPN08: break; case GameVersion::JPN39: { - if (config_ptr) { - if (const auto patches = openConfigSection (config_ptr.get (), "patches")) { - if (const auto jpn39 = openConfigSection (patches, "jpn39")) chsPatch = readConfigBool (jpn39, "chs_patch", chsPatch); - } - } - if (chsPatch) LocalizationCHT (); + INSTALL_FAST_HOOK_DYNAMIC (SceneTestModeLoading, ASLR (0x1404793D0)); + INSTALL_FAST_HOOK_DYNAMIC (SceneTestModeFinalize, ASLR (0x140479600)); + originalDeviceInitialize = L"DeviceInitialize.xml"; + if (std::filesystem::exists ("..\\..\\Data\\x64\\testmode\\DeviceInitialize_chspatch.xml")) { + usingDeviceInitialize = L"DeviceInitialize_chspatch.xml"; + WRITE_MEMORY (ASLR (0x140CD1E40), wchar_t, L"加載中\0"); + WRITE_MEMORY (ASLR (0x140CD1E28), wchar_t, L"加載中.\0"); + WRITE_MEMORY (ASLR (0x140CD1E68), wchar_t, L"加載中..\0"); + WRITE_MEMORY (ASLR (0x140CD1E50), wchar_t, L"加載中...\0"); + } else usingDeviceInitialize = originalDeviceInitialize; + originalTestMode = L"TestMode.xml"; + if (std::filesystem::exists ("..\\..\\Data\\x64\\testmode\\TestMode_chspatch.xml")) { + usingTestMode = L"TestMode_chspatch.xml"; + } else usingTestMode = originalTestMode; + } break; case GameVersion::CHN00: break; } @@ -512,8 +463,6 @@ Init () { INSTALL_FAST_HOOK_DYNAMIC (TestModeSetMenuHook, testModeSetMenu); for (auto method : hooks) method (); - // INSTALL_HOOK_DYNAMIC (TestModeGetValueHook, testModeGetValue); - // INSTALL_HOOK_DYNAMIC (TestModeSetValueHook, testModeSetValue); } void @@ -528,13 +477,10 @@ ReadTestModeValue (const wchar_t *itemId) { if (const u64 testModeMain = refTestMode (appAccessor)) { int value = 0; u64 *reader = *reinterpret_cast (testModeMain + 16); - // LogMessage (LogLevel::DEBUG, "TestMode get:{} set:{}", *reader + 256, *reader + 176); (*reinterpret_cast (*reader + 256)) (reader, itemId, &value); - // originalTestModeGetValueHook (reader, itemId, &value); return value; } } - LogMessage (LogLevel::ERROR, (std::wstring (L"Read TestMode(") + itemId + L") failed!").c_str ()); return -1; } @@ -544,7 +490,6 @@ SetTestModeValue (const wchar_t *itemId, int value) { if (const u64 testModeMain = refTestMode (appAccessor)) { u64 *reader = *reinterpret_cast (testModeMain + 16); (*reinterpret_cast (*reader + 176)) (reader, itemId, value); - // ((TestModeSetValueHook)whereTestModeSetMenuHook) (reader, itemId, value); } } } @@ -629,7 +574,6 @@ void RegisterHook (const std::function &initMethod) { LogMessage (LogLevel::DEBUG, L"Register Hook"); hooks.push_back (initMethod); - // registeredHooks.push_back (new RegisteredHook (initMethod)); } void diff --git a/src/patches/versions/JPN39.cpp b/src/patches/versions/JPN39.cpp index 6ee5022..028d3fc 100644 --- a/src/patches/versions/JPN39.cpp +++ b/src/patches/versions/JPN39.cpp @@ -192,195 +192,11 @@ CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_AI, ASLR (0x140173EDD), r13, 0x2 CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_Collabo025_026, ASLR (0x1401789AD), r13, 0x24, 0x08); CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_AprilFool, ASLR (0x140176716), rax, 0x34, 0x06); -FAST_HOOK (i64, GetLanguage, ASLR (0x140024AC0), i64 a1) { - LogMessage (LogLevel::HOOKS, "GetLanguage was called"); - const auto result = originalGetLanguage.fastcall (a1); - language = *reinterpret_cast (result); - return result; -} -FAST_HOOK (i64, GetRegionLanguage, ASLR (0x1401CE9B0), i64 a1) { - LogMessage (LogLevel::HOOKS, "GetRegionLanguage was called"); - if (patches::TestMode::ReadTestModeValue (L"ModFixLanguage") == 1) { - lua_settop (a1, 0); - lua_pushstring (a1, languageStr (language)); - return 1; - } else return originalGetRegionLanguage.fastcall (a1); -} -std::thread::id *LoadMovieThreadId = nullptr; -FUNCTION_PTR (void **, std_string_assign, ASLR (0x1400209E0), void **, const void *, size_t); -FAST_HOOK (i64, GetCabinetLanguage, ASLR (0x14014DB80), u64 *a1, i64 a2) { - LogMessage (LogLevel::HOOKS, "GetCabinetLanguage was called"); - i64 result = originalGetCabinetLanguage.fastcall (a1, a2); - if (patches::TestMode::ReadTestModeValue (L"ModFixLanguage") == 1) { - if (language == 2 && LoadMovieThreadId != nullptr && *LoadMovieThreadId == std::this_thread::get_id()) { - LogMessage (LogLevel::DEBUG, "Loading DemoMovie cn_tw, redirect to cn_cn"); - std_string_assign ((void **)result, (const void*)languageStr (4), 5); - } else { - std_string_assign ((void **)result, (const void*)languageStr (language), (language == 0 || language == 3) ? 3 : 5); - } - } - return result; -} -FAST_HOOK (i64, LoadDemoMovie, ASLR (0x1404313F0), i64 a1, i64 a2, i64 a3) { - std::thread::id this_id = std::this_thread::get_id(); - LoadMovieThreadId = &this_id; - i64 result = originalLoadDemoMovie.fastcall (a1, a2, a3); - LoadMovieThreadId = nullptr; - return result; -} -std::string onpCn = "textures/onpu_cn/onp_all.nutexb"; -MID_HOOK (ChangeOnpFile, ASLR (0x140134D16), SafetyHookContext &ctx) { - if (language == 2 && patches::TestMode::ReadTestModeValue (L"ModFixLanguage") == 1) { - LogMessage (LogLevel::DEBUG, "Load onp_all_cn"); - ctx.rdx = (uintptr_t) onpCn.c_str (); - ctx.r8 = 0x1F; - } -} - -MID_HOOK (ChangeLanguageType, ASLR (0x1400B2016), SafetyHookContext &ctx) { - LogMessage (LogLevel::HOOKS, "ChangeLanguageType was called"); - if (const auto pFontType = reinterpret_cast (ctx.rax); *pFontType == 4) *pFontType = 2; -} - MID_HOOK (CountLockedCrown, ASLR (0x1403F2A25), SafetyHookContext &ctx) { LogMessage (LogLevel::HOOKS, "CountLockedCrown was called"); ctx.r15 |= 1; } -std::map nus3bankMap; -int nus3bankIdCounter = 0; -std::map voiceCnExist; -bool enableSwitchVoice = false; -std::mutex nus3bankMtx; - -int -get_bank_id (const std::string &bankName) { - if (!nus3bankMap.contains (bankName)) { - nus3bankMap[bankName] = nus3bankIdCounter; - nus3bankIdCounter++; - } - LogMessage (LogLevel::DEBUG, "LoadBank {} id={}", bankName, nus3bankMap[bankName]); - return nus3bankMap[bankName]; -} - -void -check_voice_tail (const std::string &bankName, u8 *pBinfBlock, std::map &voiceExist, const std::string &tail) { - // check if any voice_xxx.nus3bank has xxx_cn audio inside while loading - if (bankName.starts_with ("voice_")) { - const int binfLength = *reinterpret_cast (pBinfBlock + 4); - u8 *pGrpBlock = pBinfBlock + 8 + binfLength; - const int grpLength = *reinterpret_cast (pGrpBlock + 4); - u8 *pDtonBlock = pGrpBlock + 8 + grpLength; - const int dtonLength = *reinterpret_cast (pDtonBlock + 4); - u8 *pToneBlock = pDtonBlock + 8 + dtonLength; - const int toneSize = *reinterpret_cast (pToneBlock + 8); - u8 *pToneBase = pToneBlock + 12; - for (int i = 0; i < toneSize; i++) { - if (*reinterpret_cast (pToneBase + i * 8 + 4) <= 0x0C) continue; // skip empty space - u8 *currToneBase = pToneBase + *reinterpret_cast (pToneBase + i * 8); - int titleOffset = -1; - switch (*currToneBase) { - case 0xFF: titleOffset = 9; break; // audio mark - case 0x7F: titleOffset = 5; break; // randomizer mark - default: continue; // unknown mark skip - } - if (titleOffset > 0) { - std::string title (reinterpret_cast (currToneBase + titleOffset)); - if (title.ends_with (tail)) { - if (!voiceExist.contains (bankName) || !voiceExist[bankName]) { - voiceExist[bankName] = true; - enableSwitchVoice = true; - } - return; - } - } - } - if (!voiceExist.contains (bankName) || voiceExist[bankName]) voiceExist[bankName] = false; - } -} - -MID_HOOK (GenNus3bankId, ASLR (0x1407B97BD), SafetyHookContext &ctx) { - LogMessage (LogLevel::HOOKS, "GenNus3bankId was called"); - std::lock_guard lock (nus3bankMtx); - if (reinterpret_cast (ctx.rcx + 8) != nullptr) { - u8 *pNus3bankFile = *reinterpret_cast (ctx.rcx + 8); - if (pNus3bankFile[0] == 'N' && pNus3bankFile[1] == 'U' && pNus3bankFile[2] == 'S' && pNus3bankFile[3] == '3') { - const int tocLength = *reinterpret_cast (pNus3bankFile + 16); - u8 *pPropBlock = pNus3bankFile + 20 + tocLength; - const int propLength = *reinterpret_cast (pPropBlock + 4); - u8 *pBinfBlock = pPropBlock + 8 + propLength; - const std::string bankName (reinterpret_cast (pBinfBlock + 0x11)); - check_voice_tail (bankName, pBinfBlock, voiceCnExist, "_cn"); - ctx.rax = get_bank_id (bankName); - } - } -} - -std::string -FixToneName (const std::string &bankName, std::string toneName, int voiceLang) { - if (voiceLang == 1) { - if (voiceCnExist.contains (bankName) && voiceCnExist[bankName]) return toneName + "_cn"; - } - return toneName; -} - -size_t commonSize = 0; -FAST_HOOK (i64, PlaySoundMain, ASLR (0x1404C6DC0), i64 a1) { - LogMessage (LogLevel::HOOKS, "PlaySoundMain was called"); - int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem"); - if (enableSwitchVoice && lang != 0) { - const std::string bankName (lua_tolstring (a1, -3, &commonSize)); - if (bankName[0] == 'v') { - lua_pushstring (a1, FixToneName (bankName, lua_tolstring (a1, -2, &commonSize), lang).c_str ()); - lua_replace (a1, -3); - } - } - return originalPlaySoundMain.fastcall (a1); -} - -FAST_HOOK (i64, PlaySoundMulti, ASLR (0x1404C6D60), i64 a1) { - LogMessage (LogLevel::HOOKS, "PlaySoundMulti was called"); - int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem"); - if (enableSwitchVoice && lang != 0) { - const std::string bankName (const_cast (lua_tolstring (a1, -3, &commonSize))); - if (bankName[0] == 'v') { - lua_pushstring (a1, FixToneName (bankName, lua_tolstring (a1, -2, &commonSize), lang).c_str ()); - lua_replace (a1, -3); - } - } - return originalPlaySoundMulti.fastcall (a1); -} - -FUNCTION_PTR (u64 *, append_chars_to_basic_string, ASLR (0x140028DA0), u64 *, const char *, size_t); - -u64 * -FixToneNameEnso (u64 *Src, const std::string &bankName, int voiceLang) { - if (voiceLang == 1) { - if (voiceCnExist.contains (bankName) && voiceCnExist[bankName]) Src = append_chars_to_basic_string (Src, "_cn", 3); - } - return Src; -} - -FAST_HOOK (bool, PlaySoundEnso, ASLR (0x1404ED590), u64 *a1, u64 *a2, i64 a3) { - LogMessage (LogLevel::HOOKS, "PlaySoundEnso was called"); - int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem"); - if (enableSwitchVoice && lang != 0) { - const std::string bankName = a1[3] > 0x10 ? std::string (*reinterpret_cast (a1)) : std::string (reinterpret_cast (a1)); - if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName, lang); - } - return originalPlaySoundEnso.fastcall (a1, a2, a3); -} - -FAST_HOOK (bool, PlaySoundSpecial, ASLR (0x1404ED230), u64 *a1, u64 *a2) { - LogMessage (LogLevel::HOOKS, "PlaySoundSpecial was called"); - int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem"); - if (enableSwitchVoice && lang != 0) { - const std::string bankName = a1[3] > 0x10 ? std::string (*reinterpret_cast (a1)) : std::string (reinterpret_cast (a1)); - if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName, lang); - } - return originalPlaySoundSpecial.fastcall (a1, a2); -} - int loaded_fail_count = 0; FAST_HOOK (char, LoadedBankAll, ASLR (0x1404EC2F0), u64 a1) { LogMessage (LogLevel::HOOKS, "LoadedBankAll was called"); @@ -405,68 +221,52 @@ FAST_HOOK (i32, SetMasterVolumeSpeaker, ASLR (0x140160330), i32 a1) { return originalSetMasterVolumeSpeaker.fastcall (a1 > 100 ? 100 : a1); } -// std::string *fontName = nullptr; -// HOOK (u8, SetupFontInfo, ASLR (0x14049D820), u64 a1, u64 a2, size_t a3, u64 a4) { -// LogMessage (LogLevel::HOOKS, "SetupFontInfo was called"); -// if (fontName != nullptr) delete fontName; -// fontName = new std::string (reinterpret_cast (a1) + 120); -// return originalSetupFontInfo (a1, a2, a3, a4); -// } - -// HOOK (u32, ReadFontInfoInt, ASLR (0x14049EAC0), u64 a1, u64 a2) { -// LogMessage (LogLevel::HOOKS, "ReadFontInfoInt was called"); -// const std::string attribute (reinterpret_cast (a2)); -// u32 result = originalReadFontInfoInt (a1, a2); -// if (fontName->starts_with ("cn_") && attribute == "offsetV") result += 1; -// return result; -// } - MID_HOOK (AttractDemo, ASLR (0x14045A720), SafetyHookContext &ctx) { if (TestMode::ReadTestModeValue (L"AttractDemoItem") == 1) ctx.r14 = 0; } -FAST_HOOK (u64, EnsoGameManagerInitialize, ASLR (0x1400E2520), u64 a1, u64 a2, u64 a3) { - LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerInitialize"); - u64 result = originalEnsoGameManagerInitialize.fastcall (a1, a2, a3); - LogMessage (LogLevel::DEBUG, "End EnsoGameManagerInitialize result={}", result); - return result; -} +// FAST_HOOK (u64, EnsoGameManagerInitialize, ASLR (0x1400E2520), u64 a1, u64 a2, u64 a3) { +// LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerInitialize"); +// u64 result = originalEnsoGameManagerInitialize.fastcall (a1, a2, a3); +// LogMessage (LogLevel::DEBUG, "End EnsoGameManagerInitialize result={}", result); +// return result; +// } -FAST_HOOK (u64, EnsoGameManagerLoading, ASLR (0x1400E2750), u64 a1, u64 a2, u64 a3) { - LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerLoading"); - u64 result = originalEnsoGameManagerLoading.fastcall (a1, a2, a3); - LogMessage (LogLevel::DEBUG, "End EnsoGameManagerLoading result={}", result); - return result; -} +// FAST_HOOK (u64, EnsoGameManagerLoading, ASLR (0x1400E2750), u64 a1, u64 a2, u64 a3) { +// LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerLoading"); +// u64 result = originalEnsoGameManagerLoading.fastcall (a1, a2, a3); +// LogMessage (LogLevel::DEBUG, "End EnsoGameManagerLoading result={}", result); +// return result; +// } -FAST_HOOK (bool, EnsoGameManagerPreparing, ASLR (0x1400E2990), u64 a1, u64 a2, u64 a3) { - LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerPreparing"); - bool result = originalEnsoGameManagerPreparing.fastcall (a1, a2, a3); // crashes here - LogMessage (LogLevel::DEBUG, "End EnsoGameManagerPreparing result={}", result); - return result; -} +// FAST_HOOK (bool, EnsoGameManagerPreparing, ASLR (0x1400E2990), u64 a1, u64 a2, u64 a3) { +// LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerPreparing"); +// bool result = originalEnsoGameManagerPreparing.fastcall (a1, a2, a3); // crashes here +// LogMessage (LogLevel::DEBUG, "End EnsoGameManagerPreparing result={}", result); +// return result; +// } -FAST_HOOK (u64, EnsoGraphicManagerPreparing, ASLR (0x1400F0AB0), u64 a1, u64 a2, u64 a3) { - LogMessage (LogLevel::DEBUG, "Begin EnsoGraphicManagerPreparing"); - u64 result = originalEnsoGraphicManagerPreparing.fastcall (a1, a2, a3); - LogMessage (LogLevel::DEBUG, "End EnsoGraphicManagerPreparing result={}", result); - return result; -} +// FAST_HOOK (u64, EnsoGraphicManagerPreparing, ASLR (0x1400F0AB0), u64 a1, u64 a2, u64 a3) { +// LogMessage (LogLevel::DEBUG, "Begin EnsoGraphicManagerPreparing"); +// u64 result = originalEnsoGraphicManagerPreparing.fastcall (a1, a2, a3); +// LogMessage (LogLevel::DEBUG, "End EnsoGraphicManagerPreparing result={}", result); +// return result; +// } -FAST_HOOK (u64, EnsoGameManagerStart, ASLR (0x1400E2A10), u64 a1, u64 a2, u64 a3) { - LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerStart"); - u64 result = originalEnsoGameManagerStart.fastcall (a1, a2, a3); - LogMessage (LogLevel::DEBUG, "End EnsoGameManagerStart result={}", result); - return result; -} +// FAST_HOOK (u64, EnsoGameManagerStart, ASLR (0x1400E2A10), u64 a1, u64 a2, u64 a3) { +// LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerStart"); +// u64 result = originalEnsoGameManagerStart.fastcall (a1, a2, a3); +// LogMessage (LogLevel::DEBUG, "End EnsoGameManagerStart result={}", result); +// return result; +// } -FAST_HOOK (u64, EnsoGameManagerChechEnsoEnd, ASLR (0x1400E2A10), u64 a1, u64 a2, u64 a3) { - LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerChechEnsoEnd"); - u64 result = originalEnsoGameManagerChechEnsoEnd.fastcall (a1, a2, a3); - LogMessage (LogLevel::DEBUG, "End EnsoGameManagerChechEnsoEnd result={}", result); - return result; -} +// FAST_HOOK (u64, EnsoGameManagerChechEnsoEnd, ASLR (0x1400E2A10), u64 a1, u64 a2, u64 a3) { +// LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerChechEnsoEnd"); +// u64 result = originalEnsoGameManagerChechEnsoEnd.fastcall (a1, a2, a3); +// LogMessage (LogLevel::DEBUG, "End EnsoGameManagerChechEnsoEnd result={}", result); +// return result; +// } // HOOK (DWORD*, AcquireMostCompatibleDisplayMode, ASLR (0x14064C870), i64 a1, DWORD *a2, DWORD *a3) { // LogMessage (LogLevel::DEBUG, "AcquireMostCompatibleDisplayMode {:d} {:d} {:f} {:f}", a3[0], a3[1], (float)(int)a3[2], (float)(int)a3[3]); @@ -475,24 +275,6 @@ FAST_HOOK (u64, EnsoGameManagerChechEnsoEnd, ASLR (0x1400E2A10), u64 a1, u64 a2, // return originalAcquireMostCompatibleDisplayMode (a1, a2, a3); // } -FAST_HOOK (char, SceneTestModeLoading, ASLR (0x1404793D0), u64 a1, u64 a2, u64 a3) { - // LogMessage (LogLevel::DEBUG, "Begin SceneTestModeLoading"); - char result = originalSceneTestModeLoading.fastcall (a1, a2, a3); - // LogMessage (LogLevel::DEBUG, "End originalSceneTestModeLoading result={}", (int)result); - TestMode::SetTestModeValue (L"EnableSwitchVoice", (int)enableSwitchVoice); - // if (!enableSwitchVoice) TestMode::SetTestModeValue (L"VoiceLanguageItem", 0); - // LogMessage (LogLevel::DEBUG, "End SceneTestModeLoading"); - return result; -} - -MID_HOOK (LogLoadTexture12, ASLR (0x14065BC8A), SafetyHookContext &ctx) { - char* src = (char*)(ctx.rsp + 0x2B0); - if (src != nullptr) { - std::string srcStr = std::string (src); - LogMessage (LogLevel::HOOKS, "LogLoadTexture12 src={}", srcStr); - } else LogMessage (LogLevel::HOOKS, "LogLoadTexture12 src=nullptr"); -} - constexpr i32 datatableBufferSize = 1024 * 1024 * 12; uint8_t *datatableBuffer[3] = { nullptr }; std::vector datatable_patch = {}; @@ -550,7 +332,6 @@ Init () { // Hook to get AppAccessor and ComponentAccessor INSTALL_FAST_HOOK (DeviceCheck); INSTALL_FAST_HOOK (luaL_newstate); - INSTALL_MID_HOOK (LogLoadTexture12); // INSTALL_HOOK (AcquireMostCompatibleDisplayMode); // Apply common config patch @@ -591,52 +372,6 @@ Init () { // Remove datatable size limit ReplaceDatatableBufferAddresses (); - // { - // for (auto address : memsetSizeAddresses) - // WRITE_MEMORY (ASLR (address) + 2, i32, datatableBufferSize); - - // auto bufferBase = MODULE_HANDLE - 0x03000000; - // AllocateStaticBufferNear (bufferBase, datatableBufferSize, &datatableBuffer1); - // bufferBase += datatableBufferSize; - // AllocateStaticBufferNear (bufferBase, datatableBufferSize, &datatableBuffer2); - // bufferBase += datatableBufferSize; - // AllocateStaticBufferNear (bufferBase, datatableBufferSize, &datatableBuffer3); - - // ReplaceLeaBufferAddress (datatableBuffer1Addresses, datatableBuffer1.data ()); - // ReplaceLeaBufferAddress (datatableBuffer2Addresses, datatableBuffer2.data ()); - // ReplaceLeaBufferAddress (datatableBuffer3Addresses, datatableBuffer3.data ()); - // } - - // Language - TestMode::Menu *langPatchesMenu = TestMode::CreateMenu (L"LANGUAGE PATCHES", L"LanguagePatchesMenu"); - TestMode::Menu *langResourceIndicator = TestMode::CreateMenu (L"RESOURCE INDICATOR", L"LanguageResourceIndicatorMenu"); - TestMode::RegisterItem ( - L"", - [&]() { - INSTALL_FAST_HOOK (GetLanguage); - INSTALL_FAST_HOOK (GetRegionLanguage); - INSTALL_FAST_HOOK (GetCabinetLanguage); - }, langPatchesMenu - ); - TestMode::RegisterItem( - L"", - langResourceIndicator - ); - TestMode::RegisterItemAfter( - L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='LanguageItem']", - L"", - [&](){ - INSTALL_FAST_HOOK (PlaySoundMain); - INSTALL_FAST_HOOK (PlaySoundMulti); - INSTALL_FAST_HOOK (PlaySoundEnso); - INSTALL_FAST_HOOK (PlaySoundSpecial); - } - ); - TestMode::RegisterItem(langResourceIndicator, langPatchesMenu); - TestMode::RegisterItem(langPatchesMenu); // Unlock Songs TestMode::RegisterItem ( @@ -713,20 +448,10 @@ Init () { ); TestMode::RegisterItemAfter( L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='AttractMovieItem']", - L"", [&](){ INSTALL_MID_HOOK (AttractDemo); } ); - TestMode::RegisterHook([&](){ - INSTALL_FAST_HOOK (EnsoGameManagerInitialize); - INSTALL_FAST_HOOK (EnsoGameManagerLoading); - INSTALL_FAST_HOOK (EnsoGameManagerPreparing); - INSTALL_FAST_HOOK (EnsoGraphicManagerPreparing); - INSTALL_FAST_HOOK (EnsoGameManagerStart); - INSTALL_FAST_HOOK (EnsoGameManagerChechEnsoEnd); - INSTALL_FAST_HOOK (SceneTestModeLoading); - }); - // for (size_t i=0; i < 40; i++) TestMode::RegisterItem(std::format (L"", i + 1)); // Instant Result // TestMode::RegisterModify( @@ -735,63 +460,8 @@ Init () { // [&](){} // ); - // Use chs font/wordlist instead of cht - if (chsPatch) { - WRITE_MEMORY (ASLR (0x140CD1E40), wchar_t, L"加載中\0"); - WRITE_MEMORY (ASLR (0x140CD1E28), wchar_t, L"加載中.\0"); - WRITE_MEMORY (ASLR (0x140CD1E68), wchar_t, L"加載中..\0"); - WRITE_MEMORY (ASLR (0x140CD1E50), wchar_t, L"加載中...\0"); - - bool fontExistAll = true; - const char *fontToCheck[]{"cn_30.nutexb", "cn_30.xml", "cn_32.nutexb", "cn_32.xml", "cn_64.nutexb", "cn_64.xml"}; - for (int i = 0; i < 6; i++) { - if (useLayeredfs && std::filesystem::exists (std::string ("..\\..\\Data_mods\\x64\\font\\") + fontToCheck[i])) continue; - if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\font\\") + fontToCheck[i])) continue; - fontExistAll = false; - } - if (fontExistAll) { - LogMessage (LogLevel::INFO, "Font all exist, using chs_patch font part!"); - WRITE_MEMORY (ASLR (0x140CD1AE0), char, "cn_64"); - WRITE_MEMORY (ASLR (0x140CD1AF0), char, "cn_32"); - WRITE_MEMORY (ASLR (0x140CD1AF8), char, "cn_30"); - WRITE_MEMORY (ASLR (0x140C946A0), char, "chineseSText"); - WRITE_MEMORY (ASLR (0x140C946B0), char, "chineseSFontType"); - INSTALL_MID_HOOK (ChangeLanguageType); - // INSTALL_HOOK (SetupFontInfo); - // INSTALL_HOOK (ReadFontInfoInt); - } - - bool demoMovieExistAll = true; - const char *movieToCheck[]{"movie\\attractdemo_cn_cn.wmv", "sound\\attractdemo_cn_cn.nus3bank"}; - for (int i = 0; i < 2; i++) { - if (useLayeredfs && std::filesystem::exists (std::string ("..\\..\\Data_mods\\x64\\") + movieToCheck[i])) continue; - if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\") + movieToCheck[i])) continue; - demoMovieExistAll = false; - } - if (demoMovieExistAll) { - LogMessage (LogLevel::INFO, "Resource all exist, using chs_patch attractdemo part!"); - INSTALL_FAST_HOOK (LoadDemoMovie); - } - - bool onpCnExist = false; - if (useLayeredfs && std::filesystem::exists (std::string ("..\\..\\Data_mods\\x64\\textures\\onpu_cn\\onp_all.nutexb"))) onpCnExist = true; - if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\textures\\onpu_cn\\onp_all.nutexb"))) onpCnExist = true; - if (onpCnExist) { - LogMessage (LogLevel::INFO, "onpu_cn/onp_all exist, using chs_patch onp part!"); - INSTALL_MID_HOOK (ChangeOnpFile); - } - - // LayeredFs::RegisterBeforeA ([=] (const std::string &originalFileName, const std::string ¤tFileName) -> std::string { - // if (currentFileName.find ("\\lumen\\") == std::string::npos) return ""; - // std::string fileName = currentFileName; - // fileName = replace (fileName, "\\lumen\\", "\\lumen_cn\\"); - // if (std::filesystem::exists (fileName)) return fileName; - // return ""; - // }); - } - // Fix normal song play after passing through silent song - INSTALL_MID_HOOK (GenNus3bankId); + // INSTALL_MID_HOOK (GenNus3bankId); INSTALL_FAST_HOOK (LoadedBankAll); // Disable live check diff --git a/src/pollasync.cpp b/src/pollasync.cpp index 361adad..a16ea99 100644 --- a/src/pollasync.cpp +++ b/src/pollasync.cpp @@ -20,10 +20,11 @@ uint8_t controllerDiff[SDL_GAMEPAD_BUTTON_COUNT] = { 0 }; char currentMouseWheelDirection = 0; uint8_t mouseWheelCount[2] = { 0 }; uint8_t mouseWheelDiff[2] = { 0 }; - -int maxCount = 1; - SDLAxisState currentControllerAxisState; +uint8_t controllerAxisCount[SDL_AXIS_MAX] = { 0 }; +uint8_t controllerAxisDiff[SDL_AXIS_MAX] = { 0 }; + +int maxCount = 2; SDL_Window *window; SDL_Gamepad *controllers[255]; @@ -136,19 +137,60 @@ LRESULT CALLBACK InputProc(int nCode, WPARAM wParam, LPARAM lParam) { return CallNextHookEx(keyboardHook, nCode, wParam, lParam); } -void -InitializeKeyboard () { - keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, InputProc, nullptr, 0); +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_DESTROY: PostQuitMessage(0); break; + default: return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; +} + +HWND CreateInvisibleWindow(HINSTANCE hInstance) { + WNDCLASS wc = {0}; + wc.lpfnWndProc = WndProc; + wc.hInstance = hInstance; + wc.lpszClassName = "InvisibleWindowClass"; + if (!RegisterClass(&wc)) return nullptr; + HWND hwnd = CreateWindowEx( + WS_EX_LAYERED | WS_EX_TOOLWINDOW, + wc.lpszClassName, + "Invisible Window", + 0, 0, 0, 0, 0, + NULL, NULL, hInstance, NULL + ); + if (hwnd) SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY); + return hwnd; +} + +void KeyboardMainLoop() { + HINSTANCE hInstance = GetModuleHandle(NULL); + HWND hwnd = CreateInvisibleWindow(hInstance); + if (!hwnd) LogMessage (LogLevel::ERROR, "Failed to create window!\n"); + keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, InputProc, nullptr, 0); if (keyboardHook == nullptr) LogMessage (LogLevel::ERROR, "Failed to install keyboard hook!\n"); mouseHook = SetWindowsHookEx(WH_MOUSE_LL, InputProc, nullptr, 0); - if (maxCount > 1) LogMessage (LogLevel::ERROR, "CHEATING MODE! max count is set to {}", maxCount); - atexit (DisposeKeyboard); + if (mouseHook == nullptr) LogMessage (LogLevel::ERROR, "Failed to install mouse hook!\n"); + if (maxCount > 2) LogMessage (LogLevel::ERROR, "CHEATING MODE! max count is set to {}", maxCount); + LogMessage (LogLevel::WARN, "(experimental) Using Async IO!"); + MSG msg; + while (GetMessage(&msg, hwnd, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + if (keyboardHook) UnhookWindowsHookEx(keyboardHook); + if (mouseHook) UnhookWindowsHookEx(mouseHook); +} + +void +InitializeKeyboard () { + std::thread (KeyboardMainLoop).detach (); + // atexit (DisposeKeyboard); } void DisposeKeyboard () { - if (keyboardHook) UnhookWindowsHookEx(keyboardHook); - if (mouseHook) UnhookWindowsHookEx(mouseHook); + // if (keyboardHook) UnhookWindowsHookEx(keyboardHook); + // if (mouseHook) UnhookWindowsHookEx(mouseHook); } void @@ -261,7 +303,9 @@ UpdatePoll (HWND windowHandle, bool useController) { // ScreenToClient (windowHandle, ¤tMouseState.Position); if (useController) { for (int i=0; i 0 ? 32767.0f : -32768.0f); if (wndForeground || sensorValue == 0) { switch (event.gaxis.axis) { - case SDL_GAMEPAD_AXIS_LEFTX: currentControllerAxisState.LeftRight = sensorValue; break; - case SDL_GAMEPAD_AXIS_LEFTY: currentControllerAxisState.LeftDown = sensorValue; break; - case SDL_GAMEPAD_AXIS_RIGHTX: currentControllerAxisState.RightRight = sensorValue; break; - case SDL_GAMEPAD_AXIS_RIGHTY: currentControllerAxisState.RightDown = sensorValue; break; - case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: currentControllerAxisState.LTriggerDown = sensorValue; break; - case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: currentControllerAxisState.RTriggerDown = sensorValue; break; - } + case SDL_GAMEPAD_AXIS_LEFTX: if (sensorValue < 0) { + currentControllerAxisState.LeftRight = 0.0f; + if (currentControllerAxisState.LeftLeft == 0.0f) { + controllerAxisCount[SDL_AXIS_LEFT_LEFT] = std::min (controllerAxisCount[SDL_AXIS_LEFT_LEFT] + 1, maxCount); + } + currentControllerAxisState.LeftLeft = -sensorValue; + } else { + currentControllerAxisState.LeftLeft = 0.0f; + if (currentControllerAxisState.LeftRight == 0.0f) { + controllerAxisCount[SDL_AXIS_LEFT_RIGHT] = std::min (controllerAxisCount[SDL_AXIS_LEFT_RIGHT] + 1, maxCount); + } + currentControllerAxisState.LeftRight = sensorValue; + } break; + case SDL_GAMEPAD_AXIS_LEFTY: if (sensorValue < 0) { + currentControllerAxisState.LeftDown = 0.0f; + if (currentControllerAxisState.LeftUp == 0.0f) { + controllerAxisCount[SDL_AXIS_LEFT_UP] = std::min (controllerAxisCount[SDL_AXIS_LEFT_UP] + 1, maxCount); + } + currentControllerAxisState.LeftUp = -sensorValue; + } else { + currentControllerAxisState.LeftUp = 0.0f; + if (currentControllerAxisState.LeftDown == 0.0f) { + controllerAxisCount[SDL_AXIS_LEFT_DOWN] = std::min (controllerAxisCount[SDL_AXIS_LEFT_DOWN] + 1, maxCount); + } + currentControllerAxisState.LeftDown = sensorValue; + } break; + case SDL_GAMEPAD_AXIS_RIGHTX: if (sensorValue < 0) { + currentControllerAxisState.RightRight = 0.0f; + if (currentControllerAxisState.RightLeft == 0.0f) { + controllerAxisCount[SDL_AXIS_RIGHT_LEFT] = std::min (controllerAxisCount[SDL_AXIS_RIGHT_LEFT] + 1, maxCount); + } + currentControllerAxisState.RightLeft = -sensorValue; + } else { + currentControllerAxisState.RightLeft = 0.0f; + if (currentControllerAxisState.RightRight == 0.0f) { + controllerAxisCount[SDL_AXIS_RIGHT_RIGHT] = std::min (controllerAxisCount[SDL_AXIS_RIGHT_RIGHT] + 1, maxCount); + } + currentControllerAxisState.RightRight = sensorValue; + } break; + case SDL_GAMEPAD_AXIS_RIGHTY: if (sensorValue < 0) { + currentControllerAxisState.RightDown = 0.0f; + if (currentControllerAxisState.RightUp == 0.0f) { + controllerAxisCount[SDL_AXIS_RIGHT_UP] = std::min (controllerAxisCount[SDL_AXIS_RIGHT_UP] + 1, maxCount); + } + currentControllerAxisState.RightUp = -sensorValue; + } else { + currentControllerAxisState.RightUp = 0.0f; + if (currentControllerAxisState.RightDown == 0.0f) { + controllerAxisCount[SDL_AXIS_RIGHT_DOWN] = std::min (controllerAxisCount[SDL_AXIS_RIGHT_DOWN] + 1, maxCount); + } + currentControllerAxisState.RightDown = sensorValue; + } break; + case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: { + if (currentControllerAxisState.LTriggerDown == 0.0f) { + controllerAxisCount[SDL_AXIS_LTRIGGER_DOWN] = std::min (controllerAxisCount[SDL_AXIS_LTRIGGER_DOWN] + 1, maxCount); + } + currentControllerAxisState.LTriggerDown = sensorValue; + } break; + case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: { + if (currentControllerAxisState.RTriggerDown == 0.0f) { + controllerAxisCount[SDL_AXIS_RTRIGGER_DOWN] = std::min (controllerAxisCount[SDL_AXIS_RTRIGGER_DOWN] + 1, maxCount); + } + currentControllerAxisState.RTriggerDown = sensorValue; + } break; + } break; } - break; } } } @@ -371,10 +472,6 @@ int maxKeyboardCount = 1; bool KeyboardIsTapped (const uint8_t keycode) { if (keyboardCount[keycode] > 0) { - // if (maxKeyboardCount < keyboardCount[keycode]) { - // maxKeyboardCount = keyboardCount[keycode]; - // LogMessage (LogLevel::DEBUG, "MAX KEYBOARD COLLECTED {} {}", keycode, maxKeyboardCount); - // } keyboardDiff[keycode] = 1; keyboardClean = true; return true; @@ -402,10 +499,6 @@ bool GetMouseScrollIsTapped (const Scroll scroll) { if (scroll == MOUSE_SCROLL_INVALID) return false; if (mouseWheelCount[scroll - 1] > 0) { - // if (maxWheelCount < mouseWheelCount[scroll - 1]) { - // maxWheelCount = mouseWheelCount[scroll - 1]; - // LogMessage (LogLevel::DEBUG, "MAX WHEEL COLLECTED {} {}", (int)scroll, maxWheelCount); - // } mouseWheelDiff[scroll - 1] = 1; return true; } return false; @@ -420,10 +513,6 @@ int maxButtonCount = 1; bool ControllerButtonIsTapped (const SDL_GamepadButton button) { if (controllerCount[button] > 0) { - // if (maxButtonCount < controllerCount[button]) { - // maxButtonCount = controllerCount[button]; - // LogMessage (LogLevel::DEBUG, "MAX BUTTON COLLECTED {} {}", (int)button, maxButtonCount); - // } controllerDiff[button] = 1; return true; } return false; @@ -442,18 +531,20 @@ ControllerAxisIsDown (const SDLAxis axis) { case SDL_AXIS_RIGHT_DOWN: return currentControllerAxisState.RightDown; case SDL_AXIS_LTRIGGER_DOWN: return currentControllerAxisState.LTriggerDown; case SDL_AXIS_RTRIGGER_DOWN: return currentControllerAxisState.RTriggerDown; - default: return false; + default: return 0.0f; } } bool ControllerAxisIsTapped (const SDLAxis axis) { - return ControllerAxisIsDown (axis); + if (controllerAxisCount[axis] > 0) { + controllerAxisDiff[axis] = 1; + return true; + } return false; } bool IsButtonTapped (const Keybindings &bindings) { - for (size_t i = 0; i < ConfigKeyboardButtonsCount; i++) { if (bindings.keycodes[i] == 0) continue; if (KeyboardIsTapped (bindings.keycodes[i])) return true; @@ -471,7 +562,6 @@ IsButtonTapped (const Keybindings &bindings) { if (GetMouseScrollIsTapped (bindings.scroll[i])) return true; } return false; - // return GetInternalButtonState (bindings).Tapped; } float @@ -493,6 +583,5 @@ IsButtonDown (const Keybindings &bindings) { if (GetMouseScrollIsDown (bindings.scroll[i])) return 1.0f; } return 0.0f; - // return GetInternalButtonState (bindings).Down; } #endif \ No newline at end of file