diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index c6877d558..701f18781 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -669,6 +669,7 @@ 83F1EE30245D4FC10076E182 /* imuse.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F1EE2F245D4FC10076E182 /* imuse.c */; }; 83F2CCE525A5B41600F46FA8 /* acx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F2CCE125A5B41600F46FA8 /* acx.c */; }; 83F5F8831908D0A400C8E65F /* fsb5.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F5F8821908D0A400C8E65F /* fsb5.c */; }; + 83FB239127B14696003F3062 /* mpeg.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FB238D27B14696003F3062 /* mpeg.c */; }; 83FBD506235D31F800D35BCD /* riff_ogg_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */; }; 83FC176D23AC58D100E1025F /* xma_ue3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FC176A23AC58D100E1025F /* xma_ue3.c */; }; 83FC176E23AC58D100E1025F /* csb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FC176B23AC58D100E1025F /* csb.c */; }; @@ -1488,6 +1489,7 @@ 83F2CCE125A5B41600F46FA8 /* acx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acx.c; sourceTree = ""; }; 83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Vorbis.xcodeproj; path = ../Vorbis/macosx/Vorbis.xcodeproj; sourceTree = ""; }; 83F5F8821908D0A400C8E65F /* fsb5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb5.c; sourceTree = ""; }; + 83FB238D27B14696003F3062 /* mpeg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg.c; sourceTree = ""; }; 83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff_ogg_streamfile.h; sourceTree = ""; }; 83FC176A23AC58D100E1025F /* xma_ue3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma_ue3.c; sourceTree = ""; }; 83FC176B23AC58D100E1025F /* csb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = csb.c; sourceTree = ""; }; @@ -1998,6 +2000,7 @@ 8349A9031FE6258100E26435 /* mogg.c */, 839FBFF926C354E70016A78A /* mp4_faac.c */, 836F6E6018BDC2180095E648 /* mp4.c */, + 83FB238D27B14696003F3062 /* mpeg.c */, 8306B0CB2098458E000302D4 /* msb_msh.c */, 832BF81721E0514A006F50F1 /* msf_banpresto.c */, 83C7280D22BC893D00678B4A /* msf_konami.c */, @@ -3135,6 +3138,7 @@ 83AA7F7F2519C042004C5298 /* bsf.c in Sources */, 836F6F4118BDC2190095E648 /* blocked.c in Sources */, 836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */, + 83FB239127B14696003F3062 /* mpeg.c in Sources */, 839933612591E8C1001855AF /* sbk.c in Sources */, 835B9B932730BF2D00F87EE3 /* lpcm_shade.c in Sources */, 838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 8e5453a71..eac0cd889 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -481,6 +481,18 @@ void free_mpeg(mpeg_codec_data* data); int mpeg_get_sample_rate(mpeg_codec_data* data); long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data); + +typedef struct { + int version; + int layer; + int bit_rate; + int sample_rate; + int frame_samples; + int frame_size; /* bytes */ + int channels; +} mpeg_frame_info; + +int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c index d30ce2729..fa822edca 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c @@ -116,7 +116,7 @@ int test_key_g7221(g7221_codec_data* data, off_t start, STREAMFILE* sf) { /* assumes key was set before this call */ while (test_frames < S14_KEY_MAX_TEST_FRAMES && current_frame < max_frames) { - int score, ok; + int score, res; size_t bytes; uint8_t buf[G7221_MAX_FRAME_SIZE]; @@ -127,8 +127,8 @@ int test_key_g7221(g7221_codec_data* data, off_t start, STREAMFILE* sf) { break; } - ok = g7221_decode_frame(data->ch[cur_ch].handle, buf, data->ch[cur_ch].buffer); - if (!ok) { + res = g7221_decode_frame(data->ch[cur_ch].handle, buf, data->ch[cur_ch].buffer); + if (res < 0) { total_score = -1; break; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c index 1545bd85f..fb5a51e9c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c @@ -1072,12 +1072,13 @@ static int unpack_frame(int bit_rate, const uint8_t* data, int frame_size, /*int /* test for errors (in refdec but not Namco's, useful to detect decryption) */ if (test_errors) { + int max_pad_bytes = 0x8; /* usually 0x04 and rarely ~0x08 */ int bits_left = 8 * expected_frame_size - bitpos; int i, endpos, test_bits; if (bits_left > 0) { - /* frame must be padded with 1s */ + /* frame must be padded with 1s after regular data */ endpos = bitpos; for (i = 0; i < bits_left; i++) { int bit = (data_u32[endpos >> 5] >> (31 - (endpos & 0x1F))) & 1; @@ -1087,19 +1088,21 @@ static int unpack_frame(int bit_rate, const uint8_t* data, int frame_size, /*int return -1; } - /* extra: test we aren't in the middle of padding (happens with bad keys) */ + /* extra: test we aren't in the middle of padding (happens with bad keys, this test catches most) + * After reading the whole frame, last bit position should land near last useful + * data, a few bytes into padding, so check there aren't too many padding bits. */ endpos = bitpos; - test_bits = 8 * 0x04; + test_bits = 8 * max_pad_bytes; if (test_bits > bitpos) test_bits = bitpos; for (i = 0; i < test_bits; i++) { int bit = (data_u32[endpos >> 5] >> (31 - (endpos & 0x1F))) & 1; - endpos--; + endpos--; /* from last position towards valid data */ if (bit != 1) break; } - /* so many 1s isn't very normal */ + if (i == test_bits) return -8; @@ -1186,10 +1189,9 @@ int g7221_decode_frame(g7221_handle* handle, uint8_t* data, int16_t* out_samples /* Namco also sets number of codes/samples done from unpack_frame/rmlt (ptr arg), * but they seem unused */ - return 1; -fail: - //;printf("S14: fail %i\n", res); return 0; +fail: + return res; } #if 0 @@ -1271,7 +1273,7 @@ int g7221_set_key(g7221_handle* handle, const uint8_t* key) { /* reset new key */ s14aes_set_key(handle->aes, temp_key); - return 1; -fail: return 0; +fail: + return -1; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.h b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.h index 08468c3e2..4e4040814 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.h @@ -12,7 +12,7 @@ typedef struct g7221_handle g7221_handle; /* return a handle for decoding on successful init, NULL on failure */ g7221_handle* g7221_init(int bytes_per_frame); -/* decode a frame, at code_words, into 16-bit PCM in sample_buffer */ +/* decode a frame, at code_words, into 16-bit PCM in sample_buffer. returns <0 on error */ int g7221_decode_frame(g7221_handle* handle, uint8_t* data, int16_t* out_samples); #if 0 @@ -26,7 +26,7 @@ void g7221_reset(g7221_handle* handle); /* free resources */ void g7221_free(g7221_handle* handle); -/* set new key (ignores key on failure) */ +/* set new key (ignores key on failure). returns <0 on error */ int g7221_set_key(g7221_handle* handle, const uint8_t* key); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h index c818d0f73..7ea981f2a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h @@ -75,16 +75,4 @@ int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL* stream, mpeg_codec_data* dat #endif/* VGM_USE_MPEG */ -typedef struct { - int version; - int layer; - int bit_rate; - int sample_rate; - int frame_samples; - int frame_size; /* bytes */ - int channels; -} mpeg_frame_info; - -int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info); - #endif/*_MPEG_DECODER_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 2883f8d90..649464b3f 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -21,7 +21,6 @@ static const char* extension_list[] = { //"", /* vgmstream can play extensionless files too, but plugins must accept them manually */ - "04sw", "208", "2dx9", "2pfs", @@ -625,6 +624,7 @@ static const char* extension_list[] = { "xma", "xma2", "xmu", + "xmv", "xnb", "xsh", "xsf", @@ -1382,6 +1382,7 @@ static const meta_info meta_info_list[] = { {meta_WBK, "Treyarch WBK header"}, {meta_WBK_NSLB, "Treyarch NSLB header"}, {meta_DSP_APEX, "Koei Tecmo APEX header"}, + {meta_MPEG, "MPEG header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c index 74d6724e7..a7298dc05 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c @@ -3,11 +3,31 @@ #include "../util/endianness.h" -/* FWAV and CWAV are basically identical except always LE */ -typedef enum { FWAV, CWAV } bxwav_type_t; +/* RWAV is a bit simpler, while FWAV and CWAV are basically identical except for endianness. + * From SDK info, typically .xxxx is the original NintendoWare format (usually a .xml) and .bxxxx is a + * binary/external form (like .rseq=xml then .brseq=binary, or .wav > brwav). */ +typedef enum { RWAV, FWAV, CWAV } bxwav_type_t; static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type); + +/* RWAV - NintendoWare binary Revolution wave (Wii games) */ +VGMSTREAM* init_vgmstream_brwav(STREAMFILE* sf) { + + /* checks */ + if (!is_id32be(0x00, sf, "RWAV")) + goto fail; + + /* .brwav: from tools (no games known) + * .rwav: header id */ + if (!check_extensions(sf, "brwav,rwav")) + goto fail; + + return init_vgmstream_bxwav(sf, RWAV); +fail: + return NULL; +} + /* FWAV - NintendoWare binary caFe wave (WiiU and Switch games) */ VGMSTREAM* init_vgmstream_bfwav(STREAMFILE* sf) { @@ -21,7 +41,6 @@ VGMSTREAM* init_vgmstream_bfwav(STREAMFILE* sf) { goto fail; return init_vgmstream_bxwav(sf, FWAV); - fail: return NULL; } @@ -43,7 +62,6 @@ VGMSTREAM* init_vgmstream_bcwav(STREAMFILE* sf) { goto fail; return init_vgmstream_bxwav(sf, CWAV); - fail: return NULL; } @@ -52,7 +70,7 @@ fail: static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { VGMSTREAM* vgmstream = NULL; - uint32_t info_offset, data_offset, chtb_offset; + uint32_t info_offset, data_offset, chtb_offset, file_size; int channels, loop_flag, codec, sample_rate; int big_endian; int32_t num_samples, loop_start; @@ -63,7 +81,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { read_s16_t read_s16; /* BOM check */ - if (read_u16be(0x04, sf) == 0xFEFF) { /* WiiU */ + if (read_u16be(0x04, sf) == 0xFEFF) { /* Wii, WiiU */ big_endian = 1; read_u32 = read_u32be; read_s32 = read_s32be; @@ -82,41 +100,95 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { } /* header */ - /* 0x06(2): header size (0x40) */ - /* 0x08: version */ - /* - FWAV: 0x00010200 */ - /* - CWAV: 0x00000002 (Kirby's Adventure), 0x00000102 (common), 0x00010102 (FE Fates, Hyrule Warriors Legends) */ - /* 0x0c: file size */ - /* 0x10(2): sections (2) */ + switch(type) { + case RWAV: + /* 0x06(2): version (usually 0102, 0103=Let's Tap, no diffs) */ + file_size = read_u32(0x08, sf); + /* 0x0c(2): header size (0x20) */ + /* 0x0e(2): sections (2) */ - /* 0x14(2): info mark (0x7000) */ - info_offset = read_u32(0x18, sf); - /* 0x1c: info size */ + info_offset = read_u32(0x10, sf); + /* 0x14: info size */ - /* 0x20(2): data mark (0x7001) */ - data_offset = read_u32(0x24, sf); - /* 0x28: data size */ - /* rest: padding */ + data_offset = read_u32(0x18, sf); + /* 0x1c: data size */ + break; + + case FWAV: + case CWAV: + /* 0x06(2): header size (0x40) */ + /* 0x08: version */ + /* - FWAV: 0x00010200 */ + /* - CWAV: 0x00000002 (Kirby's Adventure), 0x00000102 (common), 0x00010102 (FE Fates, Hyrule Warriors Legends) */ + file_size = read_u32(0x0c, sf); + /* 0x10(2): sections (2) */ + + /* 0x14(2): info mark (0x7000) */ + info_offset = read_u32(0x18, sf); + /* 0x1c: info size */ + + /* 0x20(2): data mark (0x7001) */ + data_offset = read_u32(0x24, sf); + /* 0x28: data size */ + /* rest: padding */ + break; + + default: + goto fail; + } + + if (file_size != get_streamfile_size(sf)) { + vgm_logi("BXWAV: wrong size %x vs %x\n", file_size, (uint32_t)get_streamfile_size(sf)); + goto fail; + } /* INFO section */ if (!is_id32be(info_offset + 0x00, sf, "INFO")) goto fail; /* 0x04: size */ - codec = read_u8(info_offset + 0x08, sf); - loop_flag = read_u8(info_offset + 0x09, sf); - /* 0x0a: padding */ - sample_rate = read_u32(info_offset + 0x0C, sf); - loop_start = read_s32(info_offset + 0x10, sf); - num_samples = read_s32(info_offset + 0x14, sf); - /* 0x18: original loop start? (slightly lower) */ - chtb_offset = info_offset + 0x1C; - channels = read_u32(chtb_offset + 0x00, sf); + + switch(type) { + case RWAV: + codec = read_u8(info_offset + 0x08, sf); + loop_flag = read_u8(info_offset + 0x09, sf); + channels = read_u8(info_offset + 0x0a, sf); + /* 0x0b: part of rate? */ + sample_rate = read_u16(info_offset + 0x0c, sf); + /* 0x0e(2): padding */ + loop_start = read_s32(info_offset + 0x10, sf); + num_samples = read_s32(info_offset + 0x14, sf); + chtb_offset = read_u32(info_offset + 0x18, sf) + info_offset + 0x08; + /* 0x1c: channel table size */ + + loop_start = dsp_nibbles_to_samples(loop_start); + num_samples = dsp_nibbles_to_samples(num_samples); + + break; + + case FWAV: + case CWAV: + codec = read_u8(info_offset + 0x08, sf); + loop_flag = read_u8(info_offset + 0x09, sf); + /* 0x0a: padding */ + sample_rate = read_u32(info_offset + 0x0C, sf); + loop_start = read_s32(info_offset + 0x10, sf); + num_samples = read_s32(info_offset + 0x14, sf); + /* 0x18: original loop start? (slightly lower) */ + chtb_offset = info_offset + 0x1C; + channels = read_u32(chtb_offset + 0x00, sf); + break; + + default: + goto fail; + } + /* channel table is parsed at the end */ /* DATA section */ if (!is_id32be(data_offset + 0x00, sf, "DATA")) goto fail; + /* 0x04: size */ /* build the VGMSTREAM */ @@ -124,6 +196,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { if (!vgmstream) goto fail; switch(type) { + case RWAV: vgmstream->meta_type = meta_RWAV; break; case FWAV: vgmstream->meta_type = meta_FWAV; break; case CWAV: vgmstream->meta_type = meta_CWAV; break; default: goto fail; @@ -138,7 +211,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { vgmstream->layout_type = layout_none; - /* only 0x02 is known, other codecs are probably from bxstm that do use them */ + /* only 0x02 is known, others can be made with SDK tools */ switch (codec) { case 0x00: vgmstream->coding_type = coding_PCM8; @@ -171,25 +244,51 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { { int ch, i; for (ch = 0; ch < channels; ch++) { - uint32_t chnf_offset, chdt_offset; - /* channel entry: */ - /* - 0x00: mark (0x7100) */ - /* - 0x02: padding */ - /* - 0x04: channel info offset (from channel table offset) */ - chnf_offset = read_u32(chtb_offset + 0x04 + ch * 0x08 + 0x04, sf) + chtb_offset; + uint32_t chnf_offset, chdt_offset, coef_offset; + switch(type) { + case RWAV: + /* channel entry: */ + /* - 0x04: channel info offset (from INFO offset after size) */ - /* channel info: */ - /* 0x00: mark (0x1F00) */ - /* 0x02: padding */ - /* 0x04: offset to channel data (from DATA offset after size ) */ - /* 0x08: ADPCM mark (0x0300=DSP, 0x0301=IMA, 0x0000=none) */ - /* 0x0a: padding */ - /* 0x0c: ADPCM offset (from channel info offset), 0xFFFFFFFF otherwise */ - /* 0x10: null? */ + chnf_offset = read_u32(chtb_offset + ch * 0x04 + 0x00, sf) + info_offset + 0x08; - if (read_u16(chnf_offset + 0x00, sf) != 0x1F00) - goto fail; - chdt_offset = read_u32(chnf_offset + 0x04, sf) + data_offset + 0x08; + /* channel info: */ + /* 0x00: offset to channel data (from DATA offset after size) */ + /* 0x04: ADPCM offset (from INFO offset after size), 0xFFFFFFFF otherwise? */ + /* 0x08: volumes x4? */ + /* 0x18: padding */ + + chdt_offset = read_u32(chnf_offset + 0x00, sf) + data_offset + 0x08; + coef_offset = read_u32(chnf_offset + 0x04, sf) + info_offset + 0x08; + break; + + case FWAV: + case CWAV: + /* channel entry: */ + /* - 0x00: mark (0x7100) */ + /* - 0x02: padding */ + /* - 0x04: channel info offset (from channel table offset) */ + + chnf_offset = read_u32(chtb_offset + 0x04 + ch * 0x08 + 0x04, sf) + chtb_offset; + + /* channel info: */ + /* 0x00: mark (0x1F00) */ + /* 0x02: padding */ + /* 0x04: offset to channel data (from DATA offset after size) */ + /* 0x08: ADPCM mark (0x0300=DSP, 0x0301=IMA, 0x0000=none) */ + /* 0x0a: padding */ + /* 0x0c: ADPCM offset (from channel info offset), 0xFFFFFFFF otherwise */ + /* 0x10: padding */ + + if (read_u16(chnf_offset + 0x00, sf) != 0x1F00) + goto fail; + chdt_offset = read_u32(chnf_offset + 0x04, sf) + data_offset + 0x08; + coef_offset = read_u32(chnf_offset + 0x0c, sf) + chnf_offset; + break; + + default: + goto fail; + } vgmstream->ch[ch].channel_start_offset = chdt_offset; vgmstream->ch[ch].offset = chdt_offset; @@ -197,20 +296,16 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { switch(codec) { case 0x02: { /* standard DSP coef + predictor + hists + loop predictor + loop hists */ - uint32_t coef_offset = read_u32(chnf_offset + 0x0c, sf) + chnf_offset; - for (i = 0; i < 16; i++) { vgmstream->ch[ch].adpcm_coef[i] = read_s16(coef_offset + 0x00 + i*0x02, sf); } - vgmstream->ch[ch].adpcm_history1_16 = read_s16(coef_offset + 0x22, sf); - vgmstream->ch[ch].adpcm_history2_16 = read_s16(coef_offset + 0x24, sf); + //vgmstream->ch[ch].adpcm_history1_16 = read_s16(coef_offset + 0x22, sf); + //vgmstream->ch[ch].adpcm_history2_16 = read_s16(coef_offset + 0x24, sf); break; } case 0x03: { /* hist + step */ - uint32_t coef_offset = read_u32(chnf_offset + 0x0c, sf) + chnf_offset; - vgmstream->ch[ch].adpcm_history1_16 = read_s16(coef_offset + 0x00, sf); vgmstream->ch[ch].adpcm_step_index = read_s16(coef_offset + 0x02, sf); break; @@ -228,3 +323,73 @@ fail: close_vgmstream(vgmstream); return NULL; } + +/* ************************************************************************* */ + +/* RWAR - NintendoWare container [BIT.TRIP BEAT (Wii), Dance Dance Revolution Hottest Party 2 (Wii)] */ +VGMSTREAM* init_vgmstream_brwar(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + uint32_t tabl_offset, data_offset; + uint32_t subfile_offset, subfile_size; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!is_id32be(0x00, sf, "RWAR")) + goto fail; + + if (!check_extensions(sf,"rwar")) + goto fail; + + /* simple container of .rwavs (inside .brsar), rarely used with single siles (DDR) */ + + /* abridged, see RWAV (same header) */ + /* 0x04(2): BOM */ + /* 0x06(2): version (usually 0100) */ + /* 0x08: file size */ + /* 0x0c(2): header size (0x20) */ + /* 0x0e(2): sections (2) */ + + tabl_offset = read_u32be(0x10, sf); + /* 0x14: tabl size */ + + data_offset = read_u32be(0x18, sf); + /* 0x1c: data size */ + + /* TABL section */ + if (!is_id32be(tabl_offset + 0x00, sf, "TABL")) + goto fail; + /* 0x04: size */ + + total_subsongs = read_u32be(tabl_offset + 0x08,sf); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* 0x00: always 0x01000000? */ + subfile_offset = read_u32be(tabl_offset + 0x0c + (target_subsong-1) * 0x0c + 0x04,sf) + data_offset; + subfile_size = read_u32be(tabl_offset + 0x0c + (target_subsong-1) * 0x0c + 0x08,sf); + + + /* DATA section */ + if (!is_id32be(data_offset + 0x00, sf, "DATA")) + goto fail; + /* 0x04: size */ + + //VGM_LOG("BRWAR: of=%x, sz=%x\n", subfile_offset, subfile_size); + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "rwav"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_brwav(temp_sf); + if (!vgmstream) goto fail; + + vgmstream->num_streams = total_subsongs; + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bik.c b/Frameworks/vgmstream/vgmstream/src/meta/bik.c index 58643d9cb..713c4f13f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bik.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bik.c @@ -12,14 +12,15 @@ VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) { size_t stream_size; /* checks */ - /* .bik/bik2/bk2: standard - * .bika: fake extension for demuxed audio */ - if (!check_extensions(sf,"bik,bik2,bk2,bika")) + /* bink1/2 header, followed by version-char (audio is the same) */ + if ((read_u32be(0x00,sf) & 0xffffff00) != get_id32be("BIK\0") && + (read_u32be(0x00,sf) & 0xffffff00) != get_id32be("KB2\0")) goto fail; - /* check bink1/2 header, followed by version-char (audio is the same) */ - if ((read_32bitBE(0x00,sf) & 0xffffff00) != get_id32be("BIK\0") && - (read_32bitBE(0x00,sf) & 0xffffff00) != get_id32be("KB2\0")) + /* .bik/bik2/bk2: standard + * .xmv: Reflections games [Driver: Parallel Lines (Wii), Emergency Heroes (Wii)] + * .bika: fake extension for demuxed audio */ + if (!check_extensions(sf,"bik,bik2,bk2,xmv,bika")) goto fail; /* find target stream info and samples */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c b/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c index b785e27e0..f12eb7c77 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c @@ -234,7 +234,7 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d } } -//done: +done: VGM_ASSERT(best_score > 0, "BNSF: best key=%.24s (score=%i)\n", best_key, best_score); VGM_ASSERT(best_score < 0, "BNSF: key not found\n"); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 6f179e20e..f95f10b49 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -1050,7 +1050,7 @@ typedef struct { off_t prefetch_offset; } eaac_header; -static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_data, eaac_header *eaac); +static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE* sf_head, STREAMFILE* sf_data, eaac_header* eaac); static layered_layout_data* build_layered_eaaudiocore(STREAMFILE* sf, eaac_header *eaac, off_t start_offset); static STREAMFILE *setup_eaac_streamfile(eaac_header *ea, STREAMFILE* sf_head, STREAMFILE* sf_data); static size_t calculate_eaac_size(STREAMFILE* sf, eaac_header *ea, uint32_t num_samples, off_t start_offset, int is_ram); @@ -1137,8 +1137,9 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM || eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE || eaac.codec == EAAC_CODEC_EAXMA || - eaac.codec == EAAC_CODEC_XAS1)) { - VGM_LOG("EA EAAC: unknown actual looping for codec %x\n", eaac.codec); + eaac.codec == EAAC_CODEC_XAS1 || + eaac.codec == EAAC_CODEC_EATRAX)) { + VGM_LOG("EA EAAC: unknown actual looping %i for codec %x\n", eaac.loop_start, eaac.codec); goto fail; } } @@ -1220,7 +1221,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF if (eaac.version == EAAC_VERSION_V0 && eaac.streamed) { /* open SNS file if needed */ if (standalone) { - snsFile = open_streamfile_by_ext(sf_head, "sns"); + snsFile = open_streamfile_by_ext(sf_head, "sns"); //todo clean sf_data = snsFile; } if (!sf_data) goto fail; @@ -1255,7 +1256,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF /* special (if hacky) loop handling, see comments */ if (eaac.loop_start > 0) { - segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, &eaac); + segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, sf, &eaac); if (!data) goto fail; vgmstream->layout_data = data; vgmstream->coding_type = data->segments[0]->coding_type; @@ -1276,7 +1277,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF /* special (if hacky) loop handling, see comments */ if (eaac.loop_start > 0) { - segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, &eaac); + segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, sf, &eaac); if (!data) goto fail; vgmstream->layout_data = data; vgmstream->coding_type = data->segments[0]->coding_type; @@ -1301,14 +1302,14 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF /* special (if hacky) loop handling, see comments */ if (eaac.loop_start > 0) { - segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, &eaac); + segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, sf, &eaac); if (!data) goto fail; vgmstream->layout_data = data; vgmstream->coding_type = data->segments[0]->coding_type; vgmstream->layout_type = layout_segmented; } else { - temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00); + temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00, 0); if (!temp_sf) goto fail; vgmstream->codec_data = init_mpeg_custom(temp_sf, 0x00, &vgmstream->coding_type, vgmstream->channels, type, &cfg); @@ -1336,7 +1337,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF vgmstream->coding_type = coding_SPEEX; vgmstream->layout_type = layout_none; - temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00); + temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00, 0); if (!temp_sf) goto fail; break; @@ -1349,9 +1350,21 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF /* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */ +#if 0 + /* For looped EATRAX, since we are using a deblocker SF no need to make segmented looping, though it works [Madden NFL 13 Demo (Vita)] + * An issue with segmented is that AT9 state is probably not reset between loops, which segmented can't simulate */ + if (eaac.loop_start > 0) { + segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf_head, sf, &eaac); + if (!data) goto fail; + vgmstream->layout_data = data; + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + } +#endif + cfg.channels = eaac.channels; /* sub-header after normal header */ - cfg.config_data = read_32bitBE(header_offset + header_size + 0x00,sf_head); + cfg.config_data = read_u32be(header_offset + header_size + 0x00,sf_head); /* 0x04: data size without blocks, LE b/c why make sense (but don't use it in case of truncated files) */ /* 0x08: 16b frame size (same as config data) */ @@ -1360,7 +1373,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; - temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00); + temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00, 0); if (!temp_sf) goto fail; break; @@ -1372,7 +1385,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF case EAAC_CODEC_EAMP3: { /* "EM30": EA-MP3 [Need for Speed 2015 (PS4), FIFA 2021 (PC)] */ mpeg_custom_config cfg = {0}; - temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00); + temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00, 0); if (!temp_sf) goto fail; vgmstream->codec_data = init_mpeg_custom(temp_sf, 0x00, &vgmstream->coding_type, vgmstream->channels, MPEG_EAMP3, &cfg); @@ -1420,7 +1433,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF case 4: cfg.coupled_count = 2; break; /* 2ch+2ch, 2 streams */ //case 3: /* 2ch+1ch, 2 streams */ case 2: cfg.coupled_count = 1; break; /* 2ch, 1 stream */ - //case 1: cfg.coupled_count = 0; break; /* 1ch, 1 stream */ + case 1: cfg.coupled_count = 0; break; /* 1ch, 1 stream [Madden 22 (PC)] */ default: goto fail; } } @@ -1431,7 +1444,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF /* We *don't* remove EA blocks b/c in Multi Opus 1 block = 1 Opus packet * Regular EAOPUS uses layers to fake multichannel, this is normal multichannel Opus. * This can be used for stereo too, so probably replaces EAOPUS. */ - //temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,0,0, 0x00); + //temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,0,0, 0x00, 0); //if (!temp_sf) goto fail; vgmstream->codec_data = init_ffmpeg_ea_opusm(sf, offset, data_size, &cfg); @@ -1613,10 +1626,12 @@ fail: * We use the segmented layout, since the eaac_streamfile doesn't handle padding, * and the segments seem fully separate (so even skipping would probably decode wrong). */ // todo reorganize code for more standard init -static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_data, eaac_header *eaac) { +static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_head, STREAMFILE *sf_data, eaac_header *eaac) { segmented_layout_data *data = NULL; STREAMFILE* temp_sf = NULL; + uint32_t data_size = get_streamfile_size(sf_data); off_t offsets[2] = { 0x00, eaac->loop_offset }; + uint32_t sizes[2] = { eaac->loop_offset, data_size - eaac->loop_offset }; off_t start_offset; int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start}; int segment_count = 2; /* intro/loop */ @@ -1628,8 +1643,11 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf if (!data) goto fail; for (i = 0; i < segment_count; i++) { - data->segments[i] = allocate_vgmstream(eaac->channels, 0); - if (!data->segments[i]) goto fail; + VGMSTREAM* vgmstream = allocate_vgmstream(eaac->channels, 0); + + if (!vgmstream) goto fail; + data->segments[i] = vgmstream; + data->segments[i]->sample_rate = eaac->sample_rate; data->segments[i]->num_samples = num_samples[i]; //data->segments[i]->meta_type = eaac->meta_type; /* bleh */ @@ -1669,7 +1687,7 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf start_offset = 0x00; /* must point to the custom streamfile's beginning */ - temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]); + temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i], sizes[i]); if (!temp_sf) goto fail; data->segments[i]->codec_data = init_mpeg_custom(temp_sf, 0x00, &data->segments[i]->coding_type, eaac->channels, type, &cfg); @@ -1678,11 +1696,37 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf break; } #endif +#ifdef VGM_USE_ATRAC9 + case EAAC_CODEC_EATRAX: { /* EATrax (unknown FourCC) [Need for Speed: Most Wanted (Vita)] */ + atrac9_config cfg = {0}; + + /* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */ + + cfg.channels = eaac->channels; + /* sub-header after normal header */ + cfg.config_data = read_u32be(0x14 + 0x00, sf_head); //todo pass header offset + /* 0x04: data size without blocks, LE b/c why make sense (but don't use it in case of truncated files) */ + /* 0x08: 16b frame size (same as config data) */ + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + + //todo should make sizes + temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,0,0, offsets[i], sizes[i]); + if (!temp_sf) goto fail; + + break; + } +#endif default: goto fail; } - if (!vgmstream_open_stream(data->segments[i],temp_sf == NULL ? sf_data : temp_sf, start_offset)) + if (!vgmstream_open_stream(data->segments[i], temp_sf == NULL ? sf_data : temp_sf, start_offset)) goto fail; close_streamfile(temp_sf); @@ -1732,7 +1776,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_ size_t stream_size; int is_xma1; - temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset); + temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset, 0); if (!temp_sf) goto fail; stream_size = get_streamfile_size(temp_sf); @@ -1764,7 +1808,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_ size_t data_size; /* We'll remove EA blocks and pass raw data to FFmpeg Opus decoder */ - temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset); + temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset, 0); if (!temp_sf) goto fail; skip = ea_opus_get_encoder_delay(0x00, temp_sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h index b14514530..3cb7a4ed9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h @@ -13,6 +13,7 @@ typedef struct { int stream_number; int stream_count; off_t stream_offset; + uint32_t stream_size; /* state */ off_t logical_offset; /* offset that corresponds to physical_offset */ @@ -30,7 +31,7 @@ typedef struct { /* Reads skipping EA's block headers, so the resulting data is smaller or larger than physical data. * physical/logical_offset will be at the start of a block and only advance when a block is done */ -static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, eaac_io_data* data) { +static size_t eaac_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, eaac_io_data* data) { size_t total_read = 0; /* ignore bad reads */ @@ -58,8 +59,8 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, /* process new block */ if (data->data_size == 0) { - data->block_flag = (uint8_t)read_8bit(data->physical_offset+0x00,streamfile); - data->block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF; + data->block_flag = (uint8_t)read_8bit(data->physical_offset+0x00,sf); + data->block_size = read_32bitBE(data->physical_offset+0x00,sf) & 0x00FFFFFF; /* ignore header block */ if (data->version == 1 && data->block_flag == 0x48) { @@ -74,9 +75,9 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, data->skip_size = 0x04 + 0x04; for (i = 0; i < data->stream_number; i++) { - data->skip_size += read_32bitBE(data->physical_offset+data->skip_size, streamfile) / 4; + data->skip_size += read_32bitBE(data->physical_offset+data->skip_size, sf) / 4; } - data->data_size = read_32bitBE(data->physical_offset+data->skip_size, streamfile) / 4; /* why size*4...? */ + data->data_size = read_32bitBE(data->physical_offset+data->skip_size, sf) / 4; /* why size*4...? */ data->skip_size += 0x04; /* skip mini header */ data->data_size -= 0x04; /* remove mini header */ if (data->data_size % XMA_FRAME_SIZE) @@ -96,7 +97,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, case 0x0a: /* EATrax */ data->skip_size = 0x08; - data->data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* also block_size - 0x08 */ + data->data_size = read_32bitBE(data->physical_offset+0x04,sf); /* also block_size - 0x08 */ break; default: @@ -125,7 +126,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, to_read = data->data_size - bytes_consumed; if (to_read > length) to_read = length; - bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf); } else { /* offset falls within logical padded data */ to_read = data->data_size + data->extra_size - bytes_consumed; @@ -141,7 +142,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, to_read = data->data_size - bytes_consumed; if (to_read > length) to_read = length; - bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf); break; } @@ -168,7 +169,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { return data->logical_size; physical_offset = data->stream_offset; - max_physical_offset = get_streamfile_size(streamfile); + max_physical_offset = physical_offset + data->stream_size; /* get size of the logical stream */ while (physical_offset < max_physical_offset) { @@ -231,7 +232,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { } /* logical size can be bigger in EA-XMA though */ - if (physical_offset > get_streamfile_size(streamfile)) { + if (physical_offset > max_physical_offset) { VGM_LOG("EA EAAC: wrong size\n"); return 0; } @@ -247,10 +248,13 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { * - EATrax: ATRAC9 frames can be split between blooks * - EAOpus: multiple Opus packets of frame size + Opus data per block */ -static STREAMFILE* setup_eaac_audio_streamfile(STREAMFILE *sf, int version, int codec, int streamed, int stream_number, int stream_count, off_t stream_offset) { +static STREAMFILE* setup_eaac_audio_streamfile(STREAMFILE* sf, int version, int codec, int streamed, int stream_number, int stream_count, uint32_t stream_offset, uint32_t stream_size) { STREAMFILE *new_sf = NULL; eaac_io_data io_data = {0}; + if (!stream_size) + stream_size = get_streamfile_size(sf) - stream_offset; + io_data.version = version; io_data.codec = codec; io_data.streamed = streamed; @@ -258,6 +262,7 @@ static STREAMFILE* setup_eaac_audio_streamfile(STREAMFILE *sf, int version, int io_data.stream_count = stream_count; io_data.stream_offset = stream_offset; io_data.physical_offset = stream_offset; + io_data.stream_size = stream_size; io_data.logical_size = eaac_io_size(sf, &io_data); /* force init */ /* setup subfile */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 6538d48f4..4ce84eb70 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -564,8 +564,10 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_bfstm(STREAMFILE* streamFile); +VGMSTREAM* init_vgmstream_brwav(STREAMFILE* sf); VGMSTREAM* init_vgmstream_bfwav(STREAMFILE* sf); VGMSTREAM* init_vgmstream_bcwav(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_brwar(STREAMFILE* sf); VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_kt_wiibgm(STREAMFILE* streamFile); @@ -972,4 +974,6 @@ VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/mpeg.c new file mode 100644 index 000000000..cbe454b98 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/mpeg.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* MPEG - standard MP1/2/3 audio MP3 */ +VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf) { +#ifdef VGM_USE_MPEG + VGMSTREAM* vgmstream = NULL; + int loop_flag = 0; + mpeg_frame_info info = {0}; + + + /* checks */ + if (!mpeg_get_frame_info(sf, 0x00, &info)) + goto fail; + + /* .mp3/mp2: standard (is .mp1 ever used in games?) + * .lmp1/2/3: for plugins + * .mus: Marc Ecko's Getting Up (PC) */ + if (!check_extensions(sf, "mp3,mp2,mp1,mus,lmp3,lmp2,lmp1")) + goto fail; + + loop_flag = 0; + + + /* build VGMSTREAM */ + vgmstream = allocate_vgmstream(info.channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MPEG; + vgmstream->sample_rate = info.sample_rate; + + /* more strict, use? */ + //mpeg_custom_config cfg = {0}; + //cfg.skip_samples = ... + //vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, fmt.channels, MPEG_STANDARD, &cfg); + + vgmstream->codec_data = init_mpeg(sf, 0x00, &vgmstream->coding_type, info.channels); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + //vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data); + vgmstream->num_samples = mpeg_get_samples(sf, 0x00, get_streamfile_size(sf)); + + + if (!vgmstream_open_stream(vgmstream, sf, 0x00)) + goto fail; + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +#else + return NULL; +#endif +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/musx.c b/Frameworks/vgmstream/vgmstream/src/meta/musx.c index 151702c2d..441d4ec43 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/musx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/musx.c @@ -608,56 +608,80 @@ static int parse_musx(STREAMFILE* sf, musx_header* musx) { case SBNK: { off_t target_offset, head_offset, data_offset; - uint8_t codec; + uint8_t codec = 0; + uint32_t version; - /* 0x00: id */ - /* 0x04: always 0x12? */ - /* 0x08: file ID (same as base file) */ - /* 0x0c: some ID? */ - - /* 0x10: table1 count */ - /* 0x14: table1 offset (from here) */ - /* 0x18: table2 count */ - /* 0x1c: table2 offset (from here) */ - - /* 0x20: table3 count */ - /* 0x24: table3 offset (from here) */ - /* 0x28: table4 count */ - /* 0x2c: table4 offset (from here) */ - - /* 0x30: table5 count (waves) */ - /* 0x34: table5 offset (from here) */ - /* 0x38: table6 count */ - /* 0x3c: table6 offset (from here) */ - - /* 0x40: table7 count */ - /* 0x44: table7 offset (from here) */ - /* 0x48: data size */ - /* 0x4c: data offset (absolute) */ - - musx->tables_offset = 0x800; - if (!is_id32be(musx->tables_offset+0x00, sf, "SBNK")) + if (!is_id32be(0x800 + 0x00, sf, "SBNK")) goto fail; - musx->total_subsongs = read_u32(musx->tables_offset+0x30, sf); + version = read_u32(0x800 + 0x04, sf); + if (version == 0x2A) { + /* - Goldeneye 007 (X360) */ + /* 0x08: "COM " */ + /* 0x0c: file ID (same as base file) */ + /* 0x10: some ID? */ + musx->tables_offset = 0x814; + } + else { + /* - v0x12 (all others) */ + /* 0x08: file ID (same as base file) */ + /* 0x0c: some ID? */ + musx->tables_offset = 0x810; + } + + /* 0x00: table1 count */ + /* 0x04: table1 offset (from here) */ + /* 0x08: table2 count */ + /* 0x0c: table2 offset (from here) */ + + /* 0x10: table3 count */ + /* 0x14: table3 offset (from here) */ + /* 0x18: table4 count */ + /* 0x1c: table4 offset (from here) */ + + /* 0x20: table5 count (waves) */ + /* 0x24: table5 offset (from here) */ + /* 0x28: table6 count */ + /* 0x2c: table6 offset (from here) */ + + /* 0x30: table7 count */ + /* 0x34: table7 offset (from here) */ + /* 0x38: data size */ + /* 0x3c: data offset (absolute in older versions) */ + + musx->total_subsongs = read_u32(musx->tables_offset+0x20, sf); if (target_subsong == 0) target_subsong = 1; if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail; - head_offset = read_u32(musx->tables_offset+0x34, sf) + musx->tables_offset + 0x34; - data_offset = read_u32(musx->tables_offset+0x4c, sf); + if (version == 0x2A) { + ;VGM_LOG("MUSX: unknown version format\n"); + goto fail; + /* subheader is 0x10 but variable?, offset table may be table 7? + - 0x00: ID? + - 0x04: samples? + - 0x08: sample rate + - 0x0a: codec? + - 0x0b: channels + - 0x0c: size? + */ + } + else { + head_offset = read_u32(musx->tables_offset+0x24, sf) + musx->tables_offset + 0x24; + data_offset = read_u32(musx->tables_offset+0x3c, sf); - target_offset = head_offset + (target_subsong - 1)*0x1c; - ;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset); + target_offset = head_offset + (target_subsong - 1) * 0x1c; + ;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset); - /* 0x00: subfile ID */ - musx->num_samples = read_s32(target_offset + 0x04, sf); - musx->loop_start_sample = read_s32(target_offset + 0x08, sf); /* -1 if no loop */ - musx->sample_rate = read_u16(target_offset + 0x0c, sf); - codec = read_u8 (target_offset + 0x0e, sf); - musx->channels = read_u8 (target_offset + 0x0f, sf); - musx->stream_offset = read_u32(target_offset + 0x10, sf) + data_offset; - musx->stream_size = read_u32(target_offset + 0x14, sf); - musx->loop_start = read_s32(target_offset + 0x18, sf); + /* 0x00: subfile ID */ + musx->num_samples = read_s32(target_offset + 0x04, sf); + musx->loop_start_sample = read_s32(target_offset + 0x08, sf); /* -1 if no loop */ + musx->sample_rate = read_u16(target_offset + 0x0c, sf); + codec = read_u8 (target_offset + 0x0e, sf); + musx->channels = read_u8 (target_offset + 0x0f, sf); + musx->stream_offset = read_u32(target_offset + 0x10, sf) + data_offset; + musx->stream_size = read_u32(target_offset + 0x14, sf); + musx->loop_start = read_s32(target_offset + 0x18, sf); + } musx->loop_end_sample = musx->num_samples; musx->loop_flag = (musx->loop_start_sample >= 0); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index 3b5619281..541cea145 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -1393,10 +1393,8 @@ VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) { dspm.header_spacing = dspm.interleave; dspm.start_offset = dspm.header_offset + 0x60; - // -VGM_LOG("%lx, %x\n", dspm.header_offset, dspm.interleave); -VGM_LOG("%x, %x\n", dspm.interleave_first_skip, dspm.interleave_first); + dspm.meta_type = meta_DSP_WIIADPCM; return init_vgmstream_dsp_common(sf, &dspm); fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 97fa44349..23dc0aa7a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -150,6 +150,9 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk break; case 16: fmt->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; + /* broken block size [Rayman 2 (DC)] */ + if (fmt->block_size == 0x02 && fmt->channels > 1) + fmt->block_size = 0x02 * fmt->channels; break; case 8: fmt->coding_type = coding_PCM8_U; @@ -234,7 +237,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk //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 0x6770: /* Ogg Vorbis (mode 2+) [Only One (PC)]*/ case 0x6771: /* Ogg Vorbis (mode 3+) [Liar-soft games] */ /* vorbis.acm codecs (official-ish, "+" = CBR-style modes?) */ fmt->coding_type = coding_OGG_VORBIS; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c index 42dc35995..77e49d494 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c @@ -2,177 +2,64 @@ #include "../coding/coding.h" #include "../util.h" -/* Wii RWAV */ - -typedef struct { - // in - off_t offset; - STREAMFILE *sf; - int32_t (*read_32bit)(off_t,STREAMFILE*); - - // out - int version; - off_t start_offset; - off_t info_chunk; - off_t wave_offset; -} rwav_data_t; - -static void read_rwav(rwav_data_t* rd) { - off_t chunk_table_offset; - off_t chunk_table_step; - off_t info_chunk; - off_t data_chunk; - - if (!is_id32be(rd->offset, rd->sf, "RWAV")) - return; - - /* big endian, version 2 */ - if (read_u32be(rd->offset+4,rd->sf) != 0xFEFF0102) - return; - - chunk_table_offset = rd->offset + 0x10; - chunk_table_step = 0x08; - - info_chunk = rd->offset + rd->read_32bit(chunk_table_offset, rd->sf); - if (!is_id32be(info_chunk, rd->sf, "INFO")) - return; - - data_chunk = rd->offset + rd->read_32bit(chunk_table_offset + chunk_table_step, rd->sf); - if (!is_id32be(data_chunk, rd->sf, "DATA")) - return; - - rd->start_offset = data_chunk + 0x08; - rd->info_chunk = info_chunk + 0x08; - rd->version = 2; - rd->wave_offset = info_chunk - 0x08; /* pretend to have a WAVE */ - - return; -} - -static void read_rwar(rwav_data_t* rd) { - if (!is_id32be(rd->offset, rd->sf, "RWAR")) - return; - - if (read_u32be(rd->offset + 0x04, rd->sf) != 0xFEFF0100) /* version 0 */ - return; - - rd->offset += 0x60; - read_rwav(rd); - rd->version = 0; - return; -} - /* RWSD is quite similar to BRSTM, but can contain several streams. - * Still, some games use it for single streams. We only support the - * single stream form here */ + * Still, some games use it for single streams. We only support the single stream form here */ +//TODO this meta is a hack as WSD is just note info, and data offsets are elsewhere, +// while this assumes whatever data follows RWSD must belong to it; rework for Wii Sports VGMSTREAM* init_vgmstream_rwsd(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - char filename[PATH_LIMIT]; - size_t wave_length; - int codec; - int channels; - int loop_flag; - int rwar = 0; - int rwav = 0; - rwav_data_t rwav_data; - + int codec, channels, loop_flag; size_t stream_size; + off_t start_offset, wave_offset = 0; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - rwav_data.version = -1; - rwav_data.start_offset = 0; - rwav_data.info_chunk = -1; - rwav_data.wave_offset = -1; - - /* check extension, case insensitive */ - sf->get_name(sf,filename,sizeof(filename)); - - if (check_extensions(sf, "rwsd")) { - ; - } - else if (check_extensions(sf, "rwar")) { - rwar = 1; - } - else if (check_extensions(sf, "rwav")) { - rwav = 1; - } - else { + if (!is_id32be(0x00, sf, "RWSD")) goto fail; - } - - read_16bit = read_16bitBE; - read_32bit = read_32bitBE; + if (!check_extensions(sf, "rwsd")) + goto fail; /* check header */ - if (rwar || rwav) { - rwav_data.offset = 0; - rwav_data.sf = sf; - rwav_data.read_32bit = read_32bit; + switch (read_u32be(0x04, sf)) { + case 0xFEFF0102: + /* ideally we would look through the chunk list for a WAVE chunk, + * but it's always in the same order */ - if (rwar) read_rwar(&rwav_data); - if (rwav) read_rwav(&rwav_data); - if (rwav_data.wave_offset < 0) goto fail; - } - else { - if (!is_id32be(0x00, sf, "RWSD")) - goto fail; - - switch (read_u32be(0x04, sf)) { - case 0xFEFF0102: - /* ideally we would look through the chunk list for a WAVE chunk, - * but it's always in the same order */ - - /* get WAVE offset, check */ - rwav_data.wave_offset = read_32bit(0x18,sf); - if (!is_id32be(rwav_data.wave_offset + 0x00, sf, "WAVE")) - goto fail; - - /* get WAVE size, check */ - wave_length = read_32bit(0x1c,sf); - if (read_32bit(rwav_data.wave_offset + 0x04,sf) != wave_length) - goto fail; - - /* check wave count */ - if (read_32bit(rwav_data.wave_offset + 0x08,sf) != 1) - goto fail; /* only support 1 */ - - rwav_data.version = 2; - break; - - case 0xFEFF0103: - rwav_data.offset = 0xe0; - rwav_data.sf = sf; - rwav_data.read_32bit = read_32bit; - - read_rwar(&rwav_data); - if (rwav_data.wave_offset < 0) goto fail; - - rwar = 1; - break; - default: + /* get WAVE offset, check */ + wave_offset = read_32bitBE(0x18,sf); + if (!is_id32be(wave_offset + 0x00, sf, "WAVE")) goto fail; - } + /* get WAVE size, check */ + wave_length = read_32bitBE(0x1c,sf); + if (read_32bitBE(wave_offset + 0x04,sf) != wave_length) + goto fail; + + /* check wave count */ + if (read_32bitBE(wave_offset + 0x08,sf) != 1) + goto fail; /* only support 1 */ + + break; + + case 0xFEFF0103: /* followed by RWAR, extract that or use .txth subfile */ + goto fail; } /* get type details */ - codec = read_u8(rwav_data.wave_offset+0x10,sf); - loop_flag = read_u8(rwav_data.wave_offset+0x11,sf); - channels = read_u8(rwav_data.wave_offset+0x12,sf); + codec = read_u8(wave_offset+0x10,sf); + loop_flag = read_u8(wave_offset+0x11,sf); + channels = read_u8(wave_offset+0x12,sf); /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channels,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = dsp_nibbles_to_samples(read_32bit(rwav_data.wave_offset+0x1c,sf)); - vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bit(rwav_data.wave_offset+0x18,sf)); + vgmstream->num_samples = dsp_nibbles_to_samples(read_32bitBE(wave_offset+0x1c,sf)); + vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bitBE(wave_offset+0x18,sf)); - vgmstream->sample_rate = (uint16_t)read_16bit(rwav_data.wave_offset + 0x14,sf); + vgmstream->sample_rate = (uint16_t)read_16bitBE(wave_offset + 0x14,sf); vgmstream->loop_end_sample = vgmstream->num_samples; switch (codec) { @@ -190,84 +77,48 @@ VGMSTREAM* init_vgmstream_rwsd(STREAMFILE* sf) { } vgmstream->layout_type = layout_none; - - if (rwar) { - vgmstream->meta_type = meta_RWAR; - } - else if (rwav) { - vgmstream->meta_type = meta_RWAV; - } - else { - vgmstream->meta_type = meta_RWSD; - } + vgmstream->meta_type = meta_RWSD; { - off_t data_start_offset; off_t codec_info_offset; int i, j; for (j = 0 ; j < vgmstream->channels; j++) { - if (rwar || rwav) { - /* This is pretty nasty, so an explaination is in order. - * At 0x10 in the info_chunk is the offset of a table with - * one entry per channel. Each entry in this table is itself - * an offset to a set of information for the channel. The - * first element in the set is the offset into DATA of the channel. - * The second element is the offset of the codec-specific setup for the channel. */ - - off_t channel_info_offset = rwav_data.info_chunk + - read_32bit(rwav_data.info_chunk + - read_32bit(rwav_data.info_chunk + 0x10,sf) + j*0x04, sf); - - data_start_offset = rwav_data.start_offset + - read_32bit(channel_info_offset + 0x00, sf); - codec_info_offset = rwav_data.info_chunk + - read_32bit(channel_info_offset + 0x04, sf); - - vgmstream->ch[j].channel_start_offset = - vgmstream->ch[j].offset = data_start_offset; - - } else { - // dummy for RWSD, must be a proper way to work this out - codec_info_offset = rwav_data.wave_offset + 0x6c + j*0x30; - } + // dummy for RWSD, must be a proper way to work this out + codec_info_offset = wave_offset + 0x6c + j*0x30; if (vgmstream->coding_type == coding_NGC_DSP) { for (i = 0; i < 16; i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bit(codec_info_offset + i*0x2, sf); + vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(codec_info_offset + i*0x2, sf); } } } } - if (rwar || rwav) { - /* */ - } - else { - if (rwav_data.version == 2) - rwav_data.start_offset = read_32bit(0x08, sf); - } + + /* this is just data size and following data may or may not be from this RWSD */ + start_offset = read_32bitBE(0x08, sf); - stream_size = read_32bit(rwav_data.wave_offset + 0x50,sf); + stream_size = read_32bitBE(wave_offset + 0x50,sf); /* open the file for reading by each channel */ { int i; - for (i=0;iget_name(sf,filename,sizeof(filename)); + + for (i = 0; i < channels; i++) { vgmstream->ch[i].streamfile = sf->open(sf,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[i].streamfile) goto fail; - if (!(rwar || rwav)) { - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - rwav_data.start_offset + i*stream_size; - } + vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = + start_offset + i*stream_size; } } return vgmstream; - fail: close_vgmstream(vgmstream); return NULL; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 8c54b65a1..8f746f00a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -1062,6 +1062,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha /* apply */ if (!txth->data_size_set) { + //TODO: this doesn't work when using name_table + subsongs, since values are pre-read /* with subsongs we want to clamp data_size from this subsong start to next subsong start */ txth->next_offset = txth->data_size; if (txth->subsong_count > 1 && txth->target_subsong < txth->subsong_count) { @@ -1205,12 +1206,9 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha else if (is_string(key,"coef_offset")) { if (!parse_num(txth->sf_head,txth,val, &txth->coef_offset)) goto fail; /* special adjustments */ - VGM_LOG("coef norm=%x\n",txth->coef_offset ); txth->coef_offset += txth->base_offset; - VGM_LOG("coef+base=%x\n",txth->coef_offset ); if (txth->subsong_spacing && !txth->is_offset_absolute) txth->coef_offset += txth->subsong_spacing * (txth->target_subsong - 1); - VGM_LOG("coef+spac=%x\n",txth->coef_offset ); } else if (is_string(key,"coef_spacing")) { if (!parse_num(txth->sf_head,txth,val, &txth->coef_spacing)) goto fail; @@ -1922,6 +1920,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_ else if ((n = is_string_field(val,"sample_rate"))) value = txth->sample_rate; else if ((n = is_string_field(val,"start_offset"))) value = txth->start_offset; else if ((n = is_string_field(val,"data_size"))) value = txth->data_size; + else if ((n = is_string_field(val,"padding_size"))) value = txth->padding_size; else if ((n = is_string_field(val,"num_samples"))) value = txth->num_samples; else if ((n = is_string_field(val,"loop_start_sample"))) value = txth->loop_start_sample; else if ((n = is_string_field(val,"loop_start"))) value = txth->loop_start_sample; @@ -1935,7 +1934,16 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_ else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size; else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset; else if ((n = is_string_field(val,"coef_offset"))) value = txth->coef_offset; + else if ((n = is_string_field(val,"coef_spacing"))) value = txth->coef_spacing; else if ((n = is_string_field(val,"hist_offset"))) value = txth->hist_offset; + else if ((n = is_string_field(val,"hist_spacing"))) value = txth->hist_spacing; + else if ((n = is_string_field(val,"chunk_count"))) value = txth->chunk_count; + else if ((n = is_string_field(val,"chunk_start"))) value = txth->chunk_start; + else if ((n = is_string_field(val,"chunk_size"))) value = txth->chunk_size; + else if ((n = is_string_field(val,"chunk_size_offset"))) value = txth->chunk_size_offset; + else if ((n = is_string_field(val,"chunk_number"))) value = txth->chunk_number; + else if ((n = is_string_field(val,"chunk_data_size"))) value = txth->chunk_data_size; + else if ((n = is_string_field(val,"chunk_header_size"))) value = txth->chunk_header_size; //todo whatever, improve else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0]; else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0]; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index d5cbf394d..04afbe876 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -3,6 +3,8 @@ #include "../coding/coding.h" #include "ubi_bao_streamfile.h" +#define BAO_MIN_VERSION 0x1B +#define BAO_MAX_VERSION 0x2A #define BAO_MAX_LAYER_COUNT 16 /* arbitrary max */ #define BAO_MAX_CHAIN_COUNT 128 /* POP:TFS goes up to ~100 */ @@ -145,6 +147,11 @@ VGMSTREAM* init_vgmstream_ubi_bao_pk(STREAMFILE* sf) { ubi_bao_header bao = { 0 }; /* checks */ + if (read_u8(0x00, sf) != 0x01) + goto fail; + if (read_u8(0x01, sf) < BAO_MIN_VERSION || read_u8(0x01, sf) > BAO_MAX_VERSION) + goto fail; + if (!check_extensions(sf, "pk,lpk,cpk")) goto fail; @@ -161,12 +168,17 @@ fail: return NULL; } -/* .BAO - single BAO files from Ubisoft's sound engine ("DARE") games in 2008+ */ +/* .BAO - single BAO files from Ubisoft's sound engine ("DARE") games in 2007+ */ VGMSTREAM* init_vgmstream_ubi_bao_atomic(STREAMFILE* sf) { ubi_bao_header bao = { 0 }; STREAMFILE* streamData = NULL; /* checks */ + if (read_u8(0x00, sf) != 0x01 && read_u8(0x00, sf) != 0x02) /* 0x01=AC1, 0x02=POP2008 */ + goto fail; + if (read_u8(0x01, sf) < BAO_MIN_VERSION || read_u8(0x01, sf) > BAO_MAX_VERSION) + goto fail; + if (!check_extensions(sf, "bao,")) goto fail; @@ -174,13 +186,9 @@ VGMSTREAM* init_vgmstream_ubi_bao_atomic(STREAMFILE* sf) { * since BAOs reference each other by id and are named by it (though the internal BAO id may * be other) we can simulate it. Extension is .bao/sbao or extensionaless in some games. */ - /* format: 0x01=AC1, 0x02=POP2008 */ - if (read_8bit(0x00, sf) != 0x01 && read_8bit(0x00, sf) != 0x02) - goto fail; - bao.is_atomic = 1; - bao.version = read_32bitBE(0x00, sf) & 0x00FFFFFF; + bao.version = read_u32be(0x00, sf) & 0x00FFFFFF; if (!config_bao_version(&bao, sf)) goto fail; @@ -644,7 +652,7 @@ static VGMSTREAM* init_vgmstream_ubi_bao_header(ubi_bao_header* bao, STREAMFILE* (uint32_t)bao->header_offset, bao->header_id, bao->stream_id, bao->prefetch_id); ;VGM_LOG("UBI BAO: stream=%x, size=%x, res=%s\n", (uint32_t)bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal")); - ;VGM_LOG("UBI BAO: type=%i, header=%x, extra=%x, prefetch=%x, size=%x\n", + ;VGM_LOG("UBI BAO: type=%i, header=%x, extra=%x, pre.of=%x, pre.sz=%x\n", bao->header_type, bao->header_size, bao->extra_size, (uint32_t)bao->prefetch_offset, bao->prefetch_size); @@ -703,8 +711,8 @@ static int parse_pk(ubi_bao_header* bao, STREAMFILE* sf) { if (target_subsong <= 0) target_subsong = 1; - bao->version = read_32bitBE(0x00, sf) & 0x00FFFFFF; - index_size = read_32bitLE(0x04, sf); /* can be 0, not including */ + bao->version = read_u32be(0x00, sf) & 0x00FFFFFF; + index_size = read_u32le(0x04, sf); /* can be 0, not including */ /* 0x08: resource table offset, always found even if not used */ /* 0x0c: always 0? */ /* 0x10: unknown, null if no entries */ @@ -1125,11 +1133,12 @@ static int parse_offsets(ubi_bao_header* bao, STREAMFILE* sf) { read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, sf); if (bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size) { - VGM_LOG("UBI BAO: stream vs resource size mismatch at %lx (%x vs %x, %x, %x)\n", offset+0x10*i, bao->stream_size, resource_size, bao->stream_skip, bao->prefetch_size); + VGM_LOG("UBI BAO: stream vs resource size mismatch at %lx (res %x vs str=%x, skip=%x, pre=%x)\n", offset+0x10*i, resource_size, bao->stream_size, bao->stream_skip, bao->prefetch_size); /* rarely resource has more data than stream (sometimes a few bytes, others +0x100000) - * sometimes short song versions, but not accessed? no samples/sizes/cues/etc in header seem to refer to that [Just Dance (Wii)] */ - if (!bao->cfg.audio_ignore_resource_size || bao->prefetch_size) + * sometimes short song versions, but not accessed? no samples/sizes/cues/etc in header seem to refer to that [Just Dance (Wii)] + * Michael Jackson The Experience also uses prefetch size + bad size (ignored) */ + if (!bao->cfg.audio_ignore_resource_size && bao->prefetch_size) goto fail; } break; @@ -1350,6 +1359,22 @@ static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int if (sf_bao) return sf_bao; } } + + /* If all else fails, try %08x.bao/%08x.sbao nomenclature. + * (id).bao is for mimicking engine loading files by internal ID, + * original names (like Common_BAO_0x5NNNNNNN, French_BAO_0x5NNNNNNN and the like) are OK too. */ + if (file_type != UBI_FORGE_b) { + /* %08x.bao nomenclature present in Assassin's Creed (Windows Vista) exe. */ + snprintf(buf,buf_size, "%08x.bao", file_id); + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; + } + else { + /* %08x.sbao nomenclature (in addition to %08x.bao) present in Shaun White Snowboarding (Windows Vista) exe. */ + snprintf(buf,buf_size, "%08x.sbao", file_id); + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; + } } else { snprintf(buf,buf_size, "BAO_0x%08x", file_id); @@ -1359,6 +1384,11 @@ static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int strcat(buf,".bao"); sf_bao = open_streamfile_by_filename(sf, buf); if (sf_bao) return sf_bao; + + /* Ditto. */ + snprintf(buf,buf_size, "%08x.bao", file_id); + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; } goto fail; @@ -1853,6 +1883,23 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) { bao->cfg.codec_map[0x06] = RAW_AT3_105; bao->cfg.file_type = UBI_FORGE_b; + + return 1; + + case 0x00260000: /* Michael Jackson: The Experience (X360)-package */ + config_bao_entry(bao, 0xB8, 0x28); + + config_bao_audio_b(bao, 0x08, 0x28, 0x30, 0x3c, 1, 1); //loop? + config_bao_audio_m(bao, 0x4c, 0x54, 0x5c, 0x64, 0x6c, 0x7c); + + config_bao_layer_m(bao, 0x00, 0x2c, 0x34, 0x4c, 0x54, 0x58, 0x00, 0x00, 1); + config_bao_layer_e(bao, 0x34, 0x00, 0x04, 0x08, 0x1c); + + bao->cfg.codec_map[0x03] = FMT_OGG; + bao->cfg.codec_map[0x04] = RAW_XMA2_NEW; + + bao->cfg.audio_ignore_resource_size = 1; /* leave_me_alone.pk */ + return 1; case 0x00270102: /* Drawsome (Wii)-package */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wbk.c b/Frameworks/vgmstream/vgmstream/src/meta/wbk.c index 4b8cf0c9d..cba32ac90 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wbk.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wbk.c @@ -170,7 +170,7 @@ VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf) { goto fail; /* always little endian, even on PS3/X360 */ - if (read_u32le(0x04, sf) != 0x01) + if (read_u16le(0x04, sf) != 0x01) goto fail; total_subsongs = read_u32le(0x10, sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 0ee26df6e..093ff448f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -410,10 +410,21 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) { vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + //if (ww.prefetch) { + // ww.data_size = ww.file_size - ww.data_offset; + //} + /* seek table seems BE dpds */ vgmstream->num_samples = xwma_dpds_get_samples(sf, ww.seek_offset, ww.seek_size, ww.channels, ww.big_endian); if (!vgmstream->num_samples) vgmstream->num_samples = xwma_get_samples(sf, ww.data_offset, ww.data_size, ww.format, ww.channels, ww.sample_rate, ww.block_size); + + /* XWMA is VBR so this is very approximate percent, meh */ + if (ww.prefetch) { /* Guardians of Middle Earth (X360) */ + vgmstream->num_samples = (int32_t)(vgmstream->num_samples * + (double)(ww.file_size - start_offset) / (double)ww.data_size); + } + break; } @@ -921,7 +932,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { } if (ww->codec == PCM || ww->codec == IMA || ww->codec == VORBIS || ww->codec == DSP || ww->codec == XMA2 || - ww->codec == OPUSNX || ww->codec == OPUS || ww->codec == OPUSWW || ww->codec == PTADPCM) { + ww->codec == OPUSNX || ww->codec == OPUS || ww->codec == OPUSWW || ww->codec == PTADPCM || ww->codec == XWMA) { ww->prefetch = 1; /* only seen those, probably all exist (XWMA, AAC, HEVAG, ATRAC9?) */ } else { vgm_logi("WWISE: wrong expected size, maybe prefetch (report)\n"); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xa_04sw.c b/Frameworks/vgmstream/vgmstream/src/meta/xa_04sw.c index 3db1a6923..8ec30a98e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xa_04sw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xa_04sw.c @@ -1,58 +1,64 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* 04SW - found in Driver: Parallel Lines (Wii) */ -VGMSTREAM * init_vgmstream_xa_04sw(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - size_t file_size, data_size; - - - /* checks */ - /* ".04sw" is just the ID, the real filename inside the file uses .XA */ - if (!check_extensions(streamFile,"xa,04sw")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x30345357) /* "04SW" */ - goto fail; - - /* after the ID goes a semi-standard DSP header */ - if (read_32bitBE(0x10,streamFile) != 0) goto fail; /* should be non looping */ - loop_flag = 0; - /* not in header it seems so just dual header check */ - channel_count = (read_32bitBE(0x04,streamFile) == read_32bitBE(0x64,streamFile)) ? 2 : 1; - - start_offset = read_32bitBE(0x04 + 0x60*channel_count,streamFile); - - file_size = get_streamfile_size(streamFile); - data_size = read_32bitBE(0x04 + 0x60*channel_count + 0x04,streamFile); - if (data_size+start_offset != file_size) goto fail; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitBE(0x0c,streamFile); - vgmstream->num_samples = read_32bitBE(0x04,streamFile); - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; - vgmstream->interleave_block_size = 0x8000; - vgmstream->interleave_last_block_size = (read_32bitBE(0x08,streamFile) / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; - - dsp_read_coefs_be(vgmstream,streamFile,0x20, 0x60); - /* the initial history offset seems different thatn standard DSP and possibly always zero */ - - vgmstream->meta_type = meta_XA_04SW; - /* the rest of the header has unknown values (several repeats) and the filename */ - - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* 04SW - Reflections games [Driver: Parallel Lines (Wii), Emergency Heroes (Wii)] */ +VGMSTREAM* init_vgmstream_xa_04sw(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, sample_rate; + int32_t num_samples; + uint32_t data_size; + + + /* checks */ + if (!is_id32be(0x00,sf, "04SW")) + goto fail; + + if (!check_extensions(sf,"xa")) + goto fail; + + /* after the ID goes a modified DSP header x2 */ + if (read_u32be(0x04 + 0x0c,sf) != 0) /* should be non looping */ + goto fail; + loop_flag = 0; + /* not in header it seems, so just dual header check */ + + num_samples = read_s32be(0x04 + 0x00,sf); + data_size = read_u32be(0x04 + 0x04,sf); + sample_rate = read_u32be(0x0c,sf); + + channels = (read_u32be(0x04 + 0x00,sf) == read_u32be(0x04 + 0x60,sf)) ? 2 : 1; /* some voice .xa */ + + /* After DSP header goes a base header with mostly unknown values (several repeats) and the filename. + * Emergency Heroes has extra 0x10 at 0x10. */ + start_offset = read_u32be(0x04 + 0x60 * 2 + 0x00, sf); + /* 0x04: data size (includes padding after DSP data in Driver, doesn't in Emergency Heroes */ + /* 0x1c/2c: channels LE? */ + /* 0x74/84: utf-16 path+filename */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = channels == 1 ? layout_none : layout_interleave; + vgmstream->interleave_block_size = 0x8000; + vgmstream->interleave_last_block_size = (data_size / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; + + dsp_read_coefs_be(vgmstream, sf, 0x04 + 0x1c, 0x60); + /* initial history offset seems different than standard DSP and possibly fixed/invalid */ + + vgmstream->meta_type = meta_XA_04SW; + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index f54d7ab99..faa3b1dbe 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -20,8 +20,10 @@ static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, VG VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_adx, init_vgmstream_brstm, + init_vgmstream_brwav, init_vgmstream_bfwav, init_vgmstream_bcwav, + init_vgmstream_brwar, init_vgmstream_nds_strm, init_vgmstream_afc, init_vgmstream_ast, @@ -519,6 +521,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_ubi_ckd_cwav, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ + init_vgmstream_mpeg, init_vgmstream_agsc, init_vgmstream_dtk, init_vgmstream_rsf, diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index b56bb783c..6b16ca2f6 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -762,6 +762,7 @@ typedef enum { meta_WBK, meta_WBK_NSLB, meta_DSP_APEX, + meta_MPEG, } meta_t; diff --git a/Info.plist b/Info.plist index bdbc2d07e..825204de1 100644 --- a/Info.plist +++ b/Info.plist @@ -82,7 +82,6 @@ CFBundleTypeExtensions - 04sw 208 2dx9 2pfs @@ -645,6 +644,7 @@ xma xma2 xmu + xmv xnb xsh xsf