diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8bfab1a..9c84a20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,8 +11,10 @@ jobs: submodules: recursive - name: Install depends run: sudo apt-get install -y mingw-w64 ninja-build nasm; pip3 install meson + - name: Upgrade gcc + run: npx xpm init && npx xpm install @xpack-dev-tools/mingw-w64-gcc@latest --verbose - name: Make - run: make setup dist-no-7z + run: export PATH=$GITHUB_WORKSPACE/xpacks/.bin:$PATH && make setup dist-no-7z - uses: actions/upload-artifact@v3 with: name: dist diff --git a/.gitignore b/.gitignore index 3f6d00f..d2651b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,13 @@ build/ +xpacks/ compile_commands.json +package.json .cache subprojects/minhook subprojects/packagecache subprojects/tomlc99 subprojects/SDL2-2.26.5 subprojects/xxHash-0.8.2 +subprojects/safetyhook dist.7z .vscode \ No newline at end of file diff --git a/README.md b/README.md index 8cbba62..54ea201 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ unlock_songs = true shared_audio = true # vertical sync vsync = false +# window mode +windowed = false [patches.cn_jun_2023] # sync test mode language to attract etc. diff --git a/dist/config.toml b/dist/config.toml index a992cb0..0d87099 100644 --- a/dist/config.toml +++ b/dist/config.toml @@ -7,6 +7,7 @@ res = { x = 1920, y = 1080 } unlock_songs = true shared_audio = true vsync = false +windowed = false [patches.cn_jun_2023] fix_language = false diff --git a/meson.build b/meson.build index e9ab937..999d809 100644 --- a/meson.build +++ b/meson.build @@ -1,18 +1,15 @@ -project('TaikoArcadeLoader', 'c', 'cpp', 'nasm', version: '1.0.0') +project('TaikoArcadeLoader', 'c', 'cpp', 'nasm', version: '1.0.0', default_options : ['c_std=c11', 'cpp_std=c++23']) warning_level = 3 debug = true optimization = 3 b_lto = true b_pgo = 'use' -cpp_std = 'c++20' cpp = meson.get_compiler('cpp') add_project_arguments( cpp.get_supported_arguments( '-D_WIN32_WINNT=_WIN32_WINNT_WIN10', - '-std=c++20', - '-Werror', ), language: 'cpp', ) @@ -23,6 +20,8 @@ add_project_link_arguments( #'-s', '-lws2_32', '-lssp', + '-lntdll', + '-Wl,--allow-multiple-definition' ), language: 'cpp', ) @@ -31,6 +30,7 @@ minhook = subproject('minhook') tomlc99 = subproject('tomlc99') sdl2 = subproject('sdl2', default_options: ['default_library=static', 'test=false', 'use_render=disabled']) xxhash = subproject('xxhash', default_options: ['default_library=static', 'cli=false']) +safetyhook = subproject('safetyhook') library( 'bnusio', @@ -39,6 +39,7 @@ library( tomlc99.get_variable('tomlc99_lib'), sdl2.get_variable('sdl2'), xxhash.get_variable('xxhash'), + safetyhook.get_variable('safetyhook_lib'), ], include_directories: [ 'src', @@ -46,6 +47,7 @@ library( tomlc99.get_variable('tomlc99_inc'), sdl2.get_variable('core_inc'), xxhash.get_variable('inc'), + safetyhook.get_variable('safetyhook_inc'), ], sources : [ 'src/dllmain.cpp', diff --git a/src/patches/cn_jun_2023.cpp b/src/patches/cn_jun_2023.cpp index b577a94..5e2fa53 100644 --- a/src/patches/cn_jun_2023.cpp +++ b/src/patches/cn_jun_2023.cpp @@ -1,5 +1,7 @@ #include "helpers.h" #include "patches.h" +#include + namespace patches::CN_JUN_2023 { u8 *haspBuffer; @@ -20,33 +22,24 @@ HOOK (i32, HaspRead, PROC_ADDRESS ("hasp_windows_x64.dll", "hasp_read"), i32, i3 return 0; } -typedef i64 (__fastcall *lua_func) (u64, u64); -lua_func lua_settop; -lua_func lua_pushboolean; -lua_func lua_pushstring; +i64 (__fastcall *lua_settop) (u64, u64) = (i64 (__fastcall *) (u64, u64))PROC_ADDRESS ("lua51.dll", "lua_settop"); +i64 (__fastcall *lua_pushboolean) (u64, u64) = (i64 (__fastcall *) (u64, u64))PROC_ADDRESS ("lua51.dll", "lua_pushboolean"); +i64 (__fastcall *lua_pushstring) (u64, u64) = (i64 (__fastcall *) (u64, u64))PROC_ADDRESS ("lua51.dll", "lua_pushstring"); -i64 __fastcall Lua_PushTrue (i64 a1) { +i64 +lua_pushtrue (i64 a1) { lua_settop (a1, 0); lua_pushboolean (a1, 1); return 1; } -HOOK (i64, AvailableMode_Dani_AI, ASLR (0x1401AC550), i64 a1) { return Lua_PushTrue (a1); } -HOOK (i64, AvailableMode_Collabo025, ASLR (0x1402BFF70), i64 *, i64 a2) { return Lua_PushTrue (a2); } -HOOK (i64, AvailableMode_Collabo026, ASLR (0x1402BC9B0), i64 a1) { return Lua_PushTrue (a1); } - -typedef i64 (__fastcall *_GetLanguage) (i64); -_GetLanguage GetLanguage; +HOOK (i64, AvailableMode_Dani_AI, ASLR (0x1401AC550), i64 a1) { return lua_pushtrue (a1); } +HOOK (i64, AvailableMode_Collabo025, ASLR (0x1402BFF70), i64 *, i64 a2) { return lua_pushtrue (a2); } +HOOK (i64, AvailableMode_Collabo026, ASLR (0x1402BC9B0), i64 a1) { return lua_pushtrue (a1); } int language = 0; -i64 __fastcall GetLanguage_Hook (i64 a1) { - auto result = GetLanguage (a1); - language = *((u32 *)result); - return result; -} - const char * -GetLanguageStr (int language) { +languageStr () { switch (language) { case 1: return "en_us"; case 2: return "cn_tw"; @@ -55,17 +48,49 @@ GetLanguageStr (int language) { default: return "jpn"; } } - +HOOK (i64, GetLanguage, ASLR (0x140023720), i64 a1) { + auto result = originalGetLanguage (a1); + language = *((u32 *)result); + return result; +} HOOK (i64, GetRegionLanguage, ASLR (0x1401AC300), i64 a1) { lua_settop (a1, 0); - lua_pushstring (a1, (u64)GetLanguageStr (language)); + lua_pushstring (a1, (u64)languageStr ()); + return 1; +} +HOOK (i64, GetCabinetLanguage, ASLR (0x1401AF270), i64, i64 a2) { + lua_settop (a2, 0); + lua_pushstring (a2, (u64)languageStr ()); return 1; } -HOOK (i64, GetCabinetLanguage, ASLR (0x1401AF270), i64, i64 a2) { - lua_settop (a2, 0); - lua_pushstring (a2, (u64)GetLanguageStr (language)); - return 1; +HOOK_DYNAMIC (char, __fastcall, AMFWTerminate, i64) { return 0; } + +const i32 datatableBufferSize = 1024 * 1024 * 12; +safetyhook::Allocation datatableBuffer1; +safetyhook::Allocation datatableBuffer2; +safetyhook::Allocation datatableBuffer3; +const std::vector datatableBuffer1Addresses = {0x140093430, 0x1400934A1, 0x1400934CB, 0x14009353C}; +const std::vector datatableBuffer2Addresses = {0x14009341C, 0x14009354B, 0x14009357E}; +const std::vector datatableBuffer3Addresses = {0x14009356F, 0x140093585, 0x1400935AF}; +const std::vector memsetSizeAddresses = {0x140093416, 0x14009342A, 0x140093569}; + +void +AllocateStaticBufferNear (void *target_address, size_t size, safetyhook::Allocation *newBuffer) { + auto allocator = safetyhook::Allocator::global (); + std::vector desired_addresses = {(uint8_t *)target_address}; + auto allocation_result = allocator->allocate_near (desired_addresses, size); + if (allocation_result.has_value ()) *newBuffer = std::move (*allocation_result); +} + +void +ReplaceLeaBufferAddress (const std::vector &bufferAddresses, void *newBufferAddress) { + for (auto bufferAddress : bufferAddresses) { + uintptr_t lea_instruction_dst = ASLR (bufferAddress) + 3; + uintptr_t lea_instruction_end = ASLR (bufferAddress) + 7; + intptr_t offset = (intptr_t)newBufferAddress - lea_instruction_end; + WRITE_MEMORY (lea_instruction_dst, i32, (i32)offset); + } } void @@ -144,25 +169,36 @@ Init () { // Disable SSLVerify WRITE_MEMORY (ASLR (0x14034C182), u8, 0x00); - HMODULE lua51Module = LoadLibrary ("lua51.dll"); - if (lua51Module) { - lua_settop = (lua_func)GetProcAddress (lua51Module, "lua_settop"); - lua_pushboolean = (lua_func)GetProcAddress (lua51Module, "lua_pushboolean"); - lua_pushstring = (lua_func)GetProcAddress (lua51Module, "lua_pushstring"); + // Remove datatable size limit + for (auto address : memsetSizeAddresses) + WRITE_MEMORY (ASLR (address) + 2, i32, datatableBufferSize); - if (fixLanguage) { - GetLanguage = (_GetLanguage)ASLR (0x140023720); - MH_Initialize (); - MH_CreateHook ((LPVOID)GetLanguage, (LPVOID)GetLanguage_Hook, (LPVOID *)&GetLanguage); - MH_EnableHook (nullptr); - INSTALL_HOOK (GetRegionLanguage); - INSTALL_HOOK (GetCabinetLanguage); - } - INSTALL_HOOK (AvailableMode_Dani_AI); - if (modeCollabo025) INSTALL_HOOK (AvailableMode_Collabo025); - if (modeCollabo026) INSTALL_HOOK (AvailableMode_Collabo026); + auto bufferBase = MODULE_HANDLE - 0x03000000; + AllocateStaticBufferNear ((void *)bufferBase, datatableBufferSize, &datatableBuffer1); + bufferBase += datatableBufferSize; + AllocateStaticBufferNear ((void *)bufferBase, datatableBufferSize, &datatableBuffer2); + bufferBase += datatableBufferSize; + AllocateStaticBufferNear ((void *)bufferBase, datatableBufferSize, &datatableBuffer3); + + ReplaceLeaBufferAddress (datatableBuffer1Addresses, datatableBuffer1.data ()); + ReplaceLeaBufferAddress (datatableBuffer2Addresses, datatableBuffer2.data ()); + ReplaceLeaBufferAddress (datatableBuffer3Addresses, datatableBuffer3.data ()); + + // Fix language + if (fixLanguage) { + INSTALL_HOOK (GetLanguage); + INSTALL_HOOK (GetRegionLanguage); + INSTALL_HOOK (GetCabinetLanguage); } + // Enable mode + INSTALL_HOOK (AvailableMode_Dani_AI); + if (modeCollabo025) INSTALL_HOOK (AvailableMode_Collabo025); + if (modeCollabo026) INSTALL_HOOK (AvailableMode_Collabo026); + + auto amHandle = (u64)GetModuleHandle ("AMFrameWork.dll"); + INSTALL_HOOK_DYNAMIC (AMFWTerminate, (void *)(amHandle + 0x25A00)); + patches::Qr::Init (); } } // namespace patches::CN_JUN_2023 diff --git a/src/patches/jp_nov_2020.cpp b/src/patches/jp_nov_2020.cpp index fff390b..b500191 100644 --- a/src/patches/jp_nov_2020.cpp +++ b/src/patches/jp_nov_2020.cpp @@ -1,5 +1,6 @@ #include "helpers.h" #include "patches.h" +#include const u64 song_data_size = 1024 * 1024 * 64; void *song_data; @@ -11,6 +12,38 @@ void *song_data; (u8)((u64)(location) >> 48), (u8)((u64)(location) >> 56) namespace patches::JP_NOV_2020 { + +HOOK_DYNAMIC (char, __fastcall, AMFWTerminate, i64) { return 0; } + +const i32 datatableBufferSize = 1024 * 1024 * 12; +safetyhook::Allocation datatableBuffer; +const std::vector datatableBufferAddresses = {0x14006D9A6, 0x14006D9D3, 0x14006E048, 0x14006E075, 0x14006E3A8, 0x14006E3D5, 0x14006E988, 0x14006E9B5, 0x14006EE22, 0x14006EE51, 0x14006F068, + 0x14006F095, 0x14006F2F8, 0x14006F325, 0x14006F698, 0x14006F6C5, 0x14006F919, 0x14006F948, 0x14006FC38, 0x14006FC67, 0x14007006C, 0x140070099, + 0x1400703E3, 0x140070412, 0x140070EB3, 0x140070EE2, 0x140071748, 0x140071775, 0x140071A68, 0x140071A95, 0x140071DD2, 0x140071E04, 0x140072E44, + 0x140072E73, 0x140073058, 0x140073085, 0x140073374, 0x1400733A0, 0x1400735E8, 0x140073615, 0x14007390C, 0x140073939, 0x140073E73, 0x140073EA6, + 0x140074A8D, 0x140074ABC, 0x140075082, 0x1400750B1, 0x140075524, 0x140075550, 0x1400758A2, 0x1400758D1, 0x140075D88, 0x140075DB5, 0x1403BA8FD}; +const std::vector memsetSizeAddresses = {0x14006D9A0, 0x14006E042, 0x14006E3A2, 0x14006E982, 0x14006EE1C, 0x14006F062, 0x14006F2F2, 0x14006F692, 0x14006F913, + 0x14006FC32, 0x140070066, 0x1400703DD, 0x140070EAD, 0x140071742, 0x140071A62, 0x140071DCC, 0x140072E3E, 0x140073052, + 0x14007336E, 0x1400735E2, 0x140073906, 0x140073E6D, 0x140074A87, 0x14007507C, 0x14007551E, 0x14007589C, 0x140075D82}; + +void +AllocateStaticBufferNear (void *target_address, size_t size, safetyhook::Allocation *newBuffer) { + auto allocator = safetyhook::Allocator::global (); + std::vector desired_addresses = {(uint8_t *)target_address}; + auto allocation_result = allocator->allocate_near (desired_addresses, size); + if (allocation_result.has_value ()) *newBuffer = std::move (*allocation_result); +} + +void +ReplaceLeaBufferAddress (const std::vector &bufferAddresses, void *newBufferAddress) { + for (auto bufferAddress : bufferAddresses) { + uintptr_t lea_instruction_dst = ASLR (bufferAddress) + 3; + uintptr_t lea_instruction_end = ASLR (bufferAddress) + 7; + intptr_t offset = (intptr_t)newBufferAddress - lea_instruction_end; + WRITE_MEMORY (lea_instruction_dst, i32, (i32)offset); + } +} + void Init () { i32 xRes = 1920; @@ -41,6 +74,15 @@ Init () { if (sharedAudio) WRITE_MEMORY (ASLR (0x140692E17), u8, 0xEB); if (!vsync) WRITE_MEMORY (ASLR (0x140517339), u8, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x90); + // Remove datatable size limit + for (auto address : memsetSizeAddresses) + WRITE_MEMORY (ASLR (address) + 2, i32, datatableBufferSize); + + auto bufferBase = MODULE_HANDLE - 0x01000000; + AllocateStaticBufferNear ((void *)bufferBase, datatableBufferSize, &datatableBuffer); + + ReplaceLeaBufferAddress (datatableBufferAddresses, datatableBuffer.data ()); + // Remove song limit WRITE_MEMORY (ASLR (0x140313726), i32, 9000); WRITE_MEMORY (ASLR (0x1402F39E6), i32, 9000); @@ -97,6 +139,7 @@ Init () { WRITE_MEMORY (ASLR (0x1403067DE), u8, GENERATE_MOV (RDX_MOV, song_data)); WRITE_MEMORY (ASLR (0x140306712), u8, GENERATE_MOV (RDX_MOV, song_data)); WRITE_MEMORY (ASLR (0x1403069A2), u8, GENERATE_MOV (RDX_MOV, song_data)); + WRITE_MEMORY (ASLR (0x1403069AC), u8, 0x90, 0x90, 0x90, 0x90, 0x90); // Unknown WRITE_MEMORY (ASLR (0x140313755), u8, GENERATE_MOV (RDX_MOV, song_data)); WRITE_MEMORY (ASLR (0x140313A0B), u8, GENERATE_MOV (RDX_MOV, song_data)); @@ -113,10 +156,9 @@ Init () { // Patch TLS v1.0 to v1.2 WRITE_MEMORY (ASLR (0x14044B1A9), u8, 0x10); - if (xRes > 0 && yRes > 0) { - WRITE_MEMORY (ASLR (0x14035FC5B), i32, xRes); - WRITE_MEMORY (ASLR (0x14035FC62), i32, yRes); - } + // Res + WRITE_MEMORY (ASLR (0x14035FC5B), i32, xRes); + WRITE_MEMORY (ASLR (0x14035FC62), i32, yRes); // Move various files to current directory auto amHandle = (u64)GetModuleHandle ("AMFrameWork.dll"); @@ -128,6 +170,7 @@ Init () { WRITE_MEMORY (amHandle + 0x34ACD, u8, 0xEB); WRITE_MEMORY (amHandle + 0x148AF, u8, 0xEB); WRITE_MEMORY (amHandle + 0x14A1A, u8, 0xEB); + INSTALL_HOOK_DYNAMIC (AMFWTerminate, (void *)(amHandle + 0x35A00)); patches::Qr::Init (); patches::AmAuth::Init (); diff --git a/src/poll.cpp b/src/poll.cpp index 7ef4cdf..925046f 100644 --- a/src/poll.cpp +++ b/src/poll.cpp @@ -222,8 +222,7 @@ InitializePoll (HWND windowHandle) { } window = SDL_CreateWindowFrom (windowHandle); - if (window != NULL) SDL_SetWindowResizable (window, SDL_TRUE); - else printError ("SDL_CreateWindowFrom (windowHandle): %s\n", SDL_GetError ()); + if (window == NULL) printError ("SDL_CreateWindowFrom (windowHandle): %s\n", SDL_GetError ()); atexit (DisposePoll); diff --git a/subprojects/packagefiles/safetyhook.patch b/subprojects/packagefiles/safetyhook.patch new file mode 100644 index 0000000..c2c7d9a --- /dev/null +++ b/subprojects/packagefiles/safetyhook.patch @@ -0,0 +1,77 @@ +--- safetyhook/meson.build ++++ safetyhook/meson.build +@@ -0,0 +1,15 @@ ++project('safetyhook', 'c', 'cpp', version: '0.1.3') ++ ++safetyhook_inc = include_directories('.') ++safetyhook_lib = static_library( ++ 'safetyhook', ++ include_directories: safetyhook_inc, ++ sources: [ ++ 'safetyhook.cpp', ++ 'Zydis.c' ++ ] ++) ++safetyhook_dep = declare_dependency( ++ link_with: safetyhook_lib, ++ include_directories: safetyhook_inc, ++) +--- safetyhook/safetyhook.cpp ++++ safetyhook/safetyhook.cpp +@@ -14,7 +14,7 @@ + #include + + #define NOMINMAX +-#include ++#include + + + namespace safetyhook { +@@ -324,7 +324,7 @@ VmtHook create_vmt(void* object) { + + #include + +-#include ++#include + + #if __has_include() + #include +@@ -858,7 +858,7 @@ std::expected MidHook::setup( + // Source file: thread_freezer.cpp + // + +-#include ++#include + #include + + +@@ -992,7 +992,7 @@ void fix_ip(CONTEXT& ctx, uint8_t* old_ip, uint8_t* new_ip) { + // Source file: utility.cpp + // + +-#include ++#include + + + namespace safetyhook { +@@ -1046,7 +1046,7 @@ bool is_executable(uint8_t* address) { + // Source file: vmt_hook.cpp + // + +-#include ++#include + + + +--- safetyhook/safetyhook.hpp ++++ safetyhook/safetyhook.hpp +@@ -898,7 +898,7 @@ namespace safetyhook { + #include + #include + +-#include ++#include + + namespace safetyhook { + /// @brief Executes a function while all other threads are frozen. Also allows for visiting each frozen thread and + diff --git a/subprojects/safetyhook.wrap b/subprojects/safetyhook.wrap new file mode 100644 index 0000000..ec9cbeb --- /dev/null +++ b/subprojects/safetyhook.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = safetyhook +source_url = https://github.com/cursey/safetyhook/releases/download/v0.1.3/safetyhook-amalgamated-zydis.zip +source_filename = safetyhook-amalgamated-zydis.zip +source_hash = 5d02299eb9b3fef3f936fba441ad44ec08f049de4cd9125092dd63e1f1f24cfe +lead_directory_missing = yes +diff_files = safetyhook.patch + +[provide] +libsafetyhook = safetyhook_dep