From 82fc05c3dcdbd14ac45b0ac22637b6d174905c9f Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 19 Sep 2021 23:54:38 +0200 Subject: [PATCH] misc cleanup --- src/meta/awb.c | 8 ++-- src/meta/bfwav.c | 89 ++++++++++++++++++++++++-------------------- src/meta/hca.c | 55 +++++++++++++++++++-------- src/meta/imuse.c | 34 +++++++---------- src/meta/nds_strm.c | 46 ++++++++++++----------- src/meta/wwise.c | 90 ++++++++++++++++++++++++--------------------- src/util/chunks.h | 1 + src/vgmstream.c | 16 ++++---- 8 files changed, 188 insertions(+), 151 deletions(-) diff --git a/src/meta/awb.c b/src/meta/awb.c index 7cff3061..177ca366 100644 --- a/src/meta/awb.c +++ b/src/meta/awb.c @@ -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; } diff --git a/src/meta/bfwav.c b/src/meta/bfwav.c index 734fdc0c..679aa7c6 100644 --- a/src/meta/bfwav.c +++ b/src/meta/bfwav.c @@ -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; diff --git a/src/meta/hca.c b/src/meta/hca.c index d7694ca9..1a9fb486 100644 --- a/src/meta/hca.c +++ b/src/meta/hca.c @@ -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 //#include @@ -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; diff --git a/src/meta/imuse.c b/src/meta/imuse.c index a8706ebe..112e0a46 100644 --- a/src/meta/imuse.c +++ b/src/meta/imuse.c @@ -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; diff --git a/src/meta/nds_strm.c b/src/meta/nds_strm.c index cbd534f5..64befb42 100644 --- a/src/meta/nds_strm.c +++ b/src/meta/nds_strm.c @@ -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; diff --git a/src/meta/wwise.c b/src/meta/wwise.c index ee8c1ec3..efa6f8c4 100644 --- a/src/meta/wwise.c +++ b/src/meta/wwise.c @@ -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; } diff --git a/src/util/chunks.h b/src/util/chunks.h index 0df77cc5..3d5ffe08 100644 --- a/src/util/chunks.h +++ b/src/util/chunks.h @@ -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); diff --git a/src/vgmstream.c b/src/vgmstream.c index fbebc29c..04edd13e 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -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,