#include "wmischook.h" #include "util/logging.h" #include "util/detour.h" #include "util/sigscan.h" #include "util/libutils.h" #include "util/utils.h" #include namespace hooks::wmisc::msacm { typedef MMRESULT (__stdcall *acmFormatSuggest_t)(HANDLE had, LPWAVEFORMATEX pwfxSrc, LPWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest); static acmFormatSuggest_t acmFormatSuggest; static uint64_t hash_waveformatex(const WAVEFORMATEX *pwfx, DWORD fdwSuggest) { //_INFO: no clashes, likely faster than hashing sizeof(WAVEFORMATEX) buffer uint64_t h = 0; h ^= (uint64_t) pwfx->nSamplesPerSec << 0; h ^= (uint64_t) pwfx->nAvgBytesPerSec << 1; h ^= (uint64_t) pwfx->nBlockAlign << 2; h ^= (uint64_t) pwfx->wBitsPerSample << 3; h ^= (uint64_t) pwfx->cbSize << 4; h ^= (uint64_t) fdwSuggest << 5; h ^= (uint64_t) pwfx->wFormatTag << 6; h ^= (uint64_t) pwfx->nChannels << 7; return h; } MMRESULT __stdcall on_acmFormatSuggest(HANDLE had, LPWAVEFORMATEX pwfxSrc, LPWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest) { static std::unordered_map> acmformats; static std::mutex mutex; // Find cached formats size_t hash = hash_waveformatex(pwfxSrc, fdwSuggest); { std::lock_guard lock(mutex); auto it = acmformats.find(hash); if (it != acmformats.end()) { // Copy cached result memcpy(pwfxDst, it->second.data(), std::min(cbwfxDst, (DWORD) (sizeof(WAVEFORMATEX) + ((WAVEFORMATEX *) it->second.data())->cbSize))); return MMSYSERR_NOERROR; } } // Cache unique formats log_info("hooks::wmisc::msacm", "Forwarding acmFormatSuggest({}) request due to cache miss", reinterpret_cast(hash)); MMRESULT r = acmFormatSuggest(had, pwfxSrc, pwfxDst, cbwfxDst, fdwSuggest); if (r == MMSYSERR_NOERROR) { std::lock_guard lock(mutex); acmformats[hash].resize(sizeof(WAVEFORMATEX) + pwfxDst->cbSize); memcpy(acmformats[hash].data(), pwfxDst, sizeof(WAVEFORMATEX) + pwfxDst->cbSize); } return r; } void attach(HINSTANCE hmodule) { // Override acmFormatSuggest HMODULE msacm = nullptr; if ((msacm = libutils::try_library("msacm32.dll"))) { acmFormatSuggest = (acmFormatSuggest_t) GetProcAddress(msacm, "acmFormatSuggest"); } log_info("hooks::wmisc::msacm", "Hooking acmFormatSuggest({})", reinterpret_cast(acmFormatSuggest)); detour::trampoline_try(reinterpret_cast(acmFormatSuggest), reinterpret_cast(on_acmFormatSuggest), reinterpret_cast(&acmFormatSuggest)); } } namespace hooks::wmisc::thumbnail { typedef bool (*ThumExists_t)(void *titleUTF_getter, int32_t id, char flag); static ThumExists_t ThumExists; bool on_thum_exists_(void *titleUTF_getter, int32_t id, char flag) { static std::unordered_map thumids; auto it = thumids.find(id); if (it != thumids.end()) { return it->second; } else { // log_misc("hooks::wmisc::thumbnail", "Caching state for [{}]", id); it = thumids.insert({id, ThumExists(titleUTF_getter, id, flag)}).first; } return it->second; } void attach(HINSTANCE hmodule) { // Reverse lookup function head (PUSH RDI) for ThumExists() specific signature, quietly fail if invalid intptr_t TX_ThumExists = patch_pipeline( hmodule, [](HMODULE hmodule, intptr_t addr) { return find_pattern_from(hmodule, "83f801750c4084ff750732c0", "XXXXXXXXXXXX", 0, 0, 0); }, [](HMODULE hmodule, intptr_t addr) { return find_pattern_from(hmodule, "4057", "XX", 0, 0, addr, true); } ); log_info("hooks::wmisc::thumbnail", "Hooking ThumExists({})", reinterpret_cast(TX_ThumExists)); detour::trampoline_try(reinterpret_cast(TX_ThumExists), reinterpret_cast(on_thum_exists_), reinterpret_cast(&ThumExists)); } } namespace hooks::wmisc::bmsound { typedef void (*BmswLogInit_t)(void *cb); extern "C" void __cdecl on_log_msg(char level, const char *scope, const char *msg) { switch (level) { case 'I': log_info(scope, "{}", msg); break; case 'W': log_warning(scope, "{}", msg); break; case 'E': log_fatal(scope, "{}", msg); break; case 'M': default: log_misc(scope, "{}", msg); break; } } void attach(HINSTANCE hmodule) { BmswLogInit_t BmswLogInit = nullptr; HMODULE bmsw; if ((bmsw = libutils::try_library(MODULE_PATH / "bmsound-wine.dll"))) { BmswLogInit = (BmswLogInit_t) GetProcAddress(bmsw, "BmswLogInit"); } if (BmswLogInit) //_REM: keep 0.2.x backward compatibility until 0.3.0 { BmswLogInit(reinterpret_cast(on_log_msg)); } } } namespace hooks::wmisc::cmovieplayer { typedef void (*GetWmvChunk_t)(uint8_t *unk); static GetWmvChunk_t GetWmvChunk_; void on_get_wmv_chunk_(uint8_t *unk) { size_t chunksz = *(uint32_t *) (unk + 0x10); void *readbuf = (void *) *(intptr_t *) (unk + 0x08); int *readsz = (int *) (unk + 0x14); //_REV: For now simply disable broken video stream *readsz = 0; } void attach(HINSTANCE hmodule) { // Remove crashing movie intptr_t TX_ThumExists = find_pattern_from(hmodule, "40534883ec20448b4110488bd9488b51088b09ff150000000089431485c0791d448bc0488d1500000000488d0d000000004883c4205b48ff25000000004883c4205bc3", "XXXXXXXXXXXXXXXXXXXXX????XXXXXXXXXXXXX????XXX????XXXXXXXX????XXXXXX", 0, 0, 0); log_info("hooks::wmisc::cmovieplayer", "Hooking GetWmvChunk({})", reinterpret_cast(TX_ThumExists)); if (detour::trampoline_try(reinterpret_cast(TX_ThumExists), reinterpret_cast(on_get_wmv_chunk_), reinterpret_cast(&GetWmvChunk_))) { log_warning("hooks::wmisc::cmovieplayer", "Video playback for wmv won't work"); } } } namespace hooks::wmisc::dmoeffector { void attach(HINSTANCE hmodule) { //_REM: Disable dmoeffector error printing for line55, could not find unique pattern (9 matches, but patching all of them is harmless) intptr_t next = (intptr_t) hmodule; while (next) { next = replace_pattern_from(hmodule, "793ae8????????895424??4c8d05????????488bc8488d15????????41b937000000e8????????ff15????????", "eb3ae8????????895424??4c8d05????????488bc8488d15????????41b937000000e8????????ff15????????", 0, 0, next - reinterpret_cast(hmodule)); if (next) log_info("hooks::wmisc::dmoeffector", "Muting error message({})", reinterpret_cast(next)); } } } namespace hooks::wmisc { static bool patchmodel_ = true; void skip_patching_by_model(bool val) { patchmodel_ = !val; } void apply_performance_hacks(HINSTANCE hmodule, const char *model, const char *ext) { if (!patchmodel_ && strcmp(model, "FORCE") != 0) return; log_info("hooks::wmisc", "Enabling performance patches for {}-{}", model, ext); long extl = strtol(ext, nullptr, 10); switch (to_hash(model)) { case to_hash("LDJ"): thumbnail::attach(hmodule); msacm::attach(hmodule); bmsound::attach(hmodule); break; case to_hash("M39"): msacm::attach(hmodule); bmsound::attach(hmodule); break; case to_hash("KFC"): msacm::attach(hmodule); bmsound::attach(hmodule); if (extl > 2019020600 && extl < 2021011800) // SDVX5 { cmovieplayer::attach(hmodule); dmoeffector::attach(hmodule); } break; case to_hash("FORCE"): switch (to_hash(ext)) { case to_hash("thumbnail"): thumbnail::attach(hmodule); break; case to_hash("msacm"): msacm::attach(hmodule); break; case to_hash("bmsound"): bmsound::attach(hmodule); break; case to_hash("cmovieplayer"): cmovieplayer::attach(hmodule); break; case to_hash("dmoeffector"): dmoeffector::attach(hmodule); break; default: break; } break; default: break; } } }