mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-17 19:19:16 +01:00
Merge pull request #954 from bnnm/riff-acb
- txtp_maker: extra subsong/renaming options - Fix some Ogg .wav [Kirara Kirara NTR (PC), Only One 2 (PC)] - Fix some .acb issues - adx: play even if key not found - misc fixes
This commit is contained in:
commit
0d390f182d
@ -32,6 +32,8 @@ class Cli(object):
|
||||
" - make .txtp for in .bnk including only subsong names that start with 'bgm'\n\n"
|
||||
" %(prog)s *.fsb -n \"{fn}<__{ss}>< [{in}]>\" -z 4 -o\n"
|
||||
" - make .txtp for all fsb, adding subsongs and stream name if they exist\n\n"
|
||||
" %(prog)s bgm.awb -or -os \"[alt%%%%i]\"\n"
|
||||
" - make .txtp and rename repeated names with a custom suffix per repeat 'i'\n\n"
|
||||
)
|
||||
|
||||
p = argparse.ArgumentParser(description=description, epilog=epilog, formatter_class=argparse.RawTextHelpFormatter)
|
||||
@ -48,6 +50,8 @@ class Cli(object):
|
||||
p.add_argument('-z', dest='zero_fill', help="Zero-fill subsong number (default: auto per subsongs)", type=int)
|
||||
p.add_argument('-ie', dest='no_internal_ext', help="Remove internal name's extension if any", action='store_true')
|
||||
p.add_argument('-m', dest='mini_txtp', help="Create mini-txtp", action='store_true')
|
||||
p.add_argument('-s', dest='subsong_start', help="Start subsong", type=int)
|
||||
p.add_argument('-S', dest='subsong_end', help="End subsong", type=int)
|
||||
p.add_argument('-o', dest='overwrite', help="Overwrite existing .txtp\n(beware when using with internal names alone)", action='store_true')
|
||||
p.add_argument('-oi', dest='overwrite_ignore', help="Ignore repeated rather than overwritting.txtp\n", action='store_true')
|
||||
p.add_argument('-or', dest='overwrite_rename', help="Rename rather than overwriting", action='store_true')
|
||||
@ -264,8 +268,22 @@ class TxtpMaker(object):
|
||||
exists = os.path.exists(outname)
|
||||
if exists and (cfg.overwrite_rename or cfg.overwrite_suffix):
|
||||
must_rename = True
|
||||
if cfg.overwrite_suffix:
|
||||
outname = outname.replace(".txtp", "%s.txtp" % (cfg.overwrite_suffix))
|
||||
|
||||
suffix = cfg.overwrite_suffix
|
||||
if suffix:
|
||||
outname = outname.replace(".txtp", "%s.txtp" % (suffix))
|
||||
if '%' in suffix:
|
||||
if outname in self.rename_map:
|
||||
rename_count = self.rename_map[outname]
|
||||
else:
|
||||
rename_count = 0
|
||||
self.rename_map[outname] = rename_count + 1
|
||||
if rename_count:
|
||||
outname = outname % (rename_count)
|
||||
else:
|
||||
#outname = outname.replace("%i", "")
|
||||
outname = re.sub(r"\%[0-9]*i", "", outname)
|
||||
|
||||
must_rename = os.path.exists(outname)
|
||||
|
||||
if must_rename:
|
||||
@ -459,8 +477,10 @@ class App(object):
|
||||
|
||||
files = []
|
||||
for root, dirnames, filenames in os.walk(dir):
|
||||
for filename in fnmatch.filter(filenames, pattern):
|
||||
files.append(os.path.join(root, filename))
|
||||
# manually test name as is too, as filter wouldn't handle stuff like "bgm[us].wav"
|
||||
for filename in filenames:
|
||||
if filename == pattern or fnmatch.fnmatch(filename, pattern):
|
||||
files.append(os.path.join(root, filename))
|
||||
|
||||
if not self.cfg.recursive:
|
||||
break
|
||||
@ -496,7 +516,13 @@ class App(object):
|
||||
created = 0
|
||||
dupes = 0
|
||||
errors = 0
|
||||
target_subsong = 1
|
||||
|
||||
subsong_start = self.cfg.subsong_start
|
||||
subsong_end = self.cfg.subsong_end
|
||||
if not subsong_start:
|
||||
subsong_start = 1
|
||||
target_subsong = subsong_start
|
||||
|
||||
while True:
|
||||
try:
|
||||
# main call to vgmstream
|
||||
@ -512,7 +538,7 @@ class App(object):
|
||||
errors += 1
|
||||
break
|
||||
|
||||
if target_subsong == 1:
|
||||
if target_subsong == subsong_start:
|
||||
log.debug("processing %s...", filename_in_clean)
|
||||
|
||||
if not maker.is_ignorable():
|
||||
@ -526,6 +552,8 @@ class App(object):
|
||||
|
||||
if not maker.has_more_subsongs(target_subsong):
|
||||
break
|
||||
if target_subsong >= subsong_end:
|
||||
break
|
||||
target_subsong += 1
|
||||
|
||||
if target_subsong % 200 == 0:
|
||||
|
@ -445,6 +445,8 @@ Adds support for multiple codecs: ATRAC3 (`.at3`), ATRAC3plus (`.at3`), XMA1/2 (
|
||||
|
||||
vgmstream's FFmpeg builds for Windows remove many unnecessary parts of FFmpeg to trim down its gigantic size, and are also built with the "vgmstream-" prefix to avoid clashing with other plugins. Current options can be seen in `ffmpeg_options.txt`. Linux usually links to the system's FFmpeg without issues.
|
||||
|
||||
Note that the options above use *libopus*, but you can use FFmpeg's *Opus* by removing `--enable-libopus` and changing `--enable-decoder`'s `libopus` to `opus`. libopus is preferable since FFmpeg's Opus decoding is buggy in some files.
|
||||
|
||||
For GCC simply use autotools (configure, make, make install), passing to `configure` the above options.
|
||||
|
||||
For MSCV it can be done through a helper: https://github.com/jb-alvarado/media-autobuild_suite
|
||||
|
@ -28,7 +28,7 @@ Also check the [examples](#examples) section for some quick recipes, of varying
|
||||
|
||||
|
||||
## Issues
|
||||
The `.txth` may be rejected if incorrect commands are found. Since at the moment vgmstream can't communicate why a file is rejected, try starting with a simple case (see examples) then add more complex commands until it fully works.
|
||||
The `.txth` may be rejected if incorrect commands are found. Errors are shown in the console log (see *USAGE* guide), better try starting with a simple case (see examples) then add more complex commands until it fully works.
|
||||
|
||||
Extension must be accepted/added to vgmstream (plugins like foobar2000 only load extensions from an accepted list in `formats.c`), or one could rename to any supported extension (like `.vgmstream`), or leave the file extensionless. Before renaming consider reporting the unknown extension so it can be added to the list (so similar games benefit, as long as the extension is a good fit). Some plugins allow playing unknown extensions too.
|
||||
|
||||
@ -221,7 +221,7 @@ interleave_first_skip = (value)
|
||||
#### ID VALUES
|
||||
Validates that `id_value` (normally set as constant value) matches value read at `id_check`. The file will be rejected and won't play if values don't match.
|
||||
|
||||
Can be redefined several times, it's checked whenever a new id_offset is found. `id_offset` can be used as an alt for `id_check`
|
||||
Can be redefined several times, it's checked whenever a new id_check is found. `id_offset` can be used as an alt for `id_check`
|
||||
```
|
||||
id_value = (value)
|
||||
id_check = (value)
|
||||
|
20
doc/USAGE.md
20
doc/USAGE.md
@ -213,15 +213,29 @@ extensions as a way to force them into vgmstream without renaming.
|
||||
#### Related issues
|
||||
Also be aware that other plugins (not vgmstream) can tell the player they handle
|
||||
some extension, then not actually play it. This makes the file unplayable as
|
||||
vgmstream doesn't even get the chance to parse that file, so you may need to
|
||||
disable the offending plugin or rename the file (for example this may happen with
|
||||
`.asf` in foobar2000/Winamp, may be fixed in newer versions).
|
||||
vgmstream doesn't even get the chance to parse it, so you may need to disable
|
||||
the offending plugin or rename the file to the fake extension shown above (for
|
||||
example this may happen with `.asf` in foobar2000/Winamp, may be fixed in newer
|
||||
versions).
|
||||
|
||||
When extracting from a bigfile, sometimes internal files don't have a proper
|
||||
extension. Those should be renamed to its correct one when possible, as the
|
||||
extractor program may guess wrong (like `.wav` instead of `.at3` or `.wem`).
|
||||
If there is no known extension, usually the header id/magic string may be used instead.
|
||||
|
||||
#### Windows 10 folder bugs
|
||||
Windows 10's *Web Media Extensions* is a pre-installed package seems to read metadata
|
||||
from files like `.ogg`, `.opus`, `.flac` and so on when opening a folder. However
|
||||
it tends to noticeably slow down opening folders, also seems to crash and leave files
|
||||
unusable when reading unsupported formats like Switch Opus (rather than Ogg Opus).
|
||||
|
||||
Renaming extensions should prevent those issues, or just uninstall those *Web
|
||||
Media Extension* better experience anyway.
|
||||
|
||||
#### Fallout SFX .ACM
|
||||
Due to technical limitations, to play Fallout 1 SFX you need to rename them from
|
||||
`.acm` to `.wavc` (forces mono).
|
||||
|
||||
### Demuxed videos
|
||||
vgmstream also supports audio from videos, but usually must be demuxed (extracted
|
||||
without modification) first, since vgmstream doesn't attempt to support most of them
|
||||
|
632
src/meta/acb.c
632
src/meta/acb.c
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@
|
||||
#define ADX_KEY_MAX_TEST_FRAMES 32768
|
||||
#define ADX_KEY_TEST_BUFFER_SIZE 0x8000
|
||||
|
||||
static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add, uint16_t subkey);
|
||||
static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t* xor_start, uint16_t* xor_mult, uint16_t* xor_add, uint16_t subkey);
|
||||
|
||||
VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf) {
|
||||
return init_vgmstream_adx_subkey(sf, 0);
|
||||
@ -34,14 +34,14 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
|
||||
|
||||
|
||||
/* checks*/
|
||||
if (read_u16be(0x00,sf) != 0x8000)
|
||||
goto fail;
|
||||
|
||||
/* .adx: standard
|
||||
* .adp: Headhunter (DC) */
|
||||
if (!check_extensions(sf,"adx,adp"))
|
||||
goto fail;
|
||||
|
||||
if (read_u16be(0x00,sf) != 0x8000)
|
||||
goto fail;
|
||||
|
||||
start_offset = read_u16be(0x02,sf) + 0x04;
|
||||
if (read_u16be(start_offset - 0x06,sf) != 0x2863 || /* "(c" */
|
||||
read_u32be(start_offset - 0x04,sf) != 0x29435249) /* ")CRI" */
|
||||
@ -79,18 +79,19 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
|
||||
|
||||
/* encryption */
|
||||
if (version == 0x0408) {
|
||||
|
||||
if (find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) {
|
||||
coding_type = coding_CRI_ADX_enc_8;
|
||||
version = 0x0400;
|
||||
vgm_logi("ADX: decryption keystring not found\n");
|
||||
}
|
||||
vgm_asserti(version != 0x0400, "ADX: decryption keystring not found\n");
|
||||
coding_type = coding_CRI_ADX_enc_8;
|
||||
version = 0x0400;
|
||||
}
|
||||
else if (version == 0x0409) {
|
||||
if (find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) {
|
||||
coding_type = coding_CRI_ADX_enc_9;
|
||||
version = 0x0400;
|
||||
vgm_logi("ADX: decryption keycode not found\n");
|
||||
}
|
||||
vgm_asserti(version != 0x0400, "ADX: decryption keycode not found\n");
|
||||
coding_type = coding_CRI_ADX_enc_9;
|
||||
version = 0x0400;
|
||||
}
|
||||
|
||||
/* version + extra data */
|
||||
|
@ -87,7 +87,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
VGMSTREAM* (*init_vgmstream_subkey)(STREAMFILE* sf, uint16_t subkey) = NULL;
|
||||
const char* extension = NULL;
|
||||
|
||||
if (read_u16be(subfile_offset, sf) == 0x8000) { /* (type 0=ADX) */
|
||||
if (read_u16be(subfile_offset, sf) == 0x8000) { /* (type 0=ADX, also 3?) */
|
||||
init_vgmstream_subkey = init_vgmstream_adx_subkey; /* Okami HD (PS4) */
|
||||
extension = "adx";
|
||||
}
|
||||
@ -99,7 +99,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
init_vgmstream = init_vgmstream_vag; /* Ukiyo no Roushi (Vita) */
|
||||
extension = "vag";
|
||||
}
|
||||
else if (is_id32be(subfile_offset,sf, "RIFF")) { /* (type 8=ATRAC3, 11=ATRAC9) */
|
||||
else if (is_id32be(subfile_offset,sf, "RIFF")) { /* (type 8=ATRAC3, 11=ATRAC9, also 18=ATRAC9?) */
|
||||
init_vgmstream = init_vgmstream_riff; /* Ukiyo no Roushi (Vita) */
|
||||
extension = "wav";
|
||||
subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x08; /* padded size, use RIFF's */
|
||||
@ -111,7 +111,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
else if (read_u32be(subfile_offset + 0x08,sf) >= 8000 && read_u32be(subfile_offset + 0x08,sf) <= 48000 &&
|
||||
read_u16be(subfile_offset + 0x0e,sf) == 0 &&
|
||||
read_u32be(subfile_offset + 0x18,sf) == 2 &&
|
||||
read_u32be(subfile_offset + 0x50,sf) == 0) { /* (type 13=DSP), probably should call some check function */
|
||||
read_u32be(subfile_offset + 0x50,sf) == 0) { /* (type 13=DSP, also 4=Wii?, 5=NDS?), probably should call some check function */
|
||||
init_vgmstream = init_vgmstream_ngc_dsp_std; /* Sonic: Lost World (WiiU) */
|
||||
extension = "dsp";
|
||||
}
|
||||
@ -125,7 +125,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
extension = "m4a";
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
else { /* 12=XMA? */
|
||||
vgm_logi("AWB: unknown codec (report)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
@ -2,34 +2,31 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* FWAV - Nintendo streams */
|
||||
VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_bfwav(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
off_t info_offset, data_offset;
|
||||
int channel_count, loop_flag, codec;
|
||||
int channels, loop_flag, codec, sample_rate;
|
||||
int big_endian;
|
||||
size_t interleave = 0;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
int nsmbu_flag = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "FWAV"))
|
||||
goto fail;
|
||||
|
||||
/* .bfwavnsmbu: fake extension to detect New Super Mario Bros U files with weird sample rate */
|
||||
if (!check_extensions(streamFile, "bfwav,fwav,bfwavnsmbu"))
|
||||
if (!check_extensions(sf, "bfwav,fwav,bfwavnsmbu"))
|
||||
goto fail;
|
||||
nsmbu_flag = check_extensions(streamFile, "bfwavnsmbu");
|
||||
|
||||
/* FWAV header */
|
||||
if (read_32bitBE(0x00, streamFile) != 0x46574156) /* "FWAV" */
|
||||
goto fail;
|
||||
/* 0x06(2): header size (0x40), 0x08: version (0x00010200), 0x0c: file size 0x10(2): sections (2) */
|
||||
|
||||
if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFEFF) { /* BE BOM check */
|
||||
/* BOM check */
|
||||
if (read_u16be(0x04, sf) == 0xFEFF) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
big_endian = 1;
|
||||
} else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* LE BOM check */
|
||||
} else if (read_u16be(0x04, sf) == 0xFFFE) {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
big_endian = 0;
|
||||
@ -37,60 +34,74 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
info_offset = read_32bit(0x18, streamFile); /* 0x14(2): info mark (0x7000), 0x1c: info size */
|
||||
data_offset = read_32bit(0x24, streamFile); /* 0x20(2): data mark (0x7001), 0x28: data size */
|
||||
/* FWAV header */
|
||||
/* 0x06(2): header size (0x40) */
|
||||
/* 0x08: version (0x00010200) */
|
||||
/* 0x0c: file size */
|
||||
/* 0x10(2): sections (2) */
|
||||
|
||||
/* 0x14(2): info mark (0x7000) */
|
||||
info_offset = read_32bit(0x18, sf);
|
||||
/* 0x1c: info size */
|
||||
|
||||
/* 0x20(2): data mark (0x7001) */
|
||||
data_offset = read_32bit(0x24, sf);
|
||||
/* 0x28: data size */
|
||||
|
||||
/* INFO section */
|
||||
if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
|
||||
if (!is_id32be(info_offset, sf, "INFO"))
|
||||
goto fail;
|
||||
codec = read_8bit(info_offset + 0x08, streamFile);
|
||||
loop_flag = read_8bit(info_offset + 0x09, streamFile);
|
||||
channel_count = read_32bit(info_offset + 0x1C, streamFile);
|
||||
codec = read_u8(info_offset + 0x08, sf);
|
||||
loop_flag = read_u8(info_offset + 0x09, sf);
|
||||
sample_rate = read_32bit(info_offset + 0x0C, sf);
|
||||
channels = read_32bit(info_offset + 0x1C, sf);
|
||||
|
||||
//TODO remove
|
||||
if (check_extensions(sf, "bfwavnsmbu"))
|
||||
sample_rate = 16000;
|
||||
|
||||
/* parse channel table */
|
||||
{
|
||||
off_t channel1_info, data_start;
|
||||
int i;
|
||||
|
||||
channel1_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*0+0x04, streamFile);
|
||||
data_start = read_32bit(channel1_info+0x04, streamFile); /* within "DATA" after 0x08 */
|
||||
channel1_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*0+0x04, sf);
|
||||
data_start = read_32bit(channel1_info+0x04, sf); /* within "DATA" after 0x08 */
|
||||
|
||||
/* channels use absolute offsets but should be ok as interleave */
|
||||
interleave = 0;
|
||||
if (channel_count > 1) {
|
||||
off_t channel2_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*1+0x04, streamFile);
|
||||
interleave = read_32bit(channel2_info+0x04, streamFile) - data_start;
|
||||
if (channels > 1) {
|
||||
off_t channel2_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*1+0x04, sf);
|
||||
interleave = read_32bit(channel2_info+0x04, sf) - data_start;
|
||||
}
|
||||
|
||||
start_offset = data_offset + 0x08 + data_start;
|
||||
|
||||
/* validate all channels just in case of multichannel with non-constant interleave */
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
/* channel table, 0x00: flag (0x7100), 0x04: channel info offset */
|
||||
off_t channel_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*i+0x04, streamFile);
|
||||
off_t channel_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*i+0x04, sf);
|
||||
/* channel info, 0x00(2): flag (0x1f00), 0x04: offset, 0x08(2): ADPCM flag (0x0300), 0x0c: ADPCM offset */
|
||||
if ((uint16_t)read_16bit(channel_info+0x00, streamFile) != 0x1F00)
|
||||
if ((uint16_t)read_16bit(channel_info+0x00, sf) != 0x1F00)
|
||||
goto fail;
|
||||
if (read_32bit(channel_info+0x04, streamFile) != data_start + interleave*i)
|
||||
if (read_32bit(channel_info+0x04, sf) != data_start + interleave*i)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bit(info_offset + 0x0C, streamFile);
|
||||
if (nsmbu_flag)
|
||||
vgmstream->sample_rate = 16000;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->num_samples = read_32bit(info_offset + 0x14, streamFile);
|
||||
vgmstream->loop_start_sample = read_32bit(info_offset + 0x10, streamFile);
|
||||
vgmstream->num_samples = read_32bit(info_offset + 0x14, sf);
|
||||
vgmstream->loop_start_sample = read_32bit(info_offset + 0x10, sf);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->meta_type = meta_FWAV;
|
||||
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
|
||||
vgmstream->layout_type = (channels == 1) ? layout_none : layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
switch (codec) {
|
||||
@ -110,9 +121,9 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) {
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
for (c = 0; c < 16; c++) {
|
||||
coef_header = info_offset + 0x1C + read_32bit(info_offset + 0x24 + (i*0x08), streamFile);
|
||||
coef_offset = read_32bit(coef_header + 0x0c, streamFile) + coef_header;
|
||||
vgmstream->ch[i].adpcm_coef[c] = read_16bit(coef_offset + c*2, streamFile);
|
||||
coef_header = info_offset + 0x1C + read_32bit(info_offset + 0x24 + (i*0x08), sf);
|
||||
coef_offset = read_32bit(coef_header + 0x0c, sf) + coef_header;
|
||||
vgmstream->ch[i].adpcm_coef[c] = read_16bit(coef_offset + c*2, sf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,7 +134,7 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -191,10 +191,21 @@ done:
|
||||
}
|
||||
|
||||
#ifdef HCA_BRUTEFORCE
|
||||
typedef enum {
|
||||
HBF_TYPE_64LE_1,
|
||||
HBF_TYPE_64BE_1,
|
||||
HBF_TYPE_32LE_1,
|
||||
HBF_TYPE_32BE_1,
|
||||
HBF_TYPE_64LE_4,
|
||||
HBF_TYPE_64BE_4,
|
||||
HBF_TYPE_32LE_4,
|
||||
HBF_TYPE_32BE_4,
|
||||
} HBF_type_t;
|
||||
|
||||
/* Bruteforce binary keys in executables and similar files, mainly for some mobile games.
|
||||
* Kinda slow but acceptable for ~20MB exes, not very optimized. Unity usually has keys
|
||||
* in plaintext (inside levelX or other base files) instead though. */
|
||||
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey, HBF_type_t type) {
|
||||
STREAMFILE* sf_keys = NULL;
|
||||
uint8_t* buf = NULL;
|
||||
int best_score = 0xFFFFFF, cur_score;
|
||||
@ -203,7 +214,7 @@ static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, uns
|
||||
uint64_t old_key = 0;
|
||||
|
||||
|
||||
VGM_LOG("HCA: test keys.bin\n");
|
||||
VGM_LOG("HCA: test keys.bin (type %i)\n", type);
|
||||
|
||||
*p_keycode = 0;
|
||||
|
||||
@ -226,17 +237,18 @@ static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, uns
|
||||
uint64_t key;
|
||||
VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos);
|
||||
|
||||
/* keys are usually u32le lower, u32le upper (u64le) but other orders may exist */
|
||||
key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32le(buf + pos + 0x04) << 32);
|
||||
//key = ((uint64_t)get_u32le(buf + pos + 0x00) << 32) | ((uint64_t)get_u32le(buf + pos + 0x04) << 0);
|
||||
//key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32be(buf + pos + 0x04) << 32);
|
||||
//key = ((uint64_t)get_u32be(buf + pos + 0x00) << 32) | ((uint64_t)get_u32be(buf + pos + 0x04) << 0);
|
||||
//key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */
|
||||
//key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */
|
||||
|
||||
/* observed files have aligned keys, change if needed */
|
||||
pos += 0x04;
|
||||
//pos++;
|
||||
/* keys are usually u64le but other orders may exist */
|
||||
switch(type) {
|
||||
case HBF_TYPE_64LE_1: key = get_u64le(buf + pos); pos += 0x01; break;
|
||||
case HBF_TYPE_64BE_1: key = get_u64be(buf + pos); pos += 0x01; break;
|
||||
case HBF_TYPE_32LE_1: key = get_u32le(buf + pos); pos += 0x01; break;
|
||||
case HBF_TYPE_32BE_1: key = get_u32be(buf + pos); pos += 0x01; break;
|
||||
case HBF_TYPE_64LE_4: key = get_u64le(buf + pos); pos += 0x04; break;
|
||||
case HBF_TYPE_64BE_4: key = get_u64be(buf + pos); pos += 0x04; break;
|
||||
case HBF_TYPE_32LE_4: key = get_u32le(buf + pos); pos += 0x04; break;
|
||||
case HBF_TYPE_32BE_4: key = get_u32be(buf + pos); pos += 0x04; break;
|
||||
default: key = 0; pos = keys_size; break;
|
||||
}
|
||||
|
||||
if (key == 0 || key == old_key)
|
||||
continue;
|
||||
@ -266,6 +278,18 @@ done:
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1);
|
||||
}
|
||||
|
||||
|
||||
#include <inttypes.h>
|
||||
//#include <stdio.h>
|
||||
|
||||
@ -303,14 +327,13 @@ static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, uns
|
||||
uint64_t key = 0;
|
||||
|
||||
bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok);
|
||||
if (!line_ok) continue; //???
|
||||
|
||||
pos += bytes_read;
|
||||
if (!line_ok) continue; /* line too long */
|
||||
|
||||
count = sscanf(line, "%" SCNd64, &key);
|
||||
if (count != 1) continue;
|
||||
|
||||
VGM_ASSERT(pos % 100000 == 0, "HCA: count %i...\n", i);
|
||||
VGM_ASSERT(pos % 10000 == 0, "HCA: count %i...\n", i);
|
||||
|
||||
if (key == 0)
|
||||
continue;
|
||||
|
@ -2,13 +2,6 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static int is_id4(const char* test, off_t offset, STREAMFILE* sf) {
|
||||
uint8_t buf[4];
|
||||
if (read_streamfile(buf, offset, sizeof(buf), sf) != sizeof(buf))
|
||||
return 0;
|
||||
return memcmp(buf, test, sizeof(buf)) == 0; /* memcmp to allow "AB\0\0" */
|
||||
}
|
||||
|
||||
/* LucasArts iMUSE (Interactive Music Streaming Engine) formats */
|
||||
VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
@ -19,19 +12,13 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .imx: The Curse of Monkey Island (PC)
|
||||
* .imc: Grim Fandango (multi)
|
||||
* .wav: Grim Fandango (multi) RIFF sfx */
|
||||
if (!check_extensions(sf, "imx,imc,wav,lwav"))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* base decoder block table */
|
||||
if (is_id4("COMP", 0x00, sf)) { /* The Curse of Monkey Island (PC), The Dig (PC) */
|
||||
if (is_id32be(0x00, sf, "COMP")) { /* The Curse of Monkey Island (PC), The Dig (PC) */
|
||||
int entries = read_u32be(0x04,sf);
|
||||
head_offset = 0x10 + entries * 0x10 + 0x02; /* base header + table + header size */
|
||||
}
|
||||
else if (is_id4("MCMP", 0x00, sf)) { /* Grim Fandango (multi), Star Wars: X-Wing Alliance (PC) */
|
||||
else if (is_id32be(0x00, sf, "MCMP")) { /* Grim Fandango (multi), Star Wars: X-Wing Alliance (PC) */
|
||||
int entries = read_u16be(0x04,sf);
|
||||
head_offset = 0x06 + entries * 0x09; /* base header + table */
|
||||
head_offset += 0x02 + read_u16be(head_offset, sf); /* + mini text header */
|
||||
@ -40,17 +27,23 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* .imx: The Curse of Monkey Island (PC)
|
||||
* .imc: Grim Fandango (multi)
|
||||
* .wav: Grim Fandango (multi) RIFF sfx */
|
||||
if (!check_extensions(sf, "imx,imc,wav,lwav"))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* "offsets" below seem to count decoded data. Data is divided into variable-sized blocks that usually
|
||||
* return 0x2000 bytes (starting from and including header). File starts with a block table to make
|
||||
* this manageable. Most offsets don't seem to match block or data boundaries so not really sure. */
|
||||
|
||||
/* main header after table */
|
||||
if (is_id4("iMUS", head_offset, sf)) { /* COMP/MCMP */
|
||||
if (is_id32be(head_offset, sf, "iMUS")) { /* COMP/MCMP */
|
||||
int header_found = 0;
|
||||
|
||||
/* 0x04: decompressed size (header size + pcm bytes) */
|
||||
if (!is_id4("MAP ", head_offset + 0x08, sf))
|
||||
if (!is_id32be(head_offset + 0x08, sf, "MAP "))
|
||||
goto fail;
|
||||
map_size = read_u32be(head_offset + 0x0c, sf);
|
||||
map_offset = head_offset + 0x10;
|
||||
@ -105,12 +98,12 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
|
||||
if (!header_found)
|
||||
goto fail;
|
||||
|
||||
if (!is_id4("DATA", head_offset + 0x10 + map_size + 0x00, sf))
|
||||
if (!is_id32be(head_offset + 0x10 + map_size + 0x00, sf, "DATA"))
|
||||
goto fail;
|
||||
data_bytes = read_u32be(head_offset + 0x10 + map_size + 0x04, sf);
|
||||
num_samples = data_bytes / channels / sizeof(int16_t);
|
||||
}
|
||||
else if (is_id4("RIFF", head_offset, sf)) { /* MCMP voices */
|
||||
else if (is_id32be(head_offset, sf, "RIFF")) { /* MCMP voices */
|
||||
/* standard (LE), with fake codec 1 and sizes also in decoded bytes (see above),
|
||||
* has standard RIFF chunks (may include extra), start offset in MCSC */
|
||||
|
||||
@ -124,7 +117,8 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
|
||||
num_samples = data_bytes / channels / sizeof(int16_t);
|
||||
}
|
||||
else {
|
||||
goto fail; /* The Dig (PC) has no header, detect? */
|
||||
vgm_logi("IMUSE: unsupported format\n");
|
||||
goto fail; /* The Dig (PC) has no header, detect? (needs a bunch of sub-codecs) */
|
||||
}
|
||||
|
||||
loop_flag = 0;
|
||||
|
@ -2,41 +2,43 @@
|
||||
#include "../util.h"
|
||||
|
||||
/* STRM - common Nintendo NDS streaming format */
|
||||
VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_nds_strm(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int channel_count, loop_flag, codec;
|
||||
int channels, loop_flag, codec, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "strm"))
|
||||
if (!is_id32be(0x00,sf, "STRM"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04,streamFile) != 0xFFFE0001 && /* Old Header Check */
|
||||
(read_32bitBE(0x04,streamFile) != 0xFEFF0001)) /* Some newer games have a new flag */
|
||||
if (!check_extensions(sf, "strm"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x10,streamFile) != 0x48454144 && /* "HEAD" */
|
||||
read_32bitLE(0x14,streamFile) != 0x50) /* 0x50-sized head is all I've seen */
|
||||
/* BOM check? */
|
||||
if (read_u32be(0x04,sf) != 0xFFFE0001 &&
|
||||
read_u32be(0x04,sf) != 0xFEFF0001) /* newer games? */
|
||||
goto fail;
|
||||
|
||||
codec = read_8bit(0x18,streamFile);
|
||||
loop_flag = read_8bit(0x19,streamFile);
|
||||
channel_count = read_8bit(0x1a,streamFile);
|
||||
if (channel_count > 2) goto fail;
|
||||
if (!is_id32be(0x10,sf, "HEAD") &&
|
||||
read_u32le(0x14,sf) != 0x50)
|
||||
goto fail;
|
||||
|
||||
start_offset = read_32bitLE(0x28,streamFile);
|
||||
codec = read_u8(0x18,sf);
|
||||
loop_flag = read_u8(0x19,sf);
|
||||
sample_rate = read_u16le(0x1c,sf);
|
||||
channels = read_u8(0x1a,sf);
|
||||
if (channels > 2) goto fail;
|
||||
|
||||
start_offset = read_u32le(0x28,sf);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = (uint16_t)read_16bitLE(0x1c,streamFile);
|
||||
vgmstream->num_samples = read_32bitLE(0x24,streamFile);
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile);
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = read_32bitLE(0x24,sf);
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x20,sf);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->meta_type = meta_STRM;
|
||||
@ -55,11 +57,11 @@ VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x30,streamFile);
|
||||
vgmstream->interleave_last_block_size = read_32bitLE(0x38,streamFile);
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x30,sf);
|
||||
vgmstream->interleave_last_block_size = read_32bitLE(0x38,sf);
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -215,7 +215,13 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x6771: /* Ogg Vorbis (mode 3+) */
|
||||
//case 0x674f: /* Ogg Vorbis (mode 1) */
|
||||
//case 0x6750: /* Ogg Vorbis (mode 2) */
|
||||
//case 0x6751: /* Ogg Vorbis (mode 3) */
|
||||
case 0x676f: /* Ogg Vorbis (mode 1+) [Only One 2 (PC)] */
|
||||
//case 0x6770: /* Ogg Vorbis (mode 2+) */
|
||||
case 0x6771: /* Ogg Vorbis (mode 3+) [Liar-soft games] */
|
||||
/* vorbis.acm codecs (official-ish, "+" = CBR-style modes?) */
|
||||
fmt->coding_type = coding_OGG_VORBIS;
|
||||
break;
|
||||
#endif
|
||||
@ -233,7 +239,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
|
||||
(read_u16 (offset+0x26,sf));
|
||||
uint32_t guid3 = read_u32be(offset+0x28,sf);
|
||||
uint32_t guid4 = read_u32be(offset+0x2c,sf);
|
||||
//;VGM_LOG("RIFF: guid %08x %08x %08x %08x\n", guid1, guid2, guid3, guid4);
|
||||
//;VGM_LOG("riff: guid %08x %08x %08x %08x\n", guid1, guid2, guid3, guid4);
|
||||
|
||||
/* PCM GUID (0x00000001,0000,0010,80,00,00,AA,00,38,9B,71) */
|
||||
if (guid1 == 0x00000001 && guid2 == 0x00000010 && guid3 == 0x800000AA && guid4 == 0x00389B71) {
|
||||
@ -280,7 +286,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
|
||||
|
||||
default:
|
||||
/* FFmpeg may play it */
|
||||
//vgm_logi("WWISE: unknown codec 0x%04x (report)\n", fmt->format);
|
||||
//vgm_logi("RIFF: unknown codec 0x%04x (report)\n", fmt->format);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -318,7 +324,10 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
||||
off_t mwv_ctrl_offset = -1;
|
||||
|
||||
|
||||
/* check extension */
|
||||
/* checks*/
|
||||
if (!is_id32be(0x00,sf,"RIFF"))
|
||||
goto fail;
|
||||
|
||||
/* .lwav: to avoid hijacking .wav
|
||||
* .xwav: fake for Xbox games (not needed anymore)
|
||||
* .da: The Great Battle VI (PS1)
|
||||
@ -355,21 +364,19 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* check header */
|
||||
if (!is_id32be(0x00,sf,"RIFF"))
|
||||
goto fail;
|
||||
riff_size = read_u32le(0x04,sf);
|
||||
|
||||
if (!is_id32be(0x08,sf, "WAVE"))
|
||||
goto fail;
|
||||
|
||||
riff_size = read_u32le(0x04,sf);
|
||||
file_size = get_streamfile_size(sf);
|
||||
|
||||
/* some games have wonky sizes, selectively fix to catch bad rips and new mutations */
|
||||
if (file_size != riff_size + 0x08) {
|
||||
uint16_t codec = read_u16le(0x14,sf);
|
||||
|
||||
if (codec == 0x6771 && riff_size + 0x08 + 0x01 == file_size)
|
||||
riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] (Sony Sound Forge?) */
|
||||
if ((codec & 0xFF00) == 0x6700 && riff_size + 0x08 + 0x01 == file_size)
|
||||
riff_size += 0x01; /* [Shikkoku no Sharnoth (PC), Only One 2 (PC)] (Sony Sound Forge?) */
|
||||
|
||||
else if (codec == 0x0069 && riff_size == file_size)
|
||||
riff_size -= 0x08; /* [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */
|
||||
@ -422,6 +429,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
||||
/* check for truncated RIFF */
|
||||
if (file_size != riff_size + 0x08) {
|
||||
vgm_logi("RIFF: wrong expected size (report/re-rip?)\n");
|
||||
VGM_LOG("riff: file_size = %x, riff_size+8 = %x\n", file_size, riff_size + 0x08); /* don't log to user */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -774,8 +782,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
||||
#endif
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case coding_OGG_VORBIS: {
|
||||
/* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */
|
||||
STREAMFILE *temp_sf = setup_riff_ogg_streamfile(sf, start_offset, data_size);
|
||||
/* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge/vorbis.acm [Shikkoku no Sharnoth (PC)],
|
||||
* and rarely other devs, not always buggy [Kirara Kirara NTR (PC), No One 2 (PC)] */
|
||||
STREAMFILE* temp_sf = setup_riff_ogg_streamfile(sf, start_offset, data_size);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_ogg_vorbis(temp_sf, 0x00, get_streamfile_size(temp_sf), NULL);
|
||||
@ -983,13 +992,13 @@ VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) {
|
||||
int FormatChunkFound = 0, DataChunkFound = 0;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "RIFX"))
|
||||
goto fail;
|
||||
|
||||
if (!check_extensions(sf, "wav,lwav"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if (!is_id32be(0x00,sf, "RIFX"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x08,sf, "WAVE"))
|
||||
goto fail;
|
||||
|
||||
|
@ -3,22 +3,22 @@
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
typedef struct {
|
||||
off_t patch_offset;
|
||||
uint32_t patch_offset;
|
||||
} riff_ogg_io_data;
|
||||
|
||||
static size_t riff_ogg_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, riff_ogg_io_data* data) {
|
||||
size_t bytes = read_streamfile(dest, offset, length, sf);
|
||||
static size_t riff_ogg_io_read(STREAMFILE* sf, uint8_t* dst, uint32_t offset, size_t length, riff_ogg_io_data* data) {
|
||||
size_t bytes = read_streamfile(dst, offset, length, sf);
|
||||
|
||||
/* has garbage init Oggs pages, patch bad flag */
|
||||
if (data->patch_offset && data->patch_offset >= offset && data->patch_offset < offset + bytes) {
|
||||
VGM_ASSERT(dest[data->patch_offset - offset] != 0x02, "RIFF Ogg: bad patch offset at %lx\n", data->patch_offset);
|
||||
dest[data->patch_offset - offset] = 0x00;
|
||||
VGM_ASSERT(dst[data->patch_offset - offset] != 0x02, "RIFF Ogg: bad patch offset at %x\n", data->patch_offset);
|
||||
dst[data->patch_offset - offset] = 0x00;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static size_t ogg_get_page(uint8_t *buf, size_t bufsize, off_t offset, STREAMFILE *sf) {
|
||||
static size_t ogg_get_page(uint8_t* buf, size_t bufsize, uint32_t offset, STREAMFILE* sf) {
|
||||
size_t segments, bytes, page_size;
|
||||
int i;
|
||||
|
||||
@ -43,8 +43,8 @@ fail:
|
||||
}
|
||||
|
||||
/* patches Ogg with weirdness */
|
||||
static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t size) {
|
||||
off_t patch_offset = 0;
|
||||
static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE* sf, uint32_t start, size_t size) {
|
||||
uint32_t patch_offset = 0;
|
||||
size_t real_size = size;
|
||||
uint8_t buf[0x1000];
|
||||
|
||||
@ -52,11 +52,11 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
|
||||
/* initial page flag is repeated and causes glitches in decoders, find bad offset */
|
||||
//todo callback could patch on-the-fly by analyzing all "OggS", but is problematic due to arbitrary offsets
|
||||
{
|
||||
off_t offset = start;
|
||||
uint32_t offset = start;
|
||||
size_t page_size;
|
||||
off_t offset_limit = start + size; /* usually in the first 0x3000 but can be +0x100000 */
|
||||
uint32_t offset_limit = start + size; /* usually in the first 0x3000 but can be +0x100000 */
|
||||
//todo this doesn't seem to help much
|
||||
STREAMFILE *temp_sf = reopen_streamfile(sf, 0x100); /* use small-ish sf to avoid reading the whole thing */
|
||||
STREAMFILE* temp_sf = reopen_streamfile(sf, 0x100); /* use small-ish sf to avoid reading the whole thing */
|
||||
|
||||
/* first page is ok */
|
||||
page_size = ogg_get_page(buf, sizeof(buf), offset, temp_sf);
|
||||
@ -66,7 +66,7 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
|
||||
page_size = ogg_get_page(buf, sizeof(buf), offset, temp_sf);
|
||||
if (page_size == 0) break;
|
||||
|
||||
if (get_u32be(buf + 0x00) != 0x4f676753) /* "OggS" */
|
||||
if (get_u32be(buf + 0x00) != get_id32be("OggS"))
|
||||
break;
|
||||
|
||||
if (get_u16be(buf + 0x04) == 0x0002) { /* start page flag */
|
||||
@ -80,8 +80,9 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
|
||||
if (patch_offset == 0)
|
||||
return NULL;
|
||||
/* no need to patch initial flag */
|
||||
//if (patch_offset == 0)
|
||||
// return NULL;
|
||||
}
|
||||
|
||||
/* has a bunch of padding(?) pages at the end with no data nor flag that confuse decoders, find actual end */
|
||||
@ -89,14 +90,14 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
|
||||
size_t chunk_size = sizeof(buf); /* not worth testing more */
|
||||
size_t max_size = size;
|
||||
size_t pos;
|
||||
off_t read_offset = start + size - chunk_size;
|
||||
uint32_t read_offset = start + size - chunk_size;
|
||||
|
||||
pos = read_streamfile(buf, read_offset, chunk_size, sf);
|
||||
if (read_offset < 0 || pos <= 0x1a) return NULL;
|
||||
|
||||
pos -= 0x1a; /* at least one OggS page */
|
||||
while (pos > 0) {
|
||||
if (get_u32be(buf + pos + 0x00) == 0x4f676753) { /* "OggS" */
|
||||
if (get_u32be(buf + pos + 0x00) == get_id32be("OggS")) {
|
||||
|
||||
if (get_u16be(buf + pos + 0x04) == 0x0004) { /* last page flag is ok */
|
||||
real_size = max_size;
|
||||
@ -112,7 +113,7 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
|
||||
|
||||
/* actual custom streamfile init */
|
||||
{
|
||||
STREAMFILE *new_sf = NULL;
|
||||
STREAMFILE* new_sf = NULL;
|
||||
riff_ogg_io_data io_data = {0};
|
||||
|
||||
io_data.patch_offset = patch_offset;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/chunks.h"
|
||||
|
||||
|
||||
/* Wwise uses a custom RIFF/RIFX header, non-standard enough that it's parsed it here.
|
||||
@ -14,6 +15,7 @@ typedef struct {
|
||||
int big_endian;
|
||||
size_t file_size;
|
||||
int truncated;
|
||||
int is_wem;
|
||||
|
||||
/* chunks references */
|
||||
off_t fmt_offset;
|
||||
@ -55,7 +57,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww);
|
||||
static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset);
|
||||
|
||||
|
||||
/* Wwise - Audiokinetic Wwise (Wave Works Interactive Sound Engine) middleware */
|
||||
/* Wwise - Audiokinetic Wwise (WaveWorks Interactive Sound Engine) middleware */
|
||||
VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
wwise_header ww = {0};
|
||||
@ -66,11 +68,16 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011)
|
||||
if (!is_id32be(0x00,sf, "RIFF") && /* LE */
|
||||
!is_id32be(0x00,sf, "RIFX")) /* BE */
|
||||
goto fail;
|
||||
|
||||
/* note that Wwise allows those extensions only, so custom engine exts shouldn't be added
|
||||
* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011)
|
||||
* .wav: older PCM/ADPCM files [Spider-Man: Web of Shadows (PC), Punch Out!! (Wii)]
|
||||
* .xma: older XMA files [Too Human (X360), Tron Evolution (X360)]
|
||||
* .ogg: older Vorbis files [The King of Fighters XII (X360)]
|
||||
* .bnk: Wwise banks for memory .wem detection */
|
||||
* .bnk: Wwise banks for memory .wem detection (hack) */
|
||||
if (!check_extensions(sf,"wem,wav,lwav,ogg,logg,xma,bnk"))
|
||||
goto fail;
|
||||
|
||||
@ -234,7 +241,6 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
else {
|
||||
/* newer Wwise (>2012) */
|
||||
off_t extra_offset = ww.fmt_offset + 0x18; /* after flag + channels */
|
||||
int is_wem = check_extensions(sf,"wem,bnk"); /* use extension as a guide for faster vorbis inits */
|
||||
|
||||
switch(ww.extra_size) {
|
||||
case 0x30:
|
||||
@ -246,7 +252,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
/* setup not detectable by header, so we'll try both; libvorbis should reject wrong codebooks
|
||||
* - standard: early (<2012), ex. The King of Fighters XIII (X360)-2011/11, .ogg (cbs are from aoTuV, too)
|
||||
* - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed (PC)-2012/11, .wem */
|
||||
cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||
cfg.setup_type = ww.is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -278,7 +284,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
vgmstream->codec_data = init_vorbis_custom(sf, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) {
|
||||
/* codebooks failed: try again with the other type */
|
||||
cfg.setup_type = is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS;
|
||||
cfg.setup_type = ww.is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS;
|
||||
vgmstream->codec_data = init_vorbis_custom(sf, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
@ -693,14 +699,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
if (read_u32be(0x00,sf) != 0x52494646 && /* "RIFF" (LE) */
|
||||
read_u32be(0x00,sf) != 0x52494658) /* "RIFX" (BE) */
|
||||
goto fail;
|
||||
if (read_u32be(0x08,sf) != 0x57415645 && /* "WAVE" */
|
||||
read_u32be(0x08,sf) != 0x58574D41) /* "XWMA" */
|
||||
goto fail;
|
||||
|
||||
ww->big_endian = read_u32be(0x00,sf) == 0x52494658; /* RIFX */
|
||||
ww->big_endian = is_id32be(0x00,sf, "RIFX");
|
||||
if (ww->big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
@ -727,50 +726,56 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!is_id32be(0x08,sf, "WAVE") &&
|
||||
!is_id32be(0x08,sf, "XWMA"))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* parse chunks (reads once linearly) */
|
||||
{
|
||||
off_t offset = 0x0c;
|
||||
while (offset < ww->file_size) {
|
||||
uint32_t type = read_u32be(offset + 0x00,sf);
|
||||
uint32_t size = read_u32 (offset + 0x04,sf);
|
||||
offset += 0x08;
|
||||
chunk_t rc = {0};
|
||||
|
||||
switch(type) {
|
||||
/* chunks are even-aligned and don't need to add padding byte, unlike real RIFFs */
|
||||
rc.be_size = ww->big_endian;
|
||||
rc.current = 0x0c;
|
||||
while (next_chunk(&rc, sf)) {
|
||||
|
||||
switch(rc.type) {
|
||||
case 0x666d7420: /* "fmt " */
|
||||
ww->fmt_offset = offset;
|
||||
ww->fmt_size = size;
|
||||
ww->fmt_offset = rc.offset;
|
||||
ww->fmt_size = rc.size;
|
||||
break;
|
||||
case 0x584D4132: /* "XMA2" */
|
||||
ww->xma2_offset = offset;
|
||||
ww->xma2_size = size;
|
||||
ww->xma2_offset = rc.offset;
|
||||
ww->xma2_size = rc.size;
|
||||
break;
|
||||
case 0x64617461: /* "data" */
|
||||
ww->data_offset = offset;
|
||||
ww->data_size = size;
|
||||
ww->data_offset = rc.offset;
|
||||
ww->data_size = rc.size;
|
||||
break;
|
||||
case 0x766F7262: /* "vorb" */
|
||||
ww->vorb_offset = offset;
|
||||
ww->vorb_size = size;
|
||||
ww->vorb_offset = rc.offset;
|
||||
ww->vorb_size = rc.size;
|
||||
break;
|
||||
case 0x57696948: /* "WiiH" */
|
||||
ww->wiih_offset = offset;
|
||||
ww->wiih_size = size;
|
||||
ww->wiih_offset = rc.offset;
|
||||
ww->wiih_size = rc.size;
|
||||
break;
|
||||
case 0x7365656B: /* "seek" */
|
||||
ww->seek_offset = offset;
|
||||
ww->seek_size = size;
|
||||
ww->seek_offset = rc.offset;
|
||||
ww->seek_size = rc.size;
|
||||
break;
|
||||
case 0x736D706C: /* "smpl" */
|
||||
ww->smpl_offset = offset;
|
||||
ww->smpl_size = size;
|
||||
ww->smpl_offset = rc.offset;
|
||||
ww->smpl_size = rc.size;
|
||||
break;
|
||||
case 0x6D657461: /* "meta" */
|
||||
ww->meta_offset = offset;
|
||||
ww->meta_size = size;
|
||||
ww->meta_offset = rc.offset;
|
||||
ww->meta_size = rc.size;
|
||||
break;
|
||||
|
||||
case 0x66616374: /* "fact" */
|
||||
/* Wwise shouldn't use fact, but if somehow some file does uncomment the following: */
|
||||
/* Wwise never uses fact, but if somehow some file does uncomment the following: */
|
||||
//if (size == 0x10 && read_u32be(offset + 0x04, sf) == 0x4C794E20) /* "LyN " */
|
||||
// goto fail; /* ignore LyN RIFF */
|
||||
goto fail;
|
||||
@ -783,12 +788,11 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* chunks are even-aligned and don't need to add padding byte, unlike real RIFFs */
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
/* use extension as a guide for certain cases */
|
||||
ww->is_wem = check_extensions(sf,"wem,bnk");
|
||||
|
||||
/* parse format (roughly spec-compliant but some massaging is needed) */
|
||||
if (ww->xma2_offset) {
|
||||
@ -806,7 +810,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
|
||||
ww->channels = read_u16(ww->fmt_offset + 0x02,sf);
|
||||
ww->sample_rate = read_u32(ww->fmt_offset + 0x04,sf);
|
||||
ww->avg_bitrate = read_u32(ww->fmt_offset + 0x08,sf);
|
||||
ww->block_size = read_u16(ww->fmt_offset + 0x0c,sf);
|
||||
ww->block_size = read_u16(ww->fmt_offset + 0x0c,sf);
|
||||
ww->bits_per_sample = read_u16(ww->fmt_offset + 0x0e,sf);
|
||||
if (ww->fmt_size > 0x10 && ww->format != 0x0165 && ww->format != 0x0166) /* ignore XMAWAVEFORMAT */
|
||||
ww->extra_size = read_u16(ww->fmt_offset + 0x10,sf);
|
||||
@ -864,7 +868,9 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
|
||||
case 0x3041: ww->codec = OPUSWW; break; /* "OPUS_WEM", added on Wwise 2019.2.3, replaces OPUS */
|
||||
case 0x8311: ww->codec = PTADPCM; break; /* added on Wwise 2019.1, replaces IMA */
|
||||
default:
|
||||
vgm_logi("WWISE: unknown codec 0x%04x (report)\n", ww->format);
|
||||
/* some .wav may end up here, only report in .wem cases (newer codecs) */
|
||||
if (ww->is_wem)
|
||||
vgm_logi("WWISE: unknown codec 0x%04x (report)\n", ww->format);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -953,22 +953,12 @@ STREAMFILE* open_streamfile(STREAMFILE* sf, const char* pathname) {
|
||||
|
||||
STREAMFILE* open_streamfile_by_ext(STREAMFILE* sf, const char* ext) {
|
||||
char filename[PATH_LIMIT];
|
||||
int filename_len, fileext_len;
|
||||
|
||||
sf->get_name(sf, filename, sizeof(filename));
|
||||
get_streamfile_name(sf, filename, sizeof(filename));
|
||||
|
||||
filename_len = strlen(filename);
|
||||
fileext_len = strlen(filename_extension(filename));
|
||||
swap_extension(filename, sizeof(filename), ext);
|
||||
|
||||
if (fileext_len == 0) {/* extensionless */
|
||||
strcat(filename,".");
|
||||
strcat(filename,ext);
|
||||
}
|
||||
else {
|
||||
strcpy(filename + filename_len - fileext_len, ext);
|
||||
}
|
||||
|
||||
return sf->open(sf, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
return open_streamfile(sf, filename);
|
||||
}
|
||||
|
||||
STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) {
|
||||
@ -978,7 +968,7 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) {
|
||||
|
||||
if (!sf || !filename || !filename[0]) return NULL;
|
||||
|
||||
sf->get_name(sf, fullname, sizeof(fullname));
|
||||
get_streamfile_name(sf, fullname, sizeof(fullname));
|
||||
|
||||
//todo normalize separators in a better way, safeops, improve copying
|
||||
|
||||
@ -1027,7 +1017,7 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) {
|
||||
strcpy(fullname, filename);
|
||||
}
|
||||
|
||||
return sf->open(sf, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
return open_streamfile(sf, fullname);
|
||||
}
|
||||
|
||||
STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size) {
|
||||
@ -1037,7 +1027,7 @@ STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size) {
|
||||
|
||||
if (buffer_size == 0)
|
||||
buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE;
|
||||
sf->get_name(sf, pathname,sizeof(pathname));
|
||||
get_streamfile_name(sf, pathname, sizeof(pathname));
|
||||
return sf->open(sf, pathname, buffer_size);
|
||||
}
|
||||
|
||||
|
48
src/util.c
48
src/util.c
@ -2,34 +2,40 @@
|
||||
#include "util.h"
|
||||
#include "streamtypes.h"
|
||||
|
||||
const char * filename_extension(const char * pathname) {
|
||||
const char * filename;
|
||||
const char * extension;
|
||||
const char* filename_extension(const char* pathname) {
|
||||
const char* extension;
|
||||
|
||||
/* favor strrchr (optimized/aligned) rather than homemade loops */
|
||||
extension = strrchr(pathname,'.');
|
||||
|
||||
/* find possible separator first to avoid misdetecting folders with dots + extensionless files
|
||||
* (allow both slashes as plugin could pass normalized '/') */
|
||||
filename = strrchr(pathname, '/');
|
||||
if (filename != NULL)
|
||||
filename++; /* skip separator */
|
||||
else {
|
||||
filename = strrchr(pathname, '\\');
|
||||
if (filename != NULL)
|
||||
filename++; /* skip separator */
|
||||
else
|
||||
filename = pathname; /* pathname has no separators (single filename) */
|
||||
if (extension != NULL) {
|
||||
/* probably has extension */
|
||||
extension++; /* skip dot */
|
||||
|
||||
/* find possible separators to avoid misdetecting folders with dots + extensionless files
|
||||
* (after the above to reduce search space, allows both slashes in case of non-normalized names) */
|
||||
if (strchr(extension, '/') == NULL && strchr(extension, '\\') == NULL)
|
||||
return extension; /* no slashes = really has extension */
|
||||
}
|
||||
|
||||
extension = strrchr(filename,'.');
|
||||
if (extension != NULL)
|
||||
extension++; /* skip dot */
|
||||
else
|
||||
extension = filename + strlen(filename); /* point to null (empty "" string for extensionless files) */
|
||||
|
||||
return extension;
|
||||
/* extensionless: point to null after current name
|
||||
* (could return NULL but prev code expects with to return an actual c-string) */
|
||||
return pathname + strlen(pathname);
|
||||
}
|
||||
|
||||
void swap_extension(char* pathname, int pathname_len, const char* swap) {
|
||||
char* extension = (char*)filename_extension(pathname);
|
||||
//todo safeops
|
||||
if (extension[0] == '\0') {
|
||||
strcat(pathname, ".");
|
||||
strcat(pathname, swap);
|
||||
}
|
||||
else {
|
||||
strcpy(extension, swap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* unused */
|
||||
/*
|
||||
void interleave_channel(sample_t * outbuffer, sample_t * inbuffer, int32_t sample_count, int channel_count, int channel_number) {
|
||||
|
@ -175,7 +175,10 @@ int round10(int val);
|
||||
|
||||
/* return a file's extension (a pointer to the first character of the
|
||||
* extension in the original filename or the ending null byte if no extension */
|
||||
const char * filename_extension(const char * filename);
|
||||
const char* filename_extension(const char* pathname);
|
||||
|
||||
/* change pathname's extension to another (or add it if extensionless) */
|
||||
void swap_extension(char* pathname, /*size_t*/ int pathname_len, const char* swap);
|
||||
|
||||
/* swap samples in machine endianness to little endian (useful to write .wav) */
|
||||
void swap_samples_le(sample_t *buf, int count);
|
||||
|
@ -13,6 +13,7 @@ typedef struct {
|
||||
int le_type; /* read type as LE instead of more common BE */
|
||||
int be_size; /* read type as BE instead of more common LE */
|
||||
int full_size; /* chunk size includes type+size */
|
||||
int alignment; /* chunks with odd size need to be aligned to even, per RIFF spec */
|
||||
} chunk_t;
|
||||
|
||||
int next_chunk(chunk_t* chunk, STREAMFILE* sf);
|
||||
|
@ -22,8 +22,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_adx,
|
||||
init_vgmstream_brstm,
|
||||
init_vgmstream_bfwav,
|
||||
init_vgmstream_bfstm,
|
||||
init_vgmstream_mca,
|
||||
init_vgmstream_nds_strm,
|
||||
init_vgmstream_agsc,
|
||||
init_vgmstream_ngc_adpdtk,
|
||||
@ -66,12 +64,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
#endif
|
||||
init_vgmstream_sli_ogg,
|
||||
init_vgmstream_sfl_ogg,
|
||||
#if 0
|
||||
init_vgmstream_mp4_aac,
|
||||
#endif
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
init_vgmstream_akb_mp4,
|
||||
#endif
|
||||
init_vgmstream_sadb,
|
||||
init_vgmstream_ps2_bmdx,
|
||||
init_vgmstream_wsi,
|
||||
@ -294,6 +286,14 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_idsp_namco,
|
||||
init_vgmstream_kt_g1l,
|
||||
init_vgmstream_kt_wiibgm,
|
||||
init_vgmstream_bfstm,
|
||||
init_vgmstream_mca,
|
||||
#if 0
|
||||
init_vgmstream_mp4_aac,
|
||||
#endif
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
init_vgmstream_akb_mp4,
|
||||
#endif
|
||||
init_vgmstream_ktss,
|
||||
init_vgmstream_hca,
|
||||
init_vgmstream_svag_snk,
|
||||
|
Loading…
x
Reference in New Issue
Block a user