diff --git a/hooks/bmsbhook.cpp b/hooks/bmsbhook.cpp index 1455bf5..1d9a869 100644 --- a/hooks/bmsbhook.cpp +++ b/hooks/bmsbhook.cpp @@ -7,6 +7,95 @@ #include "avs/core.h" #include +//_BUG: manual symbol lookup, should not link against: mf, mfplat, mfreadwrite +#include +#include +#include +#include +#include + + +void dump(const char *path, const uint8_t *src, size_t sz) +{ + FILE *fh = fopen(path, "wb"); + fwrite(src, sz, 1, fh); + fclose(fh); +} + + +static avs::core::AVS_FS_LSTAT_T avs_fs_lstat_; +int on_avs_fs_lstat(const char *path, struct avs::core::avs_stat *stat) +{ + if (strstr(path, "29095")) + { + log_info("hooks::bmsb", "avs_fs_lstat_({})", path); + } + return avs_fs_lstat_(path, stat); +} + +static avs::core::AVS_FS_MOUNT_T avs_fs_mount_; +int on_avs_fs_mount(const char *mountpoint, const char *fsroot, const char *fstype, void *data) +{ + if (strstr(fsroot, "29095")) + { + log_info("hooks::bmsb", "avs_fs_mount({}=>{}) @{} :nullptr data?{} ", fsroot, mountpoint, fstype, data == nullptr); + } + return avs_fs_mount_(mountpoint, fsroot, fstype, data); +} + + +// This implementation outsources libavcodec from host ffmpeg (so that linking ffmpeg stack with spicetools isn't required) +namespace hooks::bmsb::avcodec +{ +enum pcm_resampler : int8_t +{ + bmswac_resampler_none = -1, + bmswac_resampler_u8, + bmswac_resampler_s16, + bmswac_resampler_s32, + bmswac_resampler_f32, + bmswac_resampler_d64 +}; +typedef enum pcm_resampler pcm_resampler_t; +typedef void (*BmswConfigInit_t)(const char *path); +typedef int (*BmswSymlink_t)(const char *srcwpath, const char *dstwpath, int canonic); +typedef int (*BmswTranscoderAsfToWav_t)(const uint8_t *srcasf, uint32_t srcsz, uint8_t **dstwav, uint32_t *dstsz, pcm_resampler_t resampler); +static BmswConfigInit_t BmswConfigInit; +static BmswSymlink_t BmswSymlink; +static BmswTranscoderAsfToWav_t BmswTranscoderAsfToWav; +static HMODULE bmsw_ = nullptr; +bool init() +{ + log_info("hooks::bmsb::avcodec", "{}", __FUNCTION__); + + // Initialize bmsound-wine.dll once + if (!bmsw_ && (bmsw_ = libutils::try_library(MODULE_PATH / "bmsound-wine.dll"))) + { + BmswConfigInit = (BmswConfigInit_t) GetProcAddress(bmsw_, "BmswConfigInit"); + BmswSymlink = (BmswSymlink_t) GetProcAddress(bmsw_, "BmswSymlink"); + BmswTranscoderAsfToWav = (BmswTranscoderAsfToWav_t) GetProcAddress(bmsw_, "BmswTranscoderAsfToWav"); + + // Load config + BmswConfigInit("prop/linux.json"); + return true; + } + + log_fatal("audio::pipewire", "Library not found: '{}'", (MODULE_PATH / "bmsound-wine.dll").string()); + return true; +} + +void deinit() +{ + log_info("hooks::bmsb::avcodec", "{}", __FUNCTION__); +} +} + +// This implementation replicates wine MF wma=>wav playback bug 1:1, follows https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/multimedia/mediafoundation/AudioClip/main.cpp +namespace hooks::bmsb::mf_broken +{ + +typedef HRESULT (*MFCreateMFByteStreamOnStream_t)(IStream *, IMFByteStream **); +MFCreateMFByteStreamOnStream_t MFCreateMFByteStreamOnStream; struct wav_hdr { @@ -25,25 +114,6 @@ struct wav_hdr uint32_t datasz = -1; }; -void dump(const char *path, const char *src, size_t sz) -{ - FILE *fh = fopen(path, "wb"); - fwrite(src, sz, 1, fh); - fclose(fh); -} - -// This implementation replicates wine MF wma=>wav playback bug 1:1, follows https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/multimedia/mediafoundation/AudioClip/main.cpp -namespace hooks::bmsb::mf_broken -{ -//_BUG: manual symbol lookup, should not link against: mf, mfplat, mfreadwrite -#include -#include -#include -#include -#include -typedef HRESULT (*MFCreateMFByteStreamOnStream_t)(IStream *, IMFByteStream **); -MFCreateMFByteStreamOnStream_t MFCreateMFByteStreamOnStream; - HRESULT asf_unpack_pcm_s16le(BYTE *dst, IMFSourceReader *srcreader, uint32_t szmax, uint32_t *sz) { HRESULT hr = S_OK; @@ -180,13 +250,13 @@ void deinit() } - namespace hooks::bmsb { constexpr char SNDPATHFMT[] = "%05d/%05d"; constexpr size_t SNDPATHFMTMAX = sizeof(SNDPATHFMT) - 1; -constexpr size_t SNDPATHMAX = sizeof("AABBB/AABBBC_pre.2dx"); -constexpr size_t CACHEMAX = 32; +constexpr size_t MNTPATHMAX = sizeof("/sdAABBB/AABBB/AABBBC_pre.2dx"); +constexpr size_t AVSPATHMAX = sizeof("/data/sound/AABBB/AABBBC_pre.2dx"); +constexpr uint8_t CACHEMAX = 16; typedef struct soundbank soundbank; typedef struct snd_s3p snd_s3p; typedef struct snd_2dx snd_2dx; @@ -241,7 +311,8 @@ public: [[nodiscard]] static constexpr size_t assertsz_asf(size_t sz) noexcept { - return static_cast(static_cast(sz) * 8.81875) + sizeof(wav_hdr); // 0.wma is consistent 180 therefore VBR Q90, assuming worst case: x8.81875@~160kb/s + //_TODO: second argument data type (float/int) + return static_cast(static_cast(sz) * 14.8) + WAVHDRSIZE; // 0.wma is consistent 180 therefore VBR Q90, sometimes 160 though, but samples are all over the place } [[nodiscard]] constexpr const decltype(bank::header) *header() const noexcept @@ -265,7 +336,7 @@ public: // memory space assertion if (this->voice_ptr(this->bank_->header.voice_cnt)->fbegin + assertsz >= banksz_) { - log_info("hooks:bmsb", "snd_2dx::voice_emplace({}=>{})", banksz_, banksz_ + assertsz + ALLOCSIZE); + log_info("hooks::bmsb", "snd_2dx::voice_emplace({}=>{})", banksz_, banksz_ + assertsz + ALLOCSIZE); banksz_ += assertsz + ALLOCSIZE; bank_ = (bank *) realloc(bank_, banksz_); } @@ -292,13 +363,14 @@ public: ~snd_2dx(); private: + static constexpr size_t WAVHDRSIZE = 44; static constexpr size_t ALLOCSIZE = 4 * 1024 * 1024; struct bank *bank_; size_t banksz_; - explicit snd_2dx(char *bindbank); + explicit snd_2dx(uint8_t *bindbank); }; -snd_2dx::snd_2dx(char *bindbank) : bank_(reinterpret_cast(bindbank)), banksz_(0) +snd_2dx::snd_2dx(uint8_t *bindbank) : bank_(reinterpret_cast(bindbank)), banksz_(0) { if (bank_) return; @@ -376,7 +448,7 @@ public: return (bank::voice_hdr *) ((char *) this->bank_ + this->voice_ptr(i)->fbegin); } - static snd_s3p *unpack(char *refbank); + static snd_s3p *unpack(uint8_t *refbank); static snd_s3p *pack(int id) = delete; bank *serialize(bool trim) = delete; ~snd_s3p(); @@ -384,10 +456,10 @@ public: private: struct bank *bank_; size_t banksz_; //_TODO: keep track of allocated memory - explicit snd_s3p(char *bindbank); + explicit snd_s3p(uint8_t *bindbank); }; -snd_s3p::snd_s3p(char *bindbank) : bank_(reinterpret_cast(bindbank)), banksz_(0) +snd_s3p::snd_s3p(uint8_t *bindbank) : bank_(reinterpret_cast(bindbank)), banksz_(0) { } @@ -395,7 +467,7 @@ snd_s3p::~snd_s3p() { free(bank_); } -snd_s3p *snd_s3p::unpack(char *refbank) +snd_s3p *snd_s3p::unpack(uint8_t *refbank) { return refbank ? new snd_s3p(refbank) : nullptr; } @@ -407,15 +479,42 @@ public: static void flush_cache(); static void pop_cache(); static soundbank *get_bank(int id); - static char *read_data(const char *sndpath, const char *ext, size_t *sz); - static bool has_format(const char *sndpath, const char *ext); + static uint8_t *read_bank_any(const char *avspath, const char *ext, size_t *sz); + static bool has_format(const char *avspath, const char *ext); + static avs::core::avs_file_t map_ifs(char *avspath); int id; - char sndpath[SNDPATHMAX]; + char avspath[AVSPATHMAX]; + void cache_bank_any(const uint8_t *sndbuf, size_t sz); + + [[nodiscard]] constexpr bool is_cached() const noexcept + { + return uid_ != UID_INVALID; + } + + [[nodiscard]] constexpr const char *sndpath() const noexcept + { + const char *it = avspath + AVSPATHMAX; + size_t c = 0; + while (it >= avspath && c < 2) + { + it--; + if (*it == '/') c++; + } + it++; + if (strlen(it) > SNDPATHFMTMAX) + return SNDPATHFMT; + return it; + } + private: + static constexpr avs::core::avs_file_t E_NOT_FOUND = 0x80070002; + static constexpr uint8_t UID_INVALID = 0x00; static soundbank *cache_; static size_t cachesz_; + uint8_t uid_; + uint32_t mnt_; soundbank *next_; soundbank(); ~soundbank(); @@ -430,8 +529,8 @@ static struct offset_t memutils::VProtectGuard *guard; } offset_; -soundbank *soundbank::cache_; -size_t soundbank::cachesz_; +soundbank *soundbank::cache_ = nullptr; +size_t soundbank::cachesz_ = 0; static BmsbEnumValidSoundbanks_t BmsbEnumValidSoundbanks; soundbank *soundbank::new_instance(int id) @@ -446,44 +545,94 @@ soundbank *soundbank::new_instance(int id) cachesz_ += 1; // Clean cache (last entry indexing target excluded, cache limit reached) - if (instance->next_ && instance->next_->sndpath[0] != 's') + if (instance->next_ && instance->next_->uid_ == UID_INVALID) { + log_info("hooks::bmsb", "Cache prune"); prev = instance->next_; instance->next_ = prev->next_; + log_info("hooks::bmsb", "Removing {}::Bank[CACHE]::{}", prev->id, (uint32_t) prev->uid_); delete prev; cachesz_ -= 1; } - if (cachesz_ >= CACHEMAX) + if (cachesz_ > CACHEMAX) { - for (prev = instance; instance; (instance = instance->next_) && (prev = instance)); - prev->next_ = nullptr; - delete instance; - cachesz_ -= 1; + log_info("hooks::bmsb", "Cache full"); + for (uint8_t i = 0; i < CACHEMAX; i += 2) + { + prev = nullptr; + instance = cache_; + while (instance && instance->next_) + { + prev = instance; + instance = instance->next_; + } + prev->next_ = nullptr; + delete instance; + cachesz_ -= 1; + } } return cache_; } -soundbank::soundbank() : id(0), sndpath(""), next_(nullptr) +void soundbank::cache_bank_any(const uint8_t *sndbuf, size_t sz) +{ + // Update struct using next free cache uid + if (uid_ == UID_INVALID) + { + soundbank *it = cache_; + while (it) + { + if (uid_ == UID_INVALID || (uid_ == it->uid_ && it != this)) + { + uid_--; + it = cache_; + continue; + } + it = it->next_; + } + } + snprintf(this->avspath, sizeof(this->avspath), "data/sound/system/%02x", (unsigned int) uid_); + + // Store file + char path[AVSPATHMAX]; + snprintf(path, sizeof(path), "%s.2dx", this->avspath); + log_info("hooks::bmsb", "Storing as intermediate soundbank at '{}'", (const char *) path); + dump(path, sndbuf, sz); +} +soundbank::soundbank() : id(0), avspath(""), uid_(UID_INVALID), mnt_(E_NOT_FOUND), next_(nullptr) { } soundbank::~soundbank() { - + if (uid_ != UID_INVALID) + { + char path[AVSPATHMAX]; + snprintf(path, sizeof(path), "data/sound/system/%02x.2dx", uid_); + log_info("hooks::bmsb", "{}::Bank[CACHE]::Purge '{}'", this->id, (const char *) path); + remove(path); + } + if (mnt_ != E_NOT_FOUND) + { + //_TODO: "XCgsqzn000004d(uint32_t)" avs_fs_umount(mnt_) + } } soundbank *soundbank::get_bank(int id) { // Find in cache (latest=>oldest) - for (soundbank *it = soundbank::cache_; it; it = it->next_) + for (soundbank *it = cache_; it; it = it->next_) if (it->id == id) return it; // Find in avs (has valid s3p/.2dx resource at data/sound/AABBB/AABBB.2dx) soundbank *bank = new_instance(id); - snprintf(bank->sndpath, SNDPATHMAX, "%05d/%05d", id, id); - if (!(soundbank::has_format(bank->sndpath, ".s3p") || soundbank::has_format(bank->sndpath, ".2dx"))) + snprintf(bank->avspath, AVSPATHMAX, "data/sound/%05d", id); + if (has_format(bank->avspath, ".ifs")) + map_ifs(bank->avspath); + snprintf(bank->avspath + strlen(bank->avspath), 7, "/%05d", id); + if (!(has_format(bank->avspath, ".s3p") || has_format(bank->avspath, ".2dx"))) { - soundbank::pop_cache(); + pop_cache(); bank = nullptr; } @@ -505,16 +654,14 @@ void soundbank::pop_cache() cache_ = cache_->next_; delete prev; } -char *soundbank::read_data(const char *sndpath, const char *ext, size_t *sz) +uint8_t *soundbank::read_bank_any(const char *avspath, const char *ext, size_t *sz) { constexpr uint16_t O_RDONLY = 1; // rw:12 consistent across versions with s3p support - constexpr avs::core::avs_file_t E_NOT_FOUND = 0x80070002; + uint8_t *r = nullptr; + char path[AVSPATHMAX]; - char *r = nullptr; - char avspath[64] = ""; - snprintf(avspath, sizeof(avspath), "/data/sound/%s%s", sndpath, ext); - - avs::core::avs_file_t fd = avs::core::avs_fs_open(avspath, O_RDONLY, 420); + snprintf(path, sizeof(path), "%s%s", avspath, ext); + avs::core::avs_file_t fd = avs::core::avs_fs_open(path, O_RDONLY, 420); if (fd != E_NOT_FOUND) { { @@ -522,18 +669,44 @@ char *soundbank::read_data(const char *sndpath, const char *ext, size_t *sz) avs::core::avs_fs_fstat(fd, &st); *sz = st.filesize; } - r = (char *) malloc(*sz); - if (r) avs::core::avs_fs_read(fd, reinterpret_cast(r), *sz); + r = (uint8_t *) malloc(*sz); + if (r) avs::core::avs_fs_read(fd, r, *sz); avs::core::avs_fs_close(fd); } return r; } -bool soundbank::has_format(const char *sndpath, const char *ext) +bool soundbank::has_format(const char *avspath, const char *ext) { - char avspath[64] = ""; - snprintf(avspath, sizeof(avspath), "/data/sound/%s%s", sndpath, ext); + char path[AVSPATHMAX] = ""; + snprintf(path, sizeof(path), "%s%s", avspath, ext); avs::core::avs_stat st; // NOLINT(cppcoreguidelines-pro-type-member-init) - return static_cast(avs::core::avs_fs_lstat(avspath, &st)); // lstat should be faster than open + bool r = static_cast(avs::core::avs_fs_lstat(path, &st)); // lstat should be faster than open + log_info("hooks::bmsb", "'{}' exists? => {}", path, r); + return static_cast(r); +} +avs::core::avs_file_t soundbank::map_ifs(char *avspath) +{ + // Map */${name}.ifs => /sc${name} + char ifspath[AVSPATHMAX]; + char mntpath[MNTPATHMAX]; + const char *it = avspath + strlen(avspath); + while (it >= avspath && *it != '/') + { + it--; + } + it++; + snprintf(ifspath, sizeof(ifspath), "%s.ifs", avspath); + snprintf(mntpath, sizeof(mntpath), "sc%s", it); + avs::core::avs_file_t r = avs::core::avs_fs_mount(mntpath, ifspath, "imagefs", nullptr); + + // Finalize avspath + if (r != E_NOT_FOUND) + { + snprintf(mntpath + strlen(mntpath), strlen(it) + 2, "/%s", it); + log_info("hooks::bmsb", "'{}' => '{}'", avspath, mntpath); + strcpy(avspath, mntpath); + } + return r; } @@ -546,38 +719,46 @@ void on_enum_valid_soundbanks(void *song, int32_t difficulty, int32_t unk2) const char *artistl = (const char *) song + 0xc0; const int32_t id = *(int32_t *) ((const char *) song + 0x3b0); - log_info("hooks::bmsb", "Processing s3p->2dx detour routine for [{}]({} - {})", id, artistl, titlel); + log_info("hooks::bmsb", "Processing s3p->2dx transcoding routine for [{}]({} - {})", id, artistl, titlel); if (!(keysounds = soundbank::get_bank(id))) { - log_info("hooks::bmsb", "soundbank::get_bank() was 0x0, trying to recover"); + log_warning("hooks::bmsb", "soundbank::get_bank() was 0x0, this shouldn't happen.."); memcpy(reinterpret_cast(offset_.RD_05d_2dx), "%05d/%05d", SNDPATHFMTMAX); BmsbEnumValidSoundbanks(song, difficulty, unk2); return; } - else if (!soundbank::has_format(keysounds->sndpath, ".2dx")) + else if (!soundbank::has_format(keysounds->avspath, ".2dx")) { - // Get s3p + // Get s3p and process into s3p soundbank for intermediate wma voice storage size_t szs3p; - char *bufs3p = soundbank::read_data(keysounds->sndpath, ".s3p", &szs3p); - - // Process s3p into wma + uint8_t *bufs3p = soundbank::read_bank_any(keysounds->avspath, ".s3p", &szs3p); snd_s3p *snds3p = snd_s3p::unpack(bufs3p); - // Process wma into wav + // Process wma voices into wav voices stored directly into 2dx soundbank + size_t i, j; + snd_2dx *snd2dx = snd_2dx::pack(id); + 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(); + avcodec::BmswTranscoderAsfToWav(voicesrc->asf(), voicesrc->sz, &voicedst_wave, &voicedst->sz, avcodec::bmswac_resampler_s16); + snd2dx->voice_ptr(j)->sz(voicedst); // updates voice_ptr table, do not skip + } - // Process wav into 2dx - - // Output 2dx into /var/tmp (z:\var\tmp) - - // symlink system/00.2dx -> /tmp/output - - // Store to cache (skip transcoding on consecutive runs) + // Output to cache (reuse output file, while in valid cache) + snd_2dx::bank *buf2dx = snd2dx->serialize(false); + keysounds->cache_bank_any(reinterpret_cast(buf2dx), buf2dx->sz()); + //_REV: symlink system/uid_.2dx -> /tmp/uid_.2dx } // Resume execution - memcpy(reinterpret_cast(offset_.RD_05d_2dx), strlen(keysounds->sndpath) == SNDPATHFMTMAX ? keysounds->sndpath : "%05d/%05d", SNDPATHFMTMAX); - log_info("hooks::bmsb", "RD_05d_2dx set as '{}'", reinterpret_cast(offset_.RD_05d_2dx)); //_REM: tests + const char *t = keysounds->sndpath(); + memcpy(reinterpret_cast(offset_.RD_05d_2dx), t, SNDPATHFMTMAX); + log_info("hooks::bmsb", "RD_05d_2dx set to '{}'", reinterpret_cast(offset_.RD_05d_2dx)); //_REM: tests log_info("hooks::bmsb", "Processing completed"); BmsbEnumValidSoundbanks(song, difficulty, unk2); } @@ -585,42 +766,59 @@ void on_enum_valid_soundbanks(void *song, int32_t difficulty, int32_t unk2) void run_tests() { size_t szs3p; + BYTE *wav = nullptr; + uint32_t wavsz = 0; char sig[5]; sig[4] = '\0'; + int id = 29095; - char *bufs3p = soundbank::read_data("25073/25073", ".s3p", &szs3p); + #if 0 + // s3p/2dx un/packing and wma=>wav transcoder + char sndpath[64]; + snprintf(sndpath, 64, "%05d/%05d", id, id); + char *bufs3p = soundbank::read_bank_any(sndpath, ".s3p", &szs3p); snd_s3p *snds3p = snd_s3p::unpack(bufs3p); if (snds3p) { // S3P/S3V - log_info("hooks:bmsb", "s3p(25073/25006)? => ({}/{})", soundbank::has_format("25073/25073", ".s3p"), soundbank::has_format("25006/25006", ".s3p")); + log_info("hooks:bmsb", "s3p(25073/25006)? => ({}/{})", soundbank::has_format(sndpath, ".s3p"), soundbank::has_format("25006/25006", ".s3p")); memcpy(sig, snds3p->header()->sig, 4); - log_info("hooks:bmsb", "25073::Bank[S3P]::Signature {}", sig); - log_info("hooks:bmsb", "25073::Bank[S3P]::Size {}", szs3p); - log_info("hooks:bmsb", "25073::Bank[S3P]::VoiceCount {}", snds3p->header()->voice_cnt); - log_info("hooks:bmsb", "25073::Bank[S3P]::Voice[0]::Size {}", snds3p->voice_ptr(0)->sz); + log_info("hooks:bmsb", "{}::Bank[S3P]::Signature {}", id, sig); + log_info("hooks:bmsb", "{}::Bank[S3P]::Size {}", id, szs3p); + log_info("hooks:bmsb", "{}::Bank[S3P]::VoiceCount {}", id, snds3p->header()->voice_cnt); + log_info("hooks:bmsb", "{}::Bank[S3P]::Voice[0]::Size {}", id, snds3p->voice_ptr(0)->sz); memcpy(sig, snds3p->voice(0)->sig, 4); - log_info("hooks:bmsb", "25073::Bank[S3P]::Voice[0]::Signature {}", sig); + log_info("hooks:bmsb", "{}::Bank[S3P]::Voice[0]::Signature {}", id, sig); + dump("raw_wma.wma", snds3p->voice(0)->asf(), snds3p->voice(0)->sz); // MF codec (tainted PCM data, bad) - BYTE *wav = nullptr; - uint32_t wavsz; - dump("mf_raw.wma", reinterpret_cast(snds3p->voice(0)->asf()), snds3p->voice(0)->sz); mf_broken::asf_to_wav(snds3p->voice(0)->asf(), snds3p->voice(0)->sz, &wav, &wavsz); - dump("mf_wav.wav", reinterpret_cast(wav), wavsz); + 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::BmswTranscoderAsfToWav(snds3p->voice(0)->asf(), snds3p->voice(0)->sz, &wav, &wavsz, avcodec::bmswac_resampler_s16); + 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(25073); - log_info("hooks:bmsb", "25073::Bank[2DX]::Name {}", snd2dx->header()->name); + snd_2dx *snd2dx = snd_2dx::pack(id); + log_info("hooks:bmsb", "{}::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); - auto *voicedst_wave = voicedst->wave(); - mf_broken::asf_to_wav(voicesrc->asf(), voicesrc->sz, &voicedst_wave, &voicedst->sz); + 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, avcodec::bmswac_resampler_s16); snd2dx->voice_ptr(j)->sz(voicedst); // updates voice_ptr table, do not skip } @@ -628,13 +826,41 @@ void run_tests() // Valid 2DX (trim is not necessary) log_info("hooks:bmsb", "25073::Bank[2DX]::dump()"); snd_2dx::bank *buf2dx = snd2dx->serialize(false); - dump("mf_25073.2dx", reinterpret_cast(buf2dx), buf2dx->sz()); + dump("ac_25073.2dx", reinterpret_cast(buf2dx), buf2dx->sz()); } else { log_info("hooks:bmsb", "Tests failed"); } + #endif + + char avspath[AVSPATHMAX] = "data/sound/29095"; + soundbank::has_format(avspath, ".ifs"); + soundbank::map_ifs(avspath); + strcat(avspath, "/29095"); + soundbank::has_format(avspath, ".s3p"); + soundbank::has_format(avspath, ".2dx"); + soundbank::has_format(avspath, "_pre.2dx"); + + return; + + // Caching and intermediate 2dx storage detection + BmsbEnumValidSoundbanks_t t = BmsbEnumValidSoundbanks; + BmsbEnumValidSoundbanks = [](void *, int32_t, int32_t) { log_info("hooks:bmsb", "BmsbEnumValidSoundbanks(0x0)"); }; + char *tmp = (char *) malloc(0x3b0 + sizeof(int32_t)); + int arr[10] = {25073, 26020, 26094, 24016, 26057, 25073, 26069, 26070, 26059, 26087}; + //int arr[10] = {25073, 24016, 26057, 25073, 26087}; + strcpy(tmp, "SJISTITLE"); + strcpy(tmp + 0xc0, "SJISARTIST"); + for (int it: arr) + { + *(int32_t *) (tmp + 0x3b0) = it; + on_enum_valid_soundbanks(tmp, 3, 0); + } + free(tmp); + BmsbEnumValidSoundbanks = t; + soundbank::flush_cache(); } @@ -651,9 +877,10 @@ void init(HINSTANCE hmodule) log_warning("hooks::bmsb", "MF initialization failed, skipping.."); } - // Tests _REM: tests - run_tests(); - return; + if (!avcodec::init()) + { + log_warning("hooks::bmsb", "AVCodec initialization failed, skipping.."); + } // Override .text offset_.TX_BmsbEnumValidSoundbanks = (intptr_t) hmodule + 0xafd780; //_TODO: Hardcoded for iidx30 for now, requires asm pattern lookup @@ -674,6 +901,12 @@ void init(HINSTANCE hmodule) detour::trampoline(reinterpret_cast(offset_.TX_BmsbEnumValidSoundbanks), reinterpret_cast(on_enum_valid_soundbanks), reinterpret_cast(&BmsbEnumValidSoundbanks)); log_info("hooks::bmsb", "Soundbank preprocessor ready"); + + // Tests _REM: tests + 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_)); + run_tests(); + } void deinit(HINSTANCE hmodule)