From 6bbec09b8d0f94a4e561384e063323cc49edbe9d Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 26 Jan 2025 01:16:52 -0800 Subject: [PATCH] Updated VGMStream to r1980-54-g35c8283f Signed-off-by: Christopher Snowhill --- .../libvgmstream.xcodeproj/project.pbxproj | 78 ++- .../vgmstream/src/base/api_decode_play.c | 21 +- .../vgmstream/vgmstream/src/base/decode.c | 126 ++-- .../vgmstream/vgmstream/src/base/decode.h | 4 +- .../vgmstream/src/base/decode_state.h | 2 - .../vgmstream/vgmstream/src/base/info.c | 16 + .../vgmstream/vgmstream/src/base/mixer.c | 64 +- .../vgmstream/src/base/mixer_ops_common.c | 139 ++-- .../vgmstream/src/base/mixer_ops_fade.c | 20 +- .../vgmstream/vgmstream/src/base/mixer_priv.h | 24 +- .../vgmstream/vgmstream/src/base/mixing.c | 8 +- .../vgmstream/vgmstream/src/base/render.c | 7 +- .../vgmstream/vgmstream/src/base/sbuf.c | 64 +- .../vgmstream/vgmstream/src/base/sbuf.h | 5 +- .../vgmstream/src/coding/atrac9_decoder.c | 2 + .../vgmstream/vgmstream/src/coding/coding.h | 22 +- .../vgmstream/src/coding/ima_decoder.c | 8 +- .../vgmstream/src/coding/ka1a_decoder.c | 146 ++++ .../vgmstream/src/coding/libs/icelib.c | 29 +- .../vgmstream/src/coding/libs/ka1a_dec.c | 636 ++++++++++++++++++ .../vgmstream/src/coding/libs/ka1a_dec.h | 42 ++ .../vgmstream/src/coding/libs/ka1a_dec_data.h | 260 +++++++ .../vgmstream/src/coding/psx_decoder.c | 57 +- .../src/coding/vorbis_custom_decoder.h | 3 +- Frameworks/vgmstream/vgmstream/src/formats.c | 64 +- .../vgmstream/vgmstream/src/layout/blocked.c | 15 +- .../vgmstream/vgmstream/src/layout/flat.c | 15 +- .../vgmstream/src/layout/interleave.c | 17 +- .../vgmstream/vgmstream/src/layout/layout.h | 6 +- .../vgmstream/src/layout/segmented.c | 14 +- .../vgmstream/vgmstream/src/meta/adx_keys.h | 3 + Frameworks/vgmstream/vgmstream/src/meta/awc.c | 2 +- .../vgmstream/src/meta/awc_streamfile.h | 3 +- .../vgmstream/vgmstream/src/meta/bcstm.c | 4 +- .../vgmstream/vgmstream/src/meta/bfwav.c | 4 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 14 + .../vgmstream/vgmstream/src/meta/hd_bd.c | 123 ++++ .../vgmstream/vgmstream/src/meta/i3ds.c | 49 ++ .../vgmstream/vgmstream/src/meta/ka1a.c | 56 ++ .../vgmstream/vgmstream/src/meta/ktac.c | 11 +- .../vgmstream/vgmstream/src/meta/ktsr.c | 300 ++++++--- .../vgmstream/src/meta/ktsr_streamfile.h | 15 +- .../vgmstream/vgmstream/src/meta/meta.h | 15 +- Frameworks/vgmstream/vgmstream/src/meta/msf.c | 69 +- .../vgmstream/vgmstream/src/meta/musx.c | 181 +++-- .../vgmstream/src/meta/ngc_dsp_std.c | 102 +-- .../vgmstream/vgmstream/src/meta/nus3audio.c | 8 +- .../vgmstream/vgmstream/src/meta/ogg_vorbis.c | 2 +- .../vgmstream/vgmstream/src/meta/pphd.c | 86 +++ Frameworks/vgmstream/vgmstream/src/meta/psf.c | 69 +- .../vgmstream/src/meta/rage_aud_streamfile.h | 3 +- .../vgmstream/vgmstream/src/meta/skex.c | 272 ++++++++ .../vgmstream/src/meta/txtp_parser.c | 6 +- Frameworks/vgmstream/vgmstream/src/meta/vag.c | 9 + .../vgmstream/vgmstream/src/meta/wave.c | 88 ++- .../vgmstream/vgmstream/src/meta/xabp.c | 62 ++ Frameworks/vgmstream/vgmstream/src/util.h | 3 + .../vgmstream/vgmstream/src/util/meta_utils.c | 8 +- .../vgmstream/vgmstream/src/util/meta_utils.h | 2 + .../vgmstream/vgmstream/src/vgmstream.c | 21 +- .../vgmstream/vgmstream/src/vgmstream.h | 4 +- .../vgmstream/vgmstream/src/vgmstream_init.c | 7 + .../vgmstream/vgmstream/src/vgmstream_types.h | 8 +- Info.plist.template | 7 + 64 files changed, 2798 insertions(+), 732 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/ka1a_decoder.c create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec.c create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec.h create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec_data.h create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/hd_bd.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/i3ds.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ka1a.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/pphd.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/skex.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/xabp.c diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index f74ce948e..52d686cb0 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -770,6 +770,16 @@ 83C7282022BC893D00678B4A /* dcs_wav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C7280C22BC893D00678B4A /* dcs_wav.c */; }; 83C7282122BC893D00678B4A /* msf_konami.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C7280D22BC893D00678B4A /* msf_konami.c */; }; 83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C7280E22BC893D00678B4A /* mta2_streamfile.h */; }; + 83CBF5312D46308200AA2D75 /* ka1a_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CBF5302D46308200AA2D75 /* ka1a_decoder.c */; }; + 83CBF5352D46309100AA2D75 /* ka1a_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CBF5332D46309100AA2D75 /* ka1a_dec.c */; }; + 83CBF5362D46309100AA2D75 /* ka1a_dec.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CBF5322D46309100AA2D75 /* ka1a_dec.h */; }; + 83CBF5372D46309100AA2D75 /* ka1a_dec_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CBF5342D46309100AA2D75 /* ka1a_dec_data.h */; }; + 83CBF5392D4630B400AA2D75 /* ka1a.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CBF5382D4630B400AA2D75 /* ka1a.c */; }; + 83CBF53B2D46314900AA2D75 /* pphd.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CBF53A2D46314900AA2D75 /* pphd.c */; }; + 83CBF53D2D46318F00AA2D75 /* xabp.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CBF53C2D46318F00AA2D75 /* xabp.c */; }; + 83CBF53F2D46319800AA2D75 /* hd_bd.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CBF53E2D46319800AA2D75 /* hd_bd.c */; }; + 83CBF5412D4631F300AA2D75 /* i3ds.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CBF5402D4631F300AA2D75 /* i3ds.c */; }; + 83CBF5432D46339200AA2D75 /* skex.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CBF5422D46339200AA2D75 /* skex.c */; }; 83D0381824A4129A004CF90F /* swav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D0381724A4129A004CF90F /* swav.c */; }; 83D1189328B2F33400AF3370 /* vab.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D1189228B2F33400AF3370 /* vab.c */; }; 83D2007A248DDB770048BD24 /* fsb_encrypted_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D20072248DDB760048BD24 /* fsb_encrypted_streamfile.h */; }; @@ -1686,6 +1696,16 @@ 83C7280C22BC893D00678B4A /* dcs_wav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dcs_wav.c; sourceTree = ""; }; 83C7280D22BC893D00678B4A /* msf_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msf_konami.c; sourceTree = ""; }; 83C7280E22BC893D00678B4A /* mta2_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mta2_streamfile.h; sourceTree = ""; }; + 83CBF5302D46308200AA2D75 /* ka1a_decoder.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ka1a_decoder.c; sourceTree = ""; }; + 83CBF5322D46309100AA2D75 /* ka1a_dec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ka1a_dec.h; sourceTree = ""; }; + 83CBF5332D46309100AA2D75 /* ka1a_dec.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ka1a_dec.c; sourceTree = ""; }; + 83CBF5342D46309100AA2D75 /* ka1a_dec_data.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ka1a_dec_data.h; sourceTree = ""; }; + 83CBF5382D4630B400AA2D75 /* ka1a.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ka1a.c; sourceTree = ""; }; + 83CBF53A2D46314900AA2D75 /* pphd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pphd.c; sourceTree = ""; }; + 83CBF53C2D46318F00AA2D75 /* xabp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = xabp.c; sourceTree = ""; }; + 83CBF53E2D46319800AA2D75 /* hd_bd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hd_bd.c; sourceTree = ""; }; + 83CBF5402D4631F300AA2D75 /* i3ds.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = i3ds.c; sourceTree = ""; }; + 83CBF5422D46339200AA2D75 /* skex.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = skex.c; sourceTree = ""; }; 83D0381724A4129A004CF90F /* swav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = swav.c; sourceTree = ""; }; 83D1189228B2F33400AF3370 /* vab.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vab.c; sourceTree = ""; }; 83D20072248DDB760048BD24 /* fsb_encrypted_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_encrypted_streamfile.h; sourceTree = ""; }; @@ -1850,36 +1870,39 @@ isa = PBXGroup; children = ( 834F7D402C7093EA003AC386 /* circus_vq_data.h */, - 834F7D412C7093EA003AC386 /* circus_vq_lib.c */, 834F7D422C7093EA003AC386 /* circus_vq_lib.h */, + 834F7D412C7093EA003AC386 /* circus_vq_lib.c */, 834F7D432C7093EA003AC386 /* circus_vq_lzxpcm.h */, - 834F7D5F2C7093EA003AC386 /* clhca.c */, 834F7D602C7093EA003AC386 /* clhca.h */, - 834F7D482C7093EA003AC386 /* compresswave_lib.c */, + 834F7D5F2C7093EA003AC386 /* clhca.c */, 834F7D492C7093EA003AC386 /* compresswave_lib.h */, - 834F7D592C7093EA003AC386 /* g7221_aes.c */, + 834F7D482C7093EA003AC386 /* compresswave_lib.c */, 834F7D5A2C7093EA003AC386 /* g7221_aes.h */, + 834F7D592C7093EA003AC386 /* g7221_aes.c */, 834F7D5B2C7093EA003AC386 /* g7221_data.h */, - 834F7D5C2C7093EA003AC386 /* g7221_lib.c */, 834F7D5D2C7093EA003AC386 /* g7221_lib.h */, - 834F7D622C7093EA003AC386 /* icelib.c */, + 834F7D5C2C7093EA003AC386 /* g7221_lib.c */, 834F7D632C7093EA003AC386 /* icelib.h */, + 834F7D622C7093EA003AC386 /* icelib.c */, + 83CBF5322D46309100AA2D75 /* ka1a_dec.h */, + 83CBF5332D46309100AA2D75 /* ka1a_dec.c */, + 83CBF5342D46309100AA2D75 /* ka1a_dec_data.h */, + 834F7D392C7093EA003AC386 /* libacm.h */, 834F7D382C7093EA003AC386 /* libacm_decode.c */, 834F7D3A2C7093EA003AC386 /* libacm_util.c */, - 834F7D392C7093EA003AC386 /* libacm.h */, - 834F7D342C7093EA003AC386 /* nwa_lib.c */, 834F7D792C7093EA003AC386 /* nwa_lib.h */, - 834F7E792C709E66003AC386 /* ongakukan_adp_lib.c */, + 834F7D342C7093EA003AC386 /* nwa_lib.c */, 834F7E7A2C709E66003AC386 /* ongakukan_adp_lib.h */, - 834F7D822C7093EA003AC386 /* relic_lib.c */, + 834F7E792C709E66003AC386 /* ongakukan_adp_lib.c */, 834F7D832C7093EA003AC386 /* relic_lib.h */, + 834F7D822C7093EA003AC386 /* relic_lib.c */, 834F7D842C7093EA003AC386 /* relic_mixfft.c */, 834F7D892C7093EA003AC386 /* tac_data.h */, - 834F7D8B2C7093EA003AC386 /* tac_lib.c */, 834F7D8C2C7093EA003AC386 /* tac_lib.h */, + 834F7D8B2C7093EA003AC386 /* tac_lib.c */, 834F7D8A2C7093EA003AC386 /* tac_ops.h */, - 834F7D352C7093EA003AC386 /* utkdec.c */, 834F7D362C7093EA003AC386 /* utkdec.h */, + 834F7D352C7093EA003AC386 /* utkdec.c */, ); path = libs; sourceTree = ""; @@ -1893,9 +1916,9 @@ 834F7D3E2C7093EA003AC386 /* atrac9_decoder.c */, 834F7D3F2C7093EA003AC386 /* celt_fsb_decoder.c */, 834F7D442C7093EA003AC386 /* circus_decoder.c */, - 834F7D452C7093EA003AC386 /* coding_utils_samples.h */, - 834F7D462C7093EA003AC386 /* coding_utils.c */, 834F7D472C7093EA003AC386 /* coding.h */, + 834F7D462C7093EA003AC386 /* coding_utils.c */, + 834F7D452C7093EA003AC386 /* coding_utils_samples.h */, 834F7D4A2C7093EA003AC386 /* compresswave_decoder.c */, 834F7D4B2C7093EA003AC386 /* derf_decoder.c */, 834F7D4C2C7093EA003AC386 /* dpcm_kcej_decoder.c */, @@ -1904,10 +1927,10 @@ 834F7D4F2C7093EA003AC386 /* ea_xa_decoder.c */, 834F7D502C7093EA003AC386 /* ea_xas_decoder.c */, 834F7D512C7093EA003AC386 /* fadpcm_decoder.c */, + 834F7D552C7093EA003AC386 /* ffmpeg_decoder.c */, 834F7D522C7093EA003AC386 /* ffmpeg_decoder_custom_mp4.c */, 834F7D532C7093EA003AC386 /* ffmpeg_decoder_custom_opus.c */, 834F7D542C7093EA003AC386 /* ffmpeg_decoder_utils.c */, - 834F7D552C7093EA003AC386 /* ffmpeg_decoder.c */, 834F7D562C7093EA003AC386 /* g72x_state.h */, 834F7D572C7093EA003AC386 /* g719_decoder.c */, 834F7D582C7093EA003AC386 /* g721_decoder.c */, @@ -1916,17 +1939,18 @@ 834F7D642C7093EA003AC386 /* ice_decoder.c */, 834F7D652C7093EA003AC386 /* ima_decoder.c */, 834F7D662C7093EA003AC386 /* imuse_decoder.c */, + 83CBF5302D46308200AA2D75 /* ka1a_decoder.c */, 834F7D672C7093EA003AC386 /* l5_555_decoder.c */, 834F7D372C7093EA003AC386 /* libs */, 834F7D682C7093EA003AC386 /* lsf_decoder.c */, 834F7D692C7093EA003AC386 /* mc3_decoder.c */, 834F7D6A2C7093EA003AC386 /* mp4_aac_decoder.c */, + 834F7D6E2C7093EA003AC386 /* mpeg_custom_utils.c */, 834F7D6B2C7093EA003AC386 /* mpeg_custom_utils_ahx.c */, 834F7D6C2C7093EA003AC386 /* mpeg_custom_utils_ealayer3.c */, 834F7D6D2C7093EA003AC386 /* mpeg_custom_utils_eamp3.c */, - 834F7D6E2C7093EA003AC386 /* mpeg_custom_utils.c */, - 834F7D6F2C7093EA003AC386 /* mpeg_decoder.c */, 834F7D702C7093EA003AC386 /* mpeg_decoder.h */, + 834F7D6F2C7093EA003AC386 /* mpeg_decoder.c */, 834F7D712C7093EA003AC386 /* msadpcm_decoder.c */, 834F7D722C7093EA003AC386 /* mta2_decoder.c */, 834F7D732C7093EA003AC386 /* mtaf_decoder.c */, @@ -1953,15 +1977,15 @@ 834F7D912C7093EA003AC386 /* vadpcm_decoder.c */, 834F7D922C7093EA003AC386 /* vorbis_custom_data_fsb.h */, 834F7D932C7093EA003AC386 /* vorbis_custom_data_wwise.h */, - 834F7D942C7093EA003AC386 /* vorbis_custom_decoder.c */, 834F7D952C7093EA003AC386 /* vorbis_custom_decoder.h */, + 834F7D942C7093EA003AC386 /* vorbis_custom_decoder.c */, + 834F7D9C2C7093EA003AC386 /* vorbis_custom_utils.c */, 834F7D962C7093EA003AC386 /* vorbis_custom_utils_awc.c */, 834F7D972C7093EA003AC386 /* vorbis_custom_utils_fsb.c */, 834F7D982C7093EA003AC386 /* vorbis_custom_utils_ogl.c */, 834F7D992C7093EA003AC386 /* vorbis_custom_utils_sk.c */, 834F7D9A2C7093EA003AC386 /* vorbis_custom_utils_vid1.c */, 834F7D9B2C7093EA003AC386 /* vorbis_custom_utils_wwise.c */, - 834F7D9C2C7093EA003AC386 /* vorbis_custom_utils.c */, 834F7D9D2C7093EA003AC386 /* wady_decoder.c */, 834F7D9E2C7093EA003AC386 /* ws_decoder.c */, 834F7D9F2C7093EA003AC386 /* xa_decoder.c */, @@ -2345,10 +2369,12 @@ 835B9B8D2730BF2D00F87EE3 /* hca_bf.h */, 83AA5D211F6E2F9C0020821C /* hca_keys.h */, 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */, + 83CBF53E2D46319800AA2D75 /* hd_bd.c */, 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */, 836F6E9E18BDC2180095E648 /* hgc1.c */, 836F6E5318BDC2180095E648 /* his.c */, 836F6E9818BDC2180095E648 /* hxd.c */, + 83CBF5402D4631F300AA2D75 /* i3ds.c */, 834FE0E0215C79EB000A5D3D /* idsp_ie.c */, 8346D97425BF838C00D1A8B0 /* idtech.c */, 8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */, @@ -2364,6 +2390,7 @@ 83269DD12399F5DE00F49FE3 /* ivag.c */, 837CEAEF23487F2C00E62A4A /* jstm.c */, 837CEAE923487F2B00E62A4A /* jstm_streamfile.h */, + 83CBF5382D4630B400AA2D75 /* ka1a.c */, 83D20075248DDB760048BD24 /* kat.c */, 83A21F83201D8981000F04B9 /* kma9.c */, 834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */, @@ -2465,6 +2492,7 @@ 834FBCE926BBC7E50095647F /* piff_tpcm.c */, 836F6E8B18BDC2180095E648 /* pona.c */, 836F6E8C18BDC2180095E648 /* pos.c */, + 83CBF53A2D46314900AA2D75 /* pphd.c */, 8306B0D620984590000302D4 /* ppst.c */, 8306B0C52098458D000302D4 /* ppst_streamfile.h */, 834FE0D5215C79E9000A5D3D /* ps_headerless.c */, @@ -2537,6 +2565,7 @@ 831BA6111EAC61A500CF89B0 /* sgxd.c */, 83AA7F782519C042004C5298 /* silence.c */, 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, + 83CBF5422D46339200AA2D75 /* skex.c */, 836F6EBB18BDC2180095E648 /* sl3.c */, 836F6EF218BDC2190095E648 /* sli.c */, 8306B0D32098458F000302D4 /* smc_smh.c */, @@ -2642,6 +2671,7 @@ 837CEAE523487F2B00E62A4A /* xa_04sw.c */, 837CEADF23487F2A00E62A4A /* xa_xa30.c */, 83FBB16E2A4FF4EC00CD0580 /* xa2_acclaim.c */, + 83CBF53C2D46318F00AA2D75 /* xabp.c */, 833A7A2D1ED11961003EC53E /* xau.c */, 834FE0D2215C79E9000A5D3D /* xau_konami.c */, 837CEAE423487F2A00E62A4A /* xavs.c */, @@ -2774,6 +2804,8 @@ 834F7E782C709D0E003AC386 /* vgmstream_limits.h in Headers */, 83D26A8226E66DC2001A9475 /* chunks.h in Headers */, 836F705518BDC2190095E648 /* streamtypes.h in Headers */, + 83CBF5362D46309100AA2D75 /* ka1a_dec.h in Headers */, + 83CBF5372D46309100AA2D75 /* ka1a_dec_data.h in Headers */, 833E82CF2A2856B200CD0580 /* reader_get_nibbles.h in Headers */, 833E82D12A2856B200CD0580 /* reader_put.h in Headers */, 83256CC328666C620036D9C0 /* reader.h in Headers */, @@ -3234,6 +3266,7 @@ 8349A91A1FE6258200E26435 /* vxn.c in Sources */, 834F7E0F2C7093EA003AC386 /* yamaha_decoder.c in Sources */, 834F7E052C7093EA003AC386 /* vorbis_custom_utils_fsb.c in Sources */, + 83CBF53F2D46319800AA2D75 /* hd_bd.c in Sources */, 834F7DC52C7093EA003AC386 /* g719_decoder.c in Sources */, 8349A8EB1FE6253900E26435 /* blocked_rage_aud.c in Sources */, 83A3F0741E3AD8B900D6A794 /* formats.c in Sources */, @@ -3260,7 +3293,10 @@ 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */, 832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */, 8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */, + 83CBF53B2D46314900AA2D75 /* pphd.c in Sources */, 834F7DCF2C7093EA003AC386 /* hca_decoder.c in Sources */, + 83CBF5352D46309100AA2D75 /* ka1a_dec.c in Sources */, + 83CBF53D2D46318F00AA2D75 /* xabp.c in Sources */, 834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */, 83F0AA6121E2028C004BBC04 /* vsv.c in Sources */, 8351F32F2212B57000A606E4 /* dsf.c in Sources */, @@ -3314,6 +3350,7 @@ 8373342C23F60CDC00DE14DC /* kwb.c in Sources */, 83A8BAE825667AA8000F5F3F /* xwav.c in Sources */, 83AA7F852519C042004C5298 /* zwv.c in Sources */, + 83CBF5312D46308200AA2D75 /* ka1a_decoder.c in Sources */, 83EED5D4203A8BC7008BEB45 /* aus.c in Sources */, 836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */, 839FBFFC26C354E70016A78A /* mp4_faac.c in Sources */, @@ -3367,6 +3404,7 @@ 836F6F8518BDC2190095E648 /* exakt_sc.c in Sources */, 8306B0F120984590000302D4 /* ppst.c in Sources */, 834F7DC22C7093EA003AC386 /* ffmpeg_decoder_utils.c in Sources */, + 83CBF5392D4630B400AA2D75 /* ka1a.c in Sources */, 832BF81C21E0514B006F50F1 /* xpcm.c in Sources */, 83F82CB62CD34747003A1072 /* ubi_apm.c in Sources */, 836F702B18BDC2190095E648 /* sdt.c in Sources */, @@ -3465,8 +3503,10 @@ 834F7E0B2C7093EA003AC386 /* wady_decoder.c in Sources */, 83709E061ECBC1A4005C03D3 /* mc3.c in Sources */, 831BA61F1EAC61A500CF89B0 /* cxs.c in Sources */, + 83CBF5432D46339200AA2D75 /* skex.c in Sources */, 834F7E182C709A1D003AC386 /* ea_schl_standard.c in Sources */, 836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */, + 83CBF5412D4631F300AA2D75 /* i3ds.c in Sources */, 8349A90B1FE6258200E26435 /* pcm_kceje.c in Sources */, 836F6F4A18BDC2190095E648 /* interleave.c in Sources */, 834F7EDC2C70A786003AC386 /* streamfile_buffer.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/base/api_decode_play.c b/Frameworks/vgmstream/vgmstream/src/base/api_decode_play.c index bc1dad7ce..31b7173b9 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/api_decode_play.c +++ b/Frameworks/vgmstream/vgmstream/src/base/api_decode_play.c @@ -1,5 +1,6 @@ #include "api_internal.h" #include "mixing.h" +#include "render.h" #if LIBVGMSTREAM_ENABLE @@ -17,24 +18,24 @@ static bool reset_buf(libvgmstream_priv_t* priv) { int input_channels = 0, output_channels = 0; vgmstream_mixing_enable(priv->vgmstream, 0, &input_channels, &output_channels); //query - int min_channels = input_channels; - if (min_channels < output_channels) - min_channels = output_channels; + int max_channels = input_channels; + if (max_channels < output_channels) + max_channels = output_channels; sfmt_t input_sfmt = mixing_get_input_sample_type(priv->vgmstream); sfmt_t output_sfmt = mixing_get_output_sample_type(priv->vgmstream); int input_sample_size = sfmt_get_sample_size(input_sfmt); int output_sample_size = sfmt_get_sample_size(output_sfmt); - int min_sample_size = input_sample_size; - if (min_sample_size < output_sample_size) - min_sample_size = output_sample_size; + int max_sample_size = input_sample_size; + if (max_sample_size < output_sample_size) + max_sample_size = output_sample_size; priv->buf.max_samples = INTERNAL_BUF_SAMPLES; priv->buf.sample_size = output_sample_size; priv->buf.channels = output_channels; - int max_bytes = priv->buf.max_samples * min_sample_size * min_channels; + int max_bytes = priv->buf.max_samples * max_sample_size * max_channels; priv->buf.data = malloc(max_bytes); if (!priv->buf.data) return false; @@ -79,7 +80,11 @@ LIBVGMSTREAM_API int libvgmstream_render(libvgmstream_t* lib) { if (!priv->pos.play_forever && to_get + priv->pos.current > priv->pos.play_samples) to_get = priv->pos.play_samples - priv->pos.current; - int decoded = render_vgmstream(priv->buf.data, to_get, priv->vgmstream); + sbuf_t ssrc; + sfmt_t sfmt = mixing_get_input_sample_type(priv->vgmstream); + sbuf_init(&ssrc, sfmt, priv->buf.data, to_get, priv->vgmstream->channels); + + int decoded = render_main(&ssrc, priv->vgmstream); update_buf(priv, decoded); update_decoder_info(priv, decoded); diff --git a/Frameworks/vgmstream/vgmstream/src/base/decode.c b/Frameworks/vgmstream/vgmstream/src/base/decode.c index 43938b2e1..94de7d6f9 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/base/decode.c @@ -6,7 +6,6 @@ #include "plugins.h" #include "sbuf.h" -#if VGM_TEST_DECODER #include "../util/log.h" #include "decode_state.h" @@ -16,23 +15,26 @@ static void* decode_state_init() { } static void decode_state_reset(VGMSTREAM* vgmstream) { + if (!vgmstream->decode_state) + return; memset(vgmstream->decode_state, 0, sizeof(decode_state_t)); } +static void decode_state_free(VGMSTREAM* vgmstream) { + free(vgmstream->decode_state); +} + // this could be part of the VGMSTREAM but for now keep separate as it simplifies // some loop-related stuff void* decode_init() { return decode_state_init(); } -#endif /* custom codec handling, not exactly "decode" stuff but here to simplify adding new codecs */ void decode_free(VGMSTREAM* vgmstream) { -#if VGM_TEST_DECODER - free(vgmstream->decode_state); -#endif + decode_state_free(vgmstream); if (!vgmstream->codec_data) return; @@ -88,6 +90,10 @@ void decode_free(VGMSTREAM* vgmstream) { free_ea_mt(vgmstream->codec_data, vgmstream->channels); } + if (vgmstream->coding_type == coding_KA1A) { + free_ka1a(vgmstream->codec_data); + } + #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type == coding_FFmpeg) { free_ffmpeg(vgmstream->codec_data); @@ -151,9 +157,7 @@ void decode_free(VGMSTREAM* vgmstream) { void decode_seek(VGMSTREAM* vgmstream) { -#if VGM_TEST_DECODER decode_state_reset(vgmstream); -#endif if (!vgmstream->codec_data) return; @@ -199,6 +203,10 @@ void decode_seek(VGMSTREAM* vgmstream) { seek_ea_mt(vgmstream, vgmstream->loop_current_sample); } + if (vgmstream->coding_type == coding_KA1A) { + seek_ka1a(vgmstream, vgmstream->loop_current_sample); + } + #ifdef VGM_USE_VORBIS if (vgmstream->coding_type == coding_OGG_VORBIS) { seek_ogg_vorbis(vgmstream->codec_data, vgmstream->loop_current_sample); @@ -256,9 +264,7 @@ void decode_seek(VGMSTREAM* vgmstream) { void decode_reset(VGMSTREAM* vgmstream) { -#if VGM_TEST_DECODER decode_state_reset(vgmstream); -#endif if (!vgmstream->codec_data) return; @@ -314,6 +320,10 @@ void decode_reset(VGMSTREAM* vgmstream) { reset_ea_mt(vgmstream); } + if (vgmstream->coding_type == coding_KA1A) { + reset_ka1a(vgmstream->codec_data); + } + #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) if (vgmstream->coding_type == coding_MP4_AAC) { reset_mp4_aac(vgmstream); @@ -459,7 +469,7 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) { case coding_PCM4_U: case coding_IMA_int: case coding_DVI_IMA_int: - case coding_NW_IMA: + case coding_CAMELOT_IMA: case coding_WV6_IMA: case coding_HV_IMA: case coding_FFTA2_IMA: @@ -675,7 +685,7 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) { case coding_IMA_int: case coding_DVI_IMA: case coding_DVI_IMA_int: - case coding_NW_IMA: + case coding_CAMELOT_IMA: case coding_WV6_IMA: case coding_HV_IMA: case coding_FFTA2_IMA: @@ -857,77 +867,80 @@ bool decode_uses_internal_offset_updates(VGMSTREAM* vgmstream) { return vgmstream->coding_type == coding_MS_IMA || vgmstream->coding_type == coding_MS_IMA_mono; } -#if VGM_TEST_DECODER -// decode frames for decoders which have their own sample buffer -static void decode_frames(sbuf_t* sbuf, VGMSTREAM* vgmstream) { - const int max_empty = 10000; + +// decode frames for decoders which decode frame by frame and have their own sample buffer +static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream) { + const int max_empty = 1000; int num_empty = 0; - decode_state_t* ds = vgmstream->decode_state; + sbuf_t* ssrc = &ds->sbuf; - while (sbuf->filled < sbuf->samples) { - // decode new frame if all was consumed - if (ds->sbuf.filled == 0) { + // fill the external buf by decoding N times; may read partially that buf + while (sdst->filled < sdst->samples) { + + // decode new frame if prev one was consumed + if (ssrc->filled == 0) { bool ok = false; switch (vgmstream->coding_type) { - case coding_TAC: - ok = decode_tac_frame(vgmstream); + case coding_KA1A: + ok = decode_ka1a_frame(vgmstream); break; default: - break; + goto decode_fail; } if (!ok) goto decode_fail; } + // decoder may not fill the buffer in a few calls in some codecs, but more it's probably a bug + if (ssrc->filled == 0) { + num_empty++; + if (num_empty > max_empty) { + VGM_LOG("VGMSTREAM: deadlock?\n"); + goto decode_fail; + } + } + if (ds->discard) { - // decode may signal that decoded samples need to be discarded, because of encoder delay - // (first samples of a file need to be ignored) or a loop - int current_discard = ds->discard; - if (current_discard > ds->sbuf.filled) - current_discard = ds->sbuf.filled; + // decoder may signal that samples need to be discarded (ex. encoder delay or during loops) + int samples_discard = ds->discard; + if (samples_discard > ssrc->filled) + samples_discard = ssrc->filled; - sbuf_consume(&ds->sbuf, current_discard); - - ds->discard -= current_discard; + sbuf_consume(ssrc, samples_discard); + ds->discard -= samples_discard; + // there may be more discard in next loop } else { // copy + consume - int samples_copy = ds->sbuf.filled; - if (samples_copy > sbuf->samples - sbuf->filled) - samples_copy = sbuf->samples - sbuf->filled; + int samples_copy = sbuf_get_copy_max(sdst, ssrc); - sbuf_copy_segments(sbuf, &ds->sbuf); - sbuf_consume(&ds->sbuf, samples_copy); - - sbuf->filled += samples_copy; + sbuf_copy_segments(sdst, ssrc, samples_copy); + sbuf_consume(ssrc, samples_copy); } } return; decode_fail: - /* on error just put some 0 samples */ - VGM_LOG("VGMSTREAM: decode fail, missing %i samples\n", sbuf->samples - sbuf->filled); - sbuf_silence_rest(sbuf); + //TODO clean ssrc? + //* on error just put some 0 samples + VGM_LOG("VGMSTREAM: decode fail, missing %i samples\n", sdst->samples - sdst->filled); + sbuf_silence_rest(sdst); } -#endif + /* Decode samples into the buffer. Assume that we have written samples_filled into the * buffer already, and we have samples_to_do consecutive samples ahead of us (won't call * more than one frame if configured above to do so). * Called by layouts since they handle samples written/to_do */ -void decode_vgmstream(VGMSTREAM* vgmstream, int samples_filled, int samples_to_do, sample_t* buffer) { -#if VGM_TEST_DECODER - sbuf_t sbuf_tmp = {0}; - sbuf_t* sbuf = &sbuf_tmp; - sbuf_init_s16(sbuf, buffer, samples_filled + samples_to_do, vgmstream->channels); - sbuf->filled = samples_filled; -#endif +void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) { int ch; - buffer += samples_filled * vgmstream->channels; /* passed externally to simplify I guess */ + //TODO: this cast isn't correct for float sbuf-decoders but shouldn't be used/matter (for buffer+ch below) + int16_t* buffer = sdst->buf; + buffer += sdst->filled * vgmstream->channels; // passed externally to decoders to simplify I guess //samples_to_do -= samples_filled; /* pre-adjusted */ switch (vgmstream->coding_type) { @@ -1323,9 +1336,9 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_filled, int samples_to_d } break; } - case coding_NW_IMA: + case coding_CAMELOT_IMA: for (ch = 0; ch < vgmstream->channels; ch++) { - decode_nw_ima(&vgmstream->ch[ch], buffer+ch, + decode_camelot_ima(&vgmstream->ch[ch], buffer+ch, vgmstream->channels, vgmstream->samples_into_block, samples_to_do); } break; @@ -1660,11 +1673,14 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_filled, int samples_to_d decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); } break; - default: -#if VGM_TEST_DECODER - decode_frames(sbuf, vgmstream); -#endif + + default: { + sbuf_t stmp = *sdst; + stmp.samples = stmp.filled + samples_to_do; //TODO improve + + decode_frames(&stmp, vgmstream); break; + } } } diff --git a/Frameworks/vgmstream/vgmstream/src/base/decode.h b/Frameworks/vgmstream/vgmstream/src/base/decode.h index 4731eab47..deef71870 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/decode.h +++ b/Frameworks/vgmstream/vgmstream/src/base/decode.h @@ -3,16 +3,14 @@ #include "../vgmstream.h" -#if VGM_TEST_DECODER void* decode_init(); -#endif void decode_free(VGMSTREAM* vgmstream); void decode_seek(VGMSTREAM* vgmstream); void decode_reset(VGMSTREAM* vgmstream); /* Decode samples into the buffer. Assume that we have written samples_filled into the * buffer already, and we have samples_to_do consecutive samples ahead of us. */ -void decode_vgmstream(VGMSTREAM* vgmstream, int samples_filled, int samples_to_do, sample_t* buffer); +void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do); /* Detect loop start and save values, or detect loop end and restore (loop back). Returns true if loop was done. */ bool decode_do_loop(VGMSTREAM* vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/base/decode_state.h b/Frameworks/vgmstream/vgmstream/src/base/decode_state.h index 64bf72671..3ad712742 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/decode_state.h +++ b/Frameworks/vgmstream/vgmstream/src/base/decode_state.h @@ -1,13 +1,11 @@ #ifndef _DECODE_STATE_H #define _DECODE_STATE_H -#if VGM_TEST_DECODER #include "sbuf.h" typedef struct { int discard; sbuf_t sbuf; } decode_state_t; -#endif #endif diff --git a/Frameworks/vgmstream/vgmstream/src/base/info.c b/Frameworks/vgmstream/vgmstream/src/base/info.c index b1b94b17f..f6610ed60 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/info.c +++ b/Frameworks/vgmstream/vgmstream/src/base/info.c @@ -171,6 +171,21 @@ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) { concatn(length,desc,temp); } + sfmt_t sfmt = mixing_get_input_sample_type(vgmstream); + if (sfmt != SFMT_S16) { + const char* sfmt_desc; + switch(sfmt) { + case SFMT_FLT: sfmt_desc = "float"; break; + case SFMT_F32: sfmt_desc = "float32"; break; + case SFMT_S16: sfmt_desc = "pcm16"; break; + default: sfmt_desc = "???"; + } + + snprintf(temp,TEMPSIZE, "sample type: %s\n", sfmt_desc); + concatn(length,desc,temp); + } + + if (vgmstream->config_enabled) { int32_t samples = vgmstream->pstate.play_duration; @@ -178,6 +193,7 @@ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) { snprintf(temp,TEMPSIZE, "play duration: %d samples (%1.0f:%06.3f seconds)\n", samples, time_mm, time_ss); concatn(length,desc,temp); } + } void describe_vgmstream_info(VGMSTREAM* vgmstream, vgmstream_info* info) { diff --git a/Frameworks/vgmstream/vgmstream/src/base/mixer.c b/Frameworks/vgmstream/vgmstream/src/base/mixer.c index 7ce06043f..272fca0c2 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/mixer.c +++ b/Frameworks/vgmstream/vgmstream/src/base/mixer.c @@ -70,6 +70,34 @@ bool mixer_is_active(mixer_t* mixer) { return false; } +// TODO: probably could be pre-initialized +static void setup_mixbuf(mixer_t* mixer, sbuf_t* sbuf) { + sbuf_t* smix = &mixer->smix; + + // mixbuf can be interpreted as FLT or F32; try to use src's to keep buf as-is (less rounding errors) + if (sbuf->fmt == SFMT_F32 || sbuf->fmt == SFMT_FLT) + sbuf_init(smix, sbuf->fmt, mixer->mixbuf, sbuf->filled, sbuf->channels); //mixer->input_channels + else + sbuf_init(smix, SFMT_F32, mixer->mixbuf, sbuf->filled, sbuf->channels); + + // remix to temp buf (somehow using float buf rather than int32 is faster?) + sbuf_copy_segments(smix, sbuf, sbuf->filled); +} + +static void setup_outbuf(mixer_t* mixer, sbuf_t* sbuf) { + sbuf_t* smix = &mixer->smix; //TODO: probably could be pre-initialized + + // setup + remix to output buf (buf is expected to be big enough to handle config) + sbuf->channels = mixer->output_channels; + sbuf->filled = 0; + smix->channels = mixer->output_channels; + if (mixer->force_type) { + sbuf->fmt = mixer->force_type; + } + + sbuf_copy_segments(sbuf, smix, smix->filled); +} + void mixer_process(mixer_t* mixer, sbuf_t* sbuf, int32_t current_pos) { /* external */ @@ -78,46 +106,36 @@ void mixer_process(mixer_t* mixer, sbuf_t* sbuf, int32_t current_pos) { /* try to skip if no fades apply (set but does nothing yet) + only has fades * (could be done in mix op but avoids upgrading bufs in some cases) */ - mixer->current_subpos = 0; if (mixer->has_fade) { //;VGM_LOG("MIX: fade test %i, %i\n", data->has_non_fade, mixer_op_fade_is_active(data, current_pos, current_pos + sample_count)); if (!mixer->has_non_fade && !mixer_op_fade_is_active(mixer, current_pos, current_pos + sbuf->filled)) return; - - //;VGM_LOG("MIX: fade pos=%i\n", current_pos); - mixer->current_subpos = current_pos; } - // remix to temp buf for mixing (somehow using float buf rather than int32 is faster?) - sbuf_copy_to_f32(mixer->mixbuf, sbuf); + mixer->current_subpos = current_pos; - // apply mixing ops in order. current_channels may increase or decrease per op + setup_mixbuf(mixer, sbuf); + + // apply mixing ops in order. channesl in mixersmix may increase or decrease per op // - 2ch w/ "1+2,1u" = ch1+ch2, ch1(add and push rest) = 3ch: ch1' ch1+ch2 ch2 // - 2ch w/ "1u" = downmix to 1ch (current_channels decreases once) - mixer->current_channels = mixer->input_channels; for (int m = 0; m < mixer->chain_count; m++) { mix_op_t* mix = &mixer->chain[m]; //TO-DO: set callback switch(mix->type) { - case MIX_SWAP: mixer_op_swap(mixer, sbuf->filled, mix); break; - case MIX_ADD: mixer_op_add(mixer, sbuf->filled, mix); break; - case MIX_VOLUME: mixer_op_volume(mixer, sbuf->filled, mix); break; - case MIX_LIMIT: mixer_op_limit(mixer, sbuf->filled, mix); break; - case MIX_UPMIX: mixer_op_upmix(mixer, sbuf->filled, mix); break; - case MIX_DOWNMIX: mixer_op_downmix(mixer, sbuf->filled, mix); break; - case MIX_KILLMIX: mixer_op_killmix(mixer, sbuf->filled, mix); break; - case MIX_FADE: mixer_op_fade(mixer, sbuf->filled, mix); + case MIX_SWAP: mixer_op_swap(mixer, mix); break; + case MIX_ADD: mixer_op_add(mixer, mix); break; + case MIX_VOLUME: mixer_op_volume(mixer, mix); break; + case MIX_LIMIT: mixer_op_limit(mixer, mix); break; + case MIX_UPMIX: mixer_op_upmix(mixer, mix); break; + case MIX_DOWNMIX: mixer_op_downmix(mixer, mix); break; + case MIX_KILLMIX: mixer_op_killmix(mixer, mix); break; + case MIX_FADE: mixer_op_fade(mixer, mix); default: break; } } - // setup + remix to output buf (buf is expected to be big enough to handle config) - sbuf->channels = mixer->output_channels; - if (mixer->force_type) { - sbuf->fmt = mixer->force_type; - } - - sbuf_copy_from_f32(sbuf, mixer->mixbuf); + setup_outbuf(mixer, sbuf); } diff --git a/Frameworks/vgmstream/vgmstream/src/base/mixer_ops_common.c b/Frameworks/vgmstream/vgmstream/src/base/mixer_ops_common.c index cabf27ded..fc0b15565 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/mixer_ops_common.c +++ b/Frameworks/vgmstream/vgmstream/src/base/mixer_ops_common.c @@ -5,138 +5,147 @@ // when there are no actual float ops (ex. 'swap', if no ' volume' ) // Performance gain is probably fairly small, though. -void mixer_op_swap(mixer_t* mixer, int32_t sample_count, mix_op_t* op) { - float* sbuf = mixer->mixbuf; +void mixer_op_swap(mixer_t* mixer, mix_op_t* op) { + sbuf_t* smix = &mixer->smix; + float* dst = smix->buf; - for (int s = 0; s < sample_count; s++) { - float temp_f = sbuf[op->ch_dst]; - sbuf[op->ch_dst] = sbuf[op->ch_src]; - sbuf[op->ch_src] = temp_f; + for (int s = 0; s < smix->filled; s++) { + float temp_f = dst[op->ch_dst]; + dst[op->ch_dst] = dst[op->ch_src]; + dst[op->ch_src] = temp_f; - sbuf += mixer->current_channels; + dst += smix->channels; } } -void mixer_op_add(mixer_t* mixer, int32_t sample_count, mix_op_t* op) { - float* sbuf = mixer->mixbuf; +void mixer_op_add(mixer_t* mixer, mix_op_t* op) { + sbuf_t* smix = &mixer->smix; + float* dst = smix->buf; /* could optimize when vol == 1 to avoid one multiplication but whatevs (not common) */ - for (int s = 0; s < sample_count; s++) { - sbuf[op->ch_dst] = sbuf[op->ch_dst] + sbuf[op->ch_src] * op->vol; + for (int s = 0; s < smix->filled; s++) { + dst[op->ch_dst] = dst[op->ch_dst] + dst[op->ch_src] * op->vol; - sbuf += mixer->current_channels; + dst += smix->channels; } } -void mixer_op_volume(mixer_t* mixer, int32_t sample_count, mix_op_t* op) { - float* sbuf = mixer->mixbuf; +void mixer_op_volume(mixer_t* mixer, mix_op_t* op) { + sbuf_t* smix = &mixer->smix; + float* dst = smix->buf; if (op->ch_dst < 0) { /* "all channels", most common case */ - for (int s = 0; s < sample_count * mixer->current_channels; s++) { - sbuf[s] = sbuf[s] * op->vol; + for (int s = 0; s < smix->filled * smix->channels; s++) { + dst[s] = dst[s] * op->vol; } } else { - for (int s = 0; s < sample_count; s++) { - sbuf[op->ch_dst] = sbuf[op->ch_dst] * op->vol; + for (int s = 0; s < smix->filled; s++) { + dst[op->ch_dst] = dst[op->ch_dst] * op->vol; - sbuf += mixer->current_channels; + dst += smix->channels; } } } -void mixer_op_limit(mixer_t* mixer, int32_t sample_count, mix_op_t* op) { - float* sbuf = mixer->mixbuf; +void mixer_op_limit(mixer_t* mixer, mix_op_t* op) { + sbuf_t* smix = &mixer->smix; + float* dst = smix->buf; - const float limiter_max = 32767.0f; - const float limiter_min = -32768.0f; + const float limiter_max = smix->fmt == SFMT_FLT ? 1.0f : 32767.0f; + const float limiter_min = smix->fmt == SFMT_FLT ? -1.0f : -32768.0f; const float temp_max = limiter_max * op->vol; const float temp_min = limiter_min * op->vol; /* could optimize when vol == 1 to avoid one multiplication but whatevs (not common) */ - for (int s = 0; s < sample_count; s++) { + for (int s = 0; s < smix->filled; s++) { if (op->ch_dst < 0) { - for (int ch = 0; ch < mixer->current_channels; ch++) { - if (sbuf[ch] > temp_max) - sbuf[ch] = temp_max; - else if (sbuf[ch] < temp_min) - sbuf[ch] = temp_min; + for (int ch = 0; ch < smix->channels; ch++) { + if (dst[ch] > temp_max) + dst[ch] = temp_max; + else if (dst[ch] < temp_min) + dst[ch] = temp_min; } } else { - if (sbuf[op->ch_dst] > temp_max) - sbuf[op->ch_dst] = temp_max; - else if (sbuf[op->ch_dst] < temp_min) - sbuf[op->ch_dst] = temp_min; + if (dst[op->ch_dst] > temp_max) + dst[op->ch_dst] = temp_max; + else if (dst[op->ch_dst] < temp_min) + dst[op->ch_dst] = temp_min; } - sbuf += mixer->current_channels; + dst += smix->channels; } } -void mixer_op_upmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) { - int max_channels = mixer->current_channels; - mixer->current_channels += 1; +void mixer_op_upmix(mixer_t* mixer, mix_op_t* op) { + sbuf_t* smix = &mixer->smix; + float* sbuf = smix->buf; - float* sbuf_tmp = mixer->mixbuf + sample_count * mixer->current_channels; - float* sbuf = mixer->mixbuf + sample_count * max_channels; + int max_channels = smix->channels; + smix->channels += 1; + + float* dst = sbuf + smix->filled * smix->channels; + float* src = sbuf + smix->filled * max_channels; /* copy 'backwards' as otherwise would overwrite samples before moving them forward */ - for (int s = 0; s < sample_count; s++) { - sbuf_tmp -= mixer->current_channels; - sbuf -= max_channels; + for (int s = 0; s < smix->filled; s++) { + dst -= smix->channels; + src -= max_channels; int sbuf_ch = max_channels - 1; - for (int ch = mixer->current_channels - 1; ch >= 0; ch--) { + for (int ch = smix->channels - 1; ch >= 0; ch--) { if (ch == op->ch_dst) { - sbuf_tmp[ch] = 0; /* inserted as silent */ + dst[ch] = 0; // inserted as silent } else { - sbuf_tmp[ch] = sbuf[sbuf_ch]; /* 'pull' channels backward */ + dst[ch] = src[sbuf_ch]; // 'pull' channels backward sbuf_ch--; } } } } -void mixer_op_downmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) { - int max_channels = mixer->current_channels; - mixer->current_channels -= 1; +void mixer_op_downmix(mixer_t* mixer, mix_op_t* op) { + sbuf_t* smix = &mixer->smix; + float* src = smix->buf; + float* dst = smix->buf; - float* sbuf = mixer->mixbuf; - float* sbuf_tmp = sbuf; + int max_channels = smix->channels; + smix->channels -= 1; - for (int s = 0; s < sample_count; s++) { + for (int s = 0; s < smix->filled; s++) { for (int ch = 0; ch < op->ch_dst; ch++) { - sbuf_tmp[ch] = sbuf[ch]; /* copy untouched channels */ + dst[ch] = src[ch]; // copy untouched channels } for (int ch = op->ch_dst; ch < max_channels - 1; ch++) { - sbuf_tmp[ch] = sbuf[ch + 1]; /* 'pull' dropped channels back */ + dst[ch] = src[ch + 1]; // 'pull' dropped channels back } - sbuf_tmp += mixer->current_channels; - sbuf += max_channels; + dst += smix->channels; + src += max_channels; } } -void mixer_op_killmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) { - int max_channels = mixer->current_channels; - mixer->current_channels = op->ch_dst; /* clamp channels */ +void mixer_op_killmix(mixer_t* mixer, mix_op_t* op) { + sbuf_t* smix = &mixer->smix; + float* src = smix->buf; + float* dst = smix->buf; - float* sbuf = mixer->mixbuf; - float* sbuf_tmp = sbuf; + int max_channels = smix->channels; + smix->channels = op->ch_dst; // clamp channels - for (int s = 0; s < sample_count; s++) { - for (int ch = 0; ch < mixer->current_channels; ch++) { - sbuf_tmp[ch] = sbuf[ch]; + for (int s = 0; s < smix->filled; s++) { + for (int ch = 0; ch < smix->channels; ch++) { + dst[ch] = src[ch]; } - sbuf_tmp += mixer->current_channels; - sbuf += max_channels; + dst += smix->channels; + src += max_channels; } } diff --git a/Frameworks/vgmstream/vgmstream/src/base/mixer_ops_fade.c b/Frameworks/vgmstream/vgmstream/src/base/mixer_ops_fade.c index f0670da07..0591c07e4 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/mixer_ops_fade.c +++ b/Frameworks/vgmstream/vgmstream/src/base/mixer_ops_fade.c @@ -2,6 +2,8 @@ #include #include +//TODO: could precalculate tables + interpolate for some performance gain + #define MIXING_PI 3.14159265358979323846f static inline float get_fade_gain_curve(char shape, float index) { @@ -112,36 +114,34 @@ static bool get_fade_gain(mix_op_t* op, float* out_cur_vol, int32_t current_subp return true; } -void mixer_op_fade(mixer_t* mixer, int32_t sample_count, mix_op_t* mix) { - float* sbuf = mixer->mixbuf; +void mixer_op_fade(mixer_t* mixer, mix_op_t* mix) { + sbuf_t* smix = &mixer->smix; + float* dst = smix->buf; float new_gain = 0.0f; - int channels = mixer->current_channels; + int channels = smix->channels; int32_t current_subpos = mixer->current_subpos; //TODO optimize for case 0? - for (int s = 0; s < sample_count; s++) { + for (int s = 0; s < smix->filled; s++) { bool fade_applies = get_fade_gain(mix, &new_gain, current_subpos); if (!fade_applies) //TODO optimize? continue; if (mix->ch_dst < 0) { for (int ch = 0; ch < channels; ch++) { - sbuf[ch] = sbuf[ch] * new_gain; + dst[ch] = dst[ch] * new_gain; } } else { - sbuf[mix->ch_dst] = sbuf[mix->ch_dst] * new_gain; + dst[mix->ch_dst] = dst[mix->ch_dst] * new_gain; } - sbuf += channels; + dst += channels; current_subpos++; } - - mixer->current_subpos = current_subpos; } - bool mixer_op_fade_is_active(mixer_t* mixer, int32_t current_start, int32_t current_end) { for (int i = 0; i < mixer->chain_count; i++) { diff --git a/Frameworks/vgmstream/vgmstream/src/base/mixer_priv.h b/Frameworks/vgmstream/vgmstream/src/base/mixer_priv.h index 73fe757e7..5a7cd6fd4 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/mixer_priv.h +++ b/Frameworks/vgmstream/vgmstream/src/base/mixer_priv.h @@ -49,20 +49,20 @@ struct mixer_t { bool has_non_fade; bool has_fade; - float* mixbuf; /* internal mixing buffer */ - int current_channels; /* state: channels may increase/decrease during ops */ - int32_t current_subpos; /* state: current sample pos in the stream */ + float* mixbuf; // internal mixing buffer + sbuf_t smix; // temp sbuf + int32_t current_subpos; // state: current sample pos in the stream - sfmt_t force_type; + sfmt_t force_type; // mixer output is original buffer's by default, unless forced }; -void mixer_op_swap(mixer_t* mixer, int32_t sample_count, mix_op_t* op); -void mixer_op_add(mixer_t* mixer, int32_t sample_count, mix_op_t* op); -void mixer_op_volume(mixer_t* mixer, int32_t sample_count, mix_op_t* op); -void mixer_op_limit(mixer_t* mixer, int32_t sample_count, mix_op_t* op); -void mixer_op_upmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op); -void mixer_op_downmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op); -void mixer_op_killmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op); -void mixer_op_fade(mixer_t* mixer, int32_t sample_count, mix_op_t* op); +void mixer_op_swap(mixer_t* mixer, mix_op_t* op); +void mixer_op_add(mixer_t* mixer, mix_op_t* op); +void mixer_op_volume(mixer_t* mixer, mix_op_t* op); +void mixer_op_limit(mixer_t* mixer, mix_op_t* op); +void mixer_op_upmix(mixer_t* mixer, mix_op_t* op); +void mixer_op_downmix(mixer_t* mixer, mix_op_t* op); +void mixer_op_killmix(mixer_t* mixer, mix_op_t* op); +void mixer_op_fade(mixer_t* mixer, mix_op_t* op); bool mixer_op_fade_is_active(mixer_t* mixer, int32_t current_start, int32_t current_end); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/base/mixing.c b/Frameworks/vgmstream/vgmstream/src/base/mixing.c index 3db330d27..fd7e22aea 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/mixing.c +++ b/Frameworks/vgmstream/vgmstream/src/base/mixing.c @@ -143,9 +143,13 @@ void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_chan } sfmt_t mixing_get_input_sample_type(VGMSTREAM* vgmstream) { - // TODO: check vgmstream // TODO: on layered/segments, detect biggest value and use that (ex. if one of the layers uses flt > flt) - return SFMT_S16; + switch(vgmstream->coding_type) { + case coding_KA1A: + return SFMT_FLT; + default: + return SFMT_S16; + } } sfmt_t mixing_get_output_sample_type(VGMSTREAM* vgmstream) { diff --git a/Frameworks/vgmstream/vgmstream/src/base/render.c b/Frameworks/vgmstream/vgmstream/src/base/render.c index de878d4f4..bdabb6e17 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/render.c +++ b/Frameworks/vgmstream/vgmstream/src/base/render.c @@ -73,7 +73,6 @@ void render_reset(VGMSTREAM* vgmstream) { } int render_layout(sbuf_t* sbuf, VGMSTREAM* vgmstream) { - void* buf = sbuf->buf; int sample_count = sbuf->samples; if (sample_count == 0) @@ -90,10 +89,10 @@ int render_layout(sbuf_t* sbuf, VGMSTREAM* vgmstream) { switch (vgmstream->layout_type) { case layout_interleave: - render_vgmstream_interleave(buf, sample_count, vgmstream); + render_vgmstream_interleave(sbuf, vgmstream); break; case layout_none: - render_vgmstream_flat(buf, sample_count, vgmstream); + render_vgmstream_flat(sbuf, vgmstream); break; case layout_blocked_mxch: case layout_blocked_ast: @@ -134,7 +133,7 @@ int render_layout(sbuf_t* sbuf, VGMSTREAM* vgmstream) { case layout_blocked_ubi_sce: case layout_blocked_tt_ad: case layout_blocked_vas: - render_vgmstream_blocked(buf, sample_count, vgmstream); + render_vgmstream_blocked(sbuf, vgmstream); break; case layout_segmented: render_vgmstream_segmented(sbuf, vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/base/sbuf.c b/Frameworks/vgmstream/vgmstream/src/base/sbuf.c index e6d49c511..5f7538ce0 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/sbuf.c +++ b/Frameworks/vgmstream/vgmstream/src/base/sbuf.c @@ -3,6 +3,7 @@ //#include #include "../util.h" #include "sbuf.h" +#include "../util/log.h" void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels) { @@ -14,19 +15,15 @@ void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels } void sbuf_init_s16(sbuf_t* sbuf, int16_t* buf, int samples, int channels) { - memset(sbuf, 0, sizeof(sbuf_t)); - sbuf->buf = buf; - sbuf->samples = samples; - sbuf->channels = channels; - sbuf->fmt = SFMT_S16; + sbuf_init(sbuf, SFMT_S16, buf, samples, channels); } void sbuf_init_f32(sbuf_t* sbuf, float* buf, int samples, int channels) { - memset(sbuf, 0, sizeof(sbuf_t)); - sbuf->buf = buf; - sbuf->samples = samples; - sbuf->channels = channels; - sbuf->fmt = SFMT_F32; + sbuf_init(sbuf, SFMT_F32, buf, samples, channels); +} + +void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels) { + sbuf_init(sbuf, SFMT_FLT, buf, samples, channels); } @@ -50,19 +47,19 @@ void* sbuf_get_filled_buf(sbuf_t* sbuf) { return buf; } -void sbuf_consume(sbuf_t* sbuf, int count) { +void sbuf_consume(sbuf_t* sbuf, int samples) { int sample_size = sfmt_get_sample_size(sbuf->fmt); - if (sample_size <= 0) + if (sample_size <= 0) //??? return; - if (count > sbuf->samples || count > sbuf->filled) //TODO? + if (samples > sbuf->samples || samples > sbuf->filled) //??? return; uint8_t* buf = sbuf->buf; - buf += count * sbuf->channels * sample_size; + buf += samples * sbuf->channels * sample_size; sbuf->buf = buf; - sbuf->filled -= count; - sbuf->samples -= count; + sbuf->filled -= samples; + sbuf->samples -= samples; } /* when casting float to int, value is simply truncated: @@ -115,8 +112,6 @@ void sbuf_copy_to_f32(float* dst, sbuf_t* sbuf) { } break; } - - case SFMT_FLT: case SFMT_F32: { float* src = sbuf->buf; for (int s = 0; s < sbuf->filled * sbuf->channels; s++) { @@ -124,6 +119,13 @@ void sbuf_copy_to_f32(float* dst, sbuf_t* sbuf) { } break; } + case SFMT_FLT: { + float* src = sbuf->buf; + for (int s = 0; s < sbuf->filled * sbuf->channels; s++) { + dst[s] = src[s] * 32768.0f; + } + break; + } default: break; } @@ -157,6 +159,15 @@ void sbuf_copy_from_f32(sbuf_t* sbuf, float* src) { } } +// max samples to copy from ssrc to sdst, considering that dst may be partially filled +int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc) { + int sdst_max = sdst->samples - sdst->filled; + int samples_copy = ssrc->filled; + if (samples_copy > sdst_max) + samples_copy = sdst_max; + return samples_copy; +} + /* ugly thing to avoid repeating functions */ #define sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max) \ @@ -174,25 +185,29 @@ void sbuf_copy_from_f32(sbuf_t* sbuf, float* src) { dst[dst_pos++] = float_to_int(src[src_pos++] * value); \ } -void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc) { - /* uncommon so probably fine albeit slower-ish, 0'd other channels first */ +// copy N samples from ssrc into dst (should be clamped externally) +void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc, int samples_copy) { + if (ssrc->channels != sdst->channels) { - sbuf_silence_part(sdst, sdst->filled, ssrc->filled); + // 0'd other channels first (uncommon so probably fine albeit slower-ish) + sbuf_silence_part(sdst, sdst->filled, samples_copy); sbuf_copy_layers(sdst, ssrc, 0, ssrc->filled); #if 0 - // "faster" but lots of extra ifs, not worth it + // "faster" but lots of extra ifs per sample format, not worth it while (src_pos < src_max) { for (int ch = 0; ch < dst_channels; ch++) { dst[dst_pos++] = ch >= src_channels ? 0 : src[src_pos++]; } } #endif + //TODO: may want to handle externally? + sdst->filled += samples_copy; return; } int src_pos = 0; int dst_pos = sdst->filled * sdst->channels; - int src_max = ssrc->filled * ssrc->channels; + int src_max = samples_copy * ssrc->channels; // define all posible combos, probably there is a better way to handle this but... @@ -239,6 +254,9 @@ void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc) { float* src = ssrc->buf; sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, (1/32768.0f)); } + + //TODO: may want to handle externally? + sdst->filled += samples_copy; } diff --git a/Frameworks/vgmstream/vgmstream/src/base/sbuf.h b/Frameworks/vgmstream/vgmstream/src/base/sbuf.h index 02a56061c..e3a31542a 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/sbuf.h +++ b/Frameworks/vgmstream/vgmstream/src/base/sbuf.h @@ -34,6 +34,7 @@ typedef struct { void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels); void sbuf_init_s16(sbuf_t* sbuf, int16_t* buf, int samples, int channels); void sbuf_init_f32(sbuf_t* sbuf, float* buf, int samples, int channels); +void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels); int sfmt_get_sample_size(sfmt_t fmt); @@ -43,9 +44,11 @@ void* sbuf_get_filled_buf(sbuf_t* sbuf); void sbuf_consume(sbuf_t* sbuf, int count); /* helpers to copy between buffers; note they assume dst and src aren't the same buf */ +int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc); + void sbuf_copy_to_f32(float* dst, sbuf_t* sbuf); void sbuf_copy_from_f32(sbuf_t* sbuf, float* src); -void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc); +void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc, int samples_copy); void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int expected); void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index 6eb1a3faf..7e14359c9 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -51,8 +51,10 @@ atrac9_codec_data* init_atrac9(atrac9_config* cfg) { data->data_buffer_size = data->info.superframeSize; /* extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though) */ data->data_buffer = calloc(data->data_buffer_size + 0x10, sizeof(uint8_t)); + if (!data->data_buffer) goto fail; /* while ATRAC9 uses float internally, Sony's API only returns PCM16 */ data->sample_buffer = calloc(data->info.channels * data->info.frameSamples * data->info.framesInSuperframe, sizeof(sample_t)); + if (!data->sample_buffer) goto fail; data->samples_to_discard = cfg->encoder_delay; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 6e0524387..7d2600a60 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -20,7 +20,7 @@ void g72x_init_state(struct g72x_state* state_ptr); /* ima_decoder */ void decode_standard_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first); -void decode_nw_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_camelot_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_snds_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_otns_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_wv6_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -109,12 +109,13 @@ int32_t pcm8_bytes_to_samples(size_t bytes, int channels); void decode_psx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags, int config); void decode_psx_configurable(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size, int config); void decode_psx_pivotal(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); -int ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end); -int ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end); +bool ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end); +bool ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end); +bool ps_find_stream_info(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end, uint32_t* p_stream_size); size_t ps_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty); size_t ps_bytes_to_samples(size_t bytes, int channels); size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels); -int ps_check_format(STREAMFILE* sf, off_t offset, size_t max); +bool ps_check_format(STREAMFILE* sf, off_t offset, size_t max); /* psv_decoder */ @@ -372,9 +373,6 @@ typedef struct tac_codec_data tac_codec_data; tac_codec_data* init_tac(STREAMFILE* sf); void decode_tac(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do); -#if VGM_TEST_DECODER -bool decode_tac_frame(VGMSTREAM* vgmstream); -#endif void reset_tac(tac_codec_data* data); void seek_tac(tac_codec_data* data, int32_t num_sample); void free_tac(tac_codec_data* data); @@ -390,6 +388,16 @@ void seek_ice(ice_codec_data* data, int32_t num_sample); void free_ice(ice_codec_data* data); +/* ka1a_decoder */ +typedef struct ka1a_codec_data ka1a_codec_data; + +ka1a_codec_data* init_ka1a(int bitrate_mode, int channels_tracks); +void free_ka1a(ka1a_codec_data* data); +void reset_ka1a(ka1a_codec_data* data); +bool decode_ka1a_frame(VGMSTREAM* vgmstream); +void seek_ka1a(VGMSTREAM* v, int32_t num_sample); + + #ifdef VGM_USE_VORBIS /* ogg_vorbis_decoder */ typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index 6bff9c2cf..69f83db38 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -124,8 +124,8 @@ static void std_ima_expand_nibble_mul(VGMSTREAMCHANNEL * stream, off_t byte_offs if (*step_index > 88) *step_index=88; } -/* NintendoWare IMA (Mario Golf, Mario Tennis; maybe other Camelot games) */ -static void nw_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { +/* Camelot IMA (Mario Golf, Mario Tennis; maybe other Camelot games) */ +static void camelot_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { int sample_nibble, sample_decoded, step, delta; sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; @@ -418,7 +418,7 @@ void decode_mtf_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa stream->adpcm_step_index = step_index; } -void decode_nw_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_camelot_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i, sample_count; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -431,7 +431,7 @@ void decode_nw_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspac off_t byte_offset = stream->offset + i/2; int nibble_shift = (i&1?4:0); //low nibble order - nw_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + camelot_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ka1a_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ka1a_decoder.c new file mode 100644 index 000000000..17d23a953 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/ka1a_decoder.c @@ -0,0 +1,146 @@ +#include "coding.h" +#include "../base/decode_state.h" +#include "libs/ka1a_dec.h" + + +/* opaque struct */ +struct ka1a_codec_data { + uint8_t* buf; + float* fbuf; + + int frame_size; + void* handle; +}; + + +ka1a_codec_data* init_ka1a(int bitrate_mode, int channels_tracks) { + ka1a_codec_data* data = NULL; + int buf_size; + + data = calloc(1, sizeof(ka1a_codec_data)); + if (!data) goto fail; + + data->handle = ka1a_init(bitrate_mode, channels_tracks, 1); + if (!data->handle) goto fail; + + data->frame_size = ka1a_get_frame_size(data->handle); + if (data->frame_size <= 0) goto fail; + + buf_size = data->frame_size * channels_tracks; + data->buf = calloc(buf_size, sizeof(uint8_t)); + if (!data->buf) goto fail; + + data->fbuf = calloc(KA1A_FRAME_SAMPLES * channels_tracks, sizeof(float)); + if (!data->fbuf) goto fail; + + return data; +fail: + free_ka1a(data); + return NULL; +} + +static bool read_ka1a_frame(VGMSTREAM* v) { + ka1a_codec_data* data = v->codec_data; + int bytes; + + if (v->codec_config) { + int block = data->frame_size; + + // interleaved mode: read from each channel separately and mix in buf + for (int ch = 0; ch < v->channels; ch++) { + VGMSTREAMCHANNEL* vs = &v->ch[ch]; + + bytes = read_streamfile(data->buf + block * ch, vs->offset, block, vs->streamfile); + if (bytes != block) + return false; + + vs->offset += bytes; + } + } + else { + // single block of frames + int block = data->frame_size * v->channels; + VGMSTREAMCHANNEL* vs = &v->ch[0]; + + bytes = read_streamfile(data->buf, vs->offset, block, vs->streamfile); + if (bytes != block) + return false; + + vs->offset += bytes; + } + + return true; +} + +bool decode_ka1a_frame(VGMSTREAM* v) { + bool ok = read_ka1a_frame(v); + if (!ok) + return false; + + decode_state_t* ds = v->decode_state; + ka1a_codec_data* data = v->codec_data; + + int samples = ka1a_decode(data->handle, data->buf, data->fbuf); + if (samples < 0) + return false; + + sbuf_init_flt(&ds->sbuf, data->fbuf, KA1A_FRAME_SAMPLES, v->channels); + ds->sbuf.filled = samples; + + return true; +} + +void reset_ka1a(ka1a_codec_data* data) { + if (!data || !data->handle) return; + + ka1a_reset(data->handle); +} + +void seek_ka1a(VGMSTREAM* v, int32_t num_sample) { + ka1a_codec_data* data = v->codec_data; + decode_state_t* ds = v->decode_state; + if (!data) return; + + reset_ka1a(data); + + // find closest offset to desired sample + int32_t seek_frame = num_sample / KA1A_FRAME_SAMPLES; + int32_t seek_sample = num_sample % KA1A_FRAME_SAMPLES; + + ds->discard = seek_sample; + + if (v->codec_config) { + uint32_t seek_offset = seek_frame * data->frame_size; + + if (v->loop_ch) { + for (int ch = 0; ch < v->channels; ch++) { + v->loop_ch[ch].offset = v->loop_ch[ch].channel_start_offset + seek_offset; + } + } + } + else { + uint32_t seek_offset = seek_frame * data->frame_size * v->channels; + + if (v->loop_ch) { + v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + seek_offset; + } + } + + // (due to implicit encode delay the above is byte-exact equivalent vs a discard loop) + #if 0 + ds->discard = num_sample; + if (v->loop_ch) { + v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset; + } + #endif +} + +void free_ka1a(ka1a_codec_data* data) { + if (!data) return; + + if (data->handle) + ka1a_free(data->handle); + free(data->buf); + free(data->fbuf); + free(data); +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/libs/icelib.c b/Frameworks/vgmstream/vgmstream/src/coding/libs/icelib.c index e44bdd4b7..b2d130c75 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/libs/icelib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/libs/icelib.c @@ -9,7 +9,7 @@ //TODO change to streaming decoder // Currently lib expects most data in memory. Due to how format is designed it's not the -// easiest thing to change, to be fixed it later: +// easiest thing to change, to be fixed later: // - data is divided into 2 blocks (intro+body) that are decoded separatedly // (streaming should read up to block max) // - code data isn't divided into frames, just keeps reading from the file buf @@ -30,36 +30,11 @@ //#include "zlib.h" #include "../../util/zlib_vgmstream.h" +#include "../../util/reader_get.h" #define ICESND_MAX_CHANNELS 2 -/* ************************************************************ */ -/* COMMON */ -/* ************************************************************ */ - -static inline uint8_t get_u8(const uint8_t* p) { - uint8_t ret; - ret = ((uint16_t)(const uint8_t)p[0]) << 0; - return ret; -} - -static inline uint16_t get_u16le(const uint8_t* p) { - uint16_t ret; - ret = ((uint16_t)(const uint8_t)p[0]) << 0; - ret |= ((uint16_t)(const uint8_t)p[1]) << 8; - return ret; -} - -static inline uint32_t get_u32le(const uint8_t* p) { - uint32_t ret; - ret = ((uint32_t)(const uint8_t)p[0]) << 0; - ret |= ((uint32_t)(const uint8_t)p[1]) << 8; - ret |= ((uint32_t)(const uint8_t)p[2]) << 16; - ret |= ((uint32_t)(const uint8_t)p[3]) << 24; - return ret; -} - /* bigrp entry info as read from header */ typedef struct { uint32_t hash1; /* usually matches filename, different files vary on bytes, seems internally used to identify files */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec.c b/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec.c new file mode 100644 index 000000000..794f49971 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec.c @@ -0,0 +1,636 @@ +#include +#include +#include +#include +#include + +#include "ka1a_dec.h" +#include "ka1a_dec_data.h" +#include "../../util/reader_get.h" + +/* Decodes Koei Tecmo's KA1A, a fairly simple transform-based (FFT) mono codec. + * + * The codec seems nameless (it has a "_CODECNAME" string) so this is named after streamed files' + * fourCC. It's somewhat inefficient (not very packed) but simple so maybe designed for speed. + * OG code isn't too optimized though. + * + * Reverse engineered from exes, thanks to Kelebek1 and AceKombat for help and debugging. + * Output has been compared to memdumps and should be accurate with minor +-diffs (vs MSVC 22 /O2). + * + * Even though some parts can be simplified/optimized code tries to emulate what source code + * may look like, undoing unrolled/vectorized parts. Functions marked as 'inline' don't exist in + * decomp but surely were part of the source code, while 'unused' args may be remants/compilation details. + * + * If you are going to use this info/code elsewhere kindly credit your sources. It's the right thing to do. + */ + + +// Gets frame info based on bitrate mode, to unpack 1 frame. +// OG code calls this per frame but codec is CBR (single bitrate index) plus values +// could be precalculated per bitrate index (remnant of VBR or more complex modes?) +static void get_frame_info(int bitrate_index, int* p_steps_size, int* p_coefs_size) { + int coefs_bits = 0; + int steps_bits = 0; + + // first 8 bands use 8-bit codes and step is implicit + for (int i = 0; i < 8; i++) { + int codes = BAND_CODES[bitrate_index][i]; + coefs_bits += 8 * codes; + } + + if (bitrate_index <= 5) { + // lower bitrate modes have one 8-bit code, rest is 4-bit + coefs_bits += (MAX_BANDS - 8) * 8; + for (int i = 8; i < MAX_BANDS; i++) { + int step_bits = BAND_STEP_BITS[i]; + int codes = BAND_CODES[bitrate_index][i]; + steps_bits += step_bits * codes; + coefs_bits += 4 * (codes - 1); + } + } + else { + // higher bitrate modes use 8-bit codes + for (int i = 8; i < MAX_BANDS; i++) { + int step_bits = BAND_STEP_BITS[i]; + int codes = BAND_CODES[bitrate_index][i]; + steps_bits += step_bits * codes; + coefs_bits += 8 * codes; + } + } + + // bits to bytes + padding + *p_steps_size = (steps_bits + 7) >> 3; + *p_coefs_size = (coefs_bits + 7) >> 3; +} + +// Helper used in related functions, but not during decode. Note that 'mode' must be validated externally (-5..5). +// In practice values are: 0x60, 0x68, 0x73, 0x7d, 0x8c, 0x9b, 0xad, 0xc2, 0xd7, 0xed, 0x102. +static int get_frame_size(int bitrate_mode) { + int scalefactor_size = 0x04; + int steps_size = 0; + int coefs_size = 0; + get_frame_info(bitrate_mode + BITRATE_INDEX_MODIFIER, &steps_size, &coefs_size); + return scalefactor_size + steps_size + coefs_size; +} + + +// Convert 8-bit signed code as exp +// (note that 0.086643398 being float is important to get results closer to memdumps) +static inline float unpack_convert_code(uint8_t code, float scalefactor) { + float coef; + if (code) { + float code_f = (int8_t)code; + if (code & 0x80) { + code_f = -code_f; + scalefactor = -scalefactor; + } + + coef = expf((code_f - 127.0f) * 0.086643398f) * scalefactor; + } + else { + coef = 0.0; + } + + return coef; +} + +// Adjust current coef by -1.0..1.0 (4-bit subcode values 0..14 * 1/7 to -1.0..1.0; code 15 seems unused). +// (note that 0.14285715f being float is important to get results closer to memdumps) +static inline float unpack_convert_subcode(uint8_t code, float coef) { + return ((code * 0.14285715f) - 1.0f) * coef; +} + +// Get N bits (max 8) from data, MSB order. +// Doesn't check boundaries, but should never past src as bits come from fixed tables. +static inline int unpack_get_bits(uint8_t* src, int* p_byte_pos, int* p_bit_pos, int bits) { + int value = 0; + int byte_pos = *p_byte_pos; + int bit_pos = *p_bit_pos; + + int next_bitpos = bit_pos + bits; + if (next_bitpos > 8) { + // read between 2 bytes + if (next_bitpos <= 16) { // more shouldn't happen + uint32_t mask_lo = (1 << (8 - bit_pos)) - 1; + uint32_t mask_hi = (1 << (next_bitpos - 8)) - 1; + uint8_t code_lo = src[byte_pos+0]; + uint8_t code_hi = src[byte_pos+1]; + value = ((code_hi & mask_hi) << (8 - bit_pos)) + ((code_lo >> bit_pos) & mask_lo); + } + } + else { + // read in current byte + uint32_t mask = (1 << bits) - 1; + uint8_t code = src[byte_pos]; + value = (code >> bit_pos) & mask; + } + + bit_pos += bits; + if (next_bitpos >= 8) { + bit_pos = next_bitpos - 8; + byte_pos++; + } + + *p_byte_pos = byte_pos; + *p_bit_pos = bit_pos; + return value; +} + +// Unpack a single frame into quantized spectrum coefficients, packed like this: +// - 1 scalefactor (32-bit float) +// - N coef sub-positions aka steps (4-7 bits) per higher bands (8..21) +// - N codes (8-bit) per lower bands (0..7), of implicit positions +// - 1 main code (8-bit) per higher bands 8..21 then (N-1) coefs (8 or 4-bit) per bands +// +// Each code is converted to a coef then saved to certain position to dst buf. +// Lower bitrate modes use 4-bit codes that are relative to main coef (* +-1.0). +// +// Bands encode less coefs than dst may hold, so 'positions' are used to put coefs +// non-linearly, where unset indexes are 0 (dst must be memset before calling unpack frame). +// dst should be 1024, though usually only lower 512 are used (max step is 390 + ((1<<7) - 1)). +static void unpack_frame(uint8_t* src, float* dst, int steps_size, void* unused, int bitrate_index) { + + // copy coefs counts as they may be modified below + int band_codes_tmp[MAX_BANDS]; + for (int i = 0; i < MAX_BANDS; i++) { + band_codes_tmp[i] = BAND_CODES[bitrate_index][i]; + } + + // read base scalefactor (first 4 bytes) and setup buffers + float scalefactor = get_f32le(src); + uint8_t* src_steps = &src[0x04]; + uint8_t* src_codes = &src[0x04 + steps_size]; + + // negative scalefactor signals more/less codes for some bands (total doesn't change though) + if (scalefactor < 0.0f) { + scalefactor = -scalefactor; + + int mod = BITRATE_SUBMODE[bitrate_index]; + for (int i = 8; i < 12; i++) { + band_codes_tmp[i] += mod; + } + for (int i = 17; i < 21; i++) { + band_codes_tmp[i] -= mod; + } + } + + // coefs from lower bands (in practice fixed to 5 * 8) + int code_pos = 0; + for (int band = 0; band < 8; band++) { + int band_codes = band_codes_tmp[band]; + for (int i = 0; i < band_codes; i++) { + uint8_t code = src_codes[code_pos]; + dst[code_pos] = unpack_convert_code(code, scalefactor); + code_pos++; + } + } + + // simple bitreading helpers (struct?) + int br_bytepos = 0; + int br_bitpos = 0; // in current byte + + int subcode_pos = code_pos + (MAX_BANDS - 8); // position after bands 8..21 main coef + + uint8_t code; + float coef; + int substep; + + if (bitrate_index <= 5) { + // lower bitrates encode 1 main 8-bit coef per band and rest is main * +-1.0, position info in a bitstream + bool high_flag = false; + for (int band = 8; band < MAX_BANDS; band++) { + int band_codes = band_codes_tmp[band]; + int band_step = BAND_STEPS[band]; + int step_bits = BAND_STEP_BITS[band]; + + substep = unpack_get_bits(src_steps, &br_bytepos, &br_bitpos, step_bits); + + code = src_codes[code_pos]; + code_pos++; + + coef = unpack_convert_code(code, scalefactor); + dst[band_step + substep] = coef; + + for (int i = 1; i < band_codes; i++) { + substep = unpack_get_bits(src_steps, &br_bytepos, &br_bitpos, step_bits); + + code = src_codes[subcode_pos]; + if (high_flag) + subcode_pos++; + + uint8_t subcode = high_flag ? + (code >> 4) & 0x0F : + (code >> 0) & 0x0F; + + high_flag = !high_flag; + + dst[band_step + substep] = unpack_convert_subcode(subcode, coef); + } + } + } + else { + // higher bitrates encode all coefs normally, but still use lower bitrates' ordering scheme (see above) + for (int band = 8; band < MAX_BANDS; band++) { + int band_codes = band_codes_tmp[band]; + int band_step = BAND_STEPS[band]; + int step_bits = BAND_STEP_BITS[band]; + + substep = unpack_get_bits(src_steps, &br_bytepos, &br_bitpos, step_bits); + + code = src_codes[code_pos]; + code_pos++; + + coef = unpack_convert_code(code, scalefactor); + dst[band_step + substep] = coef; + + for (int i = 1; i < band_codes; i++) { + substep = unpack_get_bits(src_steps, &br_bytepos, &br_bitpos, step_bits); + + code = src_codes[subcode_pos]; + subcode_pos++; + + coef = unpack_convert_code(code, scalefactor); + dst[band_step + substep] = coef; + } + } + } +} + + +static void transform_twiddles(int points, float* real, float* imag, const float* tw_real, const float* tw_imag) { + for (int i = 0; i < points; i++) { + float coef_real = real[i]; + float coef_imag = imag[i]; + float twid_real = tw_real[i]; + float twid_imag = tw_imag[i]; + + real[i] = (twid_real * coef_real) - (twid_imag * coef_imag); + imag[i] = (twid_imag * coef_real) + (twid_real * coef_imag); + } +} + +static inline void transform_bit_reversal_permutation(int points, float* real, float* imag) { + const int half = points >> 1; + + int j = 0; + for (int i = 1; i < points; i++) { + + // j is typically calculated via subs of m, unsure if manual or compiler optimization + j = half ^ j; + int m = half; + while (m > j) { + m >>= 1; + j = m ^ j; + } + + if (i < j) { + float coef_real = real[i]; + float coef_imag = imag[i]; + real[i] = real[j]; + imag[i] = imag[j]; + real[j] = coef_real; + imag[j] = coef_imag; + } + } +} + +static void transform_fft(int points, void* unused, float* real, float* imag, const float* cos_table, const float* sin_table) { + const int half = points >> 1; + + transform_bit_reversal_permutation(points, real, imag); + + // these are actually the same value, so OG compilation only uses the cos_table one; added both for completeness + float w_real_base = cos_table[points >> 3]; + float w_imag_base = sin_table[points >> 3]; + + // FFT computation using twiddle factors and sub-ffts, probably some known optimization + for (int m = 4; m <= points; m <<= 1) { // 0.. (log2(256) / 2) + int m4 = m >> 2; + + for (int j = m4; j > 0; j >>= 2) { + int min = m4 - j; + int max = m4 - (j >> 1); + int i_md = min + 2 * m4; + + for (int k = min; k < max; k++) { + int i_lo = i_md - m4; + int i_hi = i_md + m4; + + float coef_im_a = imag[k] - imag[i_lo]; + float coef_re_a = real[k] - real[i_lo]; + real[k] = real[i_lo] + real[k]; + imag[k] = imag[i_lo] + imag[k]; + + float coef_re_b = real[i_hi] - real[i_md]; + float coef_im_b = imag[i_hi] - imag[i_md]; + float tmp_ra_ib = coef_re_a - coef_im_b; + float tmp_rb_ia = coef_re_b + coef_im_a; + float tmp_ib_ra = coef_im_b + coef_re_a; + float tmp_ia_rb = coef_im_a - coef_re_b; + + real[i_md] = real[i_hi] + real[i_md]; + imag[i_md] = imag[i_hi] + imag[i_md]; + real[i_lo] = tmp_ra_ib; + imag[i_lo] = tmp_rb_ia; + real[i_hi] = tmp_ib_ra; + imag[i_hi] = tmp_ia_rb; + + i_md++; + } + } + + if (m >= points) + continue; + + for (int j = m4; j > 0; j >>= 2) { + int min = m + m4 - j; + int max = m + m4 - (j >> 1); + int i_md = min + 2 * m4; + + for (int k = min; k < max; k++) { + int i_lo = i_md - m4; + int i_hi = i_md + m4; + + float coef_im_a = imag[k] - imag[i_lo]; + float coef_re_a = real[k] - real[i_lo]; + real[k] = real[i_lo] + real[k]; + imag[k] = imag[i_lo] + imag[k]; + + float coef_re_b = real[i_hi] - real[i_md]; + float coef_im_b = imag[i_hi] - imag[i_md]; + float tmp_ra_ib = coef_re_a - coef_im_b; + float tmp_rb_ia = coef_re_b + coef_im_a; + float tmp_ib_ra = coef_im_b + coef_re_a; + float tmp_ia_rb = coef_im_a - coef_re_b; + + real[i_md] = real[i_hi] + real[i_md]; + imag[i_md] = imag[i_hi] + imag[i_md]; + real[i_lo] = (tmp_rb_ia + tmp_ra_ib) * w_real_base; + imag[i_lo] = (tmp_rb_ia - tmp_ra_ib) * w_real_base; + real[i_hi] = (tmp_ia_rb - tmp_ib_ra) * w_imag_base; + imag[i_hi] = (-tmp_ia_rb - tmp_ib_ra) * w_imag_base; + + i_md++; + } + } + + int tmp_j = half; + for (int m2 = m * 2; m2 < points; m2 += m) { + // ??? + int tmp_m = half; + for (tmp_j ^= tmp_m; tmp_m > tmp_j; tmp_j ^= tmp_m) { + tmp_m = tmp_m >> 1; + } + + int table_index = tmp_j >> 2; + float w_real1 = cos_table[table_index]; + float w_imag1 = -sin_table[table_index]; + float w_real3 = cos_table[table_index * 3]; + float w_imag3 = -sin_table[table_index * 3]; + + for (int j = m4; j > 0; j >>= 2) { + int min = m2 + m4 - j; + int max = m2 + m4 - (j >> 1); + int i_md = min + 2 * m4; + + for (int k = min; k < max; k++) { + int i_lo = i_md - m4; + int i_hi = i_md + m4; + + float coef_im_a = imag[k] - imag[i_lo]; + float coef_re_a = real[k] - real[i_lo]; + real[k] = real[i_lo] + real[k]; + imag[k] = imag[i_lo] + imag[k]; + + float coef_im_b = imag[i_hi] - imag[i_md]; + float coef_re_b = real[i_hi] - real[i_md]; + float tmp_ra_ib = coef_re_a - coef_im_b; + float tmp_rb_ia = coef_re_b + coef_im_a; + float tmp_ib_ra = coef_im_b + coef_re_a; + float tmp_ia_rb = coef_im_a - coef_re_b; + + real[i_md] = real[i_hi] + real[i_md]; + imag[i_md] = imag[i_hi] + imag[i_md]; + real[i_lo] = (tmp_ra_ib * w_real1) - (tmp_rb_ia * w_imag1); + imag[i_lo] = (tmp_ra_ib * w_imag1) + (tmp_rb_ia * w_real1); + real[i_hi] = (tmp_ib_ra * w_real3) - (tmp_ia_rb * w_imag3); + imag[i_hi] = (tmp_ib_ra * w_imag3) + (tmp_ia_rb * w_real3); + + i_md++; + } + } + } + } + + // final swapping + for (int m = half; m > 0; m >>= 2) { + int min = half - m; + int max = half - (m >> 1); + + for (int k = min; k < max; k++) { + float coef_im = imag[k] - imag[k + half]; + float coef_re = real[k] - real[k + half]; + real[k] = real[k + half] + real[k]; + imag[k] = imag[k + half] + imag[k]; + real[k + half] = coef_re; + imag[k + half] = coef_im; + } + } +} + +// Transform unpacked time-domain coefficients (spectrum) to samples using inverse FFT. +// Seemingly a variation/simplification of the Cooley-Tukey algorithm (radix-4?). +void transform_frame(void* unused1, float* src, float* dst, void* unused2, float* fft_buf) { + float* real = fft_buf; + float* imag = fft_buf + 256; + + // initialize buffers from src + for (int i = 0; i < 256; i++) { + real[i] = src[i * 2]; + imag[255 - i] = src[i * 2 + 1]; + } + + transform_twiddles(256, real, imag, TWIDDLES_REAL, TWIDDLES_IMAG); + transform_fft(256, NULL, real, imag, COS_TABLE, SIN_TABLE); + transform_twiddles(256, real, imag, TWIDDLES_REAL, TWIDDLES_IMAG); + + // Scale results by (1 / 512) + for (int i = 0; i < 256; i++) { + real[i] *= 0.001953125f; + imag[i] *= 0.001953125f; + } + + // Reorder output (input buf may be reused as output here as there is no overlap). + // Note that input is 512 coefs but output is 1024 samples (externally combined with prev samples) + int pos = 0; + for (int i = 0; i < 128; i++) { + dst[pos++] = real[128 + i]; + dst[pos++] = -imag[127 - i]; + } + for (int i = 0; i < 256; i++) { + dst[pos++] = imag[i]; + dst[pos++] = -real[255 - i]; + } + for (int i = 0; i < 128; i++) { + dst[pos++] = -real[i]; + dst[pos++] = imag[255 - i]; + } +} + +// Decodes a block of frames (see .h) +// +// To get 512 samples decoder needs to combine samples from prev + current frame (MP3 granule-style?). +// though will only output samples from current. prev-frame can be optionally used to setup overlapping +// samples with 'setup_flag'. Since decoding current-frame will also setup the overlap for next frame, +// prev data and predecode-flag are only needed on init or after seeking. +// +// Original decoder expects 2 blocks in src (1 frame * channels * tracks): src[0] = prev, src[block-size] = curr +// (even if prev isn't used). This isn't very flexible, so this decoder expects only 1 block. +// Probably setup this odd way due to how data is read/handled in KT's engine. +static void decode_frame(unsigned char* src, int tracks, int channels, float* dst, int bitrate_mode, int setup_flag, float* prev, float* temp) { + float* fft_buf = &temp[0]; //size 512 * 2 + float* coefs = &temp[512 * 2]; //size 512 * 2 + + int bitrate_index = bitrate_mode + BITRATE_INDEX_MODIFIER; + int steps_size = 0; + int coefs_size = 0; + get_frame_info(bitrate_index, &steps_size, &coefs_size); + int frame_size = 0x04 + steps_size + coefs_size; + + // decode 'prev block of frames' (optional as it just setups 'prev' buf, no samples are written) + if (setup_flag) { + uint8_t* src_block = &src[0]; // 1st block in src + + for (int track = 0; track < tracks; track++) { + int frame_num = channels * track; + + for (int ch = 0; ch < channels; ch++) { + uint8_t* frame = &src_block[frame_num * frame_size]; + + memset(coefs, 0, FRAME_SAMPLES * sizeof(float)); + unpack_frame(frame, coefs, steps_size, NULL, bitrate_index); + transform_frame(NULL, coefs, coefs, NULL, fft_buf); + + int interleave = frame_num * FRAME_SAMPLES; + for (int i = 0; i < FRAME_SAMPLES; i++) { + // save samples for 'current block of frames' and overlap + prev[interleave + i] = coefs[512 + i] * OVERLAP_WINDOW[511 - i]; + } + + frame_num++; + } + } + } + + if (setup_flag) // OG MOD: changed to expect only 1 block per call + return; + + // decode 'current block of frames' (writes 512 samples, plus setups 'prev' buf) + { + //uint8_t* src_block = &src[channels * tracks * frame_size]; // 2nd block in src in OG code + uint8_t* src_block = &src[0]; // OG MOD: changed to expect only 1 block per call + + for (int track = 0; track < tracks; track++) { + int frame_num = channels * track; + + float* dst_track = &dst[frame_num * FRAME_SAMPLES]; + for (int ch = 0; ch < channels; ch++) { + uint8_t* frame = &src_block[frame_num * frame_size]; + + memset(coefs, 0, FRAME_SAMPLES * sizeof(float)); + unpack_frame(frame, coefs, steps_size, NULL, bitrate_index); + transform_frame(NULL, coefs, coefs, NULL, fft_buf); + + int interleave = frame_num * FRAME_SAMPLES; + for (int i = 0; i < FRAME_SAMPLES; i++) { + coefs[i] *= OVERLAP_WINDOW[i]; + coefs[512 + i] *= OVERLAP_WINDOW[511 - i]; + dst_track[i * channels + ch] = coefs[i] + prev[interleave + i]; + } + + // save overlapped samples for next + memcpy(&prev[interleave], &coefs[512], FRAME_SAMPLES * sizeof(float)); + + frame_num++; + } + } + } +} + +//----------------------------------------------------------------------------- +// API (not part of original code) + +struct ka1a_handle_t { + // config + int bitrate_mode; + int channels; + int tracks; + + // state + bool setup_flag; // next frame will be used as setup and won't output samples + float temp[1024 * 2]; // fft + spectrum coefs buf + float* prev; // at least samples * channels * tracks +}; + +ka1a_handle_t* ka1a_init(int bitrate_mode, int channels, int tracks) { + + int bitrate_index = bitrate_mode + BITRATE_INDEX_MODIFIER; + if (bitrate_index < 0 || bitrate_index >= MAX_BITRATES) + return NULL; + + if (channels * tracks <= 0 || channels * tracks > MAX_CHANNELS_TRACKS) + return NULL; + + ka1a_handle_t* ctx = calloc(1, sizeof(ka1a_handle_t)); + if (!ctx) goto fail; + + ctx->prev = calloc(1, FRAME_SAMPLES * channels * tracks * sizeof(float)); + if (!ctx) goto fail; + + ctx->bitrate_mode = bitrate_mode; + ctx->channels = channels; + ctx->tracks = tracks; + + ka1a_reset(ctx); + + return ctx; +fail: + ka1a_free(ctx); + return NULL; +} + +void ka1a_free(ka1a_handle_t* ctx) { + if (!ctx) + return; + + free(ctx->prev); + free(ctx); +} + +void ka1a_reset(ka1a_handle_t* ctx) { + if (!ctx) + return; + + ctx->setup_flag = true; + // no need to reset buffers as on next decode frame will be used to setup them. +} + +int ka1a_decode(ka1a_handle_t* ctx, unsigned char* src, float* dst) { + if (!ctx) + return -1; + + decode_frame(src, ctx->tracks, ctx->channels, dst, ctx->bitrate_mode, ctx->setup_flag, ctx->prev, ctx->temp); + + if (ctx->setup_flag) { + ctx->setup_flag = false; + return 0; + } + + return FRAME_SAMPLES; +} + +int ka1a_get_frame_size(ka1a_handle_t* ctx) { + if (!ctx) + return 0; + return get_frame_size(ctx->bitrate_mode); +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec.h b/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec.h new file mode 100644 index 000000000..4c3fe3739 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec.h @@ -0,0 +1,42 @@ +#ifndef _KA1A_DEC_ +#define _KA1A_DEC_ + +/* Decodes Koei Tecmo's KA1A, a fairly simple transform-based (FFT) mono codec. */ + + +//#define KA1A_FRAME_SIZE_MAX 0x200 +#define KA1A_FRAME_SAMPLES 512 + + +typedef struct ka1a_handle_t ka1a_handle_t; + +/* Inits decoder. + * - bitrate_mode: value from header (-5..5) + * - channels: Nch-interleaved tracks + * - tracks: number of parts of N-ch + * + * Channel/tracks define final interleaved output per ka1a_decode: + * [track0 ch0 ch1 ch0 ch1... x512][track1 ch0 ch1 ch0 ch1... x512]... + * Codec is mono though, so this can be safely reinterpreted, ex. channels = tracks * channels, tracks = 1: + * [track0 ch0 ch1 ch3 ch4 ch5 ch6... x512] + * or even make N single decoders per track/channel and pass single frames. + */ +ka1a_handle_t* ka1a_init(int bitrate_mode, int channels, int tracks); + +void ka1a_free(ka1a_handle_t* handle); + +void ka1a_reset(ka1a_handle_t* handle); + +/* Decodes one block of data. + * Returns samples done, 0 on setup or negative or error. + * After init/reset next decode won't input samples (similar to encoder delay). + * + * src should have frame_size * channels * tracks. + * dst should have KA1A_FRAME_SAMPLES * channels * tracks (see init for interleave info). + */ +int ka1a_decode(ka1a_handle_t* handle, unsigned char* src, float* dst); + +// Get current frame size for one single frame. +int ka1a_get_frame_size(ka1a_handle_t* handle); + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec_data.h b/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec_data.h new file mode 100644 index 000000000..fb7271b32 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/libs/ka1a_dec_data.h @@ -0,0 +1,260 @@ +#ifndef _KA1A_DEC_DATA_ +#define _KA1A_DEC_DATA_ + +#define MAX_CHANNELS_TRACKS 32 //arbitrary max + +#define FRAME_SAMPLES 512 +#define MAX_BANDS 21 +#define FFT_POINTS 256 +#define MAX_BITRATES 11 + +// bitrate mode in header is defined from -5 to 5, where negative are lower bitrate modes which use +// less resolution for some codes. Related functions need to add +5 to index so it's pretty pointless. +#define BITRATE_INDEX_MODIFIER 5 + +// default number of quantized coefficients encoded per band, for each bitrate modes +static const int BAND_CODES[MAX_BITRATES][MAX_BANDS] = { + {5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, }, + {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, }, + {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, }, + {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }, + {5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, }, + {5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, }, + {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, }, + {5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, }, + {5, 5, 5, 5, 5, 5, 5, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, }, + {5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, }, + {5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, }, +}; + +// Number of modified coefs to be added/substracted to some bands, for each bitrate mode (varies per frame) +// Total per 1 band shouldn't go over 10. +static const int BITRATE_SUBMODE[MAX_BITRATES] = { + 0, 0, 0, 2, 2, 2, 4, 3, 2, 1, 0, +}; + +// base positions in dst buffer for coefs in frame. A sub-position (implicit or from a bitstream) sets +// the final index, which doesn't need to be linear. +// ex. band 13 may write 6 coefs to dst[120 + step], where step may be 0, 11, 6, 2, 8, 13 +// (max 19; unset indexes are implicitly 0) +static const int BAND_STEPS[MAX_BANDS] = { + 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 120, 140, 170, 200, 240, 300, 390, +}; + +// lower bands are 0 since all tables above are fixed to 8 +static const int BAND_STEP_BITS[MAX_BANDS] = { + 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 7, 7, +}; + +// 360 cosine, close to: for (0..256) t[i] = cos(2 * PI * i / points) with some rounding? +static const float COS_TABLE[FFT_POINTS] = { + 1.0, 0.99969882, 0.99879545, 0.99729043, 0.99518472, 0.99247956, 0.98917651, 0.98527765, + 0.98078525, 0.97570211, 0.97003126, 0.96377605, 0.95694035, 0.94952816, 0.94154406, 0.93299282, + 0.9238795, 0.91420972, 0.90398932, 0.8932243, 0.88192123, 0.87008697, 0.8577286, 0.84485358, + 0.8314696, 0.81758481, 0.80320752, 0.78834641, 0.77301043, 0.75720882, 0.74095112, 0.7242471, + 0.70710677, 0.68954051, 0.67155892, 0.65317279, 0.63439327, 0.61523157, 0.59569931, 0.57580817, + 0.55557019, 0.53499764, 0.5141027, 0.4928982, 0.47139665, 0.44961131, 0.42755511, 0.40524128, + 0.38268343, 0.35989496, 0.33688983, 0.31368166, 0.29028463, 0.26671275, 0.24298012, 0.21910122, + 0.19509023, 0.17096186, 0.1467305, 0.12241063, 0.098017134, 0.073564492, 0.04906765, 0.024541136, + -0.0000000437, -0.024541223, -0.049067739, -0.073564574, -0.098017223, -0.12241071, -0.14673057, -0.17096195, + -0.19509032, -0.21910131, -0.2429802, -0.26671284, -0.29028472, -0.31368172, -0.33688992, -0.35989505, + -0.38268352, -0.40524134, -0.42755508, -0.44961137, -0.47139683, -0.49289817, -0.51410276, -0.5349977, + -0.55557036, -0.57580817, -0.59569937, -0.61523169, -0.63439327, -0.65317285, -0.67155904, -0.68954068, + -0.70710677, -0.72424716, -0.74095124, -0.75720882, -0.77301049, -0.78834647, -0.80320764, -0.81758481, + -0.83146966, -0.84485364, -0.8577286, -0.87008703, -0.88192135, -0.8932243, -0.90398932, -0.91420978, + -0.92387962, -0.93299282, -0.94154412, -0.94952822, -0.95694035, -0.96377605, -0.97003126, -0.97570217, + -0.98078531, -0.98527765, -0.98917651, -0.9924795, -0.99518472, -0.99729049, -0.99879545, -0.99969882, + -1.0, -0.99969882, -0.99879545, -0.99729043, -0.99518472, -0.9924795, -0.98917651, -0.98527765, + -0.98078525, -0.97570211, -0.97003126, -0.96377605, -0.95694029, -0.94952816, -0.94154406, -0.93299276, + -0.9238795, -0.91420972, -0.90398926, -0.89322418, -0.88192123, -0.87008691, -0.85772854, -0.84485358, + -0.83146954, -0.81758469, -0.80320752, -0.78834641, -0.77301037, -0.7572087, -0.74095112, -0.72424704, + -0.70710665, -0.68954057, -0.67155892, -0.65317291, -0.63439333, -0.61523157, -0.59569919, -0.57580805, + -0.55557001, -0.53499734, -0.51410282, -0.4928982, -0.47139668, -0.44961122, -0.42755494, -0.40524107, + -0.38268313, -0.35989511, -0.33688986, -0.31368169, -0.29028454, -0.26671258, -0.24297991, -0.21910091, + -0.19509038, -0.17096189, -0.14673041, -0.12241054, -0.098016933, -0.073564284, -0.049067326, -0.024541287, + 0.0000000119, 0.024541309, 0.049067825, 0.073564783, 0.098017432, 0.12241104, 0.14673042, 0.17096192, + 0.19509041, 0.2191014, 0.24298041, 0.26671305, 0.29028502, 0.31368169, 0.33688989, 0.35989514, + 0.3826836, 0.40524155, 0.42755538, 0.44961166, 0.47139671, 0.49289823, 0.51410282, 0.53499776, + 0.55557042, 0.57580847, 0.59569925, 0.61523157, 0.63439333, 0.65317291, 0.6715591, 0.68954074, + 0.70710701, 0.72424704, 0.74095112, 0.75720888, 0.77301055, 0.78834653, 0.8032077, 0.81758499, + 0.8314696, 0.84485358, 0.85772866, 0.87008709, 0.88192135, 0.89322442, 0.90398943, 0.91420972, + 0.92387956, 0.93299282, 0.94154412, 0.94952828, 0.95694041, 0.96377617, 0.97003126, 0.97570211, + 0.98078531, 0.98527765, 0.98917657, 0.99247956, 0.99518478, 0.99729043, 0.99879545, 0.99969882, +}; + +// 360 sine, close to: for (0..256) t[i] = cos(2 * PI * i / points) with some rounding? +static const float SIN_TABLE[FFT_POINTS] = { + 0.0, 0.024541229, 0.049067676, 0.073564567, 0.098017141, 0.12241068, 0.14673047, 0.1709619, + 0.19509032, 0.21910124, 0.2429802, 0.26671278, 0.29028466, 0.31368175, 0.33688986, 0.35989505, + 0.38268346, 0.40524134, 0.42755508, 0.44961134, 0.47139674, 0.49289823, 0.51410276, 0.53499764, + 0.55557024, 0.57580823, 0.59569931, 0.61523163, 0.63439333, 0.65317285, 0.67155898, 0.68954057, + 0.70710677, 0.7242471, 0.74095118, 0.75720888, 0.77301043, 0.78834641, 0.80320752, 0.81758481, + 0.83146966, 0.84485358, 0.85772866, 0.87008697, 0.88192129, 0.8932243, 0.90398932, 0.91420978, + 0.9238795, 0.93299282, 0.94154406, 0.94952822, 0.95694035, 0.96377605, 0.97003126, 0.97570211, + 0.98078531, 0.98527765, 0.98917651, 0.99247956, 0.99518472, 0.99729043, 0.99879545, 0.99969882, + 1.0, 0.99969882, 0.99879545, 0.99729043, 0.99518472, 0.9924795, 0.98917651, 0.98527765, + 0.98078525, 0.97570211, 0.97003126, 0.96377605, 0.95694029, 0.94952816, 0.94154406, 0.93299282, + 0.9238795, 0.91420972, 0.90398932, 0.8932243, 0.88192123, 0.87008703, 0.8577286, 0.84485352, + 0.83146954, 0.81758481, 0.80320752, 0.78834635, 0.77301049, 0.75720882, 0.74095106, 0.72424698, + 0.70710677, 0.68954051, 0.67155886, 0.65317285, 0.63439327, 0.61523151, 0.59569913, 0.57580817, + 0.55557019, 0.53499746, 0.51410276, 0.49289814, 0.47139663, 0.44961137, 0.42755505, 0.40524122, + 0.38268328, 0.35989505, 0.3368898, 0.3136816, 0.29028472, 0.26671273, 0.24298008, 0.21910107, + 0.19509031, 0.17096181, 0.14673033, 0.1224107, 0.098017097, 0.073564447, 0.049067486, 0.02454121, + -0.000000087399997, -0.024541385, -0.049067661, -0.073564619, -0.098017268, -0.12241087, -0.1467305, -0.17096199, + -0.19509049, -0.21910124, -0.24298024, -0.2667129, -0.29028487, -0.31368178, -0.33688995, -0.3598952, + -0.38268343, -0.4052414, -0.42755523, -0.44961151, -0.47139677, -0.49289829, -0.51410288, -0.53499764, + -0.5555703, -0.57580835, -0.59569931, -0.61523163, -0.63439339, -0.65317297, -0.67155898, -0.68954062, + -0.70710689, -0.7242471, -0.74095118, -0.75720876, -0.77301043, -0.78834647, -0.80320758, -0.81758493, + -0.83146977, -0.84485376, -0.85772854, -0.87008697, -0.88192129, -0.89322436, -0.90398937, -0.91420984, + -0.92387968, -0.93299276, -0.94154406, -0.94952822, -0.95694035, -0.96377611, -0.97003132, -0.97570223, + -0.98078525, -0.98527765, -0.98917651, -0.99247956, -0.99518472, -0.99729049, -0.99879545, -0.99969882, + -1.0, -0.99969882, -0.99879545, -0.99729043, -0.99518472, -0.9924795, -0.98917651, -0.98527765, + -0.98078525, -0.97570211, -0.9700312, -0.96377599, -0.95694023, -0.94952822, -0.94154406, -0.93299276, + -0.92387944, -0.91420966, -0.90398914, -0.89322412, -0.88192129, -0.87008697, -0.85772854, -0.84485346, + -0.83146948, -0.81758463, -0.80320758, -0.78834641, -0.77301043, -0.75720876, -0.740951, -0.72424692, + -0.70710653, -0.68954062, -0.67155898, -0.65317279, -0.63439316, -0.61523145, -0.59569907, -0.57580793, + -0.5555703, -0.53499764, -0.5141027, -0.49289808, -0.47139654, -0.44961107, -0.42755479, -0.40524137, + -0.38268343, -0.35989496, -0.33688971, -0.31368154, -0.2902844, -0.2667124, -0.24298023, -0.21910122, +}; + +// similar but not quite: for (0..256) t[i] = cos(2 * PI * i / points); +static const float TWIDDLES_REAL[FFT_POINTS] = { + 0.9999997, 0.99997616, 0.999915, 0.99981618, 0.99967968, 0.99950558, 0.99929386, 0.99904448, + 0.99875754, 0.99843293, 0.99807078, 0.99767107, 0.99723375, 0.99675888, 0.99624652, 0.9956966, + 0.99510926, 0.99448442, 0.99382216, 0.99312246, 0.99238533, 0.99161088, 0.99079913, 0.98995006, + 0.98906368, 0.98814011, 0.98717928, 0.98618132, 0.98514622, 0.98407406, 0.98296481, 0.98181856, + 0.98063534, 0.97941524, 0.97815824, 0.9768644, 0.97553378, 0.97416645, 0.97276247, 0.97132182, + 0.96984458, 0.96833086, 0.96678072, 0.96519411, 0.96357119, 0.96191204, 0.96021664, 0.95848507, + 0.95671743, 0.95491374, 0.9530741, 0.95119864, 0.9492873, 0.94734025, 0.94535756, 0.94333923, + 0.94128537, 0.93919611, 0.9370715, 0.93491161, 0.93271649, 0.93048626, 0.92822099, 0.92592078, + 0.92358571, 0.92121589, 0.91881138, 0.9163723, 0.91389865, 0.91139066, 0.90884835, 0.90627176, + 0.90366107, 0.90101641, 0.89833778, 0.89562535, 0.89287919, 0.89009941, 0.88728613, 0.88443941, + 0.88155943, 0.87864625, 0.8757, 0.87272078, 0.86970866, 0.86666387, 0.86358637, 0.86047643, + 0.85733402, 0.85415941, 0.85095257, 0.84771371, 0.84444296, 0.84114039, 0.83780617, 0.83444041, + 0.83104324, 0.82761478, 0.82415515, 0.82066447, 0.8171429, 0.81359059, 0.81000769, 0.80639422, + 0.80275041, 0.79907632, 0.79537225, 0.7916382, 0.78787428, 0.7840808, 0.78025776, 0.77640527, + 0.77252364, 0.76861292, 0.76467323, 0.76070476, 0.75670767, 0.75268203, 0.74862808, 0.74454594, + 0.74043584, 0.73629779, 0.73213202, 0.72793871, 0.72371799, 0.71947002, 0.71519494, 0.71089298, + 0.70656419, 0.70220888, 0.6978271, 0.69341904, 0.68898481, 0.68452471, 0.68003887, 0.67552733, + 0.67099041, 0.66642827, 0.66184098, 0.65722877, 0.65259188, 0.64793038, 0.64324445, 0.63853431, + 0.63380021, 0.62904215, 0.62426049, 0.61945528, 0.61462677, 0.60977507, 0.60490042, 0.60000306, + 0.59508306, 0.59014064, 0.58517605, 0.58018941, 0.57518089, 0.57015073, 0.56509918, 0.56002629, + 0.5549323, 0.5498175, 0.54468191, 0.53952587, 0.5343495, 0.52915293, 0.52393651, 0.51870036, + 0.51344466, 0.50816965, 0.50287557, 0.4975625, 0.49223068, 0.48688033, 0.48151165, 0.47612482, + 0.47072011, 0.46529773, 0.45985776, 0.45440048, 0.44892606, 0.44343477, 0.43792677, 0.43240228, + 0.42686164, 0.42130479, 0.41573209, 0.41014373, 0.40453994, 0.39892092, 0.39328688, 0.38763815, + 0.3819747, 0.37629688, 0.3706049, 0.36489895, 0.35917926, 0.35344607, 0.34769964, 0.34194005, + 0.33616757, 0.33038244, 0.32458487, 0.31877509, 0.31295338, 0.30711982, 0.30127469, 0.2954182, + 0.28955057, 0.28367206, 0.27778289, 0.27188337, 0.26597348, 0.26005358, 0.2541239, 0.24818464, + 0.24223605, 0.23627833, 0.23031183, 0.22433653, 0.21835281, 0.21236086, 0.20636091, 0.20035319, + 0.19433793, 0.18831547, 0.1822858, 0.17624927, 0.1702061, 0.16415653, 0.15810078, 0.15203907, + 0.14597176, 0.13989884, 0.13382064, 0.1277374, 0.12164936, 0.11555674, 0.10945977, 0.10335879, + 0.097253807, 0.091145165, 0.085033081, 0.078917801, 0.072799556, 0.066678561, 0.060555179, 0.054429397, + 0.048301566, 0.042171918, 0.036040682, 0.029908087, 0.023774367, 0.01763987, 0.011504591, 0.0053688786, +}; + +// similar but not quite: for (0..256) t[i] = -sin(2 * PI * i / points); +static const float TWIDDLES_IMAG[] = { + -0.00076699042, -0.0069028586, -0.013038468, -0.019173585, -0.025307981, -0.031441424, -0.037573684, -0.043704528, + -0.049833726, -0.05596105, -0.062086266, -0.068209141, -0.074329458, -0.080446973, -0.086561449, -0.092672676, + -0.098780416, -0.10488442, -0.1109845, -0.11708038, -0.12317186, -0.12925872, -0.13534068, -0.14141756, + -0.14748912, -0.15355512, -0.15961535, -0.16566958, -0.17171754, -0.17775905, -0.18379387, -0.18982176, + -0.19584252, -0.20185591, -0.20786169, -0.21385963, -0.21984953, -0.22583117, -0.23180428, -0.23776868, + -0.24372412, -0.24967039, -0.25560728, -0.26153448, -0.26745188, -0.27335921, -0.27925625, -0.28514278, + -0.29101855, -0.2968834, -0.30273706, -0.3085793, -0.31440994, -0.32022873, -0.3260355, -0.33182994, + -0.33761194, -0.3433812, -0.34913751, -0.35488072, -0.36061054, -0.36632681, -0.37202924, -0.3777177, + -0.38339195, -0.38905174, -0.39469689, -0.40032718, -0.40594241, -0.41154233, -0.41712674, -0.42269552, + -0.42824832, -0.43378502, -0.43930539, -0.44480923, -0.45029631, -0.45576641, -0.4612194, -0.46665499, + -0.47207305, -0.47747329, -0.48285556, -0.48821968, -0.49356541, -0.49889252, -0.50420088, -0.50949031, + -0.51476049, -0.52001131, -0.52524251, -0.53045398, -0.53564543, -0.54081678, -0.54596776, -0.55109817, + -0.55620778, -0.56129652, -0.56636411, -0.57141036, -0.57643509, -0.58143818, -0.58641928, -0.59137839, + -0.59631521, -0.60122955, -0.60612124, -0.61099017, -0.61583608, -0.62065876, -0.62545812, -0.63023394, + -0.63498604, -0.63971418, -0.64441824, -0.6490981, -0.6537534, -0.6583842, -0.66299021, -0.66757119, + -0.67212707, -0.67665768, -0.68116277, -0.68564218, -0.69009584, -0.69452351, -0.69892502, -0.70330018, + -0.70764893, -0.71197104, -0.71626627, -0.72053456, -0.72477579, -0.72898966, -0.73317605, -0.73733491, + -0.74146605, -0.74556917, -0.74964428, -0.75369114, -0.75770962, -0.76169956, -0.76566088, -0.76959336, + -0.77349681, -0.77737117, -0.78121626, -0.78503191, -0.78881806, -0.79257452, -0.79630113, -0.79999769, + -0.80366421, -0.80730045, -0.81090623, -0.81448156, -0.81802624, -0.82154006, -0.825023, -0.82847482, + -0.83189553, -0.83528483, -0.83864272, -0.84196901, -0.84526366, -0.84852648, -0.85175729, -0.85495609, + -0.85812271, -0.86125702, -0.86435878, -0.86742812, -0.8704648, -0.8734687, -0.87643969, -0.87937772, + -0.88228261, -0.88515425, -0.88799256, -0.8907975, -0.89356887, -0.89630663, -0.89901066, -0.90168083, + -0.90431696, -0.90691912, -0.90948713, -0.91202086, -0.91452032, -0.91698533, -0.91941583, -0.92181164, + -0.92417276, -0.92649913, -0.92879063, -0.93104714, -0.93326861, -0.93545491, -0.93760598, -0.93972176, + -0.9418022, -0.94384718, -0.94585657, -0.94783038, -0.94976848, -0.95167089, -0.9535374, -0.95536804, + -0.95716274, -0.95892137, -0.96064389, -0.96233022, -0.96398038, -0.96559417, -0.96717167, -0.96871275, + -0.97021735, -0.97168541, -0.97311687, -0.97451174, -0.97586989, -0.97719133, -0.97847593, -0.97972375, + -0.98093462, -0.98210859, -0.98324561, -0.98434556, -0.98540848, -0.98643428, -0.987423, -0.98837447, + -0.98928875, -0.99016583, -0.99100554, -0.991808, -0.99257314, -0.99330086, -0.99399126, -0.99464417, + -0.99525958, -0.99583763, -0.99637812, -0.99688113, -0.99734658, -0.99777448, -0.99816483, -0.99851763, + -0.99883282, -0.99911034, -0.99935031, -0.99955267, -0.99971735, -0.99984443, -0.99993384, -0.99998558, +}; + +// seems custom, perhaps based on some common one with some alpha? +static const float OVERLAP_WINDOW[FRAME_SAMPLES] = { + 0.00041374451, 0.00063187029, 0.00083242479, 0.0010303947, 0.0012312527, 0.0014377162, 0.0016513923, 0.001873354, + 0.0021043862, 0.0023451056, 0.0025960256, 0.0028575913, 0.0031302026, 0.0034142293, 0.003710018, 0.0040178993, + 0.0043381932, 0.00467121, 0.0050172545, 0.0053766258, 0.00574962, 0.0061365301, 0.0065376465, 0.0069532581, + 0.007383653, 0.0078291167, 0.008289936, 0.0087663941, 0.0092587769, 0.0097673666, 0.010292448, 0.010834301, + 0.01139321, 0.011969455, 0.012563316, 0.013175075, 0.01380501, 0.0144534, 0.015120523, 0.015806656, + 0.016512074, 0.017237054, 0.017981868, 0.018746791, 0.019532094, 0.020338045, 0.021164915, 0.022012968, + 0.022882473, 0.023773693, 0.02468689, 0.025622323, 0.026580252, 0.027560933, 0.028564619, 0.02959156, + 0.030642008, 0.031716209, 0.032814406, 0.033936843, 0.035083756, 0.036255382, 0.037451953, 0.038673703, + 0.039920855, 0.041193634, 0.042492259, 0.043816946, 0.045167912, 0.046545364, 0.047949508, 0.049380545, + 0.050838675, 0.05232409, 0.053836983, 0.055377539, 0.056945939, 0.058542356, 0.06016697, 0.061819945, + 0.063501447, 0.065211624, 0.066950649, 0.068718657, 0.070515797, 0.07234221, 0.074198022, 0.07608337, + 0.07799837, 0.07994315, 0.081917815, 0.083922468, 0.085957222, 0.088022165, 0.090117387, 0.092242986, + 0.094399013, 0.096585557, 0.098802686, 0.10105046, 0.10332893, 0.10563815, 0.10797815, 0.11034897, + 0.11275065, 0.1151832, 0.11764663, 0.12014097, 0.12266621, 0.12522236, 0.12780938, 0.13042729, + 0.13307604, 0.13575561, 0.13846597, 0.14120705, 0.14397883, 0.14678125, 0.14961423, 0.15247771, + 0.15537159, 0.15829581, 0.16125028, 0.16423489, 0.16724953, 0.17029409, 0.17336844, 0.17647249, + 0.17960605, 0.18276905, 0.18596126, 0.18918259, 0.19243285, 0.19571187, 0.19901948, 0.2023555, + 0.20571974, 0.20911199, 0.21253204, 0.21597971, 0.21945477, 0.22295699, 0.22648615, 0.23004198, + 0.23362428, 0.23723276, 0.2408672, 0.2445273, 0.24821278, 0.25192341, 0.25565886, 0.25941887, + 0.26320317, 0.26701137, 0.27084324, 0.27469841, 0.27857658, 0.28247747, 0.28640065, 0.29034585, + 0.29431269, 0.29830083, 0.30230993, 0.30633962, 0.31038952, 0.31445926, 0.31854844, 0.32265672, + 0.32678369, 0.33092892, 0.33509207, 0.33927271, 0.34347042, 0.3476848, 0.35191545, 0.35616189, + 0.36042371, 0.36470053, 0.36899185, 0.37329727, 0.37761635, 0.38194862, 0.38629359, 0.3906509, + 0.39502001, 0.3994005, 0.4037919, 0.40819371, 0.41260549, 0.41702676, 0.42145702, 0.42589581, + 0.43034267, 0.43479711, 0.43925858, 0.44372663, 0.44820082, 0.45268059, 0.45716542, 0.4616549, + 0.4661485, 0.47064567, 0.47514597, 0.47964889, 0.4841539, 0.48866051, 0.49316826, 0.49767655, + 0.50218499, 0.50669295, 0.51120001, 0.5157057, 0.52020943, 0.52471071, 0.52920908, 0.53370398, + 0.53819495, 0.54268152, 0.54716307, 0.5516392, 0.55610937, 0.56057316, 0.56502998, 0.56947935, + 0.57392085, 0.57835394, 0.5827781, 0.58719289, 0.59159786, 0.59599245, 0.60037625, 0.60474873, + 0.6091094, 0.61345792, 0.61779374, 0.62211639, 0.62642545, 0.63072038, 0.63500077, 0.63926625, + 0.6435163, 0.64775056, 0.65196848, 0.65616965, 0.66035372, 0.66452026, 0.66866881, 0.67279899, + 0.67691034, 0.6810025, 0.6850751, 0.68912768, 0.69315994, 0.69717139, 0.7011618, 0.70513064, + 0.70907766, 0.71300244, 0.7169047, 0.72078407, 0.72464013, 0.72847265, 0.73228133, 0.73606575, + 0.73982555, 0.74356061, 0.74727046, 0.75095487, 0.75461364, 0.7582463, 0.76185274, 0.76543266, + 0.76898569, 0.77251172, 0.77601039, 0.77948159, 0.78292501, 0.78634042, 0.78972763, 0.79308641, + 0.79641658, 0.79971796, 0.80299026, 0.80623347, 0.80944729, 0.81263155, 0.81578618, 0.81891102, + 0.82200587, 0.82507062, 0.82810515, 0.83110934, 0.83408308, 0.83702624, 0.83993882, 0.84282064, + 0.84567159, 0.84849167, 0.85128081, 0.85403895, 0.85676599, 0.85946196, 0.86212677, 0.86476046, + 0.86736292, 0.86993414, 0.87247425, 0.87498307, 0.87746072, 0.87990719, 0.88232255, 0.88470674, + 0.88705987, 0.88938189, 0.89167297, 0.89393318, 0.89616245, 0.89836091, 0.90052873, 0.90266585, + 0.90477246, 0.90684867, 0.90889448, 0.91091013, 0.91289562, 0.91485113, 0.91677684, 0.91867274, + 0.92053914, 0.9223761, 0.92418379, 0.92596233, 0.92771196, 0.92943287, 0.93112504, 0.93278885, + 0.9344244, 0.936032, 0.93761164, 0.93916368, 0.94068825, 0.94218558, 0.94365591, 0.94509935, + 0.94651628, 0.94790679, 0.9492712, 0.95060962, 0.95192248, 0.95320988, 0.95447206, 0.95570934, + 0.95692199, 0.95811015, 0.95927411, 0.96041423, 0.96153063, 0.96262366, 0.96369362, 0.96474063, + 0.96576512, 0.96676731, 0.96774739, 0.96870577, 0.96964264, 0.97055829, 0.97145301, 0.97232717, + 0.97318095, 0.97401464, 0.97482848, 0.97562289, 0.97639805, 0.97715431, 0.97789192, 0.97861117, + 0.97931236, 0.97999579, 0.98066169, 0.98131043, 0.98194218, 0.98255736, 0.98315614, 0.98373884, + 0.9843058, 0.98485726, 0.98539352, 0.98591483, 0.98642153, 0.9869138, 0.98739201, 0.98785633, + 0.98830712, 0.98874468, 0.98916918, 0.98958093, 0.98998028, 0.99036741, 0.99074256, 0.99110597, + 0.991458, 0.99179888, 0.99212885, 0.99244815, 0.99275702, 0.9930557, 0.99334443, 0.9936235, + 0.99389309, 0.99415344, 0.99440479, 0.99464744, 0.99488151, 0.99510723, 0.99532485, 0.9955346, + 0.99573666, 0.99593133, 0.99611866, 0.99629897, 0.99647242, 0.99663919, 0.99679953, 0.99695361, + 0.9971016, 0.99724364, 0.99737996, 0.99751073, 0.99763614, 0.9977563, 0.99787146, 0.99798179, + 0.99808735, 0.99818838, 0.99828494, 0.99837732, 0.99846554, 0.99854976, 0.99863017, 0.99870688, + 0.99878007, 0.99884975, 0.99891615, 0.99897939, 0.99903959, 0.99909681, 0.99915117, 0.99920285, + 0.9992519, 0.99929845, 0.99934256, 0.9993844, 0.99942398, 0.99946147, 0.99949694, 0.99953043, + 0.99956208, 0.99959201, 0.99962014, 0.99964666, 0.9996717, 0.99969524, 0.99971735, 0.99973816, + 0.99975771, 0.99977601, 0.99979317, 0.99980927, 0.99982429, 0.99983829, 0.99985141, 0.99986362, + 0.99987501, 0.99988562, 0.99989551, 0.99990469, 0.99991322, 0.99992108, 0.99992836, 0.99993503, + 0.99994129, 0.99994701, 0.99995226, 0.99995708, 0.99996156, 0.99996561, 0.99996936, 0.9999727, + 0.9999758, 0.9999786, 0.99998116, 0.99998349, 0.99998552, 0.99998742, 0.99998909, 0.99999058, + 0.99999189, 0.99999309, 0.99999416, 0.99999511, 0.99999589, 0.99999666, 0.99999726, 0.99999779, + 0.99999827, 0.99999863, 0.99999899, 0.99999923, 0.99999946, 0.99999964, 0.99999976, 0.99999988, +}; + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c index bf620b4a9..6adc97139 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c @@ -250,39 +250,43 @@ void decode_psx_pivotal(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channels * - 0x7 (0111): End marker and don't decode * - 0x8+(1NNN): Not valid */ -static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * p_loop_start, int32_t * p_loop_end, int config) { +static int ps_find_stream_info_internal(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end, uint32_t* p_stream_size, int config) { int num_samples = 0, loop_start = 0, loop_end = 0; - int loop_start_found = 0, loop_end_found = 0; + bool loop_start_found = false, loop_end_found = false; off_t offset = start_offset; off_t max_offset = start_offset + data_size; size_t interleave_consumed = 0; - int detect_full_loops = config & 1; + bool detect_full_loops = config & 1; + bool stop_on_null = config & 2; + int frames = 0; if (data_size == 0 || channels == 0 || (channels > 1 && interleave == 0)) return 0; while (offset < max_offset) { - uint8_t flag = read_u8(offset+0x01, sf) & 0x0F; /* lower nibble only (for HEVAG) */ + uint16_t header = read_u16be(offset+0x00, sf); + uint8_t flag = header & 0x0F; /* lower nibble only (for HEVAG) */; + frames++; /* theoretically possible and would use last 0x06 */ VGM_ASSERT_ONCE(loop_start_found && flag == 0x06, "PS LOOPS: multiple loop start found at %x\n", (uint32_t)offset); if (flag == 0x06 && !loop_start_found) { loop_start = num_samples; /* loop start before this frame */ - loop_start_found = 1; + loop_start_found = true; } if (flag == 0x03 && !loop_end) { loop_end = num_samples + 28; /* loop end after this frame */ - loop_end_found = 1; + loop_end_found = true; /* ignore strange case in Commandos (PS2), has many loop starts and ends */ if (channels == 1 && offset + 0x10 < max_offset && (read_u8(offset + 0x11, sf) & 0x0F) == 0x06) { loop_end = 0; - loop_end_found = 0; + loop_end_found = false; } if (loop_start_found && loop_end_found) @@ -296,7 +300,7 @@ static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, siz if (flag == 0x01 && detect_full_loops) { static const uint8_t eof[0x10] = {0xFF,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t buf[0x10]; - uint8_t hdr = read_u8(offset + 0x00, sf); + uint8_t hdr = (header >> 8) & 0xFF; int read = read_streamfile(buf, offset+0x10, sizeof(buf), sf); if (read > 0 @@ -310,8 +314,8 @@ static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, siz if (hdr == buf[0] && memcmp(buf+1, eof+1, sizeof(buf) - 1) == 0) { loop_start = 28; /* skip first frame as it's null in PS-ADPCM */ loop_end = num_samples + 28; /* loop end after this frame */ - loop_start_found = 1; - loop_end_found = 1; + loop_start_found = true; + loop_end_found = true; //;VGM_LOG("PS LOOPS: full loop found\n"); break; } @@ -326,8 +330,19 @@ static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, siz interleave_consumed += 0x10; if (interleave_consumed == interleave) { interleave_consumed = 0; - offset += interleave*(channels - 1); + offset += interleave * (channels - 1); } + + // stream done flag + if (stop_on_null && offset > start_offset && (flag & 0x01)) { + frames++; + break; + } + } + + if (p_stream_size) { + // uses frames rather than offsets to take interleave into account + *p_stream_size = frames * 0x10 * channels; } VGM_ASSERT(loop_start_found && !loop_end_found, "PS LOOPS: found loop start but not loop end\n"); @@ -341,15 +356,21 @@ static int ps_find_loop_offsets_internal(STREAMFILE* sf, off_t start_offset, siz return 1; } + return 0; /* no loop */ } -int ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end) { - return ps_find_loop_offsets_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, 0); +//TODO: rename as it returns samples +bool ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end) { + return ps_find_stream_info_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, NULL, 0x00); } -int ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end) { - return ps_find_loop_offsets_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, 1); +bool ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end) { + return ps_find_stream_info_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, NULL, 0x01); +} + +bool ps_find_stream_info(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* p_loop_start, int32_t* p_loop_end, uint32_t* p_stream_size) { + return ps_find_stream_info_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, p_stream_size, 0x02); } size_t ps_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty) { @@ -440,7 +461,7 @@ size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels) { } /* test PS-ADPCM frames for correctness */ -int ps_check_format(STREAMFILE* sf, off_t offset, size_t max) { +bool ps_check_format(STREAMFILE* sf, off_t offset, size_t max) { off_t max_offset = offset + max; if (max_offset > get_streamfile_size(sf)) max_offset = get_streamfile_size(sf); @@ -450,10 +471,10 @@ int ps_check_format(STREAMFILE* sf, off_t offset, size_t max) { uint8_t flags = read_8bit(offset+0x01,sf); if (predictor > 5 || flags > 7) { - return 0; + return false; } offset += 0x10; } - return 1; + return true; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h index b9d8ca8ac..d7a7f9e4c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h @@ -52,13 +52,12 @@ int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL* stream, vorbis_custom_codec int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); int vorbis_custom_parse_packet_awc(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); -#endif/* VGM_USE_VORBIS */ /* other utils to make/parse vorbis stuff */ int build_header_comment(uint8_t* buf, int bufsize); int build_header_identification(uint8_t* buf, int bufsize, vorbis_custom_config* cfg); void load_blocksizes(vorbis_custom_config* cfg, int blocksize_short, int blocksize_long); bool load_header_packet(STREAMFILE* sf, vorbis_custom_codec_data* data, uint32_t packet_size, int packet_skip, uint32_t* p_offset); - +#endif/* VGM_USE_VORBIS */ #endif/*_VORBIS_CUSTOM_DECODER_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index c3dfe85a7..8f6ec98f6 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -25,7 +25,7 @@ static const char* extension_list[] = { "208", "2dx9", "3do", - "3ds", //txth/reserved [F1 2011 (3DS)] + "3ds", "4", //for Game.com audio "8", //txth/reserved [Gungage (PS1)] "800", @@ -221,7 +221,10 @@ static const char* extension_list[] = { "h4m", "hab", + "hbd", "hca", + "hd", + "hd2", "hd3", "hdr", "hdt", @@ -271,6 +274,8 @@ static const char* extension_list[] = { "joe", "jstm", + "k2sb", + "ka1a", "kat", "kces", "kcey", //fake extension/header id for .pcm (renamed, to be removed) @@ -426,6 +431,7 @@ static const char* extension_list[] = { "past", "pcm", "pdt", + "phd", "pk", "pona", "pos", @@ -516,6 +522,7 @@ static const char* extension_list[] = { "sgb", "sgd", "sgt", + "skx", "slb", //txth/reserved [THE Nekomura no Hitobito (PS2)] "sli", "smc", @@ -760,17 +767,17 @@ const char** vgmstream_get_common_formats(size_t* size) { typedef struct { coding_t type; - const char *description; + const char* description; } coding_info; typedef struct { layout_t type; - const char *description; + const char* description; } layout_info; typedef struct { meta_t type; - const char *description; + const char* description; } meta_info; @@ -829,7 +836,7 @@ static const coding_info coding_info_list[] = { {coding_IMA_int, "IMA 4-bit ADPCM (mono/interleave)"}, {coding_DVI_IMA, "Intel DVI 4-bit IMA ADPCM"}, {coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (mono/interleave)"}, - {coding_NW_IMA, "NintendoWare IMA 4-bit ADPCM"}, + {coding_CAMELOT_IMA, "Camelot IMA 4-bit ADPCM"}, {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, {coding_QD_IMA, "Quantic Dream 4-bit IMA ADPCM"}, {coding_WV6_IMA, "Gorilla Systems WV6 4-bit IMA ADPCM"}, @@ -907,6 +914,7 @@ static const coding_info coding_info_list[] = { {coding_TAC, "tri-Ace Codec"}, {coding_ICE_RANGE, "Inti Creates Range Codec"}, {coding_ICE_DCT, "Inti Creates DCT Codec"}, + {coding_KA1A, "Koei Tecmo KA1A Codec"}, #ifdef VGM_USE_VORBIS {coding_OGG_VORBIS, "Ogg Vorbis"}, @@ -1449,11 +1457,14 @@ static const meta_info meta_info_list[] = { {meta_DSP_ASURA, "Rebellion DSP header"}, {meta_ONGAKUKAN_RIFF_ADP, "Ongakukan RIFF WAVE header"}, {meta_SDD, "Doki Denki DSBH header"}, + {meta_KA1A, "Koei Tecmo KA1A header"}, + {meta_HD_BD, "Sony HD+BD header"}, + {meta_PPHD, "Sony PPHD header"}, + {meta_XABP, "cavia XABp header"}, + {meta_I3DS, "Codemasters i3DS header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { - int i, list_length; - const char *description; #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type == coding_FFmpeg) { @@ -1471,7 +1482,7 @@ void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t ou } #endif - description = "CANNOT DECODE"; + const char* description = "CANNOT DECODE"; switch (vgmstream->coding_type) { #ifdef VGM_USE_FFMPEG @@ -1481,23 +1492,22 @@ void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t ou description = "FFmpeg"; break; #endif - default: - list_length = sizeof(coding_info_list) / sizeof(coding_info); - for (i = 0; i < list_length; i++) { + default: { + int list_length = sizeof(coding_info_list) / sizeof(coding_info); + for (int i = 0; i < list_length; i++) { if (coding_info_list[i].type == vgmstream->coding_type) description = coding_info_list[i].description; } break; + } } strncpy(out, description, out_size); } static const char* get_layout_name(layout_t layout_type) { - int i, list_length; - - list_length = sizeof(layout_info_list) / sizeof(layout_info); - for (i = 0; i < list_length; i++) { + int list_length = sizeof(layout_info_list) / sizeof(layout_info); + for (int i = 0; i < list_length; i++) { if (layout_info_list[i].type == layout_type) return layout_info_list[i].description; } @@ -1505,13 +1515,12 @@ static const char* get_layout_name(layout_t layout_type) { return NULL; } -static int has_sublayouts(VGMSTREAM** vgmstreams, int count) { - int i; - for (i = 0; i < count; i++) { +static bool has_sublayouts(VGMSTREAM** vgmstreams, int count) { + for (int i = 0; i < count; i++) { if (vgmstreams[i]->layout_type == layout_segmented || vgmstreams[i]->layout_type == layout_layered) - return 1; + return true; } - return 0; + return false; } /* Makes a mixed description, considering a segments/layers can contain segments/layers infinitely, like: @@ -1529,7 +1538,7 @@ static int has_sublayouts(VGMSTREAM** vgmstreams, int count) { * ("mixed" is added externally) */ static int get_layout_mixed_description(VGMSTREAM* vgmstream, char* dst, int dst_size) { - int i, count, done = 0; + int count, done = 0; VGMSTREAM** vgmstreams = NULL; if (vgmstream->layout_type == layout_layered) { @@ -1555,7 +1564,7 @@ static int get_layout_mixed_description(VGMSTREAM* vgmstream, char* dst, int dst dst[done++] = '['; } - for (i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { done += get_layout_mixed_description(vgmstreams[i], dst + done, dst_size - done); } @@ -1568,7 +1577,7 @@ static int get_layout_mixed_description(VGMSTREAM* vgmstream, char* dst, int dst void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { const char* description; - int mixed = 0; + bool mixed = false; description = get_layout_name(vgmstream->layout_type); if (!description) description = "INCONCEIVABLE"; @@ -1599,13 +1608,10 @@ void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t ou } void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { - int i, list_length; - const char* description; + const char* description = "THEY SHOULD HAVE SENT A POET"; - description = "THEY SHOULD HAVE SENT A POET"; - - list_length = sizeof(meta_info_list) / sizeof(meta_info); - for (i=0; i < list_length; i++) { + int list_length = sizeof(meta_info_list) / sizeof(meta_info); + for (int i = 0; i < list_length; i++) { if (meta_info_list[i].type == vgmstream->meta_type) description = meta_info_list[i].description; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index ad3355aec..acea36ae7 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -8,7 +8,7 @@ /* Decodes samples for blocked streams. * Data is divided into headered blocks with a bunch of data. The layout calls external helper functions * when a block is decoded, and those must parse the new block and move offsets accordingly. */ -void render_vgmstream_blocked(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { +void render_vgmstream_blocked(sbuf_t* sdst, VGMSTREAM* vgmstream) { int frame_size = decode_get_frame_size(vgmstream); int samples_per_frame = decode_get_samples_per_frame(vgmstream); @@ -25,8 +25,7 @@ void render_vgmstream_blocked(sample_t* outbuf, int32_t sample_count, VGMSTREAM* samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; } - int samples_filled = 0; - while (samples_filled < sample_count) { + while (sdst->filled < sdst->samples) { int samples_to_do; if (vgmstream->loop_flag && decode_do_loop(vgmstream)) { @@ -54,15 +53,15 @@ void render_vgmstream_blocked(sample_t* outbuf, int32_t sample_count, VGMSTREAM* } samples_to_do = decode_get_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - if (samples_to_do > sample_count - samples_filled) - samples_to_do = sample_count - samples_filled; + if (samples_to_do > sdst->samples - sdst->filled) + samples_to_do = sdst->samples - sdst->filled; if (samples_to_do > 0) { /* samples_this_block = 0 is allowed (empty block, do nothing then move to next block) */ - decode_vgmstream(vgmstream, samples_filled, samples_to_do, outbuf); + decode_vgmstream(sdst, vgmstream, samples_to_do); } - samples_filled += samples_to_do; + sdst->filled += samples_to_do; vgmstream->current_sample += samples_to_do; vgmstream->samples_into_block += samples_to_do; @@ -92,7 +91,7 @@ void render_vgmstream_blocked(sample_t* outbuf, int32_t sample_count, VGMSTREAM* return; decode_fail: - sbuf_silence_s16(outbuf, sample_count, vgmstream->channels, samples_filled); + sbuf_silence_rest(sdst); } /* helper functions to parse new block */ diff --git a/Frameworks/vgmstream/vgmstream/src/layout/flat.c b/Frameworks/vgmstream/vgmstream/src/layout/flat.c index 5a58605c2..83220af40 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/flat.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/flat.c @@ -6,14 +6,13 @@ /* Decodes samples for flat streams. * Data forms a single stream, and the decoder may internally skip chunks and move offsets as needed. */ -void render_vgmstream_flat(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { +void render_vgmstream_flat(sbuf_t* sdst, VGMSTREAM* vgmstream) { int samples_per_frame = decode_get_samples_per_frame(vgmstream); int samples_this_block = vgmstream->num_samples; /* do all samples if possible */ /* write samples */ - int samples_filled = 0; - while (samples_filled < sample_count) { + while (sdst->filled < sdst->samples) { if (vgmstream->loop_flag && decode_do_loop(vgmstream)) { /* handle looping */ @@ -21,22 +20,22 @@ void render_vgmstream_flat(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vg } int samples_to_do = decode_get_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - if (samples_to_do > sample_count - samples_filled) - samples_to_do = sample_count - samples_filled; + if (samples_to_do > sdst->samples - sdst->filled) + samples_to_do = sdst->samples - sdst->filled; if (samples_to_do <= 0) { /* when decoding more than num_samples */ VGM_LOG_ONCE("FLAT: wrong samples_to_do\n"); goto decode_fail; } - decode_vgmstream(vgmstream, samples_filled, samples_to_do, outbuf); + decode_vgmstream(sdst, vgmstream, samples_to_do); - samples_filled += samples_to_do; + sdst->filled += samples_to_do; vgmstream->current_sample += samples_to_do; vgmstream->samples_into_block += samples_to_do; } return; decode_fail: - sbuf_silence_s16(outbuf, sample_count, vgmstream->channels, samples_filled); + sbuf_silence_rest(sdst); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c index 63bde81ac..54d78aa9c 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c @@ -143,11 +143,11 @@ static void update_offsets(layout_config_t* layout, VGMSTREAM* vgmstream, int* p * Data has interleaved chunks per channel, and once one is decoded the layout moves offsets, * skipping other chunks (essentially a simplified variety of blocked layout). * Incompatible with decoders that move offsets. */ -void render_vgmstream_interleave(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { +void render_vgmstream_interleave(sbuf_t* sdst, VGMSTREAM* vgmstream) { layout_config_t layout = {0}; if (!setup_helper(&layout, vgmstream)) { VGM_LOG_ONCE("INTERLEAVE: wrong config found\n"); - sbuf_silence_s16(outbuf, sample_count, vgmstream->channels, 0); + sbuf_silence_rest(sdst); return; } @@ -160,8 +160,7 @@ void render_vgmstream_interleave(sample_t* outbuf, int32_t sample_count, VGMSTRE if (samples_this_block == 0 && vgmstream->channels == 1) samples_this_block = vgmstream->num_samples; - int samples_filled = 0; - while (samples_filled < sample_count) { + while (sdst->filled < sdst->samples) { if (vgmstream->loop_flag && decode_do_loop(vgmstream)) { /* handle looping, restore standard interleave sizes */ @@ -170,17 +169,17 @@ void render_vgmstream_interleave(sample_t* outbuf, int32_t sample_count, VGMSTRE } int samples_to_do = decode_get_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - if (samples_to_do > sample_count - samples_filled) - samples_to_do = sample_count - samples_filled; + if (samples_to_do > sdst->samples - sdst->filled) + samples_to_do = sdst->samples - sdst->filled; if (samples_to_do <= 0) { /* happens when interleave is not set */ VGM_LOG_ONCE("INTERLEAVE: wrong samples_to_do\n"); goto decode_fail; } - decode_vgmstream(vgmstream, samples_filled, samples_to_do, outbuf); + decode_vgmstream(sdst, vgmstream, samples_to_do); - samples_filled += samples_to_do; + sdst->filled += samples_to_do; vgmstream->current_sample += samples_to_do; vgmstream->samples_into_block += samples_to_do; @@ -193,5 +192,5 @@ void render_vgmstream_interleave(sample_t* outbuf, int32_t sample_count, VGMSTRE return; decode_fail: - sbuf_silence_s16(outbuf, sample_count, vgmstream->channels, samples_filled); + sbuf_silence_rest(sdst); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 1c298c36e..3c35ec6db 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -8,9 +8,9 @@ #include "../base/sbuf.h" /* basic layouts */ -void render_vgmstream_flat(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); +void render_vgmstream_flat(sbuf_t* sbuf, VGMSTREAM* vgmstream); -void render_vgmstream_interleave(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); +void render_vgmstream_interleave(sbuf_t* sbuf, VGMSTREAM* vgmstream); /* segmented layout */ @@ -56,7 +56,7 @@ void loop_layout_layered(VGMSTREAM* vgmstream, int32_t loop_sample); /* blocked layouts */ -void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); +void render_vgmstream_blocked(sbuf_t* sbuf, VGMSTREAM* vgmstream); void block_update(off_t block_offset, VGMSTREAM* vgmstream); void block_update_ast(off_t block_ofset, VGMSTREAM* vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 02b14f6f4..322149279 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -80,16 +80,18 @@ void render_vgmstream_segmented(sbuf_t* sbuf, VGMSTREAM* vgmstream) { ssrc->buf = buf_filled; } - render_main(ssrc, data->segments[data->current_segment]); - + int samples_done = render_main(ssrc, data->segments[data->current_segment]); + samples_done = samples_to_do; // returned buf may have changed if (ssrc->buf != buf_filled) { - sbuf_copy_segments(sbuf, ssrc); + sbuf_copy_segments(sbuf, ssrc, samples_done); + } else { + //TODO ??? + sbuf->filled += samples_done; } - sbuf->filled += samples_to_do; - vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block += samples_to_do; + vgmstream->current_sample += samples_done; + vgmstream->samples_into_block += samples_done; } return; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index de4e992e6..637d1133a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -279,6 +279,9 @@ static const adxkey_info adxkey9_list[] = { /* ARGONAVIS -Kimi ga Mita Stage e- (Android) */ {0x0000,0x0000,0x0000, NULL,301179795002661}, // 000111EBE2B1D525 (+ AWB subkeys) + // Bungo Stray Dogs: Mayoi Inu Kaikitan (iOS/Android) + {0x0000,0x0000,0x0000, NULL,1655728931134731873}, // 16FA54B0C09F7661 + }; static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index bf84bcfd8..af2e9bee8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -205,7 +205,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { } break; -#ifdef VGM_USE_ATRAC9 +#ifdef VGM_USE_FFMPEG case 0x0D: { /* OPUS (PC) [Red Dead Redemption (PC)] */ if (awc.is_streamed) { vgmstream->layout_data = build_layered_awc(sf_body, &awc); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h index eb0f1da26..eda0485c1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h @@ -181,6 +181,7 @@ static uint32_t get_block_repeated_size(STREAMFILE* sf, awc_block_info_t* bi, in /* when data repeats seems to clone the last (super-)frame */ return bi->blk[channel].frame_size; +#ifdef VGM_USE_MPEG case 0x07: { /* MPEG */ /* first super-frame will repeat N VBR old sub-frames, without crossing frame_size. * In GTA5 repeated sub-frames seems to match exactly repeated samples, while RDR seems to match 1 full frame (like RAGE-aud). @@ -218,7 +219,7 @@ static uint32_t get_block_repeated_size(STREAMFILE* sf, awc_block_info_t* bi, in return skip_size; /* skip_size fills frame size */ } - +#endif case 0x0D: /* OPUS */ case 0x0F: /* ATRAC9 */ default: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c b/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c index d6eca09da..4557f6191 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c @@ -96,7 +96,7 @@ VGMSTREAM* init_vgmstream_bcstm(STREAMFILE* sf) { vgmstream->coding_type = coding_NGC_DSP; if (is_camelot_ima) { - vgmstream->coding_type = coding_NW_IMA; + vgmstream->coding_type = coding_CAMELOT_IMA; } else { off_t channel_indexes, channel_info_offset, coefs_offset; @@ -113,7 +113,7 @@ VGMSTREAM* init_vgmstream_bcstm(STREAMFILE* sf) { } break; - default: /* 0x03: IMA? */ + default: /* 0x03: regular IMA? (like .bcwav) */ goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c index c79230383..4bf7f1ba1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c @@ -211,7 +211,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { vgmstream->layout_type = layout_none; - /* only 0x02 is known, others can be made with SDK tools */ + /* only 0x02/03 are known, others can be made with SDK tools */ switch (codec) { case 0x00: vgmstream->coding_type = coding_PCM8; @@ -227,7 +227,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { break; case 0x03: - vgmstream->coding_type = coding_NW_IMA; + vgmstream->coding_type = coding_IMA_int; // 3DS eShop applet (3DS) /* hist is read below */ break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 3174f51ac..0d97d3dd9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -565,6 +565,7 @@ static const hcakey_info hcakey_list[] = { {0x9e3d6943ba67b424}, // music_0310024 {0xb58259c9d1f9ebc1}, // music_0310025 {0xbd9e17f5262e3f09}, // music_0310026 + {0xba8c9e65cf055de}, // music_0310027 {0xb921c3992807dadd}, // music_0320001 {0x38ad99a045dc971f}, // music_0320002 {0xf616642579ba5850}, // music_0320003 @@ -582,6 +583,7 @@ static const hcakey_info hcakey_list[] = { {0xf06a6bfdd00c8286}, // music_0320015 {0x2df608ef06aca41c}, // music_0320016 {0x641af19c287d4a2e}, // music_0320017 + {0xa9e5ea218873f8db}, // music_0320018 {0x82de7b71b30d7bc2}, // music_0320019 {0x100b7ca3075996fe}, // music_0320020 {0x4d1f0819b42520fc}, // music_0320021 @@ -611,6 +613,7 @@ static const hcakey_info hcakey_list[] = { {0xd5dcbaceb12dd205}, // music_0410023 {0x4b71388640b83c6c}, // music_0410024 {0x5b7c2a41095c7b76}, // music_0410025 + {0xea8a072379174ae7}, // music_0410026 {0x5d1f3fdbbb036f8d}, // music_0420001 {0xc04264e8f34ad5c0}, // music_0420002 {0x8f0e96b4f71f724f}, // music_0420003 @@ -676,6 +679,7 @@ static const hcakey_info hcakey_list[] = { {0xd3d24f1db0b74363}, // music_0520019 {0xbc99855ebbfa8e97}, // music_0520020 {0xb2b54877e3fa1bc6}, // music_0520021 + {0xc38d718006196625}, // music_0520022 {0x207ae64e50eeba80}, // music_0540001 {0xd2ce91dbfc209b10}, // music_0610001 {0xa662be1601e49476}, // music_0610002 @@ -736,6 +740,7 @@ static const hcakey_info hcakey_list[] = { {0xef287bc5146b1743}, // music_0810006 {0x1f3c1d0817b3d4be}, // music_0810008 {0x2e5c9e00274e0f2a}, // music_0810009 + {0xfd59b4043bf88390}, // music_0810010 {0x1e99d14d97ab82c5}, // music_0820001 {0x5bf7cefecda8bcb2}, // music_0820002 {0x9cf7ab0ccafa374e}, // music_0820003 @@ -798,6 +803,7 @@ static const hcakey_info hcakey_list[] = { {0x0637e592d471df60}, // music_3010037 {0xa633022c4198673a}, // music_3010038 {0x8d410b922905a207}, // music_3010039 + {0x385562787c40d11c}, // music_3010040 {0xfd3ea450350d666f}, // music_3020001 {0x5e91a3790c32e2b3}, // music_3020002 {0x358adfd1bbd3a95e}, // music_3020003 @@ -1233,6 +1239,9 @@ static const hcakey_info hcakey_list[] = { {0xafc13a64a56884e8}, // music_5050276 {0x76dfb4c3728fe8d9}, // music_5050277 {0x071a776b3ed5ab17}, // music_5050278 + {0x17ae871f1b26d068}, // music_5050279 + {0x5d3eadc8aecfa00c}, // music_5050280 + {0xc3552716a8bde9fe}, // music_5050281 {0x880a35323f69b612}, // music_5050284 {0x5c8623402d1c822d}, // music_5050286 {0xca545af62852a7b7}, // music_5050287 @@ -1249,6 +1258,8 @@ static const hcakey_info hcakey_list[] = { {0xe259362b1d601f93}, // music_5050304 {0x7698628d25ad406b}, // music_5050305 {0x34c0f6db642145a0}, // music_5050307 + {0xb7ecea9165c448da}, // music_5050308 + {0xa5e9bd945c5caf2c}, // music_5050309 {0x52c250eade92393b}, // music_9010001 {0xf66e6bb5b0599b07}, // music_9010002 {0x8582b5a60dbbf948}, // music_9010003 @@ -1504,6 +1515,9 @@ static const hcakey_info hcakey_list[] = { // Muv-Luv Dimensions (Android) {8848}, // 0000000000002290 + + // Tales of Graces f Remastered (PC) + {51485416730473395}, // 00B6E9B6B75533B3 }; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hd_bd.c b/Frameworks/vgmstream/vgmstream/src/meta/hd_bd.c new file mode 100644 index 000000000..259ec47b4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/hd_bd.c @@ -0,0 +1,123 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util/meta_utils.h" + + + /* HD+BD / HBD - Sony PS2 bank format [Parappa the Rapper 2 (PS2), Vib-Ripple (PS2)] */ +VGMSTREAM* init_vgmstream_hd_bd(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + + + /* checks */ + if (!is_id64be(0x00,sf, "IECSsreV")) + return NULL; + // 0x08: full section size + // 0x0c: version? 0x01010000, 0x00020000 LE + + // .hd: standard + // .hdb: found in PrincessSoft's PS2 games (.PAC bigfiles don't seem to have names but exe does refers to HDB) + if (!check_extensions(sf, "hd,hbd")) + return NULL; + + // bank format mainly for sequences, sometimes used for sfx/voices or pseudo-streams with bgm. + // sections: Vers > Head > Vagi (streams) > Smpl (notes?) > Sset > Prog (sequences?) + uint32_t head_offset = 0x10; + if (!is_id64be(head_offset + 0x00,sf, "IECSdaeH")) + return NULL; + uint32_t hd_size = read_u32le(head_offset + 0x0c, sf); + uint32_t bd_size = read_u32le(head_offset + 0x10, sf); + // 0x14: Prog offset + // 0x18: Sset offset + // 0x1c: Smpl offset + uint32_t vagi_offset = read_u32le(head_offset + 0x20, sf); + // 0x24: Setb offset + // rest: reserved (-1, or rarely 0 [Midnight Club 2 (PS2)]) + + meta_header_t h = { + .meta = meta_HD_BD, + }; + + h.target_subsong = sf->stream_index; + if (h.target_subsong == 0) + h.target_subsong = 1; + + if (!is_id64be(vagi_offset + 0x00,sf, "IECSigaV")) + return NULL; + //vagi_size = read_u32le(vagi_offset + 0x08,sf); // including id/size + h.total_subsongs = read_s32le(vagi_offset + 0x0c,sf); + + // mini offset table, though all vagi headers seem pasted together + uint32_t info_offset = read_u32le(vagi_offset + 0x10 + 0x04 * (h.target_subsong - 1), sf) + vagi_offset; + // often there is an extra subsong, a quirk shared with .hb2/phd (except in PrincessSoft's games?) + // (last subsongs doesn't seem to be related to others or anything like that) + // after all table entries there is always 32b padding so it should be fine to check as null + uint32_t null_offset = read_u32le(vagi_offset + 0x10 + 0x04 * h.total_subsongs, sf); + if (null_offset != 0) + h.total_subsongs += 1; + // in PrincessSoft's games last subsongs seems dummy and sets offset to internal .bd end + uint32_t last_offset = read_u32le(vagi_offset + 0x10 + 0x04 * (h.total_subsongs - 1), sf); + if (last_offset && read_u32le(vagi_offset + last_offset + 0x00, sf) == bd_size) + h.total_subsongs -= 1; + + // vagi header + h.stream_offset = read_u32le(info_offset + 0x00, sf); + h.sample_rate = read_u16le(info_offset + 0x04,sf); + uint8_t flags = read_u8 (info_offset + 0x06,sf); + uint8_t unknown = read_u8 (info_offset + 0x07,sf); // 0x00 in v1.1, 0xFF in v2.0 (?) + + if (flags > 0x01 || (unknown != 0x00 && unknown != 0xFF)) { + vgm_logi("HD+BD: unknown header flags (report)\n"); + return NULL; + } + + // calc size via next offset + uint32_t next_offset; + if (h.target_subsong == h.total_subsongs) { + next_offset = bd_size; + } + else { + uint32_t nextinfo_offset = read_u32le(vagi_offset + 0x10 + 0x04 * (h.target_subsong - 1 + 1), sf) + vagi_offset; + next_offset = read_u32le(nextinfo_offset + 0x00, sf); + } + h.stream_size = next_offset - h.stream_offset; + + h.channels = 1; + h.loop_flag = (flags & 1); //TODO test of loops is always full + h.num_samples = ps_bytes_to_samples(h.stream_size, h.channels); + h.loop_start = 0; + h.loop_end = h.num_samples; + + h.coding = coding_PSX; + h.layout = layout_none; + h.open_stream = true; + h.has_subsongs = true; + + // detect hdb pasted together (handle as a separate meta?) + if (get_streamfile_size(sf) == hd_size + bd_size) { + if (!check_extensions(sf, "hbd")) + goto fail; + + h.sf_head = sf; + h.sf_body = sf; + h.stream_offset += hd_size; + } + else { + if (get_streamfile_size(sf) != hd_size) + goto fail; + + h.sf_head = sf; + h.sf_body = open_streamfile_by_ext(sf,"bd"); + if (!h.sf_body) goto fail; + + if (get_streamfile_size(h.sf_body) != bd_size) + goto fail; + } + + vgmstream = alloc_metastream(&h); + if (sf != h.sf_body) close_streamfile(h.sf_body); + return vgmstream; +fail: + if (sf != h.sf_body) close_streamfile(h.sf_body); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/i3ds.c b/Frameworks/vgmstream/vgmstream/src/meta/i3ds.c new file mode 100644 index 000000000..1e74df9fc --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/i3ds.c @@ -0,0 +1,49 @@ +#include "meta.h" +#include "../util/meta_utils.h" +#include "../coding/coding.h" + + +/* i3DS - interleaved dsp [F1 2011 (3DS)] */ +VGMSTREAM* init_vgmstream_i3ds(STREAMFILE* sf) { + + /* checks */ + if (!is_id32be(0x00,sf, "i3DS")) + return NULL; + if (!check_extensions(sf, "3ds")) + return NULL; + + meta_header_t h = { + .meta = meta_I3DS + }; + + // 04: data start? (0x10) + h.data_size = read_u32le(0x08,sf); + h.interleave = read_u32le(0x0c,sf); + + // 2 mono CWAV headers pasted together then "DATA" then stream, no loop info but many tracks repeat + if (!is_id32be(0x10,sf, "CWAV")) + return NULL; + + h.sample_rate = read_s32le(0x10 + 0x4c, sf); + h.num_samples = read_s32le(0x10 + 0x54, sf); + h.coefs_offset = 0x10 + 0x7c; + h.coefs_spacing = 0xC0; + //h.hists_offset = 0x00; //? + //h.hists_spacing = h.coefs_spacing; + + // interleaved data starts from 0x10 after DATA (chunk *2), so unsure if header's offset are actually used + uint32_t chdt_offset = 0x08 + 0x08; //read_u32le(0x10 + 0x6c, sf) + 0x08; + + h.channels = h.interleave ? 2 : 1; + h.stream_offset = 0x10 + 0xc0 * h.channels + chdt_offset; + h.stream_size = h.data_size - h.stream_offset; + if (h.interleave > 0) + h.interleave_last = (h.stream_size % (h.interleave * h.channels)) / h.channels; + + h.coding = coding_NGC_DSP; + h.layout = layout_interleave; + h.open_stream = true; + h.sf = sf; + + return alloc_metastream(&h); +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ka1a.c b/Frameworks/vgmstream/vgmstream/src/meta/ka1a.c new file mode 100644 index 000000000..cb8190bc9 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ka1a.c @@ -0,0 +1,56 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* KA1A - Koei Tecmo's custom codec streams [Dynasty Warriors Origins (PC)] */ +VGMSTREAM* init_vgmstream_ka1a(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset; + + /* checks */ + if (!is_id32be(0x00,sf, "KA1A")) + return NULL; + /* .ka1a: header id */ + if (!check_extensions(sf,"ka1a")) + return NULL; + // KA1A don't seem found outside SRST, but probably will (like KOVS) + + //uint32_t data_size = read_u32le(0x04,sf); + int channels = read_s32le(0x08,sf); + int tracks = read_s32le(0x0c,sf); + int sample_rate = read_s32le(0x10,sf); + int32_t num_samples = read_s32le(0x14,sf); + int32_t loop_start = read_s32le(0x18,sf); + int32_t loop_region = read_s32le(0x1c,sf); + int bitrate_mode = read_s32le(0x20,sf); // signed! (may be negative) + // 0x28: reserved? + + bool loop_flag = (loop_region > 0); + + start_offset = 0x28; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels * tracks, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_KA1A; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_start + loop_region; //typically num_samples + + // KA1A interleaves tracks (ex. 2ch and 2 tracks = 512 stereo samples + 512 stereo samples). + // For vgmstream this is reinterpreted as plain channels like other KT formats do (codec handles + // this fine). Encoder delay is implicit. + vgmstream->codec_data = init_ka1a(bitrate_mode, channels * tracks); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_KA1A; + 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/ktac.c b/Frameworks/vgmstream/vgmstream/src/meta/ktac.c index 568a67a92..c075dd90a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktac.c @@ -20,16 +20,17 @@ VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) { ktac_header_t ktac = {0}; /* checks */ - /* .ktac: header id */ - if (!check_extensions(sf,"ktac")) - goto fail; if (!is_id32be(0x00,sf, "KTAC")) - goto fail; + return NULL; + + /* .ktac: header id (probable extension from debug strings is "kac" */ + if (!check_extensions(sf,"ktac")) + return NULL; /* 0x04: version? (always 1) */ ktac.file_size = read_u32le(0x08,sf); if (ktac.file_size != get_streamfile_size(sf)) - goto fail; + return NULL; ktac.mp4.stream_offset = read_u32le(0x0c,sf); ktac.mp4.stream_size = read_u32le(0x10,sf); ktac.type = read_u32le(0x14,sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c b/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c index eb37ecb47..1517d8b99 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c @@ -4,13 +4,23 @@ #include "../util/companion_files.h" #include "ktsr_streamfile.h" -typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, RIFF_ATRAC9, KOVS, KTSS, KTAC } ktsr_codec; +typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, RIFF_ATRAC9, KMA9, AT9_KM9, KOVS, KTSS, KTAC, KA1A, KA1A_INTERNAL, } ktsr_codec; #define MAX_CHANNELS 8 typedef struct { - uint32_t base_offset; bool is_srsa; + bool is_sdbs; + uint32_t as_offset; + uint32_t as_size; + uint32_t st_offset; + uint32_t st_size; +} ktsr_meta_t; + +typedef struct { + uint32_t as_offset; + uint32_t as_size; + int total_subsongs; int target_subsong; ktsr_codec codec; @@ -18,6 +28,7 @@ typedef struct { uint32_t audio_id; int platform; int format; + uint32_t codec_value; uint32_t sound_id; uint32_t sound_flags; uint32_t config_flags; @@ -37,14 +48,14 @@ typedef struct { uint32_t sound_name_offset; uint32_t config_name_offset; char name[255+1]; -} ktsr_header; +} ktsr_header_t; -static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa); -static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf); -static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE *sf, uint32_t config_data); -static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext); +static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, ktsr_meta_t* info); +static bool parse_ktsr(ktsr_header_t* ktsr, STREAMFILE* sf); +static layered_layout_data* build_layered_atrac9(ktsr_header_t* ktsr, STREAMFILE *sf, uint32_t config_data); +static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, uint32_t st_offset, ktsr_header_t* ktsr, init_vgmstream_t init_vgmstream, const char* ext); -/* KTSR - Koei Tecmo sound resource container */ +/* KTSR - Koei Tecmo sound resource container (KTSL2 sound lib) */ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { /* checks */ @@ -57,7 +68,10 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { if (!check_extensions(sf, "ktsl2asbin,asbin")) return NULL; - return init_vgmstream_ktsr_internal(sf, false) ; + ktsr_meta_t info = { + .as_size = get_streamfile_size(sf), + }; + return init_vgmstream_ktsr_internal(sf, &info); } /* ASRS - container of KTSR found in newer games */ @@ -70,7 +84,7 @@ VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf) { /* 0x08: file size */ /* 0x0c: null */ - /* .srsa: header id (as generated by common tools, probably "(something)asbin") */ + /* .srsa: header id and 'class' in hashed names */ if (!check_extensions(sf, "srsa")) return NULL; @@ -78,29 +92,100 @@ VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf) { * .srsa/srst usually have hashed filenames, so it isn't easy to match them, so this * is mainly useful for .srsa with internal streams. */ - return init_vgmstream_ktsr_internal(sf, true); + ktsr_meta_t info = { + .is_srsa = true, + .as_offset = 0x10, + .as_size = get_streamfile_size(sf) - 0x10, + .st_offset = 0x10, + }; + return init_vgmstream_ktsr_internal(sf, &info); +} + +/* sdbs - container of KTSR found in newer games */ +VGMSTREAM* init_vgmstream_sdbs(STREAMFILE* sf) { + + /* checks */ + if (!is_id32be(0x00, sf, "sdbs")) + return NULL; + + // .srsa: actual extension + if (!check_extensions(sf, "k2sb")) + return NULL; + + // mini-container of memory + stream KTSR + ktsr_meta_t info = { + .is_sdbs = true, + .as_offset = read_u32le(0x04, sf), + .as_size = read_u32le(0x08, sf), + .st_offset = read_u32le(0x0c, sf), + .st_size = read_u32le(0x10, sf), + }; + return init_vgmstream_ktsr_internal(sf, &info); } -static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { - VGMSTREAM* vgmstream = NULL; - STREAMFILE* sf_b = NULL; - ktsr_header ktsr = {0}; - int target_subsong = sf->stream_index; - int separate_offsets = 0; +static STREAMFILE* setup_sf_body(STREAMFILE* sf, ktsr_header_t* ktsr, ktsr_meta_t* info) { - ktsr.is_srsa = is_srsa; - if (ktsr.is_srsa) { - ktsr.base_offset = 0x10; + // use current + if (!ktsr->is_external) + return sf; + + // skip extra header (internals are pre-adjusted) */ + if (ktsr->is_external && info->st_offset) { + for (int i = 0; i < ktsr->channels; i++) { + ktsr->stream_offsets[i] += info->st_offset; + } + + //ktsr->extra_offset += ktsr->st_offset; // ? + } + + if (info->is_sdbs) { + // .k2sb have data pasted together + return sf; } + /* open companion body */ + STREAMFILE* sf_b = NULL; + if (info->is_srsa) { + // try parsing TXTM if present, since .srsa+srst have hashed names and don't match unless renamed + sf_b = read_filemap_file(sf, 0); + } + + if (!sf_b) { + // try (name).(ext), as seen in older games + const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin"; + if (info->is_srsa) + companion_ext = "srst"; + + sf_b = open_streamfile_by_ext(sf, companion_ext); + if (!sf_b) { + vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext); + return NULL; + } + } + + return sf_b; +} + + +static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, ktsr_meta_t* info) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_b = NULL; + ktsr_header_t ktsr = {0}; + + + ktsr.as_offset = info->as_offset; + ktsr.as_size = info->as_size; + /* checks */ - if (!is_id32be(ktsr.base_offset + 0x00, sf, "KTSR")) + if (!is_id32be(ktsr.as_offset + 0x00, sf, "KTSR")) return NULL; - if (read_u32be(ktsr.base_offset + 0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ + if (read_u32be(ktsr.as_offset + 0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ return NULL; + bool separate_offsets = false; + int target_subsong = sf->stream_index; /* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin) * This accepts .ktsl2asbin with internal data or external streams as subsongs. @@ -111,52 +196,41 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { ktsr.target_subsong = target_subsong; if (!parse_ktsr(&ktsr, sf)) - goto fail; + return NULL; if (ktsr.total_subsongs == 0) { vgm_logi("KTSR: file has no subsongs\n"); return NULL; } - /* open companion body */ - if (ktsr.is_external) { - if (ktsr.is_srsa) { - /* try parsing TXTM if present, since .srsa+srst have hashed names and don't match unless renamed */ - sf_b = read_filemap_file(sf, 0); - } - - if (!sf_b) { - /* try (name).(ext), as seen in older games */ - const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin"; - if (ktsr.is_srsa) - companion_ext = "srst"; - - sf_b = open_streamfile_by_ext(sf, companion_ext); - if (!sf_b) { - vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext); - goto fail; - } - } - } - else { - sf_b = sf; - } + sf_b = setup_sf_body(sf, &ktsr, info); + if (!sf_b) goto fail; /* subfiles */ { - VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL; + // autodetect ill-defined streams (assumes file isn't encrypted) + if (ktsr.codec == AT9_KM9) { + if (is_id32be(ktsr.stream_offsets[0], sf_b, "KMA9")) + ktsr.codec = KMA9; + else + ktsr.codec = RIFF_ATRAC9; + } + + init_vgmstream_t init_vgmstream = NULL; const char* ext; switch(ktsr.codec) { - case RIFF_ATRAC9: init_vgmstream = init_vgmstream_riff; ext = "at9"; break; - case KOVS: init_vgmstream = init_vgmstream_ogg_vorbis; ext = "kvs"; break; - case KTSS: init_vgmstream = init_vgmstream_ktss; ext = "ktss"; break; - case KTAC: init_vgmstream = init_vgmstream_ktac; ext = "ktac"; break; + case RIFF_ATRAC9: init_vgmstream = init_vgmstream_riff; ext = "at9"; break; // Nioh (PS4) + case KOVS: init_vgmstream = init_vgmstream_ogg_vorbis; ext = "kvs"; break; // Nioh (PC), Fairy Tail 2 (PC) + case KTSS: init_vgmstream = init_vgmstream_ktss; ext = "ktss"; break; // + case KTAC: init_vgmstream = init_vgmstream_ktac; ext = "ktac"; break; // Blue Reflection Tie (PS4) + case KA1A: init_vgmstream = init_vgmstream_ka1a; ext = "ka1a"; break; // Dynasty Warriors Origins (PC) + case KMA9: init_vgmstream = init_vgmstream_kma9; ext = "km9"; break; // Fairy Tail 2 (PS4) default: break; } if (init_vgmstream) { - vgmstream = init_vgmstream_ktsr_sub(sf_b, &ktsr, init_vgmstream, ext); + vgmstream = init_vgmstream_ktsr_sub(sf_b, info->st_offset, &ktsr, init_vgmstream, ext); if (!vgmstream) goto fail; if (sf_b != sf) close_streamfile(sf_b); @@ -183,16 +257,36 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { case MSADPCM: vgmstream->coding_type = coding_MSADPCM_mono; vgmstream->layout_type = layout_none; - separate_offsets = 1; + separate_offsets = true; /* 0x00: samples per frame */ vgmstream->frame_size = read_u16le(ktsr.extra_offset + 0x02, sf_b); break; + case KA1A_INTERNAL: { + // 00: bitrate mode + // XX: start offsets per channel (from hash-id start aka extra_offset - 0x48) + // XX: size per channel + // XX: padding + + int bitrate_mode = read_s32le(ktsr.extra_offset + 0x00, sf); // signed! (may be negative) + + vgmstream->codec_data = init_ka1a(bitrate_mode, ktsr.channels); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_KA1A; + vgmstream->layout_type = layout_none; + + // mono streams handled in decoder, though needs channel offsets + flag + vgmstream->codec_config = 1; + separate_offsets = true; + + break; + } + case DSP: vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; - separate_offsets = 1; + separate_offsets = true; dsp_read_coefs_le(vgmstream, sf, ktsr.extra_offset + 0x1c, 0x60); dsp_read_hist_le (vgmstream, sf, ktsr.extra_offset + 0x40, 0x60); @@ -223,8 +317,7 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { /* data offset per channel is absolute (not actual interleave since there is padding) in some cases */ if (separate_offsets) { - int i; - for (i = 0; i < ktsr.channels; i++) { + for (int i = 0; i < ktsr.channels; i++) { vgmstream->ch[i].offset = ktsr.stream_offsets[i]; } } @@ -239,17 +332,17 @@ fail: } // TODO improve, unify with other metas that do similar stuff -static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext) { +static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, uint32_t st_offset, ktsr_header_t* ktsr, init_vgmstream_t init_vgmstream, const char* ext) { VGMSTREAM* sub_vgmstream = NULL; STREAMFILE* temp_sf = NULL; - temp_sf = setup_ktsr_streamfile(sf_b, ktsr->is_external, ktsr->stream_offsets[0], ktsr->stream_sizes[0], ext); + temp_sf = setup_ktsr_streamfile(sf_b, st_offset, ktsr->is_external, ktsr->stream_offsets[0], ktsr->stream_sizes[0], ext); if (!temp_sf) return NULL; sub_vgmstream = init_vgmstream(temp_sf); close_streamfile(temp_sf); if (!sub_vgmstream) { - VGM_LOG("ktsr: can't open subfile at %x (size %x)\n", ktsr->stream_offsets[0], ktsr->stream_sizes[0]); + VGM_LOG("ktsr: can't open subfile %s at %x (size %x)\n", ext, ktsr->stream_offsets[0], ktsr->stream_sizes[0]); return NULL; } @@ -263,18 +356,17 @@ static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, V } -static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE* sf, uint32_t config_data) { +static layered_layout_data* build_layered_atrac9(ktsr_header_t* ktsr, STREAMFILE* sf, uint32_t config_data) { STREAMFILE* temp_sf = NULL; layered_layout_data* data = NULL; int layers = ktsr->channels; - int i; /* init layout */ data = init_layout_layered(layers); if (!data) goto fail; - for (i = 0; i < layers; i++) { + for (int i = 0; i < layers; i++) { data->layers[i] = allocate_vgmstream(1, 0); if (!data->layers[i]) goto fail; @@ -319,20 +411,22 @@ fail: } -static int parse_codec(ktsr_header* ktsr) { +static int parse_codec(ktsr_header_t* ktsr) { /* platform + format to codec, simplified until more codec combos are found */ switch(ktsr->platform) { case 0x01: /* PC */ - case 0x05: /* PC/Steam [Fate/Samurai Remnant (PC)] */ + case 0x05: /* PC/Steam, Android [Fate/Samurai Remnant (PC)] */ if (ktsr->format == 0x0000 && !ktsr->is_external) ktsr->codec = MSADPCM; // Warrior Orochi 4 (PC) - //else if (ktsr->format == 0x0001) - // ktsr->codec = KA1A; // Dynasty Warriors Origins (PC) + else if (ktsr->format == 0x0001) + ktsr->codec = KA1A_INTERNAL; // Dynasty Warriors Origins (PC) + else if (ktsr->format == 0x0005 && ktsr->is_external && ktsr->codec_value == 0x0840) + ktsr->codec = KTAC; // Shin Hokuto Musou (Android else if (ktsr->format == 0x0005 && ktsr->is_external) ktsr->codec = KOVS; // Atelier Ryza (PC) - //else if (ktsr->format == 0x1001 && ktsr->is_external) - // ktsr->codec = KA1A; // Dynasty Warriors Origins (PC) + else if (ktsr->format == 0x1001 && ktsr->is_external) + ktsr->codec = KA1A; // Dynasty Warriors Origins (PC) else goto fail; break; @@ -343,7 +437,7 @@ static int parse_codec(ktsr_header* ktsr) { else if (ktsr->format == 0x0005 && ktsr->is_external) ktsr->codec = KTAC; // Blue Reflection Tie (PS4) else if (ktsr->format == 0x1001 && ktsr->is_external) - ktsr->codec = RIFF_ATRAC9; // Nioh (PS4) + ktsr->codec = AT9_KM9; // Nioh (PS4)-at9, Fairy Tail 2 (PS4)-km9 (no apparent differences of flags/channels/etc) else goto fail; break; @@ -369,15 +463,14 @@ fail: return 0; } -static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset) { +static bool parse_ktsr_subfile(ktsr_header_t* ktsr, STREAMFILE* sf, uint32_t offset) { uint32_t suboffset, starts_offset, sizes_offset; - int i; - uint32_t type; - type = read_u32be(offset + 0x00, sf); /* hash-id? */ + uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ //size = read_u32le(offset + 0x04, sf); - /* probably could check the flag in sound header, but the format is kinda messy */ + // probably could check the flags in sound header, but the format is kinda messy + // (all these numbers are surely LE hashes of something) switch(type) { case 0x38D0437D: /* external [Nioh (PC/PS4), Atelier Ryza (PC)] */ @@ -392,7 +485,7 @@ static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offse * 14 external codec * 18 sample rate * 1c num samples - * 20 null? + * 20 null or codec-related value (RIFF_AT9/KM9=0x100, KTAC=0x840) * 24 loop start or -1 (loop end is num samples) * 28 channel layout (or null?) * 2c null @@ -401,9 +494,11 @@ static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offse * 38 data size * 3c always 0x0200 */ + //;VGM_LOG("header %08x at %x\n", type, offset); - ktsr->channels = read_u32le(offset + 0x0c, sf); - ktsr->format = read_u32le(offset + 0x14, sf); + ktsr->channels = read_u32le(offset + 0x0c, sf); + ktsr->format = read_u32le(offset + 0x14, sf); + ktsr->codec_value = read_u32le(offset + 0x20, sf); /* other fields will be read in the external stream */ ktsr->channel_layout = read_u32le(offset + 0x28, sf); @@ -416,8 +511,8 @@ static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offse ktsr->stream_offsets[0] = read_u32le(offset + 0x34, sf); ktsr->stream_sizes[0] = read_u32le(offset + 0x38, sf); } - ktsr->is_external = 1; - + ktsr->is_external = true; +VGM_LOG("k=%x\n", ktsr->codec_value); break; case 0x41FDBD4E: /* internal [Attack on Titan: Wings of Freedom (Vita)] */ @@ -427,14 +522,14 @@ static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offse case 0x10250527: /* internal [Fire Emblem: Three Houses DLC (Switch)] */ /* 08 subtype? (0x6029DBD2, 0xD20A92F90, 0xDC6FF709) * 0c channels - * 10 format? (00=platform's ADPCM? 01=ATRAC9?) - * 11 bps? (always 16) + * 10 format + * 11 null or sometimes 16 * 12 null * 14 sample rate * 18 num samples - * 1c null or 0x100? + * 1c null or codec-related value? * 20 loop start or -1 (loop end is num samples) - * 24 channel layout or null + * 24 null or channel layout (for 1 track in case of multi-track streams) * 28 header offset (within subfile) * 2c header size [B, C] * 30 offset to data start offset [A, C] or to data start+size [B] @@ -464,7 +559,7 @@ static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offse starts_offset = read_u32le(suboffset + 0x00, sf) + offset; sizes_offset = read_u32le(suboffset + 0x04, sf) + offset; - for (i = 0; i < ktsr->channels; i++) { + for (int i = 0; i < ktsr->channels; i++) { ktsr->stream_offsets[i] = read_u32le(starts_offset + 0x04*i, sf) + offset; ktsr->stream_sizes[i] = read_u32le(sizes_offset + 0x04*i, sf); } @@ -481,7 +576,6 @@ static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offse if (!parse_codec(ktsr)) goto fail; - return true; fail: VGM_LOG("ktsr: error parsing subheader\n"); @@ -521,22 +615,23 @@ static size_t read_string_ktsr(char* buf, size_t buf_size, off_t offset, STREAMF return 0; } -static void build_name(ktsr_header* ktsr, STREAMFILE* sf) { +static void build_name(ktsr_header_t* ktsr, STREAMFILE* sf) { char sound_name[255] = {0}; char config_name[255] = {0}; - /* names can be different or same but usually config is better */ if (ktsr->sound_name_offset) { read_string_ktsr(sound_name, sizeof(sound_name), ktsr->sound_name_offset, sf); if (ktsr->sound_flags & 0x0008) decrypt_string_ktsr(sound_name, sizeof(sound_name), ktsr->audio_id); } + if (ktsr->config_name_offset) { read_string_ktsr(config_name, sizeof(config_name), ktsr->config_name_offset, sf); if (ktsr->config_flags & 0x0200) decrypt_string_ktsr(config_name, sizeof(config_name), ktsr->audio_id); } + // names can be different or same but usually config name is better //if (longname[0] && shortname[0]) { // snprintf(ktsr->name, sizeof(ktsr->name), "%s; %s", longname, shortname); //} @@ -550,13 +645,13 @@ static void build_name(ktsr_header* ktsr, STREAMFILE* sf) { } -static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf) { +static void parse_longname(ktsr_header_t* ktsr, STREAMFILE* sf) { /* more configs than sounds is possible so we need target_id first */ uint32_t offset, end, name_offset; uint32_t stream_id; - offset = 0x40 + ktsr->base_offset; - end = get_streamfile_size(sf) - ktsr->base_offset; + offset = 0x40 + ktsr->as_offset; + end = ktsr->as_offset + ktsr->as_size; while (offset < end) { uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ uint32_t size = read_u32le(offset + 0x04, sf); @@ -581,7 +676,7 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf) { } } -static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { +static bool parse_ktsr(ktsr_header_t* ktsr, STREAMFILE* sf) { uint32_t offset, end, header_offset, name_offset; uint32_t stream_count; @@ -589,7 +684,7 @@ static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { * 04: type * 08: version? * 0a: unknown (usually 00, 02/03 seen in Vita) - * 0b: platform (01=PC, 03=Vita, 04=Switch) + * 0b: platform * 0c: audio id? (seen in multiple files/games and used as Ogg stream IDs) * 10: null * 14: null @@ -598,18 +693,18 @@ static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { * up to 40: reserved * until end: entries (totals not defined) */ - ktsr->platform = read_u8(ktsr->base_offset + 0x0b,sf); - ktsr->audio_id = read_u32le(ktsr->base_offset + 0x0c,sf); + ktsr->platform = read_u8 (ktsr->as_offset + 0x0b,sf); + ktsr->audio_id = read_u32le(ktsr->as_offset + 0x0c,sf); - if (read_u32le(ktsr->base_offset + 0x18, sf) != read_u32le(ktsr->base_offset + 0x1c, sf)) + if (read_u32le(ktsr->as_offset + 0x18, sf) != read_u32le(ktsr->as_offset + 0x1c, sf)) goto fail; - if (read_u32le(ktsr->base_offset + 0x1c, sf) != get_streamfile_size(sf) - ktsr->base_offset) { + if (read_u32le(ktsr->as_offset + 0x1c, sf) != ktsr->as_size) { vgm_logi("KTSR: incorrect file size (bad rip?)\n"); goto fail; } - offset = 0x40 + ktsr->base_offset; - end = get_streamfile_size(sf) - ktsr->base_offset; + offset = 0x40 + ktsr->as_offset; + end = ktsr->as_offset + ktsr->as_size; while (offset < end) { uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ uint32_t size = read_u32le(offset + 0x04, sf); @@ -681,15 +776,6 @@ static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { parse_longname(ktsr, sf); build_name(ktsr, sf); - /* skip TSRS header (internals are pre-adjusted) */ - if (ktsr->is_external && ktsr->base_offset) { - for (int i = 0; i < ktsr->channels; i++) { - ktsr->stream_offsets[i] += ktsr->base_offset; - } - - ktsr->extra_offset += ktsr->base_offset; /* ? */ - } - return true; fail: vgm_logi("KTSR: unknown variation (report)\n"); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h index b0cc027dd..a61ae6679 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h @@ -119,18 +119,14 @@ static size_t ktsr_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t l /* Decrypts blowfish KTSR streams */ -static STREAMFILE* setup_ktsr_streamfile(STREAMFILE* sf, bool is_external, uint32_t subfile_offset, uint32_t subfile_size, const char* extension) { +static STREAMFILE* setup_ktsr_streamfile(STREAMFILE* sf, uint32_t st_offset, bool is_external, uint32_t subfile_offset, uint32_t subfile_size, const char* extension) { STREAMFILE* new_sf = NULL; ktsr_io_data io_data = {0}; if (is_external) { - uint32_t offset = 0x00; - if (is_id32be(0x00, sf, "TSRS")) - offset += 0x10; - if (!is_id32be(offset + 0x00, sf, "KTSR")) - goto fail; - - read_streamfile(io_data.key, offset + 0x20, sizeof(io_data.key), sf); + if (!is_id32be(st_offset + 0x00, sf, "KTSR")) + return NULL; + read_streamfile(io_data.key, st_offset + 0x20, sizeof(io_data.key), sf); } /* setup subfile */ @@ -143,10 +139,7 @@ static STREAMFILE* setup_ktsr_streamfile(STREAMFILE* sf, bool is_external, uint3 new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size); if (extension) new_sf = open_fakename_streamfile_f(new_sf, NULL, extension); - return new_sf; -fail: - return NULL; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 71593e204..194a00528 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -898,6 +898,7 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf); VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_sdbs(STREAMFILE* sf); VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf); @@ -1013,4 +1014,16 @@ VGMSTREAM* init_vgmstream_adp_ongakukan(STREAMFILE* sf); VGMSTREAM* init_vgmstream_sdd(STREAMFILE* sf); -#endif /*_META_H*/ +VGMSTREAM* init_vgmstream_ka1a(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_hd_bd(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_pphd(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_xabp(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_i3ds(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_skex(STREAMFILE* sf); + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msf.c b/Frameworks/vgmstream/vgmstream/src/meta/msf.c index 247f56cda..f51ad0e01 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/msf.c @@ -11,6 +11,15 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) { /* checks */ + if ((read_u32be(0x00,sf) & 0xffffff00) != get_id32be("MSF\0")) + return NULL; + // "MSF" + n.n version: + // - 0x01: Megazone 23: Aoi Garland (PS3) + // - 0x02: Switchball (PS3) + // - 0x30 ('0'): ? + // - 0x35 ('5'): SDKs + // - 0x43 ('C'): latest/most common + /* .msf: standard * .msa: Sonic & Sega All-Stars Racing (PS3) * .at3: Silent Hill HD Collection (PS3), Z/X Zekkai no Crusade (PS3) @@ -18,12 +27,7 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) { * .str: Pac-Man and the Ghostly Adventures (PS3) * .snd: HamsterBall (PS3) */ if (!check_extensions(sf,"msf,msa,at3,mp3,str,snd")) - goto fail; - - /* check header "MSF" + version-char, usually: - * 0x01, 0x02, 0x30="0", 0x35="5", 0x43="C" (last/most common version) */ - if ((read_u32be(0x00,sf) & 0xffffff00) != 0x4D534600) /* "MSF\0" */ - goto fail; + return NULL; start_offset = 0x40; @@ -45,13 +49,11 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) { * 0x10 often goes with 0x01 but not always (Castlevania HoD); Malicious PS3 uses flag 0x2 instead */ loop_flag = (flags != 0xffffffff) && ((flags & 0x01) || (flags & 0x02)); - /* loop markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */ + /* loop offset markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */ if (loop_flag) { loop_start = read_u32be(0x18,sf); loop_end = read_u32be(0x1C,sf); /* loop duration */ loop_end = loop_start + loop_end; /* usually equals data_size but not always */ - if (loop_end > data_size) /* not seen */ - loop_end = data_size; } @@ -71,12 +73,11 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 16); + vgmstream->num_samples = pcm16_bytes_to_samples(data_size, channels); if (loop_flag){ - vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channels, 16); - vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channels, 16); + vgmstream->loop_start_sample = pcm16_bytes_to_samples(loop_start, channels); + vgmstream->loop_end_sample = pcm16_bytes_to_samples(loop_end, channels); } - break; } @@ -102,29 +103,35 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) { case 0x04: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) [Silent Hill HD (PS3)] */ case 0x05: /* ATRAC3 mid (105 kbps, frame size 152) [Atelier Rorona (PS3)] */ case 0x06: { /* ATRAC3 high (132 kbps, frame size 192) [Tekken Tag Tournament HD (PS3)] */ - int block_align, encoder_delay; - /* MSF skip samples: from tests with MSEnc and real files (ex. TTT2 eddy.msf v43, v01 demos) seems like 1162 is consistent. - * Atelier Rorona bt_normal01 needs it to properly skip the beginning garbage but usually doesn't matter. - * (note that encoder may add a fade-in with looping/resampling enabled but should be skipped) */ - encoder_delay = 1024 + 69*2; - block_align = (codec==4 ? 0x60 : (codec==5 ? 0x98 : 0xC0)) * vgmstream->channels; - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; - if (vgmstream->sample_rate == -1) /* some MSFv1 (Digi World SP) */ - vgmstream->sample_rate = 44100; /* voice tracks seems to use 44khz, not sure about other tracks */ + /* some MSFv1 voices [Digi World SP (PS3)] */ + if (vgmstream->sample_rate == -1) + vgmstream->sample_rate = 44100; - vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + int block_align = (codec==4 ? 0x60 : (codec==5 ? 0x98 : 0xC0)) * vgmstream->channels; + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align); + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align); + + /* MSF skip samples: from MSEnc tests and real files (ex. TTT2 eddy.msf v43, v01 demos) seems like 1162 is consistent. + * Often doesn't matter but sometimes there is audible garbage [Atelier Rorona (PS3)-bt_normal01] + * However full loops use offset 0 to file end, so maybe decoder doesn't actually skip samples (like in MPEG). + * MSEnc accepts samples and will adjust loops somewhat to closest frame but is not accurate enough. + * Comparing vs other platforms loop start+end need to be in sync [Mamoru-kun wa Norowarette Shimatta! (PS3)] + * For now only remove samples if wouldn't mess up loops. */ + int encoder_delay = 1024 + 69*2; + if (vgmstream->loop_flag && encoder_delay > vgmstream->loop_start_sample) { + encoder_delay = 0; + } + vgmstream->num_samples -= encoder_delay; + vgmstream->loop_start_sample -= encoder_delay; + vgmstream->loop_end_sample -= encoder_delay; + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels, vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - /* MSF loop/sample values are offsets so trickier to adjust but this seems correct */ - if (loop_flag) { - /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ - vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay - vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; - } - break; } #endif @@ -139,7 +146,7 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) { vgmstream->num_samples = mpeg_get_samples_clean(sf, start_offset, data_size, &loop_start, &loop_end, is_vbr); vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; - /* MPEG here seems stripped from ID3/Xing headers, loops are frame offsets */ + /* MSEnc seems to strip ID3/Xing headers, loops are frame offsets */ /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/musx.c b/Frameworks/vgmstream/vgmstream/src/meta/musx.c index 09ccde691..05b62d8c5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/musx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/musx.c @@ -25,6 +25,7 @@ typedef struct { int channels; int sample_rate; int loop_flag; + uint32_t flags; int32_t loop_start; int32_t loop_end; int32_t num_samples; @@ -173,7 +174,6 @@ fail: 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_u32 = read_u32be; @@ -210,7 +210,85 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) { } + /* parse loops and other info */ + if (musx->tables_offset && musx->loops_offset) { + /* cue/stream position table thing */ + /* 0x00: cues1 entries (entry size 0x34 or 0x18) + * 0x04: cues2 entries (entry size 0x20 or 0x14) + * 0x08: header size (always 0x14) + * 0x0c: cues2 start + * 0x10: volume? (usually <= 100) */ + + /* find loops (cues1 also seems to have this info but this looks ok) */ + int cues2_count = read_u32(musx->loops_offset+0x04, sf); + off_t cues2_offset = musx->loops_offset + read_u32(musx->loops_offset+0x0c, sf); + for (int i = 0; i < cues2_count; i++) { + uint32_t type, offset1, offset2; + + if (musx->is_old) { + 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_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 */ + if (type == 0x06 || type == 0x07) { /* loop / goto */ + musx->loop_start = offset2; + musx->loop_end = offset1; + musx->loop_flag = 1; + break; + } + } + } + else if (musx->loops_offset && read_u32be(musx->loops_offset, sf) != 0xABABABAB) { + /* parse loop table (loop starts are -1 if non-looping) + * 0x00: version? (always 1) + * 0x04: flags (&1=loops, &2=alt?) + * 0x08: loop start offset? + * 0x0c: loop end offset? + * 0x10: loop end sample + * 0x14: loop start sample + * 0x18: loop end offset + * 0x1c: loop start offset */ + musx->flags = read_u32le(musx->loops_offset+0x04, sf); + 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); + } + + /* fix some v10 platform (like PSP) sizes */ + if (musx->stream_size == 0) { + musx->stream_size = musx->file_size - musx->stream_offset; + + /* always padded to nearest 0x800 sector */ + if (musx->stream_size > 0x800) { + uint8_t buf[0x800]; + int pos; + off_t offset = musx->stream_offset + musx->stream_size - 0x800; + + if (read_streamfile(buf, offset, sizeof(buf), sf) != 0x800) + goto fail; + + pos = 0x800 - 0x04; + while (pos > 0) { + if (get_u32be(buf + pos) != 0xABABABAB) + break; + musx->stream_size -= 0x04; + pos -= 0x04; + } + } + } + + /* defaults */ + int default_channels, default_sample_rate; switch(musx->platform) { case 0x5053325F: /* "PS2_" */ @@ -254,27 +332,27 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) { break; case 0x5749495F: /* "WII_" */ - default_channels = 2; - default_sample_rate = 32000; - musx->codec = DAT; - break; - - case 0x5053335F: /* "PS3_" */ - default_channels = 2; - default_sample_rate = 44100; - musx->codec = DAT; - break; - case 0x58455F5F: /* "XE__" */ default_channels = 2; default_sample_rate = 32000; musx->codec = DAT; break; + case 0x5053335F: /* "PS3_" */ case 0x50435F5F: /* "PC__" */ default_channels = 2; default_sample_rate = 44100; musx->codec = DAT; + + // some v10 versions use 44100 and others 32000, the latter seem to have loop info table (even without loops) and a flag + // - 44100: Robots (PC)-v10 (no loop table), Pirates of the Caribbean: At World's End (PC)-v10 (no loop table), Beijing 2008 (loop table) + // - 32000: G-Force (PS3)-v10, Ice Age 3 (PC)-v10 (loop table with flag 2) + // The flag also exists in files with similar loop tables in the DAT* chunk + if (musx->version == 10 && musx->flags && musx->flags & 0x02) { + default_sample_rate = 32000; + } + //TO-DO: some files use 22050 but don't seem to set any flag [Beijing 2008 (PS3)] + break; case 0x50433032: /* "PC02" */ @@ -293,85 +371,6 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) { if (musx->sample_rate == 0) musx->sample_rate = default_sample_rate; - - /* parse loops and other info */ - if (musx->tables_offset && musx->loops_offset) { - int i, cues2_count; - off_t cues2_offset; - - /* cue/stream position table thing */ - /* 0x00: cues1 entries (entry size 0x34 or 0x18) - * 0x04: cues2 entries (entry size 0x20 or 0x14) - * 0x08: header size (always 0x14) - * 0x0c: cues2 start - * 0x10: volume? (usually <= 100) */ - - /* find loops (cues1 also seems to have this info but this looks ok) */ - 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_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_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 */ - if (type == 0x06 || type == 0x07) { /* loop / goto */ - musx->loop_start = offset2; - musx->loop_end = offset1; - musx->loop_flag = 1; - break; - } - } - } - 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) - * 0x08: loop start offset? - * 0x0c: loop end offset? - * 0x10: loop end sample - * 0x14: loop start sample - * 0x18: loop end offset - * 0x1c: loop start offset */ - 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); - } - - /* fix some v10 platform (like PSP) sizes */ - if (musx->stream_size == 0) { - musx->stream_size = musx->file_size - musx->stream_offset; - - /* always padded to nearest 0x800 sector */ - if (musx->stream_size > 0x800) { - uint8_t buf[0x800]; - int pos; - off_t offset = musx->stream_offset + musx->stream_size - 0x800; - - if (read_streamfile(buf, offset, sizeof(buf), sf) != 0x800) - goto fail; - - pos = 0x800 - 0x04; - while (pos > 0) { - if (get_u32be(buf + pos) != 0xABABABAB) - break; - musx->stream_size -= 0x04; - pos -= 0x04; - } - } - } - return 1; fail: return 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index 28461e49e..c48e710c1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -29,8 +29,12 @@ typedef struct { * [ex. Batallion Wars (GC), Timesplitters 2 (GC)], 0xcccc...cccc with DSPADPCMD */ } dsp_header_t; +typedef struct { + bool ignore_null_coefs; /* silent files in rare cases */ +} dsp_header_config_t; + /* read and do basic validations to the above struct */ -static bool read_dsp_header_endian(dsp_header_t* header, off_t offset, STREAMFILE* sf, bool big_endian) { +static bool read_dsp_header_endian(dsp_header_t* header, off_t offset, STREAMFILE* sf, bool big_endian, dsp_header_config_t* cfg) { get_u32_t get_u32 = big_endian ? get_u32be : get_u32le; get_u16_t get_u16 = big_endian ? get_u16be : get_u16le; get_s16_t get_s16 = big_endian ? get_s16be : get_s16le; @@ -84,9 +88,11 @@ static bool read_dsp_header_endian(dsp_header_t* header, off_t offset, STREAMFIL if (header->coef[i] == 0) zero_coefs++; } - /* some 0s are ok, more than 8 is probably wrong */ - if (zero_coefs == 16) - goto fail; + /* some 0s are ok, more than 8 is probably wrong, but rarely ok */ + if (cfg == NULL || !cfg->ignore_null_coefs) { + if (zero_coefs == 16) + goto fail; + } header->gain = get_u16(buf+0x3c); if (header->gain != 0) @@ -113,11 +119,13 @@ static bool read_dsp_header_endian(dsp_header_t* header, off_t offset, STREAMFIL fail: return false; } + static int read_dsp_header_be(dsp_header_t *header, off_t offset, STREAMFILE* file) { - return read_dsp_header_endian(header, offset, file, 1); + return read_dsp_header_endian(header, offset, file, true, NULL); } + static int read_dsp_header_le(dsp_header_t *header, off_t offset, STREAMFILE* file) { - return read_dsp_header_endian(header, offset, file, 0); + return read_dsp_header_endian(header, offset, file, false, NULL); } /* ********************************* */ @@ -139,13 +147,16 @@ typedef struct { meta_t meta_type; /* hacks */ - int force_loop; /* force full loop */ - int force_loop_seconds; /* force loop, but must be longer than this (to catch jingles) */ - int fix_looping; /* fix loop end going past num_samples */ - int fix_loop_start; /* weird files with bad loop start */ - int single_header; /* all channels share header, thus totals are off */ - int ignore_header_agreement; /* sometimes there are minor differences between headers */ - int ignore_loop_ps; /* sometimes has bad loop start ps */ + bool force_loop; /* force full loop */ + bool force_loop_seconds; /* force loop, but must be longer than this (to catch jingles) */ + bool fix_looping; /* fix loop end going past num_samples */ + bool fix_loop_start; /* weird files with bad loop start */ + bool single_header; /* all channels share header, thus totals are off (2=double) */ + bool double_header; /* all channels share header, thus totals are off (2=double) */ + bool ignore_header_agreement; /* sometimes there are minor differences between headers */ + bool ignore_initial_ps; /* rarely has bad start ps */ + bool ignore_loop_ps; /* sometimes has bad loop start ps */ + dsp_header_config_t cfg; } dsp_meta; #define COMMON_DSP_MAX_CHANNELS 6 @@ -168,7 +179,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta* dspm) { /* load standard DSP header per channel */ { for (i = 0; i < dspm->channels; i++) { - if (!read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) { + if (!read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian, &dspm->cfg)) { //;VGM_LOG("DSP: bad header\n"); return NULL; } @@ -199,7 +210,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta* dspm) { } /* check expected initial predictor/scale */ - { + if (!dspm->ignore_initial_ps) { int channels = dspm->channels; if (dspm->single_header) channels = 1; @@ -288,7 +299,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta* dspm) { if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples) vgmstream->loop_end_sample = vgmstream->num_samples; - if (dspm->single_header == 2) { /* double the samples */ + if (dspm->double_header) { /* double the samples */ vgmstream->num_samples /= dspm->channels; vgmstream->loop_start_sample /= dspm->channels; vgmstream->loop_end_sample /= dspm->channels; @@ -312,7 +323,7 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { dsp_header_t header; const size_t header_size = 0x60; off_t start_offset; - int i, channels; + int channels; /* checks */ if (!read_dsp_header_be(&header, 0x00, sf)) @@ -406,14 +417,15 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_DSP_STD; - vgmstream->allow_dual_stereo = 1; /* very common in .dsp */ + vgmstream->allow_dual_stereo = true; /* very common in .dsp */ vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; { /* adpcm coeffs/history */ - for (i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; + } vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; } @@ -433,7 +445,7 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std_le(STREAMFILE* sf) { dsp_header_t header; const size_t header_size = 0x60; off_t start_offset; - int i, channels; + int channels; /* checks */ if (!read_dsp_header_le(&header, 0x00, sf)) @@ -491,12 +503,13 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std_le(STREAMFILE* sf) { vgmstream->meta_type = meta_DSP_STD; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; - vgmstream->allow_dual_stereo = 1; + vgmstream->allow_dual_stereo = true; { /* adpcm coeffs/history */ - for (i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; + } vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; } @@ -516,7 +529,8 @@ VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf) { dsp_header_t header; const size_t header_size = 0x60; off_t start_offset; - int i, c, channels; + int channels; + /* checks */ if (!read_dsp_header_be(&header, 0x00, sf)) @@ -553,13 +567,14 @@ VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf) { if (vgmstream->interleave_block_size) vgmstream->interleave_last_block_size = (header.nibble_count / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; - for (i = 0; i < channels; i++) { + for (int i = 0; i < channels; i++) { if (!read_dsp_header_be(&header, header_size * i, sf)) goto fail; /* adpcm coeffs/history */ - for (c = 0; c < 16; c++) + for (int c = 0; c < 16; c++) { vgmstream->ch[i].adpcm_coef[c] = header.coef[c]; + } vgmstream->ch[i].adpcm_history1_16 = header.initial_hist1; vgmstream->ch[i].adpcm_history2_16 = header.initial_hist2; } @@ -621,7 +636,8 @@ VGMSTREAM* init_vgmstream_ngc_mpdsp(STREAMFILE* sf) { dspm.channels = 2; dspm.max_channels = 2; - dspm.single_header = 2; + dspm.single_header = true; + dspm.double_header = true; dspm.header_offset = 0x00; dspm.header_spacing = 0x00; /* same header for both channels */ @@ -681,33 +697,41 @@ VGMSTREAM* init_vgmstream_idsp_namco(STREAMFILE* sf) { /* checks */ if (!is_id32be(0x00,sf, "IDSP")) - goto fail; + return NULL; if (!check_extensions(sf, "idsp")) - goto fail; + return NULL; dspm.max_channels = 8; /* games do adjust loop_end if bigger than num_samples (only happens in user-created IDSPs) */ dspm.fix_looping = 1; /* 0x04: null */ - dspm.channels = read_32bitBE(0x08, sf); + dspm.channels = read_s32be(0x08, sf); /* 0x0c: sample rate */ /* 0x10: num_samples */ /* 0x14: loop start */ /* 0x18: loop end */ - dspm.interleave = read_32bitBE(0x1c,sf); /* usually 0x10 */ - dspm.header_offset = read_32bitBE(0x20,sf); - dspm.header_spacing = read_32bitBE(0x24,sf); - dspm.start_offset = read_32bitBE(0x28,sf); - /* Soul Calibur: Broken destiny (PSP), Taiko no Tatsujin: Atsumete Tomodachi Daisakusen (WiiU) */ - if (dspm.interleave == 0) /* half interleave (happens sometimes), use channel size */ - dspm.interleave = read_32bitBE(0x2c,sf); + dspm.interleave = read_u32be(0x1c,sf); /* usually 0x10 */ + dspm.header_offset = read_u32be(0x20,sf); + dspm.header_spacing = read_u32be(0x24,sf); + dspm.start_offset = read_u32be(0x28,sf); + + /* SoulCalibur Legends (Wii), Taiko no Tatsujin: Atsumete Tomodachi Daisakusen (WiiU) */ + if (dspm.interleave == 0) { + /* half interleave (uncommon), use channel size */ + dspm.interleave = read_u32be(0x2c,sf); + /* Rarely 2nd channel stars with a padding frame then real 2nd channel with initial_ps. Must be some NUS2 bug + * when importing DSP data as only happens for some subsongs and offsets/sizes are fine [We Ski (Wii), Go Vacation (Wii)] */ + dspm.ignore_initial_ps = true; + dspm.ignore_loop_ps = true; + } + + // rare but valid IDSP [Super Smash Bros. Ultimate (Switch)-vc_kirby.nus3audio] + dspm.cfg.ignore_null_coefs = true; dspm.meta_type = meta_IDSP_NAMCO; return init_vgmstream_dsp_common(sf, &dspm); -fail: - return NULL; } @@ -1323,7 +1347,7 @@ VGMSTREAM* init_vgmstream_dsp_lucasarts_ds2(STREAMFILE* sf) { dspm.channels = 2; dspm.max_channels = 2; - dspm.single_header = 1; + dspm.single_header = true; dspm.header_offset = 0x00; dspm.header_spacing = 0x00; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nus3audio.c b/Frameworks/vgmstream/vgmstream/src/meta/nus3audio.c index 10e11f8ad..3b7c9fdff 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nus3audio.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nus3audio.c @@ -15,14 +15,14 @@ VGMSTREAM* init_vgmstream_nus3audio(STREAMFILE* sf) { /* checks */ if (!is_id32be(0x00,sf, "NUS3")) - goto fail; + return NULL; if (read_u32le(0x04,sf) + 0x08 != get_streamfile_size(sf)) - goto fail; + return NULL; if (!is_id32be(0x08,sf, "AUDI")) - goto fail; + return NULL; if (!check_extensions(sf, "nus3audio")) - goto fail; + return NULL; /* parse existing chunks */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index 8f3188d6f..eed5379c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -186,7 +186,7 @@ static int _init_vgmstream_ogg_vorbis_tests(STREAMFILE* sf, ogg_vorbis_io_config cfg->start = 0x20; - /* .kvs: Atelier Sophie (PC) + /* .kvs: Atelier Sophie (PC), debug strings * .kovs: header id only? */ if (!check_extensions(sf,"kvs,kovs")) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pphd.c b/Frameworks/vgmstream/vgmstream/src/meta/pphd.c new file mode 100644 index 000000000..517b8a3d1 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/pphd.c @@ -0,0 +1,86 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util/meta_utils.h" + + + /* PPHD - Sony PSP bank format [Parappa the Rapper (PSP), Tales of Phantasia (PSP)] */ +VGMSTREAM* init_vgmstream_pphd(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + + + /* checks */ + if (!is_id32be(0x00,sf, "PPHD")) + return NULL; + // 0x04: chunk size + // 0x08: version? 0x00010000 LE + // 0x0c: -1 + + if (!check_extensions(sf, "phd")) + return NULL; + + // bank format mainly for sequences, similar to .HD/HD3 + // sections: PPPG (sequences?) > PPTN (notes?) > PPVA (streams) + // 0x10: PPPG offset + // 0x14: PPTN offset + uint32_t ppva_offset = read_u32le(0x18, sf); + // rest: reserved (-1 xN) + + meta_header_t h = { + .meta = meta_PPHD, + }; + + h.target_subsong = sf->stream_index; + if (h.target_subsong == 0) + h.target_subsong = 1; + + if (!is_id32be(ppva_offset + 0x00,sf, "PPVA")) + return NULL; + // 04: ppva size + // 08: info start? + // 0c: -1 + // 10: null + h.total_subsongs = read_s32le(ppva_offset + 0x14,sf); + // 18: -1 + // 1c: -1 + + uint32_t info_offset = ppva_offset + 0x20 + 0x10 * (h.target_subsong - 1); + // often there is an extra subsong, a quirk shared with .hb/hd3 (may be 0-padding instead) + uint32_t max_offset = ppva_offset + 0x20 + 0x10 * h.total_subsongs; + if (max_offset < get_streamfile_size(sf) && read_s32le(max_offset, sf) > 0) + h.total_subsongs += 1; + + + // header + h.stream_offset = read_u32le(info_offset + 0x00, sf); + h.sample_rate = read_s32le(info_offset + 0x04,sf); + h.stream_size = read_u32le(info_offset + 0x08,sf); + uint32_t flags = read_u32le(info_offset + 0x0c,sf); + + if (flags != 0xFFFFFFFF) { + vgm_logi("PPHD: unknown header flags (report)\n"); + return NULL; + } + + h.channels = 1; + //h.loop_flag = (flags & 1); //TODO test of loops is always full + h.num_samples = ps_bytes_to_samples(h.stream_size, h.channels); + h.loop_start = 0; + h.loop_end = h.num_samples; + + h.coding = coding_PSX; + h.layout = layout_none; + h.open_stream = true; + h.has_subsongs = true; + + h.sf_head = sf; + h.sf_body = open_streamfile_by_ext(sf,"pbd"); + if (!h.sf_body) goto fail; + + vgmstream = alloc_metastream(&h); + close_streamfile(h.sf_body); + return vgmstream; +fail: + close_streamfile(h.sf_body); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psf.c b/Frameworks/vgmstream/vgmstream/src/meta/psf.c index 9333ee748..6885445bf 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psf.c @@ -10,20 +10,19 @@ VGMSTREAM* init_vgmstream_psf_single(STREAMFILE* sf) { off_t start_offset; int loop_flag, channel_count, sample_rate, rate_value, interleave; uint32_t psf_config; - uint8_t flags; size_t data_size; coding_t codec; /* checks */ if ((read_u32be(0x00,sf) & 0xFFFFFF00) != get_id32be("PSF\0")) - goto fail; + return NULL; /* .psf: actual extension * .swd: bigfile extension */ if (!check_extensions(sf, "psf,swd")) - goto fail; + return NULL; - flags = read_8bit(0x03,sf); + uint8_t flags = read_8bit(0x03,sf); switch(flags) { case 0xC0: /* [The Great Escape (PS2), Conflict: Desert Storm (PS2)] */ case 0x40: /* [The Great Escape (PS2)] */ @@ -495,6 +494,8 @@ fail: typedef enum { UNKNOWN, IMUS, PFST, PFSM } sch_type; #define SCH_STREAM "Stream.swd" +#define SCH_STREAM_PS2 "STREAM.SWD" + /* SCH - Pivotal games multi-audio container [The Great Escape, Conflict series] */ @@ -504,7 +505,6 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) { STREAMFILE* temp_sf = NULL; off_t skip = 0, chunk_offset, target_offset = 0, header_offset, subfile_offset = 0; size_t file_size, chunk_padding, target_size = 0, subfile_size = 0; - int big_endian; int total_subsongs = 0, target_subsong = sf->stream_index; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; sch_type target_type = UNKNOWN; @@ -516,23 +516,23 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) { skip = 0x0E; if (!is_id32be(skip + 0x00,sf, "SCH\0") && !is_id32le(skip + 0x00,sf, "SCH\0")) /* (BE consoles) */ - goto fail; + return NULL; if (!check_extensions(sf, "sch")) - goto fail; + return NULL; /* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids): * no other info so total subsongs would be count of usable chunks * (offsets are probably in level .dat files) */ - big_endian = (is_id32le(skip + 0x00,sf, "SCH\0")); + int big_endian = (is_id32le(skip + 0x00,sf, "SCH\0")); if (big_endian) { read_32bit = read_32bitBE; chunk_padding = 0x18; } else { read_32bit = read_32bitLE; - chunk_padding = 0; + chunk_padding = 0x00; } file_size = get_streamfile_size(sf); @@ -598,9 +598,9 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) { switch(target_type) { case IMUS: { /* external segmented track */ - STREAMFILE *psf_sf; + STREAMFILE* psf_sf = NULL; uint8_t name_size; - char name[255]; + char name[256]; /* 0x00: config/size? * 0x04: name size @@ -611,32 +611,33 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) { */ name_size = read_u8(header_offset + 0x04, sf); - read_string(name,name_size, header_offset + 0x08, sf); + read_string(name, name_size, header_offset + 0x08, sf); - /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ + /* later Xbox games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ if (read_u8(header_offset + 0x07, sf) == 0xCC) { external_sf = open_streamfile_by_filename(sf, SCH_STREAM); - if (!external_sf) { - vgm_logi("SCH: external file '%s' not found (put together)\n", SCH_STREAM); - goto fail; + if (external_sf) { + subfile_offset = read_32bit(header_offset + 0x08 + name_size, sf); + subfile_size = get_streamfile_size(external_sf) - subfile_offset; /* not ok but meh */ + + temp_sf = setup_subfile_streamfile(external_sf, subfile_offset,subfile_size, "psf"); + if (!temp_sf) goto fail; + + psf_sf = temp_sf; } - - subfile_offset = read_32bit(header_offset + 0x08 + name_size, sf); - subfile_size = get_streamfile_size(external_sf) - subfile_offset; /* not ok but meh */ - - temp_sf = setup_subfile_streamfile(external_sf, subfile_offset,subfile_size, "psf"); - if (!temp_sf) goto fail; - - psf_sf = temp_sf; } - else { - external_sf = open_streamfile_by_filename(sf, name); - if (!external_sf) { - vgm_logi("SCH: external file '%s' not found (put together)\n", name); - goto fail; - } - psf_sf = external_sf; + /* PC games still use name + 0xCC at header, no diffs vs Xbox? [Conflict: Global Storm (PC)] */ + if (!psf_sf) { + external_sf = open_streamfile_by_filename(sf, name); + if (external_sf) { + psf_sf = external_sf; + } + } + + if (!psf_sf) { + vgm_logi("SCH: external file '%s' or '%s' not found (put together)\n", SCH_STREAM, name); + goto fail; } vgmstream = init_vgmstream_psf_segmented(psf_sf); @@ -652,7 +653,7 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) { case PFST: { /* external track */ STREAMFILE *psf_sf; uint8_t name_size; - char name[255]; + char name[256]; if (chunk_padding == 0 && target_size > 0x08 + 0x0c) { /* TGE PC/Xbox version */ /* 0x00: -1/0 @@ -697,7 +698,7 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) { } } else if (chunk_padding) { - strcpy(name, "STREAM.SWD"); /* fixed */ + strcpy(name, SCH_STREAM_PS2); /* fixed */ /* 0x00: -1 * 0x04: config/size? @@ -715,7 +716,7 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) { psf_sf = temp_sf; } else { /* others */ - strcpy(name, "STREAM.SWD"); /* fixed */ + strcpy(name, SCH_STREAM_PS2); /* fixed */ /* 0x00: -1 * 0x04: config/size? diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rage_aud_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/rage_aud_streamfile.h index e1b915d85..61922c869 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rage_aud_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/rage_aud_streamfile.h @@ -162,6 +162,7 @@ static uint32_t get_block_repeated_size(STREAMFILE* sf, rage_aud_block_info_t* b return bi->blk[channel].frame_size; } +#ifdef VGM_USE_MPEG case 0x0100: { /* MPEG */ /* first super-frame will repeat N VBR old sub-frames, without crossing frame_size. * ex. repeated frames' size could be set to 0x774 (7 sub-frames) if adding 1 more would take >0x800. @@ -189,7 +190,7 @@ static uint32_t get_block_repeated_size(STREAMFILE* sf, rage_aud_block_info_t* b return skip_size; /* skip_size fills frame size */ } - +#endif default: ;VGM_LOG("RAGE_AUD: found channel skip in codec %x\n", bi->codec); /* not seen */ return 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/skex.c b/Frameworks/vgmstream/vgmstream/src/meta/skex.c new file mode 100644 index 000000000..29d7069b4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/skex.c @@ -0,0 +1,272 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util.h" + + +/* SKEX - from SCE America second party devs [Syphon Filter: Dark Mirror (PS2/PSP), MLB 2004 (PS2), MLB 15 (Vita)] */ +VGMSTREAM* init_vgmstream_skex(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + STREAMFILE* sf_h = NULL; + + + /* checks */ + if (!is_id32be(0x00,sf, "SKEX")) + return NULL; + if (!check_extensions(sf,"skx")) + return NULL; + + // bank-like format with helper files typically found inside memory/bigfiles (.SWD, .DAT, etc) + // - .skx: external streams (pack of full formats) + // - .tbl: main stream info + // - .ctl: cues? + // - .mrk: text script related to .tbl + // usually .tbl is the header and .skx its body, but rarely may be combined so use .skx as a base + + uint16_t version = read_u16le(0x04, sf); // in hex NN.NN form + // 06: low number, seems related to file (id?) + // 08: null + // 0c: null + uint32_t head_offset = read_u32le(0x10, sf); + uint32_t head_size = read_u32le(0x14, sf); + int entries = read_u16le(0x18, sf); // even with no head_offset/size + + // micro optimization (empty banks do exist) + if (get_streamfile_size(sf) <= 0x100) { + vgm_logi("SKEX: bank has no subsongs\n"); + return NULL; + } + + // setup header + if (head_offset && head_size) { + // rare [MLB 2004 (PS2), NBA 06 (PS2)] + sf_h = sf; + } + else { + // note that may .skx may be uppercase and companion file lowercase (meaning Linux won't open this) + sf_h = open_streamfile_by_ext(sf, "tbl"); + if (!sf_h) { + vgm_logi("SKEX: companion file .tbl not found (put together)\n"); + return NULL; + } + } + + + uint32_t subfile_offset = 0, subfile_size = 0, prev_offset = 0, subfile_type = 0; + + int total_subsongs = 0; + int target_subsong = sf->stream_index; + if (target_subsong == 0) target_subsong = 1; + + // Entries have many repeats so calculate totals. + // After last entry there is a fake entry with .skx size (meaning next_offset is always valid). + // With flags = 0x1000, after all is another table with increasing low number per entry + switch(version) { + case 0x1070: { // MLB 2003 (PS2), MLB 2004 (PS2) + uint32_t offset = head_offset; + + // entries go after files + for (int i = 0; i < entries; i++) { + uint32_t curr_offset = read_u32le(offset + 0x00, sf_h); + uint32_t curr_type = read_u32le(offset + 0x04, sf_h); + // 08: null? + + offset += 0x0c; + + switch(curr_type) { + case 0x05: // .vag (mono) + case 0x0c: // .vag (stereo) + break; + default: + vgm_logi("SKEX: unknown format %x (report)\n", curr_type); + goto fail; + } + + if (prev_offset == curr_offset) + continue; + prev_offset = curr_offset; + + total_subsongs++; + + if (target_subsong == total_subsongs && !subfile_offset) { + uint32_t next_offset = read_u32le(offset, sf_h); + subfile_offset = curr_offset; + subfile_size = next_offset - curr_offset; + subfile_type = curr_type; + } + } + break; + } + + case 0x2040: // MLB 2005 (PS2) + case 0x2070: // MLB 2006 (PS2), NBA 06 (PS2), MLB (PSP) + case 0x3000: { // Syphon Filter: Dark Mirror (PS2/PSP), Syphon Filter: Logan's Shadow (PSP) + uint32_t offset = head_offset; + + // 00: header id + // 04: version + // 06: low number, seems related to file + // 08: entries (same as .skx) + // 0a: flags + // 0c: null? + // 10: entries again? + if (!is_id32be(offset + 0x00,sf_h, "STBL")) { + VGM_LOG("SKEX: incorrect .tbl\n"); + goto fail; + } + offset += 0x50; + + for (int i = 0; i < entries; i++) { + uint32_t curr_offset = read_u32le(offset + 0x00, sf_h); + // 04: 0 or 1 (doesn't seem to be related to loops, companion files or such) + // 05: null? + // 06: null? + uint8_t curr_type = read_u8 (offset + 0x07, sf_h); + + offset += 0x08; + + switch(curr_type) { + case 0x00: // dummy/config? + case 0x01: // dummy/config? + case 0x0e: // "Names" in .skx (empty?) + continue; + case 0x05: // .vag (mono) + case 0x09: // .at3 + case 0x0b: // .vpk + case 0x0c: // .vag (stereo) + break; + default: + vgm_logi("SKEX: unknown format %x (report)\n", curr_type); + goto fail; + } + + if (prev_offset == curr_offset) + continue; + prev_offset = curr_offset; + + total_subsongs++; + + if (target_subsong == total_subsongs && !subfile_offset) { + uint32_t next_offset = read_u32le(offset, sf_h); + subfile_offset = curr_offset; + subfile_size = next_offset - curr_offset; + subfile_type = curr_type; + } + } + break; + } + + case 0x5100: { // MLB 14 (Vita), MLB 15 (Vita) + uint32_t offset = head_offset; + uint16_t multiplier, align = 0; + + // 00: header id + // 04: version + // 06: low number, seems related to file + // 08: entries (same as .skx) + // 0a: null? + // 0c: file size (without padding) + // 10: offset to 2nd table + // 14: null? + // 18: offset multiplier (0x800/0x400/0x01) + // 1a: flags? (rarely 0x08) + // 1c: some entries? + // 20: null + // 24: entries again? + if (!is_id32be(offset + 0x00,sf_h, "STBL")) { + VGM_LOG("SKEX: incorrect .tbl\n"); + goto fail; + } + multiplier = read_u16le(offset + 0x18, sf_h); + + offset += 0x64; + for (int i = 0; i < entries; i++) { + uint32_t curr_offset = read_u32le(offset + 0x00, sf_h) * multiplier; + // 04: null? + uint8_t curr_type = read_u8 (offset + 0x05, sf_h); + + offset += 0x06; + + switch(curr_type) { + case 0x00: // dummy? + case 0x01: // some config? + case 0x0e: // "" + case 0x0f: // MIDX (maybe some instrument/midi definition, but data doesn't look midi-like) + continue; + case 0x02: // .at9 + case 0x42: // .at9 (no diffs?) + break; + default: + vgm_logi("SKEX: unknown format %x (report)\n", curr_type); + goto fail; + } + + // oddly misaligned by 1, no apparent flags [MLB 15 (Vita)-FEPXP.SKX] + if (curr_offset == 0x00 && multiplier == 0x800 && !align) { + align = multiplier; + } + curr_offset += align; + + if (prev_offset == curr_offset) + continue; + prev_offset = curr_offset; + + total_subsongs++; + + if (target_subsong == total_subsongs && !subfile_offset) { + uint32_t next_offset = read_u32le(offset, sf_h) * multiplier + align; + subfile_offset = curr_offset; + subfile_size = next_offset - curr_offset; + subfile_type = curr_type; + } + } + break; + } + default: + goto fail; + } + + if (total_subsongs == 0) { + vgm_logi("SKEX: bank has no subsongs\n"); //sometimes + goto fail; + } + + if (!check_subsongs(&target_subsong, total_subsongs)) + goto fail; + + ;VGM_LOG("subfile=%x, %x, %x, %i\n", subfile_offset, subfile_size, subfile_type, total_subsongs); + { + init_vgmstream_t init_vgmstream = NULL; + const char* ext; + switch(subfile_type) { + case 0x05: + case 0x0c: init_vgmstream = init_vgmstream_vag; ext = "vag"; break; + case 0x09: init_vgmstream = init_vgmstream_riff; ext = "at3"; break; + case 0x0b: init_vgmstream = init_vgmstream_vpk; ext = "vpk"; break; + case 0x02: + case 0x42: init_vgmstream = init_vgmstream_riff; ext = "at9"; break; + default: goto fail; + } + + if (subfile_type == 0x09 || subfile_type == 0x02) { // use RIFF's + subfile_size = read_u32le(subfile_offset + 0x04, sf) + 0x08; + } + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, ext); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream(temp_sf); + if (!vgmstream) goto fail; + } + + vgmstream->num_streams = total_subsongs; + + if (sf_h != sf) close_streamfile(sf_h); + close_streamfile(temp_sf); + return vgmstream; +fail: + if (sf_h != sf) close_streamfile(sf_h); + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp_parser.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp_parser.c index cfe925532..0f6ff132e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp_parser.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp_parser.c @@ -854,7 +854,7 @@ static int add_entry(txtp_header_t* txtp, char* filename, int is_default) { txtp_entry_t entry = {0}; - ;VGM_LOG("TXTP: input filename=%s\n", filename); + //;VGM_LOG("TXTP: input filename=%s\n", filename); /* parse filename: file.ext#(commands) */ { @@ -896,11 +896,11 @@ static int add_entry(txtp_header_t* txtp, char* filename, int is_default) { params = NULL; } - ;VGM_LOG("TXTP: params=%s\n", params); + //;VGM_LOG("TXTP: params=%s\n", params); parse_params(&entry, params); } - ;VGM_LOG("TXTP: output filename=%s\n", filename); + //;VGM_LOG("TXTP: output filename=%s\n", filename); clean_filename(filename); //;VGM_LOG("TXTP: clean filename='%s'\n", filename); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c index d2b9b07e7..cd179e369 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -307,6 +307,15 @@ VGMSTREAM* init_vgmstream_vag(STREAMFILE* sf) { channel_size -= 0x40; loop_flag = ps_find_loop_offsets(sf, start_offset, channel_size, channels, interleave, &loop_start_sample, &loop_end_sample); } + else if (version == 0x00000020 && is_id64be(0x20, sf, "KAudioDL") && ( (channel_size + 0x30) * 2 == file_size + || align_size(channel_size + 0x30, 0x800) * 2 == file_size || align_size(channel_size + 0x30, 0x400) * 2 == file_size) ) { + /* .SKX stereo vag (name is always KAudioDLL and streams are padded unlike memory audio) [NBA 06 (PS2)] */ + start_offset = 0x30; + interleave = file_size / 2; + channels = 2; // mono KAudioDLL streams also exist + + loop_flag = ps_find_loop_offsets(sf, start_offset, channel_size, channels, interleave, &loop_start_sample, &loop_end_sample); + } else { /* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */ start_offset = 0x30; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wave.c b/Frameworks/vgmstream/vgmstream/src/meta/wave.c index 921c8a716..c22d2efa5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wave.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wave.c @@ -8,7 +8,6 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) { uint32_t start_offset, extradata_offset, interleave; int channels, loop_flag, sample_rate, codec, version; int32_t num_samples, loop_start, loop_end; - int big_endian; read_u32_t read_u32; read_s32_t read_s32; read_f32_t read_f32; @@ -25,7 +24,7 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) { if (!check_extensions(sf, "wave")) return NULL; - big_endian = read_u32be(0x00,sf) == 0xE5B7ECFE || is_id32be(0x00,sf, "WWAV"); + bool big_endian = read_u32be(0x00,sf) == 0xE5B7ECFE || is_id32be(0x00,sf, "WWAV"); if (big_endian) { read_u32 = read_u32be; read_s32 = read_s32be; @@ -46,7 +45,7 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) { loop_end = read_s32(0x18, sf); codec = read_u8(0x1c, sf); - channels = read_u8(0x1d, sf); + channels = read_u8(0x1d, sf); // DS can only do mono if (read_u8(0x1e, sf) != 0x00) goto fail; /* unknown */ if (read_u8(0x1f, sf) != 0x00) goto fail; /* unknown */ @@ -60,14 +59,19 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) { if(!loop_flag && loop_start == 0 && loop_end == num_samples /* full loop */ && (channels > 1 || (channels == 1 && start_offset <= 0x40)) - && num_samples > 30*sample_rate) { /* in seconds */ + && num_samples > 30 * sample_rate) { /* in seconds */ loop_flag = 1; } - /* normalize codec: WWAV uses codec 0x00 for DSP */ + /* normalize codec (files generated by DsBuildWave/3dsBuildWave) */ if (codec == 0x00 && version == 0x00050000 && start_offset > 0x40) { + /* WWAV uses codec 0x00 for DSP (only one?) */ codec = 0x02; } + else if (codec == 0x02 && start_offset <= 0x40) { + /* DS games use IMA, no apparent flag (could also test ID) */ + codec = 0x03; + } /* build the VGMSTREAM */ @@ -81,39 +85,51 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) { vgmstream->meta_type = meta_WAVE; - /* not sure if there are other codecs but anyway (based on wave-segmented) */ + /* some codecs aren't used by known games but can be created by DsBuildWave/3dsBuildWave */ switch(codec) { - case 0x02: - /* DS games use IMA, no apparent flag (could also test ID) */ - if (start_offset <= 0x40) { - vgmstream->coding_type = coding_IMA_int; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - - /* extradata: - * 0x00: base hist? (only seen 0) - * 0x02: base step? (only seen 0) - * 0x04: loop hist? - * 0x06: loop step? - */ - } - else { - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - - /* ADPCM setup: 0x20 coefs + 0x06 initial ps/hist1/hist2 + 0x06 loop ps/hist1/hist2 + ?, per channel */ - int head_spacing = 0x2c; - int hist_spacing = 0x22; - if (version == 0x00050000) { /* has an extra empty 16b after coefs */ - head_spacing = 0x2e; - hist_spacing = 0x24; - } - - dsp_read_coefs(vgmstream, sf, extradata_offset + 0x00, head_spacing, big_endian); - dsp_read_hist(vgmstream, sf, extradata_offset + hist_spacing, head_spacing, big_endian); - } + case 0x00: // PCM8 (not seen) + vgmstream->coding_type = coding_PCM8; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; break; + + case 0x01: // PCM16 (not seen) + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + break; + + case 0x02: { // DSP (3DS only, common) + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + /* ADPCM setup: 0x20 coefs + 0x06 initial ps/hist1/hist2 + 0x06 loop ps/hist1/hist2 + ?, per channel */ + int head_spacing = 0x2c; + int hist_spacing = 0x22; + if (version == 0x00050000) { /* has an extra empty 16b after coefs */ + head_spacing = 0x2e; + hist_spacing = 0x24; + } + + dsp_read_coefs(vgmstream, sf, extradata_offset + 0x00, head_spacing, big_endian); + dsp_read_hist(vgmstream, sf, extradata_offset + hist_spacing, head_spacing, big_endian); + break; + } + + case 0x03: //IMA (DS uses codec 02 for IMA, common; 3DS: uses 03 but not seen) + vgmstream->coding_type = coding_IMA_int; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + /* extradata: + * 0x00: base hist? (only seen 0) + * 0x02: base step? (only seen 0) + * 0x04: loop hist? + * 0x06: loop step? + */ + break; + default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xabp.c b/Frameworks/vgmstream/vgmstream/src/meta/xabp.c new file mode 100644 index 000000000..625e49672 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xabp.c @@ -0,0 +1,62 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util/meta_utils.h" + + + /* XABp - cavia PS2 bank format [Drakengard 1/2 (PS2), Ghost in the Shell: SAC (PS2)] */ +VGMSTREAM* init_vgmstream_xabp(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + + + /* checks */ + if (!is_id32be(0x00,sf, "pBAX")) + return NULL; + + // .hd2+bd: from bigfiles + if (!check_extensions(sf, "hd2")) + return NULL; + + meta_header_t h = { + .meta = meta_XABP, + }; + h.target_subsong = sf->stream_index; + if (h.target_subsong == 0) + h.target_subsong = 1; + + // cavia's bank format inspired by .hd+bd + uint32_t bd_size = read_u32le(0x04,sf); + // 0x08: null + h.total_subsongs = read_s16le(0x0c,sf); + // 0x0e: always 0x0010? + + uint32_t head_offset = 0x10 + 0x20 * (h.target_subsong - 1); + // 00: file id? + h.sample_rate = read_s16le(head_offset + 0x16,sf); + h.stream_offset = read_u32le(head_offset + 0x18, sf); + // others: config? (can't make sense of them, don't seem quite like sizes/flags/etc) + + h.channels = 1; + + h.coding = coding_PSX; + h.layout = layout_none; + h.open_stream = true; + h.has_subsongs = true; + + h.sf_head = sf; + h.sf_body = open_streamfile_by_ext(sf,"bd"); + if (!h.sf_body) goto fail; + + // Entries/offsets aren't ordered .bd not it seems to have sizes (maybe mixes notes+streams into one) + // Since PS-ADPCM is wired to play until end frame end or loop, it's probably designed like that. + // It also repeats entries (different ID but same config) but for now just prints it as is; this also happens in bigfiles. + h.loop_flag = ps_find_stream_info(h.sf_body, h.stream_offset, bd_size - h.stream_offset, h.channels, h.interleave, &h.loop_start, &h.loop_end, &h.stream_size); + h.num_samples = ps_bytes_to_samples(h.stream_size, h.channels); + + vgmstream = alloc_metastream(&h); + close_streamfile(h.sf_body); + return vgmstream; +fail: + close_streamfile(h.sf_body); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index b1eeb496c..78a7c4351 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -52,6 +52,9 @@ uint32_t clamp_u32(uint32_t v, uint32_t min, uint32_t max); int round10(int val); +#define align_size align_size_to_block + +// returns size with padding, ex. value=0x560, block=0x100 > 0x600 size_t align_size_to_block(size_t value, size_t block_align); /* return a file's extension (a pointer to the first character of the diff --git a/Frameworks/vgmstream/vgmstream/src/util/meta_utils.c b/Frameworks/vgmstream/vgmstream/src/util/meta_utils.c index fdf5235a3..bea8b0608 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/meta_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/util/meta_utils.c @@ -15,6 +15,11 @@ VGMSTREAM* alloc_metastream(meta_header_t* h) { return NULL; } + if (h->has_subsongs && (h->target_subsong < 0 || h->target_subsong > h->total_subsongs || h->total_subsongs < 1)) { + VGM_LOG("meta: wrong subsongs %i vs %i\n", h->target_subsong, h->total_subsongs); + return NULL; + } + VGMSTREAM* vgmstream = allocate_vgmstream(h->channels, h->loop_flag); if (!vgmstream) return NULL; @@ -31,6 +36,7 @@ VGMSTREAM* alloc_metastream(meta_header_t* h) { vgmstream->num_streams = h->total_subsongs; vgmstream->stream_size = h->stream_size; vgmstream->interleave_block_size = h->interleave; + vgmstream->interleave_last_block_size = h->interleave_last; vgmstream->allow_dual_stereo = h->allow_dual_stereo; if (h->name_offset) @@ -44,7 +50,7 @@ VGMSTREAM* alloc_metastream(meta_header_t* h) { } if (h->open_stream) { - if (!vgmstream_open_stream(vgmstream, h->sf ? h->sf : h->sf_head, h->stream_offset)) + if (!vgmstream_open_stream(vgmstream, h->sf ? h->sf : h->sf_body, h->stream_offset)) goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/util/meta_utils.h b/Frameworks/vgmstream/vgmstream/src/util/meta_utils.h index 4c3b37bc9..3e7e31077 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/meta_utils.h +++ b/Frameworks/vgmstream/vgmstream/src/util/meta_utils.h @@ -24,6 +24,7 @@ typedef struct { int total_subsongs; int32_t interleave; + int32_t interleave_last; /* common helpers */ uint32_t stream_offset; /* where current stream starts */ @@ -54,6 +55,7 @@ typedef struct { bool open_stream; + bool has_subsongs; bool allow_dual_stereo; } meta_header_t; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 9780c359c..3f8ea3619 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -82,13 +82,12 @@ bool prepare_vgmstream(VGMSTREAM* vgmstream, STREAMFILE* sf) { } #endif - /* some players are picky with incorrect channel layouts */ + /* some players are picky with incorrect channel layouts (also messes ups downmixing calcs) */ if (vgmstream->channel_layout > 0) { - int output_channels = vgmstream->channels; int count = 0, max_ch = 32; for (int ch = 0; ch < max_ch; ch++) { int bit = (vgmstream->channel_layout >> ch) & 1; - if (ch > 17 && bit) { + if (ch > 17 && bit) { // unknown past 16 VGM_LOG("VGMSTREAM: wrong bit %i in channel_layout %x\n", ch, vgmstream->channel_layout); vgmstream->channel_layout = 0; break; @@ -96,8 +95,8 @@ bool prepare_vgmstream(VGMSTREAM* vgmstream, STREAMFILE* sf) { count += bit; } - if (count > output_channels) { - VGM_LOG("VGMSTREAM: wrong totals %i in channel_layout %x\n", count, vgmstream->channel_layout); + if (count != vgmstream->channels) { + VGM_LOG("VGMSTREAM: ignored mismatched channel_layout %04x, uses %i vs %i channels\n", vgmstream->channel_layout, count, vgmstream->channels); vgmstream->channel_layout = 0; } } @@ -114,6 +113,14 @@ bool prepare_vgmstream(VGMSTREAM* vgmstream, STREAMFILE* sf) { vgmstream->stream_index = sf->stream_index; } + //TODO: this should be called in setup_vgmstream sometimes, but hard to detect since it's used for other stuff + /* clean as loops are readable metadata but loop fields may contain garbage + * (done *after* dual stereo as it needs loop fields to match) */ + if (!vgmstream->loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = 0; + } + setup_vgmstream(vgmstream); /* final setup */ @@ -225,10 +232,8 @@ VGMSTREAM* allocate_vgmstream(int channels, int loop_flag) { vgmstream->mixer = mixer_init(vgmstream->channels); /* pre-init */ if (!vgmstream->mixer) goto fail; -#if VGM_TEST_DECODER vgmstream->decode_state = decode_init(); if (!vgmstream->decode_state) goto fail; -#endif //TODO: improve/init later to minimize memory /* garbage buffer for seeking/discarding (local bufs may cause stack overflows with segments/layers) @@ -420,9 +425,7 @@ static bool merge_vgmstream(VGMSTREAM* opened_vgmstream, VGMSTREAM* new_vgmstrea opened_vgmstream->layout_type = layout_none; /* fixes some odd cases */ /* discard the second VGMSTREAM */ -#if VGM_TEST_DECODER decode_free(new_vgmstream); -#endif mixer_free(new_vgmstream->mixer); free(new_vgmstream->tmpbuf); free(new_vgmstream->start_vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index c0d44216a..7512c94eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -250,9 +250,7 @@ typedef struct { void* tmpbuf; /* garbage buffer used for seeking/trimming */ size_t tmpbuf_size; /* for all channels (samples = tmpbuf_size / channels / sample_size) */ -#if VGM_TEST_DECODER - void* decode_state; /* for some decoders (TO-DO: to be mover around) */ -#endif + void* decode_state; /* for some decoders (TO-DO: to be moved around) */ } VGMSTREAM; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream_init.c b/Frameworks/vgmstream/vgmstream/src/vgmstream_init.c index ceef04b02..fd514a1c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream_init.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream_init.c @@ -510,6 +510,13 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_dsp_asura_sfx, init_vgmstream_adp_ongakukan, init_vgmstream_sdd, + init_vgmstream_ka1a, + init_vgmstream_hd_bd, + init_vgmstream_pphd, + init_vgmstream_xabp, + init_vgmstream_i3ds, + init_vgmstream_sdbs, + init_vgmstream_skex, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_agsc, diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream_types.h b/Frameworks/vgmstream/vgmstream/src/vgmstream_types.h index ec66d6574..24ade7411 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream_types.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream_types.h @@ -63,7 +63,7 @@ typedef enum { coding_IMA_int, /* IMA ADPCM (mono/interleave, low nibble first) */ coding_DVI_IMA, /* DVI IMA ADPCM (stereo or mono, high nibble first) */ coding_DVI_IMA_int, /* DVI IMA ADPCM (mono/interleave, high nibble first) */ - coding_NW_IMA, + coding_CAMELOT_IMA, coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ coding_QD_IMA, coding_WV6_IMA, /* Gorilla Systems WV6 4-bit IMA ADPCM */ @@ -145,6 +145,7 @@ typedef enum { coding_TAC, /* tri-Ace Codec (MDCT-based) */ coding_ICE_RANGE, /* Inti Creates "range" codec */ coding_ICE_DCT, /* Inti Creates "DCT" codec */ + coding_KA1A, /* Koei Tecmo codec (transform-based) */ #ifdef VGM_USE_VORBIS coding_OGG_VORBIS, /* Xiph Vorbis with Ogg layer (MDCT-based) */ @@ -710,6 +711,11 @@ typedef enum { meta_DSP_ASURA, meta_ONGAKUKAN_RIFF_ADP, meta_SDD, + meta_KA1A, + meta_HD_BD, + meta_PPHD, + meta_XABP, + meta_I3DS, } meta_t; diff --git a/Info.plist.template b/Info.plist.template index cf9f5fe32..555f94bc9 100644 --- a/Info.plist.template +++ b/Info.plist.template @@ -537,7 +537,10 @@ gwm h4m hab + hbd hca + hd + hd2 hd3 hdr hdt @@ -584,6 +587,8 @@ ivs joe jstm + k2sb + ka1a kat kces kcey @@ -724,6 +729,7 @@ past pcm pdt + phd pk pona pos @@ -812,6 +818,7 @@ sgb sgd sgt + skx slb sli smc