From 05de1e50334a0ffbaf76e5c313e147349964d0c1 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Wed, 23 Jan 2019 17:49:59 -0800 Subject: [PATCH] Updated VMStream to r1050-2028-gac28ccc5 --- .../libvgmstream.xcodeproj/project.pbxproj | 20 +- .../vgmstream/vgmstream/src/coding/coding.h | 11 +- .../vgmstream/src/coding/coding_utils.c | 9 +- .../vgmstream/src/coding/ea_xas_decoder.c | 65 +- .../coding/{pcfx_decoder.c => oki_decoder.c} | 61 +- .../vgmstream/src/coding/pcm_decoder.c | 48 +- Frameworks/vgmstream/vgmstream/src/formats.c | 21 +- Frameworks/vgmstream/vgmstream/src/meta/asf.c | 4 +- Frameworks/vgmstream/vgmstream/src/meta/baf.c | 206 +- .../vgmstream/vgmstream/src/meta/bnk_sony.c | 120 +- .../vgmstream/vgmstream/src/meta/ea_1snh.c | 5 +- .../vgmstream/vgmstream/src/meta/ea_eaac.c | 320 +- .../vgmstream/vgmstream/src/meta/ea_schl.c | 159 +- .../vgmstream/src/meta/ea_schl_fixed.c | 6 +- Frameworks/vgmstream/vgmstream/src/meta/fag.c | 2 +- .../vgmstream/vgmstream/src/meta/genh.c | 79 +- Frameworks/vgmstream/vgmstream/src/meta/gin.c | 53 + .../vgmstream/vgmstream/src/meta/meta.h | 11 +- .../vgmstream/vgmstream/src/meta/ps2_ads.c | 8 +- .../vgmstream/vgmstream/src/meta/ps2_khv.c | 65 - .../vgmstream/vgmstream/src/meta/ps2_mic.c | 81 +- .../vgmstream/vgmstream/src/meta/riff.c | 65 +- Frameworks/vgmstream/vgmstream/src/meta/sd9.c | 98 +- .../vgmstream/vgmstream/src/meta/txth.c | 110 +- .../vgmstream/vgmstream/src/meta/ubi_bao.c | 47 +- .../vgmstream/vgmstream/src/meta/ubi_sb.c | 2742 +++++++++-------- .../vgmstream/src/meta/ubi_sb_streamfile.h | 363 +++ Frameworks/vgmstream/vgmstream/src/meta/vag.c | 21 +- Frameworks/vgmstream/vgmstream/src/meta/xnb.c | 3 +- .../vgmstream/vgmstream/src/vgmstream.c | 80 +- .../vgmstream/vgmstream/src/vgmstream.h | 9 +- 31 files changed, 3156 insertions(+), 1736 deletions(-) rename Frameworks/vgmstream/vgmstream/src/coding/{pcfx_decoder.c => oki_decoder.c} (55%) create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/gin.c delete mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ps2_khv.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_streamfile.h diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 8d676dc08..4053d49c2 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -93,7 +93,6 @@ 83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; }; 832BF7FF21E050B7006F50F1 /* circus_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FC21E050B6006F50F1 /* circus_decoder.c */; }; 832BF80021E050B7006F50F1 /* mpeg_custom_utils_eamp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */; }; - 832BF80121E050B7006F50F1 /* pcfx_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */; }; 832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80221E050DB006F50F1 /* blocked_mul.c */; }; 832BF80621E050DC006F50F1 /* blocked_vs_square.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80321E050DC006F50F1 /* blocked_vs_square.c */; }; 832BF80721E050DC006F50F1 /* blocked_vs_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80421E050DC006F50F1 /* blocked_vs_str.c */; }; @@ -349,7 +348,6 @@ 836F6FE018BDC2190095E648 /* ps2_joe.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA418BDC2180095E648 /* ps2_joe.c */; }; 836F6FE118BDC2190095E648 /* ps2_jstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA518BDC2180095E648 /* ps2_jstm.c */; }; 836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA618BDC2180095E648 /* ps2_kces.c */; }; - 836F6FE318BDC2190095E648 /* ps2_khv.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA718BDC2180095E648 /* ps2_khv.c */; }; 836F6FE418BDC2190095E648 /* ps2_leg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA818BDC2180095E648 /* ps2_leg.c */; }; 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA918BDC2180095E648 /* ps2_lpcm.c */; }; 836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAA18BDC2180095E648 /* ps2_mcg.c */; }; @@ -452,6 +450,9 @@ 83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E031ECBC1A4005C03D3 /* ta_aac.c */; }; 83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */; }; 83709E0E1ECBC1C3005C03D3 /* psv_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */; }; + 8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8375737221F9507D00F01AF5 /* oki_decoder.c */; }; + 8375737621F950ED00F01AF5 /* gin.c in Sources */ = {isa = PBXBuildFile; fileRef = 8375737421F950EC00F01AF5 /* gin.c */; }; + 8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */; }; 838BDB641D3AF08C0022CA6F /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB611D3AF08C0022CA6F /* libavcodec.a */; }; 838BDB651D3AF08C0022CA6F /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB621D3AF08C0022CA6F /* libavformat.a */; }; 838BDB661D3AF08C0022CA6F /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB631D3AF08C0022CA6F /* libavutil.a */; }; @@ -733,7 +734,6 @@ 83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = ""; }; 832BF7FC21E050B6006F50F1 /* circus_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = circus_decoder.c; sourceTree = ""; }; 832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_eamp3.c; sourceTree = ""; }; - 832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcfx_decoder.c; sourceTree = ""; }; 832BF80221E050DB006F50F1 /* blocked_mul.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_mul.c; sourceTree = ""; }; 832BF80321E050DC006F50F1 /* blocked_vs_square.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vs_square.c; sourceTree = ""; }; 832BF80421E050DC006F50F1 /* blocked_vs_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vs_str.c; sourceTree = ""; }; @@ -989,7 +989,6 @@ 836F6EA418BDC2180095E648 /* ps2_joe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_joe.c; sourceTree = ""; }; 836F6EA518BDC2180095E648 /* ps2_jstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_jstm.c; sourceTree = ""; }; 836F6EA618BDC2180095E648 /* ps2_kces.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_kces.c; sourceTree = ""; }; - 836F6EA718BDC2180095E648 /* ps2_khv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_khv.c; sourceTree = ""; }; 836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = ""; }; 836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = ""; }; 836F6EAA18BDC2180095E648 /* ps2_mcg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mcg.c; sourceTree = ""; }; @@ -1092,6 +1091,9 @@ 83709E031ECBC1A4005C03D3 /* ta_aac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ta_aac.c; sourceTree = ""; }; 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mc3_decoder.c; sourceTree = ""; }; 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psv_decoder.c; sourceTree = ""; }; + 8375737221F9507D00F01AF5 /* oki_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = oki_decoder.c; sourceTree = ""; }; + 8375737421F950EC00F01AF5 /* gin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gin.c; sourceTree = ""; }; + 8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_sb_streamfile.h; sourceTree = ""; }; 838BDB611D3AF08C0022CA6F /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = ""; }; 838BDB621D3AF08C0022CA6F /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = ""; }; 838BDB631D3AF08C0022CA6F /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = ""; }; @@ -1357,7 +1359,7 @@ 836F6DF618BDC2180095E648 /* nwa_decoder.c */, 836F6DF718BDC2180095E648 /* nwa_decoder.h */, 836F6DF818BDC2180095E648 /* ogg_vorbis_decoder.c */, - 832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */, + 8375737221F9507D00F01AF5 /* oki_decoder.c */, 836F6DF918BDC2180095E648 /* pcm_decoder.c */, 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */, 836F6DFA18BDC2180095E648 /* psx_decoder.c */, @@ -1516,6 +1518,7 @@ 836F6E4D18BDC2180095E648 /* gca.c */, 836F6E4E18BDC2180095E648 /* gcsw.c */, 836F6E4F18BDC2180095E648 /* genh.c */, + 8375737421F950EC00F01AF5 /* gin.c */, 836F6E5118BDC2180095E648 /* gsp_gsb.c */, 83709DFF1ECBC1A4005C03D3 /* gtd.c */, 8342469020C4D22F00926E48 /* h4m.c */, @@ -1639,7 +1642,6 @@ 836F6EA418BDC2180095E648 /* ps2_joe.c */, 836F6EA518BDC2180095E648 /* ps2_jstm.c */, 836F6EA618BDC2180095E648 /* ps2_kces.c */, - 836F6EA718BDC2180095E648 /* ps2_khv.c */, 836F6EA818BDC2180095E648 /* ps2_leg.c */, 836F6EA918BDC2180095E648 /* ps2_lpcm.c */, 836F6EAA18BDC2180095E648 /* ps2_mcg.c */, @@ -1739,6 +1741,7 @@ 8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */, 8306B0CA2098458E000302D4 /* ubi_lyn.c */, 831BA6131EAC61A500CF89B0 /* ubi_raki.c */, + 8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */, 8349A8F41FE6257D00E26435 /* ubi_sb.c */, 834FE0C5215C79E6000A5D3D /* ue4opus.c */, 834FE0DD215C79EB000A5D3D /* utk.c */, @@ -1852,6 +1855,7 @@ 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */, 832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */, + 8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */, 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */, 836F705418BDC2190095E648 /* streamfile.h in Headers */, 83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */, @@ -2279,7 +2283,6 @@ 836F6FAF18BDC2190095E648 /* ngc_dsp_std.c in Sources */, 836F6F7118BDC2190095E648 /* ast.c in Sources */, 834FE0BF215C79A9000A5D3D /* flat.c in Sources */, - 836F6FE318BDC2190095E648 /* ps2_khv.c in Sources */, 836F6F6B18BDC2190095E648 /* agsc.c in Sources */, 836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */, 836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */, @@ -2288,6 +2291,7 @@ 836F702118BDC2190095E648 /* rs03.c in Sources */, 836F6F8818BDC2190095E648 /* fsb.c in Sources */, 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */, + 8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */, 836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */, 836F6FC018BDC2190095E648 /* p3d.c in Sources */, 836F6FC718BDC2190095E648 /* pona.c in Sources */, @@ -2301,7 +2305,6 @@ 834FE106215C79ED000A5D3D /* utk.c in Sources */, 831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */, 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */, - 832BF80121E050B7006F50F1 /* pcfx_decoder.c in Sources */, 83AA5D271F6E2F9C0020821C /* stm.c in Sources */, 831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */, 8306B0A520984552000302D4 /* blocked_ea_wve_au00.c in Sources */, @@ -2411,6 +2414,7 @@ 836F6FB118BDC2190095E648 /* ngc_ffcc_str.c in Sources */, 8306B0B620984552000302D4 /* blocked_hwas.c in Sources */, 836F6FC218BDC2190095E648 /* pc_mxst.c in Sources */, + 8375737621F950ED00F01AF5 /* gin.c in Sources */, 836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */, 836F704818BDC2190095E648 /* xbox_ims.c in Sources */, 836F6F7518BDC2190095E648 /* bnsf.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 676a313f8..fdf3fa0f1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -71,6 +71,8 @@ void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -99,7 +101,8 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* ea_xas_decoder */ -void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* sdx2_decoder */ void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -168,9 +171,10 @@ void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, /* circus_decoder */ void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -/* pcfx_decoder */ +/* oki_decoder */ void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode); -size_t pcfx_bytes_to_samples(size_t bytes, int channels); +void decode_oki16(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +size_t oki_bytes_to_samples(size_t bytes, int channels); /* ea_mt_decoder*/ ea_mt_codec_data *init_ea_mt(int channels, int type); @@ -339,6 +343,7 @@ void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample); void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); +void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, int channel_per_stream, int fix_num_samples, int fix_loop_samples); int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c index 8b601ccd2..2b464a359 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c @@ -799,14 +799,13 @@ void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_ali /* XMA hell for precise looping and gapless support, fixes raw sample values from headers * that don't count XMA's final subframe/encoder delay/encoder padding, and FFmpeg stuff. * Configurable since different headers vary for maximum annoyance. */ -void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) { +void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, int channels_per_stream, int fix_num_samples, int fix_loop_samples) { const int bytes_per_packet = 2048; const int samples_per_frame = 512; const int samples_per_subframe = 128; const int bits_frame_size = 15; int xma_version = 2; /* works ok even for XMA1 */ - int channels_per_stream = xma_get_channels_per_stream(streamFile, chunk_offset, vgmstream->channels); off_t first_packet = stream_offset; off_t last_packet = stream_offset + stream_size - bytes_per_packet; int32_t start_skip = 0, end_skip = 0; @@ -872,6 +871,12 @@ void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stre #endif } +void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) { + int channels_per_stream = xma_get_channels_per_stream(streamFile, chunk_offset, vgmstream->channels); + xma_fix_raw_samples_ch(vgmstream, streamFile, stream_offset, stream_size, channels_per_stream, fix_num_samples, fix_loop_samples); +} + + /* ******************************************** */ /* HEADER PARSING */ /* ******************************************** */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c index 148bfff88..e28c6c8c2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c @@ -9,11 +9,11 @@ static const int EA_XA_TABLE[20] = { 0, -1, -3, -4 }; -/* EA-XAS, evolution of EA-XA and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding) +/* EA-XAS v1, evolution of EA-XA/XAS and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding) * * Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?). * To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */ -void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int group, row, i; int samples_done = 0, sample_count = 0; @@ -77,3 +77,64 @@ void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin stream->offset += 0x4c * channelspacing; } } + + +/* EA-XAS v0, without complex layouts and closer to EA-XA. Somewhat based on daemon1's decoder */ +void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + off_t frame_offset; + int i; + int block_samples, frames_in, samples_done = 0, sample_count = 0; + + /* external interleave (fixed size), mono */ + block_samples = 32; + frames_in = first_sample / block_samples; + first_sample = first_sample % block_samples; + + frame_offset = stream->offset + (0x0f+0x02+0x02)*frames_in; + + /* process frames */ + { + int coef1, coef2; + int16_t hist1, hist2; + uint8_t shift; + uint32_t frame_header = (uint32_t)read_32bitLE(frame_offset, stream->streamfile); /* always LE */ + + coef1 = EA_XA_TABLE[(uint8_t)(frame_header & 0x0F) + 0]; + coef2 = EA_XA_TABLE[(uint8_t)(frame_header & 0x0F) + 4]; + hist2 = (int16_t)(frame_header & 0xFFF0); + hist1 = (int16_t)((frame_header >> 16) & 0xFFF0); + shift = 20 - ((frame_header >> 16) & 0x0F); + + /* write header samples (needed) */ + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist2; + samples_done++; + } + sample_count++; + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist1; + samples_done++; + } + sample_count++; + + /* process nibbles */ + for (i = 0; i < 0x0f*2; i++) { + uint8_t sample_byte = (uint8_t)read_8bit(frame_offset + 0x02 + 0x02 + i/2, stream->streamfile); + int sample; + + sample = get_nibble_signed(sample_byte, !(i&1)); /* upper first */ + sample = sample << shift; + sample = (sample + hist1 * coef1 + hist2 * coef2 + 128) >> 8; + sample = clamp16(sample); + + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = sample; + samples_done++; + } + sample_count++; + + hist2 = hist1; + hist1 = sample; + } + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/pcfx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c similarity index 55% rename from Frameworks/vgmstream/vgmstream/src/coding/pcfx_decoder.c rename to Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c index 63cac8562..9f39566aa 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/pcfx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c @@ -1,14 +1,14 @@ #include "coding.h" -static const int step_sizes[49] = { /* OKI table */ +static const int step_sizes[49] = { /* OKI table (subsection of IMA's table) */ 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 }; -static const int stex_indexes[16] = { /* IMA table */ +static const int stex_indexes[16] = { /* OKI table (also from IMA) */ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; @@ -54,6 +54,26 @@ static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int } } +static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) { + int code, step, delta; + + code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; + step = step_sizes[*step_index]; + + delta = (code & 0x7); + delta = (((delta * 2) + 1) * step) >> 3; /* IMA 'mul' style (standard OKI uses 'shift-add') */ + if (code & 0x8) + delta = -delta; + *hist1 += delta; + + /* standard OKI clamps hist to 2047,-2048 here */ + + *step_index += stex_indexes[code]; + if (*step_index < 0) *step_index = 0; + if (*step_index > 48) *step_index = 48; + + *out_sample = *hist1; +} /* PC-FX ADPCM decoding, variation of OKI/Dialogic/VOX ADPCM. Based on mednafen/pcfx-music-dump. * Apparently most ADPCM was made with a buggy encoder, resulting in incorrect sound in real hardware @@ -65,7 +85,7 @@ static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int * * PC-FX ISOs don't have a standard filesystem nor file formats (raw data must be custom-ripped), * so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where - * base_value is approximately ~31468.5 (follows hardware clocks), mono or stereo-interleaved. + * base_value is approximately ~31468.5 (follows hardware clocks), mono or interleaved for stereo. */ void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) { int i, sample_count = 0; @@ -86,7 +106,40 @@ void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, stream->adpcm_step_index = step_index; } -size_t pcfx_bytes_to_samples(size_t bytes, int channels) { +/* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan). + * Reverse engineered from the ELF with help from the folks at hcs. */ +void decode_oki16(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + int16_t out_sample; + int is_stereo = channelspacing > 1; + + + /* external interleave */ + + /* no header (external setup), pre-clamp for wrong values */ + if (step_index < 0) step_index=0; + if (step_index > 48) step_index=48; + + /* decode nibbles (layout: varies) */ + for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { + off_t byte_offset = is_stereo ? + stream->offset + i : /* stereo: one nibble per channel */ + stream->offset + i/2; /* mono: consecutive nibbles (assumed) */ + int nibble_shift = + is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */ + + oki16_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample); + outbuf[sample_count] = (out_sample); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + + +size_t oki_bytes_to_samples(size_t bytes, int channels) { /* 2 samples per byte (2 nibbles) in stereo or mono config */ return bytes * 2 / channels; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c index d958d4fd1..93cc1a6ec 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c @@ -78,6 +78,52 @@ void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci } } +void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, nibble_shift, is_high_first, is_stereo; + int32_t sample_count; + int16_t v; + off_t byte_offset; + + is_high_first = (vgmstream->codec_config & 1); + is_stereo = (vgmstream->channels != 1); + + for (i=first_sample,sample_count=0; ioffset + i : /* stereo: one nibble per channel (assumed, not sure if stereo version actually exists) */ + stream->offset + i/2; /* mono: consecutive nibbles */ + nibble_shift = is_high_first ? + is_stereo ? (!(channel&1) ? 4:0) : (!(i&1) ? 4:0) : /* even = high, odd = low */ + is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */ + + v = (int16_t)read_8bit(byte_offset, stream->streamfile); + v = (v >> nibble_shift) & 0x0F; + outbuf[sample_count] = v*0x11*0x100; + } +} + +void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, nibble_shift, is_high_first, is_stereo; + int32_t sample_count; + int16_t v; + off_t byte_offset; + + is_high_first = (vgmstream->codec_config & 1); + is_stereo = (vgmstream->channels != 1); + + for (i=first_sample,sample_count=0; ioffset + i : /* stereo: one nibble per channel (assumed, not sure if stereo version actually exists) */ + stream->offset + i/2; /* mono: consecutive nibbles */ + nibble_shift = is_high_first ? + is_stereo ? (!(channel&1) ? 4:0) : (!(i&1) ? 4:0) : /* even = high, odd = low */ + is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */ + + v = (int16_t)read_8bit(byte_offset, stream->streamfile); + v = (v >> nibble_shift) & 0x0F; + outbuf[sample_count] = v*0x11*0x100 - 0x8000; + } +} + static int expand_ulaw(uint8_t ulawbyte) { int sign, segment, quantization, new_sample; const int bias = 0x84; @@ -175,5 +221,5 @@ void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac } size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) { - return bytes / channels / (bits_per_sample/8); + return (bytes * 8) / channels / bits_per_sample; } diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index cabcf7829..7f35538b0 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -155,6 +155,7 @@ static const char* extension_list[] = { "gcub", "gcw", "genh", + "gin", "gms", "gsb", //"gsf", //conflicts with GBA gsf plugins? @@ -194,7 +195,7 @@ static const char* extension_list[] = { "kces", "kcey", //fake extension/header id for .pcm (renamed, to be removed) - "khv", + "khv", //fake extension/header id for .vas (renamed, to be removed) "km9", "kovs", //fake extension/header id for .kvs "kns", @@ -205,6 +206,7 @@ static const char* extension_list[] = { "l", "laac", //fake extension for .aac (tri-Ace) "lac3", //fake extension for .ac3, FFmpeg/not parsed + "lasf", //fake extension for .asf (various) "leg", "lflac", //fake extension for .flac, FFmpeg/not parsed "lin", @@ -348,6 +350,7 @@ static const char* extension_list[] = { "sb5", "sb6", "sb7", + "sbr", "sm0", "sm1", "sm2", @@ -551,11 +554,13 @@ static const coding_info coding_info_list[] = { {coding_PCM16LE, "Little Endian 16-bit PCM"}, {coding_PCM16BE, "Big Endian 16-bit PCM"}, {coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"}, - {coding_PCM8, "8-bit PCM"}, - {coding_PCM8_int, "8-bit PCM with 1 byte interleave (block)"}, + {coding_PCM8, "8-bit signed PCM"}, + {coding_PCM8_int, "8-bit signed PCM with 1 byte interleave (block)"}, {coding_PCM8_U, "8-bit unsigned PCM"}, {coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"}, {coding_PCM8_SB, "8-bit PCM with sign bit"}, + {coding_PCM4, "4-bit signed PCM"}, + {coding_PCM4_U, "4-bit unsigned PCM"}, {coding_ULAW, "8-bit u-Law"}, {coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"}, {coding_ALAW, "8-bit a-Law"}, @@ -584,7 +589,8 @@ static const coding_info coding_info_list[] = { {coding_EA_XA_int, "Electronic Arts EA-XA 4-bit ADPCM v1 (mono/interleave)"}, {coding_EA_XA_V2, "Electronic Arts EA-XA 4-bit ADPCM v2"}, {coding_MAXIS_XA, "Maxis EA-XA 4-bit ADPCM"}, - {coding_EA_XAS, "Electronic Arts EA-XAS 4-bit ADPCM"}, + {coding_EA_XAS_V0, "Electronic Arts EA-XAS 4-bit ADPCM v0"}, + {coding_EA_XAS_V1, "Electronic Arts EA-XAS 4-bit ADPCM v1"}, {coding_IMA, "IMA 4-bit ADPCM"}, {coding_IMA_int, "IMA 4-bit ADPCM (mono/interleave)"}, @@ -615,6 +621,7 @@ static const coding_info coding_info_list[] = { {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, + {coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"}, {coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"}, {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, @@ -631,6 +638,7 @@ static const coding_info coding_info_list[] = { {coding_ASF, "Argonaut ASF 4-bit ADPCM"}, {coding_XMD, "Konami XMD 4-bit ADPCM"}, {coding_PCFX, "PC-FX 4-bit ADPCM"}, + {coding_OKI16, "OKI 4-bit ADPCM (16-bit output)"}, {coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"}, {coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, @@ -763,7 +771,7 @@ static const meta_info meta_info_list[] = { {meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"}, {meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, - {meta_PS2_MIC, "assume KOEI MIC file by .mic extension"}, + {meta_PS2_MIC, "KOEI .MIC header"}, {meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"}, {meta_DSP_MSS, "Double DSP header stereo by .mss extension"}, {meta_DSP_GCM, "Double DSP header stereo by .gcm extension"}, @@ -993,7 +1001,7 @@ static const meta_info meta_info_list[] = { {meta_PS3_CPS, "tri-Crescendo CPS Header"}, {meta_SQEX_SCD, "Square-Enix SCD header"}, {meta_NGC_NST_DSP, "Animaniacs NST header"}, - {meta_BAF, ".baf WAVE header"}, + {meta_BAF, "Bizarre Creations .baf header"}, {meta_PS3_MSF, "Sony MSF header"}, {meta_NUB_VAG, "Namco NUB VAG header"}, {meta_PS3_PAST, "SNDP header"}, @@ -1153,6 +1161,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_ADPCMX, "AQUASTYLE ADPY header"}, {meta_OGG_OPUS, "Ogg Opus header"}, {meta_IMC, "iNiS .IMC header"}, + {meta_GIN, "Electronic Arts Gnsu header"}, }; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/asf.c b/Frameworks/vgmstream/vgmstream/src/meta/asf.c index fc094d36f..57ecfae8d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/asf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/asf.c @@ -9,7 +9,9 @@ VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "asf")) + /* .asf: original + * .lasf: fake for plugins */ + if (!check_extensions(streamFile, "asf,lasf")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x41534600) /* "ASF\0" */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/baf.c b/Frameworks/vgmstream/vgmstream/src/meta/baf.c index dff1503fc..80308a4f8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/baf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/baf.c @@ -1,9 +1,189 @@ #include "meta.h" +#include "../coding/coding.h" -/* .BAF - Bizarre Creations (Blur, James Bond 007: Blood Stone, etc) */ + +/* .BAF - Bizarre Creations bank file [Blur (PS3), Project Gotham Racing 4 (X360), Geometry Wars (PC)] */ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t WAVE_size, DATA_size; + off_t start_offset, header_offset, name_offset; + size_t stream_size; + int loop_flag, channel_count, sample_rate, version, codec; + int total_subsongs, target_subsong = streamFile->stream_index; + int32_t (*read_32bit)(off_t,STREAMFILE*); + + + /* checks */ + if (!check_extensions(streamFile, "baf")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x42414E4B) /* "BANK" */ + goto fail; + + /* use BANK size to check endianness */ + if (guess_endianness32bit(0x04,streamFile)) { + read_32bit = read_32bitBE; + } else { + read_32bit = read_32bitLE; + } + + /* 0x04: bank size */ + version = read_32bit(0x08,streamFile); + if (version != 0x03 && version != 0x04) + goto fail; + total_subsongs = read_32bit(0x0c,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + /* - in v3 */ + /* 0x10: 0? */ + /* 0x11: bank name */ + /* - in v4 */ + /* 0x10: 1? */ + /* 0x11: padding flag? */ + /* 0x12: bank name */ + + /* find target WAVE chunk */ + { + int i; + off_t offset = read_32bit(0x04, streamFile); + + for (i = 0; i < total_subsongs; i++) { + if (i+1 == target_subsong) + break; + offset += read_32bit(offset+0x04, streamFile); /* WAVE size, variable per codec */ + } + header_offset = offset; + } + + /* parse header */ + if (read_32bitBE(header_offset+0x00, streamFile) != 0x57415645) /* "WAVE" */ + goto fail; + codec = read_32bit(header_offset+0x08, streamFile); + name_offset = header_offset + 0x0c; + start_offset = read_32bit(header_offset+0x2c, streamFile); + stream_size = read_32bit(header_offset+0x30, streamFile); + switch(codec) { + case 0x03: + switch(version) { + case 0x03: /* Geometry Wars (PC) */ + sample_rate = read_32bit(header_offset + 0x38, streamFile); + channel_count = read_32bit(header_offset + 0x40, streamFile); + /* no actual flag, just loop +15sec songs */ + loop_flag = (pcm_bytes_to_samples(stream_size, channel_count, 16) > 15*sample_rate); + break; + + case 0x04: /* Project Gotham Racing 4 (X360) */ + sample_rate = read_32bit(header_offset + 0x3c, streamFile); + channel_count = read_32bit(header_offset + 0x44, streamFile); + loop_flag = read_8bit(header_offset+0x4b, streamFile); + break; + } + break; + + case 0x07: /* Blur (PS3) */ + sample_rate = read_32bit(header_offset+0x40, streamFile); + loop_flag = read_8bit(header_offset+0x48, streamFile); + channel_count = read_8bit(header_offset+0x4b, streamFile); + break; + + case 0x08: /* Project Gotham Racing (X360) */ + sample_rate = read_32bit(header_offset+0x3c, streamFile); + channel_count = read_32bit(header_offset+0x44, streamFile); + loop_flag = read_8bit(header_offset+0x54, streamFile) != 0; + break; + + default: + VGM_LOG("BAF: unknown version %x\n", version); + goto fail; + } + /* others: pan/vol? fixed values? (0x19, 0x10) */ + + /* after WAVEs there may be padding then DATAs chunks, but offsets point after DATA size */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_BAF; + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + + switch(codec) { + case 0x03: + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + + case 0x07: + vgmstream->coding_type = coding_PSX_cfg; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x21; + + vgmstream->num_samples = read_32bit(header_offset+0x44, streamFile); + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + + case 0x08: { + uint8_t buf[0x100]; + int bytes; + + bytes = ffmpeg_make_riff_xma1(buf,0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, 0); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* need to manually find sample offsets, it was a thing with XMA1 */ + { + ms_sample_data msd = {0}; + + msd.xma_version = 1; + msd.channels = channel_count; + msd.data_offset = start_offset; + msd.data_size = stream_size; + msd.loop_flag = loop_flag; + msd.loop_start_b = read_32bit(header_offset+0x4c, streamFile); + msd.loop_end_b = read_32bit(header_offset+0x50, streamFile); + msd.loop_start_subframe = (read_8bit(header_offset+0x55, streamFile) >> 0) & 0x0f; + msd.loop_end_subframe = (read_8bit(header_offset+0x55, streamFile) >> 4) & 0x0f; + xma_get_samples(&msd, streamFile); + + vgmstream->num_samples = msd.num_samples; /* also at 0x58, but unreliable? */ + vgmstream->loop_start_sample = msd.loop_start_sample; + vgmstream->loop_end_sample = msd.loop_end_sample; + } + + xma_fix_raw_samples_ch(vgmstream, streamFile, start_offset, stream_size, channel_count, 1,1); + break; + } + + default: + VGM_LOG("BAF: unknown codec %x\n", codec); + goto fail; + } + + read_string(vgmstream->stream_name,0x20+1, name_offset,streamFile); + + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* awful PS3 splits of the above with bad offsets and all */ +VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t WAVE_size, stream_size; off_t start_offset; long sample_count; int sample_rate; @@ -13,38 +193,31 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { int channels; int loop_flag = 0; - /* check extensions */ + /* checks */ if ( !check_extensions(streamFile, "baf") ) goto fail; - - /* check WAVE */ if (read_32bitBE(0,streamFile) != 0x57415645) /* "WAVE" */ goto fail; WAVE_size = read_32bitBE(4,streamFile); if (WAVE_size != 0x4c) /* && WAVE_size != 0x50*/ goto fail; - /* check for DATA after WAVE */ if (read_32bitBE(WAVE_size,streamFile) != 0x44415441) /* "DATA"*/ goto fail; /* check that WAVE size is data size */ - DATA_size = read_32bitBE(0x30,streamFile); - if (read_32bitBE(WAVE_size+4,streamFile)-8 != DATA_size) goto fail; + stream_size = read_32bitBE(0x30,streamFile); + if (read_32bitBE(WAVE_size+4,streamFile)-8 != stream_size) goto fail; - /*if (WAVE_size == 0x50) sample_count = DATA_size * frame_samples / frame_size / channels;*/ sample_count = read_32bitBE(0x44,streamFile); - - /*if (WAVE_size == 0x50) sample_rate = read_32bitBE(0x3c,streamFile);*/ sample_rate = read_32bitBE(0x40,streamFile); - /* unsure how to detect channel count, so use a hack */ - channels = (long long)DATA_size / frame_size * frame_samples / sample_count; + channels = (long long)stream_size / frame_size * frame_samples / sample_count; + start_offset = WAVE_size + 8; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = WAVE_size + 8; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = sample_count; @@ -53,14 +226,11 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { vgmstream->interleave_block_size = frame_size; vgmstream->meta_type = meta_BAF; - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - return vgmstream; fail: close_vgmstream(vgmstream); return NULL; } - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c index f7924cf06..b5437b691 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c @@ -3,16 +3,16 @@ typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec; -/* BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */ +/* .BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { -#if 1 VGMSTREAM * vgmstream = NULL; off_t start_offset, stream_offset, name_offset = 0; size_t stream_size, interleave = 0; off_t sblk_offset, data_offset; size_t data_size; - int channel_count = 0, loop_flag, sample_rate, parts, version; + int channel_count = 0, loop_flag, sample_rate, parts, version, big_endian; int loop_start = 0, loop_end = 0; + uint32_t pitch, flags; uint32_t atrac9_info = 0; int total_subsongs, target_subsong = streamFile->stream_index; @@ -28,10 +28,12 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { if (read_32bitBE(0x00,streamFile) == 0x00000003) { /* PS3 */ read_32bit = read_32bitBE; read_16bit = read_16bitBE; + big_endian = 1; } else if (read_32bitBE(0x00,streamFile) == 0x03000000) { /* Vita/PS4 */ read_32bit = read_32bitLE; read_16bit = read_16bitLE; + big_endian = 0; } else { goto fail; @@ -44,11 +46,13 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { /* 0x0c: sklb size */ data_offset = read_32bit(0x10,streamFile); data_size = read_32bit(0x14,streamFile); + /* when sblk_offset >= 0x20: */ /* 0x18: ZLSD small footer, rare [Yakuza 6's Puyo Puyo (PS4)] */ /* 0x1c: ZLSD size */ /* SE banks, also used for music. Most table fields seems reserved/defaults and - * don't change much between subsongs or files, so they aren't described in detail */ + * don't change much between subsongs or files, so they aren't described in detail. + * Entry sizes are variable (usually flag + extra size xN) so table offsets are needed. */ /* SBlk part: parse header */ @@ -63,7 +67,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { off_t table1_offset, table2_offset, table3_offset, table4_offset; size_t section_entries, material_entries, stream_entries; size_t table1_entry_size; - off_t table1_suboffset, table2_suboffset, table3_suboffset; + off_t table1_suboffset, table2_suboffset; off_t table2_entry_offset = 0, table3_entry_offset = 0; int table4_entry_id = -1; off_t table4_entries_offset, table4_names_offset; @@ -75,7 +79,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { case 0x09: /* Puyo Puyo Tetris (PS4) */ section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */ material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x08 */ - stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: ~0x60 */ + stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: ~0x18 + variable */ table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile); table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,streamFile); table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,streamFile); @@ -84,7 +88,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { table1_entry_size = 0x0c; table1_suboffset = 0x08; table2_suboffset = 0x00; - table3_suboffset = 0x10; break; case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */ @@ -95,12 +98,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,streamFile); section_entries = (uint16_t)read_16bit(sblk_offset+0x38,streamFile); /* entry size: ~0x24 */ material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,streamFile); /* entry size: ~0x08 */ - stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,streamFile); /* entry size: ~0x90 + variable (sometimes) */ + stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,streamFile); /* entry size: ~0x5c + variable */ table1_entry_size = 0x24; table1_suboffset = 0x0c; table2_suboffset = 0x00; - table3_suboffset = 0x44; break; default: @@ -150,14 +152,64 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* this means some subsongs repeat streams, that can happen in some sfx banks, whatevs */ if (total_subsongs != stream_entries) { - //;VGM_LOG("BNK: subsongs %i vs table3 %i don't match\n", total_subsongs, stream_entries); + VGM_LOG("BNK: subsongs %i vs table3 %i don't match\n", total_subsongs, stream_entries); /* find_dupes...? */ } + //;VGM_LOG("BNK: header entry at %lx\n", table3_offset+table3_entry_offset); /* parse sounds */ - stream_offset = read_32bit(table3_offset+table3_entry_offset+table3_suboffset+0x00,streamFile); - stream_size = read_32bit(table3_offset+table3_entry_offset+table3_suboffset+0x04,streamFile); + switch(version) { + case 0x03: + case 0x04: + case 0x09: + pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,streamFile); + flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,streamFile); + stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,streamFile); + stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,streamFile); + + /* must use some log/formula but whatevs */ + switch(pitch) { + case 0xC6: sample_rate = 50000; break; //? + case 0xC4: sample_rate = 48000; break; + case 0xC3: sample_rate = 46000; break; //? + case 0xC2: sample_rate = 44100; break; + case 0xBC: sample_rate = 36000; break; //? + case 0xBA: sample_rate = 32000; break; //? + case 0xB6: sample_rate = 22050; break; + case 0xAA: sample_rate = 11025; break; + default: + VGM_LOG("BNK: unknown pitch %x\n", pitch); + goto fail; + } + break; + + case 0x0d: + case 0x0e: + flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,streamFile); + stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,streamFile); + stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,streamFile); + pitch = (uint32_t)read_32bit(table3_offset+table3_entry_offset+0x4c,streamFile); + + /* this looks like "((pitch >> 9) & 0xC000) | ((pitch >> 8) & 0xFFFF)" but... why??? */ + switch(pitch) { + case 0x467A0000: sample_rate = 64000; break; //? + case 0x46BB8000: sample_rate = 48000; break; + case 0x473B8000: sample_rate = 48000; break; + case 0x46AC4400: sample_rate = 44100; break; + case 0x47AC4400: sample_rate = 44100; break; + case 0x472C4400: sample_rate = 44100; break; + default: + VGM_LOG("BNK: unknown pitch %x\n", pitch); + goto fail; + } + break; + + default: + goto fail; + } + + //;VGM_LOG("BNK: stream at %lx + %x\n", stream_offset, stream_size); /* parse names */ switch(version) { @@ -215,7 +267,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { switch(version) { case 0x03: case 0x04: - sample_rate = 48000; /* seems ok */ channel_count = 1; /* hack for PS3 files that use dual subsongs as stereo */ @@ -224,14 +275,19 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { stream_size = stream_size*channel_count; total_subsongs = 1; } - interleave = stream_size / channel_count; + + if (flags & 0x80) { + codec = PCM16; /* rare [Wipeout HD (PS3)] */ + } + else { + loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end); + loop_flag = (flags & 0x40); /* no loops values in sight so may only apply to PS-ADPCM flags */ + + codec = PSX; + } + //postdata_size = 0x10; /* last frame may be garbage */ - - loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end); - loop_flag = (loop_start > 28); /* ignore full loops since they just fadeout + repeat */ - - codec = PSX; break; case 0x09: @@ -243,7 +299,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { case 0x05: /* ATRAC9 stereo */ if (read_32bit(start_offset+0x08,streamFile) + 0x08 != extradata_size) /* repeat? */ goto fail; - sample_rate = 48000; /* seems ok */ channel_count = (type == 0x02) ? 1 : 2; atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,streamFile); @@ -274,7 +329,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { case 0x05: /* ATRAC9 stereo */ if (read_32bit(start_offset+0x10,streamFile) + 0x10 != extradata_size) /* repeat? */ goto fail; - sample_rate = 48000; /* seems ok */ channel_count = (type == 0x02) ? 1 : 2; atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,streamFile); @@ -291,7 +345,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */ case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */ - sample_rate = 48000; /* seems ok */ /* 0x10: null? */ channel_count = read_32bit(start_offset+0x14,streamFile); interleave = 0x02; @@ -304,7 +357,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { break; case 0x00: /* PS-ADPCM (test banks) */ - sample_rate = 48000; /* seems ok */ /* 0x10: null? */ channel_count = read_32bit(start_offset+0x14,streamFile); interleave = 0x02; @@ -366,7 +418,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { } #endif case PCM16: - vgmstream->coding_type = coding_PCM16LE; + vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; @@ -376,7 +428,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { break; case PSX: - vgmstream->sample_rate = 48000; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; @@ -387,7 +438,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { break; case HEVAG: - vgmstream->sample_rate = 48000; vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; @@ -410,6 +460,22 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { return vgmstream; fail: close_vgmstream(vgmstream); -#endif return NULL; } + + +#if 0 +/* .BNK - Sony's bank, earlier version [Jak and Daxter (PS2), NCAA Gamebreaker 2001 (PS2)] */ +VGMSTREAM * init_vgmstream_bnk_sony_v2(STREAMFILE *streamFile) { + /* 0x00: 0x00000001 + * 0x04: sections (2 or 3) + * 0x08+ similar to v3 but "SBv2" + * table formats is a bit different + * header is like v3 but stream size is in other table? + */ + +fail: + close_vgmstream(vgmstream); + return NULL; +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index 3091383d5..2ec1da29b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -40,11 +40,12 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { /* checks */ - /* .asf/as4: common + /* .asf/as4: common, + * .lasf: fake for plugins * .cnk: some PS games * .sng: fake for plugins (to mimic EA SCHl's common extension) * .uv/tgq: some SAT games (video only?) */ - if (!check_extensions(streamFile,"asf,as4,cnk,sng,uv,tgq")) + if (!check_extensions(streamFile,"asf,lasf,as4,cnk,sng,uv,tgq")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 21b905681..958846de3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -121,7 +121,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) { } header_offset = 0x10; /* SNR header */ - start_offset = read_32bit(0x08,streamFile); /* SPS blocks */ + start_offset = read_32bit(0x08,streamFile); /* SNS blocks */ vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, header_offset, start_offset, meta_EA_SNU); if (!vgmstream) goto fail; @@ -134,7 +134,7 @@ fail: } /* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */ -VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE *streamFile) { int is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; off_t bnk_offset, header_table_offset, base_offset, unk_struct_offset, table_offset, snd_entry_offset, ast_offset; off_t num_entries_off, base_offset_off, entries_off, sound_table_offset_off; @@ -143,8 +143,8 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { uint8_t num_entries, extra_entries; off_t sound_table_offsets[0x2000]; VGMSTREAM *vgmstream; - int32_t (*read_32bit)(off_t,STREAMFILE*); - int16_t (*read_16bit)(off_t,STREAMFILE*); + int32_t(*read_32bit)(off_t, STREAMFILE*); + int16_t(*read_16bit)(off_t, STREAMFILE*); /* check extension */ if (!check_extensions(streamFile, "abk")) @@ -154,7 +154,7 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { goto fail; /* use table offset to check endianness */ - if (guess_endianness32bit(0x1C,streamFile)) { + if (guess_endianness32bit(0x1C, streamFile)) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { @@ -183,15 +183,13 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { base_offset_off = 0x2C; entries_off = 0x3C; sound_table_offset_off = 0x04; - } - else if (header_table_offset == 0x78) { + } else if (header_table_offset == 0x78) { /* FIFA 08 has a bunch of extra zeroes all over the place, don't know what's up with that */ num_entries_off = 0x40; base_offset_off = 0x54; entries_off = 0x68; sound_table_offset_off = 0x0C; - } - else { + } else { goto fail; } @@ -207,10 +205,8 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { /* For some reason, there are duplicate entries pointing at the same sound tables */ is_dupe = 0; - for (k = 0; k < total_sound_tables; k++) - { - if (table_offset==sound_table_offsets[k]) - { + for (k = 0; k < total_sound_tables; k++) { + if (table_offset == sound_table_offsets[k]) { is_dupe = 1; break; } @@ -248,7 +244,7 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { if (bnk_target_index == 0xFFFF || ast_offset == 0) goto fail; - + vgmstream = parse_s10a_header(streamFile, bnk_offset, bnk_target_index, ast_offset); if (!vgmstream) goto fail; @@ -288,8 +284,7 @@ static VGMSTREAM * parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint1 vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, snr_offset, sns_offset, meta_EA_SNR_SNS); if (!vgmstream) goto fail; - } - else { + } else { /* streamed asset */ astFile = open_streamfile_by_ext(streamFile, "ast"); if (!astFile) @@ -314,7 +309,90 @@ fail: return NULL; } -/* EA HDR/STH/DAT - seen in early 7th-gen games, used for storing speech */ +/* EA SBR/SBS - used in older 7th gen games for storing SFX */ +VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE *streamFile) { + uint32_t num_sounds, type_desc; + uint16_t num_metas, meta_type; + uint32_t i; + off_t table_offset, types_offset, entry_offset, metas_offset, data_offset, snr_offset, sns_offset; + STREAMFILE *sbsFile = NULL, *streamData = NULL; + VGMSTREAM *vgmstream = NULL; + int target_stream = streamFile->stream_index; + + if (!check_extensions(streamFile, "sbr")) + goto fail; + + if (read_32bitBE(0x00, streamFile) != 0x53424B52) /* "SBKR" */ + goto fail; + + /* SBR files are always big endian */ + num_sounds = read_32bitBE(0x1c, streamFile); + table_offset = read_32bitBE(0x24, streamFile); + types_offset = read_32bitBE(0x28, streamFile); + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds) + goto fail; + + entry_offset = table_offset + 0x0a * (target_stream - 1); + num_metas = read_16bitBE(entry_offset + 0x04, streamFile); + metas_offset = read_32bitBE(entry_offset + 0x06, streamFile); + + snr_offset = 0xFFFFFFFF; + sns_offset = 0xFFFFFFFF; + + for (i = 0; i < num_metas; i++) { + entry_offset = metas_offset + 0x06 * i; + meta_type = read_16bitBE(entry_offset, streamFile); + data_offset = read_32bitBE(entry_offset + 0x02, streamFile); + + type_desc = read_32bitBE(types_offset + 0x06 * meta_type, streamFile); + + switch (type_desc) { + case 0x534E5231: /* "SNR1" */ + snr_offset = data_offset; + break; + case 0x534E5331: /* "SNS1" */ + sns_offset = read_32bitBE(data_offset, streamFile); + break; + default: + break; + } + } + + if (snr_offset == 0xFFFFFFFF) + goto fail; + + if (sns_offset == 0xFFFFFFFF) { + /* RAM asset */ + streamData = streamFile; + sns_offset = snr_offset + get_snr_size(streamFile, snr_offset); + } else { + /* streamed asset */ + sbsFile = open_streamfile_by_ext(streamFile, "sbs"); + if (!sbsFile) + goto fail; + + if (read_32bitBE(0x00, sbsFile) != 0x53424B53) /* "SBKS" */ + goto fail; + + streamData = sbsFile; + } + + vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamData, snr_offset, sns_offset, meta_EA_SNR_SNS); + if (!vgmstream) + goto fail; + + vgmstream->num_streams = num_sounds; + close_streamfile(sbsFile); + return vgmstream; + +fail: + close_streamfile(sbsFile); + return NULL; +} + +/* EA HDR/STH/DAT - seen in older 7th gen games, used for storing speech */ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { int target_stream = streamFile->stream_index; uint32_t i; @@ -372,7 +450,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { sns_offset = 0; for (i = 0; i < total_sounds; i++) { - snr_offset = (off_t)read_16bitBE(0x10 + (0x02+userdata_size) * i, streamFile) + 0x04; + snr_offset = (uint16_t)read_16bitBE(0x10 + (0x02+userdata_size) * i, streamFile) + 0x04; if (i == target_stream - 1) break; @@ -416,7 +494,7 @@ fail: } /* EA MPF/MUS combo - used in older 7th gen games for storing music */ -VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE *streamFile) { uint32_t num_sounds; uint8_t version, sub_version, block_id; off_t table_offset, entry_offset, snr_offset, sns_offset; @@ -491,6 +569,179 @@ fail: return NULL; } +/* EA Harmony Sample Bank - used in 8th gen EA Sports games */ +VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *streamFile) { + uint32_t num_dsets, set_sounds, chunk_id; + uint32_t i; + uint8_t set_type, flag, offset_size; + off_t data_offset, table_offset, dset_offset, base_offset, sound_table_offset, sound_offset, header_offset, start_offset; + STREAMFILE *sbsFile = NULL, *streamData = NULL; + VGMSTREAM *vgmstream = NULL; + int target_stream = streamFile->stream_index, total_sounds, local_target, is_streamed = 0; + int32_t(*read_32bit)(off_t, STREAMFILE*); + int16_t(*read_16bit)(off_t, STREAMFILE*); + + if (!check_extensions(streamFile, "sbr")) + goto fail; + + if (read_32bitBE(0x00, streamFile) == 0x53426C65) { /* "SBle" */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + /* Logically, big endian version starts with SBbe. However, this format is + * only used on 8th gen systems so far so big endian version probably doesn't exist. */ +#if 0 + } else if (read_32bitBE(0x00, streamFile) == 0x53426265) { /* "SBbe" */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; +#endif + } else { + goto fail; + } + + num_dsets = read_16bit(0x0a, streamFile); + data_offset = read_32bit(0x20, streamFile); + table_offset = read_32bit(0x24, streamFile); + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0) + goto fail; + + total_sounds = 0; + sound_offset = 0xFFFFFFFF; + + /* The bank is split into DSET sections each of which references one or multiple sounds. */ + /* Each set can contain RAM sounds (stored in SBR in data section) or streamed sounds (stored separately in SBS file). */ + for (i = 0; i < num_dsets; i++) { + dset_offset = read_32bit(table_offset + 0x08 * i, streamFile); + if (read_32bit(dset_offset, streamFile) != 0x44534554) /* "DSET" */ + goto fail; + + set_sounds = read_32bit(dset_offset + 0x38, streamFile); + local_target = target_stream - total_sounds - 1; + dset_offset += 0x48; + + /* Find RAM or OFF chunk */ + while(1) { + chunk_id = read_32bit(dset_offset, streamFile); + if (chunk_id == 0x2E52414D) { /* ".RAM" */ + break; + } else if (chunk_id == 0x2E4F4646) { /* ".OFF" */ + break; + } else if (chunk_id == 0x2E4C4452 || /* ".LDR" */ + chunk_id == 0x2E4F424A || /* ".OBJ" */ + (chunk_id & 0xFF00FFFF) == 0x2E00534C) { /* ".?SL */ + dset_offset += 0x18; + } else { + goto fail; + } + } + + /* Different set types store offsets differently */ + set_type = read_8bit(dset_offset + 0x05, streamFile); + + if (set_type == 0x00) { + total_sounds++; + if (local_target < 0 || local_target > 0) + continue; + + sound_offset = read_32bit(dset_offset + 0x08, streamFile); + } else if (set_type == 0x01) { + total_sounds += 2; + if (local_target < 0 || local_target > 1) + continue; + + base_offset = read_32bit(dset_offset + 0x08, streamFile); + + if (local_target == 0) { + sound_offset = base_offset; + } else { + sound_offset = base_offset + (uint16_t)read_16bit(dset_offset + 0x06, streamFile); + } + } else if (set_type == 0x02 || set_type == 0x03) { + flag = read_8bit(dset_offset + 0x06, streamFile); + offset_size = read_8bit(dset_offset + 0x07, streamFile); + base_offset = read_32bit(dset_offset + 0x08, streamFile); + sound_table_offset = read_32bit(dset_offset + 0x10, streamFile); + + if (offset_size == 0x04 && flag != 0x00) { + set_sounds = base_offset; + } + + total_sounds += set_sounds; + if (local_target < 0 || local_target >= set_sounds) + continue; + + if (offset_size == 0x02) { + sound_offset = (uint16_t)read_16bit(sound_table_offset + 0x02 * local_target, streamFile); + if (flag != 0x00) sound_offset *= (off_t)pow(2, flag); + sound_offset += base_offset; + } else if (offset_size == 0x04) { + sound_offset = read_32bit(sound_table_offset + 0x04 * local_target, streamFile); + if (flag == 0x00) sound_offset += base_offset; + } + } else if (set_type == 0x04) { + total_sounds += set_sounds; + if (local_target < 0 || local_target >= set_sounds) + continue; + + sound_table_offset = read_32bit(dset_offset + 0x10, streamFile); + sound_offset = read_32bit(sound_table_offset + 0x08 * local_target, streamFile); + } else { + goto fail; + } + + if (chunk_id == 0x2E52414D) { /* ".RAM" */ + is_streamed = 0; + } else if (chunk_id == 0x2E4F4646) { /* ".OFF" */ + is_streamed = 1; + } + } + + if (sound_offset == 0xFFFFFFFF) + goto fail; + + if (!is_streamed) { + /* RAM asset */ + if (read_32bitBE(data_offset, streamFile) != 0x64617461) /* "data" */ + goto fail; + + streamData = streamFile; + sound_offset += data_offset; + } else { + /* streamed asset */ + sbsFile = open_streamfile_by_ext(streamFile, "sbs"); + if (!sbsFile) + goto fail; + + if (read_32bitBE(0x00, sbsFile) != 0x64617461) /* "data" */ + goto fail; + + streamData = sbsFile; + + if (read_32bitBE(sound_offset, streamData) == 0x736C6F74) { + /* skip "slot" section */ + sound_offset += 0x30; + } + } + + if (read_8bit(sound_offset, streamData) != EAAC_BLOCKID1_HEADER) + goto fail; + + header_offset = sound_offset + 0x04; + start_offset = sound_offset + (read_32bitBE(sound_offset, streamData) & 0x00FFFFFF); + vgmstream = init_vgmstream_eaaudiocore_header(streamData, streamData, header_offset, start_offset, meta_EA_SNR_SNS); + if (!vgmstream) + goto fail; + + vgmstream->num_streams = total_sounds; + close_streamfile(sbsFile); + return vgmstream; + +fail: + close_streamfile(sbsFile); + return NULL; +} + /* ************************************************************************* */ typedef struct { @@ -562,11 +813,28 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST eaac.loop_start = read_32bitBE(header_offset+0x08, streamHead); eaac.loop_end = eaac.num_samples; - /* RAM assets only have one block, even if they (rarely) set loop_start > 0 */ - if (eaac.streamed) + if (eaac.streamed) { eaac.loop_offset = read_32bitBE(header_offset+0x0c, streamHead); - else + + if (eaac.version == EAAC_VERSION_V0) { + /* SNR+SNS are separate so offsets are relative to the data start + * (first .SNS block, or extra data before the .SNS block in case of .SNU) */ + eaac.loop_offset = eaac.stream_offset + eaac.loop_offset; + } else { + /* SPS have headers+data together so offsets are relative to the file start [ex. FIFA 18 (PC)] */ + eaac.loop_offset = header_offset - 0x04 + eaac.loop_offset; + } + } + else if (eaac.loop_start > 0) { + /* RAM assets have two blocks in case of actual loops */ + /* find the second block by getting the first block size */ + eaac.loop_offset = read_32bitBE(eaac.stream_offset, streamData) & 0x00FFFFF; + eaac.loop_offset = eaac.stream_offset + eaac.loop_offset; + } + else { + /* RAM assets only one block in case in case of full loops */ eaac.loop_offset = eaac.stream_offset; /* implicit */ + } //todo EATrax has extra values in header, which would coexist with loop values if (eaac.codec == EAAC_CODEC_EATRAX) { @@ -621,7 +889,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST case EAAC_CODEC_EAXMA: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */ /* special (if hacky) loop handling, see comments */ - if (eaac.streamed && eaac.loop_start > 0) { + if (eaac.loop_start > 0) { segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac); if (!data) goto fail; vgmstream->layout_data = data; @@ -640,7 +908,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST #endif case EAAC_CODEC_XAS: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */ - vgmstream->coding_type = coding_EA_XAS; + vgmstream->coding_type = coding_EA_XAS_V1; vgmstream->layout_type = layout_blocked_ea_sns; break; @@ -658,7 +926,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST start_offset = 0x00; /* must point to the custom streamfile's beginning */ /* special (if hacky) loop handling, see comments */ - if (eaac.streamed && eaac.loop_start > 0) { + if (eaac.loop_start > 0) { segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac); if (!data) goto fail; vgmstream->layout_data = data; @@ -785,7 +1053,7 @@ static size_t get_snr_size(STREAMFILE *streamFile, off_t offset) { static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac) { segmented_layout_data *data = NULL; STREAMFILE* temp_streamFile[2] = {0}; - off_t offsets[2] = { eaac->stream_offset, eaac->stream_offset + eaac->loop_offset }; + off_t offsets[2] = { eaac->stream_offset, eaac->loop_offset }; int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start}; int segment_count = 2; /* intro/loop */ int i; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index b38c90503..fbe12d034 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -115,11 +115,12 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* check extension */ /* they don't seem enforced by EA's tools but usually: - * .asf: ~early [ex. Need for Speed (PC)] + * .asf: ~early (audio stream file?) [ex. Need for Speed (PC)] + * .lasf: fake for plugins * .str: ~early [ex. FIFA 2002 (PS1)] * .eam: ~mid (fake?) * .exa: ~mid [ex. 007 - From Russia with Love] - * .sng: ~late (fake?) + * .sng: ~late (FIFA games) * .aud: ~late [ex. FIFA 14 (3DS)] * .strm: MySims Kingdom (Wii) * .stm: FIFA 12 (3DS) @@ -129,7 +130,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { * .gsf: 007 - Everything or Nothing (GC) * .mus: map/mpf+mus only? * (extensionless): SSX (PS2) (inside .big) */ - if (!check_extensions(streamFile,"asf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) + if (!check_extensions(streamFile,"asf,lasf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) goto fail; /* check header */ @@ -187,15 +188,16 @@ fail: /* streamed assets are stored externally in AST file (mostly seen in earlier 6th-gen games) */ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; - off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset; + off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset, schl_loop_offset; uint32_t i, j, k, num_sounds, total_sound_tables; uint16_t num_tables; uint8_t sound_type, num_entries; off_t sound_table_offsets[0x2000]; STREAMFILE * astData = NULL; - VGMSTREAM * vgmstream; - int32_t (*read_32bit)(off_t,STREAMFILE*); - int16_t (*read_16bit)(off_t,STREAMFILE*); + VGMSTREAM * vgmstream = NULL; + segmented_layout_data *data_s = NULL; + int32_t(*read_32bit)(off_t, STREAMFILE*); + int16_t(*read_16bit)(off_t, STREAMFILE*); /* check extension */ if (!check_extensions(streamFile, "abk")) @@ -205,7 +207,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { goto fail; /* use table offset to check endianness */ - if (guess_endianness32bit(0x1C,streamFile)) { + if (guess_endianness32bit(0x1C, streamFile)) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { @@ -240,10 +242,8 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { /* For some reason, there are duplicate entries pointing at the same sound tables */ is_dupe = 0; - for (k = 0; k < total_sound_tables; k++) - { - if (table_offset==sound_table_offsets[k]) - { + for (k = 0; k < total_sound_tables; k++) { + if (table_offset == sound_table_offsets[k]) { is_dupe = 1; break; } @@ -278,45 +278,85 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { if (target_entry_offset == 0) goto fail; - /* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed and prefetched(?) */ + /* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed looped */ /* 0x01: ??? */ /* 0x04: index for normal sounds, offset for streamed sounds */ - /* 0x08: offset for prefetched sounds */ + /* 0x08: loop offset for streamed sounds */ sound_type = read_8bit(target_entry_offset + 0x00, streamFile); - + switch (sound_type) { - case 0x00: - if (!bnk_offset) - goto fail; + case 0x00: + if (!bnk_offset) + goto fail; - bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1; - vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, 1); - if (!vgmstream) - goto fail; - break; + bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1; + vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, 1); + if (!vgmstream) + goto fail; - case 0x01: - case 0x02: - astData = open_streamfile_by_ext(streamFile, "ast"); - if (!astData) - goto fail; + break; + + case 0x01: + astData = open_streamfile_by_ext(streamFile, "ast"); + if (!astData) + goto fail; - if (sound_type == 0x01) schl_offset = read_32bit(target_entry_offset + 0x04, streamFile); - else - schl_offset = read_32bit(target_entry_offset + 0x08, streamFile); + if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER) + goto fail; - if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER) + vgmstream = parse_schl_block(astData, schl_offset, 0); + if (!vgmstream) + goto fail; + + break; + + case 0x02: + astData = open_streamfile_by_ext(streamFile, "ast"); + if (!astData) + goto fail; + + /* looped sounds basically consist of two independent segments + * the first one is loop start, the second one is loop body */ + schl_offset = read_32bit(target_entry_offset + 0x04, streamFile); + schl_loop_offset = read_32bit(target_entry_offset + 0x08, streamFile); + + if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER || + read_32bitBE(schl_loop_offset, astData) != EA_BLOCKID_HEADER) + goto fail; + + /* init layout */ + data_s = init_layout_segmented(2); + if (!data_s) goto fail; + + /* load intro and loop segments */ + data_s->segments[0] = parse_schl_block(astData, schl_offset, 0); + if (!data_s->segments[0]) goto fail; + data_s->segments[1] = parse_schl_block(astData, schl_loop_offset, 0); + if (!data_s->segments[1]) goto fail; + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data_s)) + goto fail; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(data_s->segments[0]->channels, 1); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = data_s->segments[0]->sample_rate; + vgmstream->num_samples = data_s->segments[0]->num_samples + data_s->segments[1]->num_samples; + vgmstream->loop_start_sample = data_s->segments[0]->num_samples; + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_EA_SCHL; + vgmstream->coding_type = data_s->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + vgmstream->layout_data = data_s; + break; + + default: goto fail; - - vgmstream = parse_schl_block(astData, schl_offset, 0); - if (!vgmstream) - goto fail; - break; - - default: - goto fail; - break; + break; } vgmstream->num_streams = total_sounds; @@ -325,6 +365,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { fail: close_streamfile(astData); + free_layout_segmented(data_s); return NULL; } @@ -344,7 +385,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { /* 0x05: number of files */ /* 0x06: ??? */ /* 0x07: offset multiplier flag */ - /* 0x08: combined size of all sounds without padding divided by 0x0100 */ + /* 0x08: combined size of all sounds without padding divided by offset mult */ /* 0x0C: table start */ /* no nice way to validate these so we do what we can */ @@ -358,14 +399,14 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { userdata_size = read_8bit(0x04, streamFile) & 0x0F; total_sounds = read_8bit(0x05, streamFile); - offset_mult = (off_t)read_8bit(0x07, streamFile) * 0x0100 + 0x0100; + offset_mult = (uint8_t)read_8bit(0x07, streamFile) * 0x0100 + 0x0100; if (target_stream == 0) target_stream = 1; if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) goto fail; /* offsets are always big endian */ - schl_offset = (off_t)read_16bitBE(0x0C + (0x02+userdata_size) * (target_stream-1), streamFile) * offset_mult; + schl_offset = (uint16_t)read_16bitBE(0x0C + (0x02+userdata_size) * (target_stream-1), streamFile) * offset_mult; if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER) goto fail; @@ -460,7 +501,7 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) { } /* - * 0x04: ??? + * 0x04: version * 0x05: intro segment * 0x06: number of segments * 0x07: userdata entry size (incorrect?) @@ -548,14 +589,14 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { if (version == 3 && sub_version == 1) { /* SSX Tricky */ /* we need to go through the first two sections to find sound table */ sec1_num = read_16bit(0x12, streamFile); - sec2_size = read_8bit(0x0e, streamFile); + sec2_size = read_8bit(0x0d, streamFile) * read_8bit(0x0e, streamFile); sec2_num = read_8bit(0x0f, streamFile); sec3_num = read_8bit(0x10, streamFile); sec4_num = read_8bit(0x11, streamFile); /* get the last entry offset */ section_offset = 0x24; - entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04; + entry_offset = (uint16_t)read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04; subentry_num = read_8bit(entry_offset + 0x0b, streamFile); section_offset = entry_offset + 0x0c + subentry_num * 0x04; @@ -570,14 +611,14 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { off_mult = 0x04; } else if (version == 3 && sub_version == 4) { /* Harry Potter and the Chamber of Secrets */ sec1_num = read_16bit(0x12, streamFile); - sec2_size = read_8bit(0x0e, streamFile); + sec2_size = read_8bit(0x0d, streamFile) * read_8bit(0x0e, streamFile); sec2_num = read_8bit(0x0f, streamFile); sec3_num = read_8bit(0x10, streamFile); sec4_num = read_8bit(0x11, streamFile); /* get the last entry offset */ section_offset = 0x24; - entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04; + entry_offset = (uint16_t)read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04; if (big_endian) { subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 19) & 0xFF; } else { @@ -601,7 +642,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { /* get the last entry offset */ section_offset = 0x20; - entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04; + entry_offset = (uint16_t)read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04; if (big_endian) { subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 15) & 0xFF; } else { @@ -609,7 +650,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { } section_offset = entry_offset + 0x10 + subentry_num * 0x04; - entry_offset = read_16bit(section_offset + (sec2_num - 1) * 0x02, streamFile) * 0x04; + entry_offset = (uint16_t)read_16bit(section_offset + (sec2_num - 1) * 0x02, streamFile) * 0x04; if (big_endian) { subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 10) & 0xFF; } else { @@ -1423,15 +1464,15 @@ static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t star multiple_schl = 1; } - /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this. - * Get total samples by parsing block headers, needed when multiple files are stitched together. - * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped - * music (.map/lin). Subfiles always share header, except num_samples. */ - num_samples += vgmstream->current_block_samples; + if (vgmstream->current_block_samples > 0) { + /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this. + * Get total samples by parsing block headers, needed when multiple files are stitched together. + * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped + * music (.map/lin). Subfiles always share header, except num_samples. */ + num_samples += vgmstream->current_block_samples; - /* Stream size is almost never provided in bank files so we have to calc it manually */ - if (vgmstream->current_block_samples != 0) { - stream_size += vgmstream->next_block_offset - vgmstream->current_block_offset - 0x0c; + /* Stream size is almost never provided in bank files so we have to calc it manually */ + stream_size += vgmstream->next_block_offset - vgmstream->ch[0].offset; } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c index c374f874c..eb5f48737 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c @@ -29,8 +29,10 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) { ea_header ea = {0}; - /* check extension */ - if (!check_extensions(streamFile,"asf")) + /* checks */ + /* .asf: original + * .lasf: fake for plugins */ + if (!check_extensions(streamFile,"asf,lasf")) goto fail; /* check header (see ea_schl.c for more info about blocks) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fag.c b/Frameworks/vgmstream/vgmstream/src/meta/fag.c index 473e77c66..7a38115e4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fag.c @@ -32,7 +32,7 @@ VGMSTREAM * init_vgmstream_fag(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->meta_type = meta_FAG; - vgmstream->sample_rate = 24000; + vgmstream->sample_rate = 22050; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/genh.c b/Frameworks/vgmstream/vgmstream/src/meta/genh.c index acdf9d01d..c3bdfad9f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/genh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/genh.c @@ -6,38 +6,42 @@ /* known GENH types */ typedef enum { - PSX = 0, /* PSX ADPCM */ - XBOX = 1, /* XBOX IMA ADPCM */ - NGC_DTK = 2, /* NGC ADP/DTK ADPCM */ - PCM16BE = 3, /* 16bit big endian PCM */ - PCM16LE = 4, /* 16bit little endian PCM */ - PCM8 = 5, /* 8bit PCM */ - SDX2 = 6, /* SDX2 (3D0 games) */ - DVI_IMA = 7, /* DVI IMA ADPCM */ - MPEG = 8, /* MPEG (MP3) */ - IMA = 9, /* IMA ADPCM */ - AICA = 10, /* AICA ADPCM (dreamcast) */ - MSADPCM = 11, /* MS ADPCM (windows) */ - NGC_DSP = 12, /* NGC DSP (GC) */ - PCM8_U_int = 13, /* 8bit unsigned PCM (interleaved) */ - PSX_bf = 14, /* PSX ADPCM bad flagged */ - MS_IMA = 15, /* Microsoft IMA ADPCM */ - PCM8_U = 16, /* 8bit unsigned PCM */ - APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */ - ATRAC3 = 18, /* raw ATRAC3 */ - ATRAC3PLUS = 19, /* raw ATRAC3PLUS */ - XMA1 = 20, /* raw XMA1 */ - XMA2 = 21, /* raw XMA2 */ - FFMPEG = 22, /* any headered FFmpeg format */ - AC3 = 23, /* AC3/SPDIF */ - PCFX = 24, /* PC-FX ADPCM */ + PSX = 0, /* PS-ADPCM */ + XBOX = 1, /* XBOX IMA ADPCM */ + NGC_DTK = 2, /* NGC ADP/DTK ADPCM */ + PCM16BE = 3, /* 16-bit big endian PCM */ + PCM16LE = 4, /* 16-bit little endian PCM */ + PCM8 = 5, /* 8-bit PCM */ + SDX2 = 6, /* SDX2 (3D0 games) */ + DVI_IMA = 7, /* DVI IMA ADPCM (high nibble first) */ + MPEG = 8, /* MPEG (MP3) */ + IMA = 9, /* IMA ADPCM (low nibble first) */ + AICA = 10, /* AICA ADPCM (Dreamcast games) */ + MSADPCM = 11, /* MS ADPCM (Windows games) */ + NGC_DSP = 12, /* NGC DSP (Nintendo games) */ + PCM8_U_int = 13, /* 8-bit unsigned PCM (interleaved) */ + PSX_bf = 14, /* PS-ADPCM with bad flags */ + MS_IMA = 15, /* Microsoft IMA ADPCM */ + PCM8_U = 16, /* 8-bit unsigned PCM */ + APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */ + ATRAC3 = 18, /* Raw ATRAC3 */ + ATRAC3PLUS = 19, /* Raw ATRAC3PLUS */ + XMA1 = 20, /* Raw XMA1 */ + XMA2 = 21, /* Raw XMA2 */ + FFMPEG = 22, /* Any headered FFmpeg format */ + AC3 = 23, /* AC3/SPDIF */ + PCFX = 24, /* PC-FX ADPCM */ + PCM4 = 25, /* 4-bit signed PCM (3rd and 4th gen games) */ + PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */ + OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */ } genh_type; typedef struct { genh_type codec; int codec_mode; - size_t interleave; + size_t interleave; + size_t interleave_last; int channels; int32_t sample_rate; @@ -114,6 +118,9 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case FFMPEG: coding = coding_FFmpeg; break; #endif case PCFX: coding = coding_PCFX; break; + case PCM4: coding = coding_PCM4; break; + case PCM4_U: coding = coding_PCM4_U; break; + case OKI16: coding = coding_OKI16; break; default: goto fail; } @@ -137,6 +144,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case coding_PCM16BE: case coding_PCM8: case coding_PCM8_U: + case coding_PCM4: + case coding_PCM4_U: case coding_SDX2: case coding_PSX: case coding_PSX_badflags: @@ -145,6 +154,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case coding_AICA: case coding_APPLE_IMA4: vgmstream->interleave_block_size = genh.interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; if (vgmstream->channels > 1) { if (coding == coding_SDX2) { @@ -185,15 +195,24 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { } } + if (coding == coding_PCM4 || coding == coding_PCM4_U) { + /* high nibble or low nibble first */ + vgmstream->codec_config = genh.codec_mode; + } break; case coding_PCFX: vgmstream->interleave_block_size = genh.interleave; vgmstream->layout_type = layout_interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; if (genh.codec_mode >= 0 && genh.codec_mode <= 3) vgmstream->codec_config = genh.codec_mode; break; + case coding_OKI16: + vgmstream->layout_type = layout_none; + break; + case coding_MS_IMA: if (!genh.interleave) goto fail; /* creates garbage */ @@ -211,11 +230,13 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { if (genh.codec_mode == 1) { /* mono interleave */ coding = coding_XBOX_IMA_int; vgmstream->layout_type = layout_interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; vgmstream->interleave_block_size = genh.interleave; } else { /* 1ch mono, or stereo interleave */ vgmstream->layout_type = genh.interleave ? layout_interleave : layout_none; vgmstream->interleave_block_size = genh.interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) goto fail; /* only 2ch+..+2ch layout is known */ } @@ -229,6 +250,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { if (!genh.interleave) goto fail; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = genh.interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; } else if (genh.coef_interleave_type == 1) { if (!genh.interleave) goto fail; coding = coding_NGC_DSP_subint; @@ -403,8 +425,8 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) { genh->coef_split_spacing = read_32bitLE(0x38,streamFile); } - /* extended fields */ - if (header_size >= 0x54) { + /* extended + reserved fields */ + if (header_size >= 0x100) { genh->num_samples = read_32bitLE(0x40,streamFile); genh->skip_samples = read_32bitLE(0x44,streamFile); /* for FFmpeg based codecs */ genh->skip_samples_mode = read_8bit(0x48,streamFile); /* 0=autodetect, 1=force manual value @ 0x44 */ @@ -414,6 +436,7 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) { if ((genh->codec == XMA1 || genh->codec == XMA2) && genh->codec_mode==0) genh->codec_mode = read_8bit(0x4a,streamFile); genh->data_size = read_32bitLE(0x50,streamFile); + genh->interleave_last = read_32bitLE(0x54,streamFile); } if (genh->data_size == 0) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gin.c b/Frameworks/vgmstream/vgmstream/src/meta/gin.c new file mode 100644 index 000000000..36953a6e5 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/gin.c @@ -0,0 +1,53 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .gin - EA engine sounds [Need for Speed: Most Wanted (multi)] */ +VGMSTREAM * init_vgmstream_gin(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate, num_samples; + + + /* checks */ + if (!check_extensions(streamFile, "gin")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x476E7375) /* "Gnsu" */ + goto fail; + + /* contains mapped values for engine RPM sounds but we'll just play the whole thing */ + /* 0x04: size? "20\00\00"? */ + /* 0x08/0c: min/max float RPM? */ + /* 0x10: RPM up? (pitch/frequency) table size */ + /* 0x14: RPM ??? table size */ + /* always LE even on X360/PS3 */ + + num_samples = read_32bitLE(0x18, streamFile); + sample_rate = read_32bitLE(0x1c, streamFile); + start_offset = 0x20 + + (read_32bitLE(0x10, streamFile) + 1) * 0x04 + + (read_32bitLE(0x14, streamFile) + 1) * 0x04; + channel_count = 1; + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_GIN; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + vgmstream->coding_type = coding_EA_XAS_V0; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x13; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 24c9f5fc9..2807d5196 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -472,8 +472,6 @@ VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_voi(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_khv(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ngc_rkv(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_p3d(STREAMFILE* streamFile); @@ -511,6 +509,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ngc_nst_dsp(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_baf(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE* streamFile); @@ -679,9 +678,11 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE * streamFile); @@ -823,4 +824,6 @@ VGMSTREAM * init_vgmstream_imc_container(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_smp(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_gin(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c index b72fb8858..fc00f4679 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c @@ -174,7 +174,13 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) { loop_end_offset = loop_end * 0x10; } #endif - if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */ + if (loop_end <= body_size / 0x200 && coding_type == coding_PCM16LE) { /* close to body_size */ + /* Gofun-go no Sekai: loops is address * 0x200 */ + loop_flag = 1; + loop_start_offset = loop_start * 0x200; + loop_end_offset = loop_end * 0x200; + } + else if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */ /* Armored Core - Nexus: loops is address * 0x70 */ loop_flag = 1; loop_start_offset = loop_start * 0x70; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_khv.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_khv.c deleted file mode 100644 index e700025fb..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_khv.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* KHV (from Kingdom Hearts 2) */ -/* VAG files with custom headers */ -VGMSTREAM * init_vgmstream_ps2_khv(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 0; - int channel_count; - off_t start_offset; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("khv",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x56414770) /* "VAGp" */ - goto fail; - - loop_flag = (read_32bitBE(0x14,streamFile)!=0); - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x60; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile); - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - vgmstream->meta_type = meta_PS2_KHV; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mic.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mic.c index ee7c917d3..14850d20b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mic.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mic.c @@ -1,75 +1,48 @@ #include "meta.h" -#include "../util.h" - -/* MIC - - PS2 MIC format is an interleaved format found in most of KOEI Games - The header always start the long value 0x800 which is the start - of the BGM datas. - - 2008-05-15 - Fastelbja : First version ... -*/ +#include "../coding/coding.h" +/* .MIC - from KOEI games [Crimson Sea 2 (PS2), Dynasty Tactics 2 (PS2)] */ VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + off_t start_offset; + int loop_flag, channel_count, loop_start, loop_end, sample_rate; + size_t interleave, block_size; - int loop_flag=0; - int channel_count; - int i; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("mic",filename_extension(filename))) goto fail; - - /* check Header */ - if (read_32bitLE(0x00,streamFile) != 0x800) + /* checks */ + if (!check_extensions(streamFile, "mic")) goto fail; - /* check loop */ - loop_flag = (read_32bitLE(0x14,streamFile)!=1); - - channel_count=read_32bitLE(0x08,streamFile); + start_offset = read_32bitLE(0x00,streamFile); + if (start_offset != 0x800) goto fail; + sample_rate = read_32bitLE(0x04,streamFile); + channel_count = read_32bitLE(0x08,streamFile); + interleave = read_32bitLE(0x0c,streamFile); + loop_end = read_32bitLE(0x10,streamFile); + loop_start = read_32bitLE(0x14,streamFile); + loop_flag = (loop_start != 1); + block_size = interleave * channel_count; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - - /* Compression Scheme */ - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x10,streamFile)*14*channel_count; - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile)*14*channel_count; - vgmstream->loop_end_sample = read_32bitLE(0x10,streamFile)*14*channel_count; - } - - vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile); - vgmstream->layout_type = layout_interleave; vgmstream->meta_type = meta_PS2_MIC; + vgmstream->sample_rate = sample_rate; - /* open the file for reading by each channel */ - { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + vgmstream->coding_type = coding_PSX; + vgmstream->interleave_block_size = interleave; + vgmstream->layout_type = layout_interleave; - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(0x800+vgmstream->interleave_block_size*i); - } - } + vgmstream->num_samples = ps_bytes_to_samples(loop_end * block_size, channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start * block_size, channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index c0fc8683b..f4f0d5889 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -10,6 +10,7 @@ static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size); #endif + /* return milliseconds */ static long parse_adtl_marker(unsigned char * marker) { long hh,mm,ss,ms; @@ -236,6 +237,9 @@ fail: return -1; } +static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset); + + VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; riff_fmt_chunk fmt = {0}; @@ -682,17 +686,74 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RIFF_WAVE_MWV; } - if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) goto fail; - return vgmstream; + /* UE4 uses half-interleave mono MSADPCM, try to autodetect without breaking normal MSADPCM */ + if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, streamFile, &fmt, fact_sample_count, start_offset)) { + int ch; + size_t half_interleave = data_size / vgmstream->channels; + + vgmstream->coding_type = coding_MSADPCM_int; + + /* only works with half-interleave as frame_size and interleave are merged ATM*/ + for (ch = 0; ch < vgmstream->channels; ch++) { + vgmstream->ch[ch].channel_start_offset = + vgmstream->ch[ch].offset = start_offset + half_interleave*ch; + } + } + + return vgmstream; fail: close_vgmstream(vgmstream); return NULL; } +/* UE4 MSADPCM is quite normal but has a few minor quirks we can use to detect it */ +static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset) { + + /* stereo only */ + if (fmt->channel_count != 2) + goto fail; + + /* UE4 class is "ADPCM", assume it's the extension too */ + if (!check_extensions(streamFile, "adpcm")) + goto fail; + + /* UE4 encoder doesn't add "fact" */ + if (fact_sample_count != 0) + goto fail; + + /* fixed block size */ + if (fmt->block_size != 0x200) + goto fail; + + /* later UE4 versions use 0x36 (at 0x32 may be fact_samples?) */ + if (fmt->size != 0x32 && fmt->size != 0x36) + goto fail; + + /* size 0x32 in older UE4 matches standard MSADPCM, so add extra detection */ + if (fmt->size == 0x32) { + off_t offset = start_offset; + off_t max_offset = 5 * fmt->block_size; /* try N blocks */ + if (max_offset > get_streamfile_size(streamFile)) + max_offset = get_streamfile_size(streamFile); + + /* their encoder doesn't calculate optimal coefs and uses fixed values every frame + * (could do it for fmt size 0x36 too but maybe they'll fix it in the future) */ + while (offset <= max_offset) { + if (read_8bit(offset+0x00, streamFile) != 0 || read_16bitLE(offset+0x01, streamFile) != 0x00E6) + goto fail; + offset += fmt->block_size; + } + } + + return 1; +fail: + return 0; +} + VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; riff_fmt_chunk fmt = {0}; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sd9.c b/Frameworks/vgmstream/vgmstream/src/meta/sd9.c index ad097aafa..9c373bf86 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sd9.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sd9.c @@ -1,81 +1,61 @@ #include "meta.h" #include "../util.h" -/* SD9 (found in beatmaniaIIDX16 - EMPRESS (Arcade) */ +/* SD9 (found in beatmania IIDX Arcade games) */ VGMSTREAM * init_vgmstream_sd9(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + int loop_flag, channel_count; - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sd9",filename_extension(filename))) goto fail; + /* check extension */ + if (!check_extensions(streamFile, "sd9")) + goto fail; /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x53443900) /* SD9 */ - goto fail; - if (read_32bitBE(0x20,streamFile) != 0x52494646) /* RIFF */ - goto fail; - if (read_32bitBE(0x28,streamFile) != 0x57415645) /* WAVE */ - goto fail; - if (read_32bitBE(0x2c,streamFile) != 0x666D7420) /* fmt */ - goto fail; - if (read_32bitBE(0x72,streamFile) != 0x64617461) /* data */ - goto fail; + if (read_32bitBE(0x0, streamFile) != 0x53443900) /* SD9 */ + goto fail; + if (read_32bitBE(0x20, streamFile) != 0x52494646) /* RIFF */ + goto fail; + if (read_32bitBE(0x28, streamFile) != 0x57415645) /* WAVE */ + goto fail; + if (read_32bitBE(0x2c, streamFile) != 0x666D7420) /* fmt */ + goto fail; + if (read_32bitBE(0x72, streamFile) != 0x64617461) /* data */ + goto fail; - loop_flag = (read_16bitLE(0x0e,streamFile)==0x1); - channel_count = read_16bitLE(0x36,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + /* Probably better to check if loop end exists and use as loop flag. + Blame SD9s from beatmania IIDX 21: Spada that have a flase flag + but still "loop" */ + + //loop_flag = (read_16bitLE(0x0e,streamFile)==0x1); + loop_flag = read_32bitLE(0x18, streamFile); // use loop end + channel_count = read_16bitLE(0x36, streamFile); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ + /* fill in the vital statistics */ start_offset = 0x7a; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x38,streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = read_32bitLE(0x38, streamFile); vgmstream->coding_type = coding_MSADPCM; - vgmstream->num_samples = read_32bitLE(0x6e,streamFile); - if (loop_flag) { - if (read_16bitLE(0x1C,streamFile)==1) - { - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile)/2/channel_count; - vgmstream->loop_end_sample = read_32bitLE(0x18,streamFile)/2/channel_count; - } - else - { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } + vgmstream->num_samples = read_32bitLE(0x6e, streamFile); + if (loop_flag) { + vgmstream->loop_start_sample = read_32bitLE(0x14, streamFile) / 2 / channel_count; + vgmstream->loop_end_sample = read_32bitLE(0x18, streamFile) / 2 / channel_count; } + vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = read_16bitLE(0x40,streamFile); + vgmstream->interleave_block_size = read_16bitLE(0x40, streamFile); vgmstream->meta_type = meta_SD9; /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - - return vgmstream; + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; + return vgmstream; fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; -} +} \ No newline at end of file diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 4dbb72a75..6bee9bb5d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -6,31 +6,34 @@ /* known TXTH types */ typedef enum { - PSX = 0, /* PSX ADPCM */ - XBOX = 1, /* XBOX IMA ADPCM */ - NGC_DTK = 2, /* NGC ADP/DTK ADPCM */ - PCM16BE = 3, /* 16bit big endian PCM */ - PCM16LE = 4, /* 16bit little endian PCM */ - PCM8 = 5, /* 8bit PCM */ - SDX2 = 6, /* SDX2 (3D0 games) */ - DVI_IMA = 7, /* DVI IMA ADPCM */ - MPEG = 8, /* MPEG (MP3) */ - IMA = 9, /* IMA ADPCM */ - AICA = 10, /* AICA ADPCM (dreamcast) */ - MSADPCM = 11, /* MS ADPCM (windows) */ - NGC_DSP = 12, /* NGC DSP (GC) */ - PCM8_U_int = 13, /* 8bit unsigned PCM (interleaved) */ - PSX_bf = 14, /* PSX ADPCM bad flagged */ - MS_IMA = 15, /* Microsoft IMA ADPCM */ - PCM8_U = 16, /* 8bit unsigned PCM */ - APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */ - ATRAC3 = 18, /* raw ATRAC3 */ - ATRAC3PLUS = 19, /* raw ATRAC3PLUS */ - XMA1 = 20, /* raw XMA1 */ - XMA2 = 21, /* raw XMA2 */ - FFMPEG = 22, /* any headered FFmpeg format */ - AC3 = 23, /* AC3/SPDIF */ - PCFX = 24, /* PC-FX ADPCM */ + PSX = 0, /* PS-ADPCM */ + XBOX = 1, /* XBOX IMA ADPCM */ + NGC_DTK = 2, /* NGC ADP/DTK ADPCM */ + PCM16BE = 3, /* 16-bit big endian PCM */ + PCM16LE = 4, /* 16-bit little endian PCM */ + PCM8 = 5, /* 8-bit PCM */ + SDX2 = 6, /* SDX2 (3D0 games) */ + DVI_IMA = 7, /* DVI IMA ADPCM (high nibble first) */ + MPEG = 8, /* MPEG (MP3) */ + IMA = 9, /* IMA ADPCM (low nibble first) */ + AICA = 10, /* AICA ADPCM (Dreamcast games) */ + MSADPCM = 11, /* MS ADPCM (Windows games) */ + NGC_DSP = 12, /* NGC DSP (Nintendo games) */ + PCM8_U_int = 13, /* 8-bit unsigned PCM (interleaved) */ + PSX_bf = 14, /* PS-ADPCM with bad flags */ + MS_IMA = 15, /* Microsoft IMA ADPCM */ + PCM8_U = 16, /* 8-bit unsigned PCM */ + APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */ + ATRAC3 = 18, /* Raw ATRAC3 */ + ATRAC3PLUS = 19, /* Raw ATRAC3PLUS */ + XMA1 = 20, /* Raw XMA1 */ + XMA2 = 21, /* Raw XMA2 */ + FFMPEG = 22, /* Any headered FFmpeg format */ + AC3 = 23, /* AC3/SPDIF */ + PCFX = 24, /* PC-FX ADPCM */ + PCM4 = 25, /* 4-bit signed PCM (3rd and 4th gen games) */ + PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */ + OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */ } txth_type; typedef struct { @@ -46,6 +49,7 @@ typedef struct { uint32_t id_offset; uint32_t interleave; + uint32_t interleave_last; uint32_t channels; uint32_t sample_rate; @@ -176,6 +180,9 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case FFMPEG: coding = coding_FFmpeg; break; #endif case PCFX: coding = coding_PCFX; break; + case PCM4: coding = coding_PCM4; break; + case PCM4_U: coding = coding_PCM4_U; break; + case OKI16: coding = coding_OKI16; break; default: goto fail; } @@ -212,6 +219,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case coding_PCM16BE: case coding_PCM8: case coding_PCM8_U: + case coding_PCM4: + case coding_PCM4_U: case coding_SDX2: case coding_PSX: case coding_PSX_badflags: @@ -220,6 +229,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case coding_AICA: case coding_APPLE_IMA4: vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; if (vgmstream->channels > 1) { if (coding == coding_SDX2) { @@ -260,15 +270,25 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->ch[i].adpcm_step_index = 0x7f; } } + + if (coding == coding_PCM4 || coding == coding_PCM4_U) { + /* high nibble or low nibble first */ + vgmstream->codec_config = txth.codec_mode; + } break; case coding_PCFX: vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; vgmstream->layout_type = layout_interleave; if (txth.codec_mode >= 0 && txth.codec_mode <= 3) vgmstream->codec_config = txth.codec_mode; break; + case coding_OKI16: + vgmstream->layout_type = layout_none; + break; + case coding_MS_IMA: if (!txth.interleave) goto fail; /* creates garbage */ @@ -287,10 +307,12 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding = coding_XBOX_IMA_int; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; } else { /* 1ch mono, or stereo interleave */ vgmstream->layout_type = txth.interleave ? layout_interleave : layout_none; vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) goto fail; /* only 2ch+..+2ch layout is known */ } @@ -303,6 +325,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { if (txth.channels > 1 && txth.codec_mode == 0) { if (!txth.interleave) goto fail; vgmstream->layout_type = layout_interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; vgmstream->interleave_block_size = txth.interleave; } else if (txth.channels > 1 && txth.codec_mode == 1) { if (!txth.interleave) goto fail; @@ -466,8 +489,10 @@ fail: static STREAMFILE * open_txth(STREAMFILE * streamFile) { + char basename[PATH_LIMIT]; char filename[PATH_LIMIT]; char fileext[PATH_LIMIT]; + const char *subext; STREAMFILE * streamText; /* try "(path/)(name.ext).txth" */ @@ -476,6 +501,22 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { streamText = open_streamfile(streamFile,filename); if (streamText) return streamText; + /* try "(path/)(.sub.ext).txth" */ + get_streamfile_basename(streamFile,basename,PATH_LIMIT); + subext = filename_extension(basename); + if (subext != NULL) { + get_streamfile_path(streamFile,filename,PATH_LIMIT); + get_streamfile_ext(streamFile,fileext,PATH_LIMIT); + strcat(filename,"."); + strcat(filename, subext); + strcat(filename,"."); + strcat(filename, fileext); + strcat(filename, ".txth"); + + streamText = open_streamfile(streamFile,filename); + if (streamText) return streamText; + } + /* try "(path/)(.ext).txth" */ get_streamfile_path(streamFile,filename,PATH_LIMIT); get_streamfile_ext(streamFile,fileext,PATH_LIMIT); @@ -583,6 +624,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG; else if (0==strcmp(val,"AC3")) txth->codec = AC3; else if (0==strcmp(val,"PCFX")) txth->codec = PCFX; + else if (0==strcmp(val,"PCM4")) txth->codec = PCM4; + else if (0==strcmp(val,"PCM4_U")) txth->codec = PCM4_U; + else if (0==strcmp(val,"OKI16")) txth->codec = OKI16; else goto fail; } else if (0==strcmp(key,"codec_mode")) { @@ -617,6 +661,15 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail; } } + else if (0==strcmp(key,"interleave_last")) { + if (0==strcmp(val,"auto")) { + if (txth->channels > 0 && txth->interleave > 0) + txth->interleave_last = (txth->data_size % (txth->interleave * txth->channels)) / txth->channels; + } + else { + if (!parse_num(txth->streamHead,txth,val, &txth->interleave_last)) goto fail; + } + } else if (0==strcmp(key,"channels")) { if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail; } @@ -863,6 +916,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v } else { /* known field */ if (0==strcmp(val,"interleave")) *out_value = txth->interleave; + if (0==strcmp(val,"interleave_last")) *out_value = txth->interleave_last; else if (0==strcmp(val,"channels")) *out_value = txth->channels; else if (0==strcmp(val,"sample_rate")) *out_value = txth->sample_rate; else if (0==strcmp(val,"start_offset")) *out_value = txth->start_offset; @@ -913,6 +967,9 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { case PCM8_U_int: case PCM8_U: return pcm_bytes_to_samples(bytes, txth->channels, 8); + case PCM4: + case PCM4_U: + return pcm_bytes_to_samples(bytes, txth->channels, 4); case MSADPCM: if (!txth->interleave) return 0; return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels); @@ -938,7 +995,8 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { case AICA: return aica_bytes_to_samples(bytes, txth->channels); case PCFX: - return pcfx_bytes_to_samples(bytes, txth->channels); + case OKI16: + return oki_bytes_to_samples(bytes, txth->channels); /* untested */ case SDX2: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index 75af0e623..dad178830 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -32,7 +32,7 @@ typedef struct { int subtypes_count[9]; } ubi_bao_header; -static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset); +static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong); static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile); static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE *streamFile); static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile); @@ -265,7 +265,7 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { size_t index_size, index_header_size; off_t bao_offset, resources_offset; int target_subsong = streamFile->stream_index; - uint8_t *index_buffer = NULL; + STREAMFILE *streamIndex = NULL; STREAMFILE *streamTest = NULL; @@ -274,6 +274,8 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { goto fail; /* index and resources always LE */ + if (target_subsong == 0) target_subsong = 1; + /* 0x01(3): version, major/minor/release (numbering continues from .sb0/sm0) */ index_size = read_32bitLE(0x04, streamFile); /* can be 0, not including */ resources_offset = read_32bitLE(0x08, streamFile); /* always found even if not used */ @@ -294,21 +296,22 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { VGM_LOG("BAO: index too big\n"); goto fail; } - index_buffer = malloc(index_size); - read_streamfile(index_buffer, index_header_size, index_size, streamFile); - /* use smaller I/O buffer for performance, as this read lots of small BAO headers all over the place */ + /* use smaller I/O buffers for performance, as this read lots of small headers all over the place */ + streamIndex = reopen_streamfile(streamFile, index_size); + if (!streamIndex) goto fail; + streamTest = reopen_streamfile(streamFile, 0x100); if (!streamTest) goto fail; /* parse index to get target subsong N = Nth audio header BAO */ bao_offset = index_header_size + index_size; for (i = 0; i < index_entries; i++) { - //uint32_t bao_id = get_32bitLE(index_buffer + 0x08*i+ 0x00); - size_t bao_size = get_32bitLE(index_buffer + 0x08*i + 0x04); + //uint32_t bao_id = read_32bitLE(index_header_size + 0x08*i + 0x00, streamIndex); + size_t bao_size = read_32bitLE(index_header_size + 0x08*i + 0x04, streamIndex); /* parse and continue to find out total_subsongs */ - if (!parse_bao(bao, streamTest, bao_offset)) + if (!parse_bao(bao, streamTest, bao_offset, target_subsong)) goto fail; bao_offset += bao_size; /* files simply concat BAOs */ @@ -407,21 +410,20 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { ;VGM_LOG("BAO stream: id=%x, offset=%x, size=%x, res=%s\n", bao->stream_id, (uint32_t)bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal")); - free(index_buffer); + close_streamfile(streamIndex); close_streamfile(streamTest); return 1; fail: - free(index_buffer); + close_streamfile(streamIndex); close_streamfile(streamTest); return 0; } /* parse a single BAO (binary audio object) descriptor */ -static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) { +static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong) { int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; uint32_t bao_version, descriptor_type, descriptor_subtype; size_t header_size; - int target_subsong = streamFile->stream_index; /* 0x00(1): class? usually 0x02 but older BAOs have 0x01 too */ @@ -463,11 +465,11 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) /* for debugging purposes */ switch(descriptor_subtype) { case 0x00000001: bao->subtypes_count[1]++; break; /* standard */ - case 0x00000002: bao->subtypes_count[2]++; break; /* multilayer??? related to other header BAOs? */ + case 0x00000002: bao->subtypes_count[2]++; break; /* related to localized BAOs? (.lpk) */ case 0x00000003: bao->subtypes_count[3]++; break; /* related to other header BAOs? */ case 0x00000004: bao->subtypes_count[4]++; break; /* related to other header BAOs? */ case 0x00000005: bao->subtypes_count[5]++; break; /* related to other header BAOs? */ - case 0x00000006: bao->subtypes_count[6]++; break; /* some multilayer/table? may contain sounds??? */ + case 0x00000006: bao->subtypes_count[6]++; break; /* multilayer with multiple sounds */ case 0x00000007: bao->subtypes_count[7]++; break; /* related to other header BAOs? */ case 0x00000008: bao->subtypes_count[8]++; break; /* ? (almost empty with some unknown value) */ default: @@ -476,13 +478,26 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) } //;VGM_ASSERT(descriptor_subtype != 0x01, "UBI BAO: subtype %x at %lx (%lx)\n", descriptor_subtype, offset, offset+header_size+0x04); + if (descriptor_subtype == 0x06) { + ;VGM_LOG("UBI BAO: layer subtype at %lx (%lx)\n", offset, offset+header_size+0x04); + /* todo fix layers + * for scott pilgrim: + * - 0x50: layer count + * - 0x78: layer headers size? + * - 0x7c: prefetch size + * - 0xb4: layer header xN (size 0x30) + * (this header has sample rate, channels, codec, various sizes/num samples) + * - 0x114: good ol' Ubi SB layer header v0x00100009 with classic v0x03 blocked data + * (standard prefetch style, part of data then cut in the middle and links to stream) + */ + goto fail; + } + /* ignore unknown subtypes */ if (descriptor_subtype != 0x00000001) return 1; bao->total_subsongs++; - if (target_subsong == 0) target_subsong = 1; - if (target_subsong != bao->total_subsongs) return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index b9bef55e5..242e642aa 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -1,16 +1,19 @@ #include "meta.h" +#include "../layout/layout.h" #include "../coding/coding.h" +#include "ubi_sb_streamfile.h" -typedef enum { UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG } ubi_sb_codec; +typedef enum { UBI_IMA, UBI_UNK, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV } ubi_sb_codec; typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform; +typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE } ubi_sb_type; + typedef struct { ubi_sb_platform platform; + int is_psp_old; int big_endian; - int total_streams; - int is_external; - int autodetect_external; - ubi_sb_codec codec; + int total_subsongs; + int bank_subsongs; /* map base header info */ off_t map_start; @@ -21,103 +24,137 @@ typedef struct { uint32_t map_zero; off_t map_offset; off_t map_size; - char map_name[255]; + char map_name[0x24]; uint32_t map_unknown; - /* SB main/fixed info */ - uint32_t version; - uint32_t version_empty; + /* SB info (some values are derived depending if it's standard sbX or map sbX) */ + int is_map; + uint32_t version; /* 16b+16b major+minor version */ + uint32_t version_empty; /* map sbX versions are empty */ + /* events (often share header_id/type with some descriptors, + * but may exist without headers or header exist without them) */ size_t section1_num; + size_t section1_offset; + /* descriptors, audio header or other config types */ size_t section2_num; + size_t section2_offset; + /* internal streams table, referenced by each header */ size_t section3_num; + size_t section3_offset; + /* section with sounds in some map versions */ size_t section4_num; + size_t section4_offset; + /* extra table, config for certain types (DSP coefs, external resources, layer headers, etc) */ size_t sectionX_size; + size_t sectionX_offset; + /* unknown, usually -1 but can be others (0/1/2/etc) */ int flag1; int flag2; - /* maps data config */ - int is_map; - int map_version; - /* header/stream info config */ - /* audio header varies slightly per game but not enough parse case by case, + /* audio header varies slightly per game/version but not enough parse case by case, * instead we configure sizes and offsets to where each variable is */ + int map_version; size_t section1_entry_size; size_t section2_entry_size; size_t section3_entry_size; size_t resource_name_size; - off_t cfg_stream_size; - off_t cfg_stream_offset; - off_t cfg_extra_offset; - off_t cfg_stream_id; - off_t cfg_stream_type; - - off_t cfg_external_flag; /* if the song is internal or external */ - off_t cfg_samples_flag; /* some headers have two possible locations for num_samples */ - off_t cfg_samples_bitflag; - off_t cfg_num_samples; - off_t cfg_num_samples2; - off_t cfg_sample_rate; - off_t cfg_channels; - off_t cfg_stream_name; /* where the resource name is within the header */ - off_t cfg_extra_name; /* where the resource name is within sectionX */ - off_t cfg_xma_offset; - int has_short_channels; /* channels value can be 16b or 32b */ - int has_internal_names; /* resource name doubles as internal name in some cases */ - int has_extra_name_flag; /* if cfg_extra_name is set (since often extra_name = -1 is 'not set' and >= 0 is offset) */ - int has_rotating_ids; /* stream id isn't set but is assigned using sequential rotation of sorts */ - //todo see if has_extra_name_flag can be removed - - /* derived */ - size_t section1_offset; - size_t section2_offset; - size_t section3_offset; - size_t section4_offset; - size_t sectionX_offset; - size_t sounds_offset; + /* type 0x01 (audio) config */ + off_t cfga_extra_offset; + off_t cfga_stream_size; + off_t cfga_stream_offset; + off_t cfga_stream_type; + off_t cfga_group_id; + off_t cfga_external_flag; + off_t cfga_loop_flag; + off_t cfga_num_samples; + off_t cfga_num_samples2; + off_t cfga_sample_rate; + off_t cfga_channels; + off_t cfga_stream_name; + off_t cfga_extra_name; + off_t cfga_xma_offset; + int cfga_and_external_flag; + int cfga_and_loop_flag; + int cfga_and_group_id; + int cfga_has_internal_names; + /* type 0x05/0c (sequence) config */ + off_t cfgs_extra_offset; + off_t cfgs_sequence_loop; + off_t cfgs_sequence_single; + off_t cfgs_sequence_count; + off_t cfgs_entry_number; + size_t sequence_entry_size; + /* type 0x06/0d (layer) config */ + off_t cfgl_extra_offset; + off_t cfgl_layer_count; + off_t cfgl_stream_size; + off_t cfgl_stream_offset; + off_t cfgl_stream_name; + off_t cfgl_extra_name; + off_t cfgl_sample_rate; + off_t cfgl_channels; + off_t cfgl_stream_type; + off_t cfgl_num_samples; + size_t layer_entry_size; /* header/stream info */ - uint32_t header_id; /* 16b+16b group+sound id identifier (should be unique within sbX, but not smX) */ - uint32_t header_type; /* audio type (we only need 'standard audio' or 'layered audio') */ - size_t stream_size; /* size of the audio data */ - off_t stream_offset; /* offset within the data section (internal) or absolute (external) to the audio */ + ubi_sb_type type; /* unified type */ + ubi_sb_codec codec; /* unified codec */ + int header_index; /* entry number within section2 */ + off_t header_offset; /* entry offset within section2 */ + uint32_t header_id; /* 16b+16b group+sound identifier (unique within a sbX, but not smX), may start from 0 */ + uint32_t header_type; /* parsed type (we only need audio types) */ off_t extra_offset; /* offset within sectionX to extra data */ - - uint32_t stream_id; /* internal resource id needed to locate info */ + off_t stream_offset; /* offset within the data section (internal) or absolute (external) to the audio */ + size_t stream_size; /* size of the audio data */ uint32_t stream_type; /* rough codec value */ - int num_samples; /* usually only for external resources */ + uint32_t group_id; /* internal id to reference in section3 */ + + int loop_flag; /* stream loops (normally internal sfx, but also external music) */ + int loop_start; /* usually 0 */ + int num_samples; /* should match manually calculated samples */ int sample_rate; int channels; - char resource_name[255]; /* filename to the external stream, or internal stream info for some games */ - char readable_name[255]; /* constructed name to show externally */ + off_t xma_header_offset; /* some XMA have extra header stuff */ - int header_index; /* position within section2 (considering all possible header types) */ - off_t header_offset; /* offset of parsed audio header */ - off_t xma_header_offset; /* XMA has some extra header stuff*/ + int layer_count; /* number of layers in a layer type */ + int sequence_count; /* number of segments in a sequence type */ + int sequence_chain[64]; /* sequence of entry numbers */ + int sequence_loop; /* chain index to loop */ + int sequence_single; /* if que sequence plays once (loops by default) */ - int types[16]; /* counts for each possible header types, for debugging */ + int is_external; /* stream is in a external file */ + char resource_name[0x24]; /* filename to the external stream, or internal stream info for some games */ + + char readable_name[255]; /* final subsong name */ + int types[16]; /* counts each header types, for debugging */ + int allowed_types[16]; } ubi_sb_header; -static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *streamFile); -static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_stream); +static VGMSTREAM * init_vgmstream_ubi_sb_header(ubi_sb_header *sb, STREAMFILE* streamTest, STREAMFILE *streamFile); +static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subsong); +static int parse_header(ubi_sb_header * sb, STREAMFILE *streamFile, off_t offset, int index); static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile); static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile); -/* .SBx - banks from Ubisoft's sound engine ("DARE" / "UbiSound Driver") games in ~2000-2008+ */ + +/* .SBx - banks from Ubisoft's DARE (Digital Audio Rendering Engine) engine games in ~2000-2008+ */ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { + VGMSTREAM* vgmstream = NULL; STREAMFILE *streamTest = NULL; int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL; - ubi_sb_header sb = { 0 }; - int ok; - int target_stream = streamFile->stream_index; + ubi_sb_header sb = {0}; + int target_subsong = streamFile->stream_index; - /* check extension (number represents the platform, see later) */ + + /* checks (number represents the platform, see later) */ if (!check_extensions(streamFile, "sb0,sb1,sb2,sb3,sb4,sb5,sb6,sb7")) goto fail; /* .sbX (sound bank) is a small multisong format (loaded in memory?) that contains SFX data * but can also reference .ss0/ls0 (sound stream) external files for longer streams. - * A companion .sp0 (sound project) describes files and if it uses BANKs (.sb0) or MAPs (.sm0). */ + * A companion .sp0 (sound project) describes files and if it uses BANKs (.sbX) or MAPs (.smX). */ /* PLATFORM DETECTION */ @@ -129,40 +166,39 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { read_32bit = read_32bitLE; } - if (target_stream == 0) target_stream = 1; + if (target_subsong <= 0) target_subsong = 1; - /* use smaller I/O buffer for performance, as this read lots of small headers all over the place */ + /* use smaller header buffer for performance */ streamTest = reopen_streamfile(streamFile, 0x100); if (!streamTest) goto fail; /* SB HEADER */ - /* SBx layout: base header, section1, section2, extra section, section3, data (all except base header can be null) */ + /* SBx layout: header, section1, section2, extra section, section3, data (all except header can be null) */ sb.is_map = 0; - sb.version = read_32bit(0x00, streamFile); /* 16b+16b major/minor version */ - sb.section1_num = read_32bit(0x04, streamFile); /* group headers? */ - sb.section2_num = read_32bit(0x08, streamFile); /* streams headers (internal or external) */ - sb.section3_num = read_32bit(0x0c, streamFile); /* internal streams table */ - sb.sectionX_size = read_32bit(0x10, streamFile); /* extra table, unknown (config for non-audio types) except with DSP = coefs */ - sb.flag1 = read_32bit(0x14, streamFile); /* unknown, usually -1 but can be others (0/1/2/etc) */ - sb.flag2 = read_32bit(0x18, streamFile); /* unknown, usually -1 but can be others */ + sb.version = read_32bit(0x00, streamFile); + sb.section1_num = read_32bit(0x04, streamFile); + sb.section2_num = read_32bit(0x08, streamFile); + sb.section3_num = read_32bit(0x0c, streamFile); + sb.sectionX_size = read_32bit(0x10, streamFile); + sb.flag1 = read_32bit(0x14, streamFile); + sb.flag2 = read_32bit(0x18, streamFile); - ok = config_sb_version(&sb, streamFile); - if (!ok) goto fail; + if (!config_sb_version(&sb, streamFile)) + goto fail; sb.section1_offset = 0x1c; sb.section2_offset = sb.section1_offset + sb.section1_entry_size * sb.section1_num; sb.sectionX_offset = sb.section2_offset + sb.section2_entry_size * sb.section2_num; sb.section3_offset = sb.sectionX_offset + sb.sectionX_size; - sb.sounds_offset = sb.section3_offset + sb.section3_entry_size * sb.section3_num; - if (!parse_sb_header(&sb, streamTest, target_stream)) + if (!parse_sb_header(&sb, streamTest, target_subsong)) goto fail; - close_streamfile(streamTest); - /* CREATE VGMSTREAM */ - return init_vgmstream_ubi_sb_main(&sb, streamFile); + vgmstream = init_vgmstream_ubi_sb_header(&sb, streamTest, streamFile); + close_streamfile(streamTest); + return vgmstream; fail: close_streamfile(streamTest); @@ -171,20 +207,20 @@ fail: /* .SMx - maps (sets of custom SBx files) also from Ubisoft's sound engine games in ~2000-2008+ */ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { + VGMSTREAM* vgmstream = NULL; STREAMFILE *streamTest = NULL; int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL; - //int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL; - ubi_sb_header sb = { 0 }; - int ok, i; - int target_stream = streamFile->stream_index; + ubi_sb_header sb = {0}, target_sb = {0}; + int target_subsong = streamFile->stream_index; + int i; - /* check extension (number represents the platform, see later) */ + /* checks (number represents platform, lmX are localized variations) */ if (!check_extensions(streamFile, "sm0,sm1,sm2,sm3,sm4,sm5,sm6,sm7,lm0,lm1,lm2,lm3,lm4,lm5,lm6,lm7")) goto fail; /* .smX (sound map) is a set of slightly different sbX files, compiled into one "map" file. - * Map has a sbX per named area (example: menu, level1, boss1, level2...). + * Map has a sbX (called "submap") per named area (example: menu, level1, boss1, level2...). * This counts subsongs from all sbX, so totals can be massive, but there are splitters into mini-smX. */ @@ -197,39 +233,38 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { read_32bit = read_32bitLE; } - if (target_stream == 0) target_stream = 1; + if (target_subsong <= 0) target_subsong = 1; - /* use smaller I/O buffer for performance, as this read lots of small headers all over the place */ + /* use smaller header buffer for performance */ streamTest = reopen_streamfile(streamFile, 0x100); if (!streamTest) goto fail; /* SM BASE HEADER */ - /* SMx layout: base header with N map area offset/sizes (some? offsets within a SBx are relative) */ - /* SBx layout: base header, section1, section2, section4, extra section, section3, data (all except base header can be null?) */ + /* SMx layout: header with N map area offset/sizes + custom SBx with relative offsets */ sb.is_map = 1; sb.version = read_32bit(0x00, streamFile); sb.map_start = read_32bit(0x04, streamFile); sb.map_num = read_32bit(0x08, streamFile); - ok = config_sb_version(&sb, streamFile); - if (!ok || sb.map_version == 0) goto fail; + if (!config_sb_version(&sb, streamFile)) + goto fail; - sb.map_entry_size = (sb.map_version < 2) ? 0x30 : 0x34; for (i = 0; i < sb.map_num; i++) { off_t offset = sb.map_start + i * sb.map_entry_size; - /* SM AREA HEADER */ + /* SUBMAP HEADER */ sb.map_type = read_32bit(offset + 0x00, streamFile); /* usually 0/1=first, 0=rest */ sb.map_zero = read_32bit(offset + 0x04, streamFile); sb.map_offset = read_32bit(offset + 0x08, streamFile); sb.map_size = read_32bit(offset + 0x0c, streamFile); /* includes sbX header, but not internal streams */ - read_string(sb.map_name, 0x20+1, offset + 0x10, streamFile); /* null-terminated and may contain garbage after null */ + read_string(sb.map_name, sizeof(sb.map_name), offset + 0x10, streamFile); /* null-terminated and may contain garbage after null */ if (sb.map_version >= 3) sb.map_unknown = read_32bit(offset + 0x30, streamFile); /* uncommon, id/config? longer name? mem garbage? */ /* SB HEADER */ + /* SBx layout: base header, section1, section2, section4, extra section, section3, data (all except header can be null?) */ sb.version_empty = read_32bit(sb.map_offset + 0x00, streamFile); /* sbX in maps don't set version */ sb.section1_offset = read_32bit(sb.map_offset + 0x04, streamFile) + sb.map_offset; sb.section1_num = read_32bit(sb.map_offset + 0x08, streamFile); @@ -254,119 +289,97 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { sb.sectionX_offset += sb.section4_offset; /* for some reason, this is relative to section 4 here */ } - VGM_ASSERT(sb.map_type != 0 && sb.map_type != 1, "UBI SM: unknown map_type %x\n", (uint32_t)offset); - VGM_ASSERT(sb.map_zero != 0, "UBI SM: unknown map_zero %x\n", (uint32_t)offset); + VGM_ASSERT(sb.map_type != 0 && sb.map_type != 1, "UBI SM: unknown map_type at %x\n", (uint32_t)offset); + VGM_ASSERT(sb.map_zero != 0, "UBI SM: unknown map_zero at %x\n", (uint32_t)offset); //;VGM_ASSERT(sb.map_unknown != 0, "UBI SM: unknown map_unknown at %x\n", (uint32_t)offset); - VGM_ASSERT(sb.version_empty != 0, "UBI SM: unknown version_empty %x\n", (uint32_t)offset); + VGM_ASSERT(sb.version_empty != 0, "UBI SM: unknown version_empty at %x\n", (uint32_t)offset); - if (!parse_sb_header(&sb, streamTest, target_stream)) + if (!parse_sb_header(&sb, streamTest, target_subsong)) goto fail; + + /* snapshot of current sb if subsong was found + * (it gets rewritten and we need exact values for sequences and stuff) */ + if (sb.type != UBI_NONE) { + target_sb = sb; /* memcpy */ + sb.type = UBI_NONE; /* reset parsed flag */ + } } - if (sb.total_streams == 0) { - VGM_LOG("UBI SB: no streams\n"); - goto fail; - } - - if (target_stream < 0 || target_stream > sb.total_streams) { - VGM_LOG("UBI SB: wrong target stream (target=%i, total=%i)\n", target_stream, sb.total_streams); - goto fail; - } - - close_streamfile(streamTest); + target_sb.total_subsongs = sb.total_subsongs; /* CREATE VGMSTREAM */ - return init_vgmstream_ubi_sb_main(&sb, streamFile); + vgmstream = init_vgmstream_ubi_sb_header(&target_sb, streamTest, streamFile); + close_streamfile(streamTest); + return vgmstream; fail: close_streamfile(streamTest); return NULL; } -static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *streamFile) { +/* ************************************************************************* */ + +static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *streamHead, STREAMFILE *streamData, off_t start_offset) { VGMSTREAM * vgmstream = NULL; - STREAMFILE *streamData = NULL; - off_t start_offset; - int loop_flag = 0; - - /* open external stream if needed */ - if (sb->autodetect_external) { /* works most of the time but could give false positives */ - VGM_LOG("UBI SB: autodetecting external stream '%s'\n", sb->resource_name); - - streamData = open_streamfile_by_filename(streamFile,sb->resource_name); - if (!streamData) { - streamData = streamFile; /* assume internal */ - if (sb->stream_size > get_streamfile_size(streamData)) { - VGM_LOG("UBI SB: expected external stream\n"); - goto fail; - } - } else { - sb->is_external = 1; - } - } - else if (sb->is_external) { - streamData = open_streamfile_by_filename(streamFile,sb->resource_name); - if (!streamData) { - VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name); - goto fail; - } - } - else { - streamData = streamFile; - } - //;VGM_LOG("UBI SB: stream offset=%x, size=%x, external=%i\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external); - - start_offset = sb->stream_offset; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(sb->channels,loop_flag); + vgmstream = allocate_vgmstream(sb->channels, sb->loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = sb->sample_rate; - vgmstream->num_streams = sb->total_streams; - vgmstream->stream_size = sb->stream_size; vgmstream->meta_type = meta_UBI_SB; + vgmstream->sample_rate = sb->sample_rate; + vgmstream->num_streams = sb->total_subsongs; + vgmstream->stream_size = sb->stream_size; + + vgmstream->num_samples = sb->num_samples; + vgmstream->loop_start_sample = sb->loop_start; + vgmstream->loop_end_sample = sb->num_samples; switch(sb->codec) { - case UBI_ADPCM: + case UBI_IMA: vgmstream->coding_type = coding_UBI_IMA; vgmstream->layout_type = layout_none; - vgmstream->num_samples = ubi_ima_bytes_to_samples(sb->stream_size, sb->channels, streamData, start_offset); break; + case UBI_UNK: + // todo some kind of blocked layout + Ubi 4/6-bit ADPCM? + // used in Splinter Cell and some Myst IV banks (ex. puzzle_si_splintercell.sb2) + VGM_LOG("UBI SB: unsupported Ubi ADPCM found\n"); + goto fail; + case RAW_PCM: vgmstream->coding_type = coding_PCM16LE; /* always LE even on Wii */ vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(sb->stream_size, sb->channels, 16); break; case RAW_PSX: vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = (sb->stream_type == 0x00) ? sb->stream_size / sb->channels : 0x10; /* TODO: needs testing */ - vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels) ; + if (vgmstream->num_samples == 0) { /* early PS2 games may not set it for a few internal streams */ + vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels) ; + vgmstream->loop_end_sample = vgmstream->num_samples; + } break; case RAW_XBOX: vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_none; - vgmstream->num_samples = xbox_ima_bytes_to_samples(sb->stream_size, sb->channels); break; case RAW_DSP: vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = sb->stream_size / sb->channels; - vgmstream->num_samples = dsp_bytes_to_samples(sb->stream_size, sb->channels); + vgmstream->interleave_block_size = align_size_to_block(sb->stream_size / sb->channels, 0x08); /* frame-aligned */ /* DSP extra info entry size is 0x40 (first/last 0x10 = unknown), per channel */ - dsp_read_coefs_be(vgmstream,streamFile,sb->extra_offset + 0x10, 0x40); + dsp_read_coefs_be(vgmstream,streamHead,sb->extra_offset + 0x10, 0x40); break; case FMT_VAG: - /* skip VAG header (some sb4 use VAG and others raw PSX) */ + /* skip VAG header (some sb4 use VAG and others raw PSX) */ //todo remove if (read_32bitBE(start_offset, streamData) == 0x56414770) { /* "VAGp" */ start_offset += 0x30; sb->stream_size -= 0x30; @@ -375,15 +388,13 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = sb->stream_size / sb->channels; - vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels); - break; #ifdef VGM_USE_FFMPEG case FMT_AT3: { ffmpeg_codec_data *ffmpeg_data; - /* skip weird value (3, 4) in Brothers in Arms: D-Day (PSP) */ + /* skip weird value (3, 4) in Brothers in Arms: D-Day (PSP) */ //todo remove if (read_32bitBE(start_offset+0x04,streamData) == 0x52494646) { VGM_LOG("UBI SB: skipping unknown value 0x%x before RIFF\n", read_32bitBE(start_offset+0x00,streamData)); start_offset += 0x04; @@ -395,15 +406,6 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - - if (sb->num_samples == 0) /* sometimes not known */ - sb->num_samples = ffmpeg_data->totalSamples; - vgmstream->num_samples = sb->num_samples; - if (sb->num_samples != ffmpeg_data->totalSamples) { - VGM_LOG("UBI SB: header samples differ (%i vs %i)\n", sb->num_samples, (size_t)ffmpeg_data->totalSamples); - goto fail; - } - if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamData, start_offset)); break; @@ -422,12 +424,12 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str ffmpeg_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, sb->stream_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; - vgmstream->num_samples = sb->num_samples; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } + //todo: some XMA1 decode a bit strangely at certain positions (FFmpeg bug?) case FMT_XMA1: { ffmpeg_codec_data *ffmpeg_data; uint8_t buf[0x100]; @@ -445,18 +447,16 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str sec1_num = read_32bitBE(header_offset + 0x28, streamData); sec3_num = read_32bitBE(header_offset + 0x2c, streamData); num_frames = sec2_num; - bits_per_frame = 4; - if (flag == 0x04) { - sec1_num--; - sec2_num += 4; - } else if (flag == 0x02) { + bits_per_frame = 4; + if (flag == 0x02 || flag == 0x04) bits_per_frame = 2; - } + else if (flag == 0x08) + bits_per_frame = 1; header_size = 0x30; header_size += sec1_num * 0x04; - header_size += align_size_to_block(sec2_num * bits_per_frame, 32) / 8; /* bitstream with 4 or 2 bits for each frame */ + header_size += align_size_to_block(sec2_num * bits_per_frame, 32) / 8; /* bitstream seek table? */ header_size += sec3_num * 0x08; start_offset += header_size; data_size = num_frames * 0x800; @@ -468,8 +468,8 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = sb->num_samples; - vgmstream->stream_size = data_size; + + xma_fix_raw_samples_ch(vgmstream, streamData, start_offset, data_size, sb->channels, 0, 0); break; } @@ -478,20 +478,21 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str uint8_t buf[0x100]; size_t bytes, chunk_size; off_t header_offset; - + VGM_ASSERT(sb->is_external, "Ubi SB: Raw XMA used for external sound\n"); /* get XMA header from extra section */ chunk_size = 0x20; header_offset = sb->xma_header_offset; - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, sb->stream_size, streamFile, 1); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, sb->stream_size, streamHead, 1); ffmpeg_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, sb->stream_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = sb->num_samples; + + xma_fix_raw_samples_ch(vgmstream, streamData, start_offset, sb->stream_size, sb->channels, 0, 0); break; } @@ -503,23 +504,55 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - - vgmstream->num_samples = sb->num_samples; /* ffmpeg_data->totalSamples */ - VGM_ASSERT(sb->num_samples != ffmpeg_data->totalSamples, "UBI SB: header samples differ\n"); break; } - #endif + case FMT_CWAV: + if (sb->channels > 1) goto fail; /* unknown layout */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; + + dsp_read_coefs_le(vgmstream,streamData,start_offset + 0x7c, 0x40); + start_offset += 0xe0; /* skip CWAV header */ + break; + default: VGM_LOG("UBI SB: unknown codec\n"); goto fail; } - strcpy(vgmstream->stream_name, sb->readable_name); - /* open the actual for decoding (streamData can be an internal or external stream) */ if ( !vgmstream_open_stream(vgmstream, streamData, start_offset) ) goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +static VGMSTREAM * init_vgmstream_ubi_sb_audio(ubi_sb_header *sb, STREAMFILE *streamTest, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE *streamData = NULL; + + /* open external stream if needed */ + if (sb->is_external) { + streamData = open_streamfile_by_filename(streamFile,sb->resource_name); + if (streamData == NULL) { + VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name); + goto fail; + } + } + else { + streamData = streamFile; + } + + + /* init actual VGMSTREAM */ + vgmstream = init_vgmstream_ubi_sb_base(sb, streamTest, streamData, sb->stream_offset); + if (!vgmstream) goto fail; + if (sb->is_external && streamData) close_streamfile(streamData); return vgmstream; @@ -530,419 +563,459 @@ fail: return NULL; } +static VGMSTREAM * init_vgmstream_ubi_sb_layer(ubi_sb_header *sb, STREAMFILE *streamTest, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + STREAMFILE *streamData = NULL; + int i; -/* debug stuff, for now */ -static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t offset) { - - /* all types may contain memory garbage, making it harder to identify - * usually next types can contain memory from the previous type header, - * so if some non-audio type looks like audio it's probably repeating old data. - * This even happens for common fields (ex. type 06 at 0x08 has prev garbage, not stream size) */ - - switch(header_type) { - case 0x01: sb->types[0x01]++; break; /* audio (all games) */ - case 0x02: sb->types[0x02]++; break; /* config? (later games) */ - case 0x03: sb->types[0x03]++; break; /* config? (later games) */ - case 0x04: sb->types[0x04]++; break; /* config? (all games) [recheck: SC:CT] */ - case 0x05: sb->types[0x05]++; break; /* config? (all games) [recheck: SC:CT] */ - case 0x06: sb->types[0x06]++; break; /* layer (later games) */ - case 0x07: sb->types[0x07]++; break; /* config? (later games) [recheck: SC:CT] */ - case 0x08: sb->types[0x08]++; break; /* config? (all games) [recheck: SC:CT] */ - //case 0x09: sb->types[0x09]++; break; /* ? */ - case 0x0a: sb->types[0x0a]++; break; /* config? (early games) */ - //case 0x0b: sb->types[0x0b]++; break; /* ? */ - case 0x0c: sb->types[0x0c]++; break; /* config? (early games) */ - case 0x0d: sb->types[0x0d]++; break; /* layer (early games) */ - case 0x0e: sb->types[0x0e]++; break; /* config? (early games) */ - case 0x0f: sb->types[0x0f]++; break; /* config? (early games) */ - default: - VGM_LOG("UBI SB: unknown type %x at %x size %x\n", header_type, (uint32_t)offset, sb->section2_entry_size); - break; //goto fail; + /* open external stream if needed */ + if (sb->is_external) { + streamData = open_streamfile_by_filename(streamFile,sb->resource_name); + if (streamData == NULL) { + VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name); + goto fail; + } + } + else { + streamData = streamFile; } - ;VGM_ASSERT(header_type == 0x06 || header_type == 0x0d, - "UBI SB: type %x at %x size %x\n", header_type, (uint32_t)offset, sb->section2_entry_size); + /* init layout */ + data = init_layout_layered(sb->layer_count); + if (!data) goto fail; - /* layer info for later - * some values may be flags/config as multiple 0x06 can point to the same layer, with different 'flags' */ + /* open all layers and mix */ + for (i = 0; i < sb->layer_count; i++) { + /* prepare streamfile from a single layer section */ + temp_streamFile = setup_ubi_sb_streamfile(streamData, sb->stream_offset, sb->stream_size, i, sb->layer_count, sb->big_endian); + if (!temp_streamFile) goto fail; - /* 0x0d layer [Splinter Cell] */ - /* - type header: - * 0x18: stream offset? - * 0x20: (sample rate * layers) + 1? - * 0x24: layers/channels? - * 0x30: external flag - * 0x34: external name - * 0x5C: stream offset - * 0x64: stream size (not including padding) - * 0x78/7c: codec? - * - * - layer header at stream_offset: - * 0x00: version? (0x02000000) - * 0x04: layers - * 0x08: stream size (not including padding) - * 0x0c: size? - * 0x10: count? - * 0x14: min block size? - * - * - blocked data (unlike other layers, first block data is standard Ubi IMA headers, v3): - * 0x00: block number (from 0x01 to block_count) - * 0x04: current offset (within stream_offset) - * - per layer: - * 0x00: layer data size (varies between blocks, and one layer may have more than other, even the header) - */ + /* build the layer VGMSTREAM (standard sb with custom streamfile) */ + data->layers[i] = init_vgmstream_ubi_sb_base(sb, streamTest, temp_streamFile, 0x00); + if (!data->layers[i]) goto fail; - /* Rainbow Six 3 */ //todo also check layer header - /* Prince of Persia: Sands of Time (all) 0x000A0004 */ - /* Batman: Rise of Sin Tzu (2003)(GC)-map - 0x000A0002 */ - /* - type header (bizarrely but thankfully doesn't change between platforms): - * 0x1c: sample rate * layers - * 0x20: layers/channels? - * 0x2c: external flag? - * 0x30: external name - * 0x58: stream offset - * 0x5c: original rate * layers? - * 0x60: stream size (not including padding) - * 0x64: number of samples - * - * - layer header at stream_offset (BE on GC): - * 0x00: version? (0x04) - * 0x04: layers - * 0x08: stream size (not including padding) - * 0x0c: blocks count - * 0x10: block header size - * 0x14: block size - * 0x18: ? - * 0x1c: size of next section - * - per layer: - * 0x00: layer header size - * codec header per layer - * 0x00~0x20: standard Ubi IMA header (version 0x05, LE) - * - * - blocked data: - * 0x00: block number (from 0x01 to block_count) - * 0x04: current offset (within stream_offset) - * 0x08: always 0x03 - * - per layer: - * 0x00: layer data size (varies between blocks, and one layer may have more than other) - */ + close_streamfile(temp_streamFile); + } - /* Splinter Cell: Essentials (PSP)-map 0x0012000C */ - /* - type header: - * 0x08: header extra offset - * 0x1c: layers - * 0x28: external flag? - * 0x2c: external flag? - * 0x30: stream name - * 0x5c: config? - * 0x60: stream size - * 0x64: stream offset? - * - * - in header extra offset - * 0x00: sample rate - * 0x04: 16? - * 0x08: channels? - * 0x0c: codec? - * - * - layer header at stream_offset: - * 0x00: version? (0x07000000) - * 0x04: 0x03? - * 0x08: layers/channels? - * 0x0c: stream size - * 0x10: blocks count - * 0x14: block header size - * 0x18: block size - * - per layer: - * 0x00: approximate layer data size per block - * - per layer - * 0x00~0x0c: weird header thing? -1/0/0c... - * - * - blocked data: - * 0x00: block number (from 0x01 to block_count) - * 0x04: current offset (within stream_offset) - * 0x08: always 0x03 - * - per layer: - * 0x00: layer data size (varies between blocks, and one layer may have more than other) - */ + if (!setup_layout_layered(data)) + goto fail; + + /* build the base VGMSTREAM */ + vgmstream = allocate_vgmstream(sb->channels * sb->layer_count, sb->loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_UBI_SB; + vgmstream->sample_rate = sb->sample_rate; + vgmstream->num_streams = sb->total_subsongs; + vgmstream->stream_size = sb->stream_size; + + vgmstream->num_samples = sb->num_samples; + vgmstream->loop_start_sample = sb->loop_start; + vgmstream->loop_end_sample = sb->num_samples; + + vgmstream->coding_type = data->layers[0]->coding_type; + vgmstream->layout_type = layout_layered; + vgmstream->layout_data = data; - /* 0x06 layer [TMNT (PS2)-bank] */ - /* - type header: - * 0x08: header extra offset - * 0x1c: external flag? - * 0x20: layers/channels? - * 0x24: layers/channels? - * 0x28: config? same for all - * 0x2c: stream size - * 0x30: stream offset - * 0x38: name extra offset - * - * - in header extra offset - * 0x00: sample rate - * 0x04: channels? - * 0x08: codec? - * - * - layer header at stream_offset: - * 0x00: version? (0x08000B00) - * 0x04: config? (0x0e/0x0b/etc) - * 0x08: layers/channels? - * 0x0c: blocks count - * 0x10: block header size - * 0x14: size of header sizes + codec headers - * 0x18: block size - * - per layer: - * 0x00: layer header size - * - per layer - * 0x00~0x20: standard Ubi IMA header (version 0x05) - * - * - blocked data: - * 0x00: always 0x03 - * 0x04: block size - * - per layer: - * 0x00: layer data size (varies between blocks, and one layer may have more than other) - */ - - /* Splinter Cell 3D (2011)(3DS)-map 0x00130001 */ //todo - - /* Open Season (2005)(PS2)-map 0x00180003 */ - /* Rainbow Six Vegas (2007)(PSP)-bank 0x00180006 */ - /* Star Wars - Lethal Alliance (2006)(PSP)-map 0x00180007 */ - /* - type header: - * 0x0c: header extra offset - * 0x20: layers - * 0x2c: stream size - * 0x30: stream offset - * 0x38: name extra offset - * - * - in header extra offset - * 0x00: sample rate - * 0x04: 16? - * 0x08: channels? - * 0x0c: codec (03=Ubi, 01=PCM16LE in SW:LA) - * - * - layer header at stream_offset: - * - blocked data: - * same as TMNT PSP-map - */ - - /* TMNT (2007)(PSP)-map 0x00190001 */ - /* - type header: - * 0x0c: header extra offset - * 0x20: layers - * 0x24: total channels? - * 0x28: config? - * 0x2c: stream size - * 0x30: stream offset - * 0x38: name extra offset - * - * - in header extra offset - * 0x00: sample rate - * 0x04: channels? - * 0x08: codec? - * - * - layer header at stream_offset: - * - blocked data: - * same as TMNT PS2-bank, but codec header size is 0 - */ - - //todo Surf's Up (PC?)-map - - /* Splinter Cell Classic Trilogy HD (2011)(PS3)-map 0x001d0000 */ - /* - type header: - * 0x0c: header extra offset - * 0x20: layers - * 0x44: stream size - * 0x48: stream offset - * 0x54: name extra offset - * - * - in header extra offset - * 0x00: sample rate - * 0x04: channels? - * 0x08: codec? - * - * - layer header at stream_offset: - * - blocked data: - * same as TMNT PS2-bank, but codec header size is 0 - */ + if (sb->is_external && streamData) close_streamfile(streamData); + return vgmstream; +fail: + close_streamfile(temp_streamFile); + if (sb->is_external && streamData) close_streamfile(streamData); + if (vgmstream) + close_vgmstream(vgmstream); + else + free_layout_layered(data); + return NULL; } -static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_stream) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - int i, j, k, current_type = -1, current_id = -1, bank_streams = 0, prev_streams; +static VGMSTREAM * init_vgmstream_ubi_sb_sequence(ubi_sb_header *sb, STREAMFILE *streamTest, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + segmented_layout_data* data = NULL; + int i; - if (sb->big_endian) { - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - } else { - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - } + /* init layout */ + data = init_layout_segmented(sb->sequence_count); + if (!data) goto fail; - prev_streams = sb->total_streams; + sb->num_samples = 0; - /* find target stream info in section2 */ - for (i = 0; i < sb->section2_num; i++) { - off_t offset = sb->section2_offset + sb->section2_entry_size*i; - uint32_t header_type; + /* open all segments and mix */ + for (i = 0; i < sb->sequence_count; i++) { + ubi_sb_header temp_sb = *sb; /* memcpy'ed */ + int entry_index = sb->sequence_chain[i]; + off_t entry_offset = sb->section2_offset + sb->section2_entry_size * entry_index; - header_type = read_32bit(offset + 0x04, streamFile); - parse_header_type(sb, header_type, offset); + ;VGM_LOG("UBI SB: index=%x, offset=%lx\n", entry_index, entry_offset); - /* ignore non-audio entries */ - if (header_type != 0x01) - continue; - - /* weird case when there is no internal substream ID and just seem to rotate every time type changes, joy */ - if (sb->has_rotating_ids) { /* assumes certain configs can't happen in this case */ - int current_is_external = 0; - int type = read_32bit(offset + sb->cfg_stream_type, streamFile); - - if (sb->cfg_external_flag) { - current_is_external = read_32bit(offset + sb->cfg_external_flag, streamFile); - } else if (sb->has_extra_name_flag && read_32bit(offset + sb->cfg_extra_name, streamFile) != 0xFFFFFFFF) { - current_is_external = 1; /* -1 in extra_name means internal */ - } - - if (!current_is_external) { - if (sb->is_map) { - if (current_type == -1) - current_type = type; - if (current_id == -1) /* they seem to always start with 0 in maps */ - current_id = 0x00; - - if (!current_is_external) { - if (current_type != type) { - current_type = type; - current_id++; /* rotate */ - /* we'll warp it around later when parsing section 3 */ - } - } - } else { - if (current_type == -1) - current_type = type; - if (current_id == -1) /* use first ID in section3 */ - current_id = read_32bit(sb->section3_offset + 0x00, streamFile); - - if (!current_is_external) { - if (current_type != type) { - current_type = type; - current_id++; /* rotate */ - if (current_id >= sb->section3_num) - current_id = 0; /* reset */ - } - } - } - } - } - - /* update streams (total_stream also doubles as current) */ - sb->total_streams++; - bank_streams++; - if (sb->total_streams != target_stream) - continue; - - /* parse audio entry based on config */ - sb->header_index = i; - sb->header_offset = offset; - - sb->header_id = read_32bit(offset + 0x00, streamFile); - sb->header_type = read_32bit(offset + 0x04, streamFile); - sb->stream_size = read_32bit(offset + sb->cfg_stream_size, streamFile); - sb->extra_offset = read_32bit(offset + sb->cfg_extra_offset, streamFile) + sb->sectionX_offset; - sb->stream_offset = read_32bit(offset + sb->cfg_stream_offset, streamFile); - sb->channels = (sb->has_short_channels) ? - (uint16_t)read_16bit(offset + sb->cfg_channels, streamFile) : - (uint32_t)read_32bit(offset + sb->cfg_channels, streamFile); - sb->sample_rate = read_32bit(offset + sb->cfg_sample_rate, streamFile); - sb->stream_type = read_32bit(offset + sb->cfg_stream_type, streamFile); - - /* some games may store number of samples at different locations */ - if (sb->cfg_samples_flag && read_32bit(offset + sb->cfg_samples_flag, streamFile) != 0) { - sb->num_samples = read_32bit(offset + sb->cfg_num_samples2, streamFile); - } else if (sb->cfg_samples_bitflag && (read_32bit(offset + sb->cfg_samples_bitflag, streamFile) & 0x10)) { - sb->num_samples = read_32bit(offset + sb->cfg_num_samples2, streamFile); - VGM_ASSERT(sb->num_samples == 0, "UBI SB: bad bitflag found\n"); - } else if (sb->cfg_num_samples) { - sb->num_samples = read_32bit(offset + sb->cfg_num_samples, streamFile); - } - - if (sb->has_rotating_ids) { - sb->stream_id = current_id; - } else if (sb->cfg_stream_id) { - sb->stream_id = read_32bit(offset + sb->cfg_stream_id, streamFile); - } - - /* external stream name can be found in the header (first versions) or the sectionX table (later versions) */ - if (sb->cfg_stream_name) { - read_string(sb->resource_name, sb->resource_name_size, offset + sb->cfg_stream_name, streamFile); - } else { - sb->cfg_stream_name = read_32bit(offset + sb->cfg_extra_name, streamFile); - read_string(sb->resource_name, sb->resource_name_size, sb->sectionX_offset + sb->cfg_stream_name, streamFile); - } - - /* external flag is not always set and must be derived */ - if (sb->cfg_external_flag) { - sb->is_external = read_32bit(offset + sb->cfg_external_flag, streamFile); - } else if (sb->has_extra_name_flag && read_32bit(offset + sb->cfg_extra_name, streamFile) != 0xFFFFFFFF) { - sb->is_external = 1; /* -1 in extra_name means internal */ - } else if (sb->section3_num == 0) { - sb->is_external = 1; - } else { - sb->autodetect_external = 1; /* let the parser guess later */ - - if (sb->resource_name[0] == '\0') - sb->autodetect_external = 0; /* no name */ - if (sb->sectionX_size > 0 && sb->cfg_stream_name > sb->sectionX_size) - sb->autodetect_external = 0; /* name outside extra table == is internal */ - } - - //todo check sb->has_internal_names in case of garbage - /* build a full name for stream */ - if (sb->is_map) { - if (sb->resource_name[0]) { - snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%d/%08x/%s", sb->map_name, bank_streams, sb->header_id, sb->resource_name); - } else { - snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%d/%08x", sb->map_name, bank_streams, sb->header_id); - } - } else { - if (sb->resource_name[0]) { - snprintf(sb->readable_name, sizeof(sb->readable_name), "%08x/%s", sb->header_id, sb->resource_name); - } else { - snprintf(sb->readable_name, sizeof(sb->readable_name), "%s", sb->resource_name); - } - } - - } - - //;VGM_LOG("UBI SB: types "); for (int i = 0; i < 16; i++) { VGM_ASSERT(sb->types[i], "%02x=%i ",i,sb->types[i]); } VGM_LOG("\n"); - - - if (sb->is_map) { - if (bank_streams == 0 || target_stream <= prev_streams || target_stream > sb->total_streams) - return 1; /* Target stream is not in this map */ - } else { - if (sb->total_streams == 0) { - VGM_LOG("UBI SB: no streams\n"); + /* parse expected entry */ + if (!parse_header(&temp_sb, streamTest, entry_offset, entry_index)) goto fail; + + if (temp_sb.type != UBI_AUDIO && temp_sb.type != UBI_LAYER) { + VGM_LOG("UBI SB: unexpected sequence entry type at %x\n", (uint32_t)entry_offset); + goto fail; /* technically ok but too much recursiveness? */ } - if (target_stream < 0 || target_stream > sb->total_streams) { - VGM_LOG("UBI SB: wrong target stream (target=%i, total=%i)\n", target_stream, sb->total_streams); - goto fail; - } + /* build the layer VGMSTREAM (current sb entry config) */ + data->segments[i] = init_vgmstream_ubi_sb_header(&temp_sb, streamTest, streamFile); + if (!data->segments[i]) goto fail; + + if (i == sb->sequence_loop) + sb->loop_start = sb->num_samples; + sb->num_samples += data->segments[i]->num_samples; } - if (!(sb->cfg_stream_id || sb->has_rotating_ids || sb->is_map) && sb->section3_num > 1) { - VGM_LOG("UBI SB: unexpected number of internal stream groups %i\n", sb->section3_num); + + if (!setup_layout_segmented(data)) + goto fail; + + /* build the base VGMSTREAM */ + vgmstream = allocate_vgmstream(data->segments[0]->channels, !sb->sequence_single); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_UBI_SB; + vgmstream->sample_rate = data->segments[0]->sample_rate; + vgmstream->num_streams = sb->total_subsongs; + //vgmstream->stream_size = sb->stream_size; /* auto when getting avg br */ + + vgmstream->num_samples = sb->num_samples; + vgmstream->loop_start_sample = sb->loop_start; + vgmstream->loop_end_sample = sb->num_samples; + + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + vgmstream->layout_data = data; + + return vgmstream; +fail: + if (vgmstream) + close_vgmstream(vgmstream); + else + free_layout_segmented(data); + return NULL; +} + +static VGMSTREAM * init_vgmstream_ubi_sb_header(ubi_sb_header *sb, STREAMFILE* streamTest, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + + if (sb->total_subsongs == 0) { + VGM_LOG("UBI SB: no subsongs\n"); goto fail; } - //;VGM_LOG("UBI SB: target at %x (cfg %x), extra=%x, name=%s\n", (uint32_t)sb->header_offset, sb->section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name); + ;VGM_LOG("UBI SB: target at %x + %x, extra=%x, name=%s, id=%i, t=%i\n", + (uint32_t)sb->header_offset, sb->section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name, sb->group_id, sb->stream_type); + + ;VGM_LOG("UBI SB: stream offset=%x, size=%x, name=%s\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external ? sb->resource_name : "internal" ); + + switch(sb->type) { + case UBI_AUDIO: + vgmstream = init_vgmstream_ubi_sb_audio(sb, streamTest, streamFile); + break; + + case UBI_LAYER: + vgmstream = init_vgmstream_ubi_sb_layer(sb, streamTest, streamFile); + break; + + case UBI_SEQUENCE: + vgmstream = init_vgmstream_ubi_sb_sequence(sb, streamTest, streamFile); + break; + + case UBI_NONE: + default: + VGM_LOG("UBI SB: subsong not found/parsed\n"); + goto fail; + } + + if (!vgmstream) goto fail; + + strcpy(vgmstream->stream_name, sb->readable_name); + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* ************************************************************************* */ + +static void build_readable_name(char * buf, size_t buf_size, ubi_sb_header * sb) { + const char *grp_name; + const char *res_name; + uint32_t id; + uint32_t type; + int index; + + /* config */ + if (sb->is_map) + grp_name = sb->map_name; + else + grp_name = "bank"; //NULL + id = sb->header_id; + type = sb->header_type; + if (sb->is_map) + index = sb->header_index; //sb->bank_subsongs; + else + index = sb->header_index; //-1 + + if (sb->type == UBI_SEQUENCE) { + if (sb->sequence_single) { + if (sb->sequence_count == 1) + res_name = "single"; + else + res_name = "multi"; + } + else { + if (sb->sequence_count == 1) + res_name = "single-loop"; + else + res_name = (sb->sequence_loop == 0) ? "multi-loop" : "intro-loop"; + } + } + else { + if (sb->is_external || sb->cfga_has_internal_names) + res_name = sb->resource_name; + else + res_name = NULL; + } - /* happens in some versions */ + /* create name */ + if (grp_name) { + if (res_name && res_name[0]) { + if (index >= 0) + snprintf(buf,buf_size, "%s/%04d/%02x-%08x/%s", grp_name, index, type, id, res_name); + else + snprintf(buf,buf_size, "%s/%02x-%08x/%s", grp_name, type, id, res_name); + } + else { + if (index >= 0) + snprintf(buf,buf_size, "%s/%04d/%02x-%08x", grp_name, index, type, id); + else + snprintf(buf,buf_size, "%s/%02x-%08x", grp_name, type, id); + } + } + else { + if (res_name && res_name[0]) { + if (index >= 0) + snprintf(buf,buf_size, "%04d/%02x-%08x/%s", index, type, id, res_name); + else + snprintf(buf,buf_size, "%02x-%08x/%s", type, id, res_name); + } else { + if (index >= 0) + snprintf(buf,buf_size, "%04d/%02x-%08x", index, type, id); + else + snprintf(buf,buf_size, "%02x-%08x", type, id); + } + } +} + +static int parse_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = sb->big_endian ? read_16bitBE : read_16bitLE; + + /* audio header */ + sb->type = UBI_AUDIO; + + sb->extra_offset = read_32bit(offset + sb->cfga_extra_offset, streamFile) + sb->sectionX_offset; + sb->stream_size = read_32bit(offset + sb->cfga_stream_size, streamFile); + sb->stream_offset = read_32bit(offset + sb->cfga_stream_offset, streamFile); + sb->channels = (sb->cfga_channels % 4) ? /* non-aligned offset is always 16b */ + (uint16_t)read_16bit(offset + sb->cfga_channels, streamFile) : + (uint32_t)read_32bit(offset + sb->cfga_channels, streamFile); + sb->sample_rate = read_32bit(offset + sb->cfga_sample_rate, streamFile); + sb->stream_type = read_32bit(offset + sb->cfga_stream_type, streamFile); + + if (sb->stream_size == 0) { + VGM_LOG("UBI SB: bad stream size\n"); + goto fail; + } + + if (sb->cfga_loop_flag) { + sb->loop_flag = (read_32bit(offset + sb->cfga_loop_flag, streamFile) & sb->cfga_and_loop_flag); + } + + if (sb->loop_flag) { + sb->loop_start = read_32bit(offset + sb->cfga_num_samples, streamFile); + sb->num_samples = read_32bit(offset + sb->cfga_num_samples2, streamFile) + sb->loop_start; + if (sb->cfga_num_samples == sb->cfga_num_samples2) { /* early games just repeat and don't set loop start */ + sb->num_samples = sb->loop_start; + sb->loop_start = 0; + } + /* loop starts that aren't 0 do exist but are very rare (ex. Beowulf PSP sb 82, index 575) */ + } else { + sb->num_samples = read_32bit(offset + sb->cfga_num_samples, streamFile); + } + + if (sb->cfga_group_id) { + sb->group_id = read_32bit(offset + sb->cfga_group_id, streamFile); + if (sb->cfga_and_group_id) sb->group_id &= sb->cfga_and_group_id; + + /* normalize bitflag, known groups are only id 0/1 (if needed could calculate + * (shift-right value here, based on cfga_and_group_id first 1-bit) */ + if (sb->group_id > 1) + sb->group_id = 1; + } + + if (sb->cfga_external_flag) { + sb->is_external = (read_32bit(offset + sb->cfga_external_flag, streamFile) & sb->cfga_and_external_flag); + } + + if (sb->resource_name_size > sizeof(sb->resource_name)) { + goto fail; + } + + /* external stream name can be found in the header (first versions) or the sectionX table (later versions) */ + if (sb->cfga_stream_name) { + read_string(sb->resource_name, sb->resource_name_size, offset + sb->cfga_stream_name, streamFile); + } else { + sb->cfga_stream_name = read_32bit(offset + sb->cfga_extra_name, streamFile); + if (sb->cfgl_stream_name != 0xFFFFFFFF) + read_string(sb->resource_name, sb->resource_name_size, sb->sectionX_offset + sb->cfga_stream_name, streamFile); + } + + /* points at XMA1 header in the extra section (only for RAW_XMA1, garbage ignored otherwise) */ + if (sb->cfga_xma_offset) { + sb->xma_header_offset = read_32bit(offset + sb->cfga_xma_offset, streamFile) + sb->sectionX_offset; + } + + return 1; +fail: + return 0; +} + +static int parse_type_sequence(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + off_t table_offset; + int i; + + /* sequence chain */ + sb->type = UBI_SEQUENCE; + + sb->extra_offset = read_32bit(offset + sb->cfgs_extra_offset, streamFile) + sb->sectionX_offset; + sb->sequence_loop = read_32bit(offset + sb->cfgs_sequence_loop, streamFile); + sb->sequence_single = read_32bit(offset + sb->cfgs_sequence_single, streamFile); + sb->sequence_count = read_32bit(offset + sb->cfgs_sequence_count, streamFile); + + if (sb->sequence_entry_size == 0) { + VGM_LOG("Ubi SB: sequence entry size not configured at %x\n", (uint32_t)offset); + goto fail; + } + if (sb->sequence_count > sizeof(sb->sequence_chain)) { /* arbitrary max */ + VGM_LOG("Ubi SB: incorrect layer count\n"); + goto fail; + } + + /* get chain in extra table */ + table_offset = sb->extra_offset; + for (i = 0; i < sb->sequence_count; i++) { + uint32_t entry_number = (uint32_t)read_32bit(table_offset+sb->cfgs_entry_number, streamFile); + + /* some sequences have an upper bit for some reason */ + ;VGM_ASSERT_ONCE(entry_number & 0x80000000, "UBI SB: sequence bit entry found at %x\n", (uint32_t)sb->extra_offset); + + entry_number = entry_number & 0x7FFFFFFF; + if (entry_number > sb->section2_num) { + VGM_LOG("UBI SB: chain with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset); + goto fail; + } + + sb->sequence_chain[i] = entry_number; + + table_offset += sb->sequence_entry_size; + } + + return 1; +fail: + return 0; +} + +static int parse_type_layer(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = sb->big_endian ? read_16bitBE : read_16bitLE; + off_t table_offset; + int i; + + /* layer header */ + sb->type = UBI_LAYER; + + sb->extra_offset = read_32bit(offset + sb->cfgl_extra_offset, streamFile) + sb->sectionX_offset; + sb->layer_count = read_32bit(offset + sb->cfgl_layer_count, streamFile); + sb->stream_size = read_32bit(offset + sb->cfgl_stream_size, streamFile); + sb->stream_offset = read_32bit(offset + sb->cfgl_stream_offset, streamFile); + + if (sb->stream_size == 0) { + VGM_LOG("UBI SB: bad stream size\n"); + goto fail; + } + + if (sb->layer_entry_size == 0) { + VGM_LOG("Ubi SB: layer entry size not configured at %x\n", (uint32_t)offset); + goto fail; + } + if (sb->layer_count > 16) { /* arbitrary max */ + VGM_LOG("Ubi SB: incorrect layer count\n"); + goto fail; + } + + /* get 1st layer header in extra table and validate all headers match */ + table_offset = sb->extra_offset; + sb->channels = (sb->cfgl_channels % 4) ? /* non-aligned offset is always 16b */ + (uint16_t)read_16bit(table_offset + sb->cfgl_channels, streamFile) : + (uint32_t)read_32bit(table_offset + sb->cfgl_channels, streamFile); + sb->sample_rate = read_32bit(table_offset + sb->cfgl_sample_rate, streamFile); + sb->stream_type = read_32bit(table_offset + sb->cfgl_stream_type, streamFile); + sb->num_samples = read_32bit(table_offset + sb->cfgl_num_samples, streamFile); + + for (i = 0; i < sb->layer_count; i++) { + int channels = (sb->cfgl_channels % 4) ? /* non-aligned offset is always 16b */ + (uint16_t)read_16bit(table_offset + sb->cfgl_channels, streamFile) : + (uint32_t)read_32bit(table_offset + sb->cfgl_channels, streamFile); + int sample_rate = read_32bit(table_offset + sb->cfgl_sample_rate, streamFile); + int stream_type = read_32bit(table_offset + sb->cfgl_stream_type, streamFile); + int num_samples = read_32bit(table_offset + sb->cfgl_num_samples, streamFile); + if (sb->channels != channels || sb->sample_rate != sample_rate || sb->stream_type != stream_type) { + VGM_LOG("Ubi SB: layer headers don't match at %x\n", (uint32_t)table_offset); + goto fail; + } + + /* can be +-1 */ + if (sb->num_samples != num_samples && sb->num_samples + 1 == num_samples) { + sb->num_samples -= 1; + } + + table_offset += sb->layer_entry_size; + } + + /* all layers seem external */ + sb->is_external = 1; + + /* external stream name can be found in the header (first versions) or the sectionX table (later versions) */ + if (sb->cfgl_stream_name) { + read_string(sb->resource_name, sb->resource_name_size, offset + sb->cfgl_stream_name, streamFile); + } else { + sb->cfgl_stream_name = read_32bit(offset + sb->cfgl_extra_name, streamFile); + if (sb->cfgl_stream_name != 0xFFFFFFFF) + read_string(sb->resource_name, sb->resource_name_size, sb->sectionX_offset + sb->cfgl_stream_name, streamFile); + } + + /* layers seem to include XMA header */ + + return 1; +fail: + return 0; +} + +static int parse_stream_type(ubi_sb_header * sb) { + + if (sb->type == UBI_SEQUENCE) + return 1; + + /* happens in a few internal sounds from early Xbox games */ if (sb->stream_type > 0xFF) { VGM_LOG("UBI SB: garbage in stream_type\n"); sb->stream_type = 0; @@ -953,63 +1026,80 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe case 0x00: /* platform default (rarely external) */ switch (sb->platform) { case UBI_PC: - case UBI_3DS: sb->codec = RAW_PCM; break; - case UBI_PS2: sb->codec = RAW_PSX; break; - case UBI_PSP: - if (check_extensions(streamFile, "sb4,sm4")) { + if (sb->is_psp_old) sb->codec = FMT_VAG; - } else { + else sb->codec = RAW_PSX; - } break; - case UBI_XBOX: sb->codec = RAW_XBOX; break; - case UBI_GC: case UBI_WII: sb->codec = RAW_DSP; break; - case UBI_X360: sb->codec = RAW_XMA1; break; #if 0 - case UBI_PS3: - /* Need to confirm */ + case UBI_PS3: /* assumed, but not games seem to use it */ sb->codec = RAW_AT3; break; #endif + case UBI_3DS: + sb->codec = FMT_CWAV; + break; default: VGM_LOG("UBI SB: unknown internal format\n"); goto fail; } break; - case 0x01: /* PCM (Wii, rarely used) */ - sb->codec = RAW_PCM; + case 0x01: + switch (sb->version) { + case 0x00000003: /* Donald Duck: Goin' Quackers */ + sb->codec = RAW_DSP; + break; + default: + sb->codec = RAW_PCM; /* uncommon, ex. Wii/PSP/3DS */ + break; + } break; - case 0x02: /* PS ADPCM (PS3) */ - sb->codec = RAW_PSX; + case 0x02: + switch (sb->version) { + case 0x00000007: /* Splinter Cell, Splinter Cell: Pandora Tomorrow */ + case 0x00120012: /* Myst IV: Exile */ + sb->codec = UBI_UNK; + break; + default: + sb->codec = RAW_PSX; /* PS3 */ + break; + } break; - case 0x03: /* Ubi ADPCM (main external stream codec, has subtypes) */ - sb->codec = UBI_ADPCM; + case 0x03: + sb->codec = UBI_IMA; /* Ubi IMA v3+ (versions handled in decoder) */ break; - case 0x04: /* Ogg (later PC games) */ - sb->codec = FMT_OGG; + case 0x04: + switch (sb->version) { + case 0x00000007: /* Splinter Cell, Splinter Cell: Pandora Tomorrow */ + sb->codec = UBI_IMA; + break; + default: + sb->codec = FMT_OGG; /* later PC games */ + break; + } break; - case 0x05: /* AT3 (PSP, PS3) or XMA1 (X360) */ + case 0x05: switch (sb->platform) { case UBI_X360: sb->codec = FMT_XMA1; @@ -1024,8 +1114,8 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe } break; - case 0x06: /* PS ADPCM (later PSP and PS3(?) games) */ - sb->codec = RAW_PSX; + case 0x06: + sb->codec = RAW_PSX; /* later PSP and PS3(?) games */ break; case 0x07: @@ -1033,7 +1123,15 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe break; case 0x08: - sb->codec = FMT_AT3; + switch (sb->version) { + case 0x00000003: /* Donald Duck: Goin' Quackers */ + case 0x00000004: /* Myst III: Exile */ + sb->codec = UBI_IMA; /* Ubi IMA v2/v3 */ + break; + default: + sb->codec = FMT_AT3; + break; + } break; default: @@ -1041,76 +1139,87 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe goto fail; } - if (sb->codec == RAW_XMA1) { - /* this field is only seen in X360 games, points at XMA1 header in extra section */ - sb->xma_header_offset = read_32bit(sb->header_offset + sb->cfg_xma_offset, streamFile) + sb->sectionX_offset; + return 1; +fail: + return 0; +} + +static int parse_internal_offset(ubi_sb_header * sb, STREAMFILE *streamFile) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + int i, j, k; + + if (sb->type == UBI_SEQUENCE) + return 1; + + VGM_ASSERT(!sb->is_map && sb->section3_num > 2, "UBI SB: section3 > 2 found\n"); + + if (!(sb->cfga_group_id || sb->is_map) && sb->section3_num > 1) { + VGM_LOG("UBI SB: unexpected number of internal stream groups %i\n", sb->section3_num); + goto fail; } - /* uncommon but possible */ - //VGM_ASSERT(sb->is_external && sb->section3_num != 0, "UBI SS: mixed external and internal streams\n"); + if (sb->is_external) + return 1; - /* seems that can be safely ignored */ - //VGM_ASSERT(sb->is_external && sb->cfg_stream_id && sb->stream_id > 0, "UBI SB: unexpected external stream with stream id\n"); + /* Internal sounds are split into codec groups, with their offsets being relative to group start. + * A table contains sizes of each group, so we adjust offsets based on the group ID of our sound. + * Headers normally only use 0 or 1, and section3 may only define id1 (which the internal sound would use). + * May exist even for external streams only, and they often use id 1 too. */ - /* section 3: internal stream info */ - if (!sb->is_external) { - if (sb->is_map) { - /* maps store internal sounds offsets in a separate table, find the matching entry */ - for (i = 0; i < sb->section3_num; i++) { - off_t offset = sb->section3_offset + 0x14 * i; - off_t table_offset = read_32bit(offset + 0x04, streamFile) + sb->section3_offset; - uint32_t table_num = read_32bit(offset + 0x08, streamFile); - off_t table2_offset = read_32bit(offset + 0x0c, streamFile) + sb->section3_offset; - uint32_t table2_num = read_32bit(offset + 0x10, streamFile); + if (sb->is_map) { + /* maps store internal sounds offsets in a separate subtable, find the matching entry */ + for (i = 0; i < sb->section3_num; i++) { + off_t offset = sb->section3_offset + 0x14 * i; + off_t table_offset = read_32bit(offset + 0x04, streamFile) + sb->section3_offset; + uint32_t table_num = read_32bit(offset + 0x08, streamFile); + off_t table2_offset = read_32bit(offset + 0x0c, streamFile) + sb->section3_offset; + uint32_t table2_num = read_32bit(offset + 0x10, streamFile); - for (j = 0; j < table_num; j++) { - int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF; + for (j = 0; j < table_num; j++) { + int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF; - if (index == sb->header_index) { - if (!(sb->cfg_stream_id || sb->has_rotating_ids) && table2_num > 1) { - VGM_LOG("UBI SB: unexpected number of internal stream map groups %i\n", table2_num); - goto fail; - } - - if (sb->has_rotating_ids) { - sb->stream_id %= table2_num; - } - - sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, streamFile); - for (k = 0; k < table2_num; k++) { - /* entry layout: - * 0x00 - group ID - * 0x04 - size with padding included - * 0x08 - size without padding - * 0x0c - absolute offset */ - uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, streamFile); - if (id == sb->stream_id) { - sb->stream_offset += read_32bit(table2_offset + 0x10 * k + 0x0c, streamFile); - break; - } - } - break; + if (index == sb->header_index) { + if (!sb->cfga_group_id && table2_num > 1) { + VGM_LOG("UBI SB: unexpected number of internal stream map groups %i at %x\n", table2_num, (uint32_t)table2_offset); + goto fail; } - } - if (sb->stream_offset) + sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, streamFile); + for (k = 0; k < table2_num; k++) { + /* entry layout: + * 0x00 - group ID + * 0x04 - size with padding included + * 0x08 - size without padding + * 0x0c - absolute offset */ + uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, streamFile); + if (id == sb->group_id) { + sb->stream_offset += read_32bit(table2_offset + 0x10 * k + 0x0c, streamFile); + break; + } + } break; - } - } else { - sb->stream_offset += sb->sounds_offset; - - if ((sb->cfg_stream_id || sb->has_rotating_ids) && sb->section3_num > 1) { - /* internal sounds are split into groups based on their type with their offsets being relative to group start - * this table contains sizes of each group, adjust offset based on group ID of our sound */ - for (i = 0; i < sb->section3_num; i++) { - off_t offset = sb->section3_offset + sb->section3_entry_size * i; - - /* table has unordered ids+size, so if our id doesn't match current data offset must be beyond */ - if (read_32bit(offset + 0x00, streamFile) == sb->stream_id) - break; - sb->stream_offset += read_32bit(offset + 0x04, streamFile); } } + + if (sb->stream_offset) + break; + } + } + else { + /* banks store internal sounds after all headers and adjusted by the group table, find the matching entry */ + + off_t sounds_offset = sb->section3_offset + sb->section3_entry_size*sb->section3_num; + sb->stream_offset = sounds_offset + sb->stream_offset; + + if (sb->cfga_group_id && sb->section3_num > 1) { /* maybe should always test this? */ + for (i = 0; i < sb->section3_num; i++) { + off_t offset = sb->section3_offset + sb->section3_entry_size * i; + + /* table has unordered ids+size, so if our id doesn't match current data offset must be beyond */ + if (read_32bit(offset + 0x00, streamFile) == sb->group_id) + break; + sb->stream_offset += read_32bit(offset + 0x04, streamFile); + } } } @@ -1119,16 +1228,115 @@ fail: return 0; } +/* parse a header resource at offset */ +static int parse_header(ubi_sb_header * sb, STREAMFILE *streamFile, off_t offset, int index) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + + /* parse known headers (see config_sb for info) */ + sb->header_index = index; + sb->header_offset = offset; + + sb->header_id = read_32bit(offset + 0x00, streamFile); + sb->header_type = read_32bit(offset + 0x04, streamFile); + + switch(sb->header_type) { + case 0x01: + if (!parse_type_audio(sb, offset, streamFile)) + goto fail; + break; + case 0x05: + case 0x0c: + if (!parse_type_sequence(sb, offset, streamFile)) + goto fail; + break; + case 0x06: + case 0x0d: + if (!parse_type_layer(sb, offset, streamFile)) + goto fail; + break; + default: + VGM_LOG("UBI SB: unknown header type at %x\n", (uint32_t)offset); + goto fail; + } + + /* find actual codec (as different games' stream_type can overlap) */ + if (!parse_stream_type(sb)) + goto fail; + + /* find actual stream offset in section3 */ + if (!parse_internal_offset(sb, streamFile)) + goto fail; + + return 1; +fail: + return 0; +} + +/* parse a bank and its possible audio headers (resources). */ +static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subsong) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + int i; + + ;VGM_LOG("UBI SB: s1=%x (%x*%x), s2=%x (%x*%x), sX=%x (%x), s3=%x (%x*%x)\n", + sb->section1_offset,sb->section1_entry_size,sb->section1_num,sb->section2_offset,sb->section2_entry_size,sb->section2_num, + sb->sectionX_offset,sb->sectionX_size,sb->section3_offset,sb->section3_entry_size,sb->section3_num); + + /* find target subsong info in section2 */ + sb->bank_subsongs = 0; + for (i = 0; i < sb->section2_num; i++) { + off_t offset = sb->section2_offset + sb->section2_entry_size*i; + uint32_t header_type; + + /* check candidate types */ + /*header_id =*/ read_32bit(offset + 0x00, streamFile); /* forces buffer read */ + header_type = read_32bit(offset + 0x04, streamFile); + + if (header_type <= 0x00 || header_type >= 0x10 || header_type == 0x09 || header_type == 0x0b) { + VGM_LOG("UBI SB: unknown type %x at %x\n", header_type, (uint32_t)offset); + goto fail; + } + + sb->types[header_type]++; + + if (!sb->allowed_types[header_type]) + continue; + + /* update subsongs (keep counting even after found) */ + sb->bank_subsongs++; + sb->total_subsongs++; + if (sb->total_subsongs != target_subsong) + continue; + + /* parse target subsong */ + if (!parse_header(sb, streamFile, offset, i)) + goto fail; + + /* maps can contain +10000 subsongs, we need something helpful + * (best here right after subsong detection, since some sequence re-parse types) */ + build_readable_name(sb->readable_name, sizeof(sb->readable_name), sb); + } + + /* either target subsong found or it's in another bank (in case of maps), both handled externally */ + + ;VGM_LOG("UBI SB: types "); {int i; for (i=0;i<16;i++){ VGM_ASSERT(sb->types[i],"%02x=%i ",i,sb->types[i]); }} VGM_LOG("\n"); + + return 1; +fail: + return 0; +} + +/* ************************************************************************* */ + static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile) { char filename[PATH_LIMIT]; int filename_len; char platform_char; uint32_t version; - /* to find out hijacking platforms */ + /* to find out hijacking (LE) platforms */ version = read_32bitLE(0x00, streamFile); - /* get X from .sbX/smX */ + /* get X from .sbX/smX/lmX */ get_streamfile_name(streamFile,filename,sizeof(filename)); filename_len = strlen(filename); platform_char = filename[filename_len - 1]; @@ -1150,6 +1358,7 @@ static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile) { switch(version) { /* early PSP clashes with X360 */ case 0x0012000C: /* multiple games use this ID and all are sb4/sm4 */ sb->platform = UBI_PSP; + sb->is_psp_old = 1; break; default: sb->platform = UBI_X360; @@ -1182,282 +1391,451 @@ static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->platform == UBI_X360 || sb->platform == UBI_WII; - return 1; fail: return 0; } + +static void config_sb_entry(ubi_sb_header * sb, size_t section1_size_entry, size_t section2_size_entry) { + sb->section1_entry_size = section1_size_entry; + sb->section2_entry_size = section2_size_entry; + sb->section3_entry_size = 0x08; +} +static void config_sb_audio_fs(ubi_sb_header * sb, off_t external_flag, off_t group_id, off_t loop_flag) { + /* audio header with standard flags */ + sb->cfga_external_flag = external_flag; + sb->cfga_group_id = group_id; + sb->cfga_loop_flag = loop_flag; + sb->cfga_and_external_flag = 1; + sb->cfga_and_group_id = 1; + sb->cfga_and_loop_flag = 1; +} +static void config_sb_audio_fb(ubi_sb_header * sb, off_t flag_bits, int external_and, int group_and, int loop_and) { + /* audio header with bit flags */ + sb->cfga_external_flag = flag_bits; + sb->cfga_group_id = flag_bits; + sb->cfga_loop_flag = flag_bits; + sb->cfga_and_external_flag = external_and; + sb->cfga_and_group_id = group_and; + sb->cfga_and_loop_flag = loop_and; +} +static void config_sb_audio_hs(ubi_sb_header * sb, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_name, off_t stream_type) { + /* audio header with stream name */ + sb->cfga_channels = channels; + sb->cfga_sample_rate = sample_rate; + sb->cfga_num_samples = num_samples; + sb->cfga_num_samples2 = num_samples2; + sb->cfga_stream_name = stream_name; + sb->cfga_stream_type = stream_type; +} +static void config_sb_audio_he(ubi_sb_header * sb, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t extra_name, off_t stream_type) { + /* audio header with extra name */ + sb->cfga_channels = channels; + sb->cfga_sample_rate = sample_rate; + sb->cfga_num_samples = num_samples; + sb->cfga_num_samples2 = num_samples2; + sb->cfga_extra_name = extra_name; + sb->cfga_stream_type = stream_type; +} +static void config_sb_sequence(ubi_sb_header * sb, off_t sequence_count, off_t entry_size) { + /* sequence header and chain table */ + sb->cfgs_sequence_loop = sequence_count - 0x10; + sb->cfgs_sequence_single = sequence_count - 0x0c; + sb->cfgs_sequence_count = sequence_count; + sb->sequence_entry_size = entry_size; + sb->cfgs_entry_number = 0x00; +} +static void config_sb_layer_hs(ubi_sb_header * sb, off_t layer_count, off_t stream_size, off_t stream_offset, off_t stream_name) { + /* layer headers with stream name */ + sb->cfgl_layer_count = layer_count; + sb->cfgl_stream_size = stream_size; + sb->cfgl_stream_offset = stream_offset; + sb->cfgl_stream_name = stream_name; +} +static void config_sb_layer_he(ubi_sb_header * sb, off_t layer_count, off_t stream_size, off_t stream_offset, off_t extra_name) { + /* layer headers with extra name */ + sb->cfgl_layer_count = layer_count; + sb->cfgl_stream_size = stream_size; + sb->cfgl_stream_offset = stream_offset; + sb->cfgl_extra_name = extra_name; +} +static void config_sb_layer_sh(ubi_sb_header * sb, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples) { + /* layer sub-headers in extra table */ + sb->layer_entry_size = entry_size; + sb->cfgl_sample_rate = sample_rate; + sb->cfgl_channels = channels; + sb->cfgl_stream_type = stream_type; + sb->cfgl_num_samples = num_samples; +} + static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { int is_biadd_psp = 0; - /* The type 1 audio header varies with almost every game + platform (some kind of class serialization?), - * support is done case-by-case as offsets move slightly. Basic layout: - * - fixed part (0x00..0x14) - * - garbage, flags - * - external stream number of samples / internal garbage - * - external stream size (also in the common part) / internal garbage - * - garbage - * - external original sample rate / internal garbage - * - sample rate, pcm bits?, channels - * - stream type and external filename (internal filename too on some platforms) - * - end flags/garbage - * Garbage looks like uninitialized variables (may be null, contain part of strings, etc). - * Later version don't have filename per header but in the extra section. + /* Most of the format varies with almost every game + platform (struct serialization?). + * Support is configured case-by-case as offsets/order/fields only change slightly, + * and later games may remove fields. We only configure those actually needed. + * + * Various type use "chains" of entry numbers (defined in the extra table). + * Its format also depends on type. */ - //todo set samples flag where "varies" + /* Header types found in section2 (possibly called "resource headers"): + * + * Type 01 (old/new): + * Single audio header, external or internal, part of a chain or single. Format: + * - fixed part (id, type, stream size, extra offset, stream offset) + * - flags (as bitflags or in separate fields, around ~6 observed flags) + * - samples+samples (loop+total) + size+size (roughly equal to stream size) + * - bitrate (total sample rate) + * - base info (sample rate, pcm bits?, channels, codec) + * - external filename or internal filename on some platforms (earlier versions) + * - external filename offset in the extra table (later versions) + * - end flags? + * + * Type 02 (old?/new): + * Chain, possibly to play with config (ex: type 08 (float 0.3) + 01) + * + * Type 03 (new), 09? (old): + * Chain, other way to play things? (ex: type 03 + 04) + * + * Type 04 (old/new), 0a (old): + * Chain of N types, possibly to play one as random (usually N voice/sfx like + * like death screams, but may include sequences). + * + * Type 05 (new), 0c (old): sequences + * N audio segment, normally with lead-in but not lead-outs. Sequences can reuse + * segments (internal loops), or can be single entries (full song or leads). + * Sequences seem to include only music or cutscenes, so even single entries can be + * useful to parse, since the readable name would make them stand out. Format: + * - extra offset to chain + * - intro flag + * - outro flag + * - sequence count + * - ID-like fields in the header and sequence table may point to other chains? + * + * Type 06 (new), 0d (old): + * Layer header, stream divided into N equal parts in a blocked format. Format: + * - fixed part (id, type) + * - extra offset to layer info (sample rate, pcm bits?, channels, codec, samples) + * - layer count + * - sometimes total channels, bitrate, etc + * - flags? + * - stream size + stream offset + * - external filename, or filename offset in the extra table + * Layer blocks are handled separatedly as the format doesn't depend on sb's version/platform. + * Some values may be flags/config as multiple 0x06 can point to the same layer, with different 'flags'? + * + * Type 07 (new), 0e (old): + * - another chain of something (single entry?), rare. + * + * Type 08 (new), 0f (old): + * - audio config? (almost all fields 0 except sometimes 1.0 float in the middle). + * in older games may also point to the extra table, maybe equivalent to 02. + */ + + /* debug strings reference: + * - TYPE_SAMPLE: should be 0x01 (also "sound resource") + * - TYPE_MULTITRACK: should be 0x06/0x0d (also "multilayer resource") + * - TYPE_SILENCE: ? + * sequences may be "theme resource" + * "class descryptor" is referenced, + * + * Possible type names from .bnm (.sb's predecessor): + * 0: TYPE_INVALID + * 1: TYPE_SAMPLE + * 2: TYPE_MIDI + * 3: TYPE_CDAUDIO + * 4: TYPE_SEQUENCE + * 5: TYPE_SWITCH_OLD + * 6: TYPE_SPLIT + * 7: TYPE_THEME_OLD + * 8: TYPE_SWITCH + * 9: TYPE_THEME_OLD2 + * A: TYPE_RANDOM + * B: TYPE_THEME0 + */ + + /* All types may contain memory garbage, making it harder to identify fields (platforms + * and games are affected differently by this). Often types contain memory from the previous + * type header unless overwritten, random memory, or default initialization garbage. + * So if some non-audio type looks like audio it's probably repeating old data. + * This even happens for common fields (ex. type 6 at 0x08 has prev garbage, not stream size). */ + /* common */ - sb->section3_entry_size = 0x08; - sb->resource_name_size = 0x24; /* maybe 0x28 or 0x20 for some but ok enough (null terminated) */ - /* this is same in all games since ~2003 */ - sb->cfg_stream_size = 0x08; - sb->cfg_extra_offset = 0x0c; - sb->cfg_stream_offset = 0x10; + sb->resource_name_size = 0x24; /* maybe 0x20/0x28 for some but ok enough (null terminated) */ + /* represents map style (1=first, 2=mid, 3=latest) */ + if (sb->version <= 0x00000007) + sb->map_version = 1; + else if (sb->version < 0x00150000) + sb->map_version = 2; + else + sb->map_version = 3; - /* Batman: Vengeance (2001)(PS2)-map 0x00000003 */ - /* Batman: Vengeance (2001)(GC)-map 0x00000003 */ - /* Disney's Tarzan: Untamed (2001)(GC)-map 0x00000003 */ + sb->map_entry_size = (sb->map_version < 2) ? 0x30 : 0x34; + + if (sb->version <= 0x00000007) { + sb->cfga_stream_size = 0x0c; + sb->cfga_extra_offset = 0x10; + sb->cfga_stream_offset = 0x14; + + sb->cfgs_extra_offset = 0x10; + + sb->cfgl_extra_offset = 0x10; + } + else { + sb->cfga_stream_size = 0x08; + sb->cfga_extra_offset = 0x0c; + sb->cfga_stream_offset = 0x10; + + sb->cfgs_extra_offset = 0x0c; + + sb->cfgl_extra_offset = 0x0c; + } + + sb->allowed_types[0x01] = 1; + sb->allowed_types[0x05] = 1; + sb->allowed_types[0x0c] = 1; + sb->allowed_types[0x06] = 1; + sb->allowed_types[0x0d] = 1; #if 0 + { + STREAMFILE * streamTest; + streamTest= open_streamfile_by_filename(streamFile, ".no_audio.sbx"); + if (streamTest) { sb->allowed_types[0x01] = 0; close_streamfile(streamTest); } + + streamTest= open_streamfile_by_filename(streamFile, ".no_sequence.sbx"); + if (streamTest) { sb->allowed_types[0x05] = sb->allowed_types[0x0c] = 0; close_streamfile(streamTest); } + + streamTest= open_streamfile_by_filename(streamFile, ".no_layer.sbx"); + if (streamTest) { sb->allowed_types[0x06] = sb->allowed_types[0x0d] = 0; close_streamfile(streamTest); } + } +#endif + + //todo some dsp offsets have problems, wrong id? + //todo uses Ubi IMA v2 has has some deviation in the right channel + clicks? + //todo has some sample rate / loop configs problems? (ex Batman #5451) + //todo buggy reads in layers? + /* Disney's Tarzan: Untamed (2001)(GC)-map */ + /* Batman: Vengeance (2001)(GC)-map */ /* Donald Duck: Goin' Quackers (2002)(GC)-map */ if (sb->version == 0x00000003 && sb->platform == UBI_GC) { - /* Stream types: - * 0x01: Nintendo DSP ADPCM - * 0x08: unsupported codec, looks like standard IMA with custom 0x29 bytes header - */ - sb->section1_entry_size = 0x40; - sb->section2_entry_size = 0x6c; + config_sb_entry(sb, 0x40, 0x6c); - sb->map_version = 1; + config_sb_audio_fs(sb, 0x30, 0x2c, 0x34); + config_sb_audio_hs(sb, 0x56, 0x50, 0x48, 0x48, 0x5c, 0x58); - sb->cfg_stream_size = 0x0c; - sb->cfg_extra_offset = 0x10; - sb->cfg_stream_offset = 0x14; + config_sb_sequence(sb, 0x2c, 0x1c); - sb->cfg_external_flag = 0x30; - sb->cfg_stream_id = 0x34; - sb->cfg_num_samples = 0x48; - sb->cfg_sample_rate = 0x50; - sb->cfg_channels = 0x56; - sb->cfg_stream_type = 0x58; - sb->cfg_stream_name = 0x5c; + config_sb_layer_hs(sb, 0x20, 0x4c, 0x44, 0x34); + config_sb_layer_sh(sb, 0x1c, 0x04, 0x0a, 0x0c, 0x18); + return 1; + } - sb->has_short_channels = 1; +#if 0 + /* Batman: Vengeance (2001)(PS2)-map */ + /* Disney's Tarzan: Untamed (2001)(PS2)-map */ + if (sb->version == 0x00000003 && sb->platform == UBI_PS2) { + config_sb_entry(sb, 0x30, 0x3c); + + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4));//? + sb->cfga_group_id = 0x1c?; + sb->cfga_external_flag = 0x1c?; + sb->cfga_loop_flag = 0x1c?; + sb->cfga_num_samples = 0x28; + sb->cfga_num_samples2 = 0x28; + sb->cfga_sample_rate = 0x24; + sb->cfga_channels = 0x2a? + sb->cfga_stream_type = 0x34? 0x38; + sb->cfga_stream_name = -1; /* implicit STRM.SM1 */ + + config_sb_sequence(sb, 0x2c, 0x10); + + //layer format ??? return 1; } #endif + #if 0 + //todo offsets seems to work differently (stream offset is always 0) + /* Myst III: Exile (2001)(PS2)-map */ + if (sb->version == 0x00000004 && sb->platform == UBI_PS2) { + config_sb_entry(sb, 0x34, 0x70); + + config_sb_audio_fb(sb, 0x1c, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_hs(sb, 0x24, 0x28, 0x2c, 0x34, 0x44, 0x6c); + + //todo sequences + + //todo layers + return 1; + } +#endif + +#if 0 + //todo uses codec 02 /* Splinter Cell (2002)(PC)-map */ /* Splinter Cell: Pandora Tomorrow (2004)(PC)-map */ if (sb->version == 0x00000007 && sb->platform == UBI_PC) { - /* Stream types: - * 0x01: PCM - * 0x02: unsupported codec, appears to be Ubi IMA in a blocked layout - * 0x04: Ubi IMA v3 (not Vorbis) - */ - sb->section1_entry_size = 0x58; - sb->section2_entry_size = 0x80; + config_sb_entry(sb, 0x58, 0x80); - sb->map_version = 1; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x24?); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); + sb->cfga_has_internal_names = 1; - sb->cfg_stream_size = 0x0c; - sb->cfg_extra_offset = 0x10; - sb->cfg_stream_offset = 0x14; + //todo sequences - sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x2c; - sb->cfg_num_samples = 0x30; - sb->cfg_sample_rate = 0x44; - sb->cfg_channels = 0x4a; - sb->cfg_stream_type = 0x4c; - sb->cfg_stream_name = 0x50; - - sb->has_short_channels = 1; - sb->has_internal_names = 1; + config_sb_layer_hs(sb, 0x20, 0x64, 0x5c, 0x34); + config_sb_layer_sh(sb, 0x18, 0x00, 0x06, 0x08, 0x14); return 1; } #endif - /* Prince of Persia: Sands of Time (2003)(PC)-bank */ - if ((sb->version == 0x000A0002 && sb->platform == UBI_PC) || /* (not sure if exists, just in case) */ - (sb->version == 0x000A0004 && sb->platform == UBI_PC)) { /* main game */ - sb->section1_entry_size = 0x64; - sb->section2_entry_size = 0x80; +#if 0 + /* Splinter Cell (2002)(Xbox)-map */ + /* Splinter Cell: Pandora Tomorrow (2004)(Xbox)-map */ + if (sb->version == 0x00000007 && sb->platform == UBI_XBOX) { + config_sb_entry(sb, 0x58, 0x78); - sb->cfg_external_flag = 0x24; /* maybe 0x28 */ - sb->cfg_num_samples = 0x30; - sb->cfg_sample_rate = 0x44; - sb->cfg_channels = 0x4a; - sb->cfg_stream_type = 0x4c; - sb->cfg_stream_name = 0x50; + config_sb_audio_fs(sb, 0x28, 0x24? 0x2c?, 0x2c? 0x24?); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); + sb->cfga_has_internal_names = 1; - sb->has_short_channels = 1; - sb->has_internal_names = 1; - //has layer 0d (main game) + //todo sequences + + //todo layers + return 1; + } +#endif + + /* Prince of Persia: Sands of Time (2003)(PC)-bank 0x000A0004 / 0x000A0002 (just in case) */ + if ((sb->version == 0x000A0002 && sb->platform == UBI_PC) || + (sb->version == 0x000A0004 && sb->platform == UBI_PC)) { + config_sb_entry(sb, 0x64, 0x80); + + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); + sb->cfga_has_internal_names = 1; + + config_sb_sequence(sb, 0x28, 0x14); + + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); return 1; } - /* Prince of Persia: Sands of Time (2003)(PS2)-bank */ - if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) || /* Prince of Persia 1 port */ - (sb->version == 0x000A0004 && sb->platform == UBI_PS2)) { /* main game */ - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x6c; + /* Prince of Persia: Sands of Time (2003)(PS2)-bank 0x000A0004 / 0x000A0002 (POP1 port) */ + /* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank 0x000A0007 */ + /* Splinter Cell: Pandora Tomorrow(?) (2006)(PS2)-bank 0x000A0008 */ + /* Prince of Persia: Warrior Within (2004)(PS2)-bank 0x00120009 */ + if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) || + (sb->version == 0x000A0004 && sb->platform == UBI_PS2) || + (sb->version == 0x000A0007 && sb->platform == UBI_PS2) || + (sb->version == 0x000A0008 && sb->platform == UBI_PS2) || + (sb->version == 0x00120009 && sb->platform == UBI_PS2)) { + config_sb_entry(sb, 0x48, 0x6c); - sb->cfg_external_flag = 0; /* no apparent flag */ - sb->cfg_channels = 0x20; - sb->cfg_sample_rate = 0x24; - sb->cfg_num_samples = 0x30; - sb->cfg_stream_name = 0x40; - sb->cfg_stream_type = 0x68; + config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_hs(sb, 0x20, 0x24, 0x30, 0x38, 0x40, 0x68); /* num_samples may be null */ - //has layer 0d (main game) + config_sb_sequence(sb, 0x28, 0x10); + + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); return 1; } - /* Prince of Persia: Sands of Time (2003)(Xbox)-bank */ - if ((sb->version == 0x000A0002 && sb->platform == UBI_XBOX) || /* Prince of Persia 1 port */ - (sb->version == 0x000A0004 && sb->platform == UBI_XBOX)) { /* main game */ - sb->section1_entry_size = 0x64; - sb->section2_entry_size = 0x78; + /* Prince of Persia: Sands of Time (2003)(Xbox)-bank 0x000A0004 / 0x000A0002 (POP1 port) */ + if ((sb->version == 0x000A0002 && sb->platform == UBI_XBOX) || + (sb->version == 0x000A0004 && sb->platform == UBI_XBOX)) { + config_sb_entry(sb, 0x64, 0x78); - sb->cfg_external_flag = 0x24; /* maybe 0x28 */ - sb->cfg_num_samples = 0x30; - sb->cfg_sample_rate = 0x44; - sb->cfg_channels = 0x4a; - sb->cfg_stream_type = 0x4c; /* may contain garbage */ - sb->cfg_stream_name = 0x50; + config_sb_audio_fs(sb, 0x24, 0x28, 0x2c); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); /* stream_type may contain garbage */ + sb->cfga_has_internal_names = 1; - sb->has_short_channels = 1; - sb->has_internal_names = 1; - //has layer 0d (main game) + config_sb_sequence(sb, 0x28, 0x14); + + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); return 1; } + /* Batman: Rise of Sin Tzu (2003)(GC)-map 0x000A0002 */ + /* Prince of Persia: Sands of Time (2003)(GC)-bank 0x000A0004 / 0x000A0002 (POP1 port)*/ + if ((sb->version == 0x000A0002 && sb->platform == UBI_GC) || + (sb->version == 0x000A0004 && sb->platform == UBI_GC)) { + config_sb_entry(sb, 0x64, 0x74); - //todo Batman offsets are slightly off for some subsongs, or maybe get external wrong (ex. 222~226) - /* Batman: Rise of Sin Tzu (2003)(GC)-map - 0x000A0002 */ - /* Prince of Persia: Sands of Time (2003)(GC)-bank */ - if ((sb->version == 0x000A0002 && sb->platform == UBI_GC) || /* Prince of Persia 1 port */ - (sb->version == 0x000A0004 && sb->platform == UBI_GC)) { /* main game */ - sb->section1_entry_size = 0x64; - sb->section2_entry_size = 0x74; + config_sb_audio_fs(sb, 0x20, 0x24, 0x28); + config_sb_audio_hs(sb, 0x46, 0x40, 0x2c, 0x34, 0x4c, 0x48); - sb->map_version = 2; + config_sb_sequence(sb, 0x28, 0x14); - sb->cfg_external_flag = 0x20; /* maybe 0x24 */ - sb->cfg_num_samples = 0x2c; - sb->cfg_sample_rate = 0x40; - sb->cfg_channels = 0x46; - sb->cfg_stream_type = 0x48; - sb->cfg_stream_name = 0x4c; - - sb->has_short_channels = 1; - //has layer 0d (POP:SOT main game, Batman) - return 1; - } - - /* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank */ - if (sb->version == 0x000A0007 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x6c; - - sb->cfg_external_flag = 0; /* no apparent flag */ - sb->cfg_channels = 0x20; - sb->cfg_sample_rate = 0x24; - sb->cfg_num_samples = 0x30; - sb->cfg_stream_name = 0x40; - sb->cfg_stream_type = 0x68; - - //has layer 0d + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); return 1; } /* Myst IV Demo (2004)(PC)-bank */ - if (sb->version == 0x00100000 && sb->platform == UBI_PC) { /* final game is different */ - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0xa4; + if (sb->version == 0x00100000 && sb->platform == UBI_PC) { + config_sb_entry(sb, 0x68, 0xa4); - sb->cfg_external_flag = 0x24; - sb->cfg_num_samples = 0x34; - sb->cfg_sample_rate = 0x44; - sb->cfg_channels = 0x4c; - sb->cfg_stream_type = 0x50; - sb->cfg_stream_name = 0x54; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); + sb->cfga_has_internal_names = 1; - sb->has_internal_names = 1; + /* no sequences */ + + /* no layers */ return 1; } /* Prince of Persia: Warrior Within (2004)(PC)-bank */ if (sb->version == 0x00120009 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x6c; - sb->section2_entry_size = 0x84; + config_sb_entry(sb, 0x6c, 0x84); - sb->cfg_external_flag = 0x24; - sb->cfg_num_samples = 0x30; - sb->cfg_sample_rate = 0x44; - sb->cfg_channels = 0x4c; - sb->cfg_stream_type = 0x50; - sb->cfg_stream_name = 0x54; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); + sb->cfga_has_internal_names = 1; - sb->has_internal_names = 1; - //no layers - return 1; - } + config_sb_sequence(sb, 0x28, 0x14); - /* Prince of Persia: Warrior Within (2004)(PS2)-bank */ - if (sb->version == 0x00120009 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x6c; - - sb->cfg_external_flag = 0; /* no apparent flag */ - sb->cfg_channels = 0x20; - sb->cfg_sample_rate = 0x24; - sb->cfg_num_samples = 0x30; - sb->cfg_stream_name = 0x40; - sb->cfg_stream_type = 0x68; - - //no layers + /* no layers */ return 1; } /* Prince of Persia: Warrior Within (2004)(Xbox)-bank */ if (sb->version == 0x00120009 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x6c; - sb->section2_entry_size = 0x90; + config_sb_entry(sb, 0x6c, 0x90); - sb->cfg_external_flag = 0x24; - sb->cfg_num_samples = 0x44; - sb->cfg_sample_rate = 0x58; - sb->cfg_channels = 0x60; - sb->cfg_stream_type = 0x64; /* may contain garbage */ - sb->cfg_stream_name = 0x68; + config_sb_audio_fs(sb, 0x24, 0x28, 0x40); + config_sb_audio_hs(sb, 0x60, 0x58, 0x44, 0x4c, 0x68, 0x64); /* stream_type may contain garbage */ + sb->cfga_has_internal_names = 1; - sb->has_internal_names = 1; - //no layers + config_sb_sequence(sb, 0x28, 0x14); + + /* no layers */ return 1; } /* Prince of Persia: Warrior Within (2004)(GC)-bank */ if (sb->version == 0x00120009 && sb->platform == UBI_GC) { - sb->section1_entry_size = 0x6c; - sb->section2_entry_size = 0x78; + config_sb_entry(sb, 0x6c, 0x78); - sb->cfg_external_flag = 0x20; - sb->cfg_num_samples = 0x2c; - sb->cfg_sample_rate = 0x40; - sb->cfg_channels = 0x48; - sb->cfg_stream_type = 0x4c; - sb->cfg_stream_name = 0x50; + config_sb_audio_fs(sb, 0x20, 0x24, 0x28); + config_sb_audio_hs(sb, 0x48, 0x40, 0x2c, 0x34, 0x50, 0x4c); - //no layers + config_sb_sequence(sb, 0x28, 0x14); + + /* no layers */ return 1; } - /* two configs with same id; use project file as identifier */ + /* two configs with same id and both sb4/sm4; use project file as identifier */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP) { STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP4"); if (streamTest) { @@ -1468,564 +1846,336 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { /* Prince of Persia: Revelations (2005)(PSP)-bank */ /* Splinter Cell: Essentials (2006)(PSP)-map */ - /* Beowulf - The Game (2007)(PSP)-map */ + /* Beowulf: The Game (2007)(PSP)-map */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP && !is_biadd_psp) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x84; + config_sb_entry(sb, 0x68, 0x84); - sb->map_version = 2; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); + sb->cfga_has_internal_names = 1; - sb->cfg_external_flag = 0x24; - sb->cfg_num_samples = 0x30; - sb->cfg_sample_rate = 0x44; - sb->cfg_channels = 0x4c; - sb->cfg_stream_type = 0x50; - sb->cfg_stream_name = 0x54; + config_sb_sequence(sb, 0x28, 0x14); - sb->has_internal_names = 1; - //has layers 06 (SC:E only) - - //todo Beowulf needs rotating ids, but fails in some subsong offsets - // (ex. subsong 33415 should point to AT3 but offset is a VAGp) + config_sb_layer_hs(sb, 0x1c, 0x60, 0x64, 0x30); + config_sb_layer_sh(sb, 0x18, 0x00, 0x08, 0x0c, 0x14); return 1; } - /* Brothers in Arms - D-Day (2006)(PSP)-bank */ + //todo some .sbX have bad external stream offsets, but not all (ex. offset 0xE3641 but should be 0x0A26) + /* Brothers in Arms: D-Day (2006)(PSP)-bank */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP && is_biadd_psp) { - sb->section1_entry_size = 0x80; - sb->section2_entry_size = 0x94; + config_sb_entry(sb, 0x80, 0x94); - sb->cfg_external_flag = 0x24; - sb->cfg_samples_flag = 0x28; - sb->cfg_stream_id = 0x2c; - sb->cfg_num_samples = 0x30; - sb->cfg_num_samples2 = 0x38; - sb->cfg_sample_rate = 0x44; - sb->cfg_channels = 0x4c; - sb->cfg_stream_type = 0x50; - sb->cfg_stream_name = 0x54; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); + sb->cfga_has_internal_names = 1; - sb->has_internal_names = 1; - return 1; - } + /* no sequences */ -#if 0 - /* Splinter Cell 3D (2011)(3DS)-map */ - if (sb->version == 0x00130001 && sb->platform == UBI_3DS) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x4c; - - sb->map_version = 2; - - sb->cfg_external_flag = 0; - //sb->cfg_stream_id = 0; - sb->cfg_num_samples = 0x1c; - sb->cfg_sample_rate = 0x30; - sb->cfg_channels = 0x38; - sb->cfg_stream_type = 0x3c; - sb->cfg_extra_name = 0x40; - - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; //todo? - //has layer 06 - - //todo SC3DS codecs: - // 00: RAW_PCM - // 01: FMT_CWAV - // 03: Ubi IMA - return 1; - } -#endif - - /* Prince of Persia: The Two Thrones (2005)(PC)-bank */ - if (sb->version == 0x00150000 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x78; - - sb->cfg_external_flag = 0x2c; - sb->cfg_stream_id = 0x34; - sb->cfg_num_samples = 0x40; - sb->cfg_sample_rate = 0x54; - sb->cfg_channels = 0x5c; - sb->cfg_stream_type = 0x60; - sb->cfg_extra_name = 0x64; - - sb->has_extra_name_flag = 1; - //no layers - return 1; - } - - /* Prince of Persia: The Two Thrones (2005)(PS2)-bank */ - if (sb->version == 0x00150000 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x5c; - - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x2c; - sb->cfg_sample_rate = 0x30; - sb->cfg_num_samples = 0x3c; - sb->cfg_extra_name = 0x4c; - sb->cfg_stream_type = 0x50; - - sb->has_extra_name_flag = 1; - //no layers - return 1; - } - - /* Prince of Persia: The Two Thrones (2005)(Xbox)-bank */ - if (sb->version == 0x00150000 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; - - sb->cfg_external_flag = 0; - sb->cfg_stream_id = 0; - sb->cfg_num_samples = 0x28; - sb->cfg_sample_rate = 0x3c; - sb->cfg_channels = 0x44; - sb->cfg_stream_type = 0x48; - sb->cfg_extra_name = 0x4c; - - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; - //no layers - return 1; - } - - /* Prince of Persia: The Two Thrones (2005)(GC)-bank */ - if (sb->version == 0x00150000 && sb->platform == UBI_GC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; - - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ - sb->cfg_num_samples = 0x3c; - sb->cfg_sample_rate = 0x50; - sb->cfg_channels = 0x58; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x60; - - //no layers + /* no layers */ return 1; } /* Splinter Cell: Chaos Theory (2005)(PC)-map */ if (sb->version == 0x00120012 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x60; + config_sb_entry(sb, 0x68, 0x60); - sb->map_version = 2; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_he(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); - sb->cfg_external_flag = 0x24; - sb->cfg_num_samples = 0x30; - sb->cfg_sample_rate = 0x44; - sb->cfg_channels = 0x4c; - sb->cfg_stream_type = 0x50; - sb->cfg_extra_name = 0x54; + config_sb_sequence(sb, 0x28, 0x14); + /* no layers */ return 1; } + /* Myst IV: Revelation (2005)(PC)-bank */ /* Splinter Cell: Chaos Theory (2005)(Xbox)-map */ if (sb->version == 0x00120012 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x4c; + config_sb_entry(sb, 0x48, 0x4c); - sb->map_version = 2; + config_sb_audio_fb(sb, 0x18, (1 << 3), (1 << 4), (1 << 10)); + config_sb_audio_he(sb, 0x38, 0x30, 0x1c, 0x24, 0x40, 0x3c); - sb->cfg_external_flag = 0; - sb->cfg_stream_id = 0; - sb->cfg_num_samples = 0x18; - sb->cfg_sample_rate = 0x30; - sb->cfg_channels = 0x38; - sb->cfg_stream_type = 0x3c; - sb->cfg_extra_name = 0x40; + config_sb_sequence(sb, 0x28, 0x10); - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; + /* no layers */ return 1; } -#if 0 - /* Far cry: Instincts - Evolution (2006)(Xbox)-bank */ - if (sb->version == 0x00170000 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x6c; + /* Splinter Cell 3D (2011)(3DS)-map */ + if (sb->version == 0x00130001 && sb->platform == UBI_3DS) { + config_sb_entry(sb, 0x48, 0x4c); - sb->cfg_external_flag = 0; - sb->cfg_stream_id = 0; - sb->cfg_num_samples = 0x28; - sb->cfg_sample_rate = 0x3c; - sb->cfg_channels = 0x44; - sb->cfg_stream_type = 0x48; - sb->cfg_extra_name = 0x58; + config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_he(sb, 0x38, 0x30, 0x1c, 0x24, 0x40, 0x3c); + config_sb_sequence(sb, 0x28, 0x10); + + config_sb_layer_he(sb, 0x1c, 0x28, 0x30, 0x34); + config_sb_layer_sh(sb, 0x18, 0x00, 0x08, 0x0c, 0x14); return 1; } -#endif - /* Red Steel (2006)(Wii)-bank */ - if (sb->version == 0x00180006 && sb->platform == UBI_WII) { /* same as 0x00150000 */ - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; + /* Prince of Persia: The Two Thrones (2005)(PC)-bank */ + if (sb->version == 0x00150000 && sb->platform == UBI_PC) { + config_sb_entry(sb, 0x68, 0x78); - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ - sb->cfg_num_samples = 0x3c; - sb->cfg_sample_rate = 0x50; - sb->cfg_channels = 0x58; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x60; + config_sb_audio_fs(sb, 0x2c, 0x34, 0x30); + config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); + config_sb_sequence(sb, 0x2c, 0x14); + + /* no layers */ + return 1; + } + + /* Prince of Persia: The Two Thrones (2005)(PS2)-bank */ + if (sb->version == 0x00150000 && sb->platform == UBI_PS2) { + config_sb_entry(sb, 0x48, 0x5c); + + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_he(sb, 0x2c, 0x30, 0x3c, 0x44, 0x4c, 0x50); + + config_sb_sequence(sb, 0x2c, 0x10); + + /* no layers */ + return 1; + } + + /* Prince of Persia: The Two Thrones (2005)(Xbox)-bank 0x00150000 */ + /* Far Cry Instincts (2005)(Xbox)-bank 0x00150000 */ + /* Splinter Cell: Double Agent (2006)(Xbox)-map 0x00160002 */ + /* Far cry Instincts: Evolution (2006)(Xbox)-bank 0x00170000 */ + if ((sb->version == 0x00150000 && sb->platform == UBI_XBOX) || + (sb->version == 0x00160002 && sb->platform == UBI_XBOX) || + (sb->version == 0x00170000 && sb->platform == UBI_XBOX)) { + config_sb_entry(sb, 0x48, 0x58); + + config_sb_audio_fb(sb, 0x20, (1 << 3), (1 << 4), (1 << 10)); + config_sb_audio_he(sb, 0x44, 0x3c, 0x28, 0x30, 0x4c, 0x48); + + config_sb_sequence(sb, 0x2c, 0x10); + + config_sb_layer_he(sb, 0x20, 0x2c, 0x34, 0x3c); + config_sb_layer_sh(sb, 0x30, 0x00, 0x08, 0x0c, 0x14); + return 1; + } + + /* Prince of Persia: The Two Thrones (2005)(GC)-bank 0x00150000 */ + /* Splinter Cell: Double Agent (2006)(GC)-map 0x00160002 */ + if ((sb->version == 0x00150000 && sb->platform == UBI_GC) || + (sb->version == 0x00160002 && sb->platform == UBI_GC)) { + config_sb_entry(sb, 0x68, 0x6c); + + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); + config_sb_audio_he(sb, 0x58, 0x50, 0x3c, 0x44, 0x60, 0x5c); + + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x38, 0x40, 0x48); + config_sb_layer_sh(sb, 0x30, 0x00, 0x08, 0x0c, 0x14); + return 1; + } + + //todo Open Season (PSP) uses sequence with type 0x08 (silence?) + //todo Rainbow Six Vegas (PSP) has layers with different sample rates (but 2nd layer is silent, can be ignored) + /* Splinter Cell: Double Agent (2006)(PS2)-map 0x00160002 */ + /* Open Season (2005)(PS2)-map 0x00180003 */ + /* Open Season (2005)(PSP)-map 0x00180003 */ + /* Shaun White Snowboarding (2008)(PS2)-map 0x00180003 */ + /* Prince of Persia: Rival Swords (2007)(PSP)-bank 0x00180005 */ + /* Rainbow Six Vegas (2007)(PSP)-bank 0x00180006 */ + /* Star Wars: Lethal Alliance (2006)(PSP)-map 0x00180007 */ + if ((sb->version == 0x00160002 && sb->platform == UBI_PS2) || + (sb->version == 0x00180003 && sb->platform == UBI_PS2) || + (sb->version == 0x00180003 && sb->platform == UBI_PSP) || + (sb->version == 0x00180005 && sb->platform == UBI_PSP) || + (sb->version == 0x00180006 && sb->platform == UBI_PSP) || + (sb->version == 0x00180007 && sb->platform == UBI_PSP)) { + config_sb_entry(sb, 0x48, 0x54); + + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48); + + config_sb_sequence(sb, 0x2c, 0x10); + + config_sb_layer_he(sb, 0x20, 0x2c, 0x30, 0x38); + config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; } /* Splinter Cell: Double Agent (2006)(PC)-map */ if (sb->version == 0x00180006 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x7c; + config_sb_entry(sb, 0x68, 0x7c); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x2c, 0x34, 0x30); + config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); - sb->cfg_external_flag = 0x2c; - sb->cfg_stream_id = 0x34; - sb->cfg_channels = 0x5c; - sb->cfg_sample_rate = 0x54; - sb->cfg_num_samples = 0x40; - sb->cfg_num_samples2 = 0x48; - sb->cfg_stream_type = 0x60; - sb->cfg_extra_name = 0x64; + /* no sequences */ + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); + config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; } /* Splinter Cell: Double Agent (2006)(X360)-map */ if (sb->version == 0x00180006 && sb->platform == UBI_X360) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x78; + config_sb_entry(sb, 0x68, 0x78); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x2c, 0x30, 0x34); + config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); + sb->cfga_xma_offset = 0x70; - sb->cfg_external_flag = 0x2c; - sb->cfg_stream_id = 0x30; - sb->cfg_samples_flag = 0x34; - sb->cfg_channels = 0x5c; - sb->cfg_sample_rate = 0x54; - sb->cfg_num_samples = 0x40; - sb->cfg_num_samples2 = 0x48; - sb->cfg_stream_type = 0x60; - sb->cfg_extra_name = 0x64; - sb->cfg_xma_offset = 0x70; + /* no sequences */ + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); + config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; } - /* Splinter Cell: Double Agent (2006)(Xbox)-map */ - if (sb->version == 0x00160002 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; + /* Red Steel (2006)(Wii)-bank 0x00180006 */ + if (sb->version == 0x00180006 && sb->platform == UBI_WII) { + config_sb_entry(sb, 0x68, 0x6c); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); + config_sb_audio_he(sb, 0x58, 0x50, 0x3c, 0x44, 0x60, 0x5c); - sb->cfg_external_flag = 0; - sb->cfg_num_samples = 0x28; - sb->cfg_stream_id = 0; - sb->cfg_sample_rate = 0x3c; - sb->cfg_channels = 0x44; - sb->cfg_stream_type = 0x48; - sb->cfg_extra_name = 0x4c; + config_sb_sequence(sb, 0x2c, 0x14); - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); + config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; } - /* Splinter Cell: Double Agent (2006)(GC)-map */ - if (sb->version == 0x00160002 && sb->platform == UBI_GC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; + /* TMNT (2007)(PSP)-map 0x00190001 */ + /* Surf's Up (2007)(PSP)-map 0x00190005 */ + if ((sb->version == 0x00190001 && sb->platform == UBI_PSP) || + (sb->version == 0x00190005 && sb->platform == UBI_PSP)) { + config_sb_entry(sb, 0x48, 0x58); - sb->map_version = 3; + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); /* assumed group_flag */ + config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48); - sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x2c; - sb->cfg_num_samples = 0x3c; - sb->cfg_sample_rate = 0x50; - sb->cfg_channels = 0x58; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x60; + config_sb_sequence(sb, 0x2c, 0x10); + config_sb_layer_he(sb, 0x20, 0x2c, 0x30, 0x38); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } - /* Open Season (2005)(PS2)-map - 0x00180003 */ - /* Open Season (2005)(PSP)-map - 0x00180003 */ - /* Star Wars - Lethal Alliance (2006)(PSP)-map - 0x00180007 */ - if ((sb->version == 0x00180003 && sb->platform == UBI_PS2) || - (sb->version == 0x00180003 && sb->platform == UBI_PSP) || - (sb->version == 0x00180007 && sb->platform == UBI_PSP)) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x54; + //todo one sequence is using type 0x08 (only) with 5.0: maybe type_silence? + /* TMNT (2007)(GC)-bank */ + if (sb->version == 0x00190002 && sb->platform == UBI_GC) { + config_sb_entry(sb, 0x68, 0x6c); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); /* assumed groud_id */ + config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); - sb->cfg_external_flag = 0; - sb->cfg_samples_bitflag = 0x20; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x2c; - sb->cfg_num_samples = 0x34; - sb->cfg_num_samples = 0x3c; - sb->cfg_extra_name = 0x44; - sb->cfg_stream_type = 0x48; - - sb->has_extra_name_flag = 1; - //has layer 06 - return 1; - } - - /* Prince of Persia: Rival Swords (2007)(PSP)-bank */ - if (sb->version == 0x00180005 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x54; - - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x2c; - //sb->cfg_num_samples = 0x34 or 0x3c /* varies */ - sb->cfg_extra_name = 0x44; - sb->cfg_stream_type = 0x48; - - sb->has_extra_name_flag = 1; - return 1; - } - -#if 0 //todo join with other 0x0018000x configs? - /* Rainbow Six Vegas (2007)(PSP)-bank */ - if (sb->version == 0x00180006 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x54; - - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x2c; - //sb->cfg_num_samples = 0x34 or 0x3c /* varies */ - sb->cfg_extra_name = 0x44; - sb->cfg_stream_type = 0x48; - - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; //??? - return 1; - } -#endif - - /* Prince of Persia: Rival Swords (2007)(Wii)-bank */ - if (sb->version == 0x00190003 && sb->platform == UBI_WII) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x70; - - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ - sb->cfg_channels = 0x3c; - sb->cfg_sample_rate = 0x40; - sb->cfg_num_samples = 0x48; - sb->cfg_extra_name = 0x58; - sb->cfg_stream_type = 0x5c; - - return 1; - } - - /* TMNT (2007)(PC)-bank */ - if (sb->version == 0x00190002 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x74; - - sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x2c; - sb->cfg_channels = 0x3c; - sb->cfg_sample_rate = 0x40; - sb->cfg_num_samples = 0x48; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x58; + config_sb_sequence(sb, 0x2c, 0x14); + config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } + //todo one sequence is using type 0x08 (only) with 5.0: maybe type_silence? /* TMNT (2007)(PS2)-bank */ if (sb->version == 0x00190002 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x5c; + config_sb_entry(sb, 0x48, 0x5c); - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x2c; - //sb->cfg_num_samples = 0x34 or 0x3c /* varies */ - sb->cfg_extra_name = 0x44; - sb->cfg_stream_type = 0x48; + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); /* assumed group_flag */ + config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48); - sb->has_extra_name_flag = 1; - //has layer 06 + config_sb_sequence(sb, 0x2c, 0x10); + + config_sb_layer_he(sb, 0x20, 0x2c, 0x30, 0x38); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } - /* TMNT (2007)(GC)-bank */ - if (sb->version == 0x00190002 && sb->platform == UBI_GC) { /* same as 0x00190003 */ - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; + /* TMNT (2007)(X360)-bank 0x00190002 */ + /* Prince of Persia: Rival Swords (2007)(Wii)-bank 0x00190003 */ + /* Rainbow Six Vegas (2007)(PS3)-bank 0x00190005 */ + /* Surf's Up (2007)(PS3)-bank 0x00190005 */ + /* Surf's Up (2007)(X360)-bank 0x00190005 */ + /* Splinter Cell: Double Agent (2007)(PS3)-map 0x00190005 */ + if ((sb->version == 0x00190002 && sb->platform == UBI_X360) || + (sb->version == 0x00190003 && sb->platform == UBI_WII) || + (sb->version == 0x00190005 && sb->platform == UBI_PS3) || + (sb->version == 0x00190005 && sb->platform == UBI_X360)) { + config_sb_entry(sb, 0x68, 0x70); - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ - sb->cfg_channels = 0x3c; - sb->cfg_sample_rate = 0x40; - sb->cfg_num_samples = 0x48; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x58; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); + config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); + sb->cfga_xma_offset = 0x6c; + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } - /* TMNT (2007)(X360)-bank */ - if (sb->version == 0x00190002 && sb->platform == UBI_X360) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x70; + /* TMNT (2007)(PC)-bank 0x00190002 */ + /* Surf's Up (2007)(PC)-bank 0x00190005 */ + if ((sb->version == 0x00190002 && sb->platform == UBI_PC) || + (sb->version == 0x00190005 && sb->platform == UBI_PC)) { + config_sb_entry(sb, 0x68, 0x74); - sb->cfg_external_flag = 0x28; - sb->cfg_samples_flag = 0x30; - sb->cfg_channels = 0x3c; - sb->cfg_sample_rate = 0x40; - sb->cfg_num_samples = 0x48; - sb->cfg_num_samples2 = 0x50; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x58; - sb->cfg_xma_offset = 0x6c; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); + config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } - /* TMNT (2007)(PSP)-map */ - if (sb->version == 0x00190001 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; + /* Rainbow Six Vegas 2 (2008)(PS3)-map */ + if (sb->version == 0x001c0000 && sb->platform == UBI_PS3) { + config_sb_entry(sb, 0x64, 0x7c); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x28, 0x30, 0x34); + config_sb_audio_he(sb, 0x44, 0x48, 0x50, 0x58, 0x60, 0x64); - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x2c; - sb->cfg_num_samples = 0x34; - sb->cfg_stream_type = 0x48; - sb->cfg_extra_name = 0x44; + /* no sequences */ - sb->has_extra_name_flag = 1; - //has layer 06 - return 1; - } - - /* Surf's Up (2007)(PC)-bank */ - if (sb->version == 0x00190005 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x74; - - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ - sb->cfg_channels = 0x3c; - sb->cfg_sample_rate = 0x40; - sb->cfg_num_samples = 0x48; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x58; - - return 1; - } - - /* Surf's Up (2007)(PS3)-bank */ - /* Splinter Cell: Double Agent (2007)(PS3)-map */ - if (sb->version == 0x00190005 && sb->platform == UBI_PS3) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x70; - - sb->map_version = 3; - - sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x2c; - sb->cfg_channels = 0x3c; - sb->cfg_sample_rate = 0x40; - sb->cfg_num_samples = 0x48; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x58; - - return 1; - } - - /* Surf's Up (2007)(X360)-bank */ - if (sb->version == 0x00190005 && sb->platform == UBI_X360) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x70; - - sb->cfg_external_flag = 0x28; - sb->cfg_samples_flag = 0x30; - sb->cfg_channels = 0x3c; - sb->cfg_sample_rate = 0x40; - sb->cfg_num_samples = 0x48; - sb->cfg_num_samples2 = 0x50; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x58; - sb->cfg_xma_offset = 0x6c; - - return 1; - } - - /* Surf's Up (2007)(PSP)-map */ - if (sb->version == 0x00190005 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; - - sb->map_version = 3; - - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x2c; - sb->cfg_num_samples = 0x34; - sb->cfg_stream_type = 0x48; - sb->cfg_extra_name = 0x44; - - sb->has_extra_name_flag = 1; - //no layers + config_sb_layer_he(sb, 0x20, 0x44, 0x48, 0x54); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } /* Michael Jackson: The Experience (2010)(PSP)-map */ if (sb->version == 0x001d0000 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x40; - sb->section2_entry_size = 0x60; + config_sb_entry(sb, 0x40, 0x60); - sb->map_version = 3; + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 5)); /* assumed group_flag */ + config_sb_audio_he(sb, 0x28, 0x30, 0x38, 0x40, 0x48, 0x4c); - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x30; - sb->cfg_num_samples = 0x38; - sb->cfg_extra_name = 0x48; - sb->cfg_stream_type = 0x4c; /* 06|08 */ + /* no sequences */ - sb->has_extra_name_flag = 1; - //no layers + /* no layers */ return 1; } /* Splinter Cell Classic Trilogy HD (2011)(PS3)-map */ if (sb->version == 0x001d0000 && sb->platform == UBI_PS3) { - sb->section1_entry_size = 0x5c; - sb->section2_entry_size = 0x80; + config_sb_entry(sb, 0x5c, 0x80); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x28, 0x30, 0x34); + config_sb_audio_he(sb, 0x44, 0x4c, 0x54, 0x5c, 0x64, 0x68); + + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x44, 0x48, 0x54); + config_sb_layer_sh(sb, 0x38, 0x00, 0x04, 0x08, 0x10); - sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x30; - sb->cfg_samples_flag = 0x34; - sb->cfg_channels = 0x44; - sb->cfg_sample_rate = 0x4c; - sb->cfg_num_samples = 0x54; - sb->cfg_num_samples2 = 0x5c; - sb->cfg_stream_type = 0x68; - sb->cfg_extra_name = 0x64; - //has layer 06 return 1; } - VGM_LOG("UBI SB: unknown SB/SM version+platform for %08x\n", sb->version); + VGM_LOG("UBI SB: unknown SB/SM version+platform %08x\n", sb->version); return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_streamfile.h new file mode 100644 index 000000000..88c3b17f8 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_streamfile.h @@ -0,0 +1,363 @@ +#ifndef _UBI_SB_STREAMFILE_H_ +#define _UBI_SB_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + off_t stream_offset; + off_t stream_size; + int layer_number; + int layer_count; + int layer_max; + int big_endian; + + /* internal config */ + off_t header_next_start; /* offset to header field */ + off_t header_sizes_start; /* offset to header table */ + off_t header_data_start; /* offset to header data */ + off_t block_next_start; /* offset to block field */ + off_t block_sizes_start; /* offset to block table */ + off_t block_data_start; /* offset to block data */ + size_t header_size; /* derived */ + + /* state */ + off_t logical_offset; /* fake offset */ + off_t physical_offset; /* actual offset */ + size_t block_size; /* current size */ + size_t next_block_size; /* next size */ + size_t skip_size; /* size from block start to reach data */ + size_t data_size; /* usable size in a block */ + + size_t logical_size; +} ubi_sb_io_data; + + +static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ubi_sb_io_data* data) { + int32_t(*read_32bit)(off_t, STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE; + size_t total_read = 0; + int i; + + + /* re-start when previous offset (can't map logical<>physical offsets) */ + if (data->logical_offset < 0 || offset < data->logical_offset) { + data->physical_offset = data->stream_offset; + data->logical_offset = 0x00; + data->data_size = 0; + + /* process header block (slightly different and data size may be 0) */ + { + data->block_size = data->header_size; + data->next_block_size = read_32bit(data->physical_offset + data->header_next_start, streamfile); + + if (data->header_sizes_start) { + data->skip_size = data->header_data_start; + for (i = 0; i < data->layer_number; i++) { + data->skip_size += read_32bit(data->physical_offset + data->header_sizes_start + i*0x04, streamfile); + } + data->data_size = read_32bit(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, streamfile); + } + + if (data->data_size == 0) { + data->physical_offset += data->block_size; + } + } + } + + + /* read blocks */ + while (length > 0) { + + /* ignore EOF */ + if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) { + break; + } + + /* process new block */ + if (data->data_size == 0) { + data->block_size = data->next_block_size; + if (data->block_next_start) /* not set when fixed block size */ + data->next_block_size = read_32bit(data->physical_offset + data->block_next_start, streamfile); + + data->skip_size = data->block_data_start; + for (i = 0; i < data->layer_number; i++) { + data->skip_size += read_32bit(data->physical_offset + data->block_sizes_start + i*0x04, streamfile); + } + data->data_size = read_32bit(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, streamfile); + } + + /* move to next block */ + if (offset >= data->logical_offset + data->data_size) { + if (data->block_size == 0 || data->block_size == 0xFFFFFFFF) + break; + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_size = 0; + continue; + } + + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_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); + + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + } + + return total_read; +} + +static size_t ubi_sb_io_size(STREAMFILE *streamfile, ubi_sb_io_data* data) { + uint8_t buf[1]; + + if (data->logical_size) + return data->logical_size; + + /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ + ubi_sb_io_read(streamfile, buf, 0x7FFFFFFF, 1, data); + data->logical_size = data->logical_offset; + + return data->logical_size; +} + + +static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE; + off_t offset = data->stream_offset; + uint32_t version; + int i; + + if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) { + VGM_LOG("UBI SB: bad size\n"); + goto fail; + } + + /* Layers have a main header, then headered blocks with data. + * We configure stuff to unify parsing of all variations. */ + version = (uint32_t)read_32bit(offset+0x00, streamfile); + switch(version) { + case 0x00000002: /* Splinter Cell */ + /* - layer header + * 0x04: layer count + * 0x08: stream size + * 0x0c: block header size + * 0x10: block size (fixed) + * 0x14: min layer size? + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x04, streamfile); + + data->header_next_start = 0x10; + data->header_sizes_start = 0; + data->header_data_start = 0x18; + + data->block_next_start = 0; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + case 0x00000004: /* Prince of Persia: Sands of Time, Batman: Rise of Sin Tzu */ + /* - layer header + * 0x04: layer count + * 0x08: stream size + * 0x0c: block count + * 0x10: block header size + * 0x14: block size (fixed) + * 0x18: min layer data? + * 0x1c: size of header sizes + * 0x20+(04*N): header size per layer + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08: always 0x03 + * 0x0c+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x04, streamfile); + + data->header_next_start = 0x14; + data->header_sizes_start = 0x20; + data->header_data_start = 0x20 + data->layer_max*0x04; + + data->block_next_start = 0; + data->block_sizes_start = 0x0c; + data->block_data_start = 0x0c + data->layer_max*0x04; + break; + + case 0x00000007: /* Splinter Cell: Essentials, Splinter Cell 3D */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: stream size + * 0x10: block count + * 0x14: block header size + * 0x18: block size (fixed) + * 0x1c+(04*8): min layer data? for 8 layers (-1 after layer count) + * 0x3c: size of header sizes + * 0x40+(04*N): header size per layer + * 0xNN: header data per layer + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08: always 0x03 + * 0x0c+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x08, streamfile); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x40; + data->header_data_start = 0x40 + data->layer_max*0x04; + + data->block_next_start = 0; + data->block_sizes_start = 0x0c; + data->block_data_start = 0x0c + data->layer_max*0x04; + break; + + case 0x00040008: /* Assassin's Creed */ + case 0x000B0008: /* Open Season, Surf's Up, TMNT, Splinter Cell HD */ + case 0x000C0008: /* Splinter Cell: Double Agent */ + case 0x00100008: /* Rainbow Six 2 */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: blocks count + * 0x10: block header size + * 0x14: size of header sizes/data + * 0x18: next block size + * 0x1c+(04*N): layer header size + * 0xNN: header data per layer + * - block header: + * 0x00: always 0x03 + * 0x04: next block size + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x08, streamfile); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x1c; + data->header_data_start = 0x1c + data->layer_max*0x04; + + data->block_next_start = 0x04; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + case 0x00100009: /* Splinter Cell: Pandora Tomorrow HD, Prince of Persia 2008, Scott Pilgrim */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: blocks count + * 0x10: block header size + * 0x14: size of header sizes/data + * 0x18: next block size + * 0x1c+(04*10): usable size per layer + * 0x5c+(04*N): layer header size + * 0xNN: header data per layer + * - block header: + * 0x00: always 0x03 + * 0x04: next block size + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x08, streamfile); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x5c; + data->header_data_start = 0x5c + data->layer_max*0x04; + + data->block_next_start = 0x04; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + default: + VGM_LOG("UBI SB: unknown layer header %08x\n", version); + goto fail; + } + + /* get base size to simplify later parsing */ + data->header_size = data->header_data_start; + if (data->header_sizes_start) { + for (i = 0; i < data->layer_max; i++) { + data->header_size += read_32bit(offset + data->header_sizes_start + i*0x04, streamfile); + } + } + + /* force read header block */ + data->logical_offset = -1; + + /* just in case some headers may use less layers that stream has */ + VGM_ASSERT(data->layer_count != data->layer_max, "UBI SB: non-matching layer counts\n"); + if (data->layer_count > data->layer_max) { + VGM_LOG("UBI SB: layer count bigger than layer max\n"); + goto fail; + } + + /* Common layer quirks: + * - layer format depends on its own version and not on platform or DARE engine version + * - codec header may be in the layer header, or in the first block + * - stream size doesn't include padding + * - block number goes from 1 to block_count + * - block offset is relative to layer start + * - blocks data size varies between blocks and between layers in the same block + * - "config?" is a small value that varies between streams of the same game + * - next block size is 0 at last block + * - both Ubi SB and Ubi BAO use same-version layers + */ + + return 1; +fail: + return 0; +} + + +/* Handles deinterleaving of Ubisoft's headered+blocked 'multitrack' streams */ +static STREAMFILE* setup_ubi_sb_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, int layer_number, int layer_count, int big_endian) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + ubi_sb_io_data io_data = {0}; + size_t io_data_size = sizeof(ubi_sb_io_data); + + io_data.stream_offset = stream_offset; + io_data.stream_size = stream_size; + io_data.layer_number = layer_number; + io_data.layer_count = layer_count; + io_data.big_endian = big_endian; + + if (!ubi_sb_io_init(streamFile, &io_data)) + goto fail; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, ubi_sb_io_read,ubi_sb_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _UBI_SB_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c index e88a78a59..b4f2a9839 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -19,8 +19,10 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { * .swag: Frantix (PSP) * .str: Ben10 Galactic Racing * .vig: MX vs. ATV Untamed (PS2) - * .l/r: Crash Nitro Kart (PS2), Gradius V (PS2) */ - if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r") ) + * .l/r: Crash Nitro Kart (PS2), Gradius V (PS2) + * .vas: Kingdom Hearts II (PS2) + * .khv: fake for .vas */ + if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r,vas,khv") ) goto fail; /* check VAG Header */ @@ -112,7 +114,7 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { interleave = 0x10; loop_flag = 0; } - else if (check_extensions(streamFile,"swag")) { /* algo "VAGp" at (file_size / channels) */ + else if (check_extensions(streamFile,"swag")) { /* also "VAGp" at (file_size / channels) */ /* Frantix (PSP) */ start_offset = 0x40; /* channel_size ignores empty frame */ channel_count = 2; @@ -188,6 +190,19 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { channel_size = channel_size / channel_count; loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); } + else if (version == 0x00000004 && channel_size == file_size - 0x60 && read_32bitBE(0x1c, streamFile) != 0) { /* also .vas */ + /* Kingdom Hearts II (PS2) */ + start_offset = 0x60; + interleave = 0x10; + + loop_start_sample = read_32bitBE(0x14,streamFile); + loop_end_sample = read_32bitBE(0x18,streamFile); + loop_flag = (loop_end_sample > 0); /* maybe at 0x1d */ + channel_count = read_8bit(0x1e,streamFile); + /* 0x1f: possibly volume */ + channel_size = channel_size / channel_count; + /* mono files also have channel/volume, but start at 0x30 and are probably named .vag */ + } else { /* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */ start_offset = 0x30; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xnb.c b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c index 3e2191f0c..2ca78366b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xnb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c @@ -22,7 +22,8 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { platform = read_8bit(0x03,streamFile); big_endian = (platform == 'x'); - if (read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version only */ + if (read_8bit(0x04,streamFile) != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */ + read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version */ goto fail; flags = read_8bit(0x05,streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 8e365381b..d26a9db5c 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -253,7 +253,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_smpl, init_vgmstream_ps2_msa, init_vgmstream_ps2_voi, - init_vgmstream_ps2_khv, init_vgmstream_ngc_rkv, init_vgmstream_dsp_ddsp, init_vgmstream_p3d, @@ -279,6 +278,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_sqex_scd, init_vgmstream_ngc_nst_dsp, init_vgmstream_baf, + init_vgmstream_baf_badrip, init_vgmstream_ps3_msf, init_vgmstream_nub_vag, init_vgmstream_ps3_past, @@ -375,9 +375,11 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_vxn, init_vgmstream_ea_snr_sns, init_vgmstream_ea_sps, - init_vgmstream_ea_abk_new, + init_vgmstream_ea_abk_eaac, init_vgmstream_ea_hdr_sth_dat, - init_vgmstream_ea_mpf_mus_new, + init_vgmstream_ea_mpf_mus_eaac, + init_vgmstream_ea_sbr, + init_vgmstream_ea_sbr_harmony, init_vgmstream_ngc_vid1, init_vgmstream_flx, init_vgmstream_mogg, @@ -460,6 +462,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_imc, init_vgmstream_imc_container, init_vgmstream_smp, + init_vgmstream_gin, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ @@ -1142,6 +1145,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_SNDS_IMA: case coding_OTNS_IMA: case coding_UBI_IMA: + case coding_OKI16: return 1; case coding_IMA_int: case coding_DVI_IMA_int: @@ -1188,11 +1192,14 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_EA_XA_V2: case coding_MAXIS_XA: return 28; - case coding_EA_XAS: + case coding_EA_XAS_V0: + return 32; + case coding_EA_XAS_V1: return 128; case coding_MSADPCM: return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; + case coding_MSADPCM_int: case coding_MSADPCM_ck: return (vgmstream->interleave_block_size - 0x07)*2 + 2; case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ @@ -1323,6 +1330,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_ALP_IMA: case coding_FFTA2_IMA: case coding_PCFX: + case coding_OKI16: return 0x01; case coding_MS_IMA: case coding_RAD_IMA: @@ -1370,10 +1378,13 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x0F*vgmstream->channels; case coding_EA_XA_V2: return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */ - case coding_EA_XAS: + case coding_EA_XAS_V0: + return 0xF+0x02+0x02; + case coding_EA_XAS_V1: return 0x4c*vgmstream->channels; case coding_MSADPCM: + case coding_MSADPCM_int: case coding_MSADPCM_ck: return vgmstream->interleave_block_size; case coding_WS: @@ -1550,6 +1561,18 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; + case coding_PCM4: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm4(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch); + } + break; + case coding_PCM4_U: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm4_unsigned(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch); + } + break; case coding_ULAW: for (ch = 0; ch < vgmstream->channels; ch++) { @@ -1694,9 +1717,15 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; - case coding_EA_XAS: + case coding_EA_XAS_V0: for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xas(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + decode_ea_xas_v0(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); + } + break; + case coding_EA_XAS_V1: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xas_v1(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; @@ -1923,14 +1952,17 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to buffer+samples_written*vgmstream->channels, samples_to_do); break; case coding_MSADPCM: - if (vgmstream->channels == 2) { + case coding_MSADPCM_int: + if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) { + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); + } + } + else if (vgmstream->channels == 2) { decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels, vgmstream->samples_into_block,samples_to_do); } - else if (vgmstream->channels == 1) { - decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0); - } break; case coding_MSADPCM_ck: for (ch = 0; ch < vgmstream->channels; ch++) { @@ -2028,6 +2060,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->codec_config); } break; + case coding_OKI16: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_oki16(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); + } + break; case coding_EA_MT: for (ch = 0; ch < vgmstream->channels; ch++) { @@ -2277,11 +2315,18 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE, "\nlayout: "); concatn(length,desc,temp); + + description = get_vgmstream_layout_description(vgmstream->layout_type); + if (!description) + description = "INCONCEIVABLE"; switch (vgmstream->layout_type) { + case layout_layered: + snprintf(temp,TEMPSIZE,"%s (%i layers)",description, ((layered_layout_data*)vgmstream->layout_data)->layer_count); + break; + case layout_segmented: + snprintf(temp,TEMPSIZE,"%s (%i segments)",description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); + break; default: - description = get_vgmstream_layout_description(vgmstream->layout_type); - if (!description) - description = "INCONCEIVABLE"; snprintf(temp,TEMPSIZE,"%s",description); break; } @@ -2309,6 +2354,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) { switch (vgmstream->coding_type) { case coding_MSADPCM: + case coding_MSADPCM_int: case coding_MSADPCM_ck: case coding_MS_IMA: case coding_MC3: @@ -2723,14 +2769,13 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s if (!file) goto fail; } - for (ch=0; ch < vgmstream->channels; ch++) { + for (ch = 0; ch < vgmstream->channels; ch++) { off_t offset; if (use_same_offset_per_channel) { offset = start_offset; } else if (is_stereo_codec) { int ch_mod = (ch & 1) ? ch - 1 : ch; /* adjust odd channels (ch 0,1,2,3,4,5 > ch 0,0,2,2,4,4) */ offset = start_offset + vgmstream->interleave_block_size*ch_mod; - //VGM_LOG("ch%i offset=%lx\n", ch,offset); } else { offset = start_offset + vgmstream->interleave_block_size*ch; } @@ -2741,6 +2786,7 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s if (!file) goto fail; } + VGM_LOG("ch%i offset=%lx\n", ch,offset); vgmstream->ch[ch].streamfile = file; vgmstream->ch[ch].channel_start_offset = vgmstream->ch[ch].offset = offset; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 6c4a2276f..3292185a9 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -80,6 +80,8 @@ typedef enum { coding_PCM8_U, /* 8-bit PCM, unsigned (0x80 = 0) */ coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave (for blocks) */ coding_PCM8_SB, /* 8-bit PCM, sign bit (others are 2's complement) */ + coding_PCM4, /* 4-bit PCM, signed */ + coding_PCM4_U, /* 4-bit PCM, unsigned */ coding_ULAW, /* 8-bit u-Law (non-linear PCM) */ coding_ULAW_int, /* 8-bit u-Law (non-linear PCM) with sample-level interleave (for blocks) */ @@ -111,7 +113,8 @@ typedef enum { coding_EA_XA_int, /* Electronic Arts EA-XA ADPCM v1 (mono/interleave) */ coding_EA_XA_V2, /* Electronic Arts EA-XA ADPCM v2 */ coding_MAXIS_XA, /* Maxis EA-XA ADPCM */ - coding_EA_XAS, /* Electronic Arts EA-XAS ADPCM */ + coding_EA_XAS_V0, /* Electronic Arts EA-XAS ADPCM v0 */ + coding_EA_XAS_V1, /* Electronic Arts EA-XAS ADPCM v1 */ coding_IMA, /* IMA ADPCM (stereo or mono, low nibble first) */ coding_IMA_int, /* IMA ADPCM (mono/interleave, low nibble first) */ @@ -141,6 +144,7 @@ typedef enum { coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */ + coding_MSADPCM_int, /* Microsoft ADPCM (mono) */ coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */ coding_WS, /* Westwood Studios VBR ADPCM */ coding_AICA, /* Yamaha AICA ADPCM (stereo) */ @@ -157,6 +161,7 @@ typedef enum { coding_ASF, /* Argonaut ASF 4-bit ADPCM */ coding_XMD, /* Konami XMD 4-bit ADPCM */ coding_PCFX, /* PC-FX 4-bit ADPCM */ + coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output */ /* others */ coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ @@ -537,7 +542,6 @@ typedef enum { meta_PS2_SMPL, /* Homura */ meta_PS2_MSA, /* Psyvariar -Complete Edition- */ meta_PS2_VOI, /* RAW Danger (Zettaizetsumei Toshi 2 - Itetsuita Kiokutachi) [PS2] */ - meta_PS2_KHV, /* Kingdom Hearts 2 VAG streams */ meta_P3D, /* Prototype P3D */ meta_PS2_TK1, /* Tekken (NamCollection) */ meta_NGC_RKV, /* Legacy of Kain - Blood Omen 2 (GC) */ @@ -717,6 +721,7 @@ typedef enum { meta_DSP_ADPCMX, meta_OGG_OPUS, meta_IMC, + meta_GIN, } meta_t;