182 lines
5.6 KiB
C++
182 lines
5.6 KiB
C++
#include "wmischook.h"
|
|
#include "util/logging.h"
|
|
#include "util/detour.h"
|
|
#include "util/sigscan.h"
|
|
#include "util/libutils.h"
|
|
#include "util/utils.h"
|
|
#include <mmsystem.h>
|
|
|
|
|
|
namespace hooks::wmisc::msacm
|
|
{
|
|
typedef MMRESULT (*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<uint64_t, std::vector<char>> 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<void *>(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<void *>(acmFormatSuggest));
|
|
detour::trampoline_try(reinterpret_cast<void *>(acmFormatSuggest), reinterpret_cast<void *>(on_acmFormatSuggest), reinterpret_cast<void **>(&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<int32_t, bool> 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 beg, match, TX_ThumExists;
|
|
TX_ThumExists = beg = match = find_pattern_from(hmodule, "83f801750c4084ff750732c0", "XXXXXXXXXXXX", 0, 0, 0);
|
|
while (beg > sizeof(void *) && match >= TX_ThumExists)
|
|
{
|
|
beg -= sizeof(void *);
|
|
match = find_pattern_from(hmodule, "4057", "XX", 0, 0, beg - reinterpret_cast<intptr_t>(hmodule));
|
|
}
|
|
TX_ThumExists = match;
|
|
log_info("hooks::wmisc::thumbnail", "Hooking ThumExists({})", reinterpret_cast<void *>(TX_ThumExists));
|
|
detour::trampoline_try(reinterpret_cast<void *>(TX_ThumExists), reinterpret_cast<void *>(on_thum_exists_), reinterpret_cast<void **>(&ThumExists));
|
|
}
|
|
}
|
|
|
|
namespace hooks::wmisc::bmsound
|
|
{
|
|
typedef void (*BmswLoggerInit_t)(void *cb);
|
|
extern "C" void __cdecl log_printf(char level, const char *scope, const char *fmt)
|
|
{
|
|
switch (level)
|
|
{
|
|
case 'I':
|
|
log_info(scope, "{}", fmt);
|
|
break;
|
|
case 'W':
|
|
log_warning(scope, "{}", fmt);
|
|
break;
|
|
case 'E':
|
|
log_fatal(scope, "{}", fmt);
|
|
break;
|
|
case 'M':
|
|
default:
|
|
log_misc(scope, "{}", fmt);
|
|
break;
|
|
}
|
|
}
|
|
void attach(HINSTANCE hmodule)
|
|
{
|
|
|
|
BmswLoggerInit_t BmswLoggerInit;
|
|
HMODULE bmsw;
|
|
if ((bmsw = libutils::try_library(MODULE_PATH / "bmsound-wine.dll")))
|
|
{
|
|
BmswLoggerInit = (BmswLoggerInit_t) GetProcAddress(bmsw, "BmswLoggerInit");
|
|
BmswLoggerInit(reinterpret_cast<void*>(log_printf));
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
switch (to_hash(model))
|
|
{
|
|
case to_hash("LDJ"):
|
|
thumbnail::attach(hmodule);
|
|
msacm::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;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|