diff --git a/hooks/sndbhook.cpp b/hooks/sndbhook.cpp index 1fdc1f6..114f0c3 100644 --- a/hooks/sndbhook.cpp +++ b/hooks/sndbhook.cpp @@ -901,7 +901,7 @@ void init(HINSTANCE hmodule, const char *model, const char *ext) return; } - // Override .text .data per datacode _REV: prefer runtime offset lookup based on universal asm pattern + // Override .text .data per datacode (pattern match) long extl = strtol(ext, nullptr, 10); if (extl < 2019100800) { @@ -925,42 +925,16 @@ void init(HINSTANCE hmodule, const char *model, const char *ext) offset_.DAT_song_id = 0x67c; offset_.is_lstr_wchar = true; } - switch (to_hash(ext)) + intptr_t beg, match; + offset_.TX_EnumValidSoundbanks = beg = match = find_asm_leacstr_from(hmodule, R8, "%05d/%05d.2dx"); + while (beg > sizeof(void *) && match >= offset_.TX_EnumValidSoundbanks) //_REV: probably should be sigscan helper due to find_pattern_from performance, unify with wmischook { - case to_hash("2018091900"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0x1170b0; - break; - case to_hash("2019090200"): - case to_hash("2019100700"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0x37a540; - break; - case to_hash("2020092900"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0x5ac460; - break; - case to_hash("2021083000"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0x766bc0; - break; - case to_hash("2021091500"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0x766c60; - break; - case to_hash("2022082400"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0x46e160; - break; - case to_hash("2023090500"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0xafd780; - break; - case to_hash("2024082600"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0x8eae40; - break; - case to_hash("2025082500"): - offset_.TX_EnumValidSoundbanks = (intptr_t) hmodule + 0x969fb0; - break; - default: - break; + beg -= sizeof(void *); + match = find_pattern_from(hmodule, "4057", "XX", 0, 0, beg - reinterpret_cast(hmodule)); } snd_bank::resampler_ = avcodec::BmswTranscoderResampler() == avcodec::bmswac_resampler_none ? snd_bank::resampler_ : avcodec::BmswTranscoderResampler(); - offset_.TX_EnumValidSoundbanks = avcodec::BmswTranscoderEnumValidSoundbanksAddr() == 0x0 ? offset_.TX_EnumValidSoundbanks : (intptr_t) hmodule + (int64_t) avcodec::BmswTranscoderEnumValidSoundbanksAddr(); - if (offset_.TX_EnumValidSoundbanks == (intptr_t) hmodule) + offset_.TX_EnumValidSoundbanks = avcodec::BmswTranscoderEnumValidSoundbanksAddr() == 0x0 ? match : (intptr_t) hmodule + (int64_t) avcodec::BmswTranscoderEnumValidSoundbanksAddr(); + if (!offset_.TX_EnumValidSoundbanks) { log_warning("hooks::soundbank", "Unsupported game version({}), skipping..", ext); return; diff --git a/util/sigscan.cpp b/util/sigscan.cpp index e4abce2..9870d1e 100644 --- a/util/sigscan.cpp +++ b/util/sigscan.cpp @@ -249,3 +249,52 @@ bool get_pe_identifier(const std::filesystem::path& dll_path, uint32_t* time_dat return true; } + +intptr_t find_asm_leacstr_from(HMODULE module, reg_t reg, const char *cstr) +{ + // get module information + MODULEINFO module_info{}; + if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(module_info))) + { + return 0; + } + const unsigned char *modbegp = reinterpret_cast(module); + const unsigned char *modendp = reinterpret_cast(module) + static_cast(module_info.SizeOfImage); + + // search cstr + const unsigned char *cstr_addr = std::search( + modbegp, + modendp, + reinterpret_cast(cstr), + reinterpret_cast(cstr) + strlen(cstr) + ); + if (cstr_addr == modendp) + { + return 0; + } + +#if defined(__i386__) + // find LEA REG, [abs32] + unsigned char asmop[6] = { 0x8D, reg.v, 0x00, 0x00, 0x00, 0x00}; + int32_t dist32 = reinterpret_cast(cstr_addr); + memcpy(asmop + (sizeof(asmop) - 4), &dist32, 4); + return reinterpret_cast(std::search( + modbegp, + modendp, + asmop, + asmop + sizeof(asmop) + )); +#elif defined(__x86_64__) + // find LEA REG, [RIP + rel32] + unsigned char rex = reg.t == GPR_EXT ? 0x4C : 0x48; + unsigned char asmop[7] = {rex, 0x8D, reg.v, 0x00, 0x00, 0x00, 0x00}; + for (const unsigned char *addr = modbegp; addr < modendp - sizeof(asmop); addr++) + { + auto dist32 = static_cast(reinterpret_cast(cstr_addr) - reinterpret_cast(addr + sizeof(asmop))); + memcpy(asmop + (sizeof(asmop) - 4), &dist32, 4); + if (memcmp(addr, asmop, sizeof(asmop)) == 0) return reinterpret_cast(addr); + } +#endif + + return 0; +} diff --git a/util/sigscan.h b/util/sigscan.h index ba92920..9ef7072 100644 --- a/util/sigscan.h +++ b/util/sigscan.h @@ -89,4 +89,54 @@ intptr_t replace_pattern_from( intptr_t usage, intptr_t start_from); -bool get_pe_identifier(const std::filesystem::path& dll_path, uint32_t* time_date_stamp, uint32_t* address_of_entry_point); \ No newline at end of file +bool get_pe_identifier(const std::filesystem::path& dll_path, uint32_t* time_date_stamp, uint32_t* address_of_entry_point); + +union reg_t{ + struct{ + unsigned char t; + unsigned char v; + }; + unsigned short s; + constexpr reg_t(unsigned char t, unsigned char v) : s(v << 8 | t) {} + constexpr reg_t(unsigned short s) : s(s) {} +}; + +enum REGTYPE : unsigned char +{ + GPR_32, + GPR_64, + GPR_EXT +}; + +enum REG : unsigned short +{ + EAX = reg_t(GPR_32, 0x05).s, + ECX = reg_t(GPR_32, 0x0D).s, + EDX = reg_t(GPR_32, 0x15).s, + EBX = reg_t(GPR_32, 0x1D).s, + ESP = reg_t(GPR_32, 0x25).s, + EBP = reg_t(GPR_32, 0x2D).s, + ESI = reg_t(GPR_32, 0x35).s, + EDI = reg_t(GPR_32, 0x3D).s, + RAX = reg_t(GPR_64, 0x05).s, + RCX = reg_t(GPR_64, 0x0D).s, + RDX = reg_t(GPR_64, 0x15).s, + RBX = reg_t(GPR_64, 0x1D).s, + RSP = reg_t(GPR_64, 0x25).s, + RBP = reg_t(GPR_64, 0x2D).s, + RSI = reg_t(GPR_64, 0x35).s, + RDI = reg_t(GPR_64, 0x3D).s, + R8 = reg_t(GPR_EXT, 0x05).s, + R9 = reg_t(GPR_EXT, 0x0D).s, + R10 = reg_t(GPR_EXT, 0x15).s, + R11 = reg_t(GPR_EXT, 0x1D).s, + R12 = reg_t(GPR_EXT, 0x25).s, + R13 = reg_t(GPR_EXT, 0x2D).s, + R14 = reg_t(GPR_EXT, 0x35).s, + R15 = reg_t(GPR_EXT, 0x3D).s +}; + +intptr_t find_asm_leacstr_from( + HMODULE module, + reg_t reg, + const char *cstr); \ No newline at end of file