diff --git a/hooks/develhook.cpp b/hooks/develhook.cpp index fddf0fd..33c3488 100644 --- a/hooks/develhook.cpp +++ b/hooks/develhook.cpp @@ -12,75 +12,84 @@ #include -namespace hooks::devel::thumbnail -{ -typedef int (*BmswSymlink_t)(const char *srcwpath, const char *dstwpath, int canonic); -static BmswSymlink_t BmswSymlink; - -void run() -{ - HMODULE bmsw_ = nullptr; - if ((bmsw_ = libutils::try_library(MODULE_PATH / "bmsound-wine.dll"))) - { - BmswSymlink = (BmswSymlink_t) GetProcAddress(bmsw_, "BmswSymlink"); - } - - log_warning("hooks::devel::thumbnail", "Forcing thumbnail patch.."); - hooks::wmisc::apply_performance_hacks(avs::game::DLL_INSTANCE, "LDJ", "20250825"); -} -} - -namespace hooks::devel::avs_fs -{ -static constexpr char needle_[] = ""; // set to "" to print all -static avs::core::AVS_FS_LSTAT_T avs_fs_lstat_; -static avs::core::AVS_FS_MOUNT_T avs_fs_mount_; -static avs::core::AVS_FS_OPEN_T avs_fs_open_; - -int on_avs_fs_lstat(const char *path, struct avs::core::avs_stat *stat) -{ - if (strstr(path, needle_)) - { - log_info("hooks::devel", "{}({})", __FUNCTION__, path); - } - return avs_fs_lstat_(path, stat); -} -avs::core::avs_file_t on_avs_fs_mount(const char *mountpoint, const char *fsroot, const char *fstype, void *data) -{ - if (strstr(fsroot, needle_)) - { - log_info("hooks::devel", "{}({}=>{}) @{} :nullptr data?{} ", __FUNCTION__, fsroot, mountpoint, fstype, data == nullptr); - } - return avs_fs_mount_(mountpoint, fsroot, fstype, data); -} -avs::core::avs_file_t on_avs_fs_open(const char *name, uint16_t mode, int flags) -{ - if (strstr(name, needle_)) - { - log_info("hooks::devel", "{}({}::{}) @{}", __FUNCTION__, name, mode, flags); - } - return avs_fs_open_(name, mode, flags); -} -void run() -{ - detour::trampoline(reinterpret_cast(avs::core::avs_fs_lstat), reinterpret_cast(on_avs_fs_lstat), reinterpret_cast(&avs_fs_lstat_)); - detour::trampoline(reinterpret_cast(avs::core::avs_fs_mount), reinterpret_cast(on_avs_fs_mount), reinterpret_cast(&avs_fs_mount_)); - detour::trampoline(reinterpret_cast(avs::core::avs_fs_open), reinterpret_cast(on_avs_fs_open), reinterpret_cast(&avs_fs_open_)); -} -} - namespace hooks::devel { -void (*routine_cb_[devel_routine_last])() ={ - [avs_fs_detour] = avs_fs::run, - [sndb_test] = nullptr, - [thum_test] = thumbnail::run -}; +static class Thumbnail : public Routine +{ +public: + using Routine::Routine; + void entry_main() override + { + log_warning("hooks::devel::thumbnail", "Forcing thumbnail patch.."); + hooks::wmisc::apply_performance_hacks(avs::game::DLL_INSTANCE, "FORCE", "thumbnail"); + } +} thumbnail_("thum_test"); + +static class Msacm : public Routine +{ +public: + using Routine::Routine; + void entry_main() override + { + log_warning("hooks::devel::msacm", "Forcing msacm patch.."); + hooks::wmisc::apply_performance_hacks(avs::game::DLL_INSTANCE, "FORCE", "msacm"); + } +} msacm_("acm_test"); + +static class Wmisc : public Routine +{ +public: + using Routine::Routine; + void attach() override + { + log_warning("hooks::devel::wmisc", "Disabling auto-patching.."); + hooks::wmisc::skip_patching_by_model(true); + } +} wmisc_("wmisc_test"); + +static class Avs_fs : public Routine +{ +private: + static constexpr char needle_[] = ""; // set to "" to print all + inline static avs::core::AVS_FS_LSTAT_T avs_fs_lstat_; + inline static avs::core::AVS_FS_MOUNT_T avs_fs_mount_; + inline static avs::core::AVS_FS_OPEN_T avs_fs_open_; + + static int on_avs_fs_lstat(const char *path, struct avs::core::avs_stat *stat) + { + if (strstr(path, needle_)) + { + log_info("hooks::devel", "{}({})", __FUNCTION__, path); + } + return avs_fs_lstat_(path, stat); + } + static avs::core::avs_file_t on_avs_fs_mount(const char *mountpoint, const char *fsroot, const char *fstype, void *data) + { + if (strstr(fsroot, needle_)) + { + log_info("hooks::devel", "{}({}=>{}) @{} :nullptr data?{} ", __FUNCTION__, fsroot, mountpoint, fstype, data == nullptr); + } + return avs_fs_mount_(mountpoint, fsroot, fstype, data); + } + static avs::core::avs_file_t on_avs_fs_open(const char *name, uint16_t mode, int flags) + { + if (strstr(name, needle_)) + { + log_info("hooks::devel", "{}({}::{}) @{}", __FUNCTION__, name, mode, flags); + } + return avs_fs_open_(name, mode, flags); + } + +public: + using Routine::Routine; + void entry_main() override + { + detour::trampoline(reinterpret_cast(avs::core::avs_fs_lstat), reinterpret_cast(on_avs_fs_lstat), reinterpret_cast(&avs_fs_lstat_)); + detour::trampoline(reinterpret_cast(avs::core::avs_fs_mount), reinterpret_cast(on_avs_fs_mount), reinterpret_cast(&avs_fs_mount_)); + detour::trampoline(reinterpret_cast(avs::core::avs_fs_open), reinterpret_cast(on_avs_fs_open), reinterpret_cast(&avs_fs_open_)); + } +} avs_fs_("avs_fs_detour"); -//static constexpr unsigned int to_hash(const char *str, int h = 0) -//{ -// return !str[h] ? 5381 : (to_hash(str, h + 1) * 33) ^ str[h]; -//} std::vector from_env(const char *str) { std::vector r; @@ -94,50 +103,56 @@ std::vector from_env(const char *str) return r; } -static devel_routine_t routines() +static std::vector &routines() { - static devel_routine_t routines_ = -1; - if (routines_ == static_cast(-1)) + static std::vector routines_; + if (routines_.empty()) { - routines_ = 0; - for (const auto &routine: from_env("SPICE_DEVEL")) - { - switch (to_hash(routine.c_str())) - { - case to_hash("avs_fs_detour"): - routines_ |= 1 << avs_fs_detour; - break; - case to_hash("sndb_test"): - routines_ |= 1 << sndb_test; - break; - case to_hash("thum_test"): - routines_ |= 1 << thum_test; - break; - case to_hash("acm_test"): - routines_ |= 1 << acm_test; - break; - default: - break; - } - } + //Default routines + routines_.push_back(&avs_fs_); + routines_.push_back(&thumbnail_); + routines_.push_back(&msacm_); + routines_.push_back(&wmisc_); } - return routines_; } -bool active_routine(hooks::devel::devel_routine_t val) +Routine *active_routine(const char *name) { - return routines() & (1 << val); + unsigned int hash = to_hash(name); + for (auto &routine: routines()) + { + if (routine->hash() == hash) + return routine; + } + return nullptr; } -void bind_routine(devel_routine_t val, void (*cb)()) +void bind_routine(Routine *routine) { - routine_cb_[val] = cb; + routines().push_back(routine); +} +void log_environment() +{ + log_warning("hooks::devel", "WINEDLLOVERRIDES: '{}'", getenv("WINEDLLOVERRIDES") ? getenv("WINEDLLOVERRIDES") : "null"); } void entry_main() { - if (routines()) log_info("hooks::devel", "Processing subroutines.."); - for (devel_routine_t i = 0; i < devel_routine_last; i++) + log_environment(); + const auto rnames = from_env("SPICE_DEVEL"); + if (!rnames.empty()) log_info("hooks::devel", "Run subroutines.."); + for (const auto &routinename: from_env("SPICE_DEVEL")) { - if (active_routine(i) && routine_cb_[i]) routine_cb_[i](); + Routine *routine = active_routine(routinename.c_str()); + if (routine) routine->entry_main(); + } +} +void attach() +{ + const auto rnames = from_env("SPICE_DEVEL"); + if (!rnames.empty()) log_info("hooks::devel", "Attach subroutines.."); + for (const auto &rname: rnames) + { + Routine *routine = active_routine(rname.c_str()); + if (routine) routine->attach(); } } } \ No newline at end of file diff --git a/hooks/develhook.h b/hooks/develhook.h index 133ea94..f292dc0 100644 --- a/hooks/develhook.h +++ b/hooks/develhook.h @@ -1,20 +1,25 @@ #pragma once #include +#include "util/utils.h" namespace hooks::devel { -//_INFO: order matters, low level routines first (if you want them active during later routines) -enum devel_routine : uint32_t +//_INFO: use attach()/entry_main() to run task before game::attach()/game::entry_main() +class Routine { - avs_fs_detour = 0, - sndb_test, - thum_test, - devel_routine_last +private: + unsigned int hash_; +public: + [[nodiscard]] inline unsigned int constexpr hash() const { return hash_; } + virtual void attach() {} + virtual void entry_main() {} + explicit Routine(const char *name) : hash_(to_hash(name)) {} + virtual ~Routine() = default; }; -typedef uint32_t devel_routine_t; -bool active_routine(devel_routine_t val); -void bind_routine(devel_routine_t val, void (*cb)()); +Routine *active_routine(const char *name); +void bind_routine(Routine *routine); +void attach(); void entry_main(); } diff --git a/hooks/sndbhook.cpp b/hooks/sndbhook.cpp index 5945a4b..23af93a 100644 --- a/hooks/sndbhook.cpp +++ b/hooks/sndbhook.cpp @@ -769,105 +769,110 @@ void *on_enum_valid_soundbanks(void *song, int32_t difficulty, int32_t unk2) *((char *) song + offset_.DAT_song_id + 8 + difficulty) = suffix; return r; } -void run_tests() +static class Tests : public hooks::devel::Routine { - size_t szs3p; - BYTE *wav = nullptr; - uint32_t wavsz = 0; - char avspath[AVSPATHMAX]; - avs::core::avs_file_t avsmnt = 0; - char sig[5] = " "; - int id = 25073; - if (!mf_broken::init()) +public: + using Routine::Routine; + void entry_main() override { - log_warning("hooks::soundbank", "MF initialization failed, skipping.."); - } - - // Filesystem tests, s3p unpacking - snprintf(avspath, sizeof(avspath), "data/sound/%05d", id); - if (snd_bank::has_format(avspath, ".ifs")) avsmnt = snd_bank::map_ifs(avspath); - snprintf(avspath + strlen(avspath), 7, "/%05d", id); - snd_bank::has_format(avspath, ".s3p"); - snd_bank::has_format(avspath, ".2dx"); - snd_bank::has_format(avspath, "_pre.2dx"); - uint8_t *bufs3p = snd_bank::read_bank_any(avspath, ".s3p", &szs3p); - snd_s3p *snds3p = snd_s3p::unpack(bufs3p); - if (avsmnt) avs::core::avs_fs_umount(avsmnt); - - // S3P => WMA => WAV => 2DX pipeline - if (snds3p) - { - // S3P/S3V metadata - memcpy(sig, snds3p->header()->sig, 4); - log_info("hooks::soundbank", "{}::Bank[S3P]::Signature {}", id, sig); - log_info("hooks::soundbank", "{}::Bank[S3P]::Size {}", id, szs3p); - log_info("hooks::soundbank", "{}::Bank[S3P]::VoiceCount {}", id, snds3p->header()->voice_cnt); - log_info("hooks::soundbank", "{}::Bank[S3P]::Voice[0]::Size {}", id, snds3p->voice_ptr(0)->sz); - memcpy(sig, snds3p->voice(0)->sig, 4); - log_info("hooks::soundbank", "{}::Bank[S3P]::Voice[0]::Signature {}", id, sig); - snd_bank::dump("raw_wma.wma", snds3p->voice(0)->asf(), snds3p->voice(0)->sz); - - // MF codec (tainted PCM data, bad) - mf_broken::asf_to_wav(snds3p->voice(0)->asf(), snds3p->voice(0)->sz, &wav, &wavsz); - snd_bank::dump("mf_wav.wav", wav, wavsz); - free(wav); - wav = nullptr; - wavsz = 0; - - // AV codec (1:1 unmodified PCM data, other than resampling if enabled) - wav = (BYTE *) malloc(snd_2dx::assertsz_asf(snds3p->voice(0)->sz, avcodec::bmswac_resampler_f32)); - avcodec::BmswTranscoderAsfToWav(snds3p->voice(0)->asf(), snds3p->voice(0)->sz, &wav, &wavsz, avcodec::bmswac_resampler_f32); - snd_bank::dump("ac_wav.wav", wav, wavsz); - free(wav); - wav = nullptr; - wavsz = 0; - - // S3P=>2DX (max 4076 voices) - size_t i, j; - snd_2dx *snd2dx = snd_2dx::pack(id); - log_info("hooks::soundbank", "{}::Bank[2DX]::Name {}", id, snd2dx->header()->name); - for (i = 0; i < snds3p->header()->voice_cnt; i++) + size_t szs3p; + BYTE *wav = nullptr; + uint32_t wavsz = 0; + char avspath[AVSPATHMAX]; + avs::core::avs_file_t avsmnt = 0; + char sig[5] = " "; + int id = 25073; + if (!mf_broken::init()) { - // WMA=>WAV transcode - snd_s3p::bank::voice_hdr *voicesrc = snds3p->voice(i); - j = snd2dx->voice_emplace(snd_2dx::assertsz_asf(voicesrc->sz)); - snd_2dx::bank::voice_hdr *voicedst = snd2dx->voice(j); - BYTE *voicedst_wave = voicedst->wave(); - //mf_broken::asf_to_wav(voicesrc->asf(), voicesrc->sz, &voicedst_wave, &voicedst->sz); - avcodec::BmswTranscoderAsfToWav(voicesrc->asf(), voicesrc->sz, &voicedst_wave, &voicedst->sz, snd_bank::resampler()); - snd2dx->voice_ptr(j)->sz(voicedst); // updates voice_ptr table, do not skip - + log_warning("hooks::soundbank", "MF initialization failed, skipping.."); } - // Valid 2DX (trim is not necessary) - log_info("hooks::soundbank", "{}::Bank[2DX]::Dumping", id); - snd_2dx::bank *buf2dx = snd2dx->serialize(false); - snd_bank::dump("ac_soundbank.2dx", reinterpret_cast(buf2dx), buf2dx->sz()); - } - else - { - log_info("hooks::soundbank", "Tests failed"); - } + // Filesystem tests, s3p unpacking + snprintf(avspath, sizeof(avspath), "data/sound/%05d", id); + if (snd_bank::has_format(avspath, ".ifs")) avsmnt = snd_bank::map_ifs(avspath); + snprintf(avspath + strlen(avspath), 7, "/%05d", id); + snd_bank::has_format(avspath, ".s3p"); + snd_bank::has_format(avspath, ".2dx"); + snd_bank::has_format(avspath, "_pre.2dx"); + uint8_t *bufs3p = snd_bank::read_bank_any(avspath, ".s3p", &szs3p); + snd_s3p *snds3p = snd_s3p::unpack(bufs3p); + if (avsmnt) avs::core::avs_fs_umount(avsmnt); - // Caching and intermediate 2dx storage detection - BmsbEnumValidSoundbanks_t t = BmsbEnumValidSoundbanks; - BmsbEnumValidSoundbanks = [](void *, int32_t, int32_t) -> void * { return nullptr; }; - char *tmp = (char *) malloc(0x3b8 + strlen("00000000") + 1); - int arr[6] = {25073, 24016, 26057, 25073, 26087, 29095}; - strcpy(tmp, "SJISTITLE"); - strcpy(tmp + 0xc0, "SJISARTIST"); - strcpy(tmp + 0x3b8, "00000000"); - for (int it: arr) - { - *(int32_t *) (tmp + 0x3b0) = it; - on_enum_valid_soundbanks(tmp, 3, 0); - } + // S3P => WMA => WAV => 2DX pipeline + if (snds3p) + { + // S3P/S3V metadata + memcpy(sig, snds3p->header()->sig, 4); + log_info("hooks::soundbank", "{}::Bank[S3P]::Signature {}", id, sig); + log_info("hooks::soundbank", "{}::Bank[S3P]::Size {}", id, szs3p); + log_info("hooks::soundbank", "{}::Bank[S3P]::VoiceCount {}", id, snds3p->header()->voice_cnt); + log_info("hooks::soundbank", "{}::Bank[S3P]::Voice[0]::Size {}", id, snds3p->voice_ptr(0)->sz); + memcpy(sig, snds3p->voice(0)->sig, 4); + log_info("hooks::soundbank", "{}::Bank[S3P]::Voice[0]::Signature {}", id, sig); + snd_bank::dump("raw_wma.wma", snds3p->voice(0)->asf(), snds3p->voice(0)->sz); - free(tmp); - BmsbEnumValidSoundbanks = t; - snd_bank::flush_cache(); - mf_broken::deinit(); -} + // MF codec (tainted PCM data, bad) + mf_broken::asf_to_wav(snds3p->voice(0)->asf(), snds3p->voice(0)->sz, &wav, &wavsz); + snd_bank::dump("mf_wav.wav", wav, wavsz); + free(wav); + wav = nullptr; + wavsz = 0; + + // AV codec (1:1 unmodified PCM data, other than resampling if enabled) + wav = (BYTE *) malloc(snd_2dx::assertsz_asf(snds3p->voice(0)->sz, avcodec::bmswac_resampler_f32)); + avcodec::BmswTranscoderAsfToWav(snds3p->voice(0)->asf(), snds3p->voice(0)->sz, &wav, &wavsz, avcodec::bmswac_resampler_f32); + snd_bank::dump("ac_wav.wav", wav, wavsz); + free(wav); + wav = nullptr; + wavsz = 0; + + // S3P=>2DX (max 4076 voices) + size_t i, j; + snd_2dx *snd2dx = snd_2dx::pack(id); + log_info("hooks::soundbank", "{}::Bank[2DX]::Name {}", id, snd2dx->header()->name); + for (i = 0; i < snds3p->header()->voice_cnt; i++) + { + // WMA=>WAV transcode + snd_s3p::bank::voice_hdr *voicesrc = snds3p->voice(i); + j = snd2dx->voice_emplace(snd_2dx::assertsz_asf(voicesrc->sz)); + snd_2dx::bank::voice_hdr *voicedst = snd2dx->voice(j); + BYTE *voicedst_wave = voicedst->wave(); + //mf_broken::asf_to_wav(voicesrc->asf(), voicesrc->sz, &voicedst_wave, &voicedst->sz); + avcodec::BmswTranscoderAsfToWav(voicesrc->asf(), voicesrc->sz, &voicedst_wave, &voicedst->sz, snd_bank::resampler()); + snd2dx->voice_ptr(j)->sz(voicedst); // updates voice_ptr table, do not skip + + } + + // Valid 2DX (trim is not necessary) + log_info("hooks::soundbank", "{}::Bank[2DX]::Dumping", id); + snd_2dx::bank *buf2dx = snd2dx->serialize(false); + snd_bank::dump("ac_soundbank.2dx", reinterpret_cast(buf2dx), buf2dx->sz()); + } + else + { + log_info("hooks::soundbank", "Tests failed"); + } + + // Caching and intermediate 2dx storage detection + BmsbEnumValidSoundbanks_t t = BmsbEnumValidSoundbanks; + BmsbEnumValidSoundbanks = [](void *, int32_t, int32_t) -> void * { return nullptr; }; + char *tmp = (char *) malloc(0x3b8 + strlen("00000000") + 1); + int arr[6] = {25073, 24016, 26057, 25073, 26087, 29095}; + strcpy(tmp, "SJISTITLE"); + strcpy(tmp + 0xc0, "SJISARTIST"); + strcpy(tmp + 0x3b8, "00000000"); + for (int it: arr) + { + *(int32_t *) (tmp + 0x3b0) = it; + on_enum_valid_soundbanks(tmp, 3, 0); + } + + free(tmp); + BmsbEnumValidSoundbanks = t; + snd_bank::flush_cache(); + mf_broken::deinit(); + } +} tests_("sndb_test"); void init(HINSTANCE hmodule, const char *model, const char *ext) { if (false && IsDebuggerPresent()) @@ -960,7 +965,7 @@ void init(HINSTANCE hmodule, const char *model, const char *ext) // Setup on_enum_valid_ksbd and optional tests offset_.guard = new memutils::VProtectGuard((void *) offset_.RD_05d_2dx, SNDPATHFMTMAX); detour::trampoline(reinterpret_cast(offset_.TX_BmsbEnumValidSoundbanks), reinterpret_cast(on_enum_valid_soundbanks), reinterpret_cast(&BmsbEnumValidSoundbanks)); - hooks::devel::bind_routine(hooks::devel::sndb_test, run_tests); + hooks::devel::bind_routine(&tests_); log_info("hooks::soundbank", "Soundbank preprocessor ready"); } diff --git a/hooks/wmischook.cpp b/hooks/wmischook.cpp index ccf2694..0c19f42 100644 --- a/hooks/wmischook.cpp +++ b/hooks/wmischook.cpp @@ -55,7 +55,7 @@ MMRESULT __stdcall on_acmFormatSuggest(HANDLE had, LPWAVEFORMATEX pwfxSrc, LPWAV } return r; } -void run(HINSTANCE hmodule) +void attach(HINSTANCE hmodule) { // Override acmFormatSuggest HMODULE msacm = nullptr; @@ -89,7 +89,7 @@ bool on_thum_exists_(void *titleUTF_getter, int32_t id, char flag) return it->second; } -void run(HINSTANCE hmodule) +void attach(HINSTANCE hmodule) { // Reverse lookup function head (PUSH RDI) for ThumExists() specific signature, quietly fail if invalid intptr_t beg, match, TX_ThumExists; @@ -107,18 +107,37 @@ void run(HINSTANCE hmodule) 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::run(hmodule); - msacm::run(hmodule); + 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; + default: + break; + } break; default: break; } - } } diff --git a/hooks/wmischook.h b/hooks/wmischook.h index a63ddff..c38c9eb 100644 --- a/hooks/wmischook.h +++ b/hooks/wmischook.h @@ -4,5 +4,6 @@ namespace hooks::wmisc { +void skip_patching_by_model(bool val); void apply_performance_hacks(HINSTANCE hmodule, const char *model, const char *ext); } diff --git a/launcher/launcher.cpp b/launcher/launcher.cpp index d6db166..525eaf7 100644 --- a/launcher/launcher.cpp +++ b/launcher/launcher.cpp @@ -2069,6 +2069,9 @@ int main_implementation(int argc, char *argv[]) { // load game avs::game::load_dll(); + // devel attach + hooks::devel::attach(); + // attach games for (auto game : games) { game->attach();