From 0b09d1567b84bb94919836cc134dd762aa7302e4 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Mon, 25 Jan 2021 14:57:19 -0800 Subject: [PATCH] Updated VGMStream to r1050-3581-g8fd25a33 --- .../libvgmstream.xcodeproj/project.pbxproj | 48 +- .../vgmstream/vgmstream/src/coding/coding.h | 23 + .../src/coding/coding_utils_samples.h | 60 + .../src/coding/compresswave_decoder.c | 138 ++ .../src/coding/compresswave_decoder_lib.c | 998 ++++++++++++++ .../src/coding/compresswave_decoder_lib.h | 27 + .../vgmstream/src/coding/ea_mt_decoder.c | 411 +++--- .../vgmstream/src/coding/ea_xa_decoder.c | 558 ++++---- .../vgmstream/src/coding/mc3_decoder.c | 366 ++--- .../vgmstream/src/coding/msadpcm_decoder.c | 508 +++---- .../vgmstream/src/coding/psv_decoder.c | 414 +++--- .../vgmstream/src/coding/speex_decoder.c | 188 +++ .../vgmstream/src/coding/ubi_adpcm_decoder.c | 1176 ++++++++--------- .../vgmstream/src/coding/xa_decoder.c | 324 ++--- Frameworks/vgmstream/vgmstream/src/decode.c | 51 + Frameworks/vgmstream/vgmstream/src/formats.c | 14 +- .../vgmstream/vgmstream/src/meta/adx_keys.h | 3 + Frameworks/vgmstream/vgmstream/src/meta/ao.c | 76 +- .../vgmstream/src/meta/compresswave.c | 50 + .../vgmstream/vgmstream/src/meta/ea_eaac.c | 19 +- .../vgmstream/src/meta/ea_eaac_streamfile.h | 2 + .../src/meta/fsb_encrypted_streamfile.h | 2 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 3 + .../vgmstream/vgmstream/src/meta/idtech.c | 489 +++++++ ...{mzrt_streamfile.h => idtech_streamfile.h} | 0 .../vgmstream/vgmstream/src/meta/ktac.c | 514 +++++++ .../vgmstream/vgmstream/src/meta/meta.h | 10 +- .../vgmstream/vgmstream/src/meta/mib_mih.c | 144 +- .../vgmstream/vgmstream/src/meta/mjb_mjh.c | 83 ++ .../vgmstream/vgmstream/src/meta/msb_msh.c | 167 ++- .../vgmstream/vgmstream/src/meta/musx.c | 314 +++-- .../vgmstream/vgmstream/src/meta/mzrt.c | 151 --- .../vgmstream/src/meta/ngc_dsp_std.c | 14 +- .../vgmstream/vgmstream/src/meta/str_wav.c | 1102 +++++++-------- .../vgmstream/vgmstream/src/meta/ta_aac.c | 52 +- .../vgmstream/vgmstream/src/streamfile.h | 12 +- Frameworks/vgmstream/vgmstream/src/util.h | 25 +- .../vgmstream/vgmstream/src/vgmstream.c | 11 +- .../vgmstream/vgmstream/src/vgmstream.h | 12 +- 39 files changed, 5665 insertions(+), 2894 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/coding_utils_samples.h create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder.c create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder_lib.c create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder_lib.h create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/speex_decoder.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/compresswave.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/idtech.c rename Frameworks/vgmstream/vgmstream/src/meta/{mzrt_streamfile.h => idtech_streamfile.h} (100%) create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ktac.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/mjb_mjh.c delete mode 100644 Frameworks/vgmstream/vgmstream/src/meta/mzrt.c diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 25eadaa1e..61b9cddfd 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -133,6 +133,16 @@ 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; 8342469420C4D23000926E48 /* h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469020C4D22F00926E48 /* h4m.c */; }; 8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469520C4D23D00926E48 /* blocked_h4m.c */; }; + 8346D97925BF838C00D1A8B0 /* idtech.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97425BF838C00D1A8B0 /* idtech.c */; }; + 8346D97A25BF838C00D1A8B0 /* idtech_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */; }; + 8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97625BF838C00D1A8B0 /* ktac.c */; }; + 8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97725BF838C00D1A8B0 /* mjb_mjh.c */; }; + 8346D97D25BF838C00D1A8B0 /* compresswave.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97825BF838C00D1A8B0 /* compresswave.c */; }; + 8346D98325BF83B300D1A8B0 /* speex_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97E25BF83B200D1A8B0 /* speex_decoder.c */; }; + 8346D98425BF83B300D1A8B0 /* coding_utils_samples.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */; }; + 8346D98525BF83B300D1A8B0 /* compresswave_decoder_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */; }; + 8346D98625BF83B300D1A8B0 /* compresswave_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */; }; + 8346D98725BF83B300D1A8B0 /* compresswave_decoder_lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */; }; 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */; }; 8349A8E11FE6251F00E26435 /* ea_mt_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */; }; 8349A8E81FE6253900E26435 /* blocked_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E21FE6253800E26435 /* blocked_dec.c */; }; @@ -471,10 +481,8 @@ 837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAD723487E8300E62A4A /* bgw_streamfile.h */; }; 837CEAF123487F2C00E62A4A /* xvas.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADC23487F2900E62A4A /* xvas.c */; }; 837CEAF223487F2C00E62A4A /* raw_pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADD23487F2A00E62A4A /* raw_pcm.c */; }; - 837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */; }; 837CEAF423487F2C00E62A4A /* xa_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADF23487F2A00E62A4A /* xa_xa30.c */; }; 837CEAF523487F2C00E62A4A /* ubi_hx.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE023487F2A00E62A4A /* ubi_hx.c */; }; - 837CEAF623487F2C00E62A4A /* mzrt.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE123487F2A00E62A4A /* mzrt.c */; }; 837CEAF723487F2C00E62A4A /* nub.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE223487F2A00E62A4A /* nub.c */; }; 837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE323487F2A00E62A4A /* xmv_valve.c */; }; 837CEAF923487F2C00E62A4A /* xavs.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE423487F2A00E62A4A /* xavs.c */; }; @@ -877,6 +885,16 @@ 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; 8342469020C4D22F00926E48 /* h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h4m.c; sourceTree = ""; }; 8342469520C4D23D00926E48 /* blocked_h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_h4m.c; sourceTree = ""; }; + 8346D97425BF838C00D1A8B0 /* idtech.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idtech.c; sourceTree = ""; }; + 8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = idtech_streamfile.h; sourceTree = ""; }; + 8346D97625BF838C00D1A8B0 /* ktac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktac.c; sourceTree = ""; }; + 8346D97725BF838C00D1A8B0 /* mjb_mjh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mjb_mjh.c; sourceTree = ""; }; + 8346D97825BF838C00D1A8B0 /* compresswave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave.c; sourceTree = ""; }; + 8346D97E25BF83B200D1A8B0 /* speex_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = speex_decoder.c; sourceTree = ""; }; + 8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding_utils_samples.h; sourceTree = ""; }; + 8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave_decoder_lib.c; sourceTree = ""; }; + 8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave_decoder.c; sourceTree = ""; }; + 8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = compresswave_decoder_lib.h; sourceTree = ""; }; 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = ""; }; 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_mt_decoder.c; sourceTree = ""; }; 8349A8E21FE6253800E26435 /* blocked_dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_dec.c; sourceTree = ""; }; @@ -1215,10 +1233,8 @@ 837CEAD723487E8300E62A4A /* bgw_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bgw_streamfile.h; sourceTree = ""; }; 837CEADC23487F2900E62A4A /* xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvas.c; sourceTree = ""; }; 837CEADD23487F2A00E62A4A /* raw_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_pcm.c; sourceTree = ""; }; - 837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mzrt_streamfile.h; sourceTree = ""; }; 837CEADF23487F2A00E62A4A /* xa_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_xa30.c; sourceTree = ""; }; 837CEAE023487F2A00E62A4A /* ubi_hx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_hx.c; sourceTree = ""; }; - 837CEAE123487F2A00E62A4A /* mzrt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mzrt.c; sourceTree = ""; }; 837CEAE223487F2A00E62A4A /* nub.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub.c; sourceTree = ""; }; 837CEAE323487F2A00E62A4A /* xmv_valve.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmv_valve.c; sourceTree = ""; }; 837CEAE423487F2A00E62A4A /* xavs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xavs.c; sourceTree = ""; }; @@ -1545,8 +1561,12 @@ 83031EC0243C50A800C3F3E0 /* circus_decoder_miniz.c */, 83031EBE243C50A700C3F3E0 /* circus_decoder_miniz.h */, 832BF7FC21E050B6006F50F1 /* circus_decoder.c */, + 8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */, 831BA6221EAC61CB00CF89B0 /* coding_utils.c */, 836F6DE518BDC2180095E648 /* coding.h */, + 8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */, + 8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */, + 8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */, 834FE0AD215C798B000A5D3D /* derf_decoder.c */, 8351F3252212B53300A606E4 /* dsa_decoder.c */, 834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */, @@ -1600,6 +1620,7 @@ 8373341423F60C7B00DE14DC /* relic_decoder.c */, 836F6DFB18BDC2180095E648 /* SASSC_decoder.c */, 836F6DFC18BDC2180095E648 /* sdx2_decoder.c */, + 8346D97E25BF83B200D1A8B0 /* speex_decoder.c */, 8373341023F60C7A00DE14DC /* tgcadpcm_decoder.c */, 837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */, 83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */, @@ -1734,6 +1755,7 @@ 8306B0CF2098458F000302D4 /* caf.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */, 834FE0E8215C79EC000A5D3D /* ck.c */, + 8346D97825BF838C00D1A8B0 /* compresswave.c */, 83A8BAE425667AA7000F5F3F /* cpk.c */, 83FC177023AC59A800E1025F /* cri_utf.c */, 83FC176C23AC58D100E1025F /* cri_utf.h */, @@ -1799,6 +1821,8 @@ 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */, 836F6E5318BDC2180095E648 /* his.c */, 834FE0E0215C79EB000A5D3D /* idsp_ie.c */, + 8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */, + 8346D97425BF838C00D1A8B0 /* idtech.c */, 8399335C2591E8C0001855AF /* ifs.c */, 83C7280922BC893C00678B4A /* ikm.c */, 837CEAE623487F2B00E62A4A /* ima.c */, @@ -1816,6 +1840,7 @@ 834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */, 83A21F83201D8981000F04B9 /* kma9.c */, 836F6E5918BDC2180095E648 /* kraw.c */, + 8346D97625BF838C00D1A8B0 /* ktac.c */, 83AA7F792519C042004C5298 /* ktsc.c */, 83D20074248DDB760048BD24 /* ktsr.c */, 830EBE122004656E0023AA10 /* ktss.c */, @@ -1829,6 +1854,7 @@ 83EDE5D61A70951A005F5D84 /* mca.c */, 836F6E5E18BDC2180095E648 /* meta.h */, 834FE0DE215C79EB000A5D3D /* mib_mih.c */, + 8346D97725BF838C00D1A8B0 /* mjb_mjh.c */, 836F6E5F18BDC2180095E648 /* mn_str.c */, 8349A9031FE6258100E26435 /* mogg.c */, 836F6E6018BDC2180095E648 /* mp4.c */, @@ -1852,8 +1878,6 @@ 836F6E6318BDC2180095E648 /* musc.c */, 836F6E6418BDC2180095E648 /* musx.c */, 836F6E6518BDC2180095E648 /* myspd.c */, - 837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */, - 837CEAE123487F2A00E62A4A /* mzrt.c */, 8349A9061FE6258100E26435 /* naac.c */, 836F6E6618BDC2180095E648 /* naomi_adpcm.c */, 836F6E6718BDC2180095E648 /* naomi_spsd.c */, @@ -2156,6 +2180,8 @@ 836F705518BDC2190095E648 /* streamtypes.h in Headers */, 836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */, 83031EC9243C50A800C3F3E0 /* circus_decoder_lzxpcm.h in Headers */, + 8346D97A25BF838C00D1A8B0 /* idtech_streamfile.h in Headers */, + 8346D98425BF83B300D1A8B0 /* coding_utils_samples.h in Headers */, 83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */, 83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */, 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, @@ -2201,6 +2227,7 @@ 834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */, 839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */, 83AA7F8A2519C076004C5298 /* decode.h in Headers */, + 8346D98725BF83B300D1A8B0 /* compresswave_decoder_lib.h in Headers */, 8373342D23F60CDC00DE14DC /* bnsf_keys.h in Headers */, 834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */, 83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */, @@ -2223,7 +2250,6 @@ 83C7281822BC893D00678B4A /* sfh_streamfile.h in Headers */, 834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */, 8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */, - 837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */, 83031EC4243C50A800C3F3E0 /* circus_decoder_miniz.h in Headers */, 8349A91B1FE6258200E26435 /* adx_keys.h in Headers */, 836F6F4D18BDC2190095E648 /* layout.h in Headers */, @@ -2419,6 +2445,7 @@ 836F705318BDC2190095E648 /* streamfile.c in Sources */, 836F6F7418BDC2190095E648 /* bgw.c in Sources */, 83F1EE2D245D4FB20076E182 /* imuse_decoder.c in Sources */, + 8346D98325BF83B300D1A8B0 /* speex_decoder.c in Sources */, 836F6F7218BDC2190095E648 /* baf.c in Sources */, 83F5F8831908D0A400C8E65F /* fsb5.c in Sources */, 836F6FD418BDC2190095E648 /* ps2_dxh.c in Sources */, @@ -2447,6 +2474,7 @@ 836F6FA118BDC2190095E648 /* myspd.c in Sources */, 837CEB0123487F2C00E62A4A /* xmu.c in Sources */, 836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */, + 8346D97925BF838C00D1A8B0 /* idtech.c in Sources */, 83FF0EBC1E93282100C58054 /* wwise.c in Sources */, 836F6F7018BDC2190095E648 /* apple_caff.c in Sources */, 836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */, @@ -2459,6 +2487,7 @@ 836F702318BDC2190095E648 /* rsf.c in Sources */, 834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */, 83299FD01E7660C7003A3242 /* bik.c in Sources */, + 8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */, 8373341823F60C7B00DE14DC /* tgcadpcm_decoder.c in Sources */, 836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */, 83C7281322BC893D00678B4A /* mta2.c in Sources */, @@ -2518,6 +2547,7 @@ 836F703618BDC2190095E648 /* thp.c in Sources */, 836F6F7818BDC2190095E648 /* Cstr.c in Sources */, 83F1EE30245D4FC10076E182 /* imuse.c in Sources */, + 8346D98625BF83B300D1A8B0 /* compresswave_decoder.c in Sources */, 836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */, 836F6FD818BDC2190095E648 /* ps2_gbts.c in Sources */, 831BA61A1EAC61A500CF89B0 /* ps2_vds_vdm.c in Sources */, @@ -2564,6 +2594,7 @@ 8306B0A720984552000302D4 /* blocked_xvas.c in Sources */, 8349A9101FE6258200E26435 /* ea_eaac.c in Sources */, 836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */, + 8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */, 8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */, 836F6F6F18BDC2190095E648 /* akb.c in Sources */, 83EED5D6203A8BD7008BEB45 /* blocked_ea_swvr.c in Sources */, @@ -2821,7 +2852,6 @@ 836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */, 83AFABBC23795202002F3947 /* xssb.c in Sources */, 836F704818BDC2190095E648 /* xbox_ims.c in Sources */, - 837CEAF623487F2C00E62A4A /* mzrt.c in Sources */, 83AA7F832519C042004C5298 /* adp_konami.c in Sources */, 836F6F7518BDC2190095E648 /* bnsf.c in Sources */, 836F704318BDC2190095E648 /* wpd.c in Sources */, @@ -2847,6 +2877,8 @@ 836F6F7618BDC2190095E648 /* brstm.c in Sources */, 836F700718BDC2190095E648 /* ps2_vgv.c in Sources */, 836F704F18BDC2190095E648 /* xwb.c in Sources */, + 8346D98525BF83B300D1A8B0 /* compresswave_decoder_lib.c in Sources */, + 8346D97D25BF838C00D1A8B0 /* compresswave.c in Sources */, 8306B0AD20984552000302D4 /* blocked_caf.c in Sources */, 8306B0AA20984552000302D4 /* blocked_sthd.c in Sources */, 836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 2807bd9cb..f9ec5b3b1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -270,6 +270,17 @@ void seek_imuse(imuse_codec_data* data, int32_t num_sample); void free_imuse(imuse_codec_data* data); +/* compresswave_decoder */ +typedef struct compresswave_codec_data compresswave_codec_data; + +compresswave_codec_data* init_compresswave(STREAMFILE* sf); +void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do); +void reset_compresswave(compresswave_codec_data* data); +void seek_compresswave(compresswave_codec_data* data, int32_t num_sample); +void free_compresswave(compresswave_codec_data* data); +STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data); + + /* ea_mt_decoder*/ typedef struct ea_mt_codec_data ea_mt_codec_data; @@ -512,6 +523,18 @@ void free_celt_fsb(celt_codec_data* data); #endif +#ifdef VGM_USE_SPEEX +/* speex_decoder */ +typedef struct speex_codec_data speex_codec_data; + +speex_codec_data* init_speex_ea(int channels); +void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do); +void reset_speex(speex_codec_data* data); +void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample); +void free_speex(speex_codec_data* data); +#endif + + #ifdef VGM_USE_FFMPEG /* ffmpeg_decoder */ ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils_samples.h b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils_samples.h new file mode 100644 index 000000000..05b55da34 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils_samples.h @@ -0,0 +1,60 @@ +#ifndef _CODING_UTILS_SAMPLES_ +#define _CODING_UTILS_SAMPLES_ + +/* sample helpers */ +//TODO maybe move to .c +// (as .h can be inlined but these probably aren't called enough times that there is a notable boost) + +typedef struct { + int16_t* samples; /* current samples (pointer is moved once consumed) */ + int filled; /* samples left */ + int channels; /* max channels sample buf handles */ + //TODO may be more useful with filled+consumed and not moving *samples? +} s16buf_t; + +static void s16buf_silence(sample_t** p_outbuf, int32_t* p_samples_silence, int channels) { + int samples_silence; + + samples_silence = *p_samples_silence; + + memset(*p_outbuf, 0, samples_silence * channels * sizeof(int16_t)); + + *p_outbuf += samples_silence * channels; + *p_samples_silence -= samples_silence; +} + +static void s16buf_discard(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_discard) { + int samples_discard; + + samples_discard = *p_samples_discard; + if (samples_discard > sbuf->filled) + samples_discard = sbuf->filled; + + /* just ignore part of samples */ + + sbuf->samples += samples_discard * sbuf->channels; + sbuf->filled -= samples_discard; + + *p_samples_discard -= samples_discard; +} + +/* copy, move and mark consumed samples */ +static void s16buf_consume(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_consume) { + int samples_consume; + + samples_consume = *p_samples_consume; + if (samples_consume > sbuf->filled) + samples_consume = sbuf->filled; + + /* memcpy is safe when filled/samples_copy is 0 (but must pass non-NULL bufs) */ + memcpy(*p_outbuf, sbuf->samples, samples_consume * sbuf->channels * sizeof(int16_t)); + + sbuf->samples += samples_consume * sbuf->channels; + sbuf->filled -= samples_consume; + + *p_outbuf += samples_consume * sbuf->channels; + *p_samples_consume -= samples_consume; +} + + +#endif /* _CODING_UTILS_SAMPLES_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder.c new file mode 100644 index 000000000..ecd78fcf7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder.c @@ -0,0 +1,138 @@ +#include "coding.h" +#include "coding_utils_samples.h" +#include "compresswave_decoder_lib.h" + + +#define COMPRESSWAVE_MAX_FRAME_SAMPLES 0x1000 /* arbitrary but should be multiple of 2 for 22050 mode */ + +/* opaque struct */ +struct compresswave_codec_data { + /* config */ + STREAMFILE* sf; + TCompressWaveData* cw; + + /* frame state */ + int16_t* samples; + int frame_samples; + + /* frame state */ + s16buf_t sbuf; + int samples_discard; +}; + + +compresswave_codec_data* init_compresswave(STREAMFILE* sf) { + compresswave_codec_data* data = NULL; + + data = calloc(1, sizeof(compresswave_codec_data)); + if (!data) goto fail; + + data->sf = reopen_streamfile(sf, 0); + if (!data->sf) goto fail; + + data->frame_samples = COMPRESSWAVE_MAX_FRAME_SAMPLES; + data->samples = malloc(2 * data->frame_samples * sizeof(int16_t)); /* always stereo */ + if (!data->samples) goto fail; + + + data->cw = TCompressWaveData_Create(); + if (!data->cw) goto fail; + + TCompressWaveData_LoadFromStream(data->cw, data->sf); + + reset_compresswave(data); + + return data; +fail: + free_compresswave(data); + return NULL; +} + + +static int decode_frame(compresswave_codec_data* data, int32_t samples_to_do) { + uint32_t Len; + int ok; + + data->sbuf.samples = data->samples; + data->sbuf.channels = 2; + data->sbuf.filled = 0; + + if (samples_to_do > data->frame_samples) + samples_to_do = data->frame_samples; + if (samples_to_do % 2 && samples_to_do > 1) + samples_to_do -= 1; /* 22khz does 2 samples at once */ + + Len = samples_to_do * sizeof(int16_t) * 2; /* forced stereo */ + + ok = TCompressWaveData_Rendering(data->cw, data->sbuf.samples, Len); + if (!ok) goto fail; + + data->sbuf.filled = samples_to_do; + + return 1; +fail: + return 0; +} + + +void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { + int ok; + + + while (samples_to_do > 0) { + s16buf_t* sbuf = &data->sbuf; + + if (sbuf->filled <= 0) { + ok = decode_frame(data, samples_to_do); + if (!ok) goto fail; + } + + if (data->samples_discard) + s16buf_discard(&outbuf, sbuf, &data->samples_discard); + else + s16buf_consume(&outbuf, sbuf, &samples_to_do); + } + + return; + +fail: + VGM_LOG("COMPRESSWAVE: decode fail, missing %i samples\n", samples_to_do); + s16buf_silence(&outbuf, &samples_to_do, 2); +} + + +void reset_compresswave(compresswave_codec_data* data) { + if (!data) return; + + /* actual way to reset internal flags */ + TCompressWaveData_Stop(data->cw); + TCompressWaveData_Play(data->cw, 0); + + data->sbuf.filled = 0; + data->samples_discard = 0; + + return; +} + +void seek_compresswave(compresswave_codec_data* data, int32_t num_sample) { + if (!data) return; + + reset_compresswave(data); + data->samples_discard += num_sample; +} + +void free_compresswave(compresswave_codec_data* data) { + if (!data) + return; + + TCompressWaveData_Free(data->cw); + + close_streamfile(data->sf); + free(data->samples); + free(data); +} + +STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data) { + if (!data) return NULL; + return data->sf; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder_lib.c new file mode 100644 index 000000000..3b2978b5d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder_lib.c @@ -0,0 +1,998 @@ +#include "compresswave_decoder_lib.h" +#include +#include +#include +#include + + +/* Decodes CWav (CompressWave) audio codec, based on original delphi/pascal source code by Ko-Ta: + * - http://kota.dokkoisho.com/ + * - http://kota.dokkoisho.com/library/CompressWave.zip + * - https://web.archive.org/web/20180819144937/http://d.hatena.ne.jp/Ko-Ta/20070318/p1 + * (no license given) + * Apparently found in few Japanese (doujin?) games around 1995-2002, most notably RADIO ZONDE. + * + * This is mostly a simple re-implementation following original code, basically Pascal-classes-to-plain-C + * because why not. Only decoder part is replicated (some writting/loading/etc stuff removed or cleaned up). + * Results should be byte-exact (all is int math). + * **some parts like internal looping weren't tested (no valid files) + * + * Codec is basically huffman-coded ADPCM, that includes diff table and huffman tree setup in + * CWav header. Described by the author as being "big, heavy and with bad sound quality". + * An oddity is that mono files are output as fake stereo (repeats L/R), this is correct and agrees + * with PCM totals in header. Output sample rate is always 44100 and files marked as 22050 or mono just + * decode slightly differently. Curiously PCM output size in header may be not be multiple of 4, meaning + * files that end with garbage half-a-sample (L sample = 0, nothing for R). + */ + + +/* ************************************************************************* */ +/* common */ +/* ************************************************************************* */ +// pascal reader simulated in C +typedef struct { + STREAMFILE* File; + int64_t Position; + int64_t Size; +} TStream; + +static void TStream_Read_Uint32(TStream* this, uint32_t* value) { + uint8_t buf[0x4] = {0}; + + read_streamfile(buf, this->Position, sizeof(buf), this->File); + this->Position += 0x4; + + *value = get_u32le(buf); +} + + +/* ************************************************************************* */ +/* HuffLib.pas */ +/* ************************************************************************* */ + +#define CW_TRUE 1 +#define CW_FALSE 0 + +typedef enum { nsEmpty, nsBranch, nsLeaf, nsRoot } TNodeState; + +//------------------------------------------------------------------------------ +//structure declaration + +//node structure for huffman tree +typedef struct { + uint8_t Value; //value (0..255) + int32_t Weight; //weight value used during huffman tree creation + TNodeState State; //state + int32_t Link[2]; //bidimensional tree L/R path (-1: unused, >=0: index) +} THuffTreeNode; + +//header info for file writting +typedef struct { + char HedChar[4]; //head info + int32_t Version; //Version + uint32_t HistGraph[256]; //appearance rate + int64_t FileSize; //file size +} THuffHedState; + +//for jumping to arbitrary places (^^), various usages +typedef struct { + uint32_t BitBuf; + int32_t BitCount; + int64_t StreamPos; + uint32_t CipherBuf; +} THuffPositionData; + +//------------------------------------------------------------------------------ +//huffman encoding class +//handled values are 0~255 of 1 byte. +//takes appearance rate and makes a huffman tree for encoding + +//EXTRA: external lib part, but not really needed so all is static + +typedef struct { + //control + TStream Buff; // target stream + int64_t BeginPos; // encoded area + int Mode; // (0=initial, 1=read, 2=write) + + //bit IO + int IoCount; // 0..initial state, 1..read, 2..write + uint32_t BitBuf; // held buffer + int BitCount; // processed bit count +#if 0 + int64_t BitWriteLen; // written size +#endif + uint32_t CipherBuf; + + //huffman + THuffTreeNode Node[512]; //tree structure + uint8_t Code[256][256]; //fork support + int32_t Root; //root + + //huffman cipher bits + uint32_t CipherList[16]; + + //header info + THuffHedState Hed; +} THuff; + + +//related to huffman encoding +static void THuff_InitHuffTree(THuff* this); //initializes tree +static int THuff_InsertHuffNode(THuff* this, int v, int w, TNodeState s, int b1, int b2); //add node to tree +static void THuff_MakeHuffTree(THuff* this); + +//related to single bit IO +static void THuff_BeginBitIO(THuff* this); +static void THuff_EndBitIO(THuff* this); +static int THuff_ReadBit(THuff* this); +static uint32_t THuff__ROR(uint32_t src, uint32_t shift); + +static THuff* THuff_Create(TStream* buf); // creation +static void THuff_Free(THuff* this); // release the power +static void THuff_SetCipherCode(THuff* this, uint32_t msk); // encryption mask bits +//functions for reading +static void THuff_BeginRead(THuff* this); +static int THuff_Read(THuff* this); + +#if 0 +static int64_t THuff_GetFileSize(THuff* this); // get file size before encoding +static int THuff_GetEOF(THuff* this); // EOF detection +#endif +static void THuff_MoveBeginPosition(THuff* this); // return to initial state +static void THuff_GetPositionData(THuff* this, THuffPositionData* s); // secret +static void THuff_SetPositionData(THuff* this, THuffPositionData* s); + +//------------------------------------------------------------------------------ +//create +static THuff* THuff_Create(TStream* buf) { + THuff* this = malloc(sizeof(THuff)); + if (!this) return NULL; + + //define stream + this->Buff = *buf; + + //initialization + THuff_InitHuffTree(this); + memcpy(this->Hed.HedChar, "HUF\0", 0x4); + this->Hed.Version = 1; + this->Hed.FileSize = 0; + + //set cipher bits + this->CipherBuf = 0; + THuff_SetCipherCode(this, 0x0); + + //mode + this->Mode = 0; + + return this; +} + +//------------------------------------------------------------------------------ +//free +static void THuff_Free(THuff* this) { + if (this == NULL) return; + if (this->Mode == 2) + THuff_EndBitIO(this); + free(this); +} + +//------------------------------------------------------------------------------ +//init tree structure (unused state) +static void THuff_InitHuffTree(THuff* this) { + int i; + + for (i = 0; i < 512; i++) { + this->Node[i].State = nsEmpty; + } +} + +//------------------------------------------------------------------------------ +//add node to huffman tree +static int THuff_InsertHuffNode(THuff* this, int v, int w, TNodeState s, int b1, int b2) { + int result = 0; + int i; + + i = 0; + while ((this->Node[i].State != nsEmpty) && (i < 512)) { + i++; + } + + if (i == 512) { + result = -1; + return result; //exit; + } + + this->Node[i].Value = v & 0xFF; //BYTE(v); + this->Node[i].Weight = w; + this->Node[i].State = s; + this->Node[i].Link[0] = b1; + if (this->Node[i].Link[0] > 511) { + return -1;//? //halt; + } + this->Node[i].Link[1] = b2; + if (this->Node[i].Link[1] > 511) { + return -1;//? //halt; + } + //return entry number + result = i; + return result; +} + +//------------------------------------------------------------------------------ +//reads and expands huffman-encoded data +static int THuff_Read(THuff* this) { + int i; + + i = this->Root; + while (this->Node[i].State != nsLeaf) { + i = this->Node[i].Link[THuff_ReadBit(this)]; + } + + return this->Node[i].Value; +} + +//------------------------------------------------------------------------------ +//creates fork code from tree + +//finds node of lowest weight +static int THuff_MakeHuffTree_SerchMinNode(THuff* this, int* tNode) { + int ii, aaa1, aaa2; + + aaa1 = 0xFFFFFFF; + aaa2 = 0; + for (ii = 0 ; ii < 256; ii++) { + if (tNode[ii] != -1) { + if (this->Node[tNode[ii]].Weight < aaa1) { + aaa2 = ii; + aaa1 = this->Node[tNode[ii]].Weight; + } + } + } + return aaa2; +} + +//finds closest node +static int THuff_MakeHuffTree_SerchNearNode(THuff* this, int* tNode, int pos) { + int ii, aaa1, aaa2; + + aaa1 = 0xFFFFFFF; + aaa2 = 0; + for (ii = 0 ; ii < 256; ii++) { + if (tNode[ii] != -1) { + if ((abs(this->Node[tNode[ii]].Weight - this->Node[tNode[pos]].Weight) < aaa1) && (pos != ii)) { + aaa2 = ii; + aaa1 = this->Node[tNode[ii]].Weight; + } + } + } + return aaa2; +} + +static void THuff_MakeHuffTree_MakeHuffCodeFromTree(THuff* this, uint8_t* tCode1, int* tCodePos, int pos) { + int ii, aaa1; + + if (this->Node[pos].State == nsLeaf) { //found + tCode1[*tCodePos] = 0xFF; + aaa1 = this->Node[pos].Value; + for (ii = 0; ii < 256; ii++) { + this->Code[aaa1][ii] = tCode1[ii]; + } + } + else { //not + if (this->Node[pos].Link[0] != -1) { + tCode1[*tCodePos] = 0; + (*tCodePos)++; + THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, tCodePos, this->Node[pos].Link[0]); + } + + if (this->Node[pos].Link[1] != -1) { + tCode1[*tCodePos] = 1; + (*tCodePos)++; + THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, tCodePos, this->Node[pos].Link[1]); + } + } + + (*tCodePos)--; +} + +// creates huffman tree/codes from apparance rate (0..255) +static void THuff_MakeHuffTree(THuff* this) { + int i, aa1, aa2, aa3; + int tCodePos; + uint8_t tCode1[257]; +#if 0 + uint8_t tCode2[257]; +#endif + int tNode[257]; + + //initializes huffman tree + THuff_InitHuffTree(this); + for (i = 0; i < 256; i++) { + tNode[i] = -1; + tCode1[i] = 0; +#if 0 + tCode2[i] = 0; +#endif + } + + //adds child nodes + comparison target nodes + for (i = 0; i < 256; i++) { + tNode[i] = THuff_InsertHuffNode(this, i, this->Hed.HistGraph[i], nsLeaf, -1, -1); + } + + //creates optimal tree + for (i = 0; i < 256 - 1; i++) { + //find smallest node + aa1 = THuff_MakeHuffTree_SerchMinNode(this, tNode); + //find value closest to smallest node + aa2 = THuff_MakeHuffTree_SerchNearNode(this, tNode, aa1); + //make new node joining both together + aa3 = THuff_InsertHuffNode(this, -1, this->Node[tNode[aa1]].Weight + this->Node[tNode[aa2]].Weight, nsBranch, tNode[aa1], tNode[aa2]); + //remove aa1/2 from comparison target nodes. + tNode[aa1] = -1; + tNode[aa2] = -1; + //add created node to comparison target nodes + tNode[aa1] = aa3; + } + + //finally make added node top of the tree + this->Root = aa3; + + //create stack for data expansion from tree info + tCodePos = 0; + THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, &tCodePos, this->Root); +} + +//------------------------------------------------------------------------------ +//bit IO start process +static void THuff_BeginBitIO(THuff* this) { + this->IoCount = 0; + this->BitBuf = 0; + this->BitCount = 32; +#if 0 + this->BitWriteLen = 0; +#endif + this->CipherBuf = 0; +} + +//------------------------------------------------------------------------------ +//bit IO end process +static void THuff_EndBitIO(THuff* this) { +#if 0 + if (this->IoCount == 2 && this->BitCount > 0) { + this->BitBuf = this->BitBuf ^ this->CipherBuf; + TStream_Write(this->Buff, BitBuf,4); + } +#endif + THuff_BeginBitIO(this); +} + +//------------------------------------------------------------------------------ +//read 1 bit from file +static int THuff_ReadBit(THuff* this) { + int result; + uint32_t aaa; + + if (this->BitCount == 32) { + this->IoCount = 1; //ReadMode + if (this->Buff.Position < this->Buff.Size) { + //read + TStream_Read_Uint32(&this->Buff, &aaa); //Buff.Read(aaa,sizeof(DWORD)); + this->BitBuf = aaa ^ this->CipherBuf; + + //decryption phase + this->CipherBuf = THuff__ROR(this->CipherBuf, aaa & 7); + this->CipherBuf = this->CipherBuf ^ this->CipherList[aaa & 7]; + } + this->BitCount = 0; + } + + //return 1 bit + result = this->BitBuf & 1; + this->BitBuf = this->BitBuf >> 1; + + //advance BitCount + this->BitCount++; + + return result; +} + +//------------------------------------------------------------------------------ +//starts reading encoded data from stream + +static void TStream_Read_THuffHedState(TStream* this, THuffHedState* Hed) { + uint8_t buf[0x410]; + int i; + + read_streamfile(buf, this->Position, sizeof(buf), this->File); + this->Position += sizeof(buf); + + /* 0x00: string size (always 3) */ + memcpy(Hed->HedChar, buf+0x01, 0x03); + Hed->Version = get_u32le(buf+0x04); + for (i = 0; i < 256; i++) { + Hed->HistGraph[i] = get_u32le(buf+0x08 + i*0x04); + } + Hed->FileSize = get_u64le(buf+0x408); /* seems always 0 */ +} + +static void THuff_BeginRead(THuff* this) { + TStream_Read_THuffHedState(&this->Buff, &this->Hed); //Buff.Read(Hed,sizeof(THuffHedState)); + THuff_MakeHuffTree(this); + this->BeginPos = this->Buff.Position; + THuff_BeginBitIO(this); + this->Mode = 1; +} + +#if 0 +//------------------------------------------------------------------------------ +//get file size before encoding +static int64_t THuff_GetFileSize(THuff* this) { + return this->Hed.FileSize; +} + +//------------------------------------------------------------------------------ +//EOF detection +static int THuff_GetEOF(THuff* this) { + if (this->Buff.Position < this->Buff.Size) + return CW_FALSE; + else + return CW_TRUE; +} +#endif +//------------------------------------------------------------------------------ +//return to initial positon +static void THuff_MoveBeginPosition(THuff* this) { + THuff_EndBitIO(this); + this->Buff.Position = this->BeginPos; + THuff_BeginBitIO(this); +} + +//------------------------------------------------------------------------------ +static void THuff_GetPositionData(THuff* this, THuffPositionData* s) { + s->BitBuf = this->BitBuf; + s->BitCount = this->BitCount; + s->StreamPos = this->Buff.Position; + s->CipherBuf = this->CipherBuf; +} + +//------------------------------------------------------------------------------ +static void THuff_SetPositionData(THuff* this, THuffPositionData* s) { + this->BitBuf = s->BitBuf; + this->BitCount = s->BitCount; + this->Buff.Position = s->StreamPos; + this->CipherBuf = s->CipherBuf; +} + +//------------------------------------------------------------------------------ +static void THuff_SetCipherCode(THuff* this, uint32_t msk) { + //creates mask list + this->CipherList[0] = msk / 3; + this->CipherList[1] = msk / 17; + this->CipherList[2] = msk / 7; + this->CipherList[3] = msk / 5; + this->CipherList[4] = msk / 3; + this->CipherList[5] = msk / 11; + this->CipherList[6] = msk / 13; + this->CipherList[7] = msk / 19; +} + +//------------------------------------------------------------------------------ +static uint32_t THuff__ROR(uint32_t src, uint32_t shift) { + uint8_t num = shift % 0xFF; + return ((uint32_t)src >> num) | ((uint32_t)src << (32 - num)); +} + + +//------------------------------------------------------------------- +// CompressWaveLib.pas +//------------------------------------------------------------------- + +#define PW_MAXVOLUME 0xFFFFFFF //don't change + + +//proprietary compression file header +typedef struct { + //RIFF chunk + char HedChar[8]; // 'CmpWave' + uint32_t Channel; // 2(STEREO) / 1(MONO) + uint32_t Sample; // 44100Hz / 22050Hz + uint32_t Bit; // 16bit + int32_t Tbl[256]; // conversion table value + int64_t UnPressSize; // decompressed data size + int64_t LoopStart; // loop start/end position + int64_t LoopEnd; + uint8_t LoopCount; // loop times + char MusicTitle[128*2]; // song name + char MusicArtist[128*2]; // composer +} PRESSWAVEDATAHED; + +//for writting +typedef struct { + short RBuf; + short LBuf; +} TLRWRITEBUFFER; + + +//compression data class +struct TCompressWaveData { + //rendering flag (sets during rendering) + int NowRendering; + //flag for playback + int32_t Faa1; + int32_t Faa2; + int32_t Fvv1; + int32_t Fvv2; + + int32_t FVolume; + int32_t Ffade; + int32_t FSetVolume; + + int FEndLoop; + int32_t FLoop; + int FPlay; + int64_t FWavePosition; + int64_t FWaveLength; + //flag for 22050kHz + int32_t LBackBuf; + int32_t RBackBuf; + //for restoration + THuffPositionData PosData; + int32_t LPFaa1; + int32_t LPFaa2; + int32_t LPFvv1; + int32_t LPFvv2; + //cipher code + uint32_t CipherCode; + //hafu-hafu-hafuman + THuff* RH; + +#if 0 + TMemoryStream Data; +#endif + PRESSWAVEDATAHED Hed; + +}; + +//----------------------------------------------------------- +//create +TCompressWaveData* TCompressWaveData_Create() { + TCompressWaveData* this = malloc(sizeof(TCompressWaveData)); + if (!this) return NULL; +#if 0 + this->Data = NULL; +#endif + this->RH = NULL; + this->FWavePosition = 0; + this->FWaveLength = 0; + this->FVolume = PW_MAXVOLUME; + this->FSetVolume = PW_MAXVOLUME; + this->Ffade = 0; + this->FEndLoop = CW_FALSE; + this->FPlay = CW_FALSE; + this->NowRendering = CW_FALSE; + TCompressWaveData_SetCipherCode(this, 0); + + return this; +} + +//----------------------------------------------------------- +//free +void TCompressWaveData_Free(TCompressWaveData* this) { + if (!this) + return; + + //EXTRA: presumably for threading but OG lib doesn't properly set this to false on all errors +#if 0 + //sync + while (this->NowRendering) { + ; + } +#endif + //free + if (this->RH != NULL) + THuff_Free(this->RH); +#if 0 + if (this->Data != NULL) + TMemoryStream_Free(this->Data); +#endif + free(this); +} + + +//----------------------------------------------------------- +//outpus 44100/16bit/stereo waveform to designed buffer + +void TCompressWaveData_Rendering_ReadPress(TCompressWaveData* this, int32_t* RFlg, int32_t* LFlg) { + if (this->Hed.Channel == 2) { + *RFlg = THuff_Read(this->RH); //STEREO + *LFlg = THuff_Read(this->RH); + } + else { + *RFlg = THuff_Read(this->RH); //MONO + *LFlg = *RFlg; + } +} + +void TCompressWaveData_Rendering_WriteWave(TCompressWaveData* this, int16_t** buf1, int32_t RVol, int32_t LVol) { + TLRWRITEBUFFER bbb = {0}; + + if (this->Hed.Sample == 44100) { //44100 STEREO/MONO + bbb.RBuf = RVol; + bbb.LBuf = LVol; + (*buf1)[0] = bbb.RBuf; + (*buf1)[1] = bbb.LBuf; + (*buf1) += 2; + } + if (this->Hed.Sample == 22050) { //22050 STEREO/MONO + bbb.RBuf = (this->RBackBuf + RVol) / 2; + bbb.LBuf = (this->LBackBuf + LVol) / 2; + (*buf1)[0] = bbb.RBuf; + (*buf1)[1] = bbb.LBuf; + (*buf1) += 2; + + bbb.RBuf = RVol; + bbb.LBuf = LVol; + (*buf1)[0] = bbb.RBuf; + (*buf1)[1] = bbb.LBuf; + (*buf1) += 2; + + this->RBackBuf = RVol; + this->LBackBuf = LVol; + } +} + +int TCompressWaveData_Rendering(TCompressWaveData* this, int16_t* buf, uint32_t Len) { + int result; + int32_t RFlg, LFlg, RVol, LVol; + int i, aaa; + int16_t* buf1; + int32_t PressLength, WaveStep; + + + this->NowRendering = CW_TRUE; + result = CW_FALSE; +#if 0 + if (this->Data == NULL) { + this->NowRendering = CW_FALSE; + return result; //exit; + } +#endif + + //fadeout song stop + if ((this->FVolume < 1) && (this->FSetVolume < 1)) { + this->FPlay = CW_FALSE; + } + //if (abs(this->FSetVolume - this->FVolume) < this->Ffade) { + // this->FPlay = CW_FALSE; + //} + + //stop if FPlay (play flag) wasn't set + if (this->FPlay == CW_FALSE) { + this->NowRendering = CW_FALSE; + return result; //exit; + } + + //pre processing + RVol = this->Fvv1; + LVol = this->Fvv2; + if (this->Hed.Sample == 44100) + WaveStep = 4; + else + WaveStep = 8; + + PressLength = (int32_t)Len / WaveStep; + + //expansion processing + buf1 = buf; + for (i = 0; i < PressLength; i++) { + + //crossed over? + if (this->FWavePosition > this->FWaveLength) { + if (this->FEndLoop == CW_TRUE) { //playback with loop? + TCompressWaveData_Previous(this); + } + else { //in case of playback without loop + this->FPlay = CW_FALSE; + return result; //exit + } + } + + //loop related + if (this->Hed.LoopCount > this->FLoop) { + //if position is loop start, hold current flag/state + //shr 3 matches 8 bit aligment + if ((this->Hed.LoopStart >> 3) == (this->FWavePosition >> 3)) { + TCompressWaveData_GetLoopState(this); + } + //if reached loop end do loop. + if ((this->Hed.LoopEnd >> 3) == (this->FWavePosition >> 3)) { + if (this->Hed.LoopCount != 255) + this->FLoop++; + TCompressWaveData_SetLoopState(this); + } + } + + //read + TCompressWaveData_Rendering_ReadPress(this, &RFlg, &LFlg); + this->Faa1 = this->Faa1 + this->Hed.Tbl[RFlg]; + this->Faa2 = this->Faa2 + this->Hed.Tbl[LFlg]; + this->Fvv1 = this->Fvv1 + this->Faa1; + this->Fvv2 = this->Fvv2 + this->Faa2; + + //volume adjustment + aaa = this->FSetVolume - this->FVolume; + if (abs(aaa) < this->Ffade) { + this->FVolume = this->FSetVolume; + } + else { + if (aaa > 0) + this->FVolume = this->FVolume + this->Ffade; + else + this->FVolume = this->FVolume - this->Ffade; + } + + //threshold calcs (due to overflow) + if (this->Fvv1 > +32760) { + this->Fvv1 = +32760; + this->Faa1 = 0; + } + if (this->Fvv1 < -32760) { + this->Fvv1 = -32760; + this->Faa1 = 0; + } + if (this->Fvv2 > +32760) { + this->Fvv2 = +32760; + this->Faa2 = 0; + } + if (this->Fvv2 < -32760) { + this->Fvv2 = -32760; + this->Faa2 = 0; + } + + aaa = (this->FVolume >> 20); + RVol = this->Fvv1 * aaa / 256; + LVol = this->Fvv2 * aaa / 256; + + //expand to buffer + TCompressWaveData_Rendering_WriteWave(this, &buf1, RVol, LVol); + //advance playback position + this->FWavePosition += WaveStep; + } + + //remainder calcs + //depending on buffer lenght remainder may happen + //example: 44100 / 4 = 11025...OK 44100 / 8 = 5512.5...NG + // in that case appear as noise + if (Len % 8 == 4) { + TCompressWaveData_Rendering_WriteWave(this, &buf1, RVol, LVol); + } + + this->NowRendering = CW_FALSE; + result = CW_TRUE; + return result; +} + + +//----------------------------------------------------------- +//read compressed file from stream + +static void TStream_Read_PRESSWAVEDATAHED(TStream* this, PRESSWAVEDATAHED* Hed) { + uint8_t buf[0x538]; + int i, len; + + read_streamfile(buf, this->Position, sizeof(buf), this->File); + this->Position += sizeof(buf); + + memcpy(Hed->HedChar, buf + 0x00, 8); + Hed->Channel = get_u32le(buf + 0x08); + Hed->Sample = get_u32le(buf + 0x0c); + Hed->Bit = get_u32le(buf + 0x10); + for (i = 0; i < 256; i++) { + Hed->Tbl[i] = get_s32le(buf + 0x14 + i * 0x04); + } + Hed->UnPressSize = get_u64le(buf + 0x418); + Hed->LoopStart = get_u64le(buf + 0x420); + Hed->LoopEnd = get_u64le(buf + 0x428); + Hed->LoopCount = get_u8 (buf + 0x430); + + len = get_u8 (buf + 0x431); + memcpy(Hed->MusicTitle, buf + 0x432, len); + len = get_u8 (buf + 0x4B1); + memcpy(Hed->MusicArtist, buf + 0x4B2, len); + + /* 0x538: huffman table */ + /* 0x948: data start */ +} + +int TCompressWaveData_LoadFromStream(TCompressWaveData* this, STREAMFILE* ss) { + int result = CW_FALSE; + TStream data = {0}; + + if (ss == NULL) + return result; +#if 0 + if (this->Data != NULL) + TMemoryStream_Free(this->Data); +#endif + + data.File = ss; //data = TMemoryStream.Create; + data.Size = get_streamfile_size(ss); //data.SetSize(ss.Size); + //data.CopyFrom(ss,0); + + //get header info + data.Position = 0; + + TStream_Read_PRESSWAVEDATAHED(&data, &this->Hed); //data.Read(Hed,sizeof(PRESSWAVEDATAHED)); + this->FWaveLength = this->Hed.UnPressSize; + if (this->RH != NULL) + THuff_Free(this->RH); + this->RH = THuff_Create(&data); + if (!this->RH) return result; + + THuff_SetCipherCode(this->RH, 0x00); + THuff_BeginRead(this->RH); + + //initialize playback flag + TCompressWaveData_Stop(this); + TCompressWaveData_SetVolume(this, 1.0, 0.0); + result = CW_TRUE; + return result; +} + +//------------------------------------------------------------------------------ +//temp pause +void TCompressWaveData_Pause(TCompressWaveData* this) { + this->FPlay = CW_FALSE; +} + +//----------------------------------------------------------- +//sets volume +void TCompressWaveData_SetVolume(TCompressWaveData* this, float vol, float fade) { + float aaa; + + //EXTRA: C float seemingly can't store PW_MAXVOLUME (268435455 becomes 268435456.0), so must cast to double + // to get proper results. Otherwise volume gets slightly different vs original (no casting needed there). + // vol=1.0, fade=0.0 is the same as default params. + + aaa = vol; + //set volume threshold + if (aaa > 1.0) aaa = 1.0; + if (aaa < 0.0) aaa = 0.0; + //calc volume increse + if (fade < 0.01) { //with fade value + this->Ffade = 0; + this->FVolume = round(aaa * (double)PW_MAXVOLUME); + this->FSetVolume = this->FVolume; + } + else { //without fade value + this->Ffade = round(PW_MAXVOLUME / fade / 44100); + this->FSetVolume = round(aaa * PW_MAXVOLUME); + } +} + +//----------------------------------------------------------- +//returns fade value +float TCompressWaveData_GetFade(TCompressWaveData* this) { + if ((this->Ffade == 0) || (abs(this->FVolume - this->FSetVolume) == 0)) { + return 0; //exit; + } + return (abs(this->FVolume - this->FSetVolume)/44100) / this->Ffade; +} + +//----------------------------------------------------------- +//returns volume value +float TCompressWaveData_GetVolume(TCompressWaveData* this) { + return this->FVolume / PW_MAXVOLUME; +} + +//------------------------------------------------------------------------------ +//returns volume after fade +float TCompressWaveData_GetSetVolume(TCompressWaveData* this) { + return this->FSetVolume / PW_MAXVOLUME; +} + +//------------------------------------------------------------------------------ +//returns play time (current position). unit is secs +float TCompressWaveData_GetPlayTime(TCompressWaveData* this) { + return this->FWavePosition / (44100*4); +} + +//----------------------------------------------------------- +//returns song length. unit is secs +float TCompressWaveData_GetTotalTime(TCompressWaveData* this) { + return this->FWaveLength / (44100*4); +} + +//----------------------------------------------------------- +//play stop command. returns song to beginning +void TCompressWaveData_Stop(TCompressWaveData* this) { + //play flags to initial state + this->FWavePosition = 0; + this->Fvv1 = 0; + this->Faa1 = 0; + this->Fvv2 = 0; + this->Faa2 = 0; + this->LBackBuf = 0; + this->RBackBuf = 0; + TCompressWaveData_SetVolume(this, 1.0, 0); + this->FPlay = CW_FALSE; + this->FLoop = 0; +#if 0 + if (this->Data == NULL) + return; +#endif + + //EXTRA: presumably for threading but OG lib doesn't properly set this to false on all errors +#if 0 + //sync + while (this->NowRendering) { + ; + } +#endif + + //return to initial location + THuff_MoveBeginPosition(this->RH); +} + +//----------------------------------------------------------- +//returns song to beginning. difference vs STOP is that fade isn't initialized +void TCompressWaveData_Previous(TCompressWaveData* this) { + //play flags to initial state + this->FWavePosition = 0; + this->Fvv1 = 0; + this->Faa1 = 0; + this->Fvv2 = 0; + this->Faa2 = 0; + this->LBackBuf = 0; + this->RBackBuf = 0; + this->FLoop = 0; + +#if 0 + if (this->Data == NULL) + return; +#endif + //return to initial location + THuff_MoveBeginPosition(this->RH); +} + +//------------------------------------------------------------ +//starts song playback +void TCompressWaveData_Play(TCompressWaveData* this, int loop) { + this->FPlay = CW_TRUE; + this->FEndLoop = loop; + if ((this->FVolume == 0) && (this->FSetVolume == 0)) + TCompressWaveData_SetVolume(this, 1.0,0); +} + +//------------------------------------------------------------ +//set parameters for looping +//-------------------------------------------------------------- +//record encoded file position +//since it uses huffman needs to held those flags too +void TCompressWaveData_GetLoopState(TCompressWaveData* this) { + this->LPFaa1 = this->Faa1; + this->LPFaa2 = this->Faa2; + this->LPFvv1 = this->Fvv1; + this->LPFvv2 = this->Fvv2; + THuff_GetPositionData(this->RH, &this->PosData); +} + +//-------------------------------------------------------------- +//return to recorded encoded file position +void TCompressWaveData_SetLoopState(TCompressWaveData* this) { + this->Faa1 = this->LPFaa1; + this->Faa2 = this->LPFaa2; + this->Fvv1 = this->LPFvv1; + this->Fvv2 = this->LPFvv2; + THuff_SetPositionData(this->RH, &this->PosData); + this->FWavePosition = this->Hed.LoopStart; +} + +//----------------------------------------------------------- +//sets cipher code +void TCompressWaveData_SetCipherCode(TCompressWaveData* this, uint32_t Num) { + this->CipherCode = Num; +} + diff --git a/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder_lib.h b/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder_lib.h new file mode 100644 index 000000000..c2e438c78 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/compresswave_decoder_lib.h @@ -0,0 +1,27 @@ +#ifndef _COMPRESSWAVE_DECODER_LIB_H +#define _COMPRESSWAVE_DECODER_LIB_H +#include "../streamfile.h" + +typedef struct TCompressWaveData TCompressWaveData; + +void TCompressWaveData_GetLoopState(TCompressWaveData* this); +void TCompressWaveData_SetLoopState(TCompressWaveData* this); + +TCompressWaveData* TCompressWaveData_Create(); +void TCompressWaveData_Free(TCompressWaveData* this); +int TCompressWaveData_Rendering(TCompressWaveData* this, int16_t* buf, uint32_t Len); +int TCompressWaveData_LoadFromStream(TCompressWaveData* this, STREAMFILE* ss); +void TCompressWaveData_SetCipherCode(TCompressWaveData* this, uint32_t Num); + +void TCompressWaveData_Play(TCompressWaveData* this, int loop); +void TCompressWaveData_Stop(TCompressWaveData* this); +void TCompressWaveData_Previous(TCompressWaveData* this); +void TCompressWaveData_Pause(TCompressWaveData* this); +void TCompressWaveData_SetVolume(TCompressWaveData* this, float vol, float fade); +float TCompressWaveData_GetVolume(TCompressWaveData* this); +float TCompressWaveData_GetSetVolume(TCompressWaveData* this); +float TCompressWaveData_GetFade(TCompressWaveData* this); +float TCompressWaveData_GetPlayTime(TCompressWaveData* this); +float TCompressWaveData_GetTotalTime(TCompressWaveData* this); + +#endif /*_COMPRESSWAVE_DECODER_LIB_H */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c index 23ecbc68b..26d76b9e0 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c @@ -1,206 +1,205 @@ -#include "coding.h" - -#include "ea_mt_decoder_utk.h" - -/* Decodes EA MicroTalk (speech codec) using utkencode lib (slightly modified for vgmstream). - * EA separates MT10:1 and MT5:1 (bigger frames), but apparently are the same - * with different encoding parameters. Later revisions may have PCM blocks (rare). - * - * Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode - * Info: http://wiki.niotso.org/UTK - */ - - -//#define UTK_MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24)) -#define UTK_ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f)) -#define UTK_MIN(x,y) ((x)<(y)?(x):(y)) -#define UTK_MAX(x,y) ((x)>(y)?(x):(y)) -#define UTK_CLAMP(x,min,max) UTK_MIN(UTK_MAX(x,min),max) - -#define UTK_BUFFER_SIZE 0x1000 - -struct ea_mt_codec_data { - STREAMFILE *streamfile; - uint8_t buffer[UTK_BUFFER_SIZE]; - off_t offset; - off_t loop_offset; - int loop_sample; - - int pcm_blocks; - int samples_filled; - int samples_used; - int samples_done; - int samples_discard; - void* utk_context; -}; - -static size_t ea_mt_read_callback(void *dest, int size, void *arg); - -ea_mt_codec_data *init_ea_mt(int channels, int pcm_blocks) { - return init_ea_mt_loops(channels, pcm_blocks, 0, NULL); -} - -ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) { - ea_mt_codec_data *data = NULL; - int i; - - data = calloc(channels, sizeof(ea_mt_codec_data)); /* one decoder per channel */ - if (!data) goto fail; - - for (i = 0; i < channels; i++) { - data[i].utk_context = calloc(1, sizeof(UTKContext)); - if (!data[i].utk_context) goto fail; - utk_init(data[i].utk_context); - - data[i].pcm_blocks = pcm_blocks; - data[i].loop_sample = loop_sample; - if (loop_offsets) - data[i].loop_offset = loop_offsets[i]; - - utk_set_callback(data[i].utk_context, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback); - } - - return data; - -fail: - free_ea_mt(data, channels); - return NULL; -} - -void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) { - int i; - ea_mt_codec_data *data = vgmstream->codec_data; - ea_mt_codec_data *ch_data = &data[channel]; - UTKContext* ctx = ch_data->utk_context; - int samples_done = 0; - - - while (samples_done < samples_to_do) { - - if (ch_data->samples_filled) { - /* consume current frame */ - int samples_to_get = ch_data->samples_filled; - - /* don't go past loop, to reset decoder */ - if (ch_data->loop_sample > 0 && ch_data->samples_done < ch_data->loop_sample && - ch_data->samples_done + samples_to_get > ch_data->loop_sample) - samples_to_get = ch_data->loop_sample - ch_data->samples_done; - - if (ch_data->samples_discard) { - /* discard samples for looping */ - if (samples_to_get > ch_data->samples_discard) - samples_to_get = ch_data->samples_discard; - ch_data->samples_discard -= samples_to_get; - } - else { - /* get max samples and copy */ - if (samples_to_get > samples_to_do - samples_done) - samples_to_get = samples_to_do - samples_done; - - for (i = ch_data->samples_used; i < ch_data->samples_used + samples_to_get; i++) { - int pcm = UTK_ROUND(ctx->decompressed_frame[i]); - outbuf[0] = (int16_t)UTK_CLAMP(pcm, -32768, 32767); - outbuf += channelspacing; - } - - samples_done += samples_to_get; - } - - /* mark consumed samples */ - ch_data->samples_used += samples_to_get; - ch_data->samples_filled -= samples_to_get; - ch_data->samples_done += samples_to_get; - - /* Loops in EA-MT are done with fully separate intro/loop substreams. We must - * notify the decoder when a new substream begins (even with looping disabled). */ - if (ch_data->loop_sample > 0 && ch_data->samples_done == ch_data->loop_sample) { - ch_data->samples_filled = 0; - ch_data->samples_discard = 0; - - /* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */ - ch_data->offset = ch_data->loop_offset; - utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */ - utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */ - } - } - else { - /* new frame */ - if (ch_data->pcm_blocks) - utk_rev3_decode_frame(ctx); - else - utk_decode_frame(ctx); - - ch_data->samples_used = 0; - ch_data->samples_filled = 432; - } - } -} - -static void flush_ea_mt_offsets(VGMSTREAM *vgmstream, int is_start, int samples_discard) { - ea_mt_codec_data *data = vgmstream->codec_data; - int i; - - if (!data) return; - - - /* EA-MT frames are VBR (not byte-aligned?), so utk_decoder reads new buffer data automatically. - * When decoding starts or a SCHl block changes, flush_ea_mt must be called to reset the state. - * A bit hacky but would need some restructuring otherwise. */ - - for (i = 0; i < vgmstream->channels; i++) { - UTKContext* ctx = data[i].utk_context; - - data[i].streamfile = vgmstream->ch[i].streamfile; /* maybe should keep its own STREAMFILE? */ - if (is_start) - data[i].offset = vgmstream->ch[i].channel_start_offset; - else - data[i].offset = vgmstream->ch[i].offset; - utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */ - - if (is_start) { - utk_reset(ctx); - ctx->parsed_header = 0; - data[i].samples_done = 0; - } - - data[i].samples_filled = 0; - data[i].samples_discard = samples_discard; - } -} - -void flush_ea_mt(VGMSTREAM *vgmstream) { - flush_ea_mt_offsets(vgmstream, 0, 0); -} - -void reset_ea_mt(VGMSTREAM *vgmstream) { - flush_ea_mt_offsets(vgmstream, 1, 0); -} - -void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample) { - flush_ea_mt_offsets(vgmstream, 1, num_sample); -} - -void free_ea_mt(ea_mt_codec_data *data, int channels) { - int i; - - if (!data) - return; - - for (i = 0; i < channels; i++) { - free(data[i].utk_context); - } - free(data); -} - -/* ********************** */ - -static size_t ea_mt_read_callback(void *dest, int size, void *arg) { - ea_mt_codec_data *ch_data = arg; - int bytes_read; - - bytes_read = read_streamfile(dest,ch_data->offset,size,ch_data->streamfile); - ch_data->offset += bytes_read; - - return bytes_read; - -} +#include "coding.h" + +#include "ea_mt_decoder_utk.h" + +/* Decodes EA MicroTalk (speech codec) using utkencode lib (slightly modified for vgmstream). + * EA separates MT10:1 and MT5:1 (bigger frames), but apparently are the same + * with different encoding parameters. Later revisions may have PCM blocks (rare). + * + * Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode + * Info: http://wiki.niotso.org/UTK + */ + + +//#define UTK_MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24)) +#define UTK_ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f)) +#define UTK_MIN(x,y) ((x)<(y)?(x):(y)) +#define UTK_MAX(x,y) ((x)>(y)?(x):(y)) +#define UTK_CLAMP(x,min,max) UTK_MIN(UTK_MAX(x,min),max) + +#define UTK_BUFFER_SIZE 0x1000 + +struct ea_mt_codec_data { + STREAMFILE *streamfile; + uint8_t buffer[UTK_BUFFER_SIZE]; + off_t offset; + off_t loop_offset; + int loop_sample; + + int pcm_blocks; + int samples_filled; + int samples_used; + int samples_done; + int samples_discard; + void* utk_context; +}; + +static size_t ea_mt_read_callback(void *dest, int size, void *arg); + +ea_mt_codec_data* init_ea_mt(int channels, int pcm_blocks) { + return init_ea_mt_loops(channels, pcm_blocks, 0, NULL); +} + +ea_mt_codec_data* init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) { + ea_mt_codec_data* data = NULL; + int i; + + data = calloc(channels, sizeof(ea_mt_codec_data)); /* one decoder per channel */ + if (!data) goto fail; + + for (i = 0; i < channels; i++) { + data[i].utk_context = calloc(1, sizeof(UTKContext)); + if (!data[i].utk_context) goto fail; + utk_init(data[i].utk_context); + + data[i].pcm_blocks = pcm_blocks; + data[i].loop_sample = loop_sample; + if (loop_offsets) + data[i].loop_offset = loop_offsets[i]; + + utk_set_callback(data[i].utk_context, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback); + } + + return data; + +fail: + free_ea_mt(data, channels); + return NULL; +} + +void decode_ea_mt(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel) { + int i; + ea_mt_codec_data* data = vgmstream->codec_data; + ea_mt_codec_data* ch_data = &data[channel]; + UTKContext* ctx = ch_data->utk_context; + int samples_done = 0; + + + while (samples_done < samples_to_do) { + + if (ch_data->samples_filled) { + /* consume current frame */ + int samples_to_get = ch_data->samples_filled; + + /* don't go past loop, to reset decoder */ + if (ch_data->loop_sample > 0 && ch_data->samples_done < ch_data->loop_sample && + ch_data->samples_done + samples_to_get > ch_data->loop_sample) + samples_to_get = ch_data->loop_sample - ch_data->samples_done; + + if (ch_data->samples_discard) { + /* discard samples for looping */ + if (samples_to_get > ch_data->samples_discard) + samples_to_get = ch_data->samples_discard; + ch_data->samples_discard -= samples_to_get; + } + else { + /* get max samples and copy */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + + for (i = ch_data->samples_used; i < ch_data->samples_used + samples_to_get; i++) { + int pcm = UTK_ROUND(ctx->decompressed_frame[i]); + outbuf[0] = (int16_t)UTK_CLAMP(pcm, -32768, 32767); + outbuf += channelspacing; + } + + samples_done += samples_to_get; + } + + /* mark consumed samples */ + ch_data->samples_used += samples_to_get; + ch_data->samples_filled -= samples_to_get; + ch_data->samples_done += samples_to_get; + + /* Loops in EA-MT are done with fully separate intro/loop substreams. We must + * notify the decoder when a new substream begins (even with looping disabled). */ + if (ch_data->loop_sample > 0 && ch_data->samples_done == ch_data->loop_sample) { + ch_data->samples_filled = 0; + ch_data->samples_discard = 0; + + /* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */ + ch_data->offset = ch_data->loop_offset; + utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */ + utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */ + } + } + else { + /* new frame */ + if (ch_data->pcm_blocks) + utk_rev3_decode_frame(ctx); + else + utk_decode_frame(ctx); + + ch_data->samples_used = 0; + ch_data->samples_filled = 432; + } + } +} + +static void flush_ea_mt_offsets(VGMSTREAM* vgmstream, int is_start, int samples_discard) { + ea_mt_codec_data* data = vgmstream->codec_data; + int i; + + if (!data) return; + + + /* EA-MT frames are VBR (not byte-aligned?), so utk_decoder reads new buffer data automatically. + * When decoding starts or a SCHl block changes, flush_ea_mt must be called to reset the state. + * A bit hacky but would need some restructuring otherwise. */ + + for (i = 0; i < vgmstream->channels; i++) { + UTKContext* ctx = data[i].utk_context; + + data[i].streamfile = vgmstream->ch[i].streamfile; /* maybe should keep its own STREAMFILE? */ + if (is_start) + data[i].offset = vgmstream->ch[i].channel_start_offset; + else + data[i].offset = vgmstream->ch[i].offset; + utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */ + + if (is_start) { + utk_reset(ctx); + ctx->parsed_header = 0; + data[i].samples_done = 0; + } + + data[i].samples_filled = 0; + data[i].samples_discard = samples_discard; + } +} + +void flush_ea_mt(VGMSTREAM* vgmstream) { + flush_ea_mt_offsets(vgmstream, 0, 0); +} + +void reset_ea_mt(VGMSTREAM* vgmstream) { + flush_ea_mt_offsets(vgmstream, 1, 0); +} + +void seek_ea_mt(VGMSTREAM* vgmstream, int32_t num_sample) { + flush_ea_mt_offsets(vgmstream, 1, num_sample); +} + +void free_ea_mt(ea_mt_codec_data* data, int channels) { + int i; + + if (!data) + return; + + for (i = 0; i < channels; i++) { + free(data[i].utk_context); + } + free(data); +} + +/* ********************** */ + +static size_t ea_mt_read_callback(void *dest, int size, void *arg) { + ea_mt_codec_data *ch_data = arg; + int bytes_read; + + bytes_read = read_streamfile(dest,ch_data->offset,size,ch_data->streamfile); + ch_data->offset += bytes_read; + + return bytes_read; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c index d1e65f10b..f09cdc14e 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c @@ -1,279 +1,279 @@ -#include "coding.h" -#include "../util.h" - -/* AKA "EA ADPCM", evolved from CDXA. Inconsistently called EA XA/EA-XA/EAXA. - * Some variations contain ADPCM hist header per block, but it's handled in ea_block.c */ - -/* - * Another way to get coefs in EAXA v2, with no diffs (no idea which table is actually used in games): - * coef1 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 0]; - * coef2 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 1]; - */ -/* -static const int32_t EA_XA_TABLE2[28] = { - 0, 0, 240, 0, - 460, -208, 392, -220, - 0, 0, 240, 0, - 460, 0, 392, 0, - 0, 0, 0, 0, - -208, -1, -220, -1, - 0, 0, 0, 0x3F70 -}; -*/ - -static const int EA_XA_TABLE[20] = { - 0, 240, 460, 392, - 0, 0, -208, -220, - 0, 1, 3, 4, - 7, 8, 10, 11, - 0, -1, -3, -4 -}; - -/* EA XA v2 (always mono); like v1 but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */ -void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - uint8_t frame_info; - int32_t coef1, coef2; - int i, sample_count, shift; - - int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02; - int xa_frame_size = 0x0f; - int frame_samples = 28; - first_sample = first_sample % frame_samples; - - /* header */ - frame_info = read_8bit(stream->offset,stream->streamfile); - - if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */ - stream->adpcm_history1_32 = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile); - stream->adpcm_history2_32 = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile); - - for (i=first_sample,sample_count=0; ioffset + 0x01 + 2*0x02 + i*0x02,stream->streamfile); - } - - /* only increment offset on complete frame */ - if (i == frame_samples) - stream->offset += pcm_frame_size; - } - else { /* ADPCM frame */ - coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; - coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; - shift = (frame_info & 0x0F) + 8; - - for (i=first_sample,sample_count=0; ioffset + 0x01 + i/2); - int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */ - - sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); - sample_nibble = (sample_byte >> nibble_shift) & 0x0F; - new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ - new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8; - new_sample = clamp16(new_sample); - - outbuf[sample_count] = new_sample; - stream->adpcm_history2_32 = stream->adpcm_history1_32; - stream->adpcm_history1_32 = new_sample; - } - - /* only increment offset on complete frame */ - if (i == frame_samples) - stream->offset += xa_frame_size; - } -} - -#if 0 -/* later PC games use float math, though in the end sounds basically the same (decompiled from various exes) */ -static const double XA_K0[16] = { 0.0, 0.9375, 1.796875, 1.53125 }; -static const double XA_K1[16] = { 0.0, 0.0, -0.8125, -0.859375 }; -/* code uses look-up table but it's be equivalent to: - * (double)((nibble << 28) >> (shift + 8) >> 8) or (double)(signed_nibble << (12 - shift)) */ -static const uint32_t FLOAT_TABLE_INT[256] = { - 0x00000000,0x45800000,0x46000000,0x46400000,0x46800000,0x46A00000,0x46C00000,0x46E00000, - 0xC7000000,0xC6E00000,0xC6C00000,0xC6A00000,0xC6800000,0xC6400000,0xC6000000,0xC5800000, - 0x00000000,0x45000000,0x45800000,0x45C00000,0x46000000,0x46200000,0x46400000,0x46600000, - 0xC6800000,0xC6600000,0xC6400000,0xC6200000,0xC6000000,0xC5C00000,0xC5800000,0xC5000000, - 0x00000000,0x44800000,0x45000000,0x45400000,0x45800000,0x45A00000,0x45C00000,0x45E00000, - 0xC6000000,0xC5E00000,0xC5C00000,0xC5A00000,0xC5800000,0xC5400000,0xC5000000,0xC4800000, - 0x00000000,0x44000000,0x44800000,0x44C00000,0x45000000,0x45200000,0x45400000,0x45600000, - 0xC5800000,0xC5600000,0xC5400000,0xC5200000,0xC5000000,0xC4C00000,0xC4800000,0xC4000000, - 0x00000000,0x43800000,0x44000000,0x44400000,0x44800000,0x44A00000,0x44C00000,0x44E00000, - 0xC5000000,0xC4E00000,0xC4C00000,0xC4A00000,0xC4800000,0xC4400000,0xC4000000,0xC3800000, - 0x00000000,0x43000000,0x43800000,0x43C00000,0x44000000,0x44200000,0x44400000,0x44600000, - 0xC4800000,0xC4600000,0xC4400000,0xC4200000,0xC4000000,0xC3C00000,0xC3800000,0xC3000000, - 0x00000000,0x42800000,0x43000000,0x43400000,0x43800000,0x43A00000,0x43C00000,0x43E00000, - 0xC4000000,0xC3E00000,0xC3C00000,0xC3A00000,0xC3800000,0xC3400000,0xC3000000,0xC2800000, - 0x00000000,0x42000000,0x42800000,0x42C00000,0x43000000,0x43200000,0x43400000,0x43600000, - 0xC3800000,0xC3600000,0xC3400000,0xC3200000,0xC3000000,0xC2C00000,0xC2800000,0xC2000000, - 0x00000000,0x41800000,0x42000000,0x42400000,0x42800000,0x42A00000,0x42C00000,0x42E00000, - 0xC3000000,0xC2E00000,0xC2C00000,0xC2A00000,0xC2800000,0xC2400000,0xC2000000,0xC1800000, - 0x00000000,0x41000000,0x41800000,0x41C00000,0x42000000,0x42200000,0x42400000,0x42600000, - 0xC2800000,0xC2600000,0xC2400000,0xC2200000,0xC2000000,0xC1C00000,0xC1800000,0xC1000000, - 0x00000000,0x40800000,0x41000000,0x41400000,0x41800000,0x41A00000,0x41C00000,0x41E00000, - 0xC2000000,0xC1E00000,0xC1C00000,0xC1A00000,0xC1800000,0xC1400000,0xC1000000,0xC0800000, - 0x00000000,0x40000000,0x40800000,0x40C00000,0x41000000,0x41200000,0x41400000,0x41600000, - 0xC1800000,0xC1600000,0xC1400000,0xC1200000,0xC1000000,0xC0C00000,0xC0800000,0xC0000000, - 0x00000000,0x3F800000,0x40000000,0x40400000,0x40800000,0x40A00000,0x40C00000,0x40E00000, - 0xC1000000,0xC0E00000,0xC0C00000,0xC0A00000,0xC0800000,0xC0400000,0xC0000000,0xBF800000, - 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, -}; -static const float *FLOAT_TABLE = (const float *)FLOAT_TABLE_INT; - -void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - uint8_t frame_info; - int i, sample_count, shift; - - int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02; - int xa_frame_size = 0x0f; - int frame_samples = 28; - first_sample = first_sample % frame_samples; - - /* header */ - frame_info = read_8bit(stream->offset,stream->streamfile); - - if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */ - stream->adpcm_history1_double = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile); - stream->adpcm_history2_double = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile); - - for (i=first_sample,sample_count=0; ioffset + 0x01 + 2*0x02 + i*0x02,stream->streamfile); - } - - /* only increment offset on complete frame */ - if (i == frame_samples) - stream->offset += pcm_frame_size; - } - else { /* ADPCM frame */ - double coef1, coef2, hist1, hist2, new_sample; - - coef1 = XA_K0[(frame_info >> 4)]; - coef2 = XA_K1[(frame_info >> 4)]; - shift = (frame_info & 0x0F) + 8;// << 4; - hist1 = stream->adpcm_history1_double; - hist2 = stream->adpcm_history2_double; - - for (i=first_sample,sample_count=0; ioffset + 0x01 + i/2); - int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */ - - sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); - sample_nibble = (sample_byte >> nibble_shift) & 0x0F; - new_sample = (double)FLOAT_TABLE[sample_nibble + shift]; - new_sample = new_sample + coef1 * hist1 + coef2 * hist2; - - outbuf[sample_count] = clamp16((int)new_sample); - hist2 = hist1; - hist1 = new_sample; - } - - stream->adpcm_history1_double = hist1; - stream->adpcm_history2_double = hist2; - - /* only increment offset on complete frame */ - if (i == frame_samples) - stream->offset += xa_frame_size; - } -} -#endif - -/* EA XA v1 (mono/stereo) */ -void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { - uint8_t frame_info; - int32_t coef1, coef2; - int i, sample_count, shift; - int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */ - - int frame_size = is_stereo ? 0x0f*2 : 0x0f; - int frame_samples = 28; - first_sample = first_sample % frame_samples; - - /* header */ - if (is_stereo) { - /* coefs ch0+ch1 + shift ch0+ch1 */ - frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); - coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0]; - coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4]; - - frame_info = read_8bit(stream->offset + 0x01, stream->streamfile); - shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8; - } else { - /* coefs + shift ch0 */ - frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); - coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; - coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; - shift = (frame_info & 0x0F) + 8; - } - - /* samples */ - for (i=first_sample,sample_count=0; ioffset + 0x02 + i) : (stream->offset + 0x01 + i/2); - int nibble_shift = is_stereo ? (hn ? 4 : 0) : ((!(i & 1)) ? 4 : 0); /* high nibble first */ - - sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); - sample_nibble = (sample_byte >> nibble_shift) & 0x0F; - new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ - new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; - new_sample = clamp16(new_sample); - - outbuf[sample_count] = new_sample; - stream->adpcm_history2_32 = stream->adpcm_history1_32; - stream->adpcm_history1_32 = new_sample; - } - - /* only increment offset on complete frame */ - if (i == frame_samples) - stream->offset += frame_size; -} - -/* Maxis EA-XA v1 (mono/stereo) with byte-interleave layout in stereo mode */ -void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - uint8_t frame_info; - int32_t coef1, coef2; - int i, sample_count, shift; - - int frame_size = 0x0f * channelspacing; /* varies in mono/stereo */ - int frame_samples = 28; - first_sample = first_sample % frame_samples; - - /* header (coefs+shift ch0 + coefs+shift ch1) */ - frame_info = read_8bit(stream->offset + channel,stream->streamfile); - coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; - coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; - shift = (frame_info & 0x0F) + 8; - - /* samples */ - for (i=first_sample,sample_count=0; ioffset + 0x01*channelspacing + (channelspacing == 2 ? i/2 + channel + (i/2)*0x01 : i/2)); - int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */ - - sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); - sample_nibble = (sample_byte >> nibble_shift) & 0x0F; - new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ - new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; - new_sample = clamp16(new_sample); - - outbuf[sample_count] = new_sample; - stream->adpcm_history2_32 = stream->adpcm_history1_32; - stream->adpcm_history1_32 = new_sample; - } - - /* only increment offset on complete frame */ - if (i == frame_samples) - stream->offset += frame_size; -} - -int32_t ea_xa_bytes_to_samples(size_t bytes, int channels) { - if (channels <= 0) return 0; - return bytes / channels / 0x0f * 28; -} +#include "coding.h" +#include "../util.h" + +/* AKA "EA ADPCM", evolved from CDXA. Inconsistently called EA XA/EA-XA/EAXA. + * Some variations contain ADPCM hist header per block, but it's handled in ea_block.c */ + +/* + * Another way to get coefs in EAXA v2, with no diffs (no idea which table is actually used in games): + * coef1 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 0]; + * coef2 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 1]; + */ +/* +static const int32_t EA_XA_TABLE2[28] = { + 0, 0, 240, 0, + 460, -208, 392, -220, + 0, 0, 240, 0, + 460, 0, 392, 0, + 0, 0, 0, 0, + -208, -1, -220, -1, + 0, 0, 0, 0x3F70 +}; +*/ + +static const int EA_XA_TABLE[20] = { + 0, 240, 460, 392, + 0, 0, -208, -220, + 0, 1, 3, 4, + 7, 8, 10, 11, + 0, -1, -3, -4 +}; + +/* EA XA v2 (always mono); like v1 but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */ +void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame_info; + int32_t coef1, coef2; + int i, sample_count, shift; + + int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02; + int xa_frame_size = 0x0f; + int frame_samples = 28; + first_sample = first_sample % frame_samples; + + /* header */ + frame_info = read_8bit(stream->offset,stream->streamfile); + + if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */ + stream->adpcm_history1_32 = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile); + stream->adpcm_history2_32 = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile); + + for (i=first_sample,sample_count=0; ioffset + 0x01 + 2*0x02 + i*0x02,stream->streamfile); + } + + /* only increment offset on complete frame */ + if (i == frame_samples) + stream->offset += pcm_frame_size; + } + else { /* ADPCM frame */ + coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; + coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; + shift = (frame_info & 0x0F) + 8; + + for (i=first_sample,sample_count=0; ioffset + 0x01 + i/2); + int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */ + + sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); + sample_nibble = (sample_byte >> nibble_shift) & 0x0F; + new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ + new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8; + new_sample = clamp16(new_sample); + + outbuf[sample_count] = new_sample; + stream->adpcm_history2_32 = stream->adpcm_history1_32; + stream->adpcm_history1_32 = new_sample; + } + + /* only increment offset on complete frame */ + if (i == frame_samples) + stream->offset += xa_frame_size; + } +} + +#if 0 +/* later PC games use float math, though in the end sounds basically the same (decompiled from various exes) */ +static const double XA_K0[16] = { 0.0, 0.9375, 1.796875, 1.53125 }; +static const double XA_K1[16] = { 0.0, 0.0, -0.8125, -0.859375 }; +/* code uses look-up table but it's equivalent to: + * (double)((nibble << 28) >> (shift + 8) >> 8) or (double)(signed_nibble << (12 - shift)) */ +static const uint32_t FLOAT_TABLE_INT[256] = { + 0x00000000,0x45800000,0x46000000,0x46400000,0x46800000,0x46A00000,0x46C00000,0x46E00000, + 0xC7000000,0xC6E00000,0xC6C00000,0xC6A00000,0xC6800000,0xC6400000,0xC6000000,0xC5800000, + 0x00000000,0x45000000,0x45800000,0x45C00000,0x46000000,0x46200000,0x46400000,0x46600000, + 0xC6800000,0xC6600000,0xC6400000,0xC6200000,0xC6000000,0xC5C00000,0xC5800000,0xC5000000, + 0x00000000,0x44800000,0x45000000,0x45400000,0x45800000,0x45A00000,0x45C00000,0x45E00000, + 0xC6000000,0xC5E00000,0xC5C00000,0xC5A00000,0xC5800000,0xC5400000,0xC5000000,0xC4800000, + 0x00000000,0x44000000,0x44800000,0x44C00000,0x45000000,0x45200000,0x45400000,0x45600000, + 0xC5800000,0xC5600000,0xC5400000,0xC5200000,0xC5000000,0xC4C00000,0xC4800000,0xC4000000, + 0x00000000,0x43800000,0x44000000,0x44400000,0x44800000,0x44A00000,0x44C00000,0x44E00000, + 0xC5000000,0xC4E00000,0xC4C00000,0xC4A00000,0xC4800000,0xC4400000,0xC4000000,0xC3800000, + 0x00000000,0x43000000,0x43800000,0x43C00000,0x44000000,0x44200000,0x44400000,0x44600000, + 0xC4800000,0xC4600000,0xC4400000,0xC4200000,0xC4000000,0xC3C00000,0xC3800000,0xC3000000, + 0x00000000,0x42800000,0x43000000,0x43400000,0x43800000,0x43A00000,0x43C00000,0x43E00000, + 0xC4000000,0xC3E00000,0xC3C00000,0xC3A00000,0xC3800000,0xC3400000,0xC3000000,0xC2800000, + 0x00000000,0x42000000,0x42800000,0x42C00000,0x43000000,0x43200000,0x43400000,0x43600000, + 0xC3800000,0xC3600000,0xC3400000,0xC3200000,0xC3000000,0xC2C00000,0xC2800000,0xC2000000, + 0x00000000,0x41800000,0x42000000,0x42400000,0x42800000,0x42A00000,0x42C00000,0x42E00000, + 0xC3000000,0xC2E00000,0xC2C00000,0xC2A00000,0xC2800000,0xC2400000,0xC2000000,0xC1800000, + 0x00000000,0x41000000,0x41800000,0x41C00000,0x42000000,0x42200000,0x42400000,0x42600000, + 0xC2800000,0xC2600000,0xC2400000,0xC2200000,0xC2000000,0xC1C00000,0xC1800000,0xC1000000, + 0x00000000,0x40800000,0x41000000,0x41400000,0x41800000,0x41A00000,0x41C00000,0x41E00000, + 0xC2000000,0xC1E00000,0xC1C00000,0xC1A00000,0xC1800000,0xC1400000,0xC1000000,0xC0800000, + 0x00000000,0x40000000,0x40800000,0x40C00000,0x41000000,0x41200000,0x41400000,0x41600000, + 0xC1800000,0xC1600000,0xC1400000,0xC1200000,0xC1000000,0xC0C00000,0xC0800000,0xC0000000, + 0x00000000,0x3F800000,0x40000000,0x40400000,0x40800000,0x40A00000,0x40C00000,0x40E00000, + 0xC1000000,0xC0E00000,0xC0C00000,0xC0A00000,0xC0800000,0xC0400000,0xC0000000,0xBF800000, + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, +}; +static const float* FLOAT_TABLE = (const float *)FLOAT_TABLE_INT; + +void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame_info; + int i, sample_count, shift; + + int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02; + int xa_frame_size = 0x0f; + int frame_samples = 28; + first_sample = first_sample % frame_samples; + + /* header */ + frame_info = read_8bit(stream->offset,stream->streamfile); + + if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */ + stream->adpcm_history1_double = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile); + stream->adpcm_history2_double = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile); + + for (i=first_sample,sample_count=0; ioffset + 0x01 + 2*0x02 + i*0x02,stream->streamfile); + } + + /* only increment offset on complete frame */ + if (i == frame_samples) + stream->offset += pcm_frame_size; + } + else { /* ADPCM frame */ + double coef1, coef2, hist1, hist2, new_sample; + + coef1 = XA_K0[(frame_info >> 4)]; + coef2 = XA_K1[(frame_info >> 4)]; + shift = (frame_info & 0x0F) + 8;// << 4; + hist1 = stream->adpcm_history1_double; + hist2 = stream->adpcm_history2_double; + + for (i=first_sample,sample_count=0; ioffset + 0x01 + i/2); + int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */ + + sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); + sample_nibble = (sample_byte >> nibble_shift) & 0x0F; + new_sample = (double)FLOAT_TABLE[sample_nibble + shift]; + new_sample = new_sample + coef1 * hist1 + coef2 * hist2; + + outbuf[sample_count] = clamp16((int)new_sample); + hist2 = hist1; + hist1 = new_sample; + } + + stream->adpcm_history1_double = hist1; + stream->adpcm_history2_double = hist2; + + /* only increment offset on complete frame */ + if (i == frame_samples) + stream->offset += xa_frame_size; + } +} +#endif + +/* EA XA v1 (mono/stereo) */ +void decode_ea_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { + uint8_t frame_info; + int32_t coef1, coef2; + int i, sample_count, shift; + int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */ + + int frame_size = is_stereo ? 0x0f*2 : 0x0f; + int frame_samples = 28; + first_sample = first_sample % frame_samples; + + /* header */ + if (is_stereo) { + /* coefs ch0+ch1 + shift ch0+ch1 */ + frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); + coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0]; + coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4]; + + frame_info = read_8bit(stream->offset + 0x01, stream->streamfile); + shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8; + } else { + /* coefs + shift ch0 */ + frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); + coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; + coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; + shift = (frame_info & 0x0F) + 8; + } + + /* samples */ + for (i=first_sample,sample_count=0; ioffset + 0x02 + i) : (stream->offset + 0x01 + i/2); + int nibble_shift = is_stereo ? (hn ? 4 : 0) : ((!(i & 1)) ? 4 : 0); /* high nibble first */ + + sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); + sample_nibble = (sample_byte >> nibble_shift) & 0x0F; + new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ + new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; + new_sample = clamp16(new_sample); + + outbuf[sample_count] = new_sample; + stream->adpcm_history2_32 = stream->adpcm_history1_32; + stream->adpcm_history1_32 = new_sample; + } + + /* only increment offset on complete frame */ + if (i == frame_samples) + stream->offset += frame_size; +} + +/* Maxis EA-XA v1 (mono/stereo) with byte-interleave layout in stereo mode */ +void decode_maxis_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame_info; + int32_t coef1, coef2; + int i, sample_count, shift; + + int frame_size = 0x0f * channelspacing; /* varies in mono/stereo */ + int frame_samples = 28; + first_sample = first_sample % frame_samples; + + /* header (coefs+shift ch0 + coefs+shift ch1) */ + frame_info = read_8bit(stream->offset + channel,stream->streamfile); + coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; + coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; + shift = (frame_info & 0x0F) + 8; + + /* samples */ + for (i=first_sample,sample_count=0; ioffset + 0x01*channelspacing + (channelspacing == 2 ? i/2 + channel + (i/2)*0x01 : i/2)); + int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */ + + sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); + sample_nibble = (sample_byte >> nibble_shift) & 0x0F; + new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ + new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; + new_sample = clamp16(new_sample); + + outbuf[sample_count] = new_sample; + stream->adpcm_history2_32 = stream->adpcm_history1_32; + stream->adpcm_history1_32 = new_sample; + } + + /* only increment offset on complete frame */ + if (i == frame_samples) + stream->offset += frame_size; +} + +int32_t ea_xa_bytes_to_samples(size_t bytes, int channels) { + if (channels <= 0) return 0; + return bytes / channels / 0x0f * 28; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mc3_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mc3_decoder.c index 64e6bb17f..808318a90 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mc3_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mc3_decoder.c @@ -1,183 +1,183 @@ -#include "coding.h" -#include "../util.h" - - -static const int step_table[4] = { - 5, 1, -1, -3 -}; - -static const int mc3_table[4][4][64] = { - { - { - 2, 2, 3, 7, 15, 27, 45, 70, 104, 148, 202, 268, 347, 441, 551, 677, - 821, 984, 1168, 1374, 1602, 1854, 2131, 2435, 2767, 3127, 3517, 3938, 4392, 4880, 5402, 5960, - 6555, 7189, 7862, 8577, 9333, 10132, 10976, 11865, 12802, 13786, 14819, 15903, 17038, 18226, 19469, 20766, - 22120, 23531, 25001, 26531, 28123, 29776, 31494, 33276, 35124, 37039, 39023, 41076, 43201, 45397, 47666, 50010 - }, - { - 1, 1, 2, 5, 10, 18, 31, 48, 72, 101, 139, 184, 239, 303, 378, 465, - 564, 677, 803, 944, 1101, 1274, 1465, 1674, 1902, 2150, 2418, 2707, 3019, 3355, 3714, 4097, - 4507, 4942, 5405, 5896, 6416, 6966, 7546, 8157, 8801, 9477, 10188, 10933, 11714, 12530, 13384, 14276, - 15207, 16177, 17188, 18240, 19334, 20471, 21652, 22877, 24148, 25464, 26828, 28240, 29700, 31210, 32770, 34382 - }, - { - 0, 0, 1, 2, 4, 8, 14, 22, 32, 46, 63, 83, 108, 138, 172, 211, - 256, 307, 365, 429, 500, 579, 666, 761, 864, 977, 1099, 1230, 1372, 1525, 1688, 1862, - 2048, 2246, 2457, 2680, 2916, 3166, 3430, 3708, 4000, 4308, 4631, 4969, 5324, 5695, 6084, 6489, - 6912, 7353, 7813, 8291, 8788, 9305, 9841, 10398, 10976, 11574, 12194, 12836, 13500, 14186, 14895, 15628 - }, - { - 0, 0, 0, 0, 1, 3, 5, 8, 13, 18, 25, 33, 43, 55, 68, 84, - 102, 123, 146, 171, 200, 231, 266, 304, 345, 390, 439, 492, 549, 610, 675, 745, - 819, 898, 982, 1072, 1166, 1266, 1372, 1483, 1600, 1723, 1852, 1987, 2129, 2278, 2433, 2595, - 2765, 2941, 3125, 3316, 3515, 3722, 3936, 4159, 4390, 4629, 4877, 5134, 5400, 5674, 5958, 6251 - }, - }, - { - { - 1, 1, 2, 4, 9, 17, 28, 44, 65, 92, 126, 167, 217, 276, 344, 423, - 513, 615, 730, 858, 1001, 1159, 1332, 1522, 1729, 1954, 2198, 2461, 2745, 3050, 3376, 3725, - 4097, 4493, 4914, 5360, 5833, 6332, 6860, 7416, 8001, 8616, 9262, 9939, 10649, 11391, 12168, 12978, - 13825, 14707, 15626, 16582, 17576, 18610, 19683, 20797, 21952, 23149, 24389, 25673, 27000, 28373, 29791, 31256 - }, - { - 0, 0, 1, 2, 5, 10, 17, 26, 39, 55, 75, 100, 130, 165, 206, 254, - 308, 369, 438, 515, 600, 695, 799, 913, 1037, 1172, 1319, 1477, 1647, 1830, 2025, 2235, - 2458, 2696, 2948, 3216, 3499, 3799, 4116, 4449, 4800, 5169, 5557, 5963, 6389, 6835, 7300, 7787, - 8295, 8824, 9375, 9949, 10546, 11166, 11810, 12478, 13171, 13889, 14633, 15403, 16200, 17023, 17874, 18753 - }, - { - 0, 0, 0, 1, 3, 6, 11, 17, 26, 37, 50, 67, 86, 110, 137, 169, - 205, 246, 292, 343, 400, 463, 532, 608, 691, 781, 879, 984, 1098, 1220, 1350, 1490, - 1638, 1797, 1965, 2144, 2333, 2533, 2744, 2966, 3200, 3446, 3704, 3975, 4259, 4556, 4867, 5191, - 5530, 5882, 6250, 6632, 7030, 7444, 7873, 8319, 8781, 9259, 9755, 10269, 10800, 11349, 11916, 12502 - }, - { - 0, 0, 0, 0, 0, 1, 2, 4, 6, 9, 12, 16, 21, 27, 34, 42, - 51, 61, 73, 85, 100, 115, 133, 152, 172, 195, 219, 246, 274, 305, 337, 372, - 409, 449, 491, 536, 583, 633, 686, 741, 800, 861, 926, 993, 1064, 1139, 1216, 1297, - 1382, 1470, 1562, 1658, 1757, 1861, 1968, 2079, 2195, 2314, 2438, 2567, 2700, 2837, 2979, 3125 - }, - }, - { - { - 1, 1, 2, 5, 10, 18, 31, 48, 72, 101, 139, 184, 239, 303, 378, 465, - 564, 677, 803, 944, 1101, 1274, 1465, 1674, 1902, 2150, 2418, 2707, 3019, 3355, 3714, 4097, - 4507, 4942, 5405, 5896, 6416, 6966, 7546, 8157, 8801, 9477, 10188, 10933, 11714, 12530, 13384, 14276, - 15207, 16177, 17188, 18240, 19334, 20471, 21652, 22877, 24148, 25464, 26828, 28240, 29700, 31210, 32770, 34382 - }, - { - 1, 1, 1, 3, 7, 13, 22, 35, 52, 74, 101, 134, 173, 220, 275, 338, - 410, 492, 584, 687, 801, 927, 1065, 1217, 1383, 1563, 1758, 1969, 2196, 2440, 2701, 2980, - 3277, 3594, 3931, 4288, 4666, 5066, 5488, 5932, 6401, 6893, 7409, 7951, 8519, 9113, 9734, 10383, - 11060, 11765, 12500, 13265, 14061, 14888, 15747, 16638, 17562, 18519, 19511, 20538, 21600, 22698, 23833, 25005 - }, - { - 0, 0, 1, 2, 4, 8, 14, 22, 32, 46, 63, 83, 108, 138, 172, 211, - 256, 307, 365, 429, 500, 579, 666, 761, 864, 977, 1099, 1230, 1372, 1525, 1688, 1862, - 2048, 2246, 2457, 2680, 2916, 3166, 3430, 3708, 4000, 4308, 4631, 4969, 5324, 5695, 6084, 6489, - 6912, 7353, 7813, 8291, 8788, 9305, 9841, 10398, 10976, 11574, 12194, 12836, 13500, 14186, 14895, 15628 - }, - { - 0, 0, 0, 1, 2, 5, 8, 13, 19, 27, 37, 50, 65, 82, 103, 127, - 154, 184, 219, 257, 300, 347, 399, 456, 518, 586, 659, 738, 823, 915, 1012, 1117, - 1229, 1348, 1474, 1608, 1749, 1899, 2058, 2224, 2400, 2584, 2778, 2981, 3194, 3417, 3650, 3893, - 4147, 4412, 4687, 4974, 5273, 5583, 5905, 6239, 6585, 6944, 7316, 7701, 8100, 8511, 8937, 9376 - }, - }, - { - { - 1, 1, 2, 5, 11, 20, 34, 53, 78, 111, 151, 201, 260, 331, 413, 508, - 616, 738, 876, 1030, 1201, 1390, 1598, 1826, 2075, 2345, 2638, 2954, 3294, 3660, 4051, 4470, - 4916, 5392, 5897, 6432, 6999, 7599, 8232, 8899, 9601, 10339, 11114, 11927, 12779, 13670, 14601, 15574, - 16590, 17648, 18751, 19898, 21092, 22332, 23620, 24957, 26343, 27779, 29267, 30807, 32400, 34047, 35749, 37507 - }, - { - 1, 1, 1, 3, 6, 11, 19, 31, 45, 64, 88, 117, 152, 193, 241, 296, - 359, 430, 511, 601, 701, 811, 932, 1065, 1210, 1368, 1538, 1723, 1921, 2135, 2363, 2607, - 2868, 3145, 3440, 3752, 4083, 4433, 4802, 5191, 5600, 6031, 6483, 6957, 7454, 7974, 8517, 9085, - 9677, 10295, 10938, 11607, 12303, 13027, 13778, 14558, 15366, 16204, 17072, 17971, 18900, 19861, 20854, 21879 - }, - { - 0, 0, 0, 1, 2, 5, 8, 13, 19, 27, 37, 50, 65, 82, 103, 127, - 154, 184, 219, 257, 300, 347, 399, 456, 518, 586, 659, 738, 823, 915, 1012, 1117, - 1229, 1348, 1474, 1608, 1749, 1899, 2058, 2224, 2400, 2584, 2778, 2981, 3194, 3417, 3650, 3893, - 4147, 4412, 4687, 4974, 5273, 5583, 5905, 6239, 6585, 6944, 7316, 7701, 8100, 8511, 8937, 9376 - }, - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, - } -}; - - -/* MC3 3-bit ADPCM (Paradigm Entertainment games). - * - * Layout: blocks with 32b header + 32b ch1, 32b ch2, 32b ch1... - * Each 32b is a sub-block with 10 samples (3b x10) sharing a 'mode' of sorts. - * More than one block is rarely used though. - * - * Tables and original algorithm by daemon1 - */ -void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0; - - int32_t hist = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; - - /* internal interleave */ - int block_samples = (vgmstream->interleave_block_size - 4) / 4 / vgmstream->channels * 10; - first_sample = first_sample % block_samples; - - - /* block header */ - if (first_sample == 0) { - uint32_t header = (uint32_t)read_32bitLE(stream->offset, stream->streamfile); - header = (header >> channel*16); /* lower 16=ch1, upper 16b=ch2 */ - step_index = header & 0x3f; /* 6b */ - hist = header & 0xffc0; /* 16b sans 6b */ - if (hist > 0x7fff) hist -= 0x10000; /* sign extend */ - } - - - /* block samples */ - for (i = first_sample; i < first_sample + samples_to_do; i++) { - uint32_t subblock, mode, samples, index, sign, diff; - - /* header + ch shift + sub-block number (ex. ch0 i=10: sub-block 1, ch0 i=23: sub-block 2) */ - off_t subblock_offset = stream->offset + 4 + 4*channel + (i/10)*(4*vgmstream->channels); - int sample_shift = (i%10)*3; - - /* expand 3b */ - subblock = (uint32_t)read_32bitLE(subblock_offset, stream->streamfile); - mode = (subblock >> 30) & 0x3; /* upper 2b */ - samples = (subblock) & 0x3FFFFFFF; /* lower 3b*10 */ - - index = (samples >> sample_shift) & 3; /* lower 2b */ - sign = (samples >> sample_shift) & 4; /* upper 1b */ - diff = mc3_table[mode][index][step_index]; - if (sign == 0) - hist += (- 1 - diff); - else - hist += diff; - - /* new step + clamp */ - step_index += step_table[index]; - if (step_index < 0) step_index = 0; - else if (step_index > 63) step_index = 63; - - /* output */ - outbuf[sample_count] = hist; - sample_count += channelspacing; - } - - - /* internal interleave: increment offset on complete frame */ - if (i == block_samples) stream->offset += vgmstream->interleave_block_size; - - stream->adpcm_history1_32 = hist; - stream->adpcm_step_index = step_index; -} +#include "coding.h" +#include "../util.h" + + +static const int step_table[4] = { + 5, 1, -1, -3 +}; + +static const int mc3_table[4][4][64] = { + { + { + 2, 2, 3, 7, 15, 27, 45, 70, 104, 148, 202, 268, 347, 441, 551, 677, + 821, 984, 1168, 1374, 1602, 1854, 2131, 2435, 2767, 3127, 3517, 3938, 4392, 4880, 5402, 5960, + 6555, 7189, 7862, 8577, 9333, 10132, 10976, 11865, 12802, 13786, 14819, 15903, 17038, 18226, 19469, 20766, + 22120, 23531, 25001, 26531, 28123, 29776, 31494, 33276, 35124, 37039, 39023, 41076, 43201, 45397, 47666, 50010 + }, + { + 1, 1, 2, 5, 10, 18, 31, 48, 72, 101, 139, 184, 239, 303, 378, 465, + 564, 677, 803, 944, 1101, 1274, 1465, 1674, 1902, 2150, 2418, 2707, 3019, 3355, 3714, 4097, + 4507, 4942, 5405, 5896, 6416, 6966, 7546, 8157, 8801, 9477, 10188, 10933, 11714, 12530, 13384, 14276, + 15207, 16177, 17188, 18240, 19334, 20471, 21652, 22877, 24148, 25464, 26828, 28240, 29700, 31210, 32770, 34382 + }, + { + 0, 0, 1, 2, 4, 8, 14, 22, 32, 46, 63, 83, 108, 138, 172, 211, + 256, 307, 365, 429, 500, 579, 666, 761, 864, 977, 1099, 1230, 1372, 1525, 1688, 1862, + 2048, 2246, 2457, 2680, 2916, 3166, 3430, 3708, 4000, 4308, 4631, 4969, 5324, 5695, 6084, 6489, + 6912, 7353, 7813, 8291, 8788, 9305, 9841, 10398, 10976, 11574, 12194, 12836, 13500, 14186, 14895, 15628 + }, + { + 0, 0, 0, 0, 1, 3, 5, 8, 13, 18, 25, 33, 43, 55, 68, 84, + 102, 123, 146, 171, 200, 231, 266, 304, 345, 390, 439, 492, 549, 610, 675, 745, + 819, 898, 982, 1072, 1166, 1266, 1372, 1483, 1600, 1723, 1852, 1987, 2129, 2278, 2433, 2595, + 2765, 2941, 3125, 3316, 3515, 3722, 3936, 4159, 4390, 4629, 4877, 5134, 5400, 5674, 5958, 6251 + }, + }, + { + { + 1, 1, 2, 4, 9, 17, 28, 44, 65, 92, 126, 167, 217, 276, 344, 423, + 513, 615, 730, 858, 1001, 1159, 1332, 1522, 1729, 1954, 2198, 2461, 2745, 3050, 3376, 3725, + 4097, 4493, 4914, 5360, 5833, 6332, 6860, 7416, 8001, 8616, 9262, 9939, 10649, 11391, 12168, 12978, + 13825, 14707, 15626, 16582, 17576, 18610, 19683, 20797, 21952, 23149, 24389, 25673, 27000, 28373, 29791, 31256 + }, + { + 0, 0, 1, 2, 5, 10, 17, 26, 39, 55, 75, 100, 130, 165, 206, 254, + 308, 369, 438, 515, 600, 695, 799, 913, 1037, 1172, 1319, 1477, 1647, 1830, 2025, 2235, + 2458, 2696, 2948, 3216, 3499, 3799, 4116, 4449, 4800, 5169, 5557, 5963, 6389, 6835, 7300, 7787, + 8295, 8824, 9375, 9949, 10546, 11166, 11810, 12478, 13171, 13889, 14633, 15403, 16200, 17023, 17874, 18753 + }, + { + 0, 0, 0, 1, 3, 6, 11, 17, 26, 37, 50, 67, 86, 110, 137, 169, + 205, 246, 292, 343, 400, 463, 532, 608, 691, 781, 879, 984, 1098, 1220, 1350, 1490, + 1638, 1797, 1965, 2144, 2333, 2533, 2744, 2966, 3200, 3446, 3704, 3975, 4259, 4556, 4867, 5191, + 5530, 5882, 6250, 6632, 7030, 7444, 7873, 8319, 8781, 9259, 9755, 10269, 10800, 11349, 11916, 12502 + }, + { + 0, 0, 0, 0, 0, 1, 2, 4, 6, 9, 12, 16, 21, 27, 34, 42, + 51, 61, 73, 85, 100, 115, 133, 152, 172, 195, 219, 246, 274, 305, 337, 372, + 409, 449, 491, 536, 583, 633, 686, 741, 800, 861, 926, 993, 1064, 1139, 1216, 1297, + 1382, 1470, 1562, 1658, 1757, 1861, 1968, 2079, 2195, 2314, 2438, 2567, 2700, 2837, 2979, 3125 + }, + }, + { + { + 1, 1, 2, 5, 10, 18, 31, 48, 72, 101, 139, 184, 239, 303, 378, 465, + 564, 677, 803, 944, 1101, 1274, 1465, 1674, 1902, 2150, 2418, 2707, 3019, 3355, 3714, 4097, + 4507, 4942, 5405, 5896, 6416, 6966, 7546, 8157, 8801, 9477, 10188, 10933, 11714, 12530, 13384, 14276, + 15207, 16177, 17188, 18240, 19334, 20471, 21652, 22877, 24148, 25464, 26828, 28240, 29700, 31210, 32770, 34382 + }, + { + 1, 1, 1, 3, 7, 13, 22, 35, 52, 74, 101, 134, 173, 220, 275, 338, + 410, 492, 584, 687, 801, 927, 1065, 1217, 1383, 1563, 1758, 1969, 2196, 2440, 2701, 2980, + 3277, 3594, 3931, 4288, 4666, 5066, 5488, 5932, 6401, 6893, 7409, 7951, 8519, 9113, 9734, 10383, + 11060, 11765, 12500, 13265, 14061, 14888, 15747, 16638, 17562, 18519, 19511, 20538, 21600, 22698, 23833, 25005 + }, + { + 0, 0, 1, 2, 4, 8, 14, 22, 32, 46, 63, 83, 108, 138, 172, 211, + 256, 307, 365, 429, 500, 579, 666, 761, 864, 977, 1099, 1230, 1372, 1525, 1688, 1862, + 2048, 2246, 2457, 2680, 2916, 3166, 3430, 3708, 4000, 4308, 4631, 4969, 5324, 5695, 6084, 6489, + 6912, 7353, 7813, 8291, 8788, 9305, 9841, 10398, 10976, 11574, 12194, 12836, 13500, 14186, 14895, 15628 + }, + { + 0, 0, 0, 1, 2, 5, 8, 13, 19, 27, 37, 50, 65, 82, 103, 127, + 154, 184, 219, 257, 300, 347, 399, 456, 518, 586, 659, 738, 823, 915, 1012, 1117, + 1229, 1348, 1474, 1608, 1749, 1899, 2058, 2224, 2400, 2584, 2778, 2981, 3194, 3417, 3650, 3893, + 4147, 4412, 4687, 4974, 5273, 5583, 5905, 6239, 6585, 6944, 7316, 7701, 8100, 8511, 8937, 9376 + }, + }, + { + { + 1, 1, 2, 5, 11, 20, 34, 53, 78, 111, 151, 201, 260, 331, 413, 508, + 616, 738, 876, 1030, 1201, 1390, 1598, 1826, 2075, 2345, 2638, 2954, 3294, 3660, 4051, 4470, + 4916, 5392, 5897, 6432, 6999, 7599, 8232, 8899, 9601, 10339, 11114, 11927, 12779, 13670, 14601, 15574, + 16590, 17648, 18751, 19898, 21092, 22332, 23620, 24957, 26343, 27779, 29267, 30807, 32400, 34047, 35749, 37507 + }, + { + 1, 1, 1, 3, 6, 11, 19, 31, 45, 64, 88, 117, 152, 193, 241, 296, + 359, 430, 511, 601, 701, 811, 932, 1065, 1210, 1368, 1538, 1723, 1921, 2135, 2363, 2607, + 2868, 3145, 3440, 3752, 4083, 4433, 4802, 5191, 5600, 6031, 6483, 6957, 7454, 7974, 8517, 9085, + 9677, 10295, 10938, 11607, 12303, 13027, 13778, 14558, 15366, 16204, 17072, 17971, 18900, 19861, 20854, 21879 + }, + { + 0, 0, 0, 1, 2, 5, 8, 13, 19, 27, 37, 50, 65, 82, 103, 127, + 154, 184, 219, 257, 300, 347, 399, 456, 518, 586, 659, 738, 823, 915, 1012, 1117, + 1229, 1348, 1474, 1608, 1749, 1899, 2058, 2224, 2400, 2584, 2778, 2981, 3194, 3417, 3650, 3893, + 4147, 4412, 4687, 4974, 5273, 5583, 5905, 6239, 6585, 6944, 7316, 7701, 8100, 8511, 8937, 9376 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + } +}; + + +/* MC3 3-bit ADPCM (Paradigm Entertainment games). + * + * Layout: blocks with 32b header + 32b ch1, 32b ch2, 32b ch1... + * Each 32b is a sub-block with 10 samples (3b x10) sharing a 'mode' of sorts. + * More than one block is rarely used though. + * + * Tables and original algorithm by daemon1 + */ +void decode_mc3(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; + + int32_t hist = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + /* internal interleave */ + int block_samples = (vgmstream->interleave_block_size - 4) / 4 / vgmstream->channels * 10; + first_sample = first_sample % block_samples; + + + /* block header */ + if (first_sample == 0) { + uint32_t header = (uint32_t)read_32bitLE(stream->offset, stream->streamfile); + header = (header >> channel*16); /* lower 16=ch1, upper 16b=ch2 */ + step_index = header & 0x3f; /* 6b */ + hist = header & 0xffc0; /* 16b sans 6b */ + if (hist > 0x7fff) hist -= 0x10000; /* sign extend */ + } + + + /* block samples */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + uint32_t subblock, mode, samples, index, sign, diff; + + /* header + ch shift + sub-block number (ex. ch0 i=10: sub-block 1, ch0 i=23: sub-block 2) */ + off_t subblock_offset = stream->offset + 4 + 4*channel + (i/10)*(4*vgmstream->channels); + int sample_shift = (i%10)*3; + + /* expand 3b */ + subblock = (uint32_t)read_32bitLE(subblock_offset, stream->streamfile); + mode = (subblock >> 30) & 0x3; /* upper 2b */ + samples = (subblock) & 0x3FFFFFFF; /* lower 3b*10 */ + + index = (samples >> sample_shift) & 3; /* lower 2b */ + sign = (samples >> sample_shift) & 4; /* upper 1b */ + diff = mc3_table[mode][index][step_index]; + if (sign == 0) + hist += (- 1 - diff); + else + hist += diff; + + /* new step + clamp */ + step_index += step_table[index]; + if (step_index < 0) step_index = 0; + else if (step_index > 63) step_index = 63; + + /* output */ + outbuf[sample_count] = hist; + sample_count += channelspacing; + } + + + /* internal interleave: increment offset on complete frame */ + if (i == block_samples) stream->offset += vgmstream->interleave_block_size; + + stream->adpcm_history1_32 = hist; + stream->adpcm_step_index = step_index; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c index 3f1899db3..c299bfc75 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c @@ -1,254 +1,254 @@ -#include "../util.h" -#include "coding.h" - - -static const int16_t msadpcm_steps[16] = { - 230, 230, 230, 230, - 307, 409, 512, 614, - 768, 614, 512, 409, - 307, 230, 230, 230 -}; - -static const int16_t msadpcm_coefs[7][2] = { - { 256, 0 }, - { 512, -256 }, - { 0, 0 }, - { 192, 64 }, - { 240, 0 }, - { 460, -208 }, - { 392, -232 } -}; - -void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do) { - VGMSTREAMCHANNEL *ch1,*ch2; - STREAMFILE *streamfile; - int i, frames_in; - size_t bytes_per_frame, samples_per_frame; - off_t frame_offset; - - ch1 = &vgmstream->ch[0]; - ch2 = &vgmstream->ch[1]; - streamfile = ch1->streamfile; - - /* external interleave (variable size), stereo */ - bytes_per_frame = vgmstream->frame_size; - samples_per_frame = (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; - frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; - - frame_offset = ch1->offset + frames_in*bytes_per_frame; - - /* parse frame header */ - if (first_sample == 0) { - ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0]; - ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1]; - ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0]; - ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1]; - ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile); - ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile); - ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile); - ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile); - ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile); - ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile); - } - - /* write header samples (needed) */ - if (first_sample==0) { - outbuf[0] = ch1->adpcm_history2_16; - outbuf[1] = ch2->adpcm_history2_16; - outbuf += 2; - first_sample++; - samples_to_do--; - } - if (first_sample == 1 && samples_to_do > 0) { - outbuf[0] = ch1->adpcm_history1_16; - outbuf[1] = ch2->adpcm_history1_16; - outbuf += 2; - first_sample++; - samples_to_do--; - } - - /* decode nibbles */ - for (i = first_sample; i < first_sample+samples_to_do; i++) { - int ch; - - for (ch = 0; ch < 2; ch++) { - VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch]; - int32_t hist1,hist2, predicted; - int sample_nibble = (ch == 0) ? /* L = high nibble first */ - get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) : - get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile)); - - hist1 = stream->adpcm_history1_16; - hist2 = stream->adpcm_history2_16; - predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; - predicted = predicted / 256; - predicted = predicted + sample_nibble*stream->adpcm_scale; - outbuf[0] = clamp16(predicted); - - stream->adpcm_history2_16 = stream->adpcm_history1_16; - stream->adpcm_history1_16 = outbuf[0]; - stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; - if (stream->adpcm_scale < 0x10) - stream->adpcm_scale = 0x10; - - outbuf++; - } - } -} - -void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; - int i, frames_in; - size_t bytes_per_frame, samples_per_frame; - off_t frame_offset; - - /* external interleave (variable size), mono */ - bytes_per_frame = vgmstream->frame_size; - samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2; - frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; - - frame_offset = stream->offset + frames_in*bytes_per_frame; - - /* parse frame header */ - if (first_sample == 0) { - stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; - stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; - stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); - stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); - stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); - } - - /* write header samples (needed) */ - if (first_sample == 0) { - outbuf[0] = stream->adpcm_history2_16; - outbuf += channelspacing; - first_sample++; - samples_to_do--; - } - if (first_sample == 1 && samples_to_do > 0) { - outbuf[0] = stream->adpcm_history1_16; - outbuf += channelspacing; - first_sample++; - samples_to_do--; - } - - /* decode nibbles */ - for (i = first_sample; i < first_sample+samples_to_do; i++) { - int32_t hist1,hist2, predicted; - int sample_nibble = (i & 1) ? /* high nibble first */ - get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : - get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); - - hist1 = stream->adpcm_history1_16; - hist2 = stream->adpcm_history2_16; - predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; - predicted = predicted / 256; - predicted = predicted + sample_nibble*stream->adpcm_scale; - outbuf[0] = clamp16(predicted); - - stream->adpcm_history2_16 = stream->adpcm_history1_16; - stream->adpcm_history1_16 = outbuf[0]; - stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; - if (stream->adpcm_scale < 0x10) - stream->adpcm_scale = 0x10; - - outbuf += channelspacing; - } -} - -/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order - * (their tools may convert to float/others but internally it's all PCM16, from debugging). */ -void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; - int i, frames_in; - size_t bytes_per_frame, samples_per_frame; - off_t frame_offset; - - /* external interleave (variable size), mono */ - bytes_per_frame = vgmstream->frame_size; - samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2; - frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; - - frame_offset = stream->offset + frames_in*bytes_per_frame; - - /* parse frame header */ - if (first_sample == 0) { - stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; - stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; - stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); - stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */ - stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); - } - - /* write header samples (needed) */ - if (first_sample == 0) { - outbuf[0] = stream->adpcm_history2_16; - outbuf += channelspacing; - first_sample++; - samples_to_do--; - } - if (first_sample == 1 && samples_to_do > 0) { - outbuf[0] = stream->adpcm_history1_16; - outbuf += channelspacing; - first_sample++; - samples_to_do--; - } - - /* decode nibbles */ - for (i = first_sample; i < first_sample+samples_to_do; i++) { - int32_t hist1,hist2, predicted; - int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */ - get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : - get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); - - hist1 = stream->adpcm_history1_16; - hist2 = stream->adpcm_history2_16; - predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; - predicted = predicted >> 8; /* probably no difference vs MSADPCM */ - predicted = predicted + sample_nibble*stream->adpcm_scale; - outbuf[0] = clamp16(predicted); - - stream->adpcm_history2_16 = stream->adpcm_history1_16; - stream->adpcm_history1_16 = outbuf[0]; - stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8; - if (stream->adpcm_scale < 0x10) - stream->adpcm_scale = 0x10; - - outbuf += channelspacing; - } -} - -long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) { - if (block_size <= 0 || channels <= 0) return 0; - return (bytes / block_size) * (block_size - (7-1)*channels) * 2 / channels - + ((bytes % block_size) ? ((bytes % block_size) - (7-1)*channels) * 2 / channels : 0); -} - -/* test if MSADPCM coefs were re-defined (possible in theory but not used in practice) */ -int msadpcm_check_coefs(STREAMFILE *sf, off_t offset) { - int i; - int count = read_16bitLE(offset, sf); - if (count != 7) { - VGM_LOG("MSADPCM: bad count %i at %lx\n", count, offset); - goto fail; - } - - offset += 0x02; - for (i = 0; i < 7; i++) { - int16_t coef1 = read_16bitLE(offset + 0x00, sf); - int16_t coef2 = read_16bitLE(offset + 0x02, sf); - - if (coef1 != msadpcm_coefs[i][0] || coef2 != msadpcm_coefs[i][1]) { - VGM_LOG("MSADPCM: bad coef %i/%i vs %i/%i\n", coef1, coef2, msadpcm_coefs[i][0], msadpcm_coefs[i][1]); - goto fail; - } - offset += 0x02 + 0x02; - } - - return 1; -fail: - return 0; -} +#include "../util.h" +#include "coding.h" + + +static const int16_t msadpcm_steps[16] = { + 230, 230, 230, 230, + 307, 409, 512, 614, + 768, 614, 512, 409, + 307, 230, 230, 230 +}; + +static const int16_t msadpcm_coefs[7][2] = { + { 256, 0 }, + { 512, -256 }, + { 0, 0 }, + { 192, 64 }, + { 240, 0 }, + { 460, -208 }, + { 392, -232 } +}; + +void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do) { + VGMSTREAMCHANNEL *ch1,*ch2; + STREAMFILE *streamfile; + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; + + ch1 = &vgmstream->ch[0]; + ch2 = &vgmstream->ch[1]; + streamfile = ch1->streamfile; + + /* external interleave (variable size), stereo */ + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = ch1->offset + frames_in*bytes_per_frame; + + /* parse frame header */ + if (first_sample == 0) { + ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0]; + ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1]; + ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0]; + ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1]; + ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile); + ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile); + ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile); + ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile); + ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile); + ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile); + } + + /* write header samples (needed) */ + if (first_sample==0) { + outbuf[0] = ch1->adpcm_history2_16; + outbuf[1] = ch2->adpcm_history2_16; + outbuf += 2; + first_sample++; + samples_to_do--; + } + if (first_sample == 1 && samples_to_do > 0) { + outbuf[0] = ch1->adpcm_history1_16; + outbuf[1] = ch2->adpcm_history1_16; + outbuf += 2; + first_sample++; + samples_to_do--; + } + + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int ch; + + for (ch = 0; ch < 2; ch++) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch]; + int32_t hist1,hist2, predicted; + int sample_nibble = (ch == 0) ? /* L = high nibble first */ + get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) : + get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile)); + + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted / 256; + predicted = predicted + sample_nibble*stream->adpcm_scale; + outbuf[0] = clamp16(predicted); + + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; + + outbuf++; + } + } +} + +void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; + + /* external interleave (variable size), mono */ + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = stream->offset + frames_in*bytes_per_frame; + + /* parse frame header */ + if (first_sample == 0) { + stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; + stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; + stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); + stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); + stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); + } + + /* write header samples (needed) */ + if (first_sample == 0) { + outbuf[0] = stream->adpcm_history2_16; + outbuf += channelspacing; + first_sample++; + samples_to_do--; + } + if (first_sample == 1 && samples_to_do > 0) { + outbuf[0] = stream->adpcm_history1_16; + outbuf += channelspacing; + first_sample++; + samples_to_do--; + } + + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist1,hist2, predicted; + int sample_nibble = (i & 1) ? /* high nibble first */ + get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : + get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); + + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted / 256; + predicted = predicted + sample_nibble*stream->adpcm_scale; + outbuf[0] = clamp16(predicted); + + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; + + outbuf += channelspacing; + } +} + +/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order + * (their tools may convert to float/others but internally it's all PCM16, from debugging). */ +void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; + + /* external interleave (variable size), mono */ + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = stream->offset + frames_in*bytes_per_frame; + + /* parse frame header */ + if (first_sample == 0) { + stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; + stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; + stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); + stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */ + stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); + } + + /* write header samples (needed) */ + if (first_sample == 0) { + outbuf[0] = stream->adpcm_history2_16; + outbuf += channelspacing; + first_sample++; + samples_to_do--; + } + if (first_sample == 1 && samples_to_do > 0) { + outbuf[0] = stream->adpcm_history1_16; + outbuf += channelspacing; + first_sample++; + samples_to_do--; + } + + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist1,hist2, predicted; + int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */ + get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : + get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); + + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted >> 8; /* probably no difference vs MSADPCM */ + predicted = predicted + sample_nibble*stream->adpcm_scale; + outbuf[0] = clamp16(predicted); + + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; + + outbuf += channelspacing; + } +} + +long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) { + if (block_size <= 0 || channels <= 0) return 0; + return (bytes / block_size) * (block_size - (7-1)*channels) * 2 / channels + + ((bytes % block_size) ? ((bytes % block_size) - (7-1)*channels) * 2 / channels : 0); +} + +/* test if MSADPCM coefs were re-defined (possible in theory but not used in practice) */ +int msadpcm_check_coefs(STREAMFILE* sf, off_t offset) { + int i; + int count = read_16bitLE(offset, sf); + if (count != 7) { + VGM_LOG("MSADPCM: bad count %i at %lx\n", count, offset); + goto fail; + } + + offset += 0x02; + for (i = 0; i < 7; i++) { + int16_t coef1 = read_16bitLE(offset + 0x00, sf); + int16_t coef2 = read_16bitLE(offset + 0x02, sf); + + if (coef1 != msadpcm_coefs[i][0] || coef2 != msadpcm_coefs[i][1]) { + VGM_LOG("MSADPCM: bad coef %i/%i vs %i/%i\n", coef1, coef2, msadpcm_coefs[i][0], msadpcm_coefs[i][1]); + goto fail; + } + offset += 0x02 + 0x02; + } + + return 1; +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/psv_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/psv_decoder.c index bdb590ab9..6687f3c7e 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/psv_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/psv_decoder.c @@ -1,207 +1,207 @@ -#include -#include "coding.h" -#include "../util.h" - -/* PSVita ADPCM table */ -static const int16_t hevag_coefs[128][4] = { - { 0, 0, 0, 0 }, - { 7680, 0, 0, 0 }, - { 14720, -6656, 0, 0 }, - { 12544, -7040, 0, 0 }, - { 15616, -7680, 0, 0 }, - { 14731, -7059, 0, 0 }, - { 14507, -7366, 0, 0 }, - { 13920, -7522, 0, 0 }, - { 13133, -7680, 0, 0 }, - { 12028, -7680, 0, 0 }, - { 10764, -7680, 0, 0 }, - { 9359, -7680, 0, 0 }, - { 7832, -7680, 0, 0 }, - { 6201, -7680, 0, 0 }, - { 4488, -7680, 0, 0 }, - { 2717, -7680, 0, 0 }, - { 910, -7680, 0, 0 }, - { -910, -7680, 0, 0 }, - { -2717, -7680, 0, 0 }, - { -4488, -7680, 0, 0 }, - { -6201, -7680, 0, 0 }, - { -7832, -7680, 0, 0 }, - { -9359, -7680, 0, 0 }, - { -10764, -7680, 0, 0 }, - { -12028, -7680, 0, 0 }, - { -13133, -7680, 0, 0 }, - { -13920, -7522, 0, 0 }, - { -14507, -7366, 0, 0 }, - { -14731, -7059, 0, 0 }, - { 5376, -9216, 3328, -3072 }, - { -6400, -7168, -3328, -2304 }, - { -10496, -7424, -3584, -1024 }, - { -167, -2722, -494, -541 }, - { -7430, -2221, -2298, 424 }, - { -8001, -3166, -2814, 289 }, - { 6018, -4750, 2649, -1298 }, - { 3798, -6946, 3875, -1216 }, - { -8237, -2596, -2071, 227 }, - { 9199, 1982, -1382, -2316 }, - { 13021, -3044, -3792, 1267 }, - { 13112, -4487, -2250, 1665 }, - { -1668, -3744, -6456, 840 }, - { 7819, -4328, 2111, -506 }, - { 9571, -1336, -757, 487 }, - { 10032, -2562, 300, 199 }, - { -4745, -4122, -5486, -1493 }, - { -5896, 2378, -4787, -6947 }, - { -1193, -9117, -1237, -3114 }, - { 2783, -7108, -1575, -1447 }, - { -7334, -2062, -2212, 446 }, - { 6127, -2577, -315, -18 }, - { 9457, -1858, 102, 258 }, - { 7876, -4483, 2126, -538 }, - { -7172, -1795, -2069, 482 }, - { -7358, -2102, -2233, 440 }, - { -9170, -3509, -2674, -391 }, - { -2638, -2647, -1929, -1637 }, - { 1873, 9183, 1860, -5746 }, - { 9214, 1859, -1124, -2427 }, - { 13204, -3012, -4139, 1370 }, - { 12437, -4792, -256, 622 }, - { -2653, -1144, -3182, -6878 }, - { 9331, -1048, -828, 507 }, - { 1642, -620, -946, -4229 }, - { 4246, -7585, -533, -2259 }, - { -8988, -3891, -2807, 44 }, - { -2562, -2735, -1730, -1899 }, - { 3182, -483, -714, -1421 }, - { 7937, -3844, 2821, -1019 }, - { 10069, -2609, 314, 195 }, - { 8400, -3297, 1551, -155 }, - { -8529, -2775, -2432, -336 }, - { 9477, -1882, 108, 256 }, - { 75, -2241, -298, -6937 }, - { -9143, -4160, -2963, 5 }, - { -7270, -1958, -2156, 460 }, - { -2740, 3745, 5936, -1089 }, - { 8993, 1948, -683, -2704 }, - { 13101, -2835, -3854, 1055 }, - { 9543, -1961, 130, 250 }, - { 5272, -4270, 3124, -3157 }, - { -7696, -3383, -2907, -456 }, - { 7309, 2523, 434, -2461 }, - { 10275, -2867, 391, 172 }, - { 10940, -3721, 665, 97 }, - { 24, -310, -1262, 320 }, - { -8122, -2411, -2311, -271 }, - { -8511, -3067, -2337, 163 }, - { 326, -3846, 419, -933 }, - { 8895, 2194, -541, -2880 }, - { 12073, -1876, -2017, -601 }, - { 8729, -3423, 1674, -169 }, - { 12950, -3847, -3007, 1946 }, - { 10038, -2570, 302, 198 }, - { 9385, -2757, 1008, 41 }, - { -4720, -5006, -2852, -1161 }, - { 7869, -4326, 2135, -501 }, - { 2450, -8597, 1299, -2780 }, - { 10192, -2763, 360, 181 }, - { 11313, -4213, 833, 53 }, - { 10154, -2716, 345, 185 }, - { 9638, -1417, -737, 482 }, - { 3854, -4554, 2843, -3397 }, - { 6699, -5659, 2249, -1074 }, - { 11082, -3908, 728, 80 }, - { -1026, -9810, -805, -3462 }, - { 10396, -3746, 1367, -96 }, - { 10287, 988, -1915, -1437 }, - { 7953, 3878, -764, -3263 }, - { 12689, -3375, -3354, 2079 }, - { 6641, 3166, 231, -2089 }, - { -2348, -7354, -1944, -4122 }, - { 9290, -4039, 1885, -246 }, - { 4633, -6403, 1748, -1619 }, - { 11247, -4125, 802, 61 }, - { 9807, -2284, 219, 222 }, - { 9736, -1536, -706, 473 }, - { 8440, -3436, 1562, -176 }, - { 9307, -1021, -835, 509 }, - { 1698, -9025, 688, -3037 }, - { 10214, -2791, 368, 179 }, - { 8390, 3248, -758, -2989 }, - { 7201, 3316, 46, -2614 }, - { -88, -7809, -538, -4571 }, - { 6193, -5189, 2760, -1245 }, - { 12325, -1290, -3284, 253 }, - { 13064, -4075, -2824, 1877 }, - { 5333, 2999, 775, -1132 } -}; - - -/** - * Sony's HEVAG (High Efficiency VAG) ADPCM, used in PSVita games (hardware decoded). - * Evolution of the regular VAG (same flags and frames), uses 4 history samples and a bigger table. - * - * Original research and algorithm by id-daemon / daemon1. - */ -void decode_hevag(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - uint8_t frame[0x10] = {0}; - off_t frame_offset; - int i, frames_in, sample_count = 0; - size_t bytes_per_frame, samples_per_frame; - int coef_index, shift_factor, flag; - int32_t hist1 = stream->adpcm_history1_32; - int32_t hist2 = stream->adpcm_history2_32; - int32_t hist3 = stream->adpcm_history3_32; - int32_t hist4 = stream->adpcm_history4_32; - - - /* external interleave (fixed size), mono */ - bytes_per_frame = 0x10; - samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */ - frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; - - /* parse frame header */ - frame_offset = stream->offset + bytes_per_frame * frames_in; - read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - coef_index = (frame[0] >> 4) & 0xf; - shift_factor = (frame[0] >> 0) & 0xf; - coef_index = ((frame[1] >> 0) & 0xf0) | coef_index; - flag = (frame[1] >> 0) & 0xf; /* same flags */ - - VGM_ASSERT_ONCE(coef_index > 127 || shift_factor > 12, "HEVAG: in+correct coefs/shift at %x\n", (uint32_t)frame_offset); - if (coef_index > 127) - coef_index = 127; /* ? */ - if (shift_factor > 12) - shift_factor = 9; /* ? */ - - shift_factor = 20 - shift_factor; - /* decode nibbles */ - for (i = first_sample; i < first_sample + samples_to_do; i++) { - int32_t sample = 0; - - if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */ - uint8_t nibbles = frame[0x02 + i/2]; - - sample = (i&1 ? /* low nibble first */ - get_high_nibble_signed(nibbles): - get_low_nibble_signed(nibbles)) << shift_factor; /*scale*/ - sample = ((hist1 * hevag_coefs[coef_index][0] + - hist2 * hevag_coefs[coef_index][1] + - hist3 * hevag_coefs[coef_index][2] + - hist4 * hevag_coefs[coef_index][3]) >> 5) + sample; - sample >>= 8; - } - - outbuf[sample_count] = clamp16(sample); /*clamping*/ - sample_count += channelspacing; - - hist4 = hist3; - hist3 = hist2; - hist2 = hist1; - hist1 = sample; - } - - stream->adpcm_history1_32 = hist1; - stream->adpcm_history2_32 = hist2; - stream->adpcm_history3_32 = hist3; - stream->adpcm_history4_32 = hist4; -} +#include +#include "coding.h" +#include "../util.h" + +/* PSVita ADPCM table */ +static const int16_t hevag_coefs[128][4] = { + { 0, 0, 0, 0 }, + { 7680, 0, 0, 0 }, + { 14720, -6656, 0, 0 }, + { 12544, -7040, 0, 0 }, + { 15616, -7680, 0, 0 }, + { 14731, -7059, 0, 0 }, + { 14507, -7366, 0, 0 }, + { 13920, -7522, 0, 0 }, + { 13133, -7680, 0, 0 }, + { 12028, -7680, 0, 0 }, + { 10764, -7680, 0, 0 }, + { 9359, -7680, 0, 0 }, + { 7832, -7680, 0, 0 }, + { 6201, -7680, 0, 0 }, + { 4488, -7680, 0, 0 }, + { 2717, -7680, 0, 0 }, + { 910, -7680, 0, 0 }, + { -910, -7680, 0, 0 }, + { -2717, -7680, 0, 0 }, + { -4488, -7680, 0, 0 }, + { -6201, -7680, 0, 0 }, + { -7832, -7680, 0, 0 }, + { -9359, -7680, 0, 0 }, + { -10764, -7680, 0, 0 }, + { -12028, -7680, 0, 0 }, + { -13133, -7680, 0, 0 }, + { -13920, -7522, 0, 0 }, + { -14507, -7366, 0, 0 }, + { -14731, -7059, 0, 0 }, + { 5376, -9216, 3328, -3072 }, + { -6400, -7168, -3328, -2304 }, + { -10496, -7424, -3584, -1024 }, + { -167, -2722, -494, -541 }, + { -7430, -2221, -2298, 424 }, + { -8001, -3166, -2814, 289 }, + { 6018, -4750, 2649, -1298 }, + { 3798, -6946, 3875, -1216 }, + { -8237, -2596, -2071, 227 }, + { 9199, 1982, -1382, -2316 }, + { 13021, -3044, -3792, 1267 }, + { 13112, -4487, -2250, 1665 }, + { -1668, -3744, -6456, 840 }, + { 7819, -4328, 2111, -506 }, + { 9571, -1336, -757, 487 }, + { 10032, -2562, 300, 199 }, + { -4745, -4122, -5486, -1493 }, + { -5896, 2378, -4787, -6947 }, + { -1193, -9117, -1237, -3114 }, + { 2783, -7108, -1575, -1447 }, + { -7334, -2062, -2212, 446 }, + { 6127, -2577, -315, -18 }, + { 9457, -1858, 102, 258 }, + { 7876, -4483, 2126, -538 }, + { -7172, -1795, -2069, 482 }, + { -7358, -2102, -2233, 440 }, + { -9170, -3509, -2674, -391 }, + { -2638, -2647, -1929, -1637 }, + { 1873, 9183, 1860, -5746 }, + { 9214, 1859, -1124, -2427 }, + { 13204, -3012, -4139, 1370 }, + { 12437, -4792, -256, 622 }, + { -2653, -1144, -3182, -6878 }, + { 9331, -1048, -828, 507 }, + { 1642, -620, -946, -4229 }, + { 4246, -7585, -533, -2259 }, + { -8988, -3891, -2807, 44 }, + { -2562, -2735, -1730, -1899 }, + { 3182, -483, -714, -1421 }, + { 7937, -3844, 2821, -1019 }, + { 10069, -2609, 314, 195 }, + { 8400, -3297, 1551, -155 }, + { -8529, -2775, -2432, -336 }, + { 9477, -1882, 108, 256 }, + { 75, -2241, -298, -6937 }, + { -9143, -4160, -2963, 5 }, + { -7270, -1958, -2156, 460 }, + { -2740, 3745, 5936, -1089 }, + { 8993, 1948, -683, -2704 }, + { 13101, -2835, -3854, 1055 }, + { 9543, -1961, 130, 250 }, + { 5272, -4270, 3124, -3157 }, + { -7696, -3383, -2907, -456 }, + { 7309, 2523, 434, -2461 }, + { 10275, -2867, 391, 172 }, + { 10940, -3721, 665, 97 }, + { 24, -310, -1262, 320 }, + { -8122, -2411, -2311, -271 }, + { -8511, -3067, -2337, 163 }, + { 326, -3846, 419, -933 }, + { 8895, 2194, -541, -2880 }, + { 12073, -1876, -2017, -601 }, + { 8729, -3423, 1674, -169 }, + { 12950, -3847, -3007, 1946 }, + { 10038, -2570, 302, 198 }, + { 9385, -2757, 1008, 41 }, + { -4720, -5006, -2852, -1161 }, + { 7869, -4326, 2135, -501 }, + { 2450, -8597, 1299, -2780 }, + { 10192, -2763, 360, 181 }, + { 11313, -4213, 833, 53 }, + { 10154, -2716, 345, 185 }, + { 9638, -1417, -737, 482 }, + { 3854, -4554, 2843, -3397 }, + { 6699, -5659, 2249, -1074 }, + { 11082, -3908, 728, 80 }, + { -1026, -9810, -805, -3462 }, + { 10396, -3746, 1367, -96 }, + { 10287, 988, -1915, -1437 }, + { 7953, 3878, -764, -3263 }, + { 12689, -3375, -3354, 2079 }, + { 6641, 3166, 231, -2089 }, + { -2348, -7354, -1944, -4122 }, + { 9290, -4039, 1885, -246 }, + { 4633, -6403, 1748, -1619 }, + { 11247, -4125, 802, 61 }, + { 9807, -2284, 219, 222 }, + { 9736, -1536, -706, 473 }, + { 8440, -3436, 1562, -176 }, + { 9307, -1021, -835, 509 }, + { 1698, -9025, 688, -3037 }, + { 10214, -2791, 368, 179 }, + { 8390, 3248, -758, -2989 }, + { 7201, 3316, 46, -2614 }, + { -88, -7809, -538, -4571 }, + { 6193, -5189, 2760, -1245 }, + { 12325, -1290, -3284, 253 }, + { 13064, -4075, -2824, 1877 }, + { 5333, 2999, 775, -1132 } +}; + + +/** + * Sony's HEVAG (High Efficiency VAG) ADPCM, used in PSVita games (hardware decoded). + * Evolution of the regular VAG (same flags and frames), uses 4 history samples and a bigger table. + * + * Original research and algorithm by id-daemon / daemon1. + */ +void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x10] = {0}; + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + int coef_index, shift_factor, flag; + int32_t hist1 = stream->adpcm_history1_32; + int32_t hist2 = stream->adpcm_history2_32; + int32_t hist3 = stream->adpcm_history3_32; + int32_t hist4 = stream->adpcm_history4_32; + + + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x10; + samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */ + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + coef_index = (frame[0] >> 4) & 0xf; + shift_factor = (frame[0] >> 0) & 0xf; + coef_index = ((frame[1] >> 0) & 0xf0) | coef_index; + flag = (frame[1] >> 0) & 0xf; /* same flags */ + + VGM_ASSERT_ONCE(coef_index > 127 || shift_factor > 12, "HEVAG: in+correct coefs/shift at %x\n", (uint32_t)frame_offset); + if (coef_index > 127) + coef_index = 127; /* ? */ + if (shift_factor > 12) + shift_factor = 9; /* ? */ + + shift_factor = 20 - shift_factor; + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t sample = 0; + + if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */ + uint8_t nibbles = frame[0x02 + i/2]; + + sample = (i&1 ? /* low nibble first */ + get_high_nibble_signed(nibbles): + get_low_nibble_signed(nibbles)) << shift_factor; /*scale*/ + sample = ((hist1 * hevag_coefs[coef_index][0] + + hist2 * hevag_coefs[coef_index][1] + + hist3 * hevag_coefs[coef_index][2] + + hist4 * hevag_coefs[coef_index][3]) >> 5) + sample; + sample >>= 8; + } + + outbuf[sample_count] = clamp16(sample); /*clamping*/ + sample_count += channelspacing; + + hist4 = hist3; + hist3 = hist2; + hist2 = hist1; + hist1 = sample; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_history2_32 = hist2; + stream->adpcm_history3_32 = hist3; + stream->adpcm_history4_32 = hist4; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/speex_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/speex_decoder.c new file mode 100644 index 000000000..be8b4f25e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/speex_decoder.c @@ -0,0 +1,188 @@ +#include "coding.h" +#include "coding_utils_samples.h" + +#ifdef VGM_USE_SPEEX +#include "speex/speex.h" + +#define SPEEX_MAX_FRAME_SIZE 0x100 /* frame sizes are stored in a byte */ +#define SPEEX_MAX_FRAME_SAMPLES 640 /* nb=160, wb/uwb=320*2 */ +#define SPEEX_CTL_OK 0 /* -1=request unknown, -2=invalid param */ +#define SPEEX_DECODE_OK 0 /* -1 for end of stream, -2 corrupt stream */ + + +/* opaque struct */ +struct speex_codec_data { + /* config */ + int channels; + int samples_discard; + int encoder_delay; + + uint8_t buf[SPEEX_MAX_FRAME_SIZE]; + uint8_t frame_size; + + int16_t* samples; + int frame_samples; + + /* frame state */ + s16buf_t sbuf; + + void* state; + SpeexBits bits; +}; + + +/* raw SPEEX */ +speex_codec_data* init_speex_ea(int channels) { + int res, sample_rate; + speex_codec_data* data = NULL; + + + data = calloc(1, sizeof(speex_codec_data)); + if (!data) goto fail; + + //TODO: EA uses N decoders, unknown layout (known samples are mono) + data->channels = channels; + if (channels != 1) + goto fail; + + /* Modes: narrowband=nb, wideband=wb, ultrawideband=uwb modes. + * EASpeex seem to always use uwb so use that for now until config is needed. + * Examples normally use &speex_*_mode but export seem problematic? */ + data->state = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_UWB)); + if (!data->state) goto fail; + + speex_bits_init(&data->bits); + + res = speex_decoder_ctl(data->state, SPEEX_GET_FRAME_SIZE, &data->frame_samples); + if (res != SPEEX_CTL_OK) goto fail; + + if (data->frame_samples > SPEEX_MAX_FRAME_SAMPLES) + goto fail; + + /* forced in EA's code, doesn't seem to affect decoding (all EAAC headers use this rate too) */ + sample_rate = 32000; + res = speex_decoder_ctl(data->state, SPEEX_SET_SAMPLING_RATE, &sample_rate); + if (res != SPEEX_CTL_OK) goto fail; + + /* default "latency" for EASpeex */ + data->encoder_delay = 509; + data->samples_discard = data->encoder_delay; + + data->samples = malloc(channels * data->frame_samples * sizeof(int16_t)); + if (!data->samples) goto fail; + + return data; + +fail: + free_speex(data); + return NULL; +} + + +static int decode_frame(speex_codec_data* data) { + int res; + + data->sbuf.samples = data->samples; + data->sbuf.channels = 1; + data->sbuf.filled = 0; + + speex_bits_read_from(&data->bits, (const char*)data->buf, data->frame_size); + + res = speex_decode_int(data->state, &data->bits, data->sbuf.samples); + if (res != SPEEX_DECODE_OK) goto fail; + + data->sbuf.filled = data->frame_samples; + + return 1; +fail: + return 0; +} + +/* for simple style speex (seen in EA-Speex and libspeex's sampledec.c) */ +static int read_frame(speex_codec_data* data, VGMSTREAMCHANNEL* stream) { + uint8_t bytes; + + data->frame_size = read_u8(stream->offset, stream->streamfile); + stream->offset += 0x01; + if (data->frame_size == 0) goto fail; + + bytes = read_streamfile(data->buf, stream->offset, data->frame_size, stream->streamfile); + stream->offset += data->frame_size; + if (bytes != data->frame_size) goto fail; + + return 1; +fail: + return 0; +} + +void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) { + VGMSTREAMCHANNEL* stream = &vgmstream->ch[0]; + speex_codec_data* data = vgmstream->codec_data; + int ok; + + + while (samples_to_do > 0) { + s16buf_t* sbuf = &data->sbuf; + + if (sbuf->filled <= 0) { + ok = read_frame(data, stream); + if (!ok) goto fail; + + ok = decode_frame(data); + if (!ok) goto fail; + } + + if (data->samples_discard) + s16buf_discard(&outbuf, sbuf, &data->samples_discard); + else + s16buf_consume(&outbuf, sbuf, &samples_to_do); + } + + return; + +fail: + /* on error just put some 0 samples */ + VGM_LOG("SPEEX: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, samples_to_do); + s16buf_silence(&outbuf, &samples_to_do, data->channels); +} + + +void reset_speex(speex_codec_data* data) { + int res; + + if (!data) return; + + res = speex_decoder_ctl(data->state, SPEEX_RESET_STATE, NULL); + if (res != SPEEX_CTL_OK) goto fail; + + data->sbuf.filled = 0; + data->samples_discard = data->encoder_delay; + + return; +fail: + return; /* ? */ +} + +void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample) { + speex_codec_data* data = vgmstream->codec_data; + if (!data) return; + + reset_speex(data); + data->samples_discard += num_sample; + + /* loop offsets are set during decode; force them to stream start so discard works */ + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; +} + +void free_speex(speex_codec_data* data) { + if (!data) + return; + + speex_decoder_destroy(data->state); + speex_bits_destroy(&data->bits); + + free(data->samples); + free(data); +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c index ddd628f7a..ab44efd16 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c @@ -1,588 +1,588 @@ -#include "coding.h" - - -/* Decodes Ubisoft ADPCM, a rather complex codec with 4-bit (usually music) and 6-bit (usually voices/sfx) - * mono or stereo modes, using multiple tables and temp step/delta values. - * - * Base reverse engineering by Zench: https://bitbucket.org/Zenchreal/decubisnd - * Original ASM MMX/intrinsics to C++ by sigsegv; adapted by bnnm; special thanks to Nicknine. - * - * Data always starts with a 0x30 main header (some games have extra data before too), then frames of - * fixed size: 0x34 ADPCM setup per channel, 1 subframe + 1 padding byte, then another subframe and 1 byte. - * Subframes have 1536 samples or less (like 1024), typical sizes are 0x600 for 4-bit or 0x480 for 6-bit. - * Last frame can contain only one subframe, with less codes than normal (may use padding). Nibbles/codes - * are packed as 32-bit LE with 6-bit or 4-bit codes for all channels (processes kinda like joint stereo). - */ - -#define UBI_CHANNELS_MIN 1 -#define UBI_CHANNELS_MAX 2 -#define UBI_SUBFRAMES_PER_FRAME_MAX 2 -#define UBI_CODES_PER_SUBFRAME_MAX 1536 /* for all channels */ -#define UBI_FRAME_SIZE_MAX (0x34 * UBI_CHANNELS_MAX + (UBI_CODES_PER_SUBFRAME_MAX * 6 / 8 + 0x1) * UBI_SUBFRAMES_PER_FRAME_MAX) -#define UBI_SAMPLES_PER_FRAME_MAX (UBI_CODES_PER_SUBFRAME_MAX * UBI_SUBFRAMES_PER_FRAME_MAX) - - -typedef struct { - uint32_t signature; - uint32_t sample_count; - uint32_t subframe_count; - uint32_t codes_per_subframe_last; - uint32_t codes_per_subframe; - uint32_t subframes_per_frame; - uint32_t sample_rate; - uint32_t unknown1c; - uint32_t unknown20; - uint32_t bits_per_sample; - uint32_t unknown28; - uint32_t channels; -} ubi_adpcm_header_data; - -typedef struct { - uint32_t signature; - int32_t step1; - int32_t next1; - int32_t next2; - - int16_t coef1; - int16_t coef2; - int16_t unused1; - int16_t unused2; - - int16_t mod1; - int16_t mod2; - int16_t mod3; - int16_t mod4; - - int16_t hist1; - int16_t hist2; - int16_t unused3; - int16_t unused4; - - int16_t delta1; - int16_t delta2; - int16_t delta3; - int16_t delta4; - - int16_t delta5; - int16_t unused5; -} ubi_adpcm_channel_data; - -struct ubi_adpcm_codec_data { - ubi_adpcm_header_data header; - ubi_adpcm_channel_data ch[UBI_CHANNELS_MAX]; - - off_t start_offset; - off_t offset; - int subframe_number; - - uint8_t frame[UBI_FRAME_SIZE_MAX]; - uint8_t codes[UBI_CODES_PER_SUBFRAME_MAX]; - int16_t samples[UBI_SAMPLES_PER_FRAME_MAX]; /* for all channels, saved in L-R-L-R form */ - - size_t samples_filled; - size_t samples_consumed; - size_t samples_to_discard; -}; - -/* *********************************************************************** */ - -static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data *data, off_t offset); -static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data *data); - -ubi_adpcm_codec_data *init_ubi_adpcm(STREAMFILE *sf, off_t offset, int channels) { - ubi_adpcm_codec_data *data = NULL; - - data = calloc(1, sizeof(ubi_adpcm_codec_data)); - if (!data) goto fail; - - if (!parse_header(sf, data, offset)) { - VGM_LOG("UBI ADPCM: wrong header\n"); - goto fail; - } - - if (data->header.channels != channels) { - VGM_LOG("UBI ADPCM: wrong number of channels: %i vs %i\n", data->header.channels, channels); - goto fail; - } - - data->start_offset = offset + 0x30; - data->offset = data->start_offset; - - return data; -fail: - free_ubi_adpcm(data); - return NULL; -} - -void decode_ubi_adpcm(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do) { - STREAMFILE* sf = vgmstream->ch[0].streamfile; - ubi_adpcm_codec_data *data = vgmstream->codec_data; - uint32_t channels = data->header.channels; - int samples_done = 0; - - - /* Ubi ADPCM frames are rather big, so we decode then copy to outbuf until done */ - while (samples_done < samples_to_do) { - if (data->samples_filled) { - int samples_to_get = data->samples_filled; - - if (data->samples_to_discard) { - /* discard samples for looping */ - if (samples_to_get > data->samples_to_discard) - samples_to_get = data->samples_to_discard; - data->samples_to_discard -= samples_to_get; - } - else { - /* get max samples and copy */ - if (samples_to_get > samples_to_do - samples_done) - samples_to_get = samples_to_do - samples_done; - - memcpy(outbuf + samples_done*channels, - data->samples + data->samples_consumed*channels, - samples_to_get*channels * sizeof(sample)); - samples_done += samples_to_get; - } - - /* mark consumed samples */ - data->samples_consumed += samples_to_get; - data->samples_filled -= samples_to_get; - } - else { - decode_frame(sf, data); - } - } -} - -void reset_ubi_adpcm(ubi_adpcm_codec_data *data) { - if (!data) return; - - data->offset = data->start_offset; - data->subframe_number = 0; -} - -void seek_ubi_adpcm(ubi_adpcm_codec_data *data, int32_t num_sample) { - if (!data) return; - - //todo improve by seeking to closest frame - - reset_ubi_adpcm(data); - data->samples_to_discard = num_sample; -} - -void free_ubi_adpcm(ubi_adpcm_codec_data *data) { - if (!data) - return; - free(data); -} - - -/* ************************************************************************ */ - -static void read_header_state(uint8_t *data, ubi_adpcm_header_data *header) { - header->signature = get_32bitLE(data + 0x00); - header->sample_count = get_32bitLE(data + 0x04); - header->subframe_count = get_32bitLE(data + 0x08); - header->codes_per_subframe_last= get_32bitLE(data + 0x0c); - header->codes_per_subframe = get_32bitLE(data + 0x10); - header->subframes_per_frame = get_32bitLE(data + 0x14); - header->sample_rate = get_32bitLE(data + 0x18); /* optional? */ - header->unknown1c = get_32bitLE(data + 0x1c); /* variable */ - header->unknown20 = get_32bitLE(data + 0x20); /* null? */ - header->bits_per_sample = get_32bitLE(data + 0x24); - header->unknown28 = get_32bitLE(data + 0x28); /* 1~3? */ - header->channels = get_32bitLE(data + 0x2c); -} - -static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data *data, off_t offset) { - uint8_t buf[0x30]; - size_t bytes; - - bytes = read_streamfile(buf, offset, 0x30, sf); - if (bytes != 0x30) goto fail; - - read_header_state(buf, &data->header); - - if (data->header.signature != 0x08) - goto fail; - if (data->header.codes_per_subframe_last > UBI_CODES_PER_SUBFRAME_MAX || - data->header.codes_per_subframe > UBI_CODES_PER_SUBFRAME_MAX) - goto fail; - if (data->header.subframes_per_frame != UBI_SUBFRAMES_PER_FRAME_MAX) - goto fail; - if (data->header.bits_per_sample != 4 && data->header.bits_per_sample != 6) - goto fail; - if (data->header.channels > UBI_CHANNELS_MAX || data->header.channels < UBI_CHANNELS_MIN) - goto fail; - - return 1; -fail: - return 0; -} - -/* *********************************************************************** */ - -int32_t adpcm6_table1[64] = { - -100000000, -369, -245, -133, -33, 56, 135, 207, - 275, 338, 395, 448, 499, 548, 593, 635, - 676, 717, 755, 791, 825, 858, 889, 919, - 948, 975, 1003, 1029, 1054, 1078, 1103, 1132, - /* probably unused (partly spilled from next table) */ - 1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056, - 5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040, - 20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832, - 51200, 56320, 63488, 67704, 75776, 89088, 102400, 0, -}; - -int32_t adpcm6_table2[64] = { - 1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056, - 5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040, - 20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832, - 51200, 56320, 63488, 67704, 75776, 89088, 102400, 0, - /* probably unused */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 2, 2, 3, 3, 4, - 4, 5, 5, 5, 6, 6, 6, 7, -}; - -int32_t adpcm4_table1[16] = { - -100000000, 8, 269, 425, 545, 645, 745, 850, - /* probably unused */ - -1082465976, 1058977874, 1068540887, 1072986849, 1075167887, 1076761723, 1078439444, 1203982336, -}; - -int32_t adpcm4_table2[16] = { - -1536, 2314, 5243, 8192, 14336, 25354, 45445, 143626, - /* probably unused */ - 0, 0, 0, 1, 1, 1, 3, 7, -}; - -int32_t delta_table[33+33] = { - 1024, 1031, 1053, 1076, 1099, 1123, 1148, 1172, - 1198, 1224, 1251, 1278, 1306, 1334, 1363, 1393, - 1423, 1454, 1485, 1518, 1551, 1584, 1619, 1654, - 1690, 1726, 1764, 1802, 1841, 1881, 1922, 1964, - 2007, - -1024,-1031,-1053,-1076,-1099,-1123,-1148,-1172, - -1198,-1224,-1251,-1278,-1306,-1334,-1363,-1393, - -1423,-1454,-1485,-1518,-1551,-1584,-1619,-1654, - -1690,-1726,-1764,-1802,-1841,-1881,-1922,-1964, - -2007 -}; - - -static int sign16(int16_t test) { - return (test < 0 ? -1 : 1); -} -static int sign32(int32_t test) { - return (test < 0 ? -1 : 1); -} -static int16_t absmax16(int16_t val, int16_t absmax) { - if (val < 0) { - if (val < -absmax) return -absmax; - } else { - if (val > absmax) return absmax; - } - return val; -} -static int32_t clamp_val(int32_t val, int32_t min, int32_t max) { - if (val < min) return min; - else if (val > max) return max; - else return val; -} - - -static int16_t expand_code_6bit(uint8_t code, ubi_adpcm_channel_data* state) { - int step0_index; - int32_t code_signed, step0_next, step0, delta0; - int32_t sample_new; - - code_signed = (int32_t)code - 31; /* convert coded 0..63 value to signed value, where 0=-31 .. 31=0 .. 63=32 */ - step0_index = abs(code_signed); /* 0..32, but should only go up to 31 */ - step0_next = adpcm6_table1[step0_index] + state->step1; - step0 = (state->step1 & 0xFFFF) * 246; - step0 = (step0 + adpcm6_table2[step0_index]) >> 8; - step0 = clamp_val(step0, 271, 2560); - - delta0 = 0; - if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) { - int delta0_index = ((step0_next >> 3) & 0x1F) + (code_signed < 0 ? 33 : 0); - int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31); - delta0 = (delta_table[delta0_index] << delta0_shift) >> 10; - } - - sample_new = (int16_t)(delta0 + state->delta1 + state->hist1); - - state->hist1 = sample_new; - - state->step1 = step0; - - state->delta1 = delta0; - - VGM_ASSERT(step0_index > 31, "UBI ADPCM: index over 31\n"); - return sample_new; -} - -/* may be simplified (masks, saturation, etc) as some values should never happen in the encoder */ -static int16_t expand_code_4bit(uint8_t code, ubi_adpcm_channel_data* state) { - int step0_index; - int32_t code_signed, step0_next, step0, delta0, next0, coef1_next, coef2_next; - int32_t sample_new; - - - code_signed = (int32_t)code - 7; /* convert coded 0..15 value to signed value, where 0=-7 .. 7=0 .. 15=8 */ - step0_index = abs(code_signed); /* 0..8, but should only go up to 7 */ - step0_next = adpcm4_table1[step0_index] + state->step1; - step0 = (state->step1 & 0xFFFF) * 246; - step0 = (step0 + adpcm4_table2[step0_index]) >> 8; - step0 = clamp_val(step0, 271, 2560); - - delta0 = 0; - if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) { - int delta0_index = ((step0_next >> 3) & 0x1F) + (code_signed < 0 ? 33 : 0); - int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31); - delta0 = (delta_table[delta0_index] << delta0_shift) >> 10; - } - - next0 = (int16_t)(( - (state->mod1 * state->delta1) + (state->mod2 * state->delta2) + - (state->mod3 * state->delta3) + (state->mod4 * state->delta4) ) >> 10); - - sample_new = ((state->coef1 * state->hist1) + (state->coef2 * state->hist2)) >> 10; - sample_new = (int16_t)(delta0 + next0 + sample_new); - - coef1_next = state->coef1 * 255; - coef2_next = state->coef2 * 254; - delta0 = (int16_t)delta0; - if (delta0 + next0 != 0) { - int32_t sign1, sign2, coef_delta; - - sign1 = sign32(delta0 + next0) * sign32(state->delta1 + state->next1); - sign2 = sign32(delta0 + next0) * sign32(state->delta2 + state->next2); - - coef_delta = (int16_t)((((sign1 * 3072) + coef1_next) >> 6) & ~0x3); - coef_delta = clamp16(clamp16(coef_delta + 30719) - 30719); //??? - coef_delta = clamp16(clamp16(coef_delta + -30720) - -30720); //??? - coef_delta = ((int16_t)(sign2 * 1024) - (int16_t)(sign1 * coef_delta)) * 2; - - coef1_next += sign1 * 3072; - coef2_next += coef_delta; - } - - - state->hist2 = state->hist1; - state->hist1 = sample_new; - - state->coef2 = absmax16((int16_t)(coef2_next >> 8), 768); - state->coef1 = absmax16((int16_t)(coef1_next >> 8), 960 - state->coef2); - - state->next2 = state->next1; - state->next1 = next0; - state->step1 = step0; - - state->delta5 = state->delta4; - state->delta4 = state->delta3; - state->delta3 = state->delta2; - state->delta2 = state->delta1; - state->delta1 = delta0; - - state->mod4 = clamp16(state->mod4 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta5)) >> 8; - state->mod3 = clamp16(state->mod3 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta4)) >> 8; - state->mod2 = clamp16(state->mod2 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta3)) >> 8; - state->mod1 = clamp16(state->mod1 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta2)) >> 8; - - VGM_ASSERT(step0_index > 7, "UBI ADPCM: index over 7\n"); - return sample_new; -} - -static void decode_subframe_mono(ubi_adpcm_channel_data* ch_state, uint8_t* codes, int16_t* samples, int code_count, int bps) { - int i; - int16_t (*expand_code)(uint8_t,ubi_adpcm_channel_data*) = NULL; - - if (bps == 6) - expand_code = expand_code_6bit; - else - expand_code = expand_code_4bit; - - for (i = 0; i < code_count; i++) { - samples[i] = expand_code(codes[i], ch_state); - } -} - -static void decode_subframe_stereo(ubi_adpcm_channel_data* ch0_state, ubi_adpcm_channel_data* ch1_state, uint8_t* codes, int16_t* samples, int code_count, int bps) { - int i; - int16_t (*expand_code)(uint8_t,ubi_adpcm_channel_data*) = NULL; - - if (bps == 6) - expand_code = expand_code_6bit; - else - expand_code = expand_code_4bit; - - for (i = 0; i < code_count; i += 8) { - samples[i + 0] = expand_code(codes[i + 0], ch0_state); - samples[i + 1] = expand_code(codes[i + 2], ch0_state); - samples[i + 2] = expand_code(codes[i + 4], ch0_state); - samples[i + 3] = expand_code(codes[i + 6], ch0_state); - samples[i + 4] = expand_code(codes[i + 1], ch1_state); - samples[i + 5] = expand_code(codes[i + 3], ch1_state); - samples[i + 6] = expand_code(codes[i + 5], ch1_state); - samples[i + 7] = expand_code(codes[i + 7], ch1_state); - } - - for (i = 0; i < code_count; i += 8) { - int16_t samples_old[8]; - memcpy(samples_old, samples, sizeof(samples_old)); - - samples[0] = clamp16(samples_old[0] + samples_old[4]); - samples[1] = clamp16(samples_old[0] - samples_old[4]); - samples[2] = clamp16(samples_old[1] + samples_old[5]); - samples[3] = clamp16(samples_old[1] - samples_old[5]); - - samples[4] = clamp16(samples_old[2] + samples_old[6]); - samples[5] = clamp16(samples_old[2] - samples_old[6]); - samples[6] = clamp16(samples_old[3] + samples_old[7]); - samples[7] = clamp16(samples_old[3] - samples_old[7]); - - samples += 8; - } -} - -/* unpack uint32 LE data into 4/6-bit codes: - * - for 4-bit, 32b contain 8 codes - * ex. uint8_t 0x98576787DB5725A8... becomes 0x87675798 LE = 8 7 6 7 5 7 9 8 ... - * - for 6-bit, 32b contain ~5 codes with leftover bits used in following 32b - * ex. uint8_t 0x98576787DB5725A8... becomes 0x87675798 LE = 100001 110110 011101 010111 100110 00, - * 0xA82557DB LE = 1010 100000 100101 010101 111101 1011 ... (where last 00 | first 1010 = 001010), etc - * Codes aren't signed but rather have a particular meaning (see decoding). - */ -void unpack_codes(uint8_t *data, uint8_t* codes, int code_count, int bps) { - int i; - size_t pos = 0; - uint64_t bits = 0, input = 0; - const uint64_t mask = (bps == 6) ? 0x3f : 0x0f; - - for (i = 0; i < code_count; i++) { - if (bits < bps) { - uint32_t source32le = (uint32_t)get_32bitLE(data + pos); - pos += 0x04; - - input = (input << 32) | (uint64_t)source32le; - bits += 32; - } - - bits -= bps; - codes[i] = (uint8_t)((input >> bits) & mask); - } -} - -static void read_channel_state(uint8_t *data, ubi_adpcm_channel_data *ch) { - /* ADPCM frame state, some fields are unused and contain repeated garbage in all frames but - * probably exist for padding (original code uses MMX to operate in multiple 16b at the same time) - * or reserved for other bit modes */ - - ch->signature = get_32bitLE(data + 0x00); - ch->step1 = get_32bitLE(data + 0x04); - ch->next1 = get_32bitLE(data + 0x08); - ch->next2 = get_32bitLE(data + 0x0c); - - ch->coef1 = get_16bitLE(data + 0x10); - ch->coef2 = get_16bitLE(data + 0x12); - ch->unused1 = get_16bitLE(data + 0x14); - ch->unused2 = get_16bitLE(data + 0x16); - ch->mod1 = get_16bitLE(data + 0x18); - ch->mod2 = get_16bitLE(data + 0x1a); - ch->mod3 = get_16bitLE(data + 0x1c); - ch->mod4 = get_16bitLE(data + 0x1e); - - ch->hist1 = get_16bitLE(data + 0x20); - ch->hist2 = get_16bitLE(data + 0x22); - ch->unused3 = get_16bitLE(data + 0x24); - ch->unused4 = get_16bitLE(data + 0x26); - ch->delta1 = get_16bitLE(data + 0x28); - ch->delta2 = get_16bitLE(data + 0x2a); - ch->delta3 = get_16bitLE(data + 0x2c); - ch->delta4 = get_16bitLE(data + 0x2e); - - ch->delta5 = get_16bitLE(data + 0x30); - ch->unused5 = get_16bitLE(data + 0x32); - - VGM_ASSERT(ch->signature != 0x02, "UBI ADPCM: incorrect channel header\n"); - VGM_ASSERT(ch->unused3 != 0x00, "UBI ADPCM: found unused3 used\n"); - VGM_ASSERT(ch->unused4 != 0x00, "UBI ADPCM: found unused4 used\n"); -} - -static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data *data) { - int code_count_a, code_count_b; - size_t subframe_size_a, subframe_size_b, frame_size, bytes; - int bps = data->header.bits_per_sample; - int channels = data->header.channels; - - - /* last frame is shorter (subframe A or B may not exist), avoid over-reads in bigfiles */ - if (data->subframe_number + 1 == data->header.subframe_count) { - code_count_a = data->header.codes_per_subframe_last; - code_count_b = 0; - } else if (data->subframe_number + 2 == data->header.subframe_count) { - code_count_a = data->header.codes_per_subframe; - code_count_b = data->header.codes_per_subframe_last; - } else { - code_count_a = data->header.codes_per_subframe; - code_count_b = data->header.codes_per_subframe; - } - - subframe_size_a = (bps * code_count_a / 8); - if (subframe_size_a) subframe_size_a += 0x01; - subframe_size_b = (bps * code_count_b / 8); - if (subframe_size_b) subframe_size_b += 0x01; - - frame_size = 0x34 * channels + subframe_size_a + subframe_size_b; - - //todo check later games (ex. Myst IV) if they handle this - /* last frame can have an odd number of codes, with data ending not aligned to 32b, - * but RE'd code unpacking and stereo decoding always assume to be aligned, causing clicks in some cases - * (if data ends in 0xEE it'll try to do 0x000000EE, but only unpack codes 0 0, thus ignoring actual last 2) */ - //memset(data->frame, 0, sizeof(data->frame)); - //memset(data->codes, 0, sizeof(data->codes)); - //memset(data->samples, 0, sizeof(data->samples)); - - - bytes = read_streamfile(data->frame, data->offset, frame_size, sf); - if (bytes != frame_size) { - VGM_LOG("UBI ADPCM: wrong bytes read %x vs %x at %lx\n", bytes, frame_size, data->offset); - //goto fail; //? - } - - if (channels == 1) { - read_channel_state(data->frame + 0x00, &data->ch[0]); - - unpack_codes(data->frame + 0x34, data->codes, code_count_a, bps); - decode_subframe_mono(&data->ch[0], data->codes, &data->samples[0], code_count_a, bps); - - unpack_codes(data->frame + 0x34 + subframe_size_a, data->codes, code_count_b, bps); - decode_subframe_mono(&data->ch[0], data->codes, &data->samples[code_count_a], code_count_b, bps); - } - else if (channels == 2) { - read_channel_state(data->frame + 0x00, &data->ch[0]); - read_channel_state(data->frame + 0x34, &data->ch[1]); - - unpack_codes(data->frame + 0x68, data->codes, code_count_a, bps); - decode_subframe_stereo(&data->ch[0], &data->ch[1], data->codes, &data->samples[0], code_count_a, bps); - - unpack_codes(data->frame + 0x68 + subframe_size_a, data->codes, code_count_b, bps); - decode_subframe_stereo(&data->ch[0], &data->ch[1], data->codes, &data->samples[code_count_a], code_count_b, bps); - } - - /* frame done */ - data->offset += frame_size; - data->subframe_number += 2; - data->samples_consumed = 0; - data->samples_filled = (code_count_a + code_count_b) / channels; -} - - -int ubi_adpcm_get_samples(ubi_adpcm_codec_data *data) { - if (!data) - return 0; - - return data->header.sample_count / data->header.channels; -} +#include "coding.h" + + +/* Decodes Ubisoft ADPCM, a rather complex codec with 4-bit (usually music) and 6-bit (usually voices/sfx) + * mono or stereo modes, using multiple tables and temp step/delta values. + * + * Base reverse engineering by Zench: https://bitbucket.org/Zenchreal/decubisnd + * Original 4-bit mode ASM MMX/intrinsics to C++ by sigsegv; adapted + 6-bit mode by bnnm; special thanks to Nicknine. + * + * Data always starts with a 0x30 main header (some games have extra data before too), then frames of + * fixed size: 0x34 ADPCM setup per channel, 1 subframe + 1 padding byte, then another subframe and 1 byte. + * Subframes have 1536 samples or less (like 1024), typical sizes are 0x600 for 4-bit or 0x480 for 6-bit. + * Last frame can contain only one subframe, with less codes than normal (may use padding). Nibbles/codes + * are packed as 32-bit LE with 6-bit or 4-bit codes for all channels (processes kinda like joint stereo). + */ + +#define UBI_CHANNELS_MIN 1 +#define UBI_CHANNELS_MAX 2 +#define UBI_SUBFRAMES_PER_FRAME_MAX 2 +#define UBI_CODES_PER_SUBFRAME_MAX 1536 /* for all channels */ +#define UBI_FRAME_SIZE_MAX (0x34 * UBI_CHANNELS_MAX + (UBI_CODES_PER_SUBFRAME_MAX * 6 / 8 + 0x1) * UBI_SUBFRAMES_PER_FRAME_MAX) +#define UBI_SAMPLES_PER_FRAME_MAX (UBI_CODES_PER_SUBFRAME_MAX * UBI_SUBFRAMES_PER_FRAME_MAX) + + +typedef struct { + uint32_t signature; + uint32_t sample_count; + uint32_t subframe_count; + uint32_t codes_per_subframe_last; + uint32_t codes_per_subframe; + uint32_t subframes_per_frame; + uint32_t sample_rate; + uint32_t unknown1c; + uint32_t unknown20; + uint32_t bits_per_sample; + uint32_t unknown28; + uint32_t channels; +} ubi_adpcm_header_data; + +typedef struct { + uint32_t signature; + int32_t step1; + int32_t next1; + int32_t next2; + + int16_t coef1; + int16_t coef2; + int16_t unused1; + int16_t unused2; + + int16_t mod1; + int16_t mod2; + int16_t mod3; + int16_t mod4; + + int16_t hist1; + int16_t hist2; + int16_t unused3; + int16_t unused4; + + int16_t delta1; + int16_t delta2; + int16_t delta3; + int16_t delta4; + + int16_t delta5; + int16_t unused5; +} ubi_adpcm_channel_data; + +struct ubi_adpcm_codec_data { + ubi_adpcm_header_data header; + ubi_adpcm_channel_data ch[UBI_CHANNELS_MAX]; + + off_t start_offset; + off_t offset; + int subframe_number; + + uint8_t frame[UBI_FRAME_SIZE_MAX]; + uint8_t codes[UBI_CODES_PER_SUBFRAME_MAX]; + int16_t samples[UBI_SAMPLES_PER_FRAME_MAX]; /* for all channels, saved in L-R-L-R form */ + + size_t samples_filled; + size_t samples_consumed; + size_t samples_to_discard; +}; + +/* *********************************************************************** */ + +static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset); +static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data* data); + +ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, off_t offset, int channels) { + ubi_adpcm_codec_data* data = NULL; + + data = calloc(1, sizeof(ubi_adpcm_codec_data)); + if (!data) goto fail; + + if (!parse_header(sf, data, offset)) { + VGM_LOG("UBI ADPCM: wrong header\n"); + goto fail; + } + + if (data->header.channels != channels) { + VGM_LOG("UBI ADPCM: wrong number of channels: %i vs %i\n", data->header.channels, channels); + goto fail; + } + + data->start_offset = offset + 0x30; + data->offset = data->start_offset; + + return data; +fail: + free_ubi_adpcm(data); + return NULL; +} + +void decode_ubi_adpcm(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) { + STREAMFILE* sf = vgmstream->ch[0].streamfile; + ubi_adpcm_codec_data* data = vgmstream->codec_data; + uint32_t channels = data->header.channels; + int samples_done = 0; + + + /* Ubi ADPCM frames are rather big, so we decode then copy to outbuf until done */ + while (samples_done < samples_to_do) { + if (data->samples_filled) { + int samples_to_get = data->samples_filled; + + if (data->samples_to_discard) { + /* discard samples for looping */ + if (samples_to_get > data->samples_to_discard) + samples_to_get = data->samples_to_discard; + data->samples_to_discard -= samples_to_get; + } + else { + /* get max samples and copy */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + + memcpy(outbuf + samples_done*channels, + data->samples + data->samples_consumed*channels, + samples_to_get*channels * sizeof(sample)); + samples_done += samples_to_get; + } + + /* mark consumed samples */ + data->samples_consumed += samples_to_get; + data->samples_filled -= samples_to_get; + } + else { + decode_frame(sf, data); + } + } +} + +void reset_ubi_adpcm(ubi_adpcm_codec_data* data) { + if (!data) return; + + data->offset = data->start_offset; + data->subframe_number = 0; +} + +void seek_ubi_adpcm(ubi_adpcm_codec_data* data, int32_t num_sample) { + if (!data) return; + + //todo improve by seeking to closest frame + + reset_ubi_adpcm(data); + data->samples_to_discard = num_sample; +} + +void free_ubi_adpcm(ubi_adpcm_codec_data *data) { + if (!data) + return; + free(data); +} + + +/* ************************************************************************ */ + +static void read_header_state(uint8_t* data, ubi_adpcm_header_data* header) { + header->signature = get_32bitLE(data + 0x00); + header->sample_count = get_32bitLE(data + 0x04); + header->subframe_count = get_32bitLE(data + 0x08); + header->codes_per_subframe_last= get_32bitLE(data + 0x0c); + header->codes_per_subframe = get_32bitLE(data + 0x10); + header->subframes_per_frame = get_32bitLE(data + 0x14); + header->sample_rate = get_32bitLE(data + 0x18); /* optional? */ + header->unknown1c = get_32bitLE(data + 0x1c); /* variable */ + header->unknown20 = get_32bitLE(data + 0x20); /* null? */ + header->bits_per_sample = get_32bitLE(data + 0x24); + header->unknown28 = get_32bitLE(data + 0x28); /* 1~3? */ + header->channels = get_32bitLE(data + 0x2c); +} + +static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset) { + uint8_t buf[0x30]; + size_t bytes; + + bytes = read_streamfile(buf, offset, 0x30, sf); + if (bytes != 0x30) goto fail; + + read_header_state(buf, &data->header); + + if (data->header.signature != 0x08) + goto fail; + if (data->header.codes_per_subframe_last > UBI_CODES_PER_SUBFRAME_MAX || + data->header.codes_per_subframe > UBI_CODES_PER_SUBFRAME_MAX) + goto fail; + if (data->header.subframes_per_frame != UBI_SUBFRAMES_PER_FRAME_MAX) + goto fail; + if (data->header.bits_per_sample != 4 && data->header.bits_per_sample != 6) + goto fail; + if (data->header.channels > UBI_CHANNELS_MAX || data->header.channels < UBI_CHANNELS_MIN) + goto fail; + + return 1; +fail: + return 0; +} + +/* *********************************************************************** */ + +static const int32_t adpcm6_table1[64] = { + -100000000, -369, -245, -133, -33, 56, 135, 207, + 275, 338, 395, 448, 499, 548, 593, 635, + 676, 717, 755, 791, 825, 858, 889, 919, + 948, 975, 1003, 1029, 1054, 1078, 1103, 1132, + /* probably unused (partly spilled from next table) */ + 1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056, + 5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040, + 20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832, + 51200, 56320, 63488, 67704, 75776, 89088, 102400, 0, +}; + +static const int32_t adpcm6_table2[64] = { + 1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056, + 5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040, + 20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832, + 51200, 56320, 63488, 67704, 75776, 89088, 102400, 0, + /* probably unused */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, 5, 6, 6, 6, 7, +}; + +static const int32_t adpcm4_table1[16] = { + -100000000, 8, 269, 425, 545, 645, 745, 850, + /* probably unused */ + -1082465976, 1058977874, 1068540887, 1072986849, 1075167887, 1076761723, 1078439444, 1203982336, +}; + +static const int32_t adpcm4_table2[16] = { + -1536, 2314, 5243, 8192, 14336, 25354, 45445, 143626, + /* probably unused */ + 0, 0, 0, 1, 1, 1, 3, 7, +}; + +static const int32_t delta_table[33+33] = { + 1024, 1031, 1053, 1076, 1099, 1123, 1148, 1172, + 1198, 1224, 1251, 1278, 1306, 1334, 1363, 1393, + 1423, 1454, 1485, 1518, 1551, 1584, 1619, 1654, + 1690, 1726, 1764, 1802, 1841, 1881, 1922, 1964, + 2007, + -1024,-1031,-1053,-1076,-1099,-1123,-1148,-1172, + -1198,-1224,-1251,-1278,-1306,-1334,-1363,-1393, + -1423,-1454,-1485,-1518,-1551,-1584,-1619,-1654, + -1690,-1726,-1764,-1802,-1841,-1881,-1922,-1964, + -2007 +}; + + +static int sign16(int16_t test) { + return (test < 0 ? -1 : 1); +} +static int sign32(int32_t test) { + return (test < 0 ? -1 : 1); +} +static int16_t absmax16(int16_t val, int16_t absmax) { + if (val < 0) { + if (val < -absmax) return -absmax; + } else { + if (val > absmax) return absmax; + } + return val; +} +static int32_t clamp_val(int32_t val, int32_t min, int32_t max) { + if (val < min) return min; + else if (val > max) return max; + else return val; +} + + +static int16_t expand_code_6bit(uint8_t code, ubi_adpcm_channel_data* state) { + int step0_index; + int32_t code_signed, step0_next, step0, delta0; + int32_t sample_new; + + code_signed = (int32_t)code - 31; /* convert coded 0..63 value to signed value, where 0=-31 .. 31=0 .. 63=32 */ + step0_index = abs(code_signed); /* 0..32, but should only go up to 31 */ + step0_next = adpcm6_table1[step0_index] + state->step1; + step0 = (state->step1 & 0xFFFF) * 246; + step0 = (step0 + adpcm6_table2[step0_index]) >> 8; + step0 = clamp_val(step0, 271, 2560); + + delta0 = 0; + if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) { + int delta0_index = ((step0_next >> 3) & 0x1F) + (code_signed < 0 ? 33 : 0); + int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31); + delta0 = (delta_table[delta0_index] << delta0_shift) >> 10; + } + + sample_new = (int16_t)(delta0 + state->delta1 + state->hist1); + + state->hist1 = sample_new; + + state->step1 = step0; + + state->delta1 = delta0; + + VGM_ASSERT(step0_index > 31, "UBI ADPCM: index over 31\n"); + return sample_new; +} + +/* may be simplified (masks, saturation, etc) as some values should never happen in the encoder */ +static int16_t expand_code_4bit(uint8_t code, ubi_adpcm_channel_data* state) { + int step0_index; + int32_t code_signed, step0_next, step0, delta0, next0, coef1_next, coef2_next; + int32_t sample_new; + + + code_signed = (int32_t)code - 7; /* convert coded 0..15 value to signed value, where 0=-7 .. 7=0 .. 15=8 */ + step0_index = abs(code_signed); /* 0..8, but should only go up to 7 */ + step0_next = adpcm4_table1[step0_index] + state->step1; + step0 = (state->step1 & 0xFFFF) * 246; + step0 = (step0 + adpcm4_table2[step0_index]) >> 8; + step0 = clamp_val(step0, 271, 2560); + + delta0 = 0; + if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) { + int delta0_index = ((step0_next >> 3) & 0x1F) + (code_signed < 0 ? 33 : 0); + int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31); + delta0 = (delta_table[delta0_index] << delta0_shift) >> 10; + } + + next0 = (int16_t)(( + (state->mod1 * state->delta1) + (state->mod2 * state->delta2) + + (state->mod3 * state->delta3) + (state->mod4 * state->delta4) ) >> 10); + + sample_new = ((state->coef1 * state->hist1) + (state->coef2 * state->hist2)) >> 10; + sample_new = (int16_t)(delta0 + next0 + sample_new); + + coef1_next = state->coef1 * 255; + coef2_next = state->coef2 * 254; + delta0 = (int16_t)delta0; + if (delta0 + next0 != 0) { + int32_t sign1, sign2, coef_delta; + + sign1 = sign32(delta0 + next0) * sign32(state->delta1 + state->next1); + sign2 = sign32(delta0 + next0) * sign32(state->delta2 + state->next2); + + coef_delta = (int16_t)((((sign1 * 3072) + coef1_next) >> 6) & ~0x3); + coef_delta = clamp16(clamp16(coef_delta + 30719) - 30719); //??? + coef_delta = clamp16(clamp16(coef_delta + -30720) - -30720); //??? + coef_delta = ((int16_t)(sign2 * 1024) - (int16_t)(sign1 * coef_delta)) * 2; + + coef1_next += sign1 * 3072; + coef2_next += coef_delta; + } + + + state->hist2 = state->hist1; + state->hist1 = sample_new; + + state->coef2 = absmax16((int16_t)(coef2_next >> 8), 768); + state->coef1 = absmax16((int16_t)(coef1_next >> 8), 960 - state->coef2); + + state->next2 = state->next1; + state->next1 = next0; + state->step1 = step0; + + state->delta5 = state->delta4; + state->delta4 = state->delta3; + state->delta3 = state->delta2; + state->delta2 = state->delta1; + state->delta1 = delta0; + + state->mod4 = clamp16(state->mod4 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta5)) >> 8; + state->mod3 = clamp16(state->mod3 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta4)) >> 8; + state->mod2 = clamp16(state->mod2 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta3)) >> 8; + state->mod1 = clamp16(state->mod1 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta2)) >> 8; + + VGM_ASSERT(step0_index > 7, "UBI ADPCM: index over 7\n"); + return sample_new; +} + +static void decode_subframe_mono(ubi_adpcm_channel_data* ch_state, uint8_t* codes, int16_t* samples, int code_count, int bps) { + int i; + int16_t (*expand_code)(uint8_t,ubi_adpcm_channel_data*) = NULL; + + if (bps == 6) + expand_code = expand_code_6bit; + else + expand_code = expand_code_4bit; + + for (i = 0; i < code_count; i++) { + samples[i] = expand_code(codes[i], ch_state); + } +} + +static void decode_subframe_stereo(ubi_adpcm_channel_data* ch0_state, ubi_adpcm_channel_data* ch1_state, uint8_t* codes, int16_t* samples, int code_count, int bps) { + int i; + int16_t (*expand_code)(uint8_t,ubi_adpcm_channel_data*) = NULL; + + if (bps == 6) + expand_code = expand_code_6bit; + else + expand_code = expand_code_4bit; + + for (i = 0; i < code_count; i += 8) { + samples[i + 0] = expand_code(codes[i + 0], ch0_state); + samples[i + 1] = expand_code(codes[i + 2], ch0_state); + samples[i + 2] = expand_code(codes[i + 4], ch0_state); + samples[i + 3] = expand_code(codes[i + 6], ch0_state); + samples[i + 4] = expand_code(codes[i + 1], ch1_state); + samples[i + 5] = expand_code(codes[i + 3], ch1_state); + samples[i + 6] = expand_code(codes[i + 5], ch1_state); + samples[i + 7] = expand_code(codes[i + 7], ch1_state); + } + + for (i = 0; i < code_count; i += 8) { + int16_t samples_old[8]; + memcpy(samples_old, samples, sizeof(samples_old)); + + samples[0] = clamp16(samples_old[0] + samples_old[4]); + samples[1] = clamp16(samples_old[0] - samples_old[4]); + samples[2] = clamp16(samples_old[1] + samples_old[5]); + samples[3] = clamp16(samples_old[1] - samples_old[5]); + + samples[4] = clamp16(samples_old[2] + samples_old[6]); + samples[5] = clamp16(samples_old[2] - samples_old[6]); + samples[6] = clamp16(samples_old[3] + samples_old[7]); + samples[7] = clamp16(samples_old[3] - samples_old[7]); + + samples += 8; + } +} + +/* unpack uint32 LE data into 4/6-bit codes: + * - for 4-bit, 32b contain 8 codes + * ex. uint8_t 0x98576787DB5725A8... becomes 0x87675798 LE = 8 7 6 7 5 7 9 8 ... + * - for 6-bit, 32b contain ~5 codes with leftover bits used in following 32b + * ex. uint8_t 0x98576787DB5725A8... becomes 0x87675798 LE = 100001 110110 011101 010111 100110 00, + * 0xA82557DB LE = 1010 100000 100101 010101 111101 1011 ... (where last 00 | first 1010 = 001010), etc + * Codes aren't signed but rather have a particular meaning (see decoding). + */ +void unpack_codes(uint8_t* data, uint8_t* codes, int code_count, int bps) { + int i; + size_t pos = 0; + uint64_t bits = 0, input = 0; + const uint64_t mask = (bps == 6) ? 0x3f : 0x0f; + + for (i = 0; i < code_count; i++) { + if (bits < bps) { + uint32_t source32le = (uint32_t)get_32bitLE(data + pos); + pos += 0x04; + + input = (input << 32) | (uint64_t)source32le; + bits += 32; + } + + bits -= bps; + codes[i] = (uint8_t)((input >> bits) & mask); + } +} + +static void read_channel_state(uint8_t* data, ubi_adpcm_channel_data* ch) { + /* ADPCM frame state, some fields are unused and contain repeated garbage in all frames but + * probably exist for padding (original code uses MMX to operate in multiple 16b at the same time) + * or reserved for other bit modes */ + + ch->signature = get_32bitLE(data + 0x00); + ch->step1 = get_32bitLE(data + 0x04); + ch->next1 = get_32bitLE(data + 0x08); + ch->next2 = get_32bitLE(data + 0x0c); + + ch->coef1 = get_16bitLE(data + 0x10); + ch->coef2 = get_16bitLE(data + 0x12); + ch->unused1 = get_16bitLE(data + 0x14); + ch->unused2 = get_16bitLE(data + 0x16); + ch->mod1 = get_16bitLE(data + 0x18); + ch->mod2 = get_16bitLE(data + 0x1a); + ch->mod3 = get_16bitLE(data + 0x1c); + ch->mod4 = get_16bitLE(data + 0x1e); + + ch->hist1 = get_16bitLE(data + 0x20); + ch->hist2 = get_16bitLE(data + 0x22); + ch->unused3 = get_16bitLE(data + 0x24); + ch->unused4 = get_16bitLE(data + 0x26); + ch->delta1 = get_16bitLE(data + 0x28); + ch->delta2 = get_16bitLE(data + 0x2a); + ch->delta3 = get_16bitLE(data + 0x2c); + ch->delta4 = get_16bitLE(data + 0x2e); + + ch->delta5 = get_16bitLE(data + 0x30); + ch->unused5 = get_16bitLE(data + 0x32); + + VGM_ASSERT(ch->signature != 0x02, "UBI ADPCM: incorrect channel header\n"); + VGM_ASSERT(ch->unused3 != 0x00, "UBI ADPCM: found unused3 used\n"); + VGM_ASSERT(ch->unused4 != 0x00, "UBI ADPCM: found unused4 used\n"); +} + +static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data* data) { + int code_count_a, code_count_b; + size_t subframe_size_a, subframe_size_b, frame_size, bytes; + int bps = data->header.bits_per_sample; + int channels = data->header.channels; + + + /* last frame is shorter (subframe A or B may not exist), avoid over-reads in bigfiles */ + if (data->subframe_number + 1 == data->header.subframe_count) { + code_count_a = data->header.codes_per_subframe_last; + code_count_b = 0; + } else if (data->subframe_number + 2 == data->header.subframe_count) { + code_count_a = data->header.codes_per_subframe; + code_count_b = data->header.codes_per_subframe_last; + } else { + code_count_a = data->header.codes_per_subframe; + code_count_b = data->header.codes_per_subframe; + } + + subframe_size_a = (bps * code_count_a / 8); + if (subframe_size_a) subframe_size_a += 0x01; + subframe_size_b = (bps * code_count_b / 8); + if (subframe_size_b) subframe_size_b += 0x01; + + frame_size = 0x34 * channels + subframe_size_a + subframe_size_b; + + //todo check later games (ex. Myst IV) if they handle this + /* last frame can have an odd number of codes, with data ending not aligned to 32b, + * but RE'd code unpacking and stereo decoding always assume to be aligned, causing clicks in some cases + * (if data ends in 0xEE it'll try to do 0x000000EE, but only unpack codes 0 0, thus ignoring actual last 2) */ + //memset(data->frame, 0, sizeof(data->frame)); + //memset(data->codes, 0, sizeof(data->codes)); + //memset(data->samples, 0, sizeof(data->samples)); + + + bytes = read_streamfile(data->frame, data->offset, frame_size, sf); + if (bytes != frame_size) { + VGM_LOG("UBI ADPCM: wrong bytes read %x vs %x at %lx\n", bytes, frame_size, data->offset); + //goto fail; //? + } + + if (channels == 1) { + read_channel_state(data->frame + 0x00, &data->ch[0]); + + unpack_codes(data->frame + 0x34, data->codes, code_count_a, bps); + decode_subframe_mono(&data->ch[0], data->codes, &data->samples[0], code_count_a, bps); + + unpack_codes(data->frame + 0x34 + subframe_size_a, data->codes, code_count_b, bps); + decode_subframe_mono(&data->ch[0], data->codes, &data->samples[code_count_a], code_count_b, bps); + } + else if (channels == 2) { + read_channel_state(data->frame + 0x00, &data->ch[0]); + read_channel_state(data->frame + 0x34, &data->ch[1]); + + unpack_codes(data->frame + 0x68, data->codes, code_count_a, bps); + decode_subframe_stereo(&data->ch[0], &data->ch[1], data->codes, &data->samples[0], code_count_a, bps); + + unpack_codes(data->frame + 0x68 + subframe_size_a, data->codes, code_count_b, bps); + decode_subframe_stereo(&data->ch[0], &data->ch[1], data->codes, &data->samples[code_count_a], code_count_b, bps); + } + + /* frame done */ + data->offset += frame_size; + data->subframe_number += 2; + data->samples_consumed = 0; + data->samples_filled = (code_count_a + code_count_b) / channels; +} + + +int ubi_adpcm_get_samples(ubi_adpcm_codec_data* data) { + if (!data) + return 0; + + return data->header.sample_count / data->header.channels; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c index f82266391..6aa4fffd0 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c @@ -1,162 +1,162 @@ -#include "coding.h" -#include "../util.h" - -// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do -// XA coefs int math in different ways (see comments below), not 100% accurate. -// May be implemented like the SNES/SPC700 BRR. - -/* XA ADPCM gain values */ -#if 0 -static const float K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 }; -static const float K1[4] = { 0.0, 0.0, -0.8125, -0.859375 }; -#endif -/* K0/1 floats to int, -K*2^10 = -K*(1<<10) = -K*1024 */ -static const int IK0[4] = { 0, -960, -1840, -1568 }; -static const int IK1[4] = { 0, 0, 832, 880 }; - -/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs. - * The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new. - * - * Decoding is defined in diagrams, roughly as: - * pcm = clamp( signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 ) - * - Range (12-range=shift) and filter index are renewed every ~28 samples. - * - nibble is expanded to a signed 16b sample, reimplemented as: - * short sample = ((nibble << 12) & 0xf000) >> shift - * or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N) - * - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding: - * (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N - * (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N - * sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N - * sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N - * etc - * (rounding differences should be inaudible, so public implementations may be approximations) - * - * Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly - * differently, maybe using one of the above methods in software/CPU, but in XA's case may be done - * like the SNES/SPC700 BRR, with specific per-filter ops. - * int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240 - * PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this. - * - * XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently - * are supported by the CD hardware and will play if found. - * - * Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf - * BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples - * (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316 - */ - -void decode_xa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - uint8_t frame[0x80] = {0}; - off_t frame_offset; - int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0; - size_t bytes_per_frame, samples_per_frame; - int32_t hist1 = stream->adpcm_history1_32; - int32_t hist2 = stream->adpcm_history2_32; - - - /* data layout (mono): - * - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames - * (handled externally, this decoder only gets frames) - * - a frame ("sound group") is divided into 8 subframes ("sound unit"), with - * subframe headers ("sound parameters") first then subframe nibbles ("sound data") - * - headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header) - * (repeats may be for error correction, though probably unused) - * - nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc - * (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc) - * - * stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R - * - * example: - * subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c - * subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c - * subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d - * ... - * subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f - */ - - /* external interleave (fixed size), mono/stereo */ - bytes_per_frame = 0x80; - samples_per_frame = 28*8 / channelspacing; - frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; - - /* parse frame header */ - frame_offset = stream->offset + bytes_per_frame * frames_in; - read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - - VGM_ASSERT(get_32bitBE(frame+0x0) != get_32bitBE(frame+0x4) || get_32bitBE(frame+0x8) != get_32bitBE(frame+0xC), - "bad frames at %x\n", (uint32_t)frame_offset); - - - /* decode subframes */ - for (i = 0; i < 8 / channelspacing; i++) { - int32_t coef1, coef2; - uint8_t coef_index, shift_factor; - - /* parse current subframe (sound unit)'s header (sound parameters) */ - sp_pos = 0x04 + i*channelspacing + channel; - coef_index = (frame[sp_pos] >> 4) & 0xf; - shift_factor = (frame[sp_pos] >> 0) & 0xf; - - VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos); - if (coef_index > 4) - coef_index = 0; /* only 4 filters are used, rest is apparently 0 */ - if (shift_factor > 12) - shift_factor = 9; /* supposedly, from Nocash PSX docs */ - - coef1 = IK0[coef_index]; - coef2 = IK1[coef_index]; - - - /* decode subframe nibbles */ - for(j = 0; j < 28; j++) { - uint8_t nibbles; - int32_t new_sample; - - int su_pos = (channelspacing==1) ? - 0x10 + j*0x04 + (i/2) : /* mono */ - 0x10 + j*0x04 + i; /* stereo */ - int get_high_nibble = (channelspacing==1) ? - (i&1) : /* mono (even subframes = low, off subframes = high) */ - (channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */ - - /* skip half decodes to make sure hist isn't touched (kinda hack-ish) */ - if (!(sample_count >= first_sample && samples_done < samples_to_do)) { - sample_count++; - continue; - } - - nibbles = frame[su_pos]; - - new_sample = get_high_nibble ? - (nibbles >> 4) & 0x0f : - (nibbles >> 0) & 0x0f; - - new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ - new_sample = new_sample << 4; - new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10); - - hist2 = hist1; - hist1 = new_sample; /* must go before clamp, somehow */ - new_sample = new_sample >> 4; - new_sample = clamp16(new_sample); - - outbuf[samples_done * channelspacing] = new_sample; - samples_done++; - - sample_count++; - } - } - - stream->adpcm_history1_32 = hist1; - stream->adpcm_history2_32 = hist2; -} - -size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2) { - if (is_blocked) { - return (bytes / 0x930) * (28*8/ channels) * (is_form2 ? 18 : 16); - } - else { - return (bytes / 0x80) * (28*8 / channels); - } -} +#include "coding.h" +#include "../util.h" + +// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do +// XA coefs int math in different ways (see comments below), not 100% accurate. +// May be implemented like the SNES/SPC700 BRR. + +/* XA ADPCM gain values */ +#if 0 +static const float K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 }; +static const float K1[4] = { 0.0, 0.0, -0.8125, -0.859375 }; +#endif +/* K0/1 floats to int, -K*2^10 = -K*(1<<10) = -K*1024 */ +static const int IK0[4] = { 0, -960, -1840, -1568 }; +static const int IK1[4] = { 0, 0, 832, 880 }; + +/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs. + * The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new. + * + * Decoding is defined in diagrams, roughly as: + * pcm = clamp( signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 ) + * - Range (12-range=shift) and filter index are renewed every ~28 samples. + * - nibble is expanded to a signed 16b sample, reimplemented as: + * short sample = ((nibble << 12) & 0xf000) >> shift + * or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N) + * - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding: + * (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N + * (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N + * sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N + * sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N + * etc + * (rounding differences should be inaudible, so public implementations may be approximations) + * + * Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly + * differently, maybe using one of the above methods in software/CPU, but in XA's case may be done + * like the SNES/SPC700 BRR, with specific per-filter ops. + * int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240 + * PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this. + * + * XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently + * are supported by the CD hardware and will play if found. + * + * Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf + * BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples + * (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316 + */ + +void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x80] = {0}; + off_t frame_offset; + int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + int32_t hist1 = stream->adpcm_history1_32; + int32_t hist2 = stream->adpcm_history2_32; + + + /* data layout (mono): + * - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames + * (handled externally, this decoder only gets frames) + * - a frame ("sound group") is divided into 8 subframes ("sound unit"), with + * subframe headers ("sound parameters") first then subframe nibbles ("sound data") + * - headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header) + * (repeats may be for error correction, though probably unused) + * - nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc + * (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc) + * + * stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R + * + * example: + * subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c + * subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c + * subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d + * ... + * subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f + */ + + /* external interleave (fixed size), mono/stereo */ + bytes_per_frame = 0x80; + samples_per_frame = 28*8 / channelspacing; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + + VGM_ASSERT(get_32bitBE(frame+0x0) != get_32bitBE(frame+0x4) || get_32bitBE(frame+0x8) != get_32bitBE(frame+0xC), + "bad frames at %x\n", (uint32_t)frame_offset); + + + /* decode subframes */ + for (i = 0; i < 8 / channelspacing; i++) { + int32_t coef1, coef2; + uint8_t coef_index, shift_factor; + + /* parse current subframe (sound unit)'s header (sound parameters) */ + sp_pos = 0x04 + i*channelspacing + channel; + coef_index = (frame[sp_pos] >> 4) & 0xf; + shift_factor = (frame[sp_pos] >> 0) & 0xf; + + VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos); + if (coef_index > 4) + coef_index = 0; /* only 4 filters are used, rest is apparently 0 */ + if (shift_factor > 12) + shift_factor = 9; /* supposedly, from Nocash PSX docs */ + + coef1 = IK0[coef_index]; + coef2 = IK1[coef_index]; + + + /* decode subframe nibbles */ + for(j = 0; j < 28; j++) { + uint8_t nibbles; + int32_t new_sample; + + int su_pos = (channelspacing==1) ? + 0x10 + j*0x04 + (i/2) : /* mono */ + 0x10 + j*0x04 + i; /* stereo */ + int get_high_nibble = (channelspacing==1) ? + (i&1) : /* mono (even subframes = low, off subframes = high) */ + (channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */ + + /* skip half decodes to make sure hist isn't touched (kinda hack-ish) */ + if (!(sample_count >= first_sample && samples_done < samples_to_do)) { + sample_count++; + continue; + } + + nibbles = frame[su_pos]; + + new_sample = get_high_nibble ? + (nibbles >> 4) & 0x0f : + (nibbles >> 0) & 0x0f; + + new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ + new_sample = new_sample << 4; + new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10); + + hist2 = hist1; + hist1 = new_sample; /* must go before clamp, somehow */ + new_sample = new_sample >> 4; + new_sample = clamp16(new_sample); + + outbuf[samples_done * channelspacing] = new_sample; + samples_done++; + + sample_count++; + } + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_history2_32 = hist2; +} + +size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2) { + if (is_blocked) { + return (bytes / 0x930) * (28*8/ channels) * (is_form2 ? 18 : 16); + } + else { + return (bytes / 0x80) * (28*8 / channels); + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c index 14a2fc7a7..500012731 100644 --- a/Frameworks/vgmstream/vgmstream/src/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -40,6 +40,10 @@ void free_codec(VGMSTREAM* vgmstream) { free_imuse(vgmstream->codec_data); } + if (vgmstream->coding_type == coding_COMPRESSWAVE) { + free_compresswave(vgmstream->codec_data); + } + if (vgmstream->coding_type == coding_EA_MT) { free_ea_mt(vgmstream->codec_data, vgmstream->channels); } @@ -96,6 +100,12 @@ void free_codec(VGMSTREAM* vgmstream) { } #endif +#ifdef VGM_USE_SPEEX + if (vgmstream->coding_type == coding_SPEEX) { + free_speex(vgmstream->codec_data); + } +#endif + if (vgmstream->coding_type == coding_ACM) { free_acm(vgmstream->codec_data); } @@ -127,6 +137,10 @@ void seek_codec(VGMSTREAM* vgmstream) { seek_imuse(vgmstream->codec_data, vgmstream->loop_current_sample); } + if (vgmstream->coding_type == coding_COMPRESSWAVE) { + seek_compresswave(vgmstream->codec_data, vgmstream->loop_current_sample); + } + if (vgmstream->coding_type == coding_EA_MT) { seek_ea_mt(vgmstream, vgmstream->loop_current_sample); } @@ -171,6 +185,12 @@ void seek_codec(VGMSTREAM* vgmstream) { } #endif +#ifdef VGM_USE_SPEEX + if (vgmstream->coding_type == coding_SPEEX) { + seek_speex(vgmstream, vgmstream->loop_current_sample); + } +#endif + #ifdef VGM_USE_MPEG if (vgmstream->coding_type == coding_MPEG_custom || vgmstream->coding_type == coding_MPEG_ealayer3 || @@ -219,6 +239,10 @@ void reset_codec(VGMSTREAM* vgmstream) { reset_imuse(vgmstream->codec_data); } + if (vgmstream->coding_type == coding_COMPRESSWAVE) { + reset_compresswave(vgmstream->codec_data); + } + if (vgmstream->coding_type == coding_EA_MT) { reset_ea_mt(vgmstream); } @@ -269,6 +293,12 @@ void reset_codec(VGMSTREAM* vgmstream) { } #endif +#ifdef VGM_USE_SPEEX + if (vgmstream->coding_type == coding_SPEEX) { + reset_speex(vgmstream->codec_data); + } +#endif + #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type == coding_FFmpeg) { reset_ffmpeg(vgmstream->codec_data); @@ -469,6 +499,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { return 0; /* varies per mode */ case coding_IMUSE: return 0; /* varies per frame */ + case coding_COMPRESSWAVE: + return 0; /* multiple of 2 */ case coding_EA_MT: return 0; /* 432, but variable in looped files */ case coding_CIRCUS_VQ: @@ -492,6 +524,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { #ifdef VGM_USE_CELT case coding_CELT_FSB: return 0; /* 512? */ +#endif +#ifdef VGM_USE_SPEEX + case coding_SPEEX: + return 0; #endif default: return 0; @@ -673,6 +709,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { return 0; /* varies per mode? */ case coding_IMUSE: return 0; /* varies per frame */ + case coding_COMPRESSWAVE: + return 0; /* huffman bits */ case coding_EA_MT: return 0; /* variable (frames of bit counts or PCM frames) */ #ifdef VGM_USE_ATRAC9 @@ -682,6 +720,10 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { #ifdef VGM_USE_CELT case coding_CELT_FSB: return 0; /* varies, usually 0x80-100 */ +#endif +#ifdef VGM_USE_SPEEX + case coding_SPEEX: + return 0; /* varies, usually 0x40-60 */ #endif default: /* Vorbis, MPEG, ACM, etc */ return 0; @@ -1219,6 +1261,11 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ case coding_CELT_FSB: decode_celt_fsb(vgmstream, buffer, samples_to_do, vgmstream->channels); break; +#endif +#ifdef VGM_USE_SPEEX + case coding_SPEEX: + decode_speex(vgmstream, buffer, samples_to_do); + break; #endif case coding_ACM: decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); @@ -1375,6 +1422,10 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ decode_imuse(vgmstream, buffer, samples_to_do); break; + case coding_COMPRESSWAVE: + decode_compresswave(vgmstream->codec_data, buffer, samples_to_do); + break; + case coding_EA_MT: for (ch = 0; ch < vgmstream->channels; ch++) { decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 3d31de067..a9e4a88e0 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -118,6 +118,7 @@ static const char* extension_list[] = { "bo2", "brstm", "brstmspm", + "bsnd", "btsnd", "bvg", "bwav", @@ -138,6 +139,7 @@ static const char* extension_list[] = { "csa", //txth/reserved [LEGO Racers 2 (PS2)] "csmp", "cvs", + "cwav", "cxs", "da", @@ -250,6 +252,7 @@ static const char* extension_list[] = { "kovs", //fake extension/header id for .kvs "kns", "kraw", + "ktac", "ktsl2asbin", "ktss", //fake extension/header id for .kns "kvs", @@ -303,6 +306,7 @@ static const char* extension_list[] = { "mds", "mdsp", "med", + "mjb", "mi4", "mib", "mic", @@ -781,6 +785,7 @@ static const coding_info coding_info_list[] = { {coding_OKI4S, "OKI 4-bit ADPCM (4-shift)"}, {coding_PTADPCM, "Platinum 4-bit ADPCM"}, {coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"}, + {coding_COMPRESSWAVE, "CompressWave Huffman ADPCM"}, {coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"}, {coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, @@ -825,6 +830,9 @@ static const coding_info coding_info_list[] = { #ifdef VGM_USE_CELT {coding_CELT_FSB, "Custom CELT"}, #endif +#ifdef VGM_USE_SPEEX + {coding_SPEEX, "Custom Speex"}, +#endif #ifdef VGM_USE_FFMPEG {coding_FFmpeg, "FFmpeg"}, #endif @@ -919,7 +927,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_EXST, "Sony EXST header"}, {meta_SVAG_KCET, "Konami SVAG header"}, {meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"}, - {meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"}, + {meta_MIB_MIH, "Sony MultiStream MIH+MIB header"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, {meta_PS2_MIC, "KOEI .MIC header"}, {meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"}, @@ -1326,6 +1334,10 @@ static const meta_info meta_info_list[] = { {meta_SBK, "Team17 SBK header"}, {meta_DSP_WIIADPCM, "Exient WIIADPCM header"}, {meta_DSP_CWAC, "CRI CWAC header"}, + {meta_COMPRESSWAVE, "CompressWave .cwav header"}, + {meta_KTAC, "Koei Tecmo KTAC header"}, + {meta_MJB_MJH, "Sony MultiStream MJH+MJB header"}, + {meta_BSNF, "id Software BSNF header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index 46edc4db2..911e322c4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -243,6 +243,9 @@ static const adxkey_info adxkey9_list[] = { /* Persona 5 Royal (PS4) */ {0x0000,0x1c85,0x7043, NULL,29915170}, // 0000000001C87822 + /* Assault Lily Last Bullet (Android) */ + {0x0aca,0x0ef5,0x05c9, NULL,0}, // guessed with VGAudio (possible key: 5650EF42E5 / 370725044965) + }; static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ao.c b/Frameworks/vgmstream/vgmstream/src/meta/ao.c index 1a6f2a063..c8af91d39 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ao.c @@ -1,36 +1,40 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* .AO - from AlphaOgg lib [Cloudphobia (PC)] */ -VGMSTREAM * init_vgmstream_ao(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - - - /* checks */ - if ( !check_extensions(streamFile,"ao") ) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x414C5048) /* "ALPH" */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x414F4747) /* "AOGG" */ - goto fail; - -#ifdef VGM_USE_VORBIS - { - ogg_vorbis_meta_info_t ovmi = {0}; - - ovmi.meta_type = meta_AO; - /* values at 0x08/0x0c/0x10 may be related to looping? */ - start_offset = 0xc8; - vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); - } -#else - goto fail; -#endif - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* .AO - from AlphaOgg lib [Cloudphobia (PC), GEO ~The Sword Millennia~ Kasumi no Tani no Kaibutsu (PC)] */ +VGMSTREAM* init_vgmstream_ao(STREAMFILE *sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + + + /* checks */ + if (!check_extensions(sf,"ao")) + goto fail; + if (!is_id64be(0x00,sf, "ALPHAOGG")) + goto fail; + +#ifdef VGM_USE_VORBIS + { + ogg_vorbis_meta_info_t ovmi = {0}; + int sample_rate = read_u32le(0xF0, sf); /* Ogg header */ + + ovmi.meta_type = meta_AO; + + ovmi.loop_start = read_f32le(0x08, sf) * sample_rate; + ovmi.loop_end = read_f32le(0x0c, sf) * sample_rate; /* also num_samples in some versions */ + ovmi.loop_end_found = 1; + ovmi.loop_flag = read_u8(0x10, sf) != 0; /* count or -1=infinite, u32 in some versions */ + /* AlphaOgg defines up to 16 loop points for some reason */ + + start_offset = 0xc8; + vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi); + } +#else + goto fail; +#endif + + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/compresswave.c b/Frameworks/vgmstream/vgmstream/src/meta/compresswave.c new file mode 100644 index 000000000..c8b16d1fa --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/compresswave.c @@ -0,0 +1,50 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .CWAV - from CompressWave lib, found in few Japanese (doujin?) games around 1995-2002 [RADIO ZONDE (PC), GEO ~The Sword Millennia~ (PC)] */ +VGMSTREAM* init_vgmstream_compresswave(STREAMFILE *sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels; + + + /* checks */ + if (!check_extensions(sf, "cwav")) + goto fail; + + if (!is_id64be(0x00,sf, "CmpWave\0")) + goto fail; + + channels = 2; /* always, header channels is internal config */ + start_offset = 0x00; + loop_flag = 1; //read_u8(0x430, sf) != 0; /* wrong count, see below */ + /* codec allows to use a cipher value, not seen */ + /* there is also title and artist, but default to "UnTitled" / "NoName" */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_COMPRESSWAVE; + vgmstream->sample_rate = 44100; /* always, header rate is internal config */ + /* in PCM bytes */ + vgmstream->num_samples = read_u64le(0x418, sf) / sizeof(int16_t) / channels; + /* known files have wrong loop values and just repeat */ + vgmstream->loop_start_sample = 0; //read_u64le(0x420, sf) / sizeof(int16_t) / channels; + vgmstream->loop_end_sample = vgmstream->num_samples; //read_u64le(0x428, sf) / sizeof(int16_t) / channels; + + vgmstream->codec_data = init_compresswave(sf); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_COMPRESSWAVE; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 53b720be2..df101d597 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -1332,7 +1332,6 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM break; } - #endif #ifdef VGM_USE_FFMPEG @@ -1345,7 +1344,23 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM } #endif - case EAAC_CODEC_EASPEEX: /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ //todo +#ifdef VGM_USE_SPEEX + case EAAC_CODEC_EASPEEX: { /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) [FIFA 14 (PS4), FIFA 2020 (Switch)] */ + /* EASpeex looks normal but simplify with custom IO to avoid worrying about blocks. + * First block samples count frames' samples subtracting encoder delay. */ + + vgmstream->codec_data = init_speex_ea(eaac.channels); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_SPEEX; + vgmstream->layout_type = layout_none; + + temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00); + if (!temp_sf) goto fail; + + break; + } +#endif + default: VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec); goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h index ffd0d8d5c..b14514530 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h @@ -87,6 +87,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, case 0x05: /* EALayer3 v1 */ case 0x06: /* EALayer3 v2 "PCM" */ case 0x07: /* EALayer3 v2 "Spike" */ + case 0x09: /* EASpeex */ case 0x0b: /* EAMP3 */ case 0x0c: /* EAOpus */ data->skip_size = 0x08; @@ -208,6 +209,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { case 0x05: /* EALayer3 v1 */ case 0x06: /* EALayer3 v2 "PCM" */ case 0x07: /* EALayer3 v2 "Spike" */ + case 0x09: /* EASpeex */ case 0x0b: /* EAMP3 */ case 0x0c: /* EAOpus */ data_size = block_size - 0x08; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h index 578f1df04..7468f391d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h @@ -1,7 +1,7 @@ #ifndef _FSB_ENCRYPTED_STREAMFILE_H_ #define _FSB_ENCRYPTED_H_ -#define FSB_KEY_MAX 128 /* probably 32 */ +#define FSB_KEY_MAX 0x10000 //0x168 typedef struct { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 4c9daf378..57e46983f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -385,6 +385,9 @@ static const hcakey_info hcakey_list[] = { /* HoneyWorks Premium Live (Android) */ {20200401000000}, // 0000125F45B9D640 + /* Assault Lily Last Bullet (Android) */ + {6349046567469313}, // 00168E6C99510101 + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/idtech.c b/Frameworks/vgmstream/vgmstream/src/meta/idtech.c new file mode 100644 index 000000000..915c24bb8 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/idtech.c @@ -0,0 +1,489 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "idtech_streamfile.h" +#include + + +/* mzrt - id Tech 4.5 audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */ +VGMSTREAM* init_vgmstream_mzrt_v0(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channels, codec, sample_rate, block_size = 0, bps = 0, num_samples; + STREAMFILE* temp_sf = NULL; + + + /* checks */ + if (!check_extensions(sf, "idwav,idmsf,idxma")) + goto fail; + + if (!is_id32be(0x00,sf, "mzrt")) + goto fail; + + if (read_u32be(0x04, sf) != 0) /* version */ + goto fail; + + /* this format is bizarrely mis-aligned (and mis-designed too) */ + + num_samples = read_s32be(0x11,sf); + codec = read_u16le(0x15,sf); + switch(codec) { + case 0x0001: + case 0x0002: + case 0x0166: + channels = read_u16le(0x17,sf); + sample_rate = read_u32le(0x19, sf); + block_size = read_u16le(0x21, sf); + bps = read_u16le(0x23,sf); + + start_offset = 0x25; + break; + + case 0x0000: + sample_rate = read_u32be(0x1D, sf); + channels = read_u32be(0x21, sf); + + start_offset = 0x29; + break; + + default: + goto fail; + } + + /* skip MSADPCM data */ + if (codec == 0x0002) { + if (!msadpcm_check_coefs(sf, start_offset + 0x02 + 0x02)) + goto fail; + + start_offset += 0x02 + read_u16le(start_offset, sf); + } + + /* skip extra data */ + if (codec == 0x0166) { + start_offset += 0x02 + read_u16le(start_offset, sf); + } + + /* skip unknown table */ + if (codec == 0x0000) { + start_offset += 0x04 + read_u32be(start_offset, sf) * 0x04; + } + + /* skip unknown table */ + start_offset += 0x04 + read_u32be(start_offset, sf); + + /* skip block info */ + if (codec != 0x0000) { + /* 0x00: de-blocked size + * 0x04: block count*/ + start_offset += 0x08; + + /* idwav only uses 1 super-block though */ + temp_sf = setup_mzrt_streamfile(sf, start_offset); + if (!temp_sf) goto fail; + } + else { + /* 0x00: de-blocked size */ + start_offset += 0x04; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MZRT; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + switch(codec) { + case 0x0001: + if (bps != 16) goto fail; + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channels; + break; + + case 0x0002: + if (bps != 4) goto fail; + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->frame_size = block_size; + break; + +#ifdef VGM_USE_FFMPEG + case 0x0166: { + uint8_t buf[0x100]; + int bytes; + size_t stream_size = get_streamfile_size(temp_sf); + + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, sf, 0); + vgmstream->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00,stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples_hb(vgmstream, sf, temp_sf, 0x00, stream_size, 0x15, 0,0); + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x0000: { + mpeg_custom_config cfg = {0}; + + cfg.skip_samples = 576; /* assumed */ + + vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, temp_sf == NULL ? sf : temp_sf, temp_sf == NULL ? start_offset : 0x00)) + goto fail; + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + +/* mzrt - id Tech 5 audio [Rage (PS3), The Evil Within (PS3)] */ +VGMSTREAM* init_vgmstream_mzrt_v1(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, offset; + size_t stream_size; + int loop_flag, channels, codec, type, sample_rate, block_size = 0, bps = 0; + int32_t num_samples, loop_start = 0; + STREAMFILE* sb = NULL; + const char* extension = NULL; + + + /* checks */ + if (!check_extensions(sf, "idmsf")) //idmsa: untested + goto fail; + + if (!is_id32be(0x00,sf, "mzrt")) + goto fail; + if (read_u32be(0x04, sf) != 1) /* version */ + goto fail; + + type = read_s32be(0x09,sf); + if (type == 0) { /* Rage */ + /* 0x0d: null */ + /* 0x11: crc? */ + /* 0x15: flag? */ + offset = 0x19; + } + else { /* TEW */ + offset = 0x0D; + } + + stream_size = read_u32be(offset + 0x00,sf); + offset = read_u32be(offset + 0x04,sf); /* absolute but typically right after this */ + + /* 0x00: crc? */ + codec = read_u8(offset + 0x04,sf); /* assumed */ + switch(codec) { + case 0x00: { + /* 0x05: null? */ + num_samples = read_s32be(offset + 0x09,sf); + /* 0x0D: null? */ + /* 0x11: loop related? */ + /* 0x1d: stream size? */ + /* others: ? */ + + /* fmt at 0x31 (codec, avg bitrate, etc) */ + channels = read_u16le(offset + 0x33, sf); + sample_rate = read_u32le(offset + 0x35, sf); + block_size = read_u16le(offset + 0x3d, sf); + bps = read_u16le(offset + 0x3f, sf); + + /* 0x41: MSADPCM fmt extra */ + if (!msadpcm_check_coefs(sf, offset + 0x41 + 0x02 + 0x02)) + goto fail; + + extension = "msadpcm"; + break; + } + + case 0x01: { + uint32_t table_entries; + + /* 0x05: stream size? */ + num_samples = read_s32be(offset + 0x09,sf); + /* 0x0D: 0x40? */ + /* 0x11: loop related? */ + loop_start = read_s32be(offset + 0x15,sf); + /* 0x19: null */ + table_entries = read_u32be(offset + 0x1d,sf); + + /* skip seek table, format: frame size (16b) + frame samples (16b) + * (first entry may be 0 then next entry x2 samples to mimic encoder latency?) */ + offset += 0x21 + table_entries * 0x04; + + sample_rate = read_u32be(offset + 0x00, sf); + channels = read_u32be(offset + 0x04, sf); + /* 0x0c: MSF codec */ + + extension = type == 0 ? "msadpcm" : "msf"; + break; + } + default: + goto fail; + } + + sb = open_streamfile_by_ext(sf, extension); + if (!sb) goto fail; + + if (stream_size != get_streamfile_size(sb)) + goto fail; + + + loop_flag = (loop_start > 0); + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MZRT; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = num_samples; + + switch(codec) { + case 0x0002: + if (bps != 4) goto fail; + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->frame_size = block_size; + break; + +#ifdef VGM_USE_MPEG + case 0x01: { + mpeg_custom_config cfg = {0}; + + cfg.skip_samples = 1152; /* seems ok */ + + vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples -= cfg.skip_samples; + vgmstream->loop_start_sample -= cfg.skip_samples; + vgmstream->loop_end_sample -= cfg.skip_samples; + break; + } +#endif + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sb, start_offset)) + goto fail; + close_streamfile(sb); + return vgmstream; + +fail: + close_streamfile(sb); + close_vgmstream(vgmstream); + return NULL; +} + +/* bsnf - id Tech 5 audio [Wolfenstein: The New Order (multi), Wolfenstein: The Old Blood (PS4)] */ +VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, offset, extra_offset; + size_t stream_size; + int loop_flag, channels, codec, sample_rate; //, block_size = 0, bps = 0; + int32_t num_samples, loop_start = 0; + STREAMFILE* sb = NULL; + const char* suffix = NULL; + const char* extension = NULL; + + + /* checks */ + if (!check_extensions(sf, "bsnd")) + goto fail; + + if (!is_id32be(0x00,sf, "bsnf")) /* null-terminated string */ + goto fail; + if (read_u32be(0x05, sf) != 0x00000100) /* version */ + goto fail; + + offset = 0x18; + + stream_size = read_u32be(offset + 0x00,sf); + offset = read_u32be(offset + 0x04,sf); /* absolute but typically right after this */ + + /* 0x00: crc? */ + /* 0x04: CBR samples or 0 if VBR */ + num_samples = read_s32be(offset + 0x08,sf); + loop_start = read_s32be(offset + 0x0c,sf); + /* 0x10: stream size? */ + + codec = read_u16le(offset + 0x14,sf); + channels = read_u16le(offset + 0x16, sf); + sample_rate = read_u32le(offset + 0x18, sf); + //block_size = read_u16le(offset + 0x20, sf); + //bps = read_u16le(offset + 0x22, sf); + + extra_offset = offset + 0x24; + extension = "ogg"; /* same for all codecs */ + switch(codec) { + case 0x0055: /* msf */ + /* 0x00: table entries */ + /* 0x04: seek table, format: frame size (16b) + frame samples (16b) */ + suffix = "_msf.bsnd"; + break; + + case 0x0166: /* xma */ + /* 0x00: extra size */ + /* 0x02: xma config and block table */ + suffix = "_xma.bsnd"; + break; + + case 0x674F: /* vorbis */ + /* 0x00: extra size */ + /* 0x02: num samples */ + suffix = "_vorbis.bsnd"; + goto fail; //untested + //break; + + case 0x42D2: /* at9 */ + /* 0x00: extra size */ + /* 0x02: encoder delay */ + /* 0x04: channel config */ + /* 0x08: ATRAC9 GUID */ + /* 0x1c: ATRAC9 config */ + suffix = "_at9.bsnd"; + break; + + default: + goto fail; + } + + { + int suffix_len = strlen(suffix); + int filename_len; + char filename[PATH_LIMIT]; + + get_streamfile_filename(sf, filename, sizeof(filename)); + filename_len = strlen(filename); + + if (filename_len < suffix_len) + goto fail; + filename[filename_len - suffix_len + 0] = '.'; + filename[filename_len - suffix_len + 1] = '\0'; + strcat(filename, extension); + + sb = open_streamfile_by_filename(sf, filename); + if (!sb) goto fail; + } + + if (stream_size != get_streamfile_size(sb)) + goto fail; + + loop_flag = (loop_start > 0); + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_BSNF; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = num_samples; + + switch(codec) { + +#ifdef VGM_USE_MPEG + case 0x0055: { + mpeg_custom_config cfg = {0}; + + cfg.skip_samples = 1152; /* seems ok */ + + vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples -= cfg.skip_samples; + vgmstream->loop_start_sample -= cfg.skip_samples; + vgmstream->loop_end_sample -= cfg.skip_samples; + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x0166: { + uint8_t buf[0x100]; + size_t bytes, block_size, block_count; + + block_size = 0x800; + block_count = stream_size / block_size; + + bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), num_samples, stream_size, channels, sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sb, buf, bytes, start_offset, stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, sb, start_offset, stream_size, 0x00, 1,1); + break; + } +#endif + +#ifdef VGM_USE_VORBIS + case 0x674F: { + vgmstream->codec_data = init_ogg_vorbis(sb, start_offset, stream_size, NULL); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_none; + break; + } +#endif + +#ifdef VGM_USE_ATRAC9 + case 0x42D2: { + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.encoder_delay = read_u16le(extra_offset + 0x02,sf); + cfg.config_data = read_u32be(extra_offset + 0x1c,sf); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + break; + } +#endif + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sb, start_offset)) + goto fail; + close_streamfile(sb); + return vgmstream; + +fail: + close_streamfile(sb); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mzrt_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/idtech_streamfile.h similarity index 100% rename from Frameworks/vgmstream/vgmstream/src/meta/mzrt_streamfile.h rename to Frameworks/vgmstream/vgmstream/src/meta/idtech_streamfile.h diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktac.c b/Frameworks/vgmstream/vgmstream/src/meta/ktac.c new file mode 100644 index 000000000..54606188a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktac.c @@ -0,0 +1,514 @@ +#include "meta.h" +#include "../coding/coding.h" + + +typedef struct { + int channels; + int sample_rate; + int loop_flag; + int32_t num_samples; + int32_t loop_start; + int32_t loop_end; + uint32_t file_size; + uint32_t stream_offset; + uint32_t stream_size; + uint32_t table_offset; + uint32_t table_entries; + int type; + int encoder_delay; + int end_padding; + int frame_samples; +} ktac_header_t; + + +static int make_m4a_header(uint8_t* buf, int buf_len, ktac_header_t* ktac, STREAMFILE* sf); + +/* KTAC - Koei Tecmo custom AAC [Kin'iro no Corda 3 (Vita), Shingeki no Kyojin: Shichi kara no Dasshutsu (3DS), Dynasty Warriors (PS4)] */ +VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + ktac_header_t ktac = {0}; + + + /* checks */ + /* .ktac: header id */ + if (!check_extensions(sf,"ktac")) + goto fail; + if (!is_id32be(0x00,sf, "KTAC")) + goto fail; + + /* 0x04: version? (always 1) */ + ktac.file_size = read_u32le(0x08,sf); + if (ktac.file_size != get_streamfile_size(sf)) + goto fail; + ktac.stream_offset = read_u32le(0x0c,sf); + ktac.stream_size = read_u32le(0x10,sf); + ktac.type = read_u32le(0x14,sf); + ktac.sample_rate = read_u32le(0x18,sf); + ktac.num_samples = read_u32le(0x1c,sf); /* full samples */ + ktac.channels = read_u16le(0x20,sf); + ktac.frame_samples = read_u16le(0x22,sf); + ktac.encoder_delay = read_u16le(0x24,sf); + ktac.end_padding = read_u16le(0x26,sf); + ktac.loop_start = read_u32le(0x28,sf); + ktac.loop_end = read_u32le(0x2c,sf); + /* 0x30: ? (big, related to loops) */ + /* 0x34: ? (always null) */ + ktac.table_offset = read_u32le(0x38,sf); + ktac.table_entries= read_u32le(0x3c,sf); + + ktac.loop_flag = (ktac.loop_end > 0); + + /* type 1 files crash during sample_copy, wrong fake header/esds? + * (0=AoT, KnC3 bgm, 1=KnC3 1ch voices, 2=DW4, Atelier Ryza) */ + if (ktac.type == 1) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(ktac.channels, ktac.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_KTAC; + vgmstream->sample_rate = ktac.sample_rate; + vgmstream->num_samples = ktac.num_samples - ktac.encoder_delay - ktac.end_padding; + vgmstream->loop_start_sample = ktac.loop_start * ktac.frame_samples - ktac.encoder_delay; + vgmstream->loop_end_sample = ktac.loop_end * ktac.frame_samples - ktac.encoder_delay; + + /* KTAC uses AAC, but not type found in .aac (that has headered frames, like mp3) but raw + * packets + frame size table (similar to .mp4/m4a). We make a fake M4A header to feed FFmpeg */ +#ifdef VGM_USE_FFMPEG + { + ffmpeg_codec_data* ffmpeg_data = NULL; + int bytes; + uint8_t* buf = NULL; + int buf_len = 0x400 + ktac.table_entries * 0x4; + + if (buf_len > 0x100000) /* ??? */ + goto fail; + + buf = malloc(buf_len); + if (!buf) goto fail; + + bytes = make_m4a_header(buf, buf_len, &ktac, sf); + ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, ktac.stream_offset, ktac.stream_size); + free(buf); + + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* not part of fake header since it's kinda complex to add (iTunes string comment) */ + ffmpeg_set_skip_samples(ffmpeg_data, ktac.encoder_delay); + } +#else + goto fail; +#endif + + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* *********************************************************** */ + +/* Helpers for M4A headers, an insane soup of chunks (AKA "atoms"). + * Needs *A LOT* of atoms and fields so this is more elaborate than usual. + * - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFPreface/qtffPreface.html + */ + +/* generic additions */ +typedef struct { + uint8_t* out; + int bytes; +} m4a_state_t; + +typedef struct { + STREAMFILE* sf; + ktac_header_t* ktac; /* config */ + uint8_t* out; /* current position */ + int bytes; /* written bytes */ + m4a_state_t chunks; /* chunks offsets are absolute, save position until we know header size */ +} m4a_header_t; + +static void add_u32b(m4a_header_t* h, uint32_t value) { + put_u32be(h->out, value); + h->out += 0x04; + h->bytes += 0x04; +} + +static void add_u24b(m4a_header_t* h, uint32_t value) { + put_u16be(h->out + 0x00, (value >> 8u) & 0xFFFF); + put_u8 (h->out + 0x02, (value >> 0u) & 0xFF); + h->out += 0x03; + h->bytes += 0x03; +} + +static void add_u16b(m4a_header_t* h, uint16_t value) { + put_u16be(h->out, value); + h->out += 0x02; + h->bytes += 0x02; +} + +static void add_u8(m4a_header_t* h, uint32_t value) { + put_u8(h->out, value); + h->out += 0x01; + h->bytes += 0x01; +} + +static void add_name(m4a_header_t* h, const char* name) { + memcpy(h->out, name, 0x4); + h->out += 0x04; + h->bytes += 0x04; +} + +static void add_atom(m4a_header_t* h, const char* name, uint32_t size) { + add_u32b(h, size); + add_name(h, name); +} + +/* register + write final size for atoms of variable/complex size */ +static void save_atom(m4a_header_t* h, m4a_state_t* s) { + s->out = h->out; + s->bytes = h->bytes; +} + +static void load_atom(m4a_header_t* h, m4a_state_t* s) { + put_u32be(s->out, h->bytes - s->bytes); +} + +/* common atoms */ + +static void add_ftyp(m4a_header_t* h) { + add_atom(h, "ftyp", 0x18); + add_name(h, "M4A "); /* major brand */ + add_u32b(h, 512); /* minor version */ + add_name(h, "isom"); /* compatible brands */ + add_name(h, "iso2"); /* compatible brands */ +} + +static void add_free(m4a_header_t* h) { + add_atom(h, "free", 0x08); +} + +static void add_mdat(m4a_header_t* h) { + add_atom(h, "mdat", 0x08 + h->ktac->stream_size); +} + +/* variable atoms */ + + +static void add_stco(m4a_header_t* h) { + add_atom(h, "stco", 0x10 + 1 * 0x04); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u32b(h, 1); /* Number of entries */ + /* there may be an entry per frame, but only first seems needed */ + save_atom(h, &h->chunks); + add_u32b(h, 0); /* Absolute offset N */ +} +static void add_stsz(m4a_header_t* h) { + int i; + + add_atom(h, "stsz", 0x14 + h->ktac->table_entries * 0x04); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u32b(h, 0); /* Sample size (CBR) */ + add_u32b(h, h->ktac->table_entries); /* Number of entries (VBR) */ + for (i = 0; i < h->ktac->table_entries; i++) { + uint32_t size = read_u32le(h->ktac->table_offset + i*0x04, h->sf); + add_u32b(h, size); /* Sample N */ + } +} + +static void add_stsc(m4a_header_t* h) { + add_atom(h, "stsc", 0x1c); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u32b(h, 1); /* Number of entries */ + add_u32b(h, 1); /* First chunk */ + add_u32b(h, h->ktac->table_entries); /* Samples per chunk */ + add_u32b(h, 1); /* Sample description ID */ +} + +static void add_stts(m4a_header_t* h) { + add_atom(h, "stts", 0x18); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u32b(h, 1); /* Number of entries */ + add_u32b(h, h->ktac->table_entries); /* Sample count */ + add_u32b(h, h->ktac->frame_samples); /* Sample duration */ +} + +/* from mpeg4audio.c (also see ff_mp4_read_dec_config_descr) */ +static const int m4a_sample_rates[16] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 +}; +static const uint8_t m4a_channels[14] = { + 0, + 1, // mono (1/0) + 2, // stereo (2/0) + 3, // 3/0 + 4, // 3/1 + 5, // 3/2 + 6, // 3/2.1 + 8, // 5/2.1 + //0, + //0, + //0, + //7, // 3/3.1 + //8, // 3/2/2.1 + //24 // 3/3/3 - 5/2/3 - 3/0/0.2 +}; + +static void add_esds(m4a_header_t* h) { + uint16_t config = 0; + + /* ES_descriptor (TLV format see ISO 14496-1) and DecSpecificInfoTag define actual decoding + - config (channels/rate/etc), other atoms with the same stuff is just info + * - http://ecee.colorado.edu/~ecen5653/ecen5653/papers/ISO%2014496-1%202004.PDF */ + + { + uint8_t object_type = 0x02; /* 0x00=none, 0x01=AAC main, 0x02=AAC LC */ + uint8_t sr_index = 0; + uint8_t ch_index = 0; + uint8_t unknown = 0; + int i; + for (i = 0; i < 16; i++) { + if (m4a_sample_rates[i] == h->ktac->sample_rate) { + sr_index = i; + break; + } + } + for (i = 0; i < 8; i++) { + if (m4a_channels[i] == h->ktac->channels) { + ch_index = i; + break; + } + } + + config |= (object_type & 0x1F) << 11; /* 5b */ + config |= (sr_index & 0x0F) << 7; /* 4b */ + config |= (ch_index & 0x0F) << 3; /* 4b */ + config |= (unknown & 0x07) << 0; /* 3b */ + } + + add_atom(h, "esds", 0x33); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + + add_u8 (h, 0x03); /* ES_DescrTag */ + add_u32b(h, 0x80808022); /* size 0x22 */ + add_u16b(h, 0x0000); /* stream Id */ + add_u8 (h, 0x00); /* flags */ + + add_u8 (h, 0x04); /* DecoderConfigDescrTag */ + add_u32b(h, 0x80808014); /* size 0x14 */ + add_u8 (h, 0x40); /* object type (0x40=audio) */ + add_u8 (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */ + add_u24b(h, 0x000000); /* buffer size */ + add_u32b(h, 0); /* max bitrate (256000?)*/ + add_u32b(h, 0); /* average bitrate (256000?) */ + + add_u8 (h, 0x05); /* DecSpecificInfoTag */ + add_u32b(h, 0x80808002); /* size 0x02 */ + add_u16b(h, config); /* actual decoder info */ + + add_u8 (h, 0x06); /* SLConfigDescrTag */ + add_u32b(h, 0x80808001); /* size 0x01 */ + add_u8 (h, 0x02); /* predefined (2=default) */ +} + +static void add_mp4a(m4a_header_t* h) { + add_atom(h, "mp4a", 0x57); + add_u32b(h, 0); /* ? */ + add_u32b(h, 1); /* Data reference index */ + add_u32b(h, 0); /* Reserved */ + add_u32b(h, 0); /* Reserved 2 */ + add_u16b(h, h->ktac->channels); /* Channel count */ + add_u16b(h, 16); /* Sample size */ + add_u32b(h, 0); /* Pre-defined */ + add_u16b(h, h->ktac->sample_rate); /* Sample rate */ + add_u16b(h, 0); /* ? */ + add_esds(h); /* elementary stream descriptor */ +} + +static void add_stsd(m4a_header_t* h) { + add_atom(h, "stsd", 0x67); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u32b(h, 1); /* Number of entries */ + add_mp4a(h); +} + +static void add_stbl(m4a_header_t* h) { + m4a_state_t s; + + save_atom(h, &s); + add_atom(h, "stbl", 0x00); + add_stsd(h); /* Sample description */ + add_stts(h); /* Time-to-sample */ + add_stsc(h); /* Sample-to-chunk */ + add_stsz(h); /* Sample size */ + add_stco(h); /* Chunk offset */ + load_atom(h, &s); +} + +static void add_dinf(m4a_header_t* h) { + add_atom(h, "dinf", 0x24); + add_atom(h, "dref", 0x1c); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u32b(h, 1); /* Number of entries */ + add_atom(h, "url ", 0x0c); + add_u32b(h, 1); /* Version (1 byte) + Flags (3 byte) */ +} + +static void add_smhd(m4a_header_t* h) { + add_atom(h, "smhd", 0x10); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u16b(h, 0); /* Balance */ + add_u16b(h, 0); /* Reserved */ +} + +static void add_minf(m4a_header_t* h) { + m4a_state_t s; + + save_atom(h, &s); + add_atom(h, "minf", 0x00); + add_smhd(h); + add_dinf(h); + add_stbl(h); + load_atom(h, &s); +} + +static void add_hdlr(m4a_header_t* h) { + add_atom(h, "hdlr", 0x22); + add_u32b(h, 0); /* version (1 byte) + flags (3 byte) */ + add_u32b(h, 0); /* Component type */ + add_name(h, "soun"); /* Component subtype */ + add_u32b(h, 0); /* Component manufacturer */ + add_u32b(h, 0); /* Component flags */ + add_u32b(h, 0); /* Component flags mask */ + add_u16b(h, 0); /* Component name */ +} + +static void add_mdhd(m4a_header_t* h) { + add_atom(h, "mdhd", 0x20); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u32b(h, 0); /* Creation time */ + add_u32b(h, 0); /* Modification time */ + add_u32b(h, h->ktac->sample_rate); /* Time scale */ + add_u32b(h, h->ktac->num_samples); /* Duration */ + add_u16b(h, 0); /* Language (0xC455=eng?) */ + add_u16b(h, 0); /* Quality */ +} + +static void add_mdia(m4a_header_t* h) { + m4a_state_t s; + + save_atom(h, &s); + add_atom(h, "mdia", 0x00); + add_mdhd(h); + add_hdlr(h); + add_minf(h); + load_atom(h, &s); +} + +static void add_tkhd(m4a_header_t* h) { + add_atom(h, "tkhd", 0x5C); + add_u32b(h, 0x00000001); /* Version (1 byte) + Flags (3 byte), 1=track enabled */ + add_u32b(h, 0); /* Creation time */ + add_u32b(h, 0); /* Modification time */ + add_u32b(h, 1); /* Track ID */ + add_u32b(h, 0); /* Reserved 1 */ + add_u32b(h, h->ktac->num_samples); /* Duration */ + add_u32b(h, 0); /* Reserved 1 */ + add_u32b(h, 0); /* Reserved 2 */ + add_u16b(h, 0); /* Layer */ + add_u16b(h, 0); /* Alternate group (1?) */ + add_u16b(h, 0x0100); /* Volume */ + add_u16b(h, 0); /* Reserved */ + add_u32b(h, 0x00010000); /* matrix_A */ + add_u32b(h, 0); /* matrix_B */ + add_u32b(h, 0); /* matrix_U */ + add_u32b(h, 0); /* matrix_C */ + add_u32b(h, 0x00010000); /* matrix_D */ + add_u32b(h, 0); /* matrix_V */ + add_u32b(h, 0); /* matrix_X */ + add_u32b(h, 0); /* matrix_Y */ + add_u32b(h, 0x40000000); /* matrix_W */ + add_u32b(h, 0); /* Width */ + add_u32b(h, 0); /* Height */ +} + +static void add_trak(m4a_header_t* h) { + m4a_state_t s; + + save_atom(h, &s); + add_atom(h, "trak", 0x00); + add_tkhd(h); + add_mdia(h); + load_atom(h, &s); +} + +static void add_mvhd(m4a_header_t* h) { + add_atom(h, "mvhd", 0x6c); + add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ + add_u32b(h, 0); /* Creation time */ + add_u32b(h, 0); /* Modification time */ + add_u32b(h, h->ktac->sample_rate); /* Time scale */ + add_u32b(h, h->ktac->num_samples); /* Duration */ + add_u32b(h, 0x00010000); /* Preferred rate */ + add_u16b(h, 0x0100); /* Preferred volume */ + add_u32b(h, 0); /* Reserved 1 */ + add_u32b(h, 0); /* Reserved 2 */ + add_u16b(h, 0); /* Reserved 3 */ + add_u32b(h, 0x00010000); /* matrix_A */ + add_u32b(h, 0); /* matrix_B */ + add_u32b(h, 0); /* matrix_U */ + add_u32b(h, 0); /* matrix_C */ + add_u32b(h, 0x00010000); /* matrix_D */ + add_u32b(h, 0); /* matrix_V */ + add_u32b(h, 0); /* matrix_X */ + add_u32b(h, 0); /* matrix_Y */ + add_u32b(h, 0x40000000); /* matrix_W */ + add_u32b(h, 0); /* Preview time */ + add_u32b(h, 0); /* Preview duration */ + add_u32b(h, 0); /* Poster time */ + add_u32b(h, 0); /* Selection time */ + add_u32b(h, 0); /* Selection duration */ + add_u32b(h, 0); /* Current time */ + add_u32b(h, 2); /* Next track ID */ +} + +static void add_moov(m4a_header_t* h) { + m4a_state_t s; + + save_atom(h, &s); + add_atom(h, "moov", 0x00); + add_mvhd(h); + add_trak(h); + //add_udta(h); + load_atom(h, &s); +} + +/* *** */ + +static int make_m4a_header(uint8_t* buf, int buf_len, ktac_header_t* ktac, STREAMFILE* sf) { + m4a_header_t h = {0}; + + if (buf_len < 0x300 + ktac->table_entries * 0x4) /* approx */ + goto fail; + + h.sf = sf; + h.ktac = ktac; + h.out = buf; + + add_ftyp(&h); + add_free(&h); + add_moov(&h); + add_mdat(&h); + + + /* define absolute chunk offset after all calcs */ + put_u32be(h.chunks.out, h.bytes); + + return h.bytes; +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 361d28aa6..77f259f79 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -851,7 +851,9 @@ VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_smk(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_mzrt(STREAMFILE * streamFile); +VGMSTREAM* init_vgmstream_mzrt_v0(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_mzrt_v1(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf); VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile); @@ -936,4 +938,10 @@ VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf); VGMSTREAM* init_vgmstream_acx(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_compresswave(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mib_mih.c b/Frameworks/vgmstream/vgmstream/src/meta/mib_mih.c index e015f56b3..bb4a39202 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mib_mih.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mib_mih.c @@ -1,72 +1,72 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */ -VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamHeader = NULL; - off_t header_offset, start_offset; - size_t data_size, frame_size, frame_last, frame_count, name_size; - int channel_count, loop_flag, sample_rate; - - /* check extension */ - if (!check_extensions(streamFile, "mib")) - goto fail; - - streamHeader = open_streamfile_by_ext(streamFile,"mih"); - if (!streamHeader) goto fail; - - header_offset = 0x00; - - if (read_32bitLE(0x00,streamHeader) != 0x40) { /* header size */ - name_size = read_32bitLE(0x00, streamHeader); - if (read_32bitLE(0x04 + name_size, streamHeader) == 0x40 && - read_32bitLE(0x04 + name_size + 0x04, streamHeader) == 0x40) { - /* Marc Ecko's Getting Up (PS2) has a name at the start */ - header_offset = 0x04 + name_size + 0x04; - } else { - goto fail; - } - } - - loop_flag = 0; /* MIB+MIH don't loop (nor use PS-ADPCM flags) per spec */ - start_offset = 0x00; - - /* 0x04: padding size (always 0x20, MIH header must be multiple of 0x40) */ - frame_last = (uint32_t)read_32bitLE(header_offset + 0x05,streamHeader) & 0x00FFFFFF; /* 24b */ - channel_count = read_32bitLE(header_offset + 0x08,streamHeader); - sample_rate = read_32bitLE(header_offset + 0x0c,streamHeader); - frame_size = read_32bitLE(header_offset + 0x10,streamHeader); - frame_count = read_32bitLE(header_offset + 0x14,streamHeader); - if (frame_count == 0) { /* rarely [Gladius (PS2)] */ - frame_count = get_streamfile_size(streamFile) / (frame_size * channel_count); - } - - data_size = frame_count * frame_size; - if (frame_last) - data_size -= frame_size - frame_last; /* last frame has less usable data */ - data_size *= channel_count; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); - - vgmstream->meta_type = meta_PS2_MIB_MIH; - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = frame_size; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - close_streamfile(streamHeader); - return vgmstream; - -fail: -close_streamfile(streamHeader); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */ +VGMSTREAM* init_vgmstream_mib_mih(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sh = NULL; + off_t header_offset, start_offset; + size_t data_size, frame_size, frame_last, frame_count; + int channels, loop_flag, sample_rate; + + /* check extension */ + if (!check_extensions(sf, "mib")) + goto fail; + + sh = open_streamfile_by_ext(sf,"mih"); + if (!sh) goto fail; + + header_offset = 0x00; + + if (read_u32le(0x00,sh) != 0x40) { /* header size */ + /* Marc Ecko's Getting Up (PS2) has a name at the start (hack, not standard .mib+mih) */ + size_t name_size = read_u32le(0x00, sh); + if (read_u32le(0x04 + name_size, sh) == 0x40 && + read_u32le(0x04 + name_size + 0x04, sh) == 0x40) { + header_offset = 0x04 + name_size + 0x04; + } else { + goto fail; + } + } + + loop_flag = 0; /* MIB+MIH don't loop (nor use PS-ADPCM flags) per spec */ + start_offset = 0x00; + + /* 0x04: padding size (always 0x20, MIH header must be multiple of 0x40) */ + frame_last = read_u32le(header_offset + 0x05,sh) & 0x00FFFFFF; /* 24b */ + channels = read_u32le(header_offset + 0x08,sh); + sample_rate = read_u32le(header_offset + 0x0c,sh); + frame_size = read_u32le(header_offset + 0x10,sh); + frame_count = read_u32le(header_offset + 0x14,sh); + if (frame_count == 0) { /* rarely [Gladius (PS2)] */ + frame_count = get_streamfile_size(sf) / (frame_size * channels); + } + + data_size = frame_count * frame_size; + if (frame_last) + data_size -= frame_size - frame_last; /* last frame has less usable data */ + data_size *= channels; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MIB_MIH; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(data_size, channels); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = frame_size; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + close_streamfile(sh); + return vgmstream; + +fail: + close_streamfile(sh); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mjb_mjh.c b/Frameworks/vgmstream/vgmstream/src/meta/mjb_mjh.c new file mode 100644 index 000000000..f80b7f7b0 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/mjb_mjh.c @@ -0,0 +1,83 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* MJB+MJH - SCEE MultiStream? bank of MIB+MIH [Star Wars: Bounty Hunter (PS2)] */ +VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sh = NULL; + off_t start_offset = 0, header_offset = 0; + size_t data_size, frame_size, frame_count; + int channels, loop_flag, sample_rate; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf, "mjb")) + goto fail; + + sh = open_streamfile_by_ext(sf, "mjh"); + if (!sh) goto fail; + + /* parse entries */ + { + int i; + off_t subsong_offset = 0; + + total_subsongs = read_s32le(0x00,sh); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + for (i = 0; i < total_subsongs; i++) { + off_t offset = 0x40 + 0x40 * i; + size_t subsong_size = read_u32le(offset + 0x08,sh) * read_u32le(offset + 0x10,sh) * read_u32le(offset + 0x14,sh); + + if (i + 1== target_subsong) { + header_offset = offset; + start_offset = subsong_offset; + } + subsong_offset += subsong_size; + } + + if (!header_offset) + goto fail; + } + + VGM_LOG("3: %lx\n", start_offset); + + /* also see MIB+MIH (same thing but this excludes padding stuff) */ + if (read_u32le(header_offset + 0x00,sh) != 0x40) + goto fail; + channels = read_u32le(header_offset + 0x08,sh); + sample_rate = read_u32le(header_offset + 0x0c,sh); + frame_size = read_u32le(header_offset + 0x10,sh); + frame_count = read_u32le(header_offset + 0x14,sh); + + data_size = frame_count * frame_size * channels; + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MJB_MJH; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(data_size, channels); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = data_size; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = frame_size; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + close_streamfile(sh); + return vgmstream; + +fail: + close_streamfile(sh); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c b/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c index 424bb6c9f..14ade6c7a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c @@ -1,85 +1,82 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* MSB+MSH - SCEE MultiStream flat bank [namCollection: Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */ -VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamHeader = NULL; - off_t start_offset, header_offset = 0; - size_t stream_size; - int loop_flag = 0, channel_count, sample_rate; - int total_subsongs, target_subsong = streamFile->stream_index; - - - /* checks */ - if (!check_extensions(streamFile, "msb")) - goto fail; - - streamHeader = open_streamfile_by_ext(streamFile, "msh"); - if (!streamHeader) goto fail; - - if (read_32bitLE(0x00,streamHeader) != get_streamfile_size(streamHeader)) - goto fail; - /* 0x04: unknown */ - - /* parse entries */ - { - int i; - int entries = read_32bitLE(0x08,streamHeader); - - total_subsongs = 0; - if (target_subsong == 0) target_subsong = 1; - - for (i = 0; i < entries; i++) { - if (read_32bitLE(0x0c + 0x10*i, streamHeader) == 0) /* size 0 = empty entry */ - continue; - - total_subsongs++; - if (total_subsongs == target_subsong && !header_offset) { - header_offset = 0x0c + 0x10*i; - } - } - - if (!header_offset) goto fail; - if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - } - - - - loop_flag = 0; - channel_count = 1; - - stream_size = read_32bitLE(header_offset+0x00, streamHeader); - if (read_32bitLE(header_offset+0x04, streamHeader) != 0) /* stereo flag? */ - goto fail; - start_offset = read_32bitLE(header_offset+0x08, streamHeader); - sample_rate = read_32bitLE(header_offset+0x0c, streamHeader); /* Ace Combat 2 seems to set wrong values but probably their bug */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); - - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = stream_size; - vgmstream->meta_type = meta_MSB_MSH; - - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - - close_streamfile(streamHeader); - return vgmstream; - -fail: - close_streamfile(streamHeader); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* MSB+MSH - SCEE MultiStream flat bank [namCollection: Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */ +VGMSTREAM* init_vgmstream_msb_msh(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sh = NULL; + off_t start_offset, header_offset = 0; + size_t stream_size; + int loop_flag, channels, sample_rate; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf, "msb")) + goto fail; + + sh = open_streamfile_by_ext(sf, "msh"); + if (!sh) goto fail; + + if (read_u32le(0x00,sh) != get_streamfile_size(sh)) + goto fail; + /* 0x04: unknown */ + + /* parse entries */ + { + int i; + int entries = read_s32le(0x08,sh); + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + for (i = 0; i < entries; i++) { + if (read_u32le(0x0c + 0x10*i, sh) == 0) /* size 0 = empty entry */ + continue; + + total_subsongs++; + if (total_subsongs == target_subsong && !header_offset) { + header_offset = 0x0c + 0x10*i; + } + } + + if (!header_offset) goto fail; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + } + + + loop_flag = 0; + channels = 1; + + stream_size = read_u32le(header_offset+0x00, sh); + if (read_u32le(header_offset+0x04, sh) != 0) /* stereo flag? */ + goto fail; + start_offset = read_u32le(header_offset+0x08, sh); + sample_rate = read_u32le(header_offset+0x0c, sh); /* Ace Combat 2 seems to set wrong values but probably their bug */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MSB_MSH; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + close_streamfile(sh); + return vgmstream; + +fail: + close_streamfile(sh); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/musx.c b/Frameworks/vgmstream/vgmstream/src/meta/musx.c index 813c7e6c1..151702c2d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/musx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/musx.c @@ -2,7 +2,7 @@ #include "../coding/coding.h" typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form; -typedef enum { PSX, DSP, XBOX, IMA, DAT } musx_codec; +typedef enum { PSX, DSP, XBOX, IMA, DAT, NGCA, PCM } musx_codec; typedef struct { int big_endian; int version; @@ -31,11 +31,11 @@ typedef struct { int32_t loop_end_sample; } musx_header; -static int parse_musx(STREAMFILE *streamFile, musx_header *musx); +static int parse_musx(STREAMFILE* sf, musx_header* musx); /* MUSX - from Eurocom's games */ -VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_musx(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; musx_header musx = {0}; @@ -43,12 +43,12 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) { /* checks */ /* .sfx: actual extension (extracted from bigfiles with sometimes encrypted names) * .musx: header id */ - if (!check_extensions(streamFile, "sfx,musx")) + if (!check_extensions(sf, "sfx,musx")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ + if (!is_id32be(0x00,sf, "MUSX")) goto fail; - if (!parse_musx(streamFile, &musx)) + if (!parse_musx(sf, &musx)) goto fail; @@ -112,8 +112,41 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x08; - dsp_read_coefs(vgmstream,streamFile,musx.coefs_offset+0x1c, 0x60, musx.big_endian); - dsp_read_hist(vgmstream,streamFile,musx.coefs_offset+0x40, 0x60, musx.big_endian); + dsp_read_coefs(vgmstream,sf,musx.coefs_offset+0x1c, 0x60, musx.big_endian); + dsp_read_hist(vgmstream,sf,musx.coefs_offset+0x40, 0x60, musx.big_endian); + break; + + case PCM: + //vgmstream->num_samples = pcm_bytes_to_samples(musx.stream_size, musx.channels, 16); + //vgmstream->loop_start_sample = pcm_bytes_to_samples(musx.loop_start, musx.channels, 16); + //vgmstream->loop_end_sample = pcm_bytes_to_samples(musx.loop_end, musx.channels, 16); + + vgmstream->coding_type = musx.big_endian ? coding_PCM16BE : coding_PCM16LE; /* only seen on PC but just in case */ + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + + case NGCA: + if (!is_id32be(start_offset,sf, "NGCA")) + goto fail; + /* 0x04: size (stereo full-interleaves 2 NGCA headers but sizes count all) */ + /* 0x08: channels */ + /* 0x0c: coefs */ + musx.coefs_offset = start_offset; + start_offset += 0x40; + //musx.stream_size -= 0x40 * musx.channels; /* needed for interleave */ + + //vgmstream->num_samples = dsp_bytes_to_samples(musx.stream_size - 0x40 * musx.channels, musx.channels); + //vgmstream->loop_start_sample = dsp_bytes_to_samples(musx.loop_start + 0x40, musx.channels); + //vgmstream->loop_end_sample = dsp_bytes_to_samples(musx.loop_end - 0x40 * musx.channels, musx.channels); + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = musx.stream_size / musx.channels; + + dsp_read_coefs(vgmstream, sf, musx.coefs_offset+0x0c, vgmstream->interleave_block_size, musx.big_endian); + //dsp_read_hist(vgmstream, sf, musx.coefs_offset+0x30, 0x00, musx.big_endian); /* used? */ + break; default: @@ -127,7 +160,7 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) { if (musx.loop_flag && musx.loop_end_sample) vgmstream->loop_end_sample = musx.loop_end_sample; - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -137,21 +170,21 @@ fail: } -static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; +static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) { + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; int default_channels, default_sample_rate; if (musx->big_endian) { - read_32bit = read_32bitBE; + read_u32 = read_u32be; } else { - read_32bit = read_32bitLE; + read_u32 = read_u32le; } /* autodetect for older versions that have no info */ if (musx->platform == 0) { if (musx->big_endian) { - musx->platform = 0x47433032; /* "GC02" (fake) */ + musx->platform = get_id32be("GC02"); /* (fake) */ } else { off_t offset = musx->stream_offset; @@ -159,10 +192,10 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) { if (max > musx->stream_size) max = musx->stream_size; - if (ps_check_format(streamFile, offset, max)) { - musx->platform = 0x5053325F; /* "PS2_" */ + if (ps_check_format(sf, offset, max)) { + musx->platform = get_id32be("PS2_"); } else { - musx->platform = 0x58423032; /* "XB02" (fake) */ + musx->platform = get_id32be("XB02"); /* (fake) */ } } } @@ -258,19 +291,19 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) { * 0x10: volume? (usually <= 100) */ /* find loops (cues1 also seems to have this info but this looks ok) */ - cues2_count = read_32bit(musx->loops_offset+0x04, streamFile); - cues2_offset = musx->loops_offset + read_32bit(musx->loops_offset+0x0c, streamFile); + cues2_count = read_u32(musx->loops_offset+0x04, sf); + cues2_offset = musx->loops_offset + read_u32(musx->loops_offset+0x0c, sf); for (i = 0; i < cues2_count; i++) { uint32_t type, offset1, offset2; if (musx->is_old) { - offset1 = read_32bit(cues2_offset + i*0x20 + 0x04, streamFile); - type = read_32bit(cues2_offset + i*0x20 + 0x08, streamFile); - offset2 = read_32bit(cues2_offset + i*0x20 + 0x14, streamFile); + offset1 = read_u32(cues2_offset + i*0x20 + 0x04, sf); + type = read_u32(cues2_offset + i*0x20 + 0x08, sf); + offset2 = read_u32(cues2_offset + i*0x20 + 0x14, sf); } else { - offset1 = read_32bit(cues2_offset + i*0x14 + 0x04, streamFile); - type = read_32bit(cues2_offset + i*0x14 + 0x08, streamFile); - offset2 = read_32bit(cues2_offset + i*0x14 + 0x0c, streamFile); + offset1 = read_u32(cues2_offset + i*0x14 + 0x04, sf); + type = read_u32(cues2_offset + i*0x14 + 0x08, sf); + offset2 = read_u32(cues2_offset + i*0x14 + 0x0c, sf); } /* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */ @@ -282,7 +315,7 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) { } } } - else if (musx->loops_offset && read_32bitBE(musx->loops_offset, streamFile) != 0xABABABAB) { + else if (musx->loops_offset && read_u32be(musx->loops_offset, sf) != 0xABABABAB) { /* parse loop table (loop starts are -1 if non-looping) * 0x00: version? * 0x04: flags? (&1=loops) @@ -292,10 +325,10 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) { * 0x14: loop start sample * 0x18: loop end offset * 0x1c: loop start offset */ - musx->loop_end_sample = read_32bitLE(musx->loops_offset+0x10, streamFile); - musx->loop_start_sample = read_32bitLE(musx->loops_offset+0x14, streamFile); - musx->loop_end = read_32bitLE(musx->loops_offset+0x18, streamFile); - musx->loop_start = read_32bitLE(musx->loops_offset+0x1c, streamFile); + musx->loop_end_sample = read_s32le(musx->loops_offset+0x10, sf); + musx->loop_start_sample = read_s32le(musx->loops_offset+0x14, sf); + musx->loop_end = read_s32le(musx->loops_offset+0x18, sf); + musx->loop_start = read_s32le(musx->loops_offset+0x1c, sf); musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */ musx->loop_flag = (musx->loop_start_sample >= 0); } @@ -310,7 +343,7 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) { int pos; off_t offset = musx->stream_offset + musx->stream_size - 0x800; - if (read_streamfile(buf, offset, sizeof(buf), streamFile) != 0x800) + if (read_streamfile(buf, offset, sizeof(buf), sf) != 0x800) goto fail; pos = 0x800 - 0x04; @@ -328,58 +361,66 @@ fail: return 0; } -static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int target_subsong = streamFile->stream_index; +static int parse_musx(STREAMFILE* sf, musx_header* musx) { + int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; + int target_subsong = sf->stream_index; /* base header is LE */ - /* 0x04: file ID (referenced in external .h, sometimes files are named HC(id).sfx too) */ - musx->version = read_32bitLE(0x08,streamFile); - musx->file_size = read_32bitLE(0x0c,streamFile); - + /* 0x04: file ID (referenced in external .h, sometimes files are named [prefix](id)[suffix].sfx too) */ + musx->version = read_u32le(0x08,sf); + musx->file_size = read_u32le(0x0c,sf); switch(musx->version) { case 201: /* Sphinx and the Cursed Mummy (PS2), Buffy the Vampire Slayer: Chaos Bleeds (PS2/GC/Xbox) */ case 1: /* Athens 2004 (PS2) */ musx->platform = 0; /* guess later */ musx->tables_offset = 0x10; - musx->big_endian = guess_endianness32bit(0x10, streamFile); + musx->big_endian = guess_endianness32bit(0x10, sf); musx->is_old = 1; break; case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC), Athens 2004 (PC) */ case 5: /* Predator: Concrete Jungle (PS2/Xbox), Robots (GC) */ case 6: /* Batman Begins (GC), Ice Age 2 (PS2/PC) */ - musx->platform = read_32bitBE(0x10,streamFile); + musx->platform = read_u32be(0x10,sf); /* 0x14: some id/hash? */ /* 0x18: platform number? */ /* 0x1c: null */ musx->tables_offset = 0x20; - musx->big_endian = (musx->platform == 0x47435F5F); /* "GC__" */ + musx->big_endian = (musx->platform == get_id32be("GC__")); break; case 10: /* 007: Quantum of Solace (PS2), Pirates of the Caribbean: At World's End (PSP), GoldenEye 007 (Wii), Rio (PS3) */ - musx->platform = read_32bitBE(0x10,streamFile); + musx->platform = read_u32be(0x10,sf); /* 0x14: null */ /* 0x18: platform number? */ /* 0x1c: null */ /* 0x20: hash */ musx->tables_offset = 0; /* no tables */ - musx->big_endian = (musx->platform == 0x47435F5F || /* "GC__" */ - musx->platform == 0x58455F5F || /* "XE__" */ - musx->platform == 0x5053335F || /* "PS3_" */ - musx->platform == 0x5749495F); /* "WII_" */ + musx->big_endian = ( + musx->platform == get_id32be("GC__") || + musx->platform == get_id32be("XE__") || + musx->platform == get_id32be("PS3_") || + musx->platform == get_id32be("WII_") + ); break; default: + VGM_LOG("MUSX: unknown version\n"); goto fail; } if (musx->big_endian) { - read_32bit = read_32bitBE; + read_s32 = read_s32be; + read_u32 = read_u32be; + read_u16 = read_u16be; } else { - read_32bit = read_32bitLE; + read_s32 = read_s32le; + read_u32 = read_u32le; + read_u16 = read_u16le; } @@ -388,53 +429,56 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { off_t table1_offset, table2_offset /*, table3_offset, table4_offset*/; size_t /*table1_size, table2_size, table3_size,*/ table4_size; - table1_offset = read_32bit(musx->tables_offset+0x00, streamFile); - //table1_size = read_32bit(musx->tables_offset+0x04, streamFile); - table2_offset = read_32bit(musx->tables_offset+0x08, streamFile); - //table2_size = read_32bit(musx->tables_offset+0x0c, streamFile); - //table3_offset = read_32bit(musx->tables_offset+0x10, streamFile); - //table3_size = read_32bit(musx->tables_offset+0x14, streamFile); - //table4_offset = read_32bit(musx->tables_offset+0x18, streamFile); - table4_size = read_32bit(musx->tables_offset+0x1c, streamFile); + table1_offset = read_u32(musx->tables_offset+0x00, sf); + //table1_size = read_u32(musx->tables_offset+0x04, sf); + table2_offset = read_u32(musx->tables_offset+0x08, sf); + //table2_size = read_u32(musx->tables_offset+0x0c, sf); + //table3_offset = read_u32(musx->tables_offset+0x10, sf); + //table3_size = read_u32(musx->tables_offset+0x14, sf); + //table4_offset = read_u32(musx->tables_offset+0x18, sf); + table4_size = read_u32(musx->tables_offset+0x1c, sf); if (table2_offset == 0 || table2_offset == 0xABABABAB) { /* possibly a collection of IDs */ + VGM_LOG("MUSX: unsupported ID type\n"); goto fail; } else if (table4_size != 0 && table4_size != 0xABABABAB) { /* sfx banks have table1=id table? table2=header table, table=DSP coefs table (optional), table4=data */ musx->form = SFX_BANK; } - else if (read_32bit(table1_offset+0x00, streamFile) < 0x9 && - read_32bit(table1_offset+0x08, streamFile) == 0x14 && - read_32bit(table1_offset+0x10, streamFile) <= 100) { + else if (read_u32(table1_offset+0x00, sf) < 0x9 && + read_u32(table1_offset+0x08, sf) == 0x14 && + read_u32(table1_offset+0x10, sf) <= 100) { /* streams have a info table with a certain format */ musx->form = MFX; } - else if (read_32bit(table1_offset+0x00, streamFile) == 0 && - read_32bit(table1_offset+0x04, streamFile) > read_32bit(table1_offset+0x00, streamFile) && - read_32bit(table1_offset+0x08, streamFile) > read_32bit(table1_offset+0x04, streamFile)) { + else if (read_u32(table1_offset+0x00, sf) == 0 && + read_u32(table1_offset+0x04, sf) > read_u32(table1_offset+0x00, sf) && + read_u32(table1_offset+0x08, sf) > read_u32(table1_offset+0x04, sf)) { /* a list of offsets starting from 0, each stream then has info table at offset */ musx->form = MFX_BANK; } else { + VGM_LOG("MUSX: unsupported unknown type\n"); goto fail; } } else { - if (read_32bitBE(0x800,streamFile) == 0x53424E4B) { /* "SBNK" */ - /* SFX_BANK-like sound bank found on v10 Wii games */ + if (is_id32be(0x800,sf, "SBNK")) { + /* SFX_BANK-like sound bank found on v10 Wii/PC games [Dead Space Extraction (Wii), G-Force (PC)] */ musx->form = SBNK; } - else if (read_32bitBE(0x800,streamFile) == 0x464F524D) { /* "FORM" */ + else if (is_id32be(0x800,sf, "FORM")) { /* RIFF-like sound bank found on v10 PSP games */ musx->form = FORM; } - else if (read_32bitBE(0x800,streamFile) == 0x45535044) { /* "ESPD" */ + else if (is_id32be(0x800,sf, "ESPD")) { /* projectdetails.sfx */ goto fail; } else { + /* simplest music type*/ musx->form = MFX; } } @@ -445,27 +489,27 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { case MFX: if (musx->tables_offset) { - musx->loops_offset = read_32bit(musx->tables_offset+0x00, streamFile); + musx->loops_offset = read_u32(musx->tables_offset+0x00, sf); - musx->stream_offset = read_32bit(musx->tables_offset+0x08, streamFile); - musx->stream_size = read_32bit(musx->tables_offset+0x0c, streamFile); + musx->stream_offset = read_u32(musx->tables_offset+0x08, sf); + musx->stream_size = read_u32(musx->tables_offset+0x0c, sf); } else { - if (read_32bitBE(0x30, streamFile) != 0xABABABAB) { - uint32_t miniheader = read_32bitBE(0x40, streamFile); + if (read_u32be(0x30, sf) != 0xABABABAB) { + uint32_t miniheader = read_u32be(0x40, sf); switch(miniheader) { case 0x44415434: /* "DAT4" */ case 0x44415435: /* "DAT5" */ case 0x44415438: /* "DAT8" */ /* found on PS3/Wii (but not always?) */ - musx->stream_size = read_32bitLE(0x44, streamFile); - musx->channels = read_32bitLE(0x48, streamFile); - musx->sample_rate = read_32bitLE(0x4c, streamFile); + musx->stream_size = read_u32le(0x44, sf); + musx->channels = read_u32le(0x48, sf); + musx->sample_rate = read_u32le(0x4c, sf); musx->loops_offset = 0x50; break; default: - if (read_32bitBE(0x30, streamFile) == 0x00 && - read_32bitBE(0x34, streamFile) == 0x00) { + if (read_u32be(0x30, sf) == 0x00 && + read_u32be(0x34, sf) == 0x00) { /* no subheader [Spider-Man 4 (Wii)] */ musx->loops_offset = 0x00; } else { @@ -480,32 +524,32 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { musx->stream_size = 0; /* read later */ } - if (!parse_musx_stream(streamFile, musx)) + if (!parse_musx_stream(sf, musx)) goto fail; break; case MFX_BANK: { off_t target_offset, base_offset, data_offset; - musx->total_subsongs = read_32bit(musx->tables_offset+0x04, streamFile) / 0x04; /* size */ + musx->total_subsongs = read_u32(musx->tables_offset+0x04, sf) / 0x04; /* size */ if (target_subsong == 0) target_subsong = 1; if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail; - base_offset = read_32bit(musx->tables_offset+0x00, streamFile); - data_offset = read_32bit(musx->tables_offset+0x08, streamFile); + base_offset = read_u32(musx->tables_offset+0x00, sf); + data_offset = read_u32(musx->tables_offset+0x08, sf); - target_offset = read_32bit(base_offset + (target_subsong - 1)*0x04, streamFile) + data_offset; + target_offset = read_u32(base_offset + (target_subsong - 1)*0x04, sf) + data_offset; /* 0x00: id? */ - musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; - musx->stream_size = read_32bit(target_offset + 0x08, streamFile); + musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset; + musx->stream_size = read_u32(target_offset + 0x08, sf); musx->loops_offset = target_offset + 0x0c; /* this looks correct for PS2 and Xbox, not sure about GC */ musx->channels = 1; musx->sample_rate = 22050; - if (!parse_musx_stream(streamFile, musx)) + if (!parse_musx_stream(sf, musx)) goto fail; break; } @@ -514,13 +558,13 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { off_t target_offset, head_offset, coef_offset, data_offset; size_t coef_size; - //unk_offset = read_32bit(musx->tables_offset+0x00, streamFile); /* ids and cue-like config? */ - head_offset = read_32bit(musx->tables_offset+0x08, streamFile); - coef_offset = read_32bit(musx->tables_offset+0x10, streamFile); - coef_size = read_32bit(musx->tables_offset+0x14, streamFile); - data_offset = read_32bit(musx->tables_offset+0x18, streamFile); + //unk_offset = read_u32(musx->tables_offset+0x00, sf); /* ids and cue-like config? */ + head_offset = read_u32(musx->tables_offset+0x08, sf); + coef_offset = read_u32(musx->tables_offset+0x10, sf); + coef_size = read_u32(musx->tables_offset+0x14, sf); + data_offset = read_u32(musx->tables_offset+0x18, sf); - musx->total_subsongs = read_32bit(head_offset+0x00, streamFile); + musx->total_subsongs = read_u32(head_offset+0x00, sf); if (target_subsong == 0) target_subsong = 1; if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail; @@ -529,13 +573,13 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { target_offset = head_offset + 0x04 + (target_subsong - 1)*0x28; /* 0x00: flag? */ - musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; - musx->stream_size = read_32bit(target_offset + 0x08, streamFile); - musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile); + musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset; + musx->stream_size = read_u32(target_offset + 0x08, sf); + musx->sample_rate = read_u32(target_offset + 0x0c, sf); /* 0x10: size? */ /* 0x14: channels? */ /* 0x18: always 4? */ - musx->coefs_offset = read_32bit(target_offset + 0x1c, streamFile) + coef_offset; /* may be set even for non-GC */ + musx->coefs_offset = read_u32(target_offset + 0x1c, sf) + coef_offset; /* may be set even for non-GC */ /* 0x20: null? */ /* 0x24: sub-id? */ musx->channels = 1; @@ -544,11 +588,11 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { target_offset = head_offset + 0x04 + (target_subsong - 1)*0x20; /* 0x00: flag? */ - musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; - musx->stream_size = read_32bit(target_offset + 0x08, streamFile); - musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile); + musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset; + musx->stream_size = read_u32(target_offset + 0x08, sf); + musx->sample_rate = read_u32(target_offset + 0x0c, sf); /* 0x10: size? */ - musx->coefs_offset = read_32bit(target_offset + 0x14, streamFile) + coef_offset; /* may be set even for non-GC */ + musx->coefs_offset = read_u32(target_offset + 0x14, sf) + coef_offset; /* may be set even for non-GC */ /* 0x18: null? */ /* 0x1c: sub-id? */ musx->channels = 1; @@ -557,13 +601,81 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { if (coef_size == 0) musx->coefs_offset = 0; /* disable for later detection */ - if (!parse_musx_stream(streamFile, musx)) + if (!parse_musx_stream(sf, musx)) goto fail; break; } + case SBNK: { + off_t target_offset, head_offset, data_offset; + uint8_t codec; + + /* 0x00: id */ + /* 0x04: always 0x12? */ + /* 0x08: file ID (same as base file) */ + /* 0x0c: some ID? */ + + /* 0x10: table1 count */ + /* 0x14: table1 offset (from here) */ + /* 0x18: table2 count */ + /* 0x1c: table2 offset (from here) */ + + /* 0x20: table3 count */ + /* 0x24: table3 offset (from here) */ + /* 0x28: table4 count */ + /* 0x2c: table4 offset (from here) */ + + /* 0x30: table5 count (waves) */ + /* 0x34: table5 offset (from here) */ + /* 0x38: table6 count */ + /* 0x3c: table6 offset (from here) */ + + /* 0x40: table7 count */ + /* 0x44: table7 offset (from here) */ + /* 0x48: data size */ + /* 0x4c: data offset (absolute) */ + + musx->tables_offset = 0x800; + if (!is_id32be(musx->tables_offset+0x00, sf, "SBNK")) + goto fail; + + musx->total_subsongs = read_u32(musx->tables_offset+0x30, sf); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail; + + head_offset = read_u32(musx->tables_offset+0x34, sf) + musx->tables_offset + 0x34; + data_offset = read_u32(musx->tables_offset+0x4c, sf); + + target_offset = head_offset + (target_subsong - 1)*0x1c; + ;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset); + + /* 0x00: subfile ID */ + musx->num_samples = read_s32(target_offset + 0x04, sf); + musx->loop_start_sample = read_s32(target_offset + 0x08, sf); /* -1 if no loop */ + musx->sample_rate = read_u16(target_offset + 0x0c, sf); + codec = read_u8 (target_offset + 0x0e, sf); + musx->channels = read_u8 (target_offset + 0x0f, sf); + musx->stream_offset = read_u32(target_offset + 0x10, sf) + data_offset; + musx->stream_size = read_u32(target_offset + 0x14, sf); + musx->loop_start = read_s32(target_offset + 0x18, sf); + + musx->loop_end_sample = musx->num_samples; + musx->loop_flag = (musx->loop_start_sample >= 0); + + switch(codec) { + case 0x11: musx->codec = DAT; break; + case 0x13: musx->codec = NGCA; break; + case 0x14: musx->codec = PCM; break; + default: + VGM_LOG("MUSX: unknown SBNK codec %x\n", codec); + goto fail; + } + + break; + } + default: - VGM_LOG("MUSX: unknown form\n"); + VGM_LOG("MUSX: unsupported form\n"); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mzrt.c b/Frameworks/vgmstream/vgmstream/src/meta/mzrt.c deleted file mode 100644 index 9692c0d37..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/mzrt.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" -#include "mzrt_streamfile.h" - - -/* mzrt - idTech "4.5" audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */ -VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag = 0, channel_count, codec, sample_rate, block_size = 0, bps = 0, num_samples; - STREAMFILE *temp_streamFile = NULL; - - - /* checks */ - if (!check_extensions(streamFile, "idwav,idmsf,idxma")) - goto fail; - - if (read_32bitBE(0x00,streamFile) != 0x6D7A7274) /* "mzrt" */ - goto fail; - - /* this format is bizarrely mis-aligned (and mis-designed too) */ - - num_samples = read_32bitBE(0x11,streamFile); - codec = read_16bitLE(0x15,streamFile); - switch(codec) { - case 0x0001: - case 0x0002: - case 0x0166: - channel_count = read_16bitLE(0x17,streamFile); - sample_rate = read_32bitLE(0x19, streamFile); - block_size = read_16bitLE(0x21, streamFile); - bps = read_16bitLE(0x23,streamFile); - - start_offset = 0x25; - break; - - case 0x0000: - sample_rate = read_32bitBE(0x1D, streamFile); - channel_count = read_32bitBE(0x21, streamFile); - - start_offset = 0x29; - break; - - default: - goto fail; - } - - /* skip MSADPCM data */ - if (codec == 0x0002) { - if (!msadpcm_check_coefs(streamFile, start_offset + 0x02 + 0x02)) - goto fail; - - start_offset += 0x02 + read_16bitLE(start_offset, streamFile); - } - - /* skip extra data */ - if (codec == 0x0166) { - start_offset += 0x02 + read_16bitLE(start_offset, streamFile); - } - - /* skip unknown table */ - if (codec == 0x0000) { - start_offset += 0x04 + read_32bitBE(start_offset, streamFile) * 0x04; - } - - /* skip unknown table */ - start_offset += 0x04 + read_32bitBE(start_offset, streamFile); - - /* skip block info */ - if (codec != 0x0000) { - /* 0x00: de-blocked size - * 0x04: block count*/ - start_offset += 0x08; - - /* idwav only uses 1 super-block though */ - temp_streamFile = setup_mzrt_streamfile(streamFile, start_offset); - if (!temp_streamFile) goto fail; - } - else { - /* 0x00: de-blocked size */ - start_offset += 0x04; - } - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_MZRT; - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - - switch(codec) { - case 0x0001: - if (bps != 16) goto fail; - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = block_size / channel_count; - break; - - case 0x0002: - if (bps != 4) goto fail; - vgmstream->coding_type = coding_MSADPCM; - vgmstream->layout_type = layout_none; - vgmstream->frame_size = block_size; - break; - -#ifdef VGM_USE_FFMPEG - case 0x0166: { - uint8_t buf[0x100]; - int bytes; - size_t stream_size = get_streamfile_size(temp_streamFile); - - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, streamFile, 0); - vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,stream_size); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples_hb(vgmstream, streamFile, temp_streamFile, 0x00,stream_size, 0x15, 0,0); - break; - } -#endif - -#ifdef VGM_USE_MPEG - case 0x0000: { - mpeg_custom_config cfg = {0}; - - cfg.skip_samples = 576; /* assumed */ - - vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->layout_type = layout_none; - break; - } -#endif - - default: - goto fail; - } - - if (!vgmstream_open_stream(vgmstream,temp_streamFile == NULL ? streamFile : temp_streamFile,temp_streamFile == NULL ? start_offset : 0x00)) - goto fail; - close_streamfile(temp_streamFile); - return vgmstream; - -fail: - close_streamfile(temp_streamFile); - close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index 8d8635935..6b5155a15 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -178,13 +178,18 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { for (i = 0; i < channels; i++) { off_t loop_offset = ch_header[i].loop_start_offset; - loop_offset = loop_offset / 0x8 * 0x8; /* loop points to a nibble, but we need closest frame header */ - if (dspm->interleave) { + /* Loop offset points to a nibble, but we need closest frame header. + * Stereo doesn't always point to a proper offset unless de-adjusted with interleave first. */ + if (!dspm->interleave) { + loop_offset = loop_offset / 0x08 * 0x8; + } + else { + loop_offset = loop_offset / 0x10 * 0x8; loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave); } if (ch_header[i].loop_ps != read_u8(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) { - //;VGM_LOG("DSP: bad loop ps: %x vs at %lx\n", ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset); + //;VGM_LOG("DSP: ch%i bad loop ps: %x vs at %lx\n", i, ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset); goto fail; } } @@ -660,8 +665,6 @@ VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) { dspm.start_offset = read_32bitBE(0x48,sf); dspm.interleave = 0x10; - dspm.ignore_loop_ps = 1; /* does something strange with offsets/etc, ignore */ - dspm.meta_type = meta_DSP_SADB; return init_vgmstream_dsp_common(sf, &dspm); fail: @@ -1370,7 +1373,6 @@ VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf) { dspm.max_channels = 2; dspm.header_spacing = dspm.interleave; dspm.start_offset = dspm.header_offset + 0x60; - dspm.ignore_loop_ps = 1; /* loop offset seems relative to CWAC? also interleave affects it */ dspm.meta_type = meta_DSP_CWAC; return init_vgmstream_dsp_common(sf, &dspm); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c index 8cfd09763..21fc2a8b6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c @@ -1,528 +1,574 @@ -#include "meta.h" -#include "../coding/coding.h" - - -typedef enum { PSX, DSP, XBOX, WMA, IMA, XMA2 } strwav_codec; -typedef struct { - int32_t channels; - int32_t sample_rate; - int32_t num_samples; - int32_t loop_start; - int32_t loop_end; - int32_t loop_flag; - size_t interleave; - - off_t coefs_offset; - off_t dsps_table; - off_t coefs_table; - - uint32_t flags; - strwav_codec codec; -} strwav_header; - -static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav); - - -/* STR+WAV - Blitz Games streams+header */ -VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamHeader = NULL; - strwav_header strwav = {0}; - off_t start_offset; - - - /* checks */ - if (!check_extensions(streamFile, "str,data")) - goto fail; - - /* get external header (extracted with filenames from bigfiles) */ - { - /* try body+header combos: - * - file.wav.str=body + file.wav=header [common] - * - file.wma.str + file.wma [Fuzion Frenzy (Xbox)] - * - file.data + file (extensionless) [SpongeBob's Surf & Skate Roadtrip (X360)] */ - char basename[PATH_LIMIT]; - get_streamfile_basename(streamFile,basename,PATH_LIMIT); - streamHeader = open_streamfile_by_filename(streamFile, basename); - if (!streamHeader) { - /* try again with file.str=body + file.wav=header [Bad Boys II (PS2), Zapper (PS2)] */ - streamHeader = open_streamfile_by_ext(streamFile, "wav"); - if (!streamHeader) { - /* try again with file.str=body + file.sth=header (renamed/fake extension) */ - streamHeader = open_streamfile_by_ext(streamFile, "sth"); - if (!streamHeader) goto fail; - } - } - else { - /* header must have known extensions */ - if (!check_extensions(streamHeader, "wav,wma,")) - goto fail; - } - } - - /* detect version */ - if (!parse_header(streamHeader, &strwav)) - goto fail; - - if (strwav.flags == 0) - goto fail; - if (strwav.channels > 8) - goto fail; - - /* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */ - if (strwav.flags > 0x07) { - VGM_LOG("STR+WAV: unknown flags\n"); - goto fail; - } - - - start_offset = 0x00; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(strwav.channels,strwav.loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = strwav.sample_rate; - vgmstream->num_samples = strwav.num_samples; - if (strwav.loop_flag) { - vgmstream->loop_start_sample = strwav.loop_start; - vgmstream->loop_end_sample = strwav.loop_end; - } - - vgmstream->meta_type = meta_STR_WAV; - - switch(strwav.codec) { - case PSX: - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = strwav.interleave; - break; - - case DSP: - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = strwav.interleave; - - /* get coefs */ - { - int i, ch; - for (ch = 0; ch < vgmstream->channels; ch++) { - for (i = 0; i < 16; i++) { - off_t coef_offset; - if (strwav.dsps_table) /* mini table with offsets to DSP headers */ - coef_offset = read_32bitBE(strwav.dsps_table+0x04*ch,streamHeader) + 0x1c; - else if (strwav.coefs_table) /* mini table with offsets to coefs then header */ - coef_offset = read_32bitBE(strwav.coefs_table+0x04*ch,streamHeader); - else - coef_offset = strwav.coefs_offset + 0x60*ch; - vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(coef_offset+i*0x02,streamHeader); - } - } - } - break; - - case XBOX: - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_interleave; /* interleaved stereo for >2ch*/ - vgmstream->interleave_block_size = strwav.interleave; - if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) - goto fail; /* only 2ch+..+2ch layout is known */ - break; - - case IMA: - vgmstream->coding_type = coding_BLITZ_IMA; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = strwav.interleave; - break; - -#ifdef VGM_USE_FFMPEG - case WMA: { - ffmpeg_codec_data *ffmpeg_data = NULL; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,get_streamfile_size(streamFile)); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - vgmstream->num_samples = ffmpeg_data->totalSamples; - if (vgmstream->channels != ffmpeg_data->channels) - goto fail; - - break; - } -#endif - -#ifdef VGM_USE_FFMPEG - case XMA2: { - uint8_t buf[0x100]; - size_t stream_size; - size_t bytes, block_size, block_count; - - stream_size = get_streamfile_size(streamFile); - block_size = 0x10000; - block_count = stream_size / block_size; /* not accurate? */ - - bytes = ffmpeg_make_riff_xma2(buf,0x100, strwav.num_samples, stream_size, strwav.channels, strwav.sample_rate, block_count, block_size); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, 0x00,stream_size); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples(vgmstream, streamFile, 0x00,stream_size, 0, 0,0); - break; - } -#endif - - default: - goto fail; - } - - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - close_streamfile(streamHeader); - return vgmstream; - -fail: - close_streamfile(streamHeader); - close_vgmstream(vgmstream); - return NULL; -} - - - -/* Parse header versions. Almost every game uses its own variation (struct serialization?), - * so detection could be improved once enough versions are known. */ -static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) { - size_t header_size; - - if (read_32bitBE(0x00,streamHeader) != 0x00000000) - goto fail; - - header_size = get_streamfile_size(streamHeader); - - //todo loop start/end values may be off for some headers - - /* Fuzion Frenzy (Xbox)[2001] wma */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && - read_32bitLE(0x20,streamHeader) == 0 && /* no num_samples */ - read_32bitLE(0x24,streamHeader) == read_32bitLE(0x80,streamHeader) && /* sample rate repeat */ - read_32bitLE(0x28,streamHeader) == 0x10 && - header_size == 0x110 /* no value in header */ - ) { - strwav->num_samples = read_32bitLE(0x20,streamHeader); /* 0 but will be rectified later */ - strwav->sample_rate = read_32bitLE(0x24,streamHeader); - strwav->flags = read_32bitLE(0x2c,streamHeader); - strwav->loop_start = 0; - strwav->loop_end = 0; - - strwav->channels = read_32bitLE(0x60,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = 0; - - strwav->codec = WMA; - //;VGM_LOG("STR+WAV: header Fuzion Frenzy (Xbox)\n"); - return 1; - } - - /* Taz: Wanted (GC)[2002] */ - /* Cubix Robots for Everyone: Showdown (GC)[2003] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && - read_32bitBE(0x24,streamHeader) == read_32bitBE(0x90,streamHeader) && /* sample rate repeat */ - read_32bitBE(0x28,streamHeader) == 0x10 && - read_32bitBE(0xa0,streamHeader) == header_size /* ~0x3C0 */ - ) { - strwav->num_samples = read_32bitBE(0x20,streamHeader); - strwav->sample_rate = read_32bitBE(0x24,streamHeader); - strwav->flags = read_32bitBE(0x2c,streamHeader); - strwav->loop_start = read_32bitBE(0xb8,streamHeader); - strwav->loop_end = read_32bitBE(0xbc,streamHeader); - - strwav->channels = read_32bitBE(0x50,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->coefs_offset = 0xdc; - strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header Taz: Wanted (GC)\n"); - return 1; - } - - /* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && - read_32bitLE(0x24,streamHeader) == read_32bitLE(0xb0,streamHeader) && /* sample rate repeat */ - read_32bitLE(0x28,streamHeader) == 0x10 && - read_32bitLE(0xc0,streamHeader)*0x04 + read_32bitLE(0xc4,streamHeader) == header_size /* ~0xe0 + variable */ - ) { - strwav->num_samples = read_32bitLE(0x20,streamHeader); - strwav->sample_rate = read_32bitLE(0x24,streamHeader); - strwav->flags = read_32bitLE(0x2c,streamHeader); - strwav->loop_start = read_32bitLE(0x38,streamHeader); - strwav->loop_end = strwav->num_samples; - - strwav->channels = read_32bitLE(0x70,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = 0xD800/2; - - strwav->codec = XBOX; - //;VGM_LOG("STR+WAV: header The Fairly OddParents (Xbox)\n"); - return 1; - } - - /* Bad Boys II (GC)[2004] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && - read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */ - read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xe0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */ - read_32bitBE(0x28,streamHeader) == 0x10 && - read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == header_size /* variable + variable */ - ) { - strwav->num_samples = read_32bitBE(0x20,streamHeader); - strwav->sample_rate = read_32bitBE(0x24,streamHeader); - strwav->flags = read_32bitBE(0x2c,streamHeader); - strwav->loop_start = read_32bitBE(0xd8,streamHeader); - strwav->loop_end = read_32bitBE(0xdc,streamHeader); - - strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->dsps_table = 0xe0; - strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header Bad Boys II (GC)\n"); - return 1; - } - - /* Bad Boys II (PS2)[2004] */ - /* Pac-Man World 3 (PS2)[2005] */ - if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || - read_32bitBE(0x04,streamHeader) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ - read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */ - read_32bitLE(0x28,streamHeader) == 0x10 && - read_32bitLE(0x78,streamHeader)*0x04 + read_32bitLE(0x7c,streamHeader) == header_size /* ~0xe0 + variable */ - ) { - strwav->num_samples = read_32bitLE(0x20,streamHeader); - strwav->sample_rate = read_32bitLE(0x24,streamHeader); - strwav->flags = read_32bitLE(0x2c,streamHeader); - strwav->loop_start = read_32bitLE(0x38,streamHeader); - strwav->interleave = read_32bitLE(0x40,streamHeader) == 1 ? 0x8000 : 0x4000; - strwav->loop_end = read_32bitLE(0x54,streamHeader); - - strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; - - strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header Bad Boys II (PS2)\n"); - return 1; - } - - /* Zapper: One Wicked Cricket! (PS2)[2005] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && - read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */ - read_32bitLE(0x28,streamHeader) == 0x10 && - read_32bitLE(0x7c,streamHeader) == header_size /* ~0xD0 */ - ) { - strwav->num_samples = read_32bitLE(0x20,streamHeader); - strwav->sample_rate = read_32bitLE(0x24,streamHeader); - strwav->flags = read_32bitLE(0x2c,streamHeader); - strwav->loop_start = read_32bitLE(0x38,streamHeader); - strwav->loop_end = read_32bitLE(0x54,streamHeader); - - strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; - - strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header Zapper (PS2)\n"); - return 1; - } - - /* Zapper: One Wicked Cricket! (GC)[2005] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && - read_32bitBE(0x24,streamHeader) == read_32bitBE(0xB0,streamHeader) && /* sample rate repeat */ - read_32bitBE(0x28,streamHeader) == 0x10 && - read_32bitLE(0xc0,streamHeader) == header_size /* variable LE size */ - ) { - strwav->num_samples = read_32bitBE(0x20,streamHeader); - strwav->sample_rate = read_32bitBE(0x24,streamHeader); - strwav->flags = read_32bitBE(0x2c,streamHeader); - strwav->loop_start = read_32bitBE(0xd8,streamHeader); - strwav->loop_end = read_32bitBE(0xdc,streamHeader); - - strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->dsps_table = 0xe0; - strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header Zapper (GC)\n"); - return 1; - } - - /* Zapper: One Wicked Cricket! (PC)[2005] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && - read_32bitLE(0x24,streamHeader) == read_32bitLE(0x114,streamHeader) && /* sample rate repeat */ - read_32bitLE(0x28,streamHeader) == 0x10 && - read_32bitLE(0x12c,streamHeader) == header_size /* ~0x130 */ - ) { - strwav->num_samples = read_32bitLE(0x20,streamHeader); - strwav->sample_rate = read_32bitLE(0x24,streamHeader); - strwav->flags = read_32bitLE(0x2c,streamHeader); - strwav->loop_start = read_32bitLE(0x54,streamHeader); - strwav->loop_end = read_32bitLE(0x30,streamHeader); - - strwav->channels = read_32bitLE(0xF8,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->codec = IMA; - //;VGM_LOG("STR+WAV: header Zapper (PC)\n"); - return 1; - } - - /* Pac-Man World 3 (GC)[2005] */ - /* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && - read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */ - read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xf0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */ - read_32bitBE(0x28,streamHeader) == 0x10 && - read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == read_32bitBE(0xe0,streamHeader) && /* main size */ - (read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */ - read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */ - ) { - strwav->num_samples = read_32bitBE(0x20,streamHeader); - strwav->sample_rate = read_32bitBE(0x24,streamHeader); - strwav->flags = read_32bitBE(0x2c,streamHeader); - strwav->loop_start = read_32bitBE(0xd8,streamHeader); - strwav->loop_end = read_32bitBE(0xdc,streamHeader); - - strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->dsps_table = 0xf0; - strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header SpongeBob SquarePants (GC)\n"); - return 1; - } - - /* SpongeBob SquarePants: Creature from the Krusty Krab (PS2)[2006] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && - read_32bitLE(0x08,streamHeader) == 0x00000000 && - read_32bitLE(0x0c,streamHeader) != header_size && /* some ID */ - (header_size == 0x74 + read_32bitLE(0x64,streamHeader)*0x04 || - header_size == 0x78 + read_32bitLE(0x64,streamHeader)*0x04) - ) { - strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok? - strwav->num_samples = read_32bitLE(0x30,streamHeader); - strwav->loop_end = read_32bitLE(0x34,streamHeader); - strwav->sample_rate = read_32bitLE(0x38,streamHeader); - strwav->flags = read_32bitLE(0x3c,streamHeader); - - strwav->channels = read_32bitLE(0x64,streamHeader); /* tracks of 1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; - - strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header SpongeBob SquarePants (PS2)\n"); - return 1; - } - - /* Tak and the Guardians of Gross (PS2)[2008] */ - if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && - read_32bitLE(0x08,streamHeader) != 0x00000000 && - read_32bitLE(0x0c,streamHeader) == header_size && /* ~0x80+0x04*ch */ - read_32bitLE(0x0c,streamHeader) == 0x80 + read_32bitLE(0x70,streamHeader)*0x04 - ) { - strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok? - strwav->num_samples = read_32bitLE(0x30,streamHeader); - strwav->loop_end = read_32bitLE(0x34,streamHeader); - strwav->sample_rate = read_32bitLE(0x38,streamHeader); - strwav->flags = read_32bitLE(0x3c,streamHeader); - - strwav->channels = read_32bitLE(0x70,streamHeader); /* tracks of 1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; - - strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header Tak (PS2)\n"); - return 1; - } - - /* Tak and the Guardians of Gross (Wii)[2008] */ - /* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */ - if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || - read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */ - read_32bitLE(0x08,streamHeader) != 0x00000000 && - read_32bitBE(0x0c,streamHeader) == header_size && /* variable per header */ - read_32bitBE(0x7c,streamHeader) != 0 && /* has DSP header */ - read_32bitBE(0x38,streamHeader) == read_32bitBE(read_32bitBE(0x7c,streamHeader)+0x38,streamHeader) /* sample rate vs 1st DSP header */ - ) { - strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok? - strwav->num_samples = read_32bitBE(0x30,streamHeader); - strwav->loop_end = read_32bitBE(0x34,streamHeader); - strwav->sample_rate = read_32bitBE(0x38,streamHeader); - strwav->flags = read_32bitBE(0x3c,streamHeader); - - strwav->channels = read_32bitBE(0x70,streamHeader); /* tracks of 1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; - - strwav->coefs_table = 0x7c; - strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header Tak/HOTD:O (Wii)\n"); - return 1; - } - - /* The House of the Dead: Overkill (PS3)[2009] (not Blitz but still the same format) */ - if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || - read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */ - read_32bitLE(0x08,streamHeader) != 0x00000000 && - read_32bitBE(0x0c,streamHeader) == header_size && /* variable per header */ - read_32bitBE(0x7c,streamHeader) == 0 /* not DSP header */ - ) { - strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok? - strwav->num_samples = read_32bitBE(0x30,streamHeader); - strwav->loop_end = read_32bitBE(0x34,streamHeader); - strwav->sample_rate = read_32bitBE(0x38,streamHeader); - strwav->flags = read_32bitBE(0x3c,streamHeader); - - strwav->channels = read_32bitBE(0x70,streamHeader); /* tracks of 1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; - - strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header HOTD:O (PS3)\n"); - return 1; - } - - /* SpongeBob's Surf & Skate Roadtrip (X360)[2011] */ - if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || /* used? */ - read_32bitBE(0x04,streamHeader) == 0x00000700) && - read_32bitLE(0x08,streamHeader) != 0x00000000 && - read_32bitBE(0x0c,streamHeader) == 0x124 && /* variable, not sure about final calc */ - read_32bitBE(0x8c,streamHeader) == 0x180 /* encoder delay actually */ - //0x4c is data_size + 0x210 - ) { - strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok? - strwav->num_samples = read_32bitBE(0x30,streamHeader);//todo sometimes wrong? - strwav->loop_end = read_32bitBE(0x34,streamHeader); - strwav->sample_rate = read_32bitBE(0x38,streamHeader); - strwav->flags = read_32bitBE(0x3c,streamHeader); - - strwav->channels = read_32bitBE(0x70,streamHeader); /* multichannel XMA */ - strwav->loop_flag = strwav->flags & 0x01; - - strwav->codec = XMA2; - //;VGM_LOG("STR+WAV: header SBSSR (X360)\n"); - return 1; - } - - /* unknown */ - goto fail; - -fail: - return 0; -} +#include "meta.h" +#include "../coding/coding.h" + + +typedef enum { PSX, DSP, XBOX, WMA, IMA, XMA2 } strwav_codec; +typedef struct { + int32_t channels; + int32_t sample_rate; + int32_t num_samples; + int32_t loop_start; + int32_t loop_end; + int32_t loop_flag; + size_t interleave; + + off_t coefs_offset; + off_t dsps_table; + off_t coefs_table; + + uint32_t flags; + strwav_codec codec; +} strwav_header; + +static int parse_header(STREAMFILE* sf_h, strwav_header* strwav); + + +/* STR+WAV - Blitz Games streams+header */ +VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_h = NULL; + strwav_header strwav = {0}; + off_t start_offset; + + + /* checks */ + if (!check_extensions(sf, "str,data")) + goto fail; + + /* get external header (extracted with filenames from bigfiles) */ + { + /* try body+header combos: + * - file.wav.str=body + file.wav=header [common] + * - file.wma.str + file.wma [Fuzion Frenzy (Xbox)] + * - file.data + file (extensionless) [SpongeBob's Surf & Skate Roadtrip (X360)] */ + char basename[PATH_LIMIT]; + get_streamfile_basename(sf,basename,PATH_LIMIT); + sf_h = open_streamfile_by_filename(sf, basename); + if (!sf_h) { + /* try again with file.str=body + file.wav=header [Bad Boys II (PS2), Zapper (PS2)] */ + sf_h = open_streamfile_by_ext(sf, "wav"); + if (!sf_h) { + /* try again with file.str=body + file.sth=header (renamed/fake extension) */ + sf_h = open_streamfile_by_ext(sf, "sth"); + if (!sf_h) goto fail; + } + } + else { + /* header must have known extensions */ + if (!check_extensions(sf_h, "wav,wma,")) + goto fail; + } + } + + /* detect version */ + if (!parse_header(sf_h, &strwav)) + goto fail; + + if (strwav.flags == 0) + goto fail; + if (strwav.channels > 8) + goto fail; + + /* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */ + if (strwav.flags > 0x07) { + VGM_LOG("STR+WAV: unknown flags\n"); + goto fail; + } + + + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(strwav.channels,strwav.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = strwav.sample_rate; + vgmstream->num_samples = strwav.num_samples; + if (strwav.loop_flag) { + vgmstream->loop_start_sample = strwav.loop_start; + vgmstream->loop_end_sample = strwav.loop_end; + } + + vgmstream->meta_type = meta_STR_WAV; + + switch(strwav.codec) { + case PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = strwav.interleave; + break; + + case DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = strwav.interleave; + + /* get coefs */ + { + int i, ch; + for (ch = 0; ch < vgmstream->channels; ch++) { + for (i = 0; i < 16; i++) { + off_t coef_offset; + if (strwav.dsps_table) /* mini table with offsets to DSP headers */ + coef_offset = read_32bitBE(strwav.dsps_table+0x04*ch,sf_h) + 0x1c; + else if (strwav.coefs_table) /* mini table with offsets to coefs then header */ + coef_offset = read_32bitBE(strwav.coefs_table+0x04*ch,sf_h); + else + coef_offset = strwav.coefs_offset + 0x60*ch; + vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(coef_offset+i*0x02,sf_h); + } + } + } + break; + + case XBOX: + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_interleave; /* interleaved stereo for >2ch*/ + vgmstream->interleave_block_size = strwav.interleave; + if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) + goto fail; /* only 2ch+..+2ch layout is known */ + break; + + case IMA: + vgmstream->coding_type = coding_BLITZ_IMA; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = strwav.interleave; + break; + +#ifdef VGM_USE_FFMPEG + case WMA: { + ffmpeg_codec_data *ffmpeg_data = NULL; + + ffmpeg_data = init_ffmpeg_offset(sf, start_offset,get_streamfile_size(sf)); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = ffmpeg_data->totalSamples; + if (vgmstream->channels != ffmpeg_data->channels) + goto fail; + + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case XMA2: { + uint8_t buf[0x100]; + size_t stream_size; + size_t bytes, block_size, block_count; + + stream_size = get_streamfile_size(sf); + block_size = 0x10000; + block_count = stream_size / block_size; /* not accurate? */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, strwav.num_samples, stream_size, strwav.channels, strwav.sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, 0x00,stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, sf, 0x00,stream_size, 0, 0,0); + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + goto fail; + close_streamfile(sf_h); + return vgmstream; + +fail: + close_streamfile(sf_h); + close_vgmstream(vgmstream); + return NULL; +} + + + +/* Parse header versions. Almost every game uses its own variation (struct serialization?), + * so detection could be improved once enough versions are known. */ +static int parse_header(STREAMFILE* sf_h, strwav_header* strwav) { + size_t header_size; + + if (read_32bitBE(0x00,sf_h) != 0x00000000) + goto fail; + + header_size = get_streamfile_size(sf_h); + + //todo loop start/end values may be off for some headers + + /* Fuzion Frenzy (Xbox)[2001] wma */ + if ( read_32bitBE(0x04,sf_h) == 0x00000900 && + read_32bitLE(0x20,sf_h) == 0 && /* no num_samples */ + read_32bitLE(0x24,sf_h) == read_32bitLE(0x80,sf_h) && /* sample rate repeat */ + read_32bitLE(0x28,sf_h) == 0x10 && + header_size == 0x110 /* no value in header */ + ) { + strwav->num_samples = read_32bitLE(0x20,sf_h); /* 0 but will be rectified later */ + strwav->sample_rate = read_32bitLE(0x24,sf_h); + strwav->flags = read_32bitLE(0x2c,sf_h); + strwav->loop_start = 0; + strwav->loop_end = 0; + + strwav->channels = read_32bitLE(0x60,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = 0; + + strwav->codec = WMA; + //;VGM_LOG("STR+WAV: header Fuzion Frenzy (Xbox)\n"); + return 1; + } + + /* Taz: Wanted (GC)[2002] */ + /* Cubix Robots for Everyone: Showdown (GC)[2003] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000900 && + read_32bitBE(0x24,sf_h) == read_32bitBE(0x90,sf_h) && /* sample rate repeat */ + read_32bitBE(0x28,sf_h) == 0x10 && + read_32bitBE(0xa0,sf_h) == header_size /* ~0x3C0 */ + ) { + strwav->num_samples = read_32bitBE(0x20,sf_h); + strwav->sample_rate = read_32bitBE(0x24,sf_h); + strwav->flags = read_32bitBE(0x2c,sf_h); + strwav->loop_start = read_32bitBE(0xb8,sf_h); + strwav->loop_end = read_32bitBE(0xbc,sf_h); + + strwav->channels = read_32bitBE(0x50,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->coefs_offset = 0xdc; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header Taz: Wanted (GC)\n"); + return 1; + } + + /* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000900 && + read_32bitLE(0x24,sf_h) == read_32bitLE(0xb0,sf_h) && /* sample rate repeat */ + read_32bitLE(0x28,sf_h) == 0x10 && + read_32bitLE(0xc0,sf_h)*0x04 + read_32bitLE(0xc4,sf_h) == header_size /* ~0xe0 + variable */ + ) { + strwav->num_samples = read_32bitLE(0x20,sf_h); + strwav->sample_rate = read_32bitLE(0x24,sf_h); + strwav->flags = read_32bitLE(0x2c,sf_h); + strwav->loop_start = read_32bitLE(0x38,sf_h); + strwav->loop_end = strwav->num_samples; + + strwav->channels = read_32bitLE(0x70,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = 0xD800/2; + + strwav->codec = XBOX; + //;VGM_LOG("STR+WAV: header The Fairly OddParents (Xbox)\n"); + return 1; + } + + /* Bad Boys II (GC)[2004] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000800 && + read_32bitBE(0x24,sf_h) == read_32bitBE(0xb0,sf_h) && /* sample rate repeat */ + read_32bitBE(0x24,sf_h) == read_32bitBE(read_32bitBE(0xe0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */ + read_32bitBE(0x28,sf_h) == 0x10 && + read_32bitBE(0xc0,sf_h)*0x04 + read_32bitBE(0xc4,sf_h) == header_size /* variable + variable */ + ) { + strwav->num_samples = read_32bitBE(0x20,sf_h); + strwav->sample_rate = read_32bitBE(0x24,sf_h); + strwav->flags = read_32bitBE(0x2c,sf_h); + strwav->loop_start = read_32bitBE(0xd8,sf_h); + strwav->loop_end = read_32bitBE(0xdc,sf_h); + + strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xe0; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header Bad Boys II (GC)\n"); + return 1; + } + + /* Bad Boys II (PS2)[2004] */ + /* Pac-Man World 3 (PS2)[2005] */ + if ((read_32bitBE(0x04,sf_h) == 0x00000800 || + read_32bitBE(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ + read_32bitLE(0x24,sf_h) == read_32bitLE(0x70,sf_h) && /* sample rate repeat */ + read_32bitLE(0x28,sf_h) == 0x10 && + read_32bitLE(0x78,sf_h)*0x04 + read_32bitLE(0x7c,sf_h) == header_size /* ~0xe0 + variable */ + ) { + strwav->num_samples = read_32bitLE(0x20,sf_h); + strwav->sample_rate = read_32bitLE(0x24,sf_h); + strwav->flags = read_32bitLE(0x2c,sf_h); + strwav->loop_start = read_32bitLE(0x38,sf_h); + strwav->interleave = read_32bitLE(0x40,sf_h) == 1 ? 0x8000 : 0x4000; + strwav->loop_end = read_32bitLE(0x54,sf_h); + + strwav->channels = read_32bitLE(0x40,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header Bad Boys II (PS2)\n"); + return 1; + } + + /* Pac-Man World 3 (Xbox)[2005] */ + if ((read_u32be(0x04,sf_h) == 0x00000800 || + read_u32be(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ + read_u32le(0x24,sf_h) == read_u32le(0xB0,sf_h) && /* sample rate repeat */ + read_u32le(0x28,sf_h) == 0x10 && + read_u32le(0xE0,sf_h) + read_u32le(0xE4,sf_h) * 0x40 == header_size /* ~0x100 + cues */ + ) { + strwav->num_samples = read_u32le(0x20,sf_h); + strwav->sample_rate = read_u32le(0x24,sf_h); + strwav->flags = read_u32le(0x2c,sf_h); + strwav->loop_start = read_u32le(0x38,sf_h); + strwav->loop_end = strwav->num_samples; + + strwav->channels = read_u32le(0x70,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0xD800/2 : 0xD800; + + strwav->codec = XBOX; + //;VGM_LOG("STR+WAV: Pac-Man World 3 (Xbox)\n"); + return 1; + } + + /* Pac-Man World 3 (PC)[2005] */ + if ((read_u32be(0x04,sf_h) == 0x00000800 || + read_u32be(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ + read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */ + read_u32le(0x28,sf_h) == 0x10 && + read_u32le(0x130,sf_h) + read_u32le(0x134,sf_h) * 0x40 == header_size /* ~0x140 + cues */ + ) { + strwav->num_samples = read_u32le(0x20,sf_h); + strwav->sample_rate = read_u32le(0x24,sf_h); + strwav->flags = read_u32le(0x2c,sf_h); + strwav->loop_start = read_u32le(0x38,sf_h); + strwav->loop_end = read_u32le(0x30,sf_h); + + strwav->channels = read_u32le(0xF8,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->codec = IMA; + //;VGM_LOG("STR+WAV: Pac-Man World 3 (PC)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (PS2)[2005] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000900 && + read_32bitLE(0x24,sf_h) == read_32bitLE(0x70,sf_h) && /* sample rate repeat */ + read_32bitLE(0x28,sf_h) == 0x10 && + read_32bitLE(0x7c,sf_h) == header_size /* ~0xD0 */ + ) { + strwav->num_samples = read_32bitLE(0x20,sf_h); + strwav->sample_rate = read_32bitLE(0x24,sf_h); + strwav->flags = read_32bitLE(0x2c,sf_h); + strwav->loop_start = read_32bitLE(0x38,sf_h); + strwav->loop_end = read_32bitLE(0x54,sf_h); + + strwav->channels = read_32bitLE(0x40,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header Zapper (PS2)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (GC)[2005] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000900 && + read_32bitBE(0x24,sf_h) == read_32bitBE(0xB0,sf_h) && /* sample rate repeat */ + read_32bitBE(0x28,sf_h) == 0x10 && + read_32bitLE(0xc0,sf_h) == header_size /* variable LE size */ + ) { + strwav->num_samples = read_32bitBE(0x20,sf_h); + strwav->sample_rate = read_32bitBE(0x24,sf_h); + strwav->flags = read_32bitBE(0x2c,sf_h); + strwav->loop_start = read_32bitBE(0xd8,sf_h); + strwav->loop_end = read_32bitBE(0xdc,sf_h); + + strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xe0; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header Zapper (GC)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (PC)[2005] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000900 && + read_32bitLE(0x24,sf_h) == read_32bitLE(0x114,sf_h) && /* sample rate repeat */ + read_32bitLE(0x28,sf_h) == 0x10 && + read_32bitLE(0x12c,sf_h) == header_size /* ~0x130 */ + ) { + strwav->num_samples = read_32bitLE(0x20,sf_h); + strwav->sample_rate = read_32bitLE(0x24,sf_h); + strwav->flags = read_32bitLE(0x2c,sf_h); + strwav->loop_start = read_32bitLE(0x54,sf_h); + strwav->loop_end = read_32bitLE(0x30,sf_h); + + strwav->channels = read_32bitLE(0xF8,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->codec = IMA; + //;VGM_LOG("STR+WAV: header Zapper (PC)\n"); + return 1; + } + + /* Pac-Man World 3 (GC)[2005] */ + /* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */ + /* SpongeBob SquarePants: Creature from the Krusty Krab (Wii)[2006] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000800 && + read_32bitBE(0x24,sf_h) == read_32bitBE(0xb0,sf_h) && /* sample rate repeat */ + read_32bitBE(0x24,sf_h) == read_32bitBE(read_32bitBE(0xf0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */ + read_32bitBE(0x28,sf_h) == 0x10 && + read_32bitBE(0xc0,sf_h)*0x04 + read_32bitBE(0xc4,sf_h) == read_32bitBE(0xe0,sf_h) && /* main size */ + (read_32bitBE(0xe0,sf_h) + read_32bitBE(0xe4,sf_h)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */ + read_32bitBE(0xe0,sf_h) + read_32bitBE(0xe4,sf_h)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */ + ) { + strwav->num_samples = read_32bitBE(0x20,sf_h); + strwav->sample_rate = read_32bitBE(0x24,sf_h); + strwav->flags = read_32bitBE(0x2c,sf_h); + strwav->loop_start = read_32bitBE(0xd8,sf_h); + strwav->loop_end = read_32bitBE(0xdc,sf_h); + + strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xf0; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header SpongeBob SquarePants (GC)\n"); + return 1; + } + + /* SpongeBob SquarePants: Creature from the Krusty Krab (PS2)[2006] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000800 && + read_32bitLE(0x08,sf_h) == 0x00000000 && + read_32bitLE(0x0c,sf_h) != header_size && /* some ID */ + (header_size == 0x74 + read_32bitLE(0x64,sf_h)*0x04 || + header_size == 0x78 + read_32bitLE(0x64,sf_h)*0x04) + ) { + strwav->loop_start = read_32bitLE(0x24,sf_h); //not ok? + strwav->num_samples = read_32bitLE(0x30,sf_h); + strwav->loop_end = read_32bitLE(0x34,sf_h); + strwav->sample_rate = read_32bitLE(0x38,sf_h); + strwav->flags = read_32bitLE(0x3c,sf_h); + + strwav->channels = read_32bitLE(0x64,sf_h); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header SpongeBob SquarePants (PS2)\n"); + return 1; + } + + /* Tak and the Guardians of Gross (PS2)[2008] */ + if ( read_32bitBE(0x04,sf_h) == 0x00000800 && + read_32bitLE(0x08,sf_h) != 0x00000000 && + read_32bitLE(0x0c,sf_h) == header_size && /* ~0x80+0x04*ch */ + read_32bitLE(0x0c,sf_h) == 0x80 + read_32bitLE(0x70,sf_h)*0x04 + ) { + strwav->loop_start = read_32bitLE(0x24,sf_h); //not ok? + strwav->num_samples = read_32bitLE(0x30,sf_h); + strwav->loop_end = read_32bitLE(0x34,sf_h); + strwav->sample_rate = read_32bitLE(0x38,sf_h); + strwav->flags = read_32bitLE(0x3c,sf_h); + + strwav->channels = read_32bitLE(0x70,sf_h); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header Tak (PS2)\n"); + return 1; + } + + /* Tak and the Guardians of Gross (Wii)[2008] */ + /* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */ + /* All Star Karate (Wii)[2010] */ + if ((read_32bitBE(0x04,sf_h) == 0x00000800 || + read_32bitBE(0x04,sf_h) == 0x00000700) && /* rare? */ + read_32bitLE(0x08,sf_h) != 0x00000000 && + read_32bitBE(0x0c,sf_h) == header_size && /* variable per header */ + read_32bitBE(0x7c,sf_h) != 0 && /* has DSP header */ + read_32bitBE(0x38,sf_h) == read_32bitBE(read_32bitBE(0x7c,sf_h)+0x38,sf_h) /* sample rate vs 1st DSP header */ + ) { + strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok? + strwav->num_samples = read_32bitBE(0x30,sf_h); + strwav->loop_end = read_32bitBE(0x34,sf_h); + strwav->sample_rate = read_32bitBE(0x38,sf_h); + strwav->flags = read_32bitBE(0x3c,sf_h); + + strwav->channels = read_32bitBE(0x70,sf_h); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->coefs_table = 0x7c; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header Tak/HOTD:O (Wii)\n"); + return 1; + } + + /* The House of the Dead: Overkill (PS3)[2009] (not Blitz but still the same format) */ + if ((read_32bitBE(0x04,sf_h) == 0x00000800 || + read_32bitBE(0x04,sf_h) == 0x00000700) && /* rare? */ + read_32bitLE(0x08,sf_h) != 0x00000000 && + read_32bitBE(0x0c,sf_h) == header_size && /* variable per header */ + read_32bitBE(0x7c,sf_h) == 0 /* not DSP header */ + ) { + strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok? + strwav->num_samples = read_32bitBE(0x30,sf_h); + strwav->loop_end = read_32bitBE(0x34,sf_h); + strwav->sample_rate = read_32bitBE(0x38,sf_h); + strwav->flags = read_32bitBE(0x3c,sf_h); + + strwav->channels = read_32bitBE(0x70,sf_h); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header HOTD:O (PS3)\n"); + return 1; + } + + /* SpongeBob's Surf & Skate Roadtrip (X360)[2011] */ + if ((read_32bitBE(0x04,sf_h) == 0x00000800 || /* used? */ + read_32bitBE(0x04,sf_h) == 0x00000700) && + read_32bitLE(0x08,sf_h) != 0x00000000 && + read_32bitBE(0x0c,sf_h) == 0x124 && /* variable, not sure about final calc */ + read_32bitBE(0x8c,sf_h) == 0x180 /* encoder delay actually */ + //0x4c is data_size + 0x210 + ) { + strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok? + strwav->num_samples = read_32bitBE(0x30,sf_h);//todo sometimes wrong? + strwav->loop_end = read_32bitBE(0x34,sf_h); + strwav->sample_rate = read_32bitBE(0x38,sf_h); + strwav->flags = read_32bitBE(0x3c,sf_h); + + strwav->channels = read_32bitBE(0x70,sf_h); /* multichannel XMA */ + strwav->loop_flag = strwav->flags & 0x01; + + strwav->codec = XMA2; + //;VGM_LOG("STR+WAV: header SBSSR (X360)\n"); + return 1; + } + + /* unknown */ + goto fail; + +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c index d1c707d37..c8eab4119 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c @@ -35,8 +35,7 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) { /* .aac: actual extension, .laac: for players to avoid hijacking MP4/AAC */ if (!check_extensions(sf, "aac,laac")) goto fail; - if (read_u32be(0x00, sf) != 0x41414320 && - read_u32le(0x00, sf) != 0x41414320) /* "AAC " */ + if (!is_id32be(0x00, sf, "AAC ") && !is_id32le(0x00, sf, "AAC ")) goto fail; if (!parse_aac(sf, &aac)) @@ -92,12 +91,28 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) { } #endif #ifdef VGM_USE_ATRAC9 - case 0x08: { /* Judas Code (Vita) */ + case 0x08: /* Judas Code (Vita) */ + case 0x18: { /* Resonance of Fate (PS4) */ atrac9_config cfg = {0}; - cfg.channels = vgmstream->channels; - cfg.encoder_delay = read_s32le(aac.extra_offset + 0x04,sf); - cfg.config_data = read_u32be(aac.extra_offset + 0x08,sf); + + if (aac.codec == 0x08) { + /* 0x00: ? (related to bitrate/channels?) */ + cfg.encoder_delay = read_s32le(aac.extra_offset + 0x04,sf); + cfg.config_data = read_u32be(aac.extra_offset + 0x08,sf); + } + else { + /* 0x00: ? (related to bitrate/channels?) */ + cfg.encoder_delay = read_s16le(aac.extra_offset + 0x04,sf); + /* 0x06: samples per frame */ + /* 0x08: num samples (without encoder delay) */ + cfg.config_data = read_u32le(aac.extra_offset + 0x0c,sf); + /* 0x10: loop start sample (without encoder delay) */ + /* 0x14: loop end sample (without encoder delay) */ + /* 0x18: related to loop start (adjustment? same as loop start when less than a sample) */ + /* using loop samples causes clicks in some tracks, so maybe it's info only, + * or it's meant to be adjusted with value at 0x18 */ + } vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; @@ -158,6 +173,7 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) { } #endif default: + VGM_LOG("AAC: unknown codec %x\n", aac.codec); goto fail; } @@ -189,7 +205,7 @@ static int parse_aac_v1(STREAMFILE* sf, aac_header* aac) { /* 0x20: config? (0x00010003) */ /* 0x30+ DIR + dirn subsongs */ - if (read_u32be(0x30, sf) != 0x44495220) /* "DIR " */ + if (!is_id32be(0x30, sf, "DIR ")) goto fail; aac->total_subsongs = read_u32be(0x40, sf); @@ -220,7 +236,7 @@ static int parse_aac_v1(STREAMFILE* sf, aac_header* aac) { } } - if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ + if (!is_id32be(offset + 0x00, sf, "WAVE")) goto fail; wave_offset = offset; offset += 0x10; @@ -291,9 +307,9 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) { if (target_subsong == 0) target_subsong = 1; aac->total_subsongs = 0; - if (read_u32be(start + 0x00, sf) == 0x414D4620) { /* "AMF " */ + if (is_id32be(start + 0x00, sf, "AMF ")) { /* GUID subsongs */ - if (read_u32be(start + 0x10, sf) != 0x68656164) /* "head" */ + if (!is_id32be(start + 0x10, sf, "head")) goto fail; size = read_u32be(start + 0x10 + 0x10, sf); @@ -321,7 +337,7 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) { test_offset += entry_size; } } - else if (read_u32be(start + 0x00, sf) == 0x41534320) { /* "ASC " */ + else if (is_id32be(start + 0x00, sf, "ASC ")) { /* regular subsongs */ offset = 0; for (test_offset = 0x30; test_offset < start; test_offset += 0x10) { @@ -342,14 +358,14 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) { if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail; - if (read_u32be(offset + 0x00, sf) != 0x41534320) /* "ASC " */ + if (!is_id32be(offset + 0x00, sf, "ASC ")) goto fail; asc_offset = offset; /* ASC section has offsets to "PLBK" chunk (?) and "WAVE" (header), may be followed by "VRC " (?) */ /* 0x50: PLBK offset */ offset += read_u32be(offset + 0x54, sf); /* WAVE offset */ - if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ + if (!is_id32be(offset + 0x00, sf, "WAVE")) goto fail; offset += 0x10; @@ -415,14 +431,14 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) { /* 0x00: id */ /* 0x04: size */ /* 0x10: config? (0x00020100/0x00020002/0x00020301/etc) */ - /* 0x14: platform ("VITA"=Vita, "DRD "=Android, "MSPC"=PC) */ + /* 0x14: platform ("VITA"=Vita, "DRD "=Android, "MSPC"=PC, "PS4 "=PS4) */ /* offsets table: offset + flag? + size + align? */ offset = read_u32le(0x20, sf); /* "AAOB" table (audio object?) */ /* 0x30: "VRCB" table (some cue/config? related to subsongs? may be empty) */ /* 0x40: "WAVB" table (wave body, has offset + size per stream then data, not needed since offsets are elsewhere too) */ - if (read_u32le(offset + 0x00, sf) != 0x41414F42) /* "AAOB" */ + if (!is_id32le(offset + 0x00, sf, "AAOB")) goto fail; size = read_u32le(offset + 0x04, sf); @@ -436,7 +452,7 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) { uint32_t entry_offset = read_u32le(test_offset + 0x00, sf); /* 0x04: entry size */ - if (entry_offset == 0x41414F20) /* reached first "AAO " */ + if (entry_offset == get_id32be("AAO ")) /* reached end */ break; if (entry_offset) { /* often 0 */ @@ -450,14 +466,14 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) { if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail; - if (read_u32le(offset + 0x00, sf) != 0x41414F20) /* "AAO " */ + if (!is_id32le(offset + 0x00, sf, "AAO ")) goto fail; /* AAO section has offsets to "PLBK" chunk (?) and "WAVE" (header) */ /* 0x14: PLBK offset */ offset += read_u32le(offset + 0x18, sf); /* WAVE offset */ - if (read_u32le(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ + if (!is_id32le(offset + 0x00, sf, "WAVE")) goto fail; offset += 0x10; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index d5c4c4eeb..d21f0077b 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -307,10 +307,20 @@ static inline int min_s32(int32_t a, int32_t b) { return a < b ? a : b; } //align32, align16, clamp16, etc #endif -static inline const int is_id32be(off_t offset, STREAMFILE* sf, const char* s) { +/* fastest to compare would be read_u32x == (uint32), but should be pre-optimized (see get_id32x) */ +static inline /*const*/ int is_id32be(off_t offset, STREAMFILE* sf, const char* s) { return read_u32be(offset, sf) == get_id32be(s); } +static inline /*const*/ int is_id32le(off_t offset, STREAMFILE* sf, const char* s) { + return read_u32le(offset, sf) == get_id32be(s); +} + +static inline /*const*/ int is_id64be(off_t offset, STREAMFILE* sf, const char* s) { + return read_u64be(offset, sf) == get_id64be(s); +} + + //TODO: maybe move to streamfile.c /* guess byte endianness from a given value, return true if big endian and false if little endian */ static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) { diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index 87b903470..72eeb0066 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -7,7 +7,7 @@ #ifndef _UTIL_H #define _UTIL_H -/* very common functions, so static inline in .h is useful to avoid some call overhead */ +/* very common functions, so static (inline) in .h as compiler can optimize to avoid some call overhead */ /* host endian independent multi-byte integer reading */ @@ -98,10 +98,31 @@ static inline int clamp16(int32_t val) { else return val; } -static inline const uint32_t get_id32be(const char* s) { + +/* transforms a string to uint32 (for comparison), but if this is static + all goes well + * compiler should pre-calculate and use uint32 directly */ +static inline /*const*/ uint32_t get_id32be(const char* s) { return (uint32_t)(s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0); } +//static inline /*const*/ uint32_t get_id32le(const char* s) { +// return (uint32_t)(s[0] << 0) | (s[1] << 8) | (s[2] << 16) | (s[3] << 24); +//} + +static inline /*const*/ uint64_t get_id64be(const char* s) { + return (uint64_t)( + ((uint64_t)s[0] << 56) | + ((uint64_t)s[1] << 48) | + ((uint64_t)s[2] << 40) | + ((uint64_t)s[3] << 32) | + ((uint64_t)s[4] << 24) | + ((uint64_t)s[5] << 16) | + ((uint64_t)s[6] << 8) | + ((uint64_t)s[7] << 0) + ); +} + + /* less common functions, no need to inline */ int round10(int val); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index bcf888c07..0e52a00f0 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -463,7 +463,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_acb, init_vgmstream_rad, init_vgmstream_smk, - init_vgmstream_mzrt, + init_vgmstream_mzrt_v0, init_vgmstream_xavs, init_vgmstream_psf_single, init_vgmstream_psf_segmented, @@ -517,6 +517,11 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_dsp_cwac, init_vgmstream_ifs, init_vgmstream_acx, + init_vgmstream_compresswave, + init_vgmstream_ktac, + init_vgmstream_mjb_mjh, + init_vgmstream_mzrt_v1, + init_vgmstream_bsnf, /* 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 */ @@ -1333,6 +1338,10 @@ static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* v return acm_get_streamfile(vgmstream->codec_data); } + if (vgmstream->coding_type == coding_COMPRESSWAVE) { + return compresswave_get_streamfile(vgmstream->codec_data); + } + #ifdef VGM_USE_VORBIS if (vgmstream->coding_type == coding_OGG_VORBIS) { return ogg_vorbis_get_streamfile(vgmstream->codec_data); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 939bd8030..78cb69cf1 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -28,6 +28,7 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */ //#define VGM_USE_FFMPEG //#define VGM_USE_ATRAC9 //#define VGM_USE_CELT +//#define VGM_USE_SPEEX #ifdef VGM_USE_VORBIS @@ -178,6 +179,7 @@ typedef enum { coding_OKI4S, /* OKI 4-bit ADPCM with 16-bit output and cuadruple step */ coding_PTADPCM, /* Platinum 4-bit ADPCM */ coding_IMUSE, /* LucasArts iMUSE Variable ADPCM */ + coding_COMPRESSWAVE, /* CompressWave Huffman ADPCM */ /* others */ coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ @@ -234,6 +236,10 @@ typedef enum { coding_CELT_FSB, /* Custom Xiph CELT (MDCT-based) */ #endif +#ifdef VGM_USE_SPEEX + coding_SPEEX, /* Custom Speex (CELP-based) */ +#endif + #ifdef VGM_USE_FFMPEG coding_FFmpeg, /* Formats handled by FFmpeg (ATRAC3, XMA, AC3, etc) */ #endif @@ -361,7 +367,7 @@ typedef enum { meta_PS2_EXST, /* Shadow of Colossus EXST */ meta_SVAG_KCET, meta_PS_HEADERLESS, /* headerless PS-ADPCM */ - meta_PS2_MIB_MIH, /* MIB File + MIH Header*/ + meta_MIB_MIH, meta_PS2_MIC, /* KOEI MIC File */ meta_PS2_VAGi, /* VAGi Interleaved File */ meta_PS2_VAGp, /* VAGp Mono File */ @@ -749,6 +755,10 @@ typedef enum { meta_SBK, meta_DSP_WIIADPCM, meta_DSP_CWAC, + meta_COMPRESSWAVE, + meta_KTAC, + meta_MJB_MJH, + meta_BSNF, } meta_t; /* standard WAVEFORMATEXTENSIBLE speaker positions */