feat: Integrate linux-side avcodec support
> support only IIDX30 > working full wma=>wav transcoding pipeline with ifs support > missing unmounting behaviour (will add later) > depends on latest bmsound_wine for avcodec transcoding > related to #1
This commit is contained in:
+326
-93
@@ -7,6 +7,95 @@
|
|||||||
#include "avs/core.h"
|
#include "avs/core.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
//_BUG: manual symbol lookup, should not link against: mf, mfplat, mfreadwrite
|
||||||
|
#include <mfapi.h>
|
||||||
|
#include <mfidl.h>
|
||||||
|
#include <mfreadwrite.h>
|
||||||
|
#include <mfobjects.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
struct wav_hdr
|
||||||
{
|
{
|
||||||
@@ -25,25 +114,6 @@ struct wav_hdr
|
|||||||
uint32_t datasz = -1;
|
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 <mfapi.h>
|
|
||||||
#include <mfidl.h>
|
|
||||||
#include <mfreadwrite.h>
|
|
||||||
#include <mfobjects.h>
|
|
||||||
#include <shlwapi.h>
|
|
||||||
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 asf_unpack_pcm_s16le(BYTE *dst, IMFSourceReader *srcreader, uint32_t szmax, uint32_t *sz)
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
@@ -180,13 +250,13 @@ void deinit()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace hooks::bmsb
|
namespace hooks::bmsb
|
||||||
{
|
{
|
||||||
constexpr char SNDPATHFMT[] = "%05d/%05d";
|
constexpr char SNDPATHFMT[] = "%05d/%05d";
|
||||||
constexpr size_t SNDPATHFMTMAX = sizeof(SNDPATHFMT) - 1;
|
constexpr size_t SNDPATHFMTMAX = sizeof(SNDPATHFMT) - 1;
|
||||||
constexpr size_t SNDPATHMAX = sizeof("AABBB/AABBBC_pre.2dx");
|
constexpr size_t MNTPATHMAX = sizeof("/sdAABBB/AABBB/AABBBC_pre.2dx");
|
||||||
constexpr size_t CACHEMAX = 32;
|
constexpr size_t AVSPATHMAX = sizeof("/data/sound/AABBB/AABBBC_pre.2dx");
|
||||||
|
constexpr uint8_t CACHEMAX = 16;
|
||||||
typedef struct soundbank soundbank;
|
typedef struct soundbank soundbank;
|
||||||
typedef struct snd_s3p snd_s3p;
|
typedef struct snd_s3p snd_s3p;
|
||||||
typedef struct snd_2dx snd_2dx;
|
typedef struct snd_2dx snd_2dx;
|
||||||
@@ -241,7 +311,8 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] static constexpr size_t assertsz_asf(size_t sz) noexcept
|
[[nodiscard]] static constexpr size_t assertsz_asf(size_t sz) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<size_t>(static_cast<double>(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<size_t>(static_cast<double>(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
|
[[nodiscard]] constexpr const decltype(bank::header) *header() const noexcept
|
||||||
@@ -265,7 +336,7 @@ public:
|
|||||||
// memory space assertion
|
// memory space assertion
|
||||||
if (this->voice_ptr(this->bank_->header.voice_cnt)->fbegin + assertsz >= banksz_)
|
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;
|
banksz_ += assertsz + ALLOCSIZE;
|
||||||
bank_ = (bank *) realloc(bank_, banksz_);
|
bank_ = (bank *) realloc(bank_, banksz_);
|
||||||
}
|
}
|
||||||
@@ -292,13 +363,14 @@ public:
|
|||||||
~snd_2dx();
|
~snd_2dx();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr size_t WAVHDRSIZE = 44;
|
||||||
static constexpr size_t ALLOCSIZE = 4 * 1024 * 1024;
|
static constexpr size_t ALLOCSIZE = 4 * 1024 * 1024;
|
||||||
struct bank *bank_;
|
struct bank *bank_;
|
||||||
size_t banksz_;
|
size_t banksz_;
|
||||||
explicit snd_2dx(char *bindbank);
|
explicit snd_2dx(uint8_t *bindbank);
|
||||||
|
|
||||||
};
|
};
|
||||||
snd_2dx::snd_2dx(char *bindbank) : bank_(reinterpret_cast<struct bank *>(bindbank)), banksz_(0)
|
snd_2dx::snd_2dx(uint8_t *bindbank) : bank_(reinterpret_cast<struct bank *>(bindbank)), banksz_(0)
|
||||||
{
|
{
|
||||||
if (bank_)
|
if (bank_)
|
||||||
return;
|
return;
|
||||||
@@ -376,7 +448,7 @@ public:
|
|||||||
return (bank::voice_hdr *) ((char *) this->bank_ + this->voice_ptr(i)->fbegin);
|
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;
|
static snd_s3p *pack(int id) = delete;
|
||||||
bank *serialize(bool trim) = delete;
|
bank *serialize(bool trim) = delete;
|
||||||
~snd_s3p();
|
~snd_s3p();
|
||||||
@@ -384,10 +456,10 @@ public:
|
|||||||
private:
|
private:
|
||||||
struct bank *bank_;
|
struct bank *bank_;
|
||||||
size_t banksz_; //_TODO: keep track of allocated memory
|
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<struct bank *>(bindbank)), banksz_(0)
|
snd_s3p::snd_s3p(uint8_t *bindbank) : bank_(reinterpret_cast<struct bank *>(bindbank)), banksz_(0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -395,7 +467,7 @@ snd_s3p::~snd_s3p()
|
|||||||
{
|
{
|
||||||
free(bank_);
|
free(bank_);
|
||||||
}
|
}
|
||||||
snd_s3p *snd_s3p::unpack(char *refbank)
|
snd_s3p *snd_s3p::unpack(uint8_t *refbank)
|
||||||
{
|
{
|
||||||
return refbank ? new snd_s3p(refbank) : nullptr;
|
return refbank ? new snd_s3p(refbank) : nullptr;
|
||||||
}
|
}
|
||||||
@@ -407,15 +479,42 @@ public:
|
|||||||
static void flush_cache();
|
static void flush_cache();
|
||||||
static void pop_cache();
|
static void pop_cache();
|
||||||
static soundbank *get_bank(int id);
|
static soundbank *get_bank(int id);
|
||||||
static char *read_data(const char *sndpath, const char *ext, size_t *sz);
|
static uint8_t *read_bank_any(const char *avspath, const char *ext, size_t *sz);
|
||||||
static bool has_format(const char *sndpath, const char *ext);
|
static bool has_format(const char *avspath, const char *ext);
|
||||||
|
static avs::core::avs_file_t map_ifs(char *avspath);
|
||||||
|
|
||||||
int id;
|
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:
|
private:
|
||||||
|
static constexpr avs::core::avs_file_t E_NOT_FOUND = 0x80070002;
|
||||||
|
static constexpr uint8_t UID_INVALID = 0x00;
|
||||||
static soundbank *cache_;
|
static soundbank *cache_;
|
||||||
static size_t cachesz_;
|
static size_t cachesz_;
|
||||||
|
|
||||||
|
uint8_t uid_;
|
||||||
|
uint32_t mnt_;
|
||||||
soundbank *next_;
|
soundbank *next_;
|
||||||
soundbank();
|
soundbank();
|
||||||
~soundbank();
|
~soundbank();
|
||||||
@@ -430,8 +529,8 @@ static struct offset_t
|
|||||||
memutils::VProtectGuard *guard;
|
memutils::VProtectGuard *guard;
|
||||||
} offset_;
|
} offset_;
|
||||||
|
|
||||||
soundbank *soundbank::cache_;
|
soundbank *soundbank::cache_ = nullptr;
|
||||||
size_t soundbank::cachesz_;
|
size_t soundbank::cachesz_ = 0;
|
||||||
static BmsbEnumValidSoundbanks_t BmsbEnumValidSoundbanks;
|
static BmsbEnumValidSoundbanks_t BmsbEnumValidSoundbanks;
|
||||||
|
|
||||||
soundbank *soundbank::new_instance(int id)
|
soundbank *soundbank::new_instance(int id)
|
||||||
@@ -446,44 +545,94 @@ soundbank *soundbank::new_instance(int id)
|
|||||||
cachesz_ += 1;
|
cachesz_ += 1;
|
||||||
|
|
||||||
// Clean cache (last entry indexing target excluded, cache limit reached)
|
// 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_;
|
prev = instance->next_;
|
||||||
instance->next_ = prev->next_;
|
instance->next_ = prev->next_;
|
||||||
|
log_info("hooks::bmsb", "Removing {}::Bank[CACHE]::{}", prev->id, (uint32_t) prev->uid_);
|
||||||
delete prev;
|
delete prev;
|
||||||
cachesz_ -= 1;
|
cachesz_ -= 1;
|
||||||
}
|
}
|
||||||
if (cachesz_ >= CACHEMAX)
|
if (cachesz_ > CACHEMAX)
|
||||||
{
|
{
|
||||||
for (prev = instance; instance; (instance = instance->next_) && (prev = instance));
|
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;
|
prev->next_ = nullptr;
|
||||||
delete instance;
|
delete instance;
|
||||||
cachesz_ -= 1;
|
cachesz_ -= 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return cache_;
|
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()
|
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)
|
soundbank *soundbank::get_bank(int id)
|
||||||
{
|
{
|
||||||
// Find in cache (latest=>oldest)
|
// 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;
|
if (it->id == id) return it;
|
||||||
|
|
||||||
// Find in avs (has valid s3p/.2dx resource at data/sound/AABBB/AABBB.2dx)
|
// Find in avs (has valid s3p/.2dx resource at data/sound/AABBB/AABBB.2dx)
|
||||||
soundbank *bank = new_instance(id);
|
soundbank *bank = new_instance(id);
|
||||||
snprintf(bank->sndpath, SNDPATHMAX, "%05d/%05d", id, id);
|
snprintf(bank->avspath, AVSPATHMAX, "data/sound/%05d", id);
|
||||||
if (!(soundbank::has_format(bank->sndpath, ".s3p") || soundbank::has_format(bank->sndpath, ".2dx")))
|
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;
|
bank = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,16 +654,14 @@ void soundbank::pop_cache()
|
|||||||
cache_ = cache_->next_;
|
cache_ = cache_->next_;
|
||||||
delete prev;
|
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 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;
|
snprintf(path, sizeof(path), "%s%s", avspath, ext);
|
||||||
char avspath[64] = "";
|
avs::core::avs_file_t fd = avs::core::avs_fs_open(path, O_RDONLY, 420);
|
||||||
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);
|
|
||||||
if (fd != E_NOT_FOUND)
|
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);
|
avs::core::avs_fs_fstat(fd, &st);
|
||||||
*sz = st.filesize;
|
*sz = st.filesize;
|
||||||
}
|
}
|
||||||
r = (char *) malloc(*sz);
|
r = (uint8_t *) malloc(*sz);
|
||||||
if (r) avs::core::avs_fs_read(fd, reinterpret_cast<uint8_t *>(r), *sz);
|
if (r) avs::core::avs_fs_read(fd, r, *sz);
|
||||||
avs::core::avs_fs_close(fd);
|
avs::core::avs_fs_close(fd);
|
||||||
}
|
}
|
||||||
return r;
|
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] = "";
|
char path[AVSPATHMAX] = "";
|
||||||
snprintf(avspath, sizeof(avspath), "/data/sound/%s%s", sndpath, ext);
|
snprintf(path, sizeof(path), "%s%s", avspath, ext);
|
||||||
avs::core::avs_stat st; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
avs::core::avs_stat st; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||||
return static_cast<bool>(avs::core::avs_fs_lstat(avspath, &st)); // lstat should be faster than open
|
bool r = static_cast<bool>(avs::core::avs_fs_lstat(path, &st)); // lstat should be faster than open
|
||||||
|
log_info("hooks::bmsb", "'{}' exists? => {}", path, r);
|
||||||
|
return static_cast<bool>(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 char *artistl = (const char *) song + 0xc0;
|
||||||
const int32_t id = *(int32_t *) ((const char *) song + 0x3b0);
|
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)))
|
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<void *>(offset_.RD_05d_2dx), "%05d/%05d", SNDPATHFMTMAX);
|
memcpy(reinterpret_cast<void *>(offset_.RD_05d_2dx), "%05d/%05d", SNDPATHFMTMAX);
|
||||||
BmsbEnumValidSoundbanks(song, difficulty, unk2);
|
BmsbEnumValidSoundbanks(song, difficulty, unk2);
|
||||||
return;
|
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;
|
size_t szs3p;
|
||||||
char *bufs3p = soundbank::read_data(keysounds->sndpath, ".s3p", &szs3p);
|
uint8_t *bufs3p = soundbank::read_bank_any(keysounds->avspath, ".s3p", &szs3p);
|
||||||
|
|
||||||
// Process s3p into wma
|
|
||||||
snd_s3p *snds3p = snd_s3p::unpack(bufs3p);
|
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 to cache (reuse output file, while in valid cache)
|
||||||
|
snd_2dx::bank *buf2dx = snd2dx->serialize(false);
|
||||||
// Output 2dx into /var/tmp (z:\var\tmp)
|
keysounds->cache_bank_any(reinterpret_cast<const uint8_t *>(buf2dx), buf2dx->sz());
|
||||||
|
|
||||||
// symlink system/00.2dx -> /tmp/output
|
|
||||||
|
|
||||||
// Store to cache (skip transcoding on consecutive runs)
|
|
||||||
|
|
||||||
|
//_REV: symlink system/uid_.2dx -> /tmp/uid_.2dx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resume execution
|
// Resume execution
|
||||||
memcpy(reinterpret_cast<void *>(offset_.RD_05d_2dx), strlen(keysounds->sndpath) == SNDPATHFMTMAX ? keysounds->sndpath : "%05d/%05d", SNDPATHFMTMAX);
|
const char *t = keysounds->sndpath();
|
||||||
log_info("hooks::bmsb", "RD_05d_2dx set as '{}'", reinterpret_cast<const char *>(offset_.RD_05d_2dx)); //_REM: tests
|
memcpy(reinterpret_cast<void *>(offset_.RD_05d_2dx), t, SNDPATHFMTMAX);
|
||||||
|
log_info("hooks::bmsb", "RD_05d_2dx set to '{}'", reinterpret_cast<const char *>(offset_.RD_05d_2dx)); //_REM: tests
|
||||||
log_info("hooks::bmsb", "Processing completed");
|
log_info("hooks::bmsb", "Processing completed");
|
||||||
BmsbEnumValidSoundbanks(song, difficulty, unk2);
|
BmsbEnumValidSoundbanks(song, difficulty, unk2);
|
||||||
}
|
}
|
||||||
@@ -585,42 +766,59 @@ void on_enum_valid_soundbanks(void *song, int32_t difficulty, int32_t unk2)
|
|||||||
void run_tests()
|
void run_tests()
|
||||||
{
|
{
|
||||||
size_t szs3p;
|
size_t szs3p;
|
||||||
|
BYTE *wav = nullptr;
|
||||||
|
uint32_t wavsz = 0;
|
||||||
char sig[5];
|
char sig[5];
|
||||||
sig[4] = '\0';
|
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);
|
snd_s3p *snds3p = snd_s3p::unpack(bufs3p);
|
||||||
if (snds3p)
|
if (snds3p)
|
||||||
{
|
{
|
||||||
// S3P/S3V
|
// 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);
|
memcpy(sig, snds3p->header()->sig, 4);
|
||||||
log_info("hooks:bmsb", "25073::Bank[S3P]::Signature {}", sig);
|
log_info("hooks:bmsb", "{}::Bank[S3P]::Signature {}", id, sig);
|
||||||
log_info("hooks:bmsb", "25073::Bank[S3P]::Size {}", szs3p);
|
log_info("hooks:bmsb", "{}::Bank[S3P]::Size {}", id, szs3p);
|
||||||
log_info("hooks:bmsb", "25073::Bank[S3P]::VoiceCount {}", snds3p->header()->voice_cnt);
|
log_info("hooks:bmsb", "{}::Bank[S3P]::VoiceCount {}", id, 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]::Voice[0]::Size {}", id, snds3p->voice_ptr(0)->sz);
|
||||||
memcpy(sig, snds3p->voice(0)->sig, 4);
|
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)
|
// MF codec (tainted PCM data, bad)
|
||||||
BYTE *wav = nullptr;
|
|
||||||
uint32_t wavsz;
|
|
||||||
dump("mf_raw.wma", reinterpret_cast<const char *>(snds3p->voice(0)->asf()), snds3p->voice(0)->sz);
|
|
||||||
mf_broken::asf_to_wav(snds3p->voice(0)->asf(), snds3p->voice(0)->sz, &wav, &wavsz);
|
mf_broken::asf_to_wav(snds3p->voice(0)->asf(), snds3p->voice(0)->sz, &wav, &wavsz);
|
||||||
dump("mf_wav.wav", reinterpret_cast<const char *>(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)
|
// S3P=>2DX (max 4076 voices)
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
snd_2dx *snd2dx = snd_2dx::pack(25073);
|
snd_2dx *snd2dx = snd_2dx::pack(id);
|
||||||
log_info("hooks:bmsb", "25073::Bank[2DX]::Name {}", snd2dx->header()->name);
|
log_info("hooks:bmsb", "{}::Bank[2DX]::Name {}", id, snd2dx->header()->name);
|
||||||
for (i = 0; i < snds3p->header()->voice_cnt; i++)
|
for (i = 0; i < snds3p->header()->voice_cnt; i++)
|
||||||
{
|
{
|
||||||
// WMA=>WAV transcode
|
// WMA=>WAV transcode
|
||||||
snd_s3p::bank::voice_hdr *voicesrc = snds3p->voice(i);
|
snd_s3p::bank::voice_hdr *voicesrc = snds3p->voice(i);
|
||||||
j = snd2dx->voice_emplace(snd_2dx::assertsz_asf(voicesrc->sz));
|
j = snd2dx->voice_emplace(snd_2dx::assertsz_asf(voicesrc->sz));
|
||||||
snd_2dx::bank::voice_hdr *voicedst = snd2dx->voice(j);
|
snd_2dx::bank::voice_hdr *voicedst = snd2dx->voice(j);
|
||||||
auto *voicedst_wave = voicedst->wave();
|
BYTE *voicedst_wave = voicedst->wave();
|
||||||
mf_broken::asf_to_wav(voicesrc->asf(), voicesrc->sz, &voicedst_wave, &voicedst->sz);
|
//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
|
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)
|
// Valid 2DX (trim is not necessary)
|
||||||
log_info("hooks:bmsb", "25073::Bank[2DX]::dump()");
|
log_info("hooks:bmsb", "25073::Bank[2DX]::dump()");
|
||||||
snd_2dx::bank *buf2dx = snd2dx->serialize(false);
|
snd_2dx::bank *buf2dx = snd2dx->serialize(false);
|
||||||
dump("mf_25073.2dx", reinterpret_cast<const char *>(buf2dx), buf2dx->sz());
|
dump("ac_25073.2dx", reinterpret_cast<const uint8_t *>(buf2dx), buf2dx->sz());
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log_info("hooks:bmsb", "Tests failed");
|
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..");
|
log_warning("hooks::bmsb", "MF initialization failed, skipping..");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests _REM: tests
|
if (!avcodec::init())
|
||||||
run_tests();
|
{
|
||||||
return;
|
log_warning("hooks::bmsb", "AVCodec initialization failed, skipping..");
|
||||||
|
}
|
||||||
|
|
||||||
// Override .text
|
// Override .text
|
||||||
offset_.TX_BmsbEnumValidSoundbanks = (intptr_t) hmodule + 0xafd780; //_TODO: Hardcoded for iidx30 for now, requires asm pattern lookup
|
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<void *>(offset_.TX_BmsbEnumValidSoundbanks), reinterpret_cast<void *>(on_enum_valid_soundbanks), reinterpret_cast<void **>(&BmsbEnumValidSoundbanks));
|
detour::trampoline(reinterpret_cast<void *>(offset_.TX_BmsbEnumValidSoundbanks), reinterpret_cast<void *>(on_enum_valid_soundbanks), reinterpret_cast<void **>(&BmsbEnumValidSoundbanks));
|
||||||
|
|
||||||
log_info("hooks::bmsb", "Soundbank preprocessor ready");
|
log_info("hooks::bmsb", "Soundbank preprocessor ready");
|
||||||
|
|
||||||
|
// Tests _REM: tests
|
||||||
|
detour::trampoline(reinterpret_cast<void *>(avs::core::avs_fs_lstat), reinterpret_cast<void *>(on_avs_fs_lstat), reinterpret_cast<void **>(&avs_fs_lstat_));
|
||||||
|
detour::trampoline(reinterpret_cast<void *>(avs::core::avs_fs_mount), reinterpret_cast<void *>(on_avs_fs_mount), reinterpret_cast<void **>(&avs_fs_mount_));
|
||||||
|
run_tests();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinit(HINSTANCE hmodule)
|
void deinit(HINSTANCE hmodule)
|
||||||
|
|||||||
Reference in New Issue
Block a user