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:
bnnm 2021-09-20 00:29:58 +02:00 committed by GitHub
commit 0d390f182d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 528 additions and 681 deletions

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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,