diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 852c63eda..b3fbe8b58 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -233,6 +233,11 @@ 8351F32D2212B57000A606E4 /* 208.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32A2212B57000A606E4 /* 208.c */; }; 8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */; }; 8351F32F2212B57000A606E4 /* dsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32C2212B57000A606E4 /* dsf.c */; }; + 835B9B8F2730BF2D00F87EE3 /* ast_mv.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8A2730BF2C00F87EE3 /* ast_mv.c */; }; + 835B9B902730BF2D00F87EE3 /* ast_mmv.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */; }; + 835B9B912730BF2D00F87EE3 /* lopu_fb.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */; }; + 835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */ = {isa = PBXBuildFile; fileRef = 835B9B8D2730BF2D00F87EE3 /* hca_bf.h */; }; + 835B9B932730BF2D00F87EE3 /* lpcm_shade.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */; }; 835C883622CC17BE001B4B3F /* bwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C883122CC17BD001B4B3F /* bwav.c */; }; 835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */; }; 836C052B23F62F1A00FA07C7 /* libatrac9.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 835FC6C623F62AEF006960FA /* libatrac9.framework */; }; @@ -346,7 +351,6 @@ 836F6FCA18BDC2190095E648 /* ps2_adm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8E18BDC2180095E648 /* ps2_adm.c */; }; 836F6FCB18BDC2190095E648 /* ads.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8F18BDC2180095E648 /* ads.c */; }; 836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9118BDC2180095E648 /* ps2_ass.c */; }; - 836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9218BDC2180095E648 /* ps2_ast.c */; }; 836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9418BDC2180095E648 /* ps2_b1s.c */; }; 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9518BDC2180095E648 /* ps2_bg00.c */; }; 836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9618BDC2180095E648 /* ps2_bmdx.c */; }; @@ -363,7 +367,6 @@ 836F6FE018BDC2190095E648 /* ps2_joe.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA418BDC2180095E648 /* ps2_joe.c */; }; 836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA618BDC2180095E648 /* ps2_kces.c */; }; 836F6FE418BDC2190095E648 /* ps2_leg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA818BDC2180095E648 /* ps2_leg.c */; }; - 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA918BDC2180095E648 /* ps2_lpcm.c */; }; 836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAA18BDC2180095E648 /* ps2_mcg.c */; }; 836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAC18BDC2180095E648 /* ps2_mic.c */; }; 836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAD18BDC2180095E648 /* ps2_mihb.c */; }; @@ -519,7 +522,6 @@ 83997F5B22D9569E00633184 /* rad.c in Sources */ = {isa = PBXBuildFile; fileRef = 83997F5722D9569E00633184 /* rad.c */; }; 839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BD11F1EEE1CF200198540 /* ngc_ulw.c */; }; 839C3D27270D49FF00E13653 /* lpcm_fb.c in Sources */ = {isa = PBXBuildFile; fileRef = 839C3D22270D49FF00E13653 /* lpcm_fb.c */; }; - 839C3D28270D49FF00E13653 /* lopu.c in Sources */ = {isa = PBXBuildFile; fileRef = 839C3D26270D49FF00E13653 /* lopu.c */; }; 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */; }; 839E21E11F2EDAF100EE54D7 /* vorbis_custom_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */; }; 839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */; }; @@ -1045,6 +1047,11 @@ 8351F32A2212B57000A606E4 /* 208.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 208.c; sourceTree = ""; }; 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_bao_streamfile.h; sourceTree = ""; }; 8351F32C2212B57000A606E4 /* dsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsf.c; sourceTree = ""; }; + 835B9B8A2730BF2C00F87EE3 /* ast_mv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ast_mv.c; sourceTree = ""; }; + 835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ast_mmv.c; sourceTree = ""; }; + 835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lopu_fb.c; sourceTree = ""; }; + 835B9B8D2730BF2D00F87EE3 /* hca_bf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_bf.h; sourceTree = ""; }; + 835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpcm_shade.c; sourceTree = ""; }; 835C883122CC17BD001B4B3F /* bwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bwav.c; sourceTree = ""; }; 835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ogg_vorbis_streamfile.h; sourceTree = ""; }; 835FC6C123F62AEE006960FA /* libatrac9.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libatrac9.xcodeproj; path = ../libatrac9/libatrac9.xcodeproj; sourceTree = ""; }; @@ -1159,7 +1166,6 @@ 836F6E8E18BDC2180095E648 /* ps2_adm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_adm.c; sourceTree = ""; }; 836F6E8F18BDC2180095E648 /* ads.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ads.c; sourceTree = ""; }; 836F6E9118BDC2180095E648 /* ps2_ass.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ass.c; sourceTree = ""; }; - 836F6E9218BDC2180095E648 /* ps2_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ast.c; sourceTree = ""; }; 836F6E9418BDC2180095E648 /* ps2_b1s.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_b1s.c; sourceTree = ""; }; 836F6E9518BDC2180095E648 /* ps2_bg00.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_bg00.c; sourceTree = ""; }; 836F6E9618BDC2180095E648 /* ps2_bmdx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_bmdx.c; sourceTree = ""; }; @@ -1176,7 +1182,6 @@ 836F6EA418BDC2180095E648 /* ps2_joe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_joe.c; sourceTree = ""; }; 836F6EA618BDC2180095E648 /* ps2_kces.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_kces.c; sourceTree = ""; }; 836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = ""; }; - 836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = ""; }; 836F6EAA18BDC2180095E648 /* ps2_mcg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mcg.c; sourceTree = ""; }; 836F6EAC18BDC2180095E648 /* ps2_mic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mic.c; sourceTree = ""; }; 836F6EAD18BDC2180095E648 /* ps2_mihb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mihb.c; sourceTree = ""; }; @@ -1331,7 +1336,6 @@ 8399335E2591E8C0001855AF /* sbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sbk.c; sourceTree = ""; }; 83997F5722D9569E00633184 /* rad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rad.c; sourceTree = ""; }; 839C3D22270D49FF00E13653 /* lpcm_fb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpcm_fb.c; sourceTree = ""; }; - 839C3D26270D49FF00E13653 /* lopu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lopu.c; sourceTree = ""; }; 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vorbis_custom_data_fsb.h; sourceTree = ""; }; 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_decoder.c; sourceTree = ""; }; 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_wwise.c; sourceTree = ""; }; @@ -1827,6 +1831,8 @@ 834FE0C7215C79E7000A5D3D /* apc.c */, 836F6E3418BDC2180095E648 /* apple_caff.c */, 8315958820FEC83F007002F0 /* asf.c */, + 835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */, + 835B9B8A2730BF2C00F87EE3 /* ast_mv.c */, 836F6E3518BDC2180095E648 /* ast.c */, 8306B0D520984590000302D4 /* atsl.c */, 83A21F7C201D897F000F04B9 /* atx.c */, @@ -1919,6 +1925,7 @@ 83709DFF1ECBC1A4005C03D3 /* gtd.c */, 8342469020C4D22F00926E48 /* h4m.c */, 836F6E5218BDC2180095E648 /* halpst.c */, + 835B9B8D2730BF2D00F87EE3 /* hca_bf.h */, 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */, 83AA5D211F6E2F9C0020821C /* hca_keys.h */, 8323894F1D2246C300482226 /* hca.c */, @@ -1949,8 +1956,9 @@ 83D20074248DDB760048BD24 /* ktsr.c */, 830EBE122004656E0023AA10 /* ktss.c */, 8373342423F60CDB00DE14DC /* kwb.c */, - 839C3D26270D49FF00E13653 /* lopu.c */, + 835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */, 839C3D22270D49FF00E13653 /* lpcm_fb.c */, + 835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */, 8373341F23F60CDB00DE14DC /* lrmd_streamfile.h */, 8373342223F60CDB00DE14DC /* lrmd.c */, 836F6E5A18BDC2180095E648 /* lsf.c */, @@ -2041,7 +2049,6 @@ 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */, 836F6E8E18BDC2180095E648 /* ps2_adm.c */, 836F6E9118BDC2180095E648 /* ps2_ass.c */, - 836F6E9218BDC2180095E648 /* ps2_ast.c */, 836F6E9418BDC2180095E648 /* ps2_b1s.c */, 836F6E9518BDC2180095E648 /* ps2_bg00.c */, 836F6E9618BDC2180095E648 /* ps2_bmdx.c */, @@ -2059,7 +2066,6 @@ 836F6EA418BDC2180095E648 /* ps2_joe.c */, 836F6EA618BDC2180095E648 /* ps2_kces.c */, 836F6EA818BDC2180095E648 /* ps2_leg.c */, - 836F6EA918BDC2180095E648 /* ps2_lpcm.c */, 836F6EAA18BDC2180095E648 /* ps2_mcg.c */, 836F6EAC18BDC2180095E648 /* ps2_mic.c */, 836F6EAD18BDC2180095E648 /* ps2_mihb.c */, @@ -2354,6 +2360,7 @@ 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */, 83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */, 836F705418BDC2190095E648 /* streamfile.h in Headers */, + 835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */, 83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */, 8373342723F60CDC00DE14DC /* lrmd_streamfile.h in Headers */, 83C7281122BC893D00678B4A /* 9tav_streamfile.h in Headers */, @@ -2763,7 +2770,6 @@ 83AA7F812519C042004C5298 /* silence.c in Sources */, 834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */, 837CEA7A23487E2500E62A4A /* ffmpeg_decoder_utils.c in Sources */, - 839C3D28270D49FF00E13653 /* lopu.c in Sources */, 837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */, 836F6F6718BDC2190095E648 /* acm.c in Sources */, 834FE0FD215C79ED000A5D3D /* vpk.c in Sources */, @@ -2774,6 +2780,7 @@ 8306B0A720984552000302D4 /* blocked_xvas.c in Sources */, 8349A9101FE6258200E26435 /* ea_eaac.c in Sources */, 836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */, + 835B9B912730BF2D00F87EE3 /* lopu_fb.c in Sources */, 8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */, 8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */, 836F6F6F18BDC2190095E648 /* akb.c in Sources */, @@ -2892,7 +2899,6 @@ 836F6F8818BDC2190095E648 /* fsb.c in Sources */, 83F2CCE525A5B41600F46FA8 /* acx.c in Sources */, 83FC176D23AC58D100E1025F /* xma_ue3.c in Sources */, - 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */, 8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */, 836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */, 836F6FC018BDC2190095E648 /* p3d.c in Sources */, @@ -2946,7 +2952,6 @@ 836F6FFA18BDC2190095E648 /* spm.c in Sources */, 83C7281B22BC893D00678B4A /* strm_abylight.c in Sources */, 834D3A6E19F47C98001C54F6 /* g1l.c in Sources */, - 836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */, 832BF82A21E0514B006F50F1 /* vs_square.c in Sources */, 834FE0B4215C798C000A5D3D /* ffmpeg_decoder_custom_opus.c in Sources */, 8349A9091FE6258200E26435 /* pc_ast.c in Sources */, @@ -2986,6 +2991,7 @@ 8349A9161FE6258200E26435 /* flx.c in Sources */, 832BF82921E0514B006F50F1 /* msf_banpresto.c in Sources */, 834FE0BE215C79A9000A5D3D /* blocked_xa_aiff.c in Sources */, + 835B9B902730BF2D00F87EE3 /* ast_mmv.c in Sources */, 836F702A18BDC2190095E648 /* sd9.c in Sources */, 836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */, 836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */, @@ -3050,6 +3056,7 @@ 836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */, 836F702618BDC2190095E648 /* s14_sss.c in Sources */, 834FE102215C79ED000A5D3D /* rfrm.c in Sources */, + 835B9B8F2730BF2D00F87EE3 /* ast_mv.c in Sources */, 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, 836F702E18BDC2190095E648 /* sli.c in Sources */, 83AA7F822519C042004C5298 /* ktsc.c in Sources */, @@ -3099,6 +3106,7 @@ 836F6F4118BDC2190095E648 /* blocked.c in Sources */, 836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */, 839933612591E8C1001855AF /* sbk.c in Sources */, + 835B9B932730BF2D00F87EE3 /* lpcm_shade.c in Sources */, 838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */, 832BF82B21E0514B006F50F1 /* xopus.c in Sources */, 836F6F3418BDC2190095E648 /* nwa_decoder.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/ext_libs/celt-0110/ecintrin.h b/Frameworks/vgmstream/vgmstream/ext_libs/celt-0110/ecintrin.h new file mode 100644 index 000000000..0348e6d75 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/ext_libs/celt-0110/ecintrin.h @@ -0,0 +1,140 @@ +/* Copyright (c) 2003-2008 Timothy B. Terriberry + Copyright (c) 2008 Xiph.Org Foundation */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*Some common macros for potential platform-specific optimization.*/ +#include +#include +#if !defined(_ecintrin_H) +# define _ecintrin_H (1) + +/*Some specific platforms may have optimized intrinsic or inline assembly + versions of these functions which can substantially improve performance. + We define macros for them to allow easy incorporation of these non-ANSI + features.*/ + +/*Note that we do not provide a macro for abs(), because it is provided as a + library function, which we assume is translated into an intrinsic to avoid + the function call overhead and then implemented in the smartest way for the + target platform. + With modern gcc (4.x), this is true: it uses cmov instructions if the + architecture supports it and branchless bit-twiddling if it does not (the + speed difference between the two approaches is not measurable). + Interestingly, the bit-twiddling method was patented in 2000 (US 6,073,150) + by Sun Microsystems, despite prior art dating back to at least 1996: + http://web.archive.org/web/19961201174141/www.x86.org/ftp/articles/pentopt/PENTOPT.TXT + On gcc 3.x, however, our assumption is not true, as abs() is translated to a + conditional jump, which is horrible on deeply piplined architectures (e.g., + all consumer architectures for the past decade or more) when the sign cannot + be reliably predicted.*/ + +/*Modern gcc (4.x) can compile the naive versions of min and max with cmov if + given an appropriate architecture, but the branchless bit-twiddling versions + are just as fast, and do not require any special target architecture. + Earlier gcc versions (3.x) compiled both code to the same assembly + instructions, because of the way they represented ((_b)>(_a)) internally.*/ +#define EC_MAXI(_a,_b) ((_a)-((_a)-(_b)&-((_b)>(_a)))) +#define EC_MINI(_a,_b) ((_a)+((_b)-(_a)&-((_b)<(_a)))) +/*This has a chance of compiling branchless, and is just as fast as the + bit-twiddling method, which is slightly less portable, since it relies on a + sign-extended rightshift, which is not guaranteed by ANSI (but present on + every relevant platform).*/ +#define EC_SIGNI(_a) (((_a)>0)-((_a)<0)) +/*Slightly more portable than relying on a sign-extended right-shift (which is + not guaranteed by ANSI), and just as fast, since gcc (3.x and 4.x both) + compile it into the right-shift anyway.*/ +#define EC_SIGNMASK(_a) (-((_a)<0)) +/*Clamps an integer into the given range. + If _a>_c, then the lower bound _a is respected over the upper bound _c (this + behavior is required to meet our documented API behavior). + _a: The lower bound. + _b: The value to clamp. + _c: The upper boud.*/ +#define EC_CLAMPI(_a,_b,_c) (EC_MAXI(_a,EC_MINI(_b,_c))) + + +/*Count leading zeros. + This macro should only be used for implementing ec_ilog(), if it is defined. + All other code should use EC_ILOG() instead.*/ +#if defined(_MSC_VER) +# include +static __inline int ec_bsr(unsigned long _x){ + unsigned long ret; + _BitScanReverse(&ret,_x); + return (int)ret; +} +# define EC_CLZ0 (1) +# define EC_CLZ(_x) (-ec_bsr(_x)) +#elif defined(ENABLE_TI_DSPLIB) +# include "dsplib.h" +# define EC_CLZ0 (31) +# define EC_CLZ(_x) (_lnorm(x)) +#elif defined(__GNUC_PREREQ) +# if __GNUC_PREREQ(3,4) +# if INT_MAX>=2147483647 +# define EC_CLZ0 ((int)sizeof(unsigned)*CHAR_BIT) +# define EC_CLZ(_x) (__builtin_clz(_x)) +# elif LONG_MAX>=2147483647L +# define EC_CLZ0 ((int)sizeof(unsigned long)*CHAR_BIT) +# define EC_CLZ(_x) (__builtin_clzl(_x)) +# endif +# endif +#endif + +#if defined(EC_CLZ) +/*Note that __builtin_clz is not defined when _x==0, according to the gcc + documentation (and that of the BSR instruction that implements it on x86). + The majority of the time we can never pass it zero. + When we need to, it can be special cased.*/ +# define EC_ILOG(_x) (EC_CLZ0-EC_CLZ(_x)) +#else +static int ec_ilog2(celt_uint32 _v){ + int ret; + int m; + ret=!!_v; + m=!!(_v&0xFFFF0000)<<4; + _v>>=m; + ret|=m; + m=!!(_v&0xFF00)<<3; + _v>>=m; + ret|=m; + m=!!(_v&0xF0)<<2; + _v>>=m; + ret|=m; + m=!!(_v&0xC)<<1; + _v>>=m; + ret|=m; + ret+=!!(_v&0x2); + return ret; +} +# define EC_ILOG(_x) (ec_ilog2(_x)) +#endif + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 3d05f987d..40ea9f99e 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -115,7 +115,7 @@ void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing /* xa_decoder */ -void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8); +void decode_xa(VGMSTREAM* v, sample_t* outbuf, int32_t samples_to_do); size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps); @@ -172,6 +172,7 @@ int msadpcm_check_coefs(STREAMFILE* sf, off_t offset); /* yamaha_decoder */ void decode_aica(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_cp_ym(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); void decode_aska(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size); void decode_nxap(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t yamaha_bytes_to_samples(size_t bytes, int channels); @@ -324,9 +325,21 @@ void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do); void reset_hca(hca_codec_data* data); void loop_hca(hca_codec_data* data, int32_t num_sample); void free_hca(hca_codec_data* data); -int test_hca_key(hca_codec_data* data, unsigned long long keycode); -void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode); clHCA_stInfo* hca_get_info(hca_codec_data* data); + +typedef struct { + /* config + output */ + uint64_t key; + uint16_t subkey; + uint64_t best_key; + int best_score; + /* internals */ + uint32_t start_offset; +} hca_keytest_t; + +void test_hca_key(hca_codec_data* data, hca_keytest_t* hk); +void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode); + STREAMFILE* hca_get_streamfile(hca_codec_data* data); @@ -465,7 +478,6 @@ void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, void reset_mpeg(mpeg_codec_data* data); void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample); void free_mpeg(mpeg_codec_data* data); -void flush_mpeg(mpeg_codec_data* data); int mpeg_get_sample_rate(mpeg_codec_data* data); long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c index 627926ca0..4f3420d16 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c @@ -1,7 +1,8 @@ #include "coding.h" -#include #ifdef VGM_USE_G719 +#include + #define G719_MAX_CODES ((1280/8)) /* in int16, so max frame size is (value/8)*2 (0xF0=common, 0x140=decoder max 2560b, rare) */ struct g719_codec_data { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c index 496d54d6e..d30ce2729 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c @@ -1,7 +1,8 @@ #include "coding.h" -#include "g7221_decoder_lib.h" #ifdef VGM_USE_G7221 +#include "g7221_decoder_lib.h" + #define G7221_MAX_FRAME_SIZE 0x78 /* 960/8 */ #define G7221_MAX_FRAME_SAMPLES 640 /* 32000/50 */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c index d4183b62f..74c73693c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c @@ -3,7 +3,7 @@ struct hca_codec_data { - STREAMFILE* streamfile; + STREAMFILE* sf; clHCA_stInfo info; signed short* sample_buffer; @@ -58,8 +58,8 @@ hca_codec_data* init_hca(STREAMFILE* sf) { if (!data->sample_buffer) goto fail; /* load streamfile for reads */ - data->streamfile = reopen_streamfile(sf, 0); - if (!data->streamfile) goto fail; + data->sf = reopen_streamfile(sf, 0); + if (!data->sf) goto fail; /* set initial values */ reset_hca(data); @@ -115,7 +115,7 @@ void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { } /* read frame */ - bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile); + bytes = read_streamfile(data->data_buffer, offset, blockSize, data->sf); if (bytes != blockSize) { VGM_LOG("HCA: read %x vs expected %x bytes at %x\n", bytes, blockSize, (uint32_t)offset); break; @@ -171,7 +171,7 @@ void loop_hca(hca_codec_data* data, int32_t num_sample) { void free_hca(hca_codec_data* data) { if (!data) return; - close_streamfile(data->streamfile); + close_streamfile(data->sf); clHCA_done(data->handle); free(data->handle); free(data->data_buffer); @@ -179,6 +179,14 @@ void free_hca(hca_codec_data* data) { free(data); } +clHCA_stInfo* hca_get_info(hca_codec_data* data) { + return &data->info; +} + +STREAMFILE* hca_get_streamfile(hca_codec_data* data) { + if (!data) return NULL; + return data->sf; +} /* ************************************************************************* */ @@ -192,19 +200,23 @@ void free_hca(hca_codec_data* data) { /* ignores beginning frames (~10 is not uncommon, Dragalia Lost vocal layers have lots) */ #define HCA_KEY_MAX_SKIP_BLANKS 1200 /* 5~15 should be enough, but almost silent or badly mastered files may need tweaks - * (ex. newer Tales of the Rays files clip a lot and need +6 as some keys give almost-ok results) */ -#define HCA_KEY_MIN_TEST_FRAMES 7 -#define HCA_KEY_MAX_TEST_FRAMES 12 + * (ex. newer Tales of the Rays files clip a lot) */ +#define HCA_KEY_MIN_TEST_FRAMES 3 //7 +#define HCA_KEY_MAX_TEST_FRAMES 7 //12 /* score of 10~30 isn't uncommon in a single frame, too many frames over that is unlikely */ #define HCA_KEY_MAX_FRAME_SCORE 150 #define HCA_KEY_MAX_TOTAL_SCORE (HCA_KEY_MAX_TEST_FRAMES * 50*HCA_KEY_SCORE_SCALE) /* Test a number of frames if key decrypts correctly. * Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better). */ -int test_hca_key(hca_codec_data* data, unsigned long long keycode) { +static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk, unsigned long long keycode) { size_t test_frames = 0, current_frame = 0, blank_frames = 0; - int total_score = 0, found_regular_frame = 0; - const unsigned int blockSize = data->info.blockSize; + int total_score = 0; + const unsigned int block_size = data->info.blockSize; + uint32_t offset = hk->start_offset; + + if (!offset) + offset = data->info.headerSize; /* Due to the potentially large number of keys this must be tuned for speed. * Buffered IO seems fast enough (not very different reading a large block once vs frame by frame). @@ -216,19 +228,25 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) { /* A final score of 0 (=silent) is only possible for short files with all blank frames */ while (test_frames < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) { - off_t offset = data->info.headerSize + current_frame * blockSize; int score; size_t bytes; /* read and test frame */ - bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile); - if (bytes != blockSize) { + bytes = read_streamfile(data->data_buffer, offset, block_size, data->sf); + if (bytes != block_size) { /* normally this shouldn't happen, but pre-fetch ACB stop with frames in half, so just keep score */ //total_score = -1; break; } - score = clHCA_TestBlock(data->handle, (void*)(data->data_buffer), blockSize); + score = clHCA_TestBlock(data->handle, data->data_buffer, block_size); + + /* get first non-blank frame */ + if (!hk->start_offset && score != 0) { + hk->start_offset = offset; + } + offset += bytes; + if (score < 0 || score > HCA_KEY_MAX_FRAME_SCORE) { total_score = -1; break; @@ -236,13 +254,13 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) { current_frame++; - /* ignore silent frames at the beginning, up to a point */ - if (score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS && !found_regular_frame) { + /* ignore silent frames at the beginning, up to a point (keep skipping as + * in rare cases there are one non-blank frame then a bunch, that skew results) */ + if (score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS /*&& !hk->start_offset*/) { blank_frames++; continue; } - found_regular_frame = 1; test_frames++; /* scale values to make scores of perfect frames more detectable */ @@ -254,7 +272,6 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) { total_score += score; - /* don't bother checking more frames, other keys will get better scores */ if (total_score > HCA_KEY_MAX_TOTAL_SCORE) break; @@ -270,15 +287,37 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) { return total_score; } +void test_hca_key(hca_codec_data* data, hca_keytest_t* hk) { + int score; + uint64_t key = hk->key; + uint16_t subkey = hk->subkey; + + //;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x\n", + // (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey); + + if (subkey) { + key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) ); + } + + score = test_hca_score(data, hk, (unsigned long long)key); + + //;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n", + // (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score); + + /* wrong key */ + if (score < 0) + return; + + //;VGM_LOG("HCA: ok key=%08x%08x, subkey=%04x, score=%i\n", + // (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score); + + /* update if something better is found */ + if (hk->best_score <= 0 || (score < hk->best_score && score > 0)) { + hk->best_score = score; + hk->best_key = key; + } +} + void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode) { clHCA_SetKey(data->handle, (unsigned long long)keycode); } - -clHCA_stInfo* hca_get_info(hca_codec_data* data) { - return &data->info; -} - -STREAMFILE* hca_get_streamfile(hca_codec_data* data) { - if (!data) return NULL; - return data->streamfile; -} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder_clhca.c b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder_clhca.c index 09294066c..4a9999792 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder_clhca.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder_clhca.c @@ -57,9 +57,9 @@ #define HCA_MAX_FRAME_SIZE 0xFFFF /* lib max */ #define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted with key */ -#define HCA_SUBFRAMES_PER_FRAME 8 +#define HCA_SUBFRAMES 8 #define HCA_SAMPLES_PER_SUBFRAME 128 /* also spectrum points/etc */ -#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES_PER_FRAME*HCA_SAMPLES_PER_SUBFRAME) +#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES*HCA_SAMPLES_PER_SUBFRAME) #define HCA_MDCT_BITS 7 /* (1<<7) = 128 */ #define HCA_MIN_CHANNELS 1 @@ -88,7 +88,7 @@ typedef struct stChannel { unsigned int coded_count; /* encoded scales/resolutions/coefs */ /* subframe state */ - unsigned char intensity[HCA_SUBFRAMES_PER_FRAME]; /* intensity indexes for joins stereo (value max: 15 / 4b) */ + unsigned char intensity[HCA_SUBFRAMES]; /* intensity indexes for joins stereo (value max: 15 / 4b) */ unsigned char scalefactors[HCA_SAMPLES_PER_SUBFRAME]; /* scale indexes (value max: 64 / 6b)*/ unsigned char resolution[HCA_SAMPLES_PER_SUBFRAME]; /* resolution indexes (value max: 15 / 4b) */ unsigned char noises[HCA_SAMPLES_PER_SUBFRAME]; /* indexes to coefs that need noise fill + coefs that don't (value max: 128 / 8b) */ @@ -96,13 +96,14 @@ typedef struct stChannel { unsigned int valid_count; /* resolutions with valid values saved in 'noises' */ float gain[HCA_SAMPLES_PER_SUBFRAME]; /* gain to apply to quantized spectral data */ - float spectra[HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */ + float spectra[HCA_SUBFRAMES][HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */ + float temp[HCA_SAMPLES_PER_SUBFRAME]; /* temp for DCT-IV */ float dct[HCA_SAMPLES_PER_SUBFRAME]; /* result of DCT-IV */ float imdct_previous[HCA_SAMPLES_PER_SUBFRAME]; /* IMDCT */ /* frame state */ - float wave[HCA_SUBFRAMES_PER_FRAME][HCA_SAMPLES_PER_SUBFRAME]; /* resulting samples */ + float wave[HCA_SUBFRAMES][HCA_SAMPLES_PER_SUBFRAME]; /* resulting samples */ } stChannel; typedef struct clHCA { @@ -333,7 +334,7 @@ void clHCA_ReadSamples16(clHCA* hca, signed short *samples) { unsigned int i, j, k; /* PCM output is generally unused, but lib functions seem to use SIMD for f32 to s32 + round to zero */ - for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { + for (i = 0; i < HCA_SUBFRAMES; i++) { for (j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) { for (k = 0; k < hca->channels; k++) { f = hca->channel[k].wave[i][j]; @@ -989,8 +990,12 @@ void clHCA_SetKey(clHCA* hca, unsigned long long keycode) { } } +static int clHCA_DecodeBlock_unpack(clHCA* hca, void *data, unsigned int size); +static void clHCA_DecodeBlock_transform(clHCA* hca); + + int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) { - const int frame_samples = HCA_SUBFRAMES_PER_FRAME * HCA_SAMPLES_PER_SUBFRAME; + const int frame_samples = HCA_SUBFRAMES * HCA_SAMPLES_PER_SUBFRAME; const float scale = 32768.0f; unsigned int i, ch, sf, s; int status; @@ -1014,7 +1019,7 @@ int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) { } /* return if decode fails (happens often with wrong keys due to bad bitstream values) */ - status = clHCA_DecodeBlock(hca, data, size); + status = clHCA_DecodeBlock_unpack(hca, data, size); if (status < 0) return -1; @@ -1042,8 +1047,9 @@ int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) { } /* check decode results as (rarely) bad keys may still get here */ + clHCA_DecodeBlock_transform(hca); for (ch = 0; ch < hca->channels; ch++) { - for (sf = 0; sf < HCA_SUBFRAMES_PER_FRAME; sf++) { + for (sf = 0; sf < HCA_SUBFRAMES; sf++) { for (s = 0; s < HCA_SAMPLES_PER_SUBFRAME; s++) { float fsample = hca->channel[ch].wave[sf][s]; @@ -1095,15 +1101,15 @@ void clHCA_DecodeReset(clHCA * hca) { stChannel* ch = &hca->channel[i]; /* most values get overwritten during decode */ - //memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME); + //memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES); //memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME); //memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME); //memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME); - //memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME); + //memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SUBFRAMES * HCA_SAMPLES_PER_SUBFRAME); //memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME); //memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME); memset(ch->imdct_previous, 0, sizeof(ch->imdct_previous[0]) * HCA_SAMPLES_PER_SUBFRAME); - //memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME); + //memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES * HCA_SUBFRAMES); } } @@ -1119,23 +1125,21 @@ static void calculate_resolution(stChannel* ch, unsigned int packed_noise_level, static void calculate_gain(stChannel* ch); -static void dequantize_coefficients(stChannel* ch, clData* br); +static void dequantize_coefficients(stChannel* ch, clData* br, int subframe); -static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p); +static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p, int subframe); static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_count, unsigned int bands_per_hfr_group, - unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version); + unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version, int subframe); static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned int base_band_count, unsigned int total_band_count); -static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count); +static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count, int subframe); static void imdct_transform(stChannel* ch, int subframe); -/* takes HCA data and decodes all of a frame's samples */ -//hcadecoder_decode_block -int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) { +static int clHCA_DecodeBlock_unpack(clHCA* hca, void *data, unsigned int size) { clData br; unsigned short sync; unsigned int subframe, ch; @@ -1180,19 +1184,29 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) { } /* lib seems to use a state value to skip parts (unpacking/subframe N/etc) as needed */ - for (subframe = 0; subframe < HCA_SUBFRAMES_PER_FRAME; subframe++) { + for (subframe = 0; subframe < HCA_SUBFRAMES; subframe++) { /* unpack channel data and get dequantized spectra */ for (ch = 0; ch < hca->channels; ch++){ - dequantize_coefficients(&hca->channel[ch], &br); + dequantize_coefficients(&hca->channel[ch], &br, subframe); } + /* original code transforms subframe here, but we have it for later */ + } + + return br.bit; /* numbers of read bits for validations */ +} + +static void clHCA_DecodeBlock_transform(clHCA* hca) { + unsigned int subframe, ch; + + for (subframe = 0; subframe < HCA_SUBFRAMES; subframe++) { /* restore missing bands from spectra */ for (ch = 0; ch < hca->channels; ch++) { - reconstruct_noise(&hca->channel[ch], hca->min_resolution, hca->ms_stereo, &hca->random); + reconstruct_noise(&hca->channel[ch], hca->min_resolution, hca->ms_stereo, &hca->random, subframe); reconstruct_high_frequency(&hca->channel[ch], hca->hfr_group_count, hca->bands_per_hfr_group, - hca->stereo_band_count, hca->base_band_count, hca->total_band_count, hca->version); + hca->stereo_band_count, hca->base_band_count, hca->total_band_count, hca->version, subframe); } /* restore missing joint stereo bands */ @@ -1200,7 +1214,7 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) { for (ch = 0; ch < hca->channels - 1; ch++) { apply_intensity_stereo(&hca->channel[ch], subframe, hca->base_band_count, hca->total_band_count); - apply_ms_stereo(&hca->channel[ch], hca->ms_stereo, hca->base_band_count, hca->total_band_count); + apply_ms_stereo(&hca->channel[ch], hca->ms_stereo, hca->base_band_count, hca->total_band_count, subframe); } } @@ -1209,9 +1223,27 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) { imdct_transform(&hca->channel[ch], subframe); } } +} - return br.bit; /* numbers of read bits for validations */ +/* takes HCA data and decodes all of a frame's samples */ +//hcadecoder_decode_block +int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) { + int res; + + /* Original HCA code doesn't separate unpack + transform (unpacks most data, + * reads a subframe's spectra, transforms that subframe. + * + * Unpacking first takes a bit more memory (1 spectra per subframe) but test keys faster + * (since unpack may fail with bad keys we can skip transform). For regular decoding, this + * way somehow is slightly faster? (~3-5%, extra compiler optimizations with reduced scope?) */ + + res = clHCA_DecodeBlock_unpack(hca, data, size); + if (res < 0) + return res; + clHCA_DecodeBlock_transform(hca); + + return res; } //-------------------------------------------------- @@ -1330,7 +1362,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co ch->intensity[0] = value; if (value < 15) { bitreader_skip(br, 4); - for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) { + for (i = 1; i < HCA_SUBFRAMES; i++) { ch->intensity[i] = bitreader_read(br, 4); } } @@ -1352,7 +1384,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co ch->intensity[0] = value; if (delta_bits == 3) { /* 3+1 = 4b */ /* fixed intensities */ - for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) { + for (i = 1; i < HCA_SUBFRAMES; i++) { ch->intensity[i] = bitreader_read(br, 4); } } @@ -1361,7 +1393,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co unsigned char bmax = (2 << delta_bits) - 1; unsigned char bits = delta_bits + 1; - for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) { + for (i = 1; i < HCA_SUBFRAMES; i++) { unsigned char delta = bitreader_read(br, bits); if (delta == bmax) { value = bitreader_read(br, 4); /* encoded */ @@ -1378,7 +1410,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co } else { bitreader_skip(br, 4); - for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { + for (i = 0; i < HCA_SUBFRAMES; i++) { ch->intensity[i] = 7; } } @@ -1498,7 +1530,7 @@ static const float hcatbdecoder_read_val_table[128] = { }; /* read spectral coefficients in the bitstream */ -static void dequantize_coefficients(stChannel* ch, clData* br) { +static void dequantize_coefficients(stChannel* ch, clData* br, int subframe) { int i; unsigned int cc_count = ch->coded_count; @@ -1524,11 +1556,11 @@ static void dequantize_coefficients(stChannel* ch, clData* br) { } /* dequantize coef with gain */ - ch->spectra[i] = ch->gain[i] * qc; + ch->spectra[subframe][i] = ch->gain[i] * qc; } /* clean rest of spectra */ - memset(&ch->spectra[cc_count], 0, sizeof(ch->spectra[0]) * (HCA_SAMPLES_PER_SUBFRAME - cc_count)); + memset(&ch->spectra[subframe][cc_count], 0, sizeof(ch->spectra[subframe][0]) * (HCA_SAMPLES_PER_SUBFRAME - cc_count)); } @@ -1560,7 +1592,7 @@ static const float* hcadecoder_scale_conversion_table = (const float*)hcadecoder /* recreate resolution 0 coefs (not encoded) with pseudo-random noise based on * other coefs/scales (probably similar to AAC's perceptual noise substitution) */ -static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p) { +static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p, int subframe) { if (min_resolution > 0) /* added in v3.0 */ return; if (ch->valid_count <= 0 || ch->noise_count <= 0) @@ -1587,7 +1619,8 @@ static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsign sf_valid = ch->scalefactors[valid_index]; sc_index = (sf_noise - sf_valid + 62) & ~((sf_noise - sf_valid + 62) >> 31); - ch->spectra[noise_index] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[valid_index]; + ch->spectra[subframe][noise_index] = + hcadecoder_scale_conversion_table[sc_index] * ch->spectra[subframe][valid_index]; } *random_p = random; /* lib saves this in the bitreader, maybe for simplified passing around */ @@ -1596,7 +1629,7 @@ static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsign /* recreate missing coefs in high bands based on lower bands (probably similar to AAC's spectral band replication) */ static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_count, unsigned int bands_per_hfr_group, - unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version) { + unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version, int subframe) { if (bands_per_hfr_group == 0) /* added in v2.0, skipped in v2.0 files with 0 bands too */ return; if (ch->type == STEREO_SECONDARY) @@ -1630,7 +1663,7 @@ static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_cou sc_index = hfr_scales[group] - ch->scalefactors[lowband] + 63; sc_index = sc_index & ~(sc_index >> 31); /* clamped in v3.0 lib (in theory 6b sf are 0..128) */ - ch->spectra[highband] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[lowband]; + ch->spectra[subframe][highband] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[subframe][lowband]; highband += 1; lowband -= lowband_sub; @@ -1638,7 +1671,7 @@ static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_cou } /* last spectrum coefficient is 0 (normally highband = 128, but perhaps could 'break' before) */ - ch->spectra[highband - 1] = 0.0f; + ch->spectra[subframe][highband - 1] = 0.0f; } } @@ -1661,8 +1694,8 @@ static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned in int band; float ratio_l = hcadecoder_intensity_ratio_table[ ch_pair[1].intensity[subframe] ]; float ratio_r = 2.0f - ratio_l; /* correct, though other decoders substract 2.0 (it does use 'fsubr 2.0' and such) */ - float* sp_l = ch_pair[0].spectra; - float* sp_r = ch_pair[1].spectra; + float* sp_l = &ch_pair[0].spectra[subframe][0]; + float* sp_r = &ch_pair[1].spectra[subframe][0]; for (band = base_band_count; band < total_band_count; band++) { float coef_l = sp_l[band] * ratio_l; @@ -1674,7 +1707,7 @@ static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned in } /* restore L/R bands based on mid channel + side differences */ -static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count) { +static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count, int subframe) { if (!ms_stereo) /* added in v3.0 */ return; if (ch_pair[0].type != STEREO_PRIMARY) @@ -1683,8 +1716,8 @@ static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned { int band; const float ratio = 0.70710676908493; /* 0x3F3504F3 */ - float* sp_l = ch_pair[0].spectra; - float* sp_r = ch_pair[1].spectra; + float* sp_l = &ch_pair[0].spectra[subframe][0]; + float* sp_r = &ch_pair[1].spectra[subframe][0]; for (band = base_band_count; band < total_band_count; band++) { float coef_l = (sp_l[band] + sp_r[band]) * ratio; @@ -1867,8 +1900,8 @@ static void imdct_transform(stChannel* ch, int subframe) { { unsigned int count1 = 1; unsigned int count2 = half; - float* temp1 = ch->spectra; - float* temp2 = ch->temp; + float* temp1 = &ch->spectra[subframe][0]; + float* temp2 = &ch->temp[0]; for (i = 0; i < mdct_bits; i++) { float* swap; @@ -1897,8 +1930,8 @@ static void imdct_transform(stChannel* ch, int subframe) { { unsigned int count1 = half; unsigned int count2 = 1; - float* temp1 = ch->temp; - float* temp2 = ch->spectra; + float* temp1 = &ch->temp[0]; + float* temp2 = &ch->spectra[subframe][0]; for (i = 0; i < mdct_bits; i++) { const float* sin_table = (const float*) sin_tables_hex[i];//todo cleanup @@ -1934,15 +1967,15 @@ static void imdct_transform(stChannel* ch, int subframe) { /* copy dct */ /* (with the above optimization spectra is already modified, so this is redundant) */ for (i = 0; i < size; i++) { - ch->dct[i] = ch->spectra[i]; + ch->dct[i] = ch->spectra[subframe][i]; } #endif } /* update output/imdct with overlapped window (lib fuses this with the above) */ { - const float* dct = ch->spectra; //ch->dct; - const float* prev = ch->imdct_previous; + const float* dct = &ch->spectra[subframe][0]; //ch->dct; + const float* prev = &ch->imdct_previous[0]; for (i = 0; i < half; i++) { ch->wave[subframe][i] = hcaimdct_window_float[i] * dct[i + half] + prev[i]; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index f3cb4508f..4a91fa664 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -87,8 +87,10 @@ int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_code //case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */ /* FSBs (with FMOD DLLs) don't seem to need it. Particularly a few games (all from Wayforward?) - * contain audible garbage at the beginning, but it's actually there in-game too */ - //case MPEG_FSB: data->skip_samples = 0; break; + * contain audible garbage at the beginning, but it's actually there in-game too. + * Games doing full loops also must not have delay (reuses mpeg state on loop) */ + case MPEG_FSB: + data->skip_samples = 0; break; case MPEG_XVAG: /* set in header and needed for gapless looping */ data->skip_samples = data->config.skip_samples; break; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index d73ae220e..f083a0ff1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -481,6 +481,8 @@ decode_fail: /* UTILS */ /*********/ +static void flush_mpeg(mpeg_codec_data* data, int is_loop); + void free_mpeg(mpeg_codec_data* data) { if (!data) return; @@ -513,7 +515,7 @@ void free_mpeg(mpeg_codec_data* data) { void reset_mpeg(mpeg_codec_data* data) { if (!data) return; - flush_mpeg(data); + flush_mpeg(data, 0); #if 0 /* flush_mpeg properly resets mpg123 with mpg123_open_feed, and @@ -550,7 +552,7 @@ void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) { else { int i; - flush_mpeg(data); + flush_mpeg(data, 1); /* restart from 0 and manually discard samples, since we don't really know the correct offset */ for (i = 0; i < data->streams_size; i++) { @@ -566,7 +568,7 @@ void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) { } /* resets mpg123 decoder and its internals without seeking, useful when a new MPEG substream starts */ -void flush_mpeg(mpeg_codec_data* data) { +static void flush_mpeg(mpeg_codec_data* data, int is_loop) { if (!data) return; @@ -578,7 +580,10 @@ void flush_mpeg(mpeg_codec_data* data) { int i; /* re-start from 0 */ for (i=0; i < data->streams_size; i++) { - mpg123_open_feed(data->streams[i]->m); + /* On loop FSB retains MDCT state so it mixes with next/loop frame (confirmed with recordings). + * This only matters on full loops and if there is no encoder delay (since loops use discard right now) */ + if (is_loop && data->custom && !(data->type == MPEG_FSB)) + mpg123_open_feed(data->streams[i]->m); data->streams[i]->bytes_in_buffer = 0; data->streams[i]->buffer_full = 0; data->streams[i]->buffer_used = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.c index 52d85cbf4..9aa6552bb 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.c @@ -206,7 +206,7 @@ static uint32_t read_ubits(uint8_t bits, uint32_t offset, uint8_t* buf) { shift = offset - 8 * (offset / 8); mask = (1 << bits) - 1; pos = offset / 8; - val = (buf[pos+0]) | (buf[pos+1]<<8) | (buf[pos+2]<<16) | (buf[pos+3]<<24); + val = ((uint32_t)buf[pos+0]) | ((uint32_t)buf[pos+1]<<8) | ((uint32_t)buf[pos+2]<<16) | ((uint32_t)buf[pos+3]<<24); return (val >> shift) & mask; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c index ef46dc6a3..1061055a3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c @@ -1037,15 +1037,15 @@ static uint16_t crc16(const uint8_t* data, int length) { /* ************************************************************************* */ static uint32_t get_u32be(const uint8_t* mem) { - return (mem[0] << 24) | (mem[1] << 16) | (mem[2] << 8) | mem[3]; + return ((uint32_t)mem[0] << 24) | ((uint32_t)mem[1] << 16) | ((uint32_t)mem[2] << 8) | (uint32_t)mem[3]; } static uint32_t get_u32le(const uint8_t* mem) { - return (mem[3] << 24) | (mem[2] << 16) | (mem[1] << 8) | mem[0]; + return ((uint32_t)mem[3] << 24) | ((uint32_t)mem[2] << 16) | ((uint32_t)mem[1] << 8) | (uint32_t)mem[0]; } static uint16_t get_u16le(const uint8_t* mem) { - return (mem[1] << 8) | mem[0]; + return ((uint16_t)mem[1] << 8) | (uint16_t)mem[0]; } static int init_header(tac_header_t* header, const uint8_t* buf) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c index cb43c11f1..7845291e5 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c @@ -162,73 +162,57 @@ static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, S static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { STREAMFILE* sf_setup = NULL; + /* get from artificial external file (used if compiled without codebooks) */ { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; + char setupname[0x20]; - /* read "(dir/).fvs_{setup_id}" */ - sf->get_name(sf,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id); - sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + snprintf(setupname, sizeof(setupname), ".fvs_%08x", setup_id); + sf_setup = open_streamfile_by_filename(sf, setupname); } + /* get codebook and copy to buffer */ if (sf_setup) { - /* file found, get contents into the buffer */ size_t bytes = sf_setup->get_size(sf_setup); if (bytes > bufsize) goto fail; if (read_streamfile(buf, 0, bytes, sf_setup) != bytes) goto fail; - sf_setup->close(sf_setup); + close_streamfile(sf_setup); return bytes; } fail: - if (sf_setup) sf_setup->close(sf_setup); + close_streamfile(sf_setup); return 0; } static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { STREAMFILE* sf_setup = NULL; + /* from to get from artificial external file (used if compiled without codebooks) */ { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char* path; + char setupname[0x20]; - /* read "(dir/).fvs" */ - sf->get_name(sf,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname); - sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + snprintf(setupname, sizeof(setupname), ".fvs"); + sf_setup = open_streamfile_by_filename(sf, setupname); } + /* find codebook in mini-header (format by bnnm, feel free to change) */ if (sf_setup) { - /* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */ int entries, i; uint32_t offset = 0, size = 0; - if (read_32bitBE(0x0, sf_setup) != 0x56465653) goto fail; /* "VFVS" */ - entries = read_32bitLE(0x08, sf_setup); /* 0x04=v0, 0x0c-0x20: reserved */ + if (!is_id32be(0x00, sf_setup, "VFVS")) + goto fail; + + entries = read_u32le(0x08, sf_setup); /* 0x04=v0, 0x0c-0x20: reserved */ if (entries <= 0) goto fail; - for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */ - if ((uint32_t)read_32bitLE(0x20 + i*0x10, sf_setup) == setup_id) { - offset = read_32bitLE(0x24 + i*0x10, sf_setup); - size = read_32bitLE(0x28 + i*0x10, sf_setup); + for (i = 0; i < entries; i++) { /* entry = id, offset, size, reserved */ + if (read_u32le(0x20 + i*0x10, sf_setup) == setup_id) { + offset = read_u32le(0x24 + i*0x10, sf_setup); + size = read_u32le(0x28 + i*0x10, sf_setup); break; } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c index 54d4a39f5..e21900751 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c @@ -1144,27 +1144,18 @@ static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_ } static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf) { - STREAMFILE* sfWvc = NULL; + STREAMFILE* sf_setup = NULL; size_t wvc_size = 0; + /* get from artificial external file (used if compiled without codebooks) */ { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; + char setupname[0x20]; - /* read "(dir/).wvc" */ - sf->get_name(sf,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; + snprintf(setupname, sizeof(setupname), ".wvc"); + sf_setup = open_streamfile_by_filename(sf, setupname); + if (!sf_setup) goto fail; - snprintf(setupname,PATH_LIMIT,"%s.wvc", pathname); - sfWvc = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!sfWvc) goto fail; - - wvc_size = sfWvc->get_size(sfWvc); + wvc_size = get_streamfile_size(sf_setup); } /* find codebook and copy to buffer */ @@ -1174,24 +1165,24 @@ static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STR int codebook_count; /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ - table_start = read_u32le(wvc_size - 4, sfWvc); /* last offset */ + table_start = read_u32le(wvc_size - 4, sf_setup); /* last offset */ codebook_count = ((wvc_size - table_start) / 4) - 1; if (table_start > wvc_size || codebook_id >= codebook_count) goto fail; - codebook_offset = read_u32le(table_start + codebook_id*4, sfWvc); - codebook_size = read_u32le(table_start + codebook_id*4 + 4, sfWvc) - codebook_offset; + codebook_offset = read_u32le(table_start + codebook_id*4, sf_setup); + codebook_size = read_u32le(table_start + codebook_id*4 + 4, sf_setup) - codebook_offset; if (codebook_size > bufsize) goto fail; - if (read_streamfile(buf, codebook_offset, codebook_size, sfWvc) != codebook_size) + if (read_streamfile(buf, codebook_offset, codebook_size, sf_setup) != codebook_size) goto fail; - sfWvc->close(sfWvc); + close_streamfile(sf_setup); return codebook_size; } fail: - if (sfWvc) sfWvc->close(sfWvc); + close_streamfile(sf_setup); return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c index 8f2665011..49aab74ab 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c @@ -1,130 +1,99 @@ #include "coding.h" #include "../util.h" -// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do -// XA coefs int math in different ways (see comments below), not 100% accurate. -// May be implemented like the SNES/SPC700 BRR. - /* XA ADPCM gain values */ -#if 0 -static const float K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 }; -static const float K1[4] = { 0.0, 0.0, -0.8125, -0.859375 }; +//#define XA_FLOAT 1 +#if XA_FLOAT +/* floats as defined by the spec, but PS1's SPU would use int math */ +static const float K0[4+12] = { 0.0, 0.9375, 1.796875, 1.53125 }; +static const float K1[4+12] = { 0.0, 0.0, -0.8125, -0.859375 }; +#else +/* K0/1 floats to int with N=6: K*2^6 = K*(1<<6) = K*64 (upper ranges are supposedly 0)*/ +static const int K0[4+12] = { 0, 60, 115, 98 }; +static const int K1[4+12] = { 0, 0, -52, -55 }; #endif -/* K0/1 floats to int, -K*2^10 = -K*(1<<10) = -K*1024 */ -static const int IK0[4] = { 0, -960, -1840, -1568 }; -static const int IK1[4] = { 0, 0, 832, 880 }; + +/* EA's extended XA (N=8), reverse engineered from SAT exes. Basically the same with minor + * diffs and extra steps probably for the SH2 CPU (only does 1/2/8 shifts) */ +static const int16_t EA_TABLE[16][2] = { + { 0, 0 }, + { 240, 0 }, + { 460, -208 }, + { 392, -220 }, + { 488, -240 }, + { 328, -208 }, + { 440, -168 }, + { 420, -188 }, + { 432, -176 }, + { 240, -16 }, + { 416, -192 }, + { 424, -160 }, + { 288, -8 }, + { 436, -188 }, + { 224, -1 }, + { 272, -16 }, +}; + /* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs. * The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new. * - * Decoding is defined in diagrams, roughly as: - * pcm = clamp( signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 ) - * - Range (12-range=shift) and filter index are renewed every ~28 samples. - * - nibble is expanded to a signed 16b sample, reimplemented as: - * short sample = ((nibble << 12) & 0xf000) >> shift - * or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N) - * - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding: - * (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N - * (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N - * sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N - * sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N - * etc - * (rounding differences should be inaudible, so public implementations may be approximations) - * - * Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly - * differently, maybe using one of the above methods in software/CPU, but in XA's case may be done - * like the SNES/SPC700 BRR, with specific per-filter ops. - * int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240 - * PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this. - * - * XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently - * are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode. - * Official "sound quality level" modes: - * - Level A: 37.8hz, 8-bit - * - Level B: 37.8hz, 4-bit - * - Level C: 18.9hz, 4-bit - * - * Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf - * BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples - * (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316 - */ + * See end for accuracy information and layout info. */ -/* data layout (mono): - * - CD-XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames - * (sectors handled externally, this decoder only sees N frames) - * - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with - * 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data") - * - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header) - * (repeats may be for error correction, though probably unused) - * header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1) - * - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc - * (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc) - * - * stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R - * - * example: - * subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c - * subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c - * subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d - * ... - * subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f - * - * 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples: - * subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c - * subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d - * ... - * subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f - */ +typedef struct { + uint8_t frame[0x80]; + int16_t* sbuf; + int channels; + int32_t hist1; + int32_t hist2; + int subframes; + int is_xa8; + int is_ea; +} xa_t; -void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8) { - uint8_t frame[0x80] = {0}; - off_t frame_offset; - int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0; - size_t bytes_per_frame, samples_per_frame; - int32_t hist1 = stream->adpcm_history1_32; - int32_t hist2 = stream->adpcm_history2_32; - int subframes = (is_xa8) ? 4 : 8; - - - /* external interleave (fixed size), mono/stereo */ - bytes_per_frame = 0x80; - samples_per_frame = 28*subframes / channelspacing; - frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; - - /* parse frame header */ - frame_offset = stream->offset + bytes_per_frame * frames_in; - read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - - VGM_ASSERT(get_u32be(frame+0x0) != get_u32be(frame+0x4) || get_u32be(frame+0x8) != get_u32be(frame+0xC), - "bad frames at %x\n", (uint32_t)frame_offset); +static void decode_xa_frame(xa_t* xa, int32_t first_sample, int32_t samples_to_do, int channel) { + int i,j, samples_done = 0, sample_count = 0; + int shift_max = xa->is_xa8 ? 8 : 12; + int shift_limit = xa->is_xa8 ? 8 : 9; /* from Nocash PSX docs (in 8-bit mode max range should be 8 though) */ /* decode subframes */ - for (i = 0; i < subframes / channelspacing; i++) { + for (i = 0; i < xa->subframes / xa->channels; i++) { + uint8_t sp; + int index, shift, sp_pos; +#ifdef XA_FLOAT + float coef1, coef2; +#else int32_t coef1, coef2; - uint8_t coef_index, shift_factor; +#endif /* parse current subframe (sound unit)'s header (sound parameters) */ - sp_pos = is_xa8 ? - i*channelspacing + channel: - 0x04 + i*channelspacing + channel; - coef_index = (frame[sp_pos] >> 4) & 0xf; - shift_factor = (frame[sp_pos] >> 0) & 0xf; + sp_pos = xa->is_xa8 ? + i*xa->channels + channel : + 0x04 + i*xa->channels + channel; + sp = xa->frame[sp_pos]; + index = (sp >> 4) & 0xf; + shift = (sp >> 0) & 0xf; /* mastered values like 0xFF exist [Micro Machines (CDi), demo and release] */ - VGM_ASSERT(coef_index > 4 || shift_factor > (is_xa8 ? 8 : 12), "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos); - if (coef_index > 4) - coef_index = 0; /* only 4 filters are used, rest is apparently 0 */ - if (shift_factor > (is_xa8 ? 8 : 12)) - shift_factor = (is_xa8 ? 8 : 9); /* supposedly, from Nocash PSX docs (in 8-bit mode max range should be 8 though) */ + VGM_ASSERT_ONCE(shift > shift_max, "XA: incorrect shift %x\n", sp); + if (shift > shift_max) + shift = shift_limit; - coef1 = IK0[coef_index]; - coef2 = IK1[coef_index]; + if (xa->is_ea) { + coef1 = EA_TABLE[index][0]; + coef2 = EA_TABLE[index][1]; + } + else { + VGM_ASSERT_ONCE(index > 4, "XA: incorrect coefs %x\n", sp); + coef1 = K0[index]; + coef2 = K1[index]; + } /* decode subframe nibbles */ for(j = 0; j < 28; j++) { int32_t sample; + uint8_t su; /* skip half decodes to make sure hist isn't touched (kinda hack-ish) */ if (!(sample_count >= first_sample && samples_done < samples_to_do)) { @@ -132,47 +101,97 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i continue; } - if (is_xa8) { - int su_pos = (channelspacing==1) ? + if (xa->is_xa8) { + int su_pos = (xa->channels==1) ? 0x10 + j*0x04 + i : /* mono */ 0x10 + j*0x04 + i*2 + channel; /* stereo */ - sample = frame[su_pos]; - sample = (int16_t)((sample << 8) & 0xff00) >> shift_factor; /* 16b sign extend + scale */ + su = xa->frame[su_pos]; + sample = (int16_t)((su << 8) & 0xff00) >> shift; /* 16b sign extend + scale */ } else { - uint8_t nibbles; - int su_pos = (channelspacing==1) ? + int su_pos = (xa->channels==1) ? 0x10 + j*0x04 + (i/2) : /* mono */ 0x10 + j*0x04 + i; /* stereo */ - int get_high_nibble = (channelspacing==1) ? + int get_high_nibble = (xa->channels==1) ? (i&1) : /* mono (even subframes = low, off subframes = high) */ (channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */ - nibbles = frame[su_pos]; - sample = get_high_nibble ? - (nibbles >> 4) & 0x0f : - (nibbles >> 0) & 0x0f; - sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ + su = xa->frame[su_pos]; + su = get_high_nibble ? + (su >> 4) & 0x0f : + (su >> 0) & 0x0f; + sample = (int16_t)((su << 12) & 0xf000) >> shift; /* 16b sign extend + scale */ } - sample = sample << 4; /* scale for current IK */ - sample = sample - ((coef1*hist1 + coef2*hist2) >> 10); +#if XA_FLOAT + sample = sample + (coef1 * xa->hist1 + coef2 * xa->hist2); +#else + if (xa->is_ea) /* sample << 8 actually but UB on negatives */ + sample = (sample * 256 + coef1 * xa->hist1 + coef2 * xa->hist2) >> 8; + else + sample = sample + ((coef1 * xa->hist1 + coef2 * xa->hist2 + 32) >> 6); +#endif - hist2 = hist1; - hist1 = sample; /* must go before clamp, somehow */ - sample = sample >> 4; - sample = clamp16(sample); + xa->hist2 = xa->hist1; + xa->hist1 = sample; + sample = clamp16(sample); /* don't clamp hist */ + if (xa->is_ea) + xa->hist1 = sample; /* do clamp hist */ - outbuf[samples_done * channelspacing] = sample; + xa->sbuf[samples_done * xa->channels] = sample; samples_done++; sample_count++; } } +} - stream->adpcm_history1_32 = hist1; - stream->adpcm_history2_32 = hist2; + +void decode_xa(VGMSTREAM* v, sample_t* outbuf, int32_t samples_to_do) { + uint32_t offset = v->ch[0].offset; /* L/R share offsets */ + STREAMFILE* sf = v->ch[0].streamfile; + + int ch; + int frames_in, bytes, samples_per_frame; + uint32_t frame_offset, bytes_per_frame; + int32_t first_sample = v->samples_into_block; + xa_t xa; + + xa.channels = v->channels > 1 ? 2 : 1; /* only stereo/mono modes */ + xa.is_xa8 = (v->coding_type == coding_XA8); + xa.is_ea = (v->coding_type == coding_XA_EA); + xa.subframes = (xa.is_xa8) ? 4 : 8; + + /* external interleave (fixed size), mono/stereo */ + bytes_per_frame = sizeof(xa.frame); + samples_per_frame = 28 * xa.subframes / v->channels; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + /* parse frame header */ + frame_offset = offset + bytes_per_frame * frames_in; + bytes = read_streamfile(xa.frame, frame_offset, bytes_per_frame, sf); + if (bytes != sizeof(xa.frame)) /* ignore EOF errors */ + memset(xa.frame + bytes, 0, bytes_per_frame - bytes); + + /* headers should repeat in pairs, except in EA's modified XA */ + VGM_ASSERT_ONCE(!xa.is_ea && + (get_u32be(xa.frame+0x0) != get_u32be(xa.frame+0x4) || get_u32be(xa.frame+0x8) != get_u32be(xa.frame+0xC)), + "bad frames at %x\n", frame_offset); + + for (ch = 0; ch < xa.channels; ch++) { + VGMSTREAMCHANNEL* stream = &v->ch[ch]; + + xa.sbuf = outbuf+ch; + xa.hist1 = stream->adpcm_history1_32; + xa.hist2 = stream->adpcm_history2_32; + + decode_xa_frame(&xa, first_sample, samples_to_do, ch); + + stream->adpcm_history1_32 = xa.hist1; + stream->adpcm_history2_32 = xa.hist2; + } } @@ -185,3 +204,85 @@ size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_fo return (bytes / 0x80) * (28*subframes / channels); } } + +/* + * Official XA decoding is defined in diagrams (not implementation), roughly as: + * pcm = signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 + * - K0 = 0.0, 0.9375, 1.796875, 1.53125 / K1 = 0.0, 0.0, -0.8125, -0.859375 + * - int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240 + * - Range (12-range=shift) and filter index are renewed every ~28 samples. + * - nibble is expanded to a signed 16b sample, reimplemented as: + * short sample = ((nibble << 12) & 0xf000) >> shift + * or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N) + * - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding: + * (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N + * (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N + * sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N + * sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N + * ... + * (rounding differences should be inaudible) + * + * There isn't an official implementation, but supposedly CD-ROM controller (which reads CD-XA) pushes + * audio data to the SPU directly (http://wiki.psxdev.ru/index.php/SPU), so probably the same as PS-ADPCM. + * Emus all seem to use approximations: + * - duckstation: N=6, "s + ((h1 * K0) + (h2 * K1) + 32) / 64"; no hist clamp; s clamp; + * - mednafen: N=6, "s + ((h1 * K0) >> 6) + ((h2 * K1) >> 6)"; hist / s clamp; + * - mame: N=6, "s + ((h1 * K0) + (h2 * K1) + 32) >> 6)"; no hist / s clamp; + * - peops: N=10, "s + ((h1 * K0) + (h2 * K1) + 32) >> 10)"; no hist / s clamp; + * - pcsc: N=10, "s + ((h1 * K0) + (h2 * K1)) >> 10)"; hist / s clamp; + * - cedimu: f32, "s + (h1 * k0 + h2 * k1)"; no hist / s clamp + * It's not clear from the diagrams if hist should be clamped (seemingly not), but reportedly not + * clamping improves output waveform spikes, while N=6 seems most common, and +32 + * (note <-0 / 64 != <0 >> 6) + * + * PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this. + * Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly + * differently too, maybe using one of the above methods in software/CPU, but in XA's case may + * be done like the SNES/SPC700 BRR, with specific per-filter ops rather than a formula. + * + * XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently + * are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode. + * Official "sound quality level" modes: + * - Level A: 37.8hz, 8-bit + * - Level B: 37.8hz, 4-bit + * - Level C: 18.9hz, 4-bit + * + * Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf + * BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples + * (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316 + * Note that this is just called "ADPCM" in the "CD-ROM XA" spec (rather than "XA ADPCM" being the actual name). + */ + +/* data layout (mono): + * - CD XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames + * (sectors are handled externally, this decoder only sees 0x80 frames) + * - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with + * 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data") + * - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header) + * (repeats may be for error correction, though probably unused) + * header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1) + * - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc + * (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc) + * + * stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R + * + * example: + * 2A29282A 2A29282A 29292929 29292929 + * 0D0D20FB 0D8B011C 0EFA103B 5FEC2F42 + * ... + * + * subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c + * subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c + * subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d + * ... + * subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f + * + * 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples: + * subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c + * subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d + * ... + * subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f + * + * XA-EA found in EA SAT games set subframes header like: 0..3 null + 0..3 ok + 4..7 ok + 4..7 null + * so decoder only reads those. + */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c index 4ba8e91c1..ea66088b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c @@ -12,6 +12,10 @@ static const int scale_step_adpcmb[16] = { 57, 57, 57, 57, 77, 102, 128, 153, }; +static const int scale_step_capcom[8] = { + 58982, 58982, 58982, 58982, 78643, 104858, 131072, 157286, +}; + /* look-up for 'mul' IMA's sign*((code&7) * 2 + 1) for every code */ static const int scale_delta[16] = { 1, 3, 5, 7, 9, 11, 13, 15, @@ -59,6 +63,31 @@ static void yamaha_aica_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offs *hist1 = sample; } +/* Capcom's version of Yamaha expand */ +static void yamaha_capcom_expand_nibble(uint8_t byte, int shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) { + int code, ucode, delta, sample; + const int scale = 0x200; /* ADPCM state var, but seemingly fixed */ + + code = (byte >> shift) & 0xf; + ucode = code & 0x7; + delta = (ucode * (*step_size)) >> 2; /* custom (SH2 CPU can't do odd shifts in one op, it seems) */ + if (code & 8) + delta = -delta; + sample = *hist1 + delta; + + sample = (short)sample; /* clamp not done, but output is always low-ish */ + + *step_size = ((*step_size) * scale_step_capcom[ucode]) >> 16; + if (*step_size < 0x80) *step_size = 0x80; /* unlike usual 0x7f */ + else if (*step_size > 0x6000) *step_size = 0x6000; + + *hist1 = sample; + + /* OG code adds out sample, but seems to be always 0 (used for mono downmix?) */ + sample = ((scale * sample) >> 8) /*+ *out_sample */; + *out_sample = sample; +} + /* info about Yamaha ADPCM as created by official yadpcm.acm (in 'Yamaha-ADPCM-ACM-Driver-100-j') * - possibly RIFF codec 0x20 @@ -107,6 +136,37 @@ void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin stream->adpcm_step_index = step_size; } +/* Capcom/Saturn Yamaha ADPCM, reverse engineered from the exe (codec has no apparent name so CP_YM = Capcom Yamaha) */ +void decode_cp_ym(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { + int i, sample_count = 0; + int16_t out_sample; + int32_t hist1 = stream->adpcm_history1_16; + int step_size = stream->adpcm_step_index; + + /* no header (external setup), pre-clamp for wrong values */ + if (step_size < 0x80) step_size = 0x80; + if (step_size > 0x6000) step_size = 0x6000; + + for (i = first_sample; i < first_sample + samples_to_do; i++) { + uint8_t byte; + uint32_t offset = is_stereo ? + stream->offset + i : /* stereo: one nibble per channel */ + stream->offset + i/2; /* mono: consecutive nibbles */ + int shift = is_stereo ? + (!(channel&1) ? 0:4) : /* even = low/L, odd = high/R */ + (!(i&1) ? 0:4); /* low nibble first */ + + byte = read_u8(offset, stream->streamfile); + yamaha_capcom_expand_nibble(byte, shift, &hist1, &step_size, &out_sample); + outbuf[sample_count] = out_sample; + sample_count += channelspacing; + } + + stream->adpcm_history1_16 = hist1; + stream->adpcm_step_index = step_size; +} + + /* tri-Ace Aska ADPCM, Yamaha ADPCM-B with headered frames (reversed from Android SO's .so) * implements table with if-else/switchs too but that's too goofy */ void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c index 70b23b924..37672c3a1 100644 --- a/Frameworks/vgmstream/vgmstream/src/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -442,6 +442,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { return 0; /* variable (block-controlled) */ case coding_XA: + case coding_XA_EA: return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */ case coding_XA8: return 28*4 / vgmstream->channels; /* 4 subframes per frame, mono/stereo */ @@ -471,6 +472,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ return vgmstream->ws_output_size; case coding_AICA: + case coding_CP_YM: return 1; case coding_AICA_int: return 2; @@ -659,6 +661,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { return 0x00; /* variable (block-controlled) */ case coding_XA: + case coding_XA_EA: case coding_XA8: return 0x80; case coding_PSX: @@ -690,6 +693,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { return vgmstream->current_block_size; case coding_AICA: case coding_AICA_int: + case coding_CP_YM: return 0x01; case coding_ASKA: return vgmstream->frame_size; @@ -1005,12 +1009,9 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ } break; case coding_XA: + case coding_XA_EA: case coding_XA8: { - int is_xa8 = (vgmstream->coding_type == coding_XA8); - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_xa(&vgmstream->ch[ch], buffer+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_xa8); - } + decode_xa(vgmstream, buffer, samples_to_do); break; } case coding_EA_XA: @@ -1331,6 +1332,15 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ } break; } + case coding_CP_YM: { + int is_stereo = (vgmstream->channels > 1); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cp_ym(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + is_stereo); + } + break; + } case coding_ASKA: for (ch = 0; ch < vgmstream->channels; ch++) { decode_aska(&vgmstream->ch[ch], buffer+ch, diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index ad468ca84..597b6dfb5 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -58,9 +58,7 @@ static const char* extension_list[] = { //"aif", //common "aif-Loop", "aifc", //common? - "aifcl", //fake extension for .aif??? //"aiff", //common - "aiffl", //fake extension for .aif??? "aix", "akb", "al", @@ -85,6 +83,7 @@ static const char* extension_list[] = { "aud", "audio", //txth/reserved [Grimm Echoes (Android)] "aus", + "awa", //txth/reserved [Missing Parts Side A (PS2)] "awb", "awc", @@ -256,6 +255,7 @@ static const char* extension_list[] = { "kovs", //fake extension/header id for .kvs "kno", "kns", + "koe", "kraw", "ktac", "ktsl2asbin", @@ -335,6 +335,7 @@ static const char* extension_list[] = { "msa", "msb", "msd", + "mse", "msf", "mss", "msv", @@ -360,6 +361,7 @@ static const char* extension_list[] = { "nop", "nps", "npsf", //fake extension/header id for .nps (in bigfiles) + "nsa", "nsopus", "nub", "nub2", @@ -370,6 +372,7 @@ static const char* extension_list[] = { "nxa", //"ogg", //common + "ogg_", "ogl", "ogv", "oma", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) @@ -431,6 +434,7 @@ static const char* extension_list[] = { "sab", "sad", "saf", + "sam", //txth/reserved [Lost Kingdoms 2 (GC)] "sap", "sb0", "sb1", @@ -532,6 +536,7 @@ static const char* extension_list[] = { "txtp", "tydsp", + "u0", "ue4opus", "ulw", "um3", @@ -551,6 +556,7 @@ static const char* extension_list[] = { "vbx", //txth/reserved [THE Taxi 2 (PS2)] "vds", "vdm", + "vgi", //txth/reserved [Time Crisis II (PS2)] "vgm", //txth/reserved [Maximo (PS2)] "vgs", "vgv", @@ -567,6 +573,7 @@ static const char* extension_list[] = { "vsv", "vxn", + "w", "waa", "wac", "wad", @@ -586,6 +593,7 @@ static const char* extension_list[] = { "wd", "wem", "wii", + "wic", //txth/reserved [Road Rash (SAT)-videos] "wip", //txth/reserved [Colin McRae DiRT (PC)] "wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)] "wmus", @@ -593,6 +601,7 @@ static const char* extension_list[] = { "wpd", "wsd", "wsi", + "wst", //txth/reserved [3jigen Shoujo o Hogo Shimashita (PC)] "wua", "wv2", "wv6", @@ -735,6 +744,7 @@ static const coding_info coding_info_list[] = { {coding_XA, "CD-ROM XA 4-bit ADPCM"}, {coding_XA8, "CD-ROM XA 8-bit ADPCM"}, + {coding_XA_EA, "Electronic Arts XA 4-bit ADPCM"}, {coding_PSX, "Playstation 4-bit ADPCM"}, {coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"}, {coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"}, @@ -785,6 +795,7 @@ static const coding_info coding_info_list[] = { {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, {coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"}, + {coding_CP_YM, "Capcom Yamaha 4-bit ADPCM"}, {coding_ASKA, "tri-Ace Aska 4-bit ADPCM"}, {coding_NXAP, "Nex NXAP 4-bit ADPCM"}, {coding_TGC, "Tiger Game.com 4-bit ADPCM"}, @@ -1118,7 +1129,8 @@ static const meta_info meta_info_list[] = { {meta_XBOX_HLWAV, "Half-Life 2 .WAV header"}, {meta_MYSPD, "U-Sing .MYSPD header"}, {meta_HIS, "Her Interactive HIS header"}, - {meta_PS2_AST, "KOEI AST header"}, + {meta_AST_MV, "MicroVision AST header"}, + {meta_AST_MMV, "Marvelous AST header"}, {meta_CAPDSP, "Capcom DSP header"}, {meta_DMSG, "Microsoft RIFF DMSG header"}, {meta_PONA_3DO, "Policenauts BGM header"}, @@ -1146,7 +1158,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_XIII, "XIII dsp header"}, {meta_DSP_CABELAS, "Cabelas games .DSP header"}, {meta_PS2_ADM, "Dragon Quest V .ADM raw header"}, - {meta_PS2_LPCM, "LPCM header"}, + {meta_LPCM_SHADE, "Shade LPCM header"}, {meta_PS2_VMS, "VMS Header"}, {meta_XAU, "XPEC XAU header"}, {meta_GH3_BAR, "Guitar Hero III Mobile .bar"}, diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c index 3c7b25db0..0fe8352f6 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c @@ -1,6 +1,7 @@ #include "layout.h" #include "../coding/coding.h" #include "../vgmstream.h" +#include "../util/endianness.h" /* set up for the block at the given offset */ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) { @@ -8,7 +9,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) { int i; uint32_t block_id; size_t block_size = 0, block_header = 0, audio_size = 0; - int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; + read_s32_t read_s32 = vgmstream->codec_endian ? read_s32be : read_s32le; /* EOF reads: signal we have nothing and let the layout fail */ @@ -19,27 +20,29 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) { return; } - block_id = read_32bitBE(block_offset + 0x00, sf); + block_id = read_u32be(block_offset + 0x00, sf); /* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */ - if (guess_endianness32bit(block_offset + 0x04, sf)) - block_size = read_32bitBE(block_offset + 0x04, sf); + if (guess_endian32(block_offset + 0x04, sf)) + block_size = read_u32be(block_offset + 0x04, sf); else - block_size = read_32bitLE(block_offset + 0x04, sf); + block_size = read_u32le(block_offset + 0x04, sf); block_header = 0; - if (block_id == 0x31534E68 || block_id == 0x53454144) { /* "1SNh" "SEAD" audio header */ - int is_sead = (block_id == 0x53454144); - int is_eacs = read_32bitBE(block_offset + 0x08, sf) == 0x45414353; - int is_zero = read_32bitBE(block_offset + 0x08, sf) == 0x00; + if (block_id == get_id32be("1SNh") || block_id == get_id32be("SEAD")) { /* audio header */ + int is_sead = (block_id == get_id32be("SEAD")); + int is_eacs = is_id32be(block_offset + 0x08, sf, "EACS"); + int is_zero = read_u32be(block_offset + 0x08, sf) == 0x00; block_header = (is_eacs || is_zero) ? 0x28 : (is_sead ? 0x14 : 0x2c); if (block_header >= block_size) /* sometimes has audio data after header */ block_header = 0; - } else if (block_id == 0x31534E64 || block_id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ + } + else if (block_id == get_id32be("1SNd") || block_id == get_id32be("SNDC")) { block_header = 0x08; - } else if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == 0x31534E65) { /* EOF or "1SNe" */ + } + else if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == get_id32be("1SNe")) { /* EOF */ vgmstream->current_block_samples = -1; return; } @@ -72,20 +75,25 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) { break; case coding_PSX: + if (vgmstream->codec_config == 1) {/* extra field */ + block_header += 0x04; + audio_size -= 0x04; + } + vgmstream->current_block_samples = ps_bytes_to_samples(audio_size, vgmstream->channels); - for (i=0;ichannels;i++) { + for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = block_offset + block_header + i*(audio_size/vgmstream->channels); } break; case coding_DVI_IMA: if (vgmstream->codec_config == 1) { /* ADPCM hist */ - vgmstream->current_block_samples = read_32bit(block_offset + block_header, sf); + vgmstream->current_block_samples = read_s32(block_offset + block_header, sf); for(i = 0; i < vgmstream->channels; i++) { off_t adpcm_offset = block_offset + block_header + 0x04; - vgmstream->ch[i].adpcm_step_index = read_32bit(adpcm_offset + i*0x04 + 0x00*vgmstream->channels, sf); - vgmstream->ch[i].adpcm_history1_32 = read_32bit(adpcm_offset + i*0x04 + 0x04*vgmstream->channels, sf); + vgmstream->ch[i].adpcm_step_index = read_s32(adpcm_offset + i*0x04 + 0x00*vgmstream->channels, sf); + vgmstream->ch[i].adpcm_history1_32 = read_s32(adpcm_offset + i*0x04 + 0x04*vgmstream->channels, sf); vgmstream->ch[i].offset = adpcm_offset + 0x08*vgmstream->channels; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index 8cded1499..a21263658 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -44,36 +44,9 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { block_samples = 0; /* layout ignores this */ } -#ifdef VGM_USE_MPEG - /* "SCHl" start block, when decoding multi files pasted together */ - if (block_id == 0x5343486C) { - switch(vgmstream->coding_type) { - case coding_MPEG_custom: - case coding_MPEG_layer1: - case coding_MPEG_layer2: - case coding_MPEG_layer3: - case coding_MPEG_ealayer3: - /* need to reset MPEG decoder to reset discards and trailing samples in the buffers */ - flush_mpeg(vgmstream->codec_data); - break; - default: - break; - } - } -#endif - /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ - if (block_id == 0x00000000) - block_size = 0x04; - - /* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */ - if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) { - block_size = 0x04; - block_samples = 0; - } - - /* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */ - if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) { - block_size += 0x04 - ((block_offset + block_size) % 0x04); + if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == 0x5343456C) { /* EOF */ + vgmstream->current_block_samples = -1; + return; } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ads.c b/Frameworks/vgmstream/vgmstream/src/meta/ads.c index e4444b1e4..cfed961e8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ads.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ads.c @@ -14,6 +14,9 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf,"SShd")) + goto fail; + /* .ads: actual extension * .ss2: demuxed videos (fake?) * .pcm: Taisho Mononoke Ibunroku (PS2) @@ -23,20 +26,20 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) { if (!check_extensions(sf, "ads,ss2,pcm,adx,,800")) goto fail; - if (!is_id32be(0x00,sf,"SShd") && - !is_id32be(0x20,sf,"SSbd")) - goto fail; - if (read_32bitLE(0x04,sf) != 0x18 && /* standard header size */ - read_32bitLE(0x04,sf) != 0x20) /* True Fortune (PS2) */ + if (read_u32le(0x04,sf) != 0x18 && /* standard header size */ + read_u32le(0x04,sf) != 0x20 && /* True Fortune (PS2) */ + read_u32le(0x04,sf) != get_streamfile_size(sf) - 0x08) /* Katamari Damacy videos */ goto fail; + if (!is_id32be(0x20,sf,"SSbd")) + goto fail; /* base values (a bit unorderly since devs hack ADS too much and detection is messy) */ { - codec = read_32bitLE(0x08,sf); - sample_rate = read_32bitLE(0x0C,sf); - channels = read_32bitLE(0x10,sf); /* up to 4 [Eve of Extinction (PS2)] */ - interleave = read_32bitLE(0x14,sf); /* set even when mono */ + codec = read_u32le(0x08,sf); + sample_rate = read_s32le(0x0C,sf); + channels = read_s32le(0x10,sf); /* up to 4 [Eve of Extinction (PS2)] */ + interleave = read_s32le(0x14,sf); /* set even when mono */ switch(codec) { @@ -61,7 +64,7 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) { case 0x00: /* PCM16BE from official docs, probably never used */ default: - VGM_LOG("ADS: unknown codec\n"); + vgm_logi("ADS: unknown codec\n"); goto fail; } } @@ -127,8 +130,8 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) { { uint32_t loop_start, loop_end; - loop_start = read_32bitLE(0x18,sf); - loop_end = read_32bitLE(0x1C,sf); + loop_start = read_u32le(0x18,sf); + loop_end = read_u32le(0x1C,sf); loop_flag = 0; @@ -144,7 +147,7 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) { loop_start_offset = loop_start * 0x10; ignore_silent_frame_capcom = 1; } - else if (read_32bitBE(0x28,sf) == 0x50414421) { /* "PAD!" padding until 0x800 */ + else if (is_id32be(0x28,sf, "PAD!")) { /* padding until 0x800 */ /* Super Galdelic Hour: loop_start is PCM bytes */ loop_flag = 1; loop_start_sample = loop_start / 2 / channels; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 418bce144..d5c83e162 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -80,14 +80,14 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) { /* encryption */ if (version == 0x0408) { - if (find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) { + if (!find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) { vgm_logi("ADX: decryption keystring not found\n"); } coding_type = coding_CRI_ADX_enc_8; version = 0x0400; } else if (version == 0x0409) { - if (find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) { + if (!find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) { vgm_logi("ADX: decryption keycode not found\n"); } coding_type = coding_CRI_ADX_enc_9; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aif_asobo.c b/Frameworks/vgmstream/vgmstream/src/meta/aif_asobo.c index 5a551727b..c98333fd4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aif_asobo.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aif_asobo.c @@ -6,23 +6,24 @@ VGMSTREAM* init_vgmstream_aif_asobo(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset; size_t data_size; - int loop_flag, channel_count; + int loop_flag, channels, sample_rate; /* checks */ - /* aif: standard, .laif/aiffl: for plugins */ - if ( !check_extensions(sf,"aif,laif,aiffl") ) + if (read_u16le(0x00,sf) != 0x69) /* fmt chunk with Xbox codec */ goto fail; - if ((uint16_t)read_16bitLE(0x00,sf) != 0x69) /* Xbox codec */ + /* aif: standard, .laif: for plugins */ + if ( !check_extensions(sf,"aif,laif") ) goto fail; - channel_count = read_16bitLE(0x02,sf); /* assumed, only stereo is known */ - if (channel_count != 2) goto fail; + channels = read_u16le(0x02,sf); /* assumed, only stereo is known */ + if (channels != 2) goto fail; - /* 0x08: ? */ - if ((uint16_t)read_16bitLE(0x0c,sf) != 0x24*channel_count) /* Xbox block */ + sample_rate = read_u32le(0x04,sf); + /* 0x08: bitrate */ + if (read_u16le(0x0c,sf) != 0x24 * channels) /* Xbox block */ goto fail; - if ((uint16_t)read_16bitLE(0x0e,sf) != 0x04) /* Xbox bps */ + if (read_u16le(0x0e,sf) != 0x04) /* Xbox bps */ goto fail; loop_flag = 0; @@ -31,17 +32,17 @@ VGMSTREAM* init_vgmstream_aif_asobo(STREAMFILE* sf) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_AIF_ASOBO; - vgmstream->sample_rate = read_32bitLE(0x04,sf); - vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size,channel_count); + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channels); vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_none; - if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c index 492203f92..dcfb02980 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c @@ -81,9 +81,11 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf, "FORM")) + goto fail; + /* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC - * .laif/laifc/laiff: for plugins - * .aifcl/aiffl: for plugins? + * .laif/laiff/laifc: for plugins * .cbd2: M2 games * .bgm: Super Street Fighter II Turbo (3DO) * .acm: Crusader - No Remorse (SAT) @@ -91,15 +93,17 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { * .ai: Dragon Force (SAT) * (extensionless: Doom (3DO) * .fda: Homeworld 2 (PC) - * .n64: Turok (N64) src */ + * .n64: Turok (N64) src + * .pcm: Road Rash (SAT) + */ if (check_extensions(sf, "aif,laif,")) { is_aifc_ext = 1; is_aiff_ext = 1; } - else if (check_extensions(sf, "aifc,laifc,aifcl,afc,cbd2,bgm,fda,n64")) { + else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64")) { is_aifc_ext = 1; } - else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,aiffl")) { + else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,pcm")) { is_aiff_ext = 1; } else { @@ -107,17 +111,16 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { } file_size = get_streamfile_size(sf); - if (read_u32be(0x00,sf) != 0x464F524D && /* "FORM" */ - read_u32be(0x04,sf)+0x08 != file_size) + if (read_u32be(0x04,sf) + 0x08 != file_size) goto fail; /* AIFF originally allowed only PCM (non-compressed) audio, so newer AIFC was added, * though some AIFF with other codecs exist */ - if (read_u32be(0x08,sf) == 0x41494643) { /* "AIFC" */ + if (is_id32be(0x08,sf, "AIFC")) { if (!is_aifc_ext) goto fail; is_aifc = 1; } - else if (read_u32be(0x08,sf) == 0x41494646) { /* "AIFF" */ + else if (is_id32be(0x08,sf, "AIFF")) { if (!is_aiff_ext) goto fail; is_aiff = 1; } @@ -160,9 +163,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { if (comm_found) goto fail; comm_found = 1; - channels = read_u16be(offset + 0x00,sf); - if (channels <= 0) goto fail; - + channels = read_u16be(offset + 0x00,sf); sample_count = read_u32be(offset + 0x02,sf); /* sample_frames in theory, depends on codec */ sample_size = read_u16be(offset + 0x06,sf); sample_rate = read_f80be(offset + 0x08,sf); @@ -233,7 +234,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { coding_type = coding_PCM16BE; interleave = 2; break; - case 4: /* Crusader: No Remorse (SAT), Road Rash (3DO) */ + case 4: /* Crusader: No Remorse (SAT), Road Rash (3DO/SAT) */ coding_type = coding_XA; break; default: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ast_mmv.c b/Frameworks/vgmstream/vgmstream/src/meta/ast_mmv.c new file mode 100644 index 000000000..feed9e692 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ast_mmv.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* AST - from Marvelous(?) games [Katekyou Hitman Reborn! Dream Hyper Battle! (PS2), Binchou-tan: Shiawasegoyomi (PS2)] */ +VGMSTREAM* init_vgmstream_ast_mmv(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, data_size; + int loop_flag, channels, sample_rate, interleave; + + /* checks */ + if (!is_id32be(0x00,sf, "AST\0")) + goto fail; + + /* .ast: from executables (usually found in bigfiles) */ + if (!check_extensions(sf,"ast")) + goto fail; + + data_size = read_u32le(0x04, sf); + if (data_size != get_streamfile_size(sf)) + goto fail; + + sample_rate = read_s32le(0x08,sf); + channels = read_32bitLE(0x0C,sf); + interleave = read_u32le(0x10,sf); + /* 0x14: number of blocks */ + /* 0x18: ? (not fully related to size/time) */ + /* 0x1c: f32 time */ + loop_flag = 0; + start_offset = 0x100; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_AST_MMV; + + vgmstream->num_samples = ps_bytes_to_samples(data_size - start_offset, channels); + vgmstream->sample_rate = sample_rate; + vgmstream->interleave_block_size = interleave; + + vgmstream->layout_type = layout_interleave; + vgmstream->coding_type = coding_PSX; + + read_string(vgmstream->stream_name,0x20, 0x20,sf); + + 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/ast_mv.c b/Frameworks/vgmstream/vgmstream/src/meta/ast_mv.c new file mode 100644 index 000000000..e038f1990 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ast_mv.c @@ -0,0 +1,53 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* AST - from MicroVision lib games [P.T.O. IV (PS2), Naval Ops: Warship Gunner (PS2)] */ +VGMSTREAM* init_vgmstream_ast_mv(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, data_size, check; + int loop_flag, channels, interleave, sample_rate; + + /* checks */ + if (!is_id32be(0x00,sf, "AST\0")) + goto fail; + + if (!check_extensions(sf,"ast")) + goto fail; + + channels = 2; + sample_rate = read_s32le(0x04, sf); + interleave = read_u32le(0x08,sf); + data_size = read_u32le(0x0c,sf); /* may have garbage */ + check = read_u32be(0x10, sf); + /* rest: null/garbage */ + + loop_flag = 0; + start_offset = 0x800; + + /* there is a variation in .ikm (Zwei), with loops and different start offset */ + if (check != 0x20002000 && /* NO:WG (garbage up to start) */ + check != 0x00000000) /* PTO4 */ + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(data_size - start_offset, channels); + vgmstream->interleave_block_size = interleave; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = meta_AST_MV; + + 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/baf.c b/Frameworks/vgmstream/vgmstream/src/meta/baf.c index 6798fabc6..a6409fac8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/baf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/baf.c @@ -1,35 +1,34 @@ #include "meta.h" #include "../coding/coding.h" - /* .BAF - Bizarre Creations bank file [Blur (PS3), Project Gotham Racing 4 (X360), Geometry Wars (PC)] */ -VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_baf(STREAMFILE *sf) { VGMSTREAM * vgmstream = NULL; off_t start_offset, header_offset, name_offset; size_t stream_size; - int loop_flag, channel_count, sample_rate, version, codec, tracks; - int total_subsongs, target_subsong = streamFile->stream_index; - int32_t (*read_32bit)(off_t,STREAMFILE*); - + uint32_t channel_count, sample_rate, num_samples, version, codec, tracks; + int loop_flag, total_subsongs, target_subsong = sf->stream_index; + uint32_t (*read_u32)(off_t,STREAMFILE*); /* checks */ - if (!check_extensions(streamFile, "baf")) + if (!is_id32be(0x00, sf, "BANK")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x42414E4B) /* "BANK" */ + + if (!check_extensions(sf, "baf")) goto fail; /* use BANK size to check endianness */ - if (guess_endianness32bit(0x04,streamFile)) { - read_32bit = read_32bitBE; + if (guess_endianness32bit(0x04,sf)) { + read_u32 = read_u32be; } else { - read_32bit = read_32bitLE; + read_u32 = read_u32le; } /* 0x04: bank size */ - version = read_32bit(0x08,streamFile); + version = read_u32(0x08,sf); if (version != 0x03 && version != 0x04 && version != 0x05) goto fail; - total_subsongs = read_32bit(0x0c,streamFile); + total_subsongs = read_u32(0x0c,sf); if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* - in v3 */ @@ -43,83 +42,96 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { /* find target WAVE chunk */ { int i; - off_t offset = read_32bit(0x04, streamFile); + off_t offset = read_u32(0x04, sf); for (i = 0; i < total_subsongs; i++) { if (i+1 == target_subsong) break; - offset += read_32bit(offset+0x04, streamFile); /* WAVE size, variable per codec */ + offset += read_u32(offset+0x04, sf); /* WAVE size, variable per codec */ /* skip companion "CUE " (found in 007: Blood Stone, contains segment cues) */ - if (read_32bitBE(offset+0x00, streamFile) == 0x43554520) { - offset += read_32bit(offset+0x04, streamFile); /* CUE size */ + if (is_id32be(offset+0x00, sf, "CUE ")) { + offset += read_u32(offset+0x04, sf); /* CUE size */ } } header_offset = offset; } /* parse header */ - if (read_32bitBE(header_offset+0x00, streamFile) != 0x57415645) /* "WAVE" */ + if (!is_id32be(header_offset+0x00, sf, "WAVE")) goto fail; - codec = read_32bit(header_offset+0x08, streamFile); + codec = read_u32(header_offset+0x08, sf); name_offset = header_offset + 0x0c; - start_offset = read_32bit(header_offset+0x2c, streamFile); - stream_size = read_32bit(header_offset+0x30, streamFile); + start_offset = read_u32(header_offset+0x2c, sf); + stream_size = read_u32(header_offset+0x30, sf); tracks = 0; switch(codec) { - case 0x03: + case 0x03: /* PCM16LE */ switch(version) { case 0x03: /* Geometry Wars (PC) */ - sample_rate = read_32bit(header_offset+0x38, streamFile); - channel_count = read_32bit(header_offset+0x40, streamFile); + sample_rate = read_u32(header_offset+0x38, sf); + channel_count = read_u32(header_offset+0x40, sf); /* no actual flag, just loop +15sec songs */ loop_flag = (pcm_bytes_to_samples(stream_size, channel_count, 16) > 15*sample_rate); break; case 0x04: /* Project Gotham Racing 4 (X360) */ - sample_rate = read_32bit(header_offset+0x3c, streamFile); - channel_count = read_32bit(header_offset+0x44, streamFile); - loop_flag = read_8bit(header_offset+0x4b, streamFile); + sample_rate = read_u32(header_offset+0x3c, sf); + channel_count = read_u32(header_offset+0x44, sf); + loop_flag = read_u8(header_offset+0x4b, sf); break; default: goto fail; } + + num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); break; - case 0x07: - switch(version) { + case 0x07: /* PSX ADPCM (0x21 frame size) */ + if (version == 0x04 && read_u32(header_offset + 0x3c, sf) != 0) { + /* Blur (Prototype) (PS3) */ + sample_rate = read_u32(header_offset+0x3c, sf); + channel_count = read_u32(header_offset+0x44, sf); + loop_flag = read_u8(header_offset+0x4b, sf); + + /* mini-header at the start of the stream */ + num_samples = read_u32le(start_offset+0x04, sf) / 0x02; /* PCM size? */ + start_offset += read_u32le(start_offset+0x00, sf); + break; + } + + switch (version) { case 0x04: /* Blur (PS3) */ case 0x05: /* James Bond 007: Blood Stone (X360) */ - sample_rate = read_32bit(header_offset+0x40, streamFile); - loop_flag = read_8bit(header_offset+0x48, streamFile); - tracks = read_8bit(header_offset+0x49, streamFile); - channel_count = read_8bit(header_offset+0x4b, streamFile); + sample_rate = read_u32(header_offset+0x40, sf); + num_samples = read_u32(header_offset+0x44, sf); + loop_flag = read_u8(header_offset+0x48, sf); + tracks = read_u8(header_offset+0x49, sf); + channel_count = read_u8(header_offset+0x4b, sf); if (tracks) { channel_count = channel_count * tracks; } break; - default: goto fail; } break; - - case 0x08: + case 0x08: /* XMA1 */ switch(version) { case 0x04: /* Project Gotham Racing (X360) */ - sample_rate = read_32bit(header_offset+0x3c, streamFile); - channel_count = read_32bit(header_offset+0x44, streamFile); - loop_flag = read_8bit(header_offset+0x54, streamFile) != 0; + sample_rate = read_u32(header_offset+0x3c, sf); + channel_count = read_u32(header_offset+0x44, sf); + loop_flag = read_u8(header_offset+0x54, sf) != 0; break; case 0x05: /* James Bond 007: Blood Stone (X360) */ - sample_rate = read_32bit(header_offset+0x40, streamFile); - channel_count = read_32bit(header_offset+0x48, streamFile); - loop_flag = read_8bit(header_offset+0x58, streamFile) != 0; + sample_rate = read_u32(header_offset+0x40, sf); + channel_count = read_u32(header_offset+0x48, sf); + loop_flag = read_u8(header_offset+0x58, sf) != 0; break; default: @@ -150,9 +162,9 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); + vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->loop_end_sample = num_samples; break; case 0x07: @@ -160,9 +172,9 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x21; - vgmstream->num_samples = read_32bit(header_offset+0x44, streamFile); + vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->loop_end_sample = num_samples; break; #ifdef VGM_USE_FFMPEG @@ -170,8 +182,8 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { uint8_t buf[0x100]; int bytes; - bytes = ffmpeg_make_riff_xma1(buf,0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, 0); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size); + bytes = ffmpeg_make_riff_xma1(buf,0x100, 0, stream_size, vgmstream->channels, vgmstream->sample_rate, 0); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -185,18 +197,18 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { msd.data_offset = start_offset; msd.data_size = stream_size; msd.loop_flag = loop_flag; - msd.loop_start_b = read_32bit(header_offset+0x4c, streamFile); - msd.loop_end_b = read_32bit(header_offset+0x50, streamFile); - msd.loop_start_subframe = (read_8bit(header_offset+0x55, streamFile) >> 0) & 0x0f; - msd.loop_end_subframe = (read_8bit(header_offset+0x55, streamFile) >> 4) & 0x0f; - xma_get_samples(&msd, streamFile); + msd.loop_start_b = read_u32(header_offset+0x4c, sf); + msd.loop_end_b = read_u32(header_offset+0x50, sf); + msd.loop_start_subframe = (read_u8(header_offset+0x55, sf) >> 0) & 0x0f; + msd.loop_end_subframe = (read_u8(header_offset+0x55, sf) >> 4) & 0x0f; + xma_get_samples(&msd, sf); vgmstream->num_samples = msd.num_samples; /* also at 0x58, but unreliable? */ vgmstream->loop_start_sample = msd.loop_start_sample; vgmstream->loop_end_sample = msd.loop_end_sample; } - xma_fix_raw_samples_ch(vgmstream, streamFile, start_offset, stream_size, channel_count, 1,1); + xma_fix_raw_samples_ch(vgmstream, sf, start_offset, stream_size, channel_count, 1,1); break; } #endif @@ -206,10 +218,10 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) { goto fail; } - read_string(vgmstream->stream_name,0x20+1, name_offset,streamFile); + read_string(vgmstream->stream_name,0x20+1, name_offset,sf); - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -264,7 +276,7 @@ VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE *streamFile) { vgmstream->interleave_block_size = frame_size; vgmstream->meta_type = meta_BAF; - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h index aed4fd907..308def89b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h @@ -21,7 +21,7 @@ typedef struct _BARSTREAMFILE { /*static*/ STREAMFILE *wrap_bar_STREAMFILE(STREAMFILE *file); -static size_t read_bar(BARSTREAMFILE *streamFile, uint8_t *dest, off_t offset, size_t length) { +static size_t read_bar(BARSTREAMFILE *streamFile, uint8_t *dest, offv_t offset, size_t length) { off_t i; size_t read_length = streamFile->real_file->read(streamFile->real_file, dest, offset, length); @@ -36,7 +36,7 @@ static size_t get_size_bar(BARSTREAMFILE *streamFile) { return streamFile->real_file->get_size(streamFile->real_file); } -static size_t get_offset_bar(BARSTREAMFILE *streamFile) { +static offv_t get_offset_bar(BARSTREAMFILE *streamFile) { return streamFile->real_file->get_offset(streamFile->real_file); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c index c4cf873fe..329c74b67 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c @@ -1,41 +1,38 @@ +#include #include "meta.h" #include "../coding/coding.h" +#include "../util/endianness.h" typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec; /* .BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - off_t start_offset, stream_offset, name_offset = 0; - size_t stream_size, interleave = 0; - off_t sblk_offset, data_offset; - size_t data_size; - int channel_count = 0, loop_flag, sample_rate, parts, sblk_version, big_endian; + uint32_t start_offset, stream_offset, name_offset = 0; + uint32_t sblk_offset, data_offset; + uint32_t stream_size, data_size, interleave = 0; + int channels = 0, loop_flag, sample_rate, parts, sblk_version, big_endian; int loop_start = 0, loop_end = 0; uint32_t pitch, flags; uint32_t atrac9_info = 0; int total_subsongs, target_subsong = sf->stream_index; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - float (*read_f32)(off_t,STREAMFILE*) = NULL; + read_u16_t read_u16; + read_u32_t read_u32; + read_f32_t read_f32; bnk_codec codec; - /* checks */ - if (!check_extensions(sf, "bnk")) - goto fail; - /* bnk version */ - if (read_32bitBE(0x00,sf) == 0x00000003) { /* PS3 */ - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; + if (read_u32be(0x00,sf) == 0x03) { /* PS3 */ + read_u32 = read_u32be; + read_u16 = read_u16be; read_f32 = read_f32be; big_endian = 1; } - else if (read_32bitBE(0x00,sf) == 0x03000000) { /* PS2/Vita/PS4 */ - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; + else if (read_u32le(0x00,sf) == 0x03) { /* PS2/Vita/PS4 */ + read_u32 = read_u32le; + read_u16 = read_u16le; read_f32 = read_f32le; big_endian = 0; } @@ -43,13 +40,18 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { goto fail; } - parts = read_32bit(0x04,sf); + /* checks */ + if (!check_extensions(sf, "bnk")) + goto fail; + + + parts = read_u32(0x04,sf); if (parts < 2 || parts > 3) goto fail; - sblk_offset = read_32bit(0x08,sf); + sblk_offset = read_u32(0x08,sf); /* 0x0c: sklb size */ - data_offset = read_32bit(0x10,sf); - data_size = read_32bit(0x14,sf); + data_offset = read_u32(0x10,sf); + data_size = read_u32(0x14,sf); /* when sblk_offset >= 0x20: */ /* 0x18: ZLSD small footer, rare [Yakuza 6's Puyo Puyo (PS4)] */ /* 0x1c: ZLSD size */ @@ -60,30 +62,30 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { /* SBlk part: parse header */ - if (read_32bit(sblk_offset+0x00,sf) != 0x6B6C4253) /* "klBS" (SBlk = sample block?) */ + if (read_u32(sblk_offset+0x00,sf) != get_id32be("klBS")) /* SBlk = sample block? */ goto fail; - sblk_version = read_32bit(sblk_offset+0x04,sf); + sblk_version = read_u32(sblk_offset+0x04,sf); /* 0x08: possibly when sblk_version=0x0d, 0x03=Vita, 0x06=PS4 */ //;VGM_LOG("BNK: sblk_offset=%lx, data_offset=%lx, sblk_version %x\n", sblk_offset, data_offset, sblk_version); { int i; - off_t table1_offset, table2_offset, table3_offset, table4_offset; - size_t section_entries, material_entries, stream_entries; - size_t table1_entry_size; - off_t table1_suboffset, table2_suboffset; - off_t table2_entry_offset = 0, table3_entry_offset = 0; + uint32_t table1_offset, table2_offset, table3_offset, table4_offset; + uint32_t section_entries, material_entries, stream_entries; + uint32_t table1_entry_size; + uint32_t table1_suboffset, table2_suboffset; + uint32_t table2_entry_offset = 0, table3_entry_offset = 0; int table4_entry_id = -1; - off_t table4_entries_offset, table4_names_offset; + uint32_t table4_entries_offset, table4_names_offset; switch(sblk_version) { case 0x01: /* Ratchet & Clank (PS2) */ - section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */ - material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x28 */ - stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: none (count) */ - table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf); - table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf); + section_entries = read_u16(sblk_offset+0x16,sf); /* entry size: ~0x0c */ + material_entries = read_u16(sblk_offset+0x18,sf); /* entry size: ~0x28 */ + stream_entries = read_u16(sblk_offset+0x1a,sf); /* entry size: none (count) */ + table1_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf); + table2_offset = sblk_offset + read_u32(sblk_offset+0x20,sf); table3_offset = table2_offset; /* mixed table in this version */ table4_offset = 0; /* not included */ @@ -95,14 +97,15 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { case 0x03: /* Yu-Gi-Oh! GX - The Beginning of Destiny (PS2) */ case 0x04: /* Test banks */ case 0x05: /* Ratchet & Clank (PS3) */ + case 0x08: /* Playstation Home Arcade (Vita) */ case 0x09: /* Puyo Puyo Tetris (PS4) */ - section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */ - material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x08 */ - stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */ - table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf); - table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf); - table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,sf); - table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,sf); + section_entries = read_u16(sblk_offset+0x16,sf); /* entry size: ~0x0c */ + material_entries = read_u16(sblk_offset+0x18,sf); /* entry size: ~0x08 */ + stream_entries = read_u16(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */ + table1_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf); + table2_offset = sblk_offset + read_u32(sblk_offset+0x20,sf); + table3_offset = sblk_offset + read_u32(sblk_offset+0x34,sf); + table4_offset = sblk_offset + read_u32(sblk_offset+0x38,sf); table1_entry_size = 0x0c; table1_suboffset = 0x08; @@ -111,13 +114,13 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */ case 0x0e: /* Yakuza 6's Puyo Puyo (PS4) */ - table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,sf); - table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf); - table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,sf); - table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,sf); - section_entries = (uint16_t)read_16bit(sblk_offset+0x38,sf); /* entry size: ~0x24 */ - material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,sf); /* entry size: ~0x08 */ - stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,sf); /* entry size: ~0x5c + variable */ + table1_offset = sblk_offset + read_u32(sblk_offset+0x18,sf); + table2_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf); + table3_offset = sblk_offset + read_u32(sblk_offset+0x2c,sf); + table4_offset = sblk_offset + read_u32(sblk_offset+0x30,sf); + section_entries = read_u16(sblk_offset+0x38,sf); /* entry size: ~0x24 */ + material_entries = read_u16(sblk_offset+0x3a,sf); /* entry size: ~0x08 */ + stream_entries = read_u16(sblk_offset+0x3c,sf); /* entry size: ~0x5c + variable */ table1_entry_size = 0x24; table1_suboffset = 0x0c; @@ -125,11 +128,11 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { break; default: - VGM_LOG("BNK: unknown version %x\n", sblk_version); + vgm_logi("BNK: unknown version %x (report)\n", sblk_version); goto fail; } - //;VGM_LOG("BNK: table offsets=%lx, %lx, %lx, %lx\n", table1_offset,table2_offset,table3_offset,table4_offset); + //;VGM_LOG("BNK: table offsets=%x, %x, %x, %x\n", table1_offset,table2_offset,table3_offset,table4_offset); //;VGM_LOG("BNK: table entries=%i, %i, %i\n", section_entries,material_entries,stream_entries); @@ -159,7 +162,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { */ for (i = 0; i < material_entries; i++) { - uint8_t table2_type = read_32bit(table2_offset + (i*0x28) + 0x00, sf); + uint32_t table2_type = read_u32(table2_offset + (i*0x28) + 0x00, sf); if (table2_type != 0x01) continue; @@ -179,7 +182,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { for (i = 0; i < material_entries; i++) { uint32_t table2_value, table2_subinfo, table2_subtype; - table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,sf); + table2_value = read_u32(table2_offset+(i*0x08)+table2_suboffset+0x00,sf); table2_subinfo = (table2_value >> 0) & 0xFFFF; table2_subtype = (table2_value >> 16) & 0xFFFF; if (table2_subtype != 0x100) @@ -214,54 +217,31 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { case 0x03: case 0x04: case 0x05: + case 0x08: case 0x09: - pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,sf); - flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,sf); - stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,sf); - stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,sf); + pitch = read_u8(table3_offset+table3_entry_offset+0x02,sf); + flags = read_u8(table3_offset+table3_entry_offset+0x0f,sf); + stream_offset = read_u32(table3_offset+table3_entry_offset+0x10,sf); + stream_size = read_u32(table3_offset+table3_entry_offset+0x14,sf); - /* must use some log/formula but whatevs */ switch(pitch) { - case 0xC6: sample_rate = 50000; break; //? case 0xC4: sample_rate = 48000; break; - case 0xC3: sample_rate = 46000; break; //? case 0xC2: sample_rate = 44100; break; - case 0xBC: sample_rate = 36000; break; //? - case 0xBA: sample_rate = 32000; break; //? - case 0xB9: sample_rate = 30000; break; //? - case 0xB8: sample_rate = 28000; break; //? case 0xB6: sample_rate = 22050; break; - case 0xB4: sample_rate = 18000; break; //? - case 0xB2: sample_rate = 16000; break; //? - case 0xB0: sample_rate = 15000; break; //? - case 0xAF: sample_rate = 14000; break; //? - case 0xAE: sample_rate = 13000; break; //? - case 0xAD: sample_rate = 12500; break; //? - case 0xAC: sample_rate = 12000; break; //? - case 0xAB: sample_rate = 11050; break; //? case 0xAA: sample_rate = 11025; break; - case 0xA9: sample_rate = 10000; break; //? - case 0xA8: sample_rate = 9000; break; //? - case 0xA7: sample_rate = 8000; break; //? - case 0xA6: sample_rate = 7000; break; //? - case 0xA5: sample_rate = 6500; break; //? - case 0xA4: sample_rate = 6000; break; //? - case 0xA3: sample_rate = 5800; break; //? - case 0xA2: sample_rate = 5400; break; //? - case 0xA1: sample_rate = 5000; break; //? - case 0x9d: sample_rate = 4000; break; //? - case 0x9c: sample_rate = 3500; break; //? default: - VGM_LOG("BNK: unknown pitch %x\n", pitch); - goto fail; + /* approximate note-to-hz, not sure about real formula (observed from 0x9a to 0xc6) + * using (rate) * 2 ^ ((pitch - note) / 12) but not correct? (may be rounded) */ + sample_rate = 614.0 * pow(2.0, (float)(pitch - 0x70) / 12.0); + break; } break; case 0x0d: case 0x0e: - flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,sf); - stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,sf); - stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,sf); + flags = read_u8 (table3_offset+table3_entry_offset+0x12,sf); + stream_offset = read_u32(table3_offset+table3_entry_offset+0x44,sf); + stream_size = read_u32(table3_offset+table3_entry_offset+0x48,sf); sample_rate = (int)read_f32(table3_offset+table3_entry_offset+0x4c,sf); break; @@ -275,12 +255,13 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { switch(sblk_version) { //case 0x03: /* different format? */ //case 0x04: /* different format? */ + case 0x08: case 0x09: case 0x0d: case 0x0e: /* find if this sound has an assigned name in table1 */ for (i = 0; i < section_entries; i++) { - off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,sf); + uint32_t entry_offset = read_u16(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,sf); /* rarely (ex. Polara sfx) one name applies to multiple materials, * from current entry_offset to next entry_offset (section offsets should be in order) */ @@ -296,15 +277,15 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { /* 0x0c: table4 size */ /* variable: entries */ /* variable: names (null terminated) */ - table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, sf); + table4_entries_offset = table4_offset + read_u32(table4_offset+0x08, sf); table4_names_offset = table4_entries_offset + (0x10*section_entries); //;VGM_LOG("BNK: t4_entries=%lx, t4_names=%lx\n", table4_entries_offset, table4_names_offset); /* get assigned name from table4 names */ for (i = 0; i < section_entries; i++) { - int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, sf); + int entry_id = read_u32(table4_entries_offset+(i*0x10)+0x0c, sf); if (entry_id == table4_entry_id) { - name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, sf); + name_offset = table4_names_offset + read_u32(table4_entries_offset+(i*0x10)+0x00, sf); break; } } @@ -329,24 +310,24 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { case 0x03: case 0x04: case 0x05: - channel_count = 1; + channels = 1; /* early versions don't have PS-ADPCM size, could check next offset but it's all kind of loopy */ if (sblk_version <= 0x03 && stream_size == 0 && (flags & 0x80) == 0) { - off_t offset; - off_t max_offset = get_streamfile_size(sf); + uint32_t offset; + uint32_t max_offset = get_streamfile_size(sf); stream_size += 0x10; for (offset = data_offset + stream_offset + 0x10; offset < max_offset; offset += 0x10) { /* beginning frame (if file loops won't have end frame) */ - if (read_32bitBE(offset + 0x00, sf) == 0x00000000 && read_32bitBE(offset + 0x04, sf) == 0x00000000) + if (read_u32be(offset + 0x00, sf) == 0x00000000 && read_u32be(offset + 0x04, sf) == 0x00000000) break; stream_size += 0x10; /* end frame */ - if (read_32bitBE(offset + 0x00, sf) == 0x00077777 && read_32bitBE(offset + 0x04, sf) == 0x77777777) + if (read_u32be(offset + 0x00, sf) == 0x00077777 && read_u32be(offset + 0x04, sf) == 0x77777777) break; } @@ -356,17 +337,17 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { /* hack for PS3 files that use dual subsongs as stereo */ if (total_subsongs == 2 && stream_size * 2 == data_size) { - channel_count = 2; - stream_size = stream_size*channel_count; + channels = 2; + stream_size = stream_size*channels; total_subsongs = 1; } - interleave = stream_size / channel_count; + interleave = stream_size / channels; - if (flags & 0x80) { - codec = PCM16; /* rare [Wipeout HD (PS3)] */ + if ((flags & 0x80) && sblk_version <= 3) { /* PS Home Arcade has other flags? */ + codec = PCM16; /* rare [Wipeout HD (PS3)]-v3 */ } else { - loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end); + loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channels, interleave, &loop_start, &loop_end); loop_flag = (flags & 0x40); /* no loops values in sight so may only apply to PS-ADPCM flags */ codec = PSX; @@ -375,54 +356,68 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { //postdata_size = 0x10; /* last frame may be garbage */ break; + case 0x08: case 0x09: - type = read_16bit(start_offset+0x00,sf); - extradata_size = 0x08 + read_32bit(start_offset+0x04,sf); /* 0x14 for AT9 */ + type = read_u16(start_offset+0x00,sf); + extradata_size = 0x08 + read_u32(start_offset+0x04,sf); /* 0x14 for AT9 */ switch(type) { + case 0x00: + channels = 1; + codec = PSX; + interleave = 0x10; + break; + + case 0x01: + channels = 1; + codec = PCM16; + interleave = 0x01; + break; + + case 0x02: /* ATRAC9 mono */ case 0x05: /* ATRAC9 stereo */ - if (read_32bit(start_offset+0x08,sf) + 0x08 != extradata_size) /* repeat? */ + if (read_u32(start_offset+0x08,sf) + 0x08 != extradata_size) /* repeat? */ goto fail; - channel_count = (type == 0x02) ? 1 : 2; + channels = (type == 0x02) ? 1 : 2; - atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,sf); + atrac9_info = read_u32be(start_offset+0x0c,sf); /* 0x10: null? */ - loop_length = read_32bit(start_offset+0x14,sf); - loop_start = read_32bit(start_offset+0x18,sf); + loop_length = read_u32(start_offset+0x14,sf); + loop_start = read_u32(start_offset+0x18,sf); loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ codec = ATRAC9; break; default: - VGM_LOG("BNK: unknown type %x\n", type); + vgm_logi("BNK: unknown type %x (report)\n", type); goto fail; } break; case 0x0d: case 0x0e: - type = read_16bit(start_offset+0x00,sf); - if (read_32bit(start_offset+0x04,sf) != 0x01) /* type? */ + type = read_u16(start_offset+0x00,sf); + if (read_u32(start_offset+0x04,sf) != 0x01) /* type? */ goto fail; - extradata_size = 0x10 + read_32bit(start_offset+0x08,sf); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */ + extradata_size = 0x10 + read_u32(start_offset+0x08,sf); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */ /* 0x0c: null? */ switch(type) { case 0x02: /* ATRAC9 mono */ case 0x05: /* ATRAC9 stereo */ - if (read_32bit(start_offset+0x10,sf) + 0x10 != extradata_size) /* repeat? */ + if (read_u32(start_offset+0x10,sf) + 0x10 != extradata_size) /* repeat? */ goto fail; - channel_count = (type == 0x02) ? 1 : 2; + channels = (type == 0x02) ? 1 : 2; - atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,sf); + atrac9_info = read_u32be(start_offset+0x14,sf); /* 0x18: null? */ /* 0x1c: channels? */ /* 0x20: null? */ - loop_length = read_32bit(start_offset+0x24,sf); - loop_start = read_32bit(start_offset+0x28,sf); + loop_length = read_u32(start_offset+0x24,sf); + loop_start = read_u32(start_offset+0x28,sf); loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ codec = ATRAC9; @@ -431,11 +426,11 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */ case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */ /* 0x10: null? */ - channel_count = read_32bit(start_offset+0x14,sf); + channels = read_u32(start_offset+0x14,sf); interleave = 0x02; - loop_start = read_32bit(start_offset+0x18,sf); - loop_length = read_32bit(start_offset+0x1c,sf); + loop_start = read_u32(start_offset+0x18,sf); + loop_length = read_u32(start_offset+0x1c,sf); loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ codec = PCM16; @@ -443,24 +438,24 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { case 0x00: /* PS-ADPCM (test banks) */ /* 0x10: null? */ - channel_count = read_32bit(start_offset+0x14,sf); + channels = read_u32(start_offset+0x14,sf); interleave = 0x02; - loop_start = read_32bit(start_offset+0x18,sf); - loop_length = read_32bit(start_offset+0x1c,sf); + loop_start = read_u32(start_offset+0x18,sf); + loop_length = read_u32(start_offset+0x1c,sf); loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ codec = HEVAG; break; default: - VGM_LOG("BNK: unknown type %x\n", type); + vgm_logi("BNK: unknown type %x (report)\n", type); goto fail; } break; default: - VGM_LOG("BNK: unknown data version %x\n", sblk_version); + vgm_logi("BNK: unknown data version %x (report)\n", sblk_version); goto fail; } @@ -474,7 +469,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; @@ -518,7 +513,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; - vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); + vgmstream->num_samples = ps_bytes_to_samples(stream_size,channels); vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; break; @@ -528,7 +523,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; - vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); + vgmstream->num_samples = ps_bytes_to_samples(stream_size,channels); vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c b/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c index b4693065d..b785e27e0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c @@ -1,6 +1,6 @@ #include "meta.h" #include "../coding/coding.h" -#include "../util.h" +#include "../util/chunks.h" #include "bnsf_keys.h" @@ -13,55 +13,76 @@ static void find_bnsf_key(STREAMFILE *sf, off_t start, g7221_codec_data *data, u #endif /* BNSF - Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */ -VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset = 0, first_offset = 0x0C; - int loop_flag = 0, channel_count = 0, sample_rate; - int num_samples, loop_start = 0, loop_end = 0, loop_adjust, block_samples; +VGMSTREAM* init_vgmstream_bnsf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset = 0; + int loop_flag = 0, channels = 0, sample_rate = 0; + uint32_t num_samples = 0, loop_start = 0, loop_end = 0, loop_adjust = 0, block_samples = 0; uint32_t codec, flags = 0; - size_t bnsf_size, sdat_size, block_size; - off_t loop_chunk = 0, sfmt_chunk, sdat_chunk; + size_t bnsf_size, block_size = 0; /* checks */ - if (!check_extensions(streamFile,"bnsf")) + if (!is_id32be(0x00,sf, "BNSF")) goto fail; - if (read_32bitBE(0,streamFile) != 0x424E5346) /* "BNSF" */ + if (!check_extensions(sf,"bnsf")) goto fail; - bnsf_size = read_32bitBE(0x04,streamFile); - codec = read_32bitBE(0x08,streamFile); + bnsf_size = read_u32be(0x04,sf); + codec = read_u32be(0x08,sf); - if (bnsf_size + (codec == 0x49533232 ? 0x00 : 0x08) != get_streamfile_size(streamFile)) /* IS22 uses full size */ + if (codec != get_id32be("IS22")) /* uses full size */ + bnsf_size += 0x08; + if (bnsf_size != get_streamfile_size(sf)) goto fail; - if (!find_chunk_be(streamFile, 0x73666d74,first_offset,0, &sfmt_chunk,NULL)) /* "sfmt" */ - goto fail; - if (!find_chunk_be(streamFile, 0x73646174,first_offset,0, &sdat_chunk,&sdat_size)) /* "sdat" */ - goto fail; - if ( find_chunk_be(streamFile, 0x6C6F6F70,first_offset,0, &loop_chunk,NULL)) { /* "loop" */ - loop_flag = 1; - loop_start = read_32bitBE(loop_chunk+0x00,streamFile); /* block-aligned */ - loop_end = read_32bitBE(loop_chunk+0x04,streamFile) + 1; + { + enum { + CHUNK_sfmt = 0x73666d74, + CHUNK_sdat = 0x73646174, + CHUNK_loop = 0x6C6F6F70, + }; + chunk_t rc = {0}; + + rc.be_size = 1; + rc.current = 0x0C; + while (next_chunk(&rc, sf)) { + switch(rc.type) { + case CHUNK_sfmt: + flags = read_u16be(rc.offset+0x00,sf); + channels = read_u16be(rc.offset+0x02,sf); + sample_rate = read_s32be(rc.offset+0x04,sf); + num_samples = read_s32be(rc.offset+0x08,sf); + loop_adjust = read_s32be(rc.offset+0x0c,sf); /* 0 when no loop */ + block_size = read_u16be(rc.offset+0x10,sf); + block_samples = read_u16be(rc.offset+0x12,sf); + //max_samples = sdat_size / block_size * block_samples; + break; + + case CHUNK_loop: + loop_flag = 1; + loop_start = read_s32be(rc.offset+0x00,sf); /* block-aligned */ + loop_end = read_s32be(rc.offset+0x04,sf) + 1; + break; + + case CHUNK_sdat: + start_offset = rc.offset; + break; + + default: + break; + } + } } - flags = read_16bitBE(sfmt_chunk+0x00,streamFile); - channel_count = read_16bitBE(sfmt_chunk+0x02,streamFile); - sample_rate = read_32bitBE(sfmt_chunk+0x04,streamFile); - num_samples = read_32bitBE(sfmt_chunk+0x08,streamFile); - loop_adjust = read_32bitBE(sfmt_chunk+0x0c,streamFile); /* 0 when no loop */ - block_size = read_16bitBE(sfmt_chunk+0x10,streamFile); - block_samples = read_16bitBE(sfmt_chunk+0x12,streamFile); - //max_samples = sdat_size / block_size * block_samples; - - start_offset = sdat_chunk; - if (loop_adjust >= block_samples) /* decoder can't handle this */ goto fail; + if (!start_offset) + goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; @@ -71,7 +92,7 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { vgmstream->meta_type = meta_BNSF; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = block_size / channel_count; + vgmstream->interleave_block_size = block_size / channels; switch (codec) { #ifdef VGM_USE_G7221 @@ -80,20 +101,20 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { vgmstream->codec_data = init_g7221(vgmstream->channels, vgmstream->interleave_block_size); if (!vgmstream->codec_data) goto fail; - /* get decryption key in .bnsfkey file or list, for later games' voices - * [The Idolm@ster 2 (PS3/X360), Tales of Zestiria (PS3/PC)] */ + /* get decryption key in .bnsfkey file or list, for later games' voices and some odd BGM + * [THE iDOL@STER 2 (PS3/X360), Tales of Zestiria (PS3/PC)] */ if (flags != 0) { /* only known value is 0x02 though */ size_t keysize; uint8_t key[24] = {0}; /* keystring 0-padded to 192-bit */ - keysize = read_key_file(key, sizeof(key), streamFile); + keysize = read_key_file(key, sizeof(key), sf); #ifdef BNSF_BRUTEFORCE if (1) { - bruteforce_bnsf_key(streamFile, start_offset, vgmstream->codec_data, key); + bruteforce_bnsf_key(sf, start_offset, vgmstream->codec_data, key); } else #endif if (keysize <= 0 || keysize > sizeof(key)) { - find_bnsf_key(streamFile, start_offset, vgmstream->codec_data, key); + find_bnsf_key(sf, start_offset, vgmstream->codec_data, key); } set_key_g7221(vgmstream->codec_data, key); } @@ -118,10 +139,9 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; - fail: close_vgmstream(vgmstream); return NULL; @@ -187,9 +207,9 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d sf_keys = open_streamfile_by_filename(sf, "keys.txt"); if (!sf_keys) goto done; - + keys_size = get_streamfile_size(sf_keys); - + offset = 0x00; while (offset < keys_size) { int line_len; @@ -204,7 +224,7 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d for (j = i + BNSF_MIN_KEY_LEN; j <= line_len; j++) { int keylen = j - i; const char* key = &line[i]; - + test_key(sf, start, data, key, keylen, &best_score, best_key); if (best_score == 1) { VGM_ASSERT(best_score > 0, "BNSF: good key=%.24s (score=%i)\n", best_key, best_score); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ck.c b/Frameworks/vgmstream/vgmstream/src/meta/ck.c index 51637f768..fdd57da89 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ck.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ck.c @@ -1,177 +1,179 @@ -#include "meta.h" - - -/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */ -VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, codec, sample_rate; - int32_t num_samples, loop_start, loop_end; - size_t block_size; - - - /* checks */ - if (!check_extensions(streamFile, "cks")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */ - goto fail; - /* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */ - if (read_32bitLE(0x08,streamFile) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */ - goto fail; - if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */ - goto fail; - - codec = read_8bit(0x10,streamFile); - channel_count = read_8bit(0x11,streamFile); - sample_rate = (uint16_t)read_16bitLE(0x12,streamFile); - num_samples = read_32bitLE(0x14,streamFile) * read_16bitLE(0x1a,streamFile); /* number_of_blocks * samples_per_frame */ - block_size = read_16bitLE(0x18,streamFile); - /* 0x1c(2): volume */ - /* 0x1e(2): pan */ - loop_start = read_32bitLE(0x20,streamFile); - loop_end = read_32bitLE(0x24,streamFile); - loop_flag = read_16bitLE(0x28,streamFile) != 0; /* loop count (-1 = forever) */ - /* 0x2a(2): unused? */ - - start_offset = 0x2c; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; - - vgmstream->meta_type = meta_CKS; - - switch(codec) { - case 0x00: /* pcm16 [from tests] */ - vgmstream->coding_type = coding_PCM16LE; - break; - case 0x01: /* pcm8 [from tests] */ - vgmstream->coding_type = coding_PCM8; - break; - case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */ - vgmstream->coding_type = coding_MSADPCM_ck; - vgmstream->frame_size = block_size / channel_count; /* always 0x18 */ - break; - default: - goto fail; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = block_size / channel_count; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */ -VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, name_offset = 0; - int loop_flag, channel_count, codec, sample_rate; - int32_t num_samples, loop_start, loop_end; - size_t block_size, stream_size; - int total_subsongs, target_subsong = streamFile->stream_index; - - - /* checks */ - if (!check_extensions(streamFile, "ckb")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */ - goto fail; - /* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */ - if (read_32bitLE(0x08,streamFile) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */ - goto fail; - if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */ - goto fail; - - /* 0x10: bank name (size 0x1c+1) */ - /* 0x30/34: reserved? */ - total_subsongs = read_32bitLE(0x38,streamFile); - if (target_subsong == 0) target_subsong = 1; - if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - /* 0x3c: total_subsongs again? (ignored) */ - /* 0x40/44: unknown (ignored) */ - - /* get subsong (stream offset isn't given so must calc manually) */ - { - int i; - off_t header_offset = 0x48; - off_t stream_offset = 0x48 + total_subsongs*0x48; - - for (i = 0; i < total_subsongs; i++) { - name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */ - codec = read_8bit(header_offset+0x20,streamFile); - channel_count = read_8bit(header_offset+0x21,streamFile); - sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,streamFile); - num_samples = read_32bitLE(header_offset+0x24,streamFile) * read_16bitLE(header_offset+0x2a,streamFile); /* number_of_blocks * samples_per_frame */ - block_size = read_16bitLE(header_offset+0x28,streamFile); - /* 0x2c(2): volume */ - /* 0x2e(2): pan */ - loop_start = read_32bitLE(header_offset+0x30,streamFile); - loop_end = read_32bitLE(header_offset+0x34,streamFile); - loop_flag = read_16bitLE(header_offset+0x38,streamFile) != 0; /* loop count (-1 = forever) */ - /* 0x3a(2): unused? */ - stream_size = read_32bitLE(header_offset+0x3c,streamFile); - /* 0x40/44(4): unused? */ - - if (target_subsong == (i+1)) - break; - header_offset += 0x48; - stream_offset += stream_size; - } - - start_offset = stream_offset; - } - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; - - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = stream_size; - read_string(vgmstream->stream_name,0x1c+1, name_offset,streamFile); - - vgmstream->meta_type = meta_CKB; - - switch(codec) { - case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */ - vgmstream->coding_type = coding_PCM16LE; - break; - case 0x01: /* pcm8 */ - vgmstream->coding_type = coding_PCM8; - break; - case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */ - vgmstream->coding_type = coding_MSADPCM_ck; - /* frame_size is always 0x18 */ - break; - default: - goto fail; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = block_size / channel_count; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" + + +/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */ +VGMSTREAM* init_vgmstream_cks(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, codec, sample_rate; + int32_t num_samples, loop_start, loop_end; + size_t block_size; + + + /* checks */ + if (!is_id32be(0x00,sf, "ckmk")) + goto fail; + if (!check_extensions(sf, "cks")) + goto fail; + + /* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */ + if (read_u32le(0x08,sf) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */ + goto fail; + if (read_u32le(0x0c,sf) != 0x02) /* file version (always 0x02) */ + goto fail; + + codec = read_u8(0x10,sf); + channels = read_u8(0x11,sf); + sample_rate = read_u16le(0x12,sf); + num_samples = read_s32le(0x14,sf) * read_u16le(0x1a,sf); /* number_of_blocks * samples_per_frame */ + block_size = read_u16le(0x18,sf); + /* 0x1c(2): volume */ + /* 0x1e(2): pan */ + loop_start = read_s32le(0x20,sf); + loop_end = read_s32le(0x24,sf); + loop_flag = read_s16le(0x28,sf) != 0; /* loop count (-1 = forever) */ + /* 0x2a(2): unused? */ + + start_offset = 0x2c; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->meta_type = meta_CKS; + + switch(codec) { + case 0x00: /* pcm16 [from tests] */ + vgmstream->coding_type = coding_PCM16LE; + break; + case 0x01: /* pcm8 [from tests] */ + vgmstream->coding_type = coding_PCM8; + break; + case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */ + vgmstream->coding_type = coding_MSADPCM_ck; + vgmstream->frame_size = block_size / channels; /* always 0x18 */ + break; + default: + goto fail; + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channels; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */ +VGMSTREAM* init_vgmstream_ckb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, name_offset = 0; + int loop_flag = 0, channels = 0, codec = 0, sample_rate = 0; + int32_t num_samples = 0, loop_start = 0, loop_end = 0; + size_t block_size = 0, stream_size = 0; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!is_id32be(0x00,sf, "ckmk")) + goto fail; + if (!check_extensions(sf, "ckb")) + goto fail; + + /* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */ + if (read_u32le(0x08,sf) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */ + goto fail; + if (read_u32le(0x0c,sf) != 0x02) /* file version (always 0x02) */ + goto fail; + + /* 0x10: bank name (size 0x1c+1) */ + /* 0x30/34: reserved? */ + total_subsongs = read_u32le(0x38,sf); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + /* 0x3c: total_subsongs again? (ignored) */ + /* 0x40/44: unknown (ignored) */ + + /* get subsong (stream offset isn't given so must calc manually) */ + { + int i; + off_t header_offset = 0x48; + off_t stream_offset = 0x48 + total_subsongs*0x48; + + for (i = 0; i < total_subsongs; i++) { + name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */ + codec = read_8bit(header_offset+0x20,sf); + channels = read_8bit(header_offset+0x21,sf); + sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,sf); + num_samples = read_32bitLE(header_offset+0x24,sf) * read_16bitLE(header_offset+0x2a,sf); /* number_of_blocks * samples_per_frame */ + block_size = read_16bitLE(header_offset+0x28,sf); + /* 0x2c(2): volume */ + /* 0x2e(2): pan */ + loop_start = read_32bitLE(header_offset+0x30,sf); + loop_end = read_32bitLE(header_offset+0x34,sf); + loop_flag = read_16bitLE(header_offset+0x38,sf) != 0; /* loop count (-1 = forever) */ + /* 0x3a(2): unused? */ + stream_size = read_32bitLE(header_offset+0x3c,sf); + /* 0x40/44(4): unused? */ + + if (target_subsong == (i+1)) + break; + header_offset += 0x48; + stream_offset += stream_size; + } + + start_offset = stream_offset; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + read_string(vgmstream->stream_name,0x1c+1, name_offset,sf); + + vgmstream->meta_type = meta_CKB; + + switch(codec) { + case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */ + vgmstream->coding_type = coding_PCM16LE; + break; + case 0x01: /* pcm8 */ + vgmstream->coding_type = coding_PCM8; + break; + case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */ + vgmstream->coding_type = coding_MSADPCM_ck; + /* frame_size is always 0x18 */ + break; + default: + goto fail; + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channels; + + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index 4a7fb3f0b..206c90099 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" +#include "../util/endianness.h" #define EA_CODEC_PCM 0x00 #define EA_CODEC_ULAW 0x01 @@ -17,7 +18,9 @@ typedef struct { int32_t loop_start; int32_t loop_end; int32_t loop_start_offset; - int32_t data_offset; + uint32_t data_offset; + + uint32_t base_size; int big_endian; int loop_flag; @@ -27,7 +30,7 @@ typedef struct { int total_subsongs; } eacs_header; -static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t begin_offset); +static int parse_header(STREAMFILE* sf, eacs_header* ea, uint32_t begin_offset); static VGMSTREAM* init_vgmstream_main(STREAMFILE* sf, eacs_header* ea); static void set_ea_1snh_num_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, eacs_header* ea, int find_loop); @@ -35,24 +38,12 @@ static int get_ea_1snh_ima_version(STREAMFILE* sf, off_t start_offset, const eac /* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */ VGMSTREAM* init_vgmstream_ea_1snh(STREAMFILE* sf) { - eacs_header ea = { 0 }; - off_t offset, eacs_offset; + eacs_header ea = {0}; + off_t offset = 0x00, eacs_offset; VGMSTREAM* vgmstream = NULL; /* checks */ - /* .asf/as4: common, - * .lasf: fake for plugins - * .sng: fake for plugins (for .asf issues) - * .cnk: some PS1 games - * .uv/tgq: some SAT videos - * .tgv: videos - * (extensionless): Need for Speed (SAT) videos */ - if (!check_extensions(sf, "asf,lasf,sng,as4,cnk,uv,tgq,tgv,")) - goto fail; - - offset = 0x00; - /* in TGV videos, either TGVk or 1SNh block comes first */ if (is_id32be(0x00, sf, "TGVk")) { offset = read_u32be(0x04, sf); @@ -64,14 +55,28 @@ VGMSTREAM* init_vgmstream_ea_1snh(STREAMFILE* sf) { !is_id32be(offset + 0x00, sf, "SEAD")) goto fail; + /* .asf/as4: common, + * .lasf: fake for plugins + * .sng: fake for plugins (for .asf issues) + * .cnk: some PS1 games [Triple Play 97 (PS1), FIFA 97 (PS1)] + * .uv/tgq: some SAT videos + * .tgv: videos + * (extensionless): Need for Speed (SAT) videos */ + if (!check_extensions(sf, "asf,lasf,sng,as4,cnk,uv,tgq,tgv,")) + goto fail; + /* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end. * Video uses various blocks (TGVk/TGVf/MUVf/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */ ea.is_sead = is_id32be(offset + 0x00, sf, "SEAD"); + if (!ea.is_sead) + ea.base_size = read_u32le(offset + 0x04, sf); + eacs_offset = offset + 0x08; /* after 1SNh block id+size */ if (!parse_header(sf, &ea, eacs_offset)) goto fail; + vgmstream = init_vgmstream_main(sf, &ea); if (!vgmstream) goto fail; @@ -98,17 +103,19 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf, "EACS") && + read_u32be(0x00,sf) != 0 && !is_id32be(0x228,sf, "EACS")) + goto fail; + /* .eas: single bank [Need for Speed (PC)] - * .bnk: multi bank [Need for Speed (PC)] */ - if (!check_extensions(sf,"eas,bnk")) + * .bnk: multi bank [Need for Speed (PC)] + * .as4: single [NBA Live 96 (PC)] */ + if (!check_extensions(sf,"eas,bnk,as4")) goto fail; /* plain data without blocks, can contain N*(EACS header) + N*(data), or N (EACS header + data) */ ea.is_bank = 1; - /* use ??? as endian marker (Saturn = BE) */ - //ea.big_endian = guess_endianness32bit(0x04,sf); - if (is_id32be(0x00,sf, "EACS")) { /* single bank variant */ eacs_offset = 0x00; @@ -121,8 +128,8 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) { if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0) goto fail; - /* offsets to EACSs are scattered in the first 0x200 - * this looks dumb but seems like the only way */ + /* offsets to EACSs are scattered in the first 0x200, then 0x28 info + EACS per subsong. + * This looks dumb but seems like the only way. */ eacs_offset = 0; for (i = 0x00; i < 0x200; i += 0x04) { off_t bank_offset = read_u32le(i, sf); @@ -134,7 +141,7 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) { /* parse mini bank header */ if (ea.total_subsongs == target_subsong) { /* 0x00: some id or flags? */ - eacs_offset = read_u32le(bank_offset + 0x04, sf); + eacs_offset = read_u32le(bank_offset + 0x04, sf); /* always after 0x28 from bank_offset */ if (!is_id32be(eacs_offset, sf, "EACS")) goto fail; /* rest: not sure if part of this header */ @@ -193,10 +200,11 @@ static VGMSTREAM* init_vgmstream_main(STREAMFILE* sf, eacs_header* ea) { case EA_CODEC_PSX: /* Need for Speed (PS1) */ vgmstream->coding_type = coding_PSX; + vgmstream->codec_config = ea->codec_config; break; default: - VGM_LOG("EA EACS: unknown codec 0x%02x\n", ea->codec); + vgm_logi("EA EACS: unknown codec 0x%02x\n", ea->codec); goto fail; } @@ -210,13 +218,13 @@ fail: return NULL; } -static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) { +static int parse_header(STREAMFILE* sf, eacs_header* ea, uint32_t offset) { /* audio header endianness doesn't always match block headers, use sample rate to detect */ - int32_t (*read_s32)(off_t,STREAMFILE*); + read_s32_t read_s32; if (is_id32be(offset+0x00,sf, "EACS")) { /* EACS subheader (PC, SAT) */ - ea->big_endian = guess_endianness32bit(offset + 0x04, sf); + ea->big_endian = guess_endian32(offset + 0x04, sf); read_s32 = ea->big_endian ? read_s32be : read_s32le; ea->sample_rate = read_s32(offset+0x04, sf); @@ -236,9 +244,37 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) { ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea); /* EACS banks with empty values exist but will be rejected later */ } - else if (read_u32be(offset + 0x00, sf) == 0x00) { + else if (ea->is_sead) { + /* alt subheader (found in some PC videos) */ + ea->big_endian = guess_endian32(offset + 0x00, sf); + read_s32 = ea->big_endian ? read_s32be : read_s32le; + + ea->sample_rate = read_s32(offset+0x00, sf); + ea->channels = read_s32(offset+0x04, sf); + ea->codec = read_s32(offset+0x08, sf); + + if (ea->codec == EA_CODEC_IMA) + ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea); + } + else if (ea->base_size == 0x2c) { + /* [NBA Live 96 (PS1), Need for Speed (PS1)] */ + ea->sample_rate = read_s32le(offset+0x00, sf); + ea->channels = read_u8(offset+0x18, sf); + ea->codec = EA_CODEC_PSX; + ea->codec_config = 0; + } + else if (ea->base_size == 0x30) { + /* [FIFA 97 (PS1), Triple Play 97 (PS1)] */ + /* 0x00: 0 or some id? (same for N files) */ + ea->sample_rate = read_s32le(offset+0x04, sf); + ea->channels = read_u8(offset+0x1c, sf); + ea->codec = EA_CODEC_PSX; + ea->codec_config = 1; + } + else { + //TODO: test /* found in early videos, similar to EACS */ - ea->big_endian = guess_endianness32bit(offset + 0x04, sf); + ea->big_endian = guess_endian32(offset + 0x04, sf); read_s32 = ea->big_endian ? read_s32be : read_s32le; ea->sample_rate = read_s32(offset + 0x04, sf); @@ -250,27 +286,6 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) { if (ea->codec == EA_CODEC_IMA) ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea); } - else if (ea->is_sead) { - /* alt subheader (found in some PC videos) */ - ea->big_endian = guess_endianness32bit(offset + 0x00, sf); - read_s32 = ea->big_endian ? read_s32be : read_s32le; - - ea->sample_rate = read_s32(offset+0x00, sf); - ea->channels = read_s32(offset+0x04, sf); - ea->codec = read_s32(offset+0x08, sf); - - if (ea->codec == EA_CODEC_IMA) - ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea); - } - else { - /* alt subheader (PS1) */ - ea->big_endian = guess_endianness32bit(offset + 0x00, sf); - read_s32 = ea->big_endian ? read_s32be : read_s32le; - - ea->sample_rate = read_s32(offset+0x00, sf); - ea->channels = read_u8(offset+0x18, sf); - ea->codec = EA_CODEC_PSX; - } ea->loop_flag = (ea->loop_end > 0); @@ -281,26 +296,28 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) { static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_header* ea, int find_loop) { int32_t num_samples = 0, block_id; size_t file_size; - int32_t(*read_s32)(off_t, STREAMFILE *) = ea->big_endian ? read_s32be : read_s32le; + read_s32_t read_s32 = ea->big_endian ? read_s32be : read_s32le; int loop_end_found = 0; file_size = get_streamfile_size(sf); vgmstream->next_block_offset = ea->data_offset; while (vgmstream->next_block_offset < file_size) { - block_update_ea_1snh(vgmstream->next_block_offset, vgmstream); + block_update(vgmstream->next_block_offset, vgmstream); if (vgmstream->current_block_samples < 0) break; block_id = read_u32be(vgmstream->current_block_offset, sf); + if (find_loop) { if (vgmstream->current_block_offset == ea->loop_start_offset) { ea->loop_start = num_samples; ea->loop_flag = 1; - block_update_ea_1snh(ea->data_offset, vgmstream); + block_update(ea->data_offset, vgmstream); return; } - } else { + } + else { if (block_id == get_id32be("1SNl") ) { /* loop point found */ ea->loop_start_offset = read_s32(vgmstream->current_block_offset + 0x08, sf); ea->loop_end = num_samples; @@ -314,7 +331,7 @@ static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_h ea->num_samples = num_samples; /* reset once we're done */ - block_update_ea_1snh(ea->data_offset, vgmstream); + block_update(ea->data_offset, vgmstream); if (loop_end_found) { /* recurse to find loop start sample */ @@ -326,21 +343,27 @@ static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_h static int get_ea_1snh_ima_version(STREAMFILE* sf, off_t start_offset, const eacs_header* ea) { off_t block_offset = start_offset; size_t file_size = get_streamfile_size(sf); - int32_t (*read_s32)(off_t,STREAMFILE*) = ea->big_endian ? read_s32be : read_s32le; + read_s32_t read_s32 = ea->big_endian ? read_s32be : read_s32le; + + if (ea->type == 0xFF) /* bnk */ + return 0; while (block_offset < file_size) { uint32_t id = read_u32be(block_offset+0x00,sf); size_t block_size; /* BE in SAT, but one file may have both BE and LE chunks */ - if (guess_endianness32bit(block_offset + 0x04, sf)) + if (guess_endian32(block_offset + 0x04, sf)) block_size = read_u32be(block_offset + 0x04, sf); else block_size = read_u32le(block_offset + 0x04, sf); - if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ - size_t ima_samples = read_s32(block_offset + 0x08, sf); - size_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels; + if (block_size == 0 || block_size == -1) + break; + + if (id == get_id32be("1SNd") || id == get_id32be("SNDC")) { + int32_t ima_samples = read_s32(block_offset + 0x08, sf); + int32_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels; if (ima_samples == expected_samples) { return 1; /* has ADPCM hist (hopefully) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 8aae825a5..6474fc3bd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -260,8 +260,10 @@ static VGMSTREAM* parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t targe } else { /* streamed asset */ astFile = open_streamfile_by_ext(sf, "ast"); - if (!astFile) + if (!astFile) { + vgm_logi("EA ABK: .ast file not found (find and put together)\n"); goto fail; + } if (read_32bitBE(0x00, astFile) != 0x53313053) /* "S10S" */ goto fail; @@ -604,7 +606,7 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track /*, int num_track } } - VGM_LOG("No MPF/MUS pair specified for %s.\n", file_name); + vgm_logi("EA MPF: .mus file not found (find and put together)\n"); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 64b3ea7b0..851bfe6c6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -127,8 +127,8 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE* sf, off_t offset, int target_str static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offset, int max_length, int bnk_version); static uint32_t read_patch(STREAMFILE* sf, off_t* offset); static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* sf, off_t start_offset, const ea_header* ea); -static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int is_bnk, int standalone); -static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset, VGMSTREAM* vgmstream, int standalone); +static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int is_bnk); +static void update_ea_stream_size(STREAMFILE* sf, off_t start_offset, VGMSTREAM* vgmstream); /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) { @@ -150,9 +150,8 @@ VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) { * .hab: GoldenEye - Rogue Agent (inside .big) * .xsf: 007 - Agent Under Fire (Xbox) * .gsf: 007 - Everything or Nothing (GC) - * .mus: map/mpf+mus only? * (extensionless): SSX (PS2) (inside .big) */ - if (!check_extensions(sf,"asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) + if (!check_extensions(sf,"asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,")) goto fail; /* check header */ @@ -284,10 +283,9 @@ VGMSTREAM* init_vgmstream_ea_bnk(STREAMFILE* sf) { /* check extension */ /* .bnk: common * .sdt: Harry Potter games - * .mus: streams/jingles (rare) * .abk: GoldenEye - Rogue Agent * .ast: FIFA 2004 (inside .big) */ - if (!check_extensions(sf,"bnk,sdt,mus,abk,ast")) + if (!check_extensions(sf,"bnk,sdt,abk,ast")) goto fail; if (target_stream == 0) target_stream = 1; @@ -428,8 +426,10 @@ VGMSTREAM* init_vgmstream_ea_abk(STREAMFILE* sf) { case 0x02: astData = open_streamfile_by_ext(sf, "ast"); - if (!astData) + if (!astData) { + vgm_logi("EA ABK: .ast file not found (find and put together)\n"); goto fail; + } /* looped sounds basically consist of two independent segments * the first one is loop start, the second one is loop body */ @@ -737,7 +737,7 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track /*, int num_track } } - VGM_LOG("No MPF/MUS pair specified for %s.\n", file_name); + vgm_logi("EA MPF: .mus file not found (find and put together)\n"); return NULL; } @@ -1130,7 +1130,7 @@ static VGMSTREAM* parse_schl_block(STREAMFILE* sf, off_t offset, int standalone) start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ /* rest is common */ - return init_vgmstream_ea_variable_header(sf, &ea, start_offset, 0, standalone); + return init_vgmstream_ea_variable_header(sf, &ea, start_offset, 0); fail: return NULL; @@ -1221,7 +1221,7 @@ static VGMSTREAM* parse_bnk_header(STREAMFILE* sf, off_t offset, int target_stre start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */ /* rest is common */ - vgmstream = init_vgmstream_ea_variable_header(sf, &ea, start_offset, bnk_version, 0); + vgmstream = init_vgmstream_ea_variable_header(sf, &ea, start_offset, bnk_version); if (!vgmstream) goto fail; if (!is_embedded) { vgmstream->num_streams = real_bnk_sounds; @@ -1234,7 +1234,7 @@ fail: } /* inits VGMSTREAM from a EA header */ -static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int bnk_version, int standalone) { +static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int bnk_version) { VGMSTREAM* vgmstream = NULL; int i, ch; int is_bnk = bnk_version; @@ -1490,7 +1490,7 @@ static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* e /* TODO: Figure out how to get stream size for BNK sounds */ } else { - update_ea_stream_size_and_samples(sf, start_offset, vgmstream, standalone); + update_ea_stream_size(sf, start_offset, vgmstream); } return vgmstream; @@ -1901,18 +1901,18 @@ fail: return 0; } -static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset, VGMSTREAM *vgmstream, int standalone) { +static void update_ea_stream_size(STREAMFILE* sf, off_t start_offset, VGMSTREAM *vgmstream) { uint32_t block_id; - int32_t num_samples = 0; size_t stream_size = 0, file_size; - int multiple_schl = 0; - - file_size = get_streamfile_size(sf); /* formats with custom codecs */ - if (vgmstream->layout_type != layout_blocked_ea_schl) { + if (vgmstream->layout_type != layout_blocked_ea_schl) return; - } + + if (vgmstream->stream_size != 0) + return; + + file_size = get_streamfile_size(sf); /* manually read totals */ vgmstream->next_block_offset = start_offset; @@ -1923,20 +1923,10 @@ static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset block_id = read_32bitBE(vgmstream->current_block_offset + 0x00, sf); if (block_id == EA_BLOCKID_END) { /* banks should never contain movie "SHxx" */ - if (!standalone) - break; - } - else if (block_id == EA_BLOCKID_HEADER) { /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ - multiple_schl = 1; + break; } if (vgmstream->current_block_samples > 0) { - /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this. - * Get total samples by parsing block headers, needed when multiple files are stitched together. - * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped - * music (.map/lin). Subfiles always share header, except num_samples. */ - num_samples += vgmstream->current_block_samples; - /* stream size is almost never provided in bank files so we have to calc it manually */ stream_size += vgmstream->next_block_offset - vgmstream->ch[0].offset; } @@ -1944,17 +1934,7 @@ static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset /* reset once we're done */ block_update_ea_schl(start_offset, vgmstream); - - /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */ - if (standalone && multiple_schl) { - VGM_LOG("EA SCHl: multiple SCHl found\n"); - if (num_samples > vgmstream->num_samples) { - vgmstream->num_samples = num_samples; - } - } - - if (vgmstream->stream_size == 0) - vgmstream->stream_size = stream_size; + vgmstream->stream_size = stream_size; } /* find data start offset inside the first SCDl; not very elegant but oh well */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c index 33f7b18a4..9f8171295 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c @@ -1,118 +1,155 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - -/* Possibly the same as EA_CODEC_x in variable SCHl */ -#define EA_CODEC_PCM 0x00 -#define EA_CODEC_IMA 0x02 - -typedef struct { - int8_t version; - int8_t bps; - int8_t channels; - int8_t codec; - int16_t sample_rate; - int32_t num_samples; - - int big_endian; - int loop_flag; -} ea_fixed_header; - -static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset); - - -/* EA SCHl with fixed header - from EA games (~1997? ex. NHL 97 PC) */ -VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - size_t header_size; - ea_fixed_header ea = {0}; - - - /* checks */ - /* .asf: original - * .lasf: fake for plugins */ - if (!check_extensions(streamFile,"asf,lasf")) - goto fail; - - /* check header (see ea_schl.c for more info about blocks) */ - if (read_32bitBE(0x00,streamFile) != 0x5343486C) /* "SCHl" */ - goto fail; - - header_size = read_32bitLE(0x04,streamFile); - - if (!parse_fixed_header(streamFile,&ea, 0x08)) - goto fail; - - start_offset = header_size; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = ea.sample_rate; - vgmstream->num_samples = ea.num_samples; - //vgmstream->loop_start_sample = ea.loop_start; - //vgmstream->loop_end_sample = ea.loop_end; - - vgmstream->codec_endian = ea.big_endian; - - vgmstream->meta_type = meta_EA_SCHL_fixed; - - vgmstream->layout_type = layout_blocked_ea_schl; - - switch (ea.codec) { - case EA_CODEC_PCM: - vgmstream->coding_type = ea.bps==8 ? coding_PCM8 : (ea.big_endian ? coding_PCM16BE : coding_PCM16LE); - break; - - case EA_CODEC_IMA: - vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ - break; - - default: - VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec); - goto fail; - } - - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset) { - off_t offset = begin_offset; - - if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */ - read_32bitBE(offset+0x38, streamFile) != 0x544D706C) /* "TMpl" */ - goto fail; - - offset += 0x3c; /* after TMpl */ - ea->version = read_8bit(offset+0x00, streamFile); - ea->bps = read_8bit(offset+0x01, streamFile); - ea->channels = read_8bit(offset+0x02, streamFile); - ea->codec = read_8bit(offset+0x03, streamFile); - VGM_ASSERT(read_16bitLE(offset+0x04, streamFile) != 0, "EA SCHl fixed: unknown1 found\n"); - /* 0x04(16): unknown */ - ea->sample_rate = (uint16_t)read_16bitLE(offset+0x06, streamFile); - ea->num_samples = read_32bitLE(offset+0x08, streamFile); - VGM_ASSERT(read_32bitLE(offset+0x0c, streamFile) != -1, "EA SCHl fixed: unknown2 found\n"); /* loop start? */ - VGM_ASSERT(read_32bitLE(offset+0x10, streamFile) != -1, "EA SCHl fixed: unknown3 found\n"); /* loop end? */ - VGM_ASSERT(read_32bitLE(offset+0x14, streamFile) != 0, "EA SCHl fixed: unknown4 found\n"); /* data start? */ - VGM_ASSERT(read_32bitLE(offset+0x18, streamFile) != -1, "EA SCHl fixed: unknown5 found\n"); - VGM_ASSERT(read_32bitLE(offset+0x1c, streamFile) != 0x7F, "EA SCHl fixed: unknown6 found\n"); - - //ea->loop_flag = (ea->loop_end_sample); - - return 1; - -fail: - return 0; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +/* Possibly the same as EA_CODEC_x in variable SCHl */ +#define EA_CODEC_PCM 0x00 +#define EA_CODEC_IMA 0x02 +#define EA_CODEC_PSX 0x06 + +typedef struct { + int8_t version; + int8_t bps; + int8_t channels; + int8_t codec; + int sample_rate; + int32_t num_samples; + + int big_endian; + int loop_flag; +} ea_fixed_header; + +static int parse_fixed_header(STREAMFILE* sf, ea_fixed_header* ea); + + +/* EA SCHl with fixed header - from EA games (~1997?) */ +VGMSTREAM* init_vgmstream_ea_schl_fixed(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + size_t header_size; + ea_fixed_header ea = {0}; + + + /* checks */ + if (!is_id32be(0x00,sf, "SCHl")) + goto fail; + + /* .asf: original [NHK 97 (PC)] + * .lasf: fake for plugins + * .cnk: ps1 [NBA Live 97 (PS1)] */ + if (!check_extensions(sf,"asf,lasf,cnk")) + goto fail; + + /* see ea_schl.c for more info about blocks */ + //TODO: handle SCCl? [NBA Live 97 (PS1)] + + header_size = read_u32le(0x04,sf); + + if (!parse_fixed_header(sf, &ea)) + goto fail; + + start_offset = header_size; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = ea.sample_rate; + vgmstream->num_samples = ea.num_samples; + //vgmstream->loop_start_sample = ea.loop_start; + //vgmstream->loop_end_sample = ea.loop_end; + + vgmstream->codec_endian = ea.big_endian; + + vgmstream->meta_type = meta_EA_SCHL_fixed; + vgmstream->layout_type = layout_blocked_ea_schl; + + switch (ea.codec) { + case EA_CODEC_PCM: + vgmstream->coding_type = ea.bps==8 ? coding_PCM8 : (ea.big_endian ? coding_PCM16BE : coding_PCM16LE); + break; + + case EA_CODEC_IMA: + vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ + break; + + case EA_CODEC_PSX: + vgmstream->coding_type = coding_PSX; + break; + + default: + VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec); + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +static int parse_fixed_header(STREAMFILE* sf, ea_fixed_header* ea) { + uint32_t offset = 0x00, size = 0; + + if (is_id32be(offset+0x08, sf, "PATl")) + offset = 0x08; + else if (is_id32be(offset+0x0c, sf, "PATl")) + offset = 0x0c; /* extra field in PS1 */ + else + goto fail; + + size = read_u32le(offset+0x34, sf); + if (size == 0x20 && is_id32be(offset+0x38, sf, "TMpl")) { /* PC LE? */ + offset += 0x3c; + + ea->version = read_u8 (offset+0x00, sf); + ea->bps = read_u8 (offset+0x01, sf); + ea->channels = read_u8 (offset+0x02, sf); + ea->codec = read_u8 (offset+0x03, sf); + /* 0x04: 0? */ + ea->sample_rate = read_u16le(offset+0x06, sf); + ea->num_samples = read_s32le(offset+0x08, sf); + /* 0x0c: -1? loop_start? */ + /* 0x10: -1? loop_end? */ + /* 0x14: 0? data start? */ + /* 0x18: -1? */ + /* 0x1c: volume? (always 128) */ + } + else if (size == 0x38 && is_id32be(offset+0x38, sf, "TMxl")) { /* PSX LE? */ + offset += 0x3c; + + ea->version = read_u8 (offset+0x00, sf); + ea->bps = read_u8 (offset+0x01, sf); + ea->channels = read_u8 (offset+0x02, sf); + ea->codec = read_u8 (offset+0x03, sf); + /* 0x04: 0? */ + ea->sample_rate = read_u16le(offset+0x06, sf); + /* 0x08: 0x20C? */ + ea->num_samples = read_s32le(offset+0x0c, sf); + /* 0x10: -1? loop_start? */ + /* 0x14: -1? loop_end? */ + /* 0x18: 0x20C? */ + /* 0x1c: 0? */ + /* 0x20: 0? */ + /* 0x24: 0? */ + /* 0x28: -1? */ + /* 0x2c: -1? */ + /* 0x30: -1? */ + /* 0x34: volume? (always 128) */ + } + else { + goto fail; + } + + //ea->loop_flag = (ea->loop_end_sample); + + return 1; +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c index 1ecf83728..5075f34c6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c @@ -36,7 +36,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) { goto fail; } - temp_sf = setup_ogg_vorbis_streamfile(sf, cfg); + temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); if (!temp_sf) goto fail; vgmstream = init_vgmstream_ogg_vorbis(temp_sf); @@ -53,7 +53,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) { goto fail; } - temp_sf = setup_ogg_vorbis_streamfile(sf, cfg); + temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); if (!temp_sf) goto fail; #ifdef VGM_USE_FFMPEG //TODO: allow MP3 without FFmpeg @@ -72,7 +72,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) { goto fail; } - temp_sf = setup_ogg_vorbis_streamfile(sf, cfg); + temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); if (!temp_sf) goto fail; vgmstream = init_vgmstream_riff(temp_sf); @@ -80,12 +80,13 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) { return vgmstream; } - if (check_extensions(sf,"bgm")) { + if (check_extensions(sf,"bgm,mse,koe")) { + /* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)] + * .bgm: BGM, .mse: SE, .koe: Voice */ uint8_t keybuf[0x100]; size_t key_size; off_t start; - /* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)] */ if (id != get_id32be("RIFF")) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index ae6fcc643..2d62556cb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -221,7 +221,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) { } else { /* subsong header for normal files */ - stream_header_size = (uint16_t)read_16bitLE(header_offset+0x00,sf); + stream_header_size = read_u16le(header_offset+0x00,sf); fsb.name_offset = header_offset+0x02; fsb.name_size = 0x20-0x02; fsb.num_samples = read_32bitLE(header_offset+0x20,sf); @@ -231,7 +231,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) { fsb.mode = read_32bitLE(header_offset+0x30,sf); fsb.sample_rate = read_32bitLE(header_offset+0x34,sf); /* 0x38: defvol, 0x3a: defpan, 0x3c: defpri */ - fsb.channels = read_16bitLE(header_offset+0x3e,sf); + fsb.channels = read_u16le(header_offset+0x3e,sf); /* FSB3.1/4: * 0x40: mindistance, 0x44: maxdistance, 0x48: varfreq/size_32bits * 0x4c: varvol, 0x4e: fsb.varpan */ @@ -287,9 +287,9 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) { //;VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n"); /* sometimes there is garbage at the end or missing bytes due to improper ripping */ - VGM_ASSERT(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != sf->get_size(sf), + vgm_asserti(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != get_streamfile_size(sf), "FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n", - fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, sf->get_size(sf)); + fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, get_streamfile_size(sf)); /* autodetect unwanted loops */ { @@ -319,7 +319,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) { fsb.loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); /* disabled manually */ if (fsb.loop_flag && !enable_loop && full_loop && is_small) { - VGM_LOG("FSB: disable unwanted loop\n"); + VGM_LOG("FSB: disabled unwanted loop\n"); fsb.loop_flag = 0; } } @@ -426,8 +426,8 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) { /* get libcelt version (set in the first subsong only, but try all extradata just in case) */ if (fsb.first_extradata_offset || fsb.extradata_offset) { uint32_t lib = fsb.first_extradata_offset ? - (uint32_t)read_32bitLE(fsb.first_extradata_offset, sf) : - (uint32_t)read_32bitLE(fsb.extradata_offset, sf);; + read_u32le(fsb.first_extradata_offset, sf) : + read_u32le(fsb.extradata_offset, sf); switch(lib) { case 0x80000009: is_new_lib = 0; break; /* War Thunder (PC) */ case 0x80000010: is_new_lib = 1; break; /* Vessel (PC) */ @@ -436,7 +436,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) { } else { /* split FSBs? try to guess from observed bitstreams */ - uint16_t frame = (uint16_t)read_16bitBE(fsb.stream_offset+0x04+0x04,sf); + uint16_t frame = read_u16be(fsb.stream_offset+0x04+0x04,sf); if ((frame & 0xF000) == 0x6000 || frame == 0xFFFE) { is_new_lib = 1; } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 95e8055ae..37e58d296 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -3,11 +3,14 @@ #include "../coding/coding.h" #include "../coding/hca_decoder_clhca.h" -//#define HCA_BRUTEFORCE -#ifdef HCA_BRUTEFORCE -static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey); +#ifdef VGM_DEBUG_OUTPUT + //#define HCA_BRUTEFORCE + #ifdef HCA_BRUTEFORCE + #include "hca_bf.h" + #endif #endif -static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey); + +static int find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey); /* CRI HCA - streamed audio from CRI ADX2/Atom middleware */ @@ -57,7 +60,9 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) { } #ifdef HCA_BRUTEFORCE else if (1) { - bruteforce_hca_key(sf, hca_data, &keycode, subkey); + int ok = find_hca_key(hca_data, &keycode, subkey); + if (!ok) + bruteforce_hca_key(sf, hca_data, &keycode, subkey); } #endif else { @@ -123,60 +128,32 @@ fail: } -static inline void test_key(hca_codec_data* hca_data, uint64_t key, uint16_t subkey, int* best_score, uint64_t* best_keycode) { - int score; - - //;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x\n", - // (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey); - - if (subkey) { - key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) ); - } - - score = test_hca_key(hca_data, (unsigned long long)key); - - //;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n", - // (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score); - - /* wrong key */ - if (score < 0) - return; - - //;VGM_LOG("HCA: ok key=%08x%08x, subkey=%04x, score=%i\n", - // (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score); - - /* update if something better is found */ - if (*best_score <= 0 || (score < *best_score && score > 0)) { - *best_score = score; - *best_keycode = key; - } -} - -/* try to find the decryption key from a list. */ -static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey) { - const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info); - int best_score = -1; +/* try to find the decryption key from a list */ +static int find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey) { + const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_list[0]); int i; + hca_keytest_t hk = {0}; - *p_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */ + hk.best_key = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */ + hk.subkey = subkey; for (i = 0; i < keys_length; i++) { - uint64_t key = hcakey_list[i].key; + hk.key = hcakey_list[i].key; - test_key(hca_data, key, subkey, &best_score, p_keycode); - if (best_score == 1) + test_hca_key(hca_data, &hk); + if (hk.best_score == 1) goto done; #if 0 { int j; - size_t subkeys_size = hcakey_list[i].subkeys_size; - const uint16_t *subkeys = hcakey_list[i].subkeys; + const uint16_t* subkeys = hcakey_list[i].subkeys; if (subkeys_size > 0 && subkey == 0) { for (j = 0; j < subkeys_size; j++) { - test_key(hca_data, key, subkeys[j], &best_score, p_keycode); - if (best_score == 1) + hk.subkey = subkeys[j]; + test_hca_key(hca_data, &hk); + if (hk.best_score == 1) goto done; } } @@ -185,201 +162,9 @@ static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t } done: - VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n", - (uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score); - vgm_asserti(best_score < 0, "HCA: decryption key not found\n"); + *p_keycode = hk.best_key; + VGM_ASSERT(hk.best_score > 1, "HCA: best key=%08x%08x (score=%i)\n", + (uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), hk.best_score); + vgm_asserti(hk.best_score <= 0, "HCA: decryption key not found\n"); + return hk.best_score > 0; } - -#ifdef HCA_BRUTEFORCE -typedef enum { - HBF_TYPE_64LE_1, - HBF_TYPE_64BE_1, - HBF_TYPE_32LE_1, - HBF_TYPE_32BE_1, - HBF_TYPE_64LE_4, - HBF_TYPE_64BE_4, - HBF_TYPE_32LE_4, - HBF_TYPE_32BE_4, -} HBF_type_t; - -/* Bruteforce binary keys in executables and similar files, mainly for some mobile games. - * Kinda slow but acceptable for ~20MB exes, not very optimized. Unity usually has keys - * in plaintext (inside levelX or other base files) instead though. */ -static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey, HBF_type_t type) { - STREAMFILE* sf_keys = NULL; - uint8_t* buf = NULL; - int best_score = 0xFFFFFF, cur_score; - off_t keys_size, bytes; - int pos; - uint64_t old_key = 0; - uint64_t key = 0; - - - /* load whole file in memory for performance (exes with keys shouldn't be too big) */ - sf_keys = open_streamfile_by_filename(sf, "keys.bin"); - if (!sf_keys) return; - - VGM_LOG("HCA: test keys.bin (type %i)\n", type); - *p_keycode = 0; - - keys_size = get_streamfile_size(sf_keys); - - buf = malloc(keys_size); - if (!buf) goto done; - - bytes = read_streamfile(buf, 0, keys_size, sf_keys); - if (bytes != keys_size) goto done; - - VGM_LOG("HCA: start\n"); - - pos = 0; - while (pos < keys_size - 4) { - VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos); - - /* keys are usually u64le but other orders may exist */ - switch(type) { - case HBF_TYPE_64LE_1: key = get_u64le(buf + pos); pos += 0x01; break; - case HBF_TYPE_64BE_1: key = get_u64be(buf + pos); pos += 0x01; break; - case HBF_TYPE_32LE_1: key = get_u32le(buf + pos); pos += 0x01; break; - case HBF_TYPE_32BE_1: key = get_u32be(buf + pos); pos += 0x01; break; - case HBF_TYPE_64LE_4: key = get_u64le(buf + pos); pos += 0x04; break; - case HBF_TYPE_64BE_4: key = get_u64be(buf + pos); pos += 0x04; break; - case HBF_TYPE_32LE_4: key = get_u32le(buf + pos); pos += 0x04; break; - case HBF_TYPE_32BE_4: key = get_u32be(buf + pos); pos += 0x04; break; - default: key = 0; pos = keys_size; break; - } - - if (key == 0 || key == old_key) - continue; - old_key = key; - - cur_score = 0; - test_key(hca_data, key, subkey, &cur_score, p_keycode); - if (cur_score == 1) { - best_score = cur_score; - goto done; - } - - if (cur_score > 0 && cur_score <= 500) { - VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n", - (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04); - if (best_score > cur_score) - best_score = cur_score; - } - } - -done: - if (best_score < 0 || best_score > 10000) { - *p_keycode = 0; - VGM_LOG("HCA: good key not found\n"); - } - else { - /* print key as p_keycode includes subkey */ - VGM_LOG("HCA: best key=%08x%08x (score=%i)\n", - (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), best_score); - } - - close_streamfile(sf_keys); - free(buf); -} - -static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1); -/* - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1); - - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4); -*/ -} - - -#include -//#include - -/* same as the above but for txt lines. */ -static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { - STREAMFILE* sf_keys = NULL; - uint8_t* buf = NULL; - int best_score = 0xFFFFFF, cur_score; - off_t keys_size, bytes; - int i = 0, pos; - char line[1024]; - - - - /* load whole file in memory for performance (exes with keys shouldn't be too big) */ - sf_keys = open_streamfile_by_filename(sf, "keys.txt"); - if (!sf_keys) return; - - VGM_LOG("HCA: test keys.txt\n"); - *p_keycode = 0; - - keys_size = get_streamfile_size(sf_keys); - - buf = malloc(keys_size); - if (!buf) goto done; - - bytes = read_streamfile(buf, 0, keys_size, sf_keys); - if (bytes != keys_size) goto done; - - VGM_LOG("HCA: start\n"); - - pos = 0; - while (pos < keys_size) { - int bytes_read, line_ok, count; - uint64_t key = 0; - - bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok); - pos += bytes_read; - if (!line_ok) continue; /* line too long */ - - count = sscanf(line, "%" SCNd64, &key); - if (count != 1) continue; - - VGM_ASSERT(pos % 10000 == 0, "HCA: count %i...\n", i); - - if (key == 0) - continue; - i++; - - cur_score = 0; - test_key(hca_data, key, subkey, &cur_score, p_keycode); - if (cur_score == 1) - goto done; - - if (cur_score > 0 && cur_score <= 500) { - VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n", - (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04); - if (best_score > cur_score) - best_score = cur_score; - } - } - -done: - VGM_LOG("HCA: done %i keys.txt\n", i); - VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n", - (uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score); - VGM_ASSERT(best_score < 0, "HCA: key not found\n"); - if (best_score < 0 || best_score > 10000) - *p_keycode = 0; - - close_streamfile(sf_keys); - free(buf); -} - -static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { - bruteforce_hca_key_bin(sf, hca_data, p_keycode, subkey); - if (*p_keycode != 0) - return; - - bruteforce_hca_key_txt(sf, hca_data, p_keycode, subkey); - if (*p_keycode != 0) - return; -} - -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_bf.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_bf.h new file mode 100644 index 000000000..8d43460a5 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_bf.h @@ -0,0 +1,257 @@ +#ifndef _HCA_BF_ +#define _HCA_BF_ + +#include "meta.h" +#include "../coding/coding.h" + +#ifdef HCA_BRUTEFORCE + +static void bruteforce_process_result(hca_keytest_t* hk, unsigned long long* p_keycode) { + *p_keycode = hk->best_key; + if (hk->best_score < 0 || hk->best_score > 10000) { + VGM_LOG("HCA BF: no good key found\n"); + } + else { + VGM_LOG("HCA BF: best key=%08x%08x (score=%i)\n", + (uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), hk->best_score); + } +} + +typedef enum { + HBF_TYPE_64LE_1, + HBF_TYPE_64BE_1, + HBF_TYPE_32LE_1, + HBF_TYPE_32BE_1, + HBF_TYPE_64LE_4, + HBF_TYPE_64BE_4, + HBF_TYPE_32LE_4, + HBF_TYPE_32BE_4, +} HBF_type_t; + +/* Bruteforce binary keys in executables and similar files, mainly for some mobile games. + * Kinda slow but acceptable for ~100MB exes, not very optimized. Unity usually has keys + * in plaintext (inside levelX or other base files) instead though, use test below. */ +static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey, HBF_type_t type) { + STREAMFILE* sf_keys = NULL; + uint8_t* buf = NULL; + uint32_t keys_size, bytes; + int pos, step; + uint64_t key = 0, old_key = 0; + hca_keytest_t hk = {0}; + + hk.subkey = subkey; + + + /* load whole file in memory for performance (exes with keys shouldn't be too big) */ + sf_keys = open_streamfile_by_filename(sf, "keys.bin"); + if (!sf_keys) return; + + VGM_LOG("HCA BF: test keys.bin (type %i)\n", type); + *p_keycode = 0; + + keys_size = get_streamfile_size(sf_keys); + + buf = malloc(keys_size); + if (!buf) { + VGM_LOG("HCA BF: key file too big!\n"); + goto done; + } + + bytes = read_streamfile(buf, 0, keys_size, sf_keys); + if (bytes != keys_size) goto done; + + VGM_LOG("HCA BF: start .bin\n"); + + switch(type) { + case HBF_TYPE_64LE_1: + case HBF_TYPE_64BE_1: + case HBF_TYPE_32LE_1: + case HBF_TYPE_32BE_1: step = 0x01; break; + case HBF_TYPE_64LE_4: + case HBF_TYPE_64BE_4: + case HBF_TYPE_32LE_4: + case HBF_TYPE_32BE_4: step = 0x04; break; + default: goto done; + } + + pos = 0; + while (pos < keys_size - 8) { + VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos); + + /* keys are usually u64le but other orders may exist */ + switch(type) { + case HBF_TYPE_64LE_1: key = get_u64le(buf + pos); break; + case HBF_TYPE_64BE_1: key = get_u64be(buf + pos); break; + case HBF_TYPE_32LE_1: key = get_u32le(buf + pos); break; + case HBF_TYPE_32BE_1: key = get_u32be(buf + pos); break; + case HBF_TYPE_64LE_4: key = get_u64le(buf + pos); break; + case HBF_TYPE_64BE_4: key = get_u64be(buf + pos); break; + case HBF_TYPE_32LE_4: key = get_u32le(buf + pos); break; + case HBF_TYPE_32BE_4: key = get_u32be(buf + pos); break; + default: goto done; + } + pos += step; + + if (key == 0 || key == old_key) + continue; + old_key = key; + + hk.key = key; + test_hca_key(hca_data, &hk); + if (hk.best_score == 1) + goto done; + } + +done: + bruteforce_process_result(&hk, p_keycode); + close_streamfile(sf_keys); + free(buf); +} + +static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { + bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1); +/* + bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1); + bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1); + bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1); + + bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4); + bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4); + bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4); + bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4); +*/ +} + + +#include +//#include + +/* same as the above but for txt lines. */ +static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { + STREAMFILE* sf_keys = NULL; + uint8_t* buf = NULL; + uint32_t keys_size, bytes; + char line[1024]; + int i = 0, pos; + uint64_t key = 0; + hca_keytest_t hk = {0}; + + hk.subkey = subkey; + + + /* load whole file in memory for performance (exes with keys shouldn't be too big) */ + sf_keys = open_streamfile_by_filename(sf, "keys.txt"); + if (!sf_keys) return; + + VGM_LOG("HCA BF: test keys.txt\n"); + *p_keycode = 0; + + keys_size = get_streamfile_size(sf_keys); + + buf = malloc(keys_size); + if (!buf) { + VGM_LOG("HCA BF: key file too big!\n"); + goto done; + } + + bytes = read_streamfile(buf, 0, keys_size, sf_keys); + if (bytes != keys_size) goto done; + + VGM_LOG("HCA BF: start .txt\n"); + + pos = 0; + while (pos < keys_size) { + int bytes_read, line_ok, count; + key = 0; + + bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok); + pos += bytes_read; + if (!line_ok) continue; /* line too long */ + + count = sscanf(line, "%" SCNd64, &key); + if (count != 1) continue; + + VGM_ASSERT(pos % 10000 == 0, "HCA: count %i...\n", i); + + if (key == 0) + continue; + i++; + + hk.key = key; + test_hca_key(hca_data, &hk); + if (hk.best_score == 1) + goto done; + } + +done: + bruteforce_process_result(&hk, p_keycode); + close_streamfile(sf_keys); + free(buf); +} + +/* same as the above but good ol' bruteforce numbers (useful for games with keys that are dates) */ +static void bruteforce_hca_key_num(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { + STREAMFILE* sf_keys = NULL; + uint32_t keys_size; + uint64_t min, max; + uint64_t key = 0; + hca_keytest_t hk = {0}; + + hk.subkey = subkey; + + + /* load whole file in memory for performance (exes with keys shouldn't be too big) */ + sf_keys = open_streamfile_by_filename(sf, "keys.num"); + if (!sf_keys) return; + + VGM_LOG("HCA BF: test keys.num\n"); + *p_keycode = 0; + + keys_size = get_streamfile_size(sf_keys); + + /* don't set too high as it does ~70000 keys per second, do the math */ + if (keys_size < 0x10) { + min = 0; + max = 0xFFFFFFFF; + } + else { + min = read_u64be(0x00, sf_keys); + max = read_u64be(0x08, sf_keys); + } + + VGM_LOG("HCA BF: start .num\n"); + + while (min < max) { + key = min; + + min++; + VGM_ASSERT(min % 0x100000 == 0, "HCA: count %x...\n", (uint32_t)min); + + hk.key = key; + test_hca_key(hca_data, &hk); + if (hk.best_score == 1) + goto done; + } + +done: + bruteforce_process_result(&hk, p_keycode); + close_streamfile(sf_keys); +} + +static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { + bruteforce_hca_key_bin(sf, hca_data, p_keycode, subkey); + if (*p_keycode != 0) + return; + + bruteforce_hca_key_txt(sf, hca_data, p_keycode, subkey); + if (*p_keycode != 0) + return; + + bruteforce_hca_key_num(sf, hca_data, p_keycode, subkey); + if (*p_keycode != 0) + return; +} + +#endif + +#endif /*_HCA_BF_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 759a4c27b..2708fa19f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -48,7 +48,7 @@ static const hcakey_info hcakey_list[] = { // - Ro-Kyu-Bu! Naisho no Shutter Chance (PSVita) {1234253142}, // 0000000049913556 - // Idolm@ster Cinderella Stage (iOS/Android) + // THE iDOLM@STER Cinderella Girls: Starlight Stage (iOS/Android) // Shadowverse (iOS/Android) {59751358413602}, // 00003657F27E3B22 @@ -67,16 +67,15 @@ static const hcakey_info hcakey_list[] = { // Sonic Runners (iOS/Android) {19910623}, // 00000000012FCFDF - // Fate/Grand Order (iOS/Android) base assets - {12345}, // 0000000000003039 - - // Fate/Grand Order (iOS/Android) download assets *unconfirmed - {9117927877783581796}, // 7E89631892EBF464 + // Fate/Grand Order (iOS/Android) + {12345}, // 0000000000003039 - base assets + {9117927877783581796}, // 7E89631892EBF464 - downloaded assets *unconfirmed // Raramagi (iOS/Android) {45719322}, // 0000000002B99F1A - // Idolm@ster Million Live (iOS/Android) + // THE iDOLM@STER Million Live! (iOS/Android) + // THE iDOLM@STER SideM GROWING STARS (Android) {765765765765765}, // 0002B875BC731A85 // Kurokishi to Shiro no Maou (iOS/Android) @@ -775,7 +774,48 @@ static const hcakey_info hcakey_list[] = { // Nogizaka 46 Fractal (Android) {984635491346198130}, // 0DAA20C336EEAE72 - + + // NEO: The World Ends With You (PC) + {53346933792338754}, // 00BD86C0EE8C7342 + + // THE iDOLM@STER Starlit Season (PS4/PC) + {0x1e03b570b6145d1d}, // BGM + {0x1da915aaa181a461}, // SE + {0x1c82b6ab7487a5ec}, // Voice + {0x6d275d3666c2f9c8}, // Sng001 + {0x0f53815df3044e6d}, // Sng002 + {0x158778e2e2fab347}, // Sng003 + {0x16b75e8b5247d46b}, // Sng004 + {0x157df8a6047048fc}, // Sng005 + {0x184d358b50b658d0}, // Sng006 + {0x157fb75af4ddd983}, // Sng007 + {0x404ba38c3e470827}, // Sng008 + {0x01d0b788a3b60d48}, // Sng009 + {0x021718d55d0960c9}, // Sng010 + {0x0021c5993d2b901c}, // Sng011 + {0x08237bcb9b711087}, // Sng012 + {0x01af60402e1228a5}, // Sng013 + {0x4eec18ab73a1a634}, // Sng014 + {0x1855099898b11ad9}, // Sng015 + {0x57ef8f2ea5d54db5}, // Sng016 + {0x17cc6975d67e2a1f}, // Sng017 + {0x0a5d0fc8cc5c4502}, // Sng018 + {0x198ea1a17416050b}, // Sng019 + {0x2aa3b8abad207a1e}, // Sng020 + {0x33d98a3a9f9bfdef}, // Sng026 + {0x2284fd5ca82c78f4}, // Sng027 + {0x178a76b6436d20f0}, // Sng028 + {0x3ff99f2fed65a1ed}, // Sng030 + + // Ulala: Idle Adventure (Android) + {20191022}, // 000000000134172E + + // Girls' Frontline: Project Neural Cloud (Android) + {210222522032314}, // 0000BF323EBFE0BA + + // Super Robot Wars 30 (PC) + {6734488621090458}, // 0017ECFB5201069A + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ikm.c b/Frameworks/vgmstream/vgmstream/src/meta/ikm.c index f73db803e..f32246b57 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ikm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ikm.c @@ -2,74 +2,50 @@ #include "../coding/coding.h" -/* IKM - MiCROViSiON PS2 container [Zwei (PS2)] */ -VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) { + VGMSTREAM* v = NULL; off_t start_offset; - int loop_flag, channel_count; + int loop_flag, channels; - - /* checks */ - if (!is_id32be(0x00,sf, "IKM\0")) - goto fail; - if (!check_extensions(sf,"ikm")) - goto fail; - - /* 0x20: type 03? */ if (!is_id32be(0x40,sf, "AST\0")) goto fail; loop_flag = (read_s32le(0x14, sf) > 0); - channel_count = read_s32le(0x50, sf); + channels = read_s32le(0x50, sf); start_offset = 0x800; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; + v = allocate_vgmstream(channels, loop_flag); + if (!v) goto fail; - vgmstream->meta_type = meta_IKM; - vgmstream->sample_rate = read_s32le(0x44, sf); - vgmstream->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channel_count); - vgmstream->loop_start_sample = read_s32le(0x14, sf); - vgmstream->loop_end_sample = read_s32le(0x18, sf); - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; /* @0x40 / channels */ + v->meta_type = meta_IKM; + v->sample_rate = read_s32le(0x44, sf); + v->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channels); + v->loop_start_sample = read_s32le(0x14, sf); + v->loop_end_sample = read_s32le(0x18, sf); + v->coding_type = coding_PSX; + v->layout_type = layout_interleave; + v->interleave_block_size = 0x10; /* @0x40 / channels */ - if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + if (!vgmstream_open_stream(v, sf, start_offset)) goto fail; - return vgmstream; + return v; fail: - close_vgmstream(vgmstream); + close_vgmstream(v); return NULL; } -/* IKM - MiCROViSiON PC container [Chaos Legion (PC), Legend of Galactic Heroes (PC)] */ -VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) { + VGMSTREAM* v = NULL; off_t start_offset; - - /* checks */ - if (!is_id32be(0x00,sf, "IKM\0")) - goto fail; - if (!check_extensions(sf,"ikm")) - goto fail; - /* 0x20: type 01? */ - /* find "OggS" start */ - if (is_id32be(0x30,sf, "OggS")) { + if (is_id32be(0x30,sf, "OggS")) start_offset = 0x30; /* Chaos Legion (PC) */ - } - else if (is_id32be(0x800,sf, "OggS")) { + else start_offset = 0x800; /* Legend of Galactic Heroes (PC) */ - } - else { - goto fail; - } - { ogg_vorbis_meta_info_t ovmi = {0}; @@ -81,31 +57,23 @@ VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) { ovmi.loop_flag = ovmi.loop_end > 0; ovmi.stream_size = read_s32le(0x24, sf); - vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); + v = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); + if (!v) goto fail; } - return vgmstream; + return v; fail: - close_vgmstream(vgmstream); + close_vgmstream(v); return NULL; } -/* IKM - MiCROViSiON PSP container [The Legend of Heroes: A Tear of Vermillion (PSP)] */ -VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) { + VGMSTREAM* v = NULL; STREAMFILE* temp_sf = NULL; off_t start_offset; size_t data_size; - - /* checks */ - if (!is_id32be(0x00,sf, "IKM\0")) - goto fail; - if (!check_extensions(sf,"ikm")) - goto fail; - - /* 0x20: type 00? */ if (!is_id32be(0x800,sf, "RIFF")) goto fail; @@ -116,16 +84,43 @@ VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) { temp_sf = setup_subfile_streamfile(sf, start_offset, data_size, "at3"); if (!temp_sf) goto fail; - vgmstream = init_vgmstream_riff(temp_sf); - if (!vgmstream) goto fail; + v = init_vgmstream_riff(temp_sf); + if (!v) goto fail; - vgmstream->meta_type = meta_IKM; + v->meta_type = meta_IKM; close_streamfile(temp_sf); - return vgmstream; + return v; fail: close_streamfile(temp_sf); - close_vgmstream(vgmstream); + close_vgmstream(v); + return NULL; +} + +/* IKM - MiCROViSiON container */ +VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf) { + uint32_t type; + + /* checks */ + if (!is_id32be(0x00,sf, "IKM\0")) + goto fail; + + if (!check_extensions(sf,"ikm")) + goto fail; + + type = read_u32le(0x20, sf); + switch(type) { + case 0x00: /* The Legend of Heroes: A Tear of Vermillion (PSP) */ + return init_vgmstream_ikm_psp(sf); + case 0x01: /* Chaos Legion (PC), Legend of Galactic Heroes (PC) */ + return init_vgmstream_ikm_pc(sf); + case 0x03: /* Zwei (PS2) */ + return init_vgmstream_ikm_ps2(sf); + default: + goto fail; + } + +fail: return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/lopu.c b/Frameworks/vgmstream/vgmstream/src/meta/lopu_fb.c similarity index 84% rename from Frameworks/vgmstream/vgmstream/src/meta/lopu.c rename to Frameworks/vgmstream/vgmstream/src/meta/lopu_fb.c index f62853197..67ed6d6ca 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/lopu.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/lopu_fb.c @@ -30,7 +30,13 @@ VGMSTREAM* init_vgmstream_lopu_fb(STREAMFILE* sf) { /* rest: null */ loop_flag = (loop_end > 0); /* -1 if no loop */ + + /* Must remove skip or some files decode past limit. loop_end equals to PC (.ogg) version's max + * samples, but in some case (stage_park) goes slightly past max but is still valid. + * (loops shouldn't remove skip as they wouldn't match PC/bgm.txt loop times) */ num_samples -= skip; + if (num_samples < loop_end) + num_samples = loop_end; /* build the VGMSTREAM */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/lpcm_shade.c b/Frameworks/vgmstream/vgmstream/src/meta/lpcm_shade.c new file mode 100644 index 000000000..2f2b09139 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/lpcm_shade.c @@ -0,0 +1,51 @@ +#include "meta.h" + +/* LPCM - from Shade's 'Shade game library' (ShdLib) [Ah! My Goddess (PS2), Warship Gunner (PS2)] */ +VGMSTREAM* init_vgmstream_lpcm_shade(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + uint32_t loop_flag, channels; + + /* checks */ + if (!is_id32be(0x00, sf, "LPCM")) + goto fail; + + /* .w: real extension + * .lpcm: fake (header id) */ + if (!check_extensions(sf, "w,lpcm")) + goto fail; + + /* extra checks since header is kind of simple */ + if (read_s32le(0x04,sf) * 0x02 * 2 > get_streamfile_size(sf)) /* data size is less than total samples */ + goto fail; + if (read_u32le(0x10,sf) != 0) /* just in case */ + goto fail; + + start_offset = 0x800; /* assumed, closer to num_samples */ + + loop_flag = read_s32le(0x8,sf) != 0; + channels = 2; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_LPCM_SHADE; + vgmstream->sample_rate = 48000; + + vgmstream->num_samples = read_s32le(0x4,sf); + vgmstream->loop_start_sample = read_s32le(0x8,sf); + vgmstream->loop_end_sample = read_s32le(0xc,sf); + + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; +fail: + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index c2e097fae..628079ffe 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -222,9 +222,7 @@ VGMSTREAM * init_vgmstream_leg(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_filp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ikm_ps2(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ikm_pc(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ikm_psp(STREAMFILE * streamFile); +VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf); VGMSTREAM * init_vgmstream_sfs(STREAMFILE * streamFile); @@ -444,7 +442,9 @@ VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_his(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE* streamFile); +VGMSTREAM* init_vgmstream_ast_mv(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_ast_mmv(STREAMFILE* sf); VGMSTREAM * init_vgmstream_dmsg(STREAMFILE* streamFile); @@ -480,7 +480,7 @@ VGMSTREAM * init_vgmstream_ps2_wad(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE* streamFile); +VGMSTREAM* init_vgmstream_lpcm_shade(STREAMFILE* sf); VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE* streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index fea8a905f..f587967c2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -131,13 +131,38 @@ static const uint32_t xiph_mappings[] = { }; -/* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */ -static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { +static VGMSTREAM* _init_vgmstream_ogg_vorbis_cfg_ovmi(STREAMFILE* sf, ogg_vorbis_io_config_data* cfg, ogg_vorbis_meta_info_t* ovmi) { VGMSTREAM* vgmstream = NULL; STREAMFILE* temp_sf = NULL; + + + if (cfg->is_encrypted) { + temp_sf = setup_ogg_vorbis_streamfile(sf, cfg); + if (!temp_sf) goto fail; + } + + if (ovmi->meta_type == 0) { + if (cfg->is_encrypted || ovmi->decryption_callback != NULL) + ovmi->meta_type = meta_OGG_encrypted; + else + ovmi->meta_type = meta_OGG_VORBIS; + } + + vgmstream = _init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, cfg->start, ovmi); + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + return NULL; +} + + +/* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */ +static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) { ogg_vorbis_io_config_data cfg = {0}; ogg_vorbis_meta_info_t ovmi = {0}; - off_t start_offset = 0; int is_ogg = 0; int is_um3 = 0; @@ -171,7 +196,8 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { is_sngw = 1; } else if (check_extensions(sf,"isd")) { /* .isd: Inti Creates PC games */ is_isd = 1; - } else if (check_extensions(sf,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */ + } else if (check_extensions(sf,"rpgmvo,ogg_")) { + /* .rpgmvo: RPG Maker MV games (PC), .ogg_: RPG Maker MZ games (PC) */ is_rpgmvo = 1; } else if (check_extensions(sf,"eno")) { /* .eno: Metronomicon (PC) */ is_eno = 1; @@ -238,7 +264,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { ovmi.decryption_callback = kovs_ogg_decryption_callback; ovmi.meta_type = meta_OGG_KOVS; - start_offset = 0x20; + cfg.start = 0x20; } if (is_sngw) { /* [Capcom's MT Framework PC games] */ @@ -321,7 +347,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { if (sf_isl) { STREAMFILE* dec_sf = NULL; - dec_sf = setup_ogg_vorbis_streamfile(sf_isl, cfg); + dec_sf = setup_ogg_vorbis_streamfile(sf_isl, &cfg); if (dec_sf) { off_t loop_offset; char basename[PATH_LIMIT]; @@ -353,22 +379,22 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { } } - if (is_rpgmvo) { /* [RPG Maker MV (PC)] */ + if (is_rpgmvo) { /* [RPG Maker MV (PC), RPG Maker MZ (PC)] */ if (read_32bitBE(0x00,sf) != 0x5250474D && /* "RPGM" */ read_32bitBE(0x00,sf) != 0x56000000) { /* "V\0\0\0" */ goto fail; } ovmi.decryption_callback = rpgmvo_ogg_decryption_callback; - start_offset = 0x10; + cfg.start = 0x10; } if (is_eno) { /* [Metronomicon (PC)] */ /* first byte probably derives into key, but this works too */ - cfg.key[0] = (uint8_t)read_8bit(0x05,sf); /* regular ogg have a zero at this offset = easy key */ + cfg.key[0] = read_u8(0x05,sf); /* regular ogg have a zero at this offset = easy key */ cfg.key_len = 1; cfg.is_encrypted = 1; - start_offset = 0x01; /* "OggS" starts after key-thing */ + cfg.start = 0x01; /* "OggS" starts after key-thing */ } if (is_gwm) { /* [Adagio: Cloudburst (PC)] */ @@ -397,7 +423,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */ int i; /* found at file_size-1 but this works too (same key for most files but can vary) */ - uint8_t base_key = (uint8_t)read_8bit(0x04,sf) - 0x04; + uint8_t base_key = read_u8(0x04,sf) - 0x04; cfg.key_len = 256; for (i = 0; i < cfg.key_len; i++) { @@ -408,13 +434,13 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { } if (is_bgm) { /* [Fortissimo (PC)] */ - size_t file_size = get_streamfile_size(sf); + uint32_t file_size = get_streamfile_size(sf); uint8_t key[0x04]; uint32_t xor_be; - put_32bitLE(key, (uint32_t)file_size); + put_u32le(key, file_size); xor_be = get_u32be(key); - if ((read_32bitBE(0x00,sf) ^ xor_be) == 0x4F676753) { /* "OggS" */ + if ((read_u32be(0x00,sf) ^ xor_be) == get_id32be("OggS")) { int i; cfg.key_len = 4; for (i = 0; i < cfg.key_len; i++) { @@ -425,28 +451,80 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { } - if (cfg.is_encrypted) { - temp_sf = setup_ogg_vorbis_streamfile(sf, cfg); - if (!temp_sf) goto fail; - } - - if (ovmi.meta_type == 0) { - if (cfg.is_encrypted || ovmi.decryption_callback != NULL) - ovmi.meta_type = meta_OGG_encrypted; - else - ovmi.meta_type = meta_OGG_VORBIS; - } - - vgmstream = _init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, start_offset, &ovmi); - - close_streamfile(temp_sf); - return vgmstream; - + return _init_vgmstream_ogg_vorbis_cfg_ovmi(sf, &cfg, &ovmi); fail: - close_streamfile(temp_sf); return NULL; } +/* Ogg Vorbis - encrypted .ogg [Yumekoi Tensei (PC)] */ +static VGMSTREAM* _init_vgmstream_ogg_vorbis_tink(STREAMFILE* sf) { + ogg_vorbis_io_config_data cfg = {0}; + ogg_vorbis_meta_info_t ovmi = {0}; + uint32_t start; + + /* checks */ + if (is_id32be(0x00, sf, "Tink")) { + start = 0x00; + } + else if (is_id32be(0x0c, sf, "Tink")) { + ovmi.loop_start = read_u32le(0x00, sf); + ovmi.loop_end = read_u32le(0x04, sf); + ovmi.loop_flag = read_u32le(0x0c, sf); + ovmi.loop_end_found = 1; + start = 0x0c; + } + else { + goto fail; + } + + if (!check_extensions(sf,"u0")) + goto fail; + + cfg.is_encrypted = 1; + cfg.is_header_swap = 1; + cfg.start = start; + cfg.max_offset = 0xE1F; + cfg.key_len = 0xE1F; + + if (sizeof(cfg.key) < cfg.key_len) + goto fail; + + /* copy key */ + { + static const char* keystring = "BB3206F-F171-4885-A131-EC7FBA6FF491 Copyright 2004 Cyberworks \"TinkerBell\"., all rights reserved."; + int i, keystring_len; + start = 0; + + memset(cfg.key, 0, 0x04); + put_u8 (cfg.key + 0x04, 0x44); + + keystring_len = strlen(keystring) + 1; /* including null */ + for (i = 0x05; i < cfg.key_len; i += keystring_len) { + int copy = keystring_len; + if (i + copy > cfg.key_len) + copy = cfg.key_len - i; + memcpy(cfg.key + i, keystring, copy); + } + } + + return _init_vgmstream_ogg_vorbis_cfg_ovmi(sf, &cfg, &ovmi); +fail: + return NULL; +} + +static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) { + VGMSTREAM* v; + + v = _init_vgmstream_ogg_vorbis_common(sf); + if (v) return v; + + v = _init_vgmstream_ogg_vorbis_tink(sf); + if (v) return v; + + return NULL; +} + + static VGMSTREAM* _init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) { VGMSTREAM* vgmstream = NULL; ogg_vorbis_codec_data* data = NULL; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_streamfile.h index c7b1e1056..b767562eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_streamfile.h @@ -5,10 +5,12 @@ typedef struct { int is_encrypted; - uint8_t key[0x100]; + uint8_t key[0x1000]; size_t key_len; int is_nibble_swap; int is_header_swap; + uint32_t start; + uint32_t max_offset; } ogg_vorbis_io_config_data; typedef struct { @@ -19,20 +21,35 @@ typedef struct { static size_t ogg_vorbis_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, ogg_vorbis_io_data* data) { static const uint8_t header_swap[4] = { 0x4F,0x67,0x67,0x53 }; /* "OggS" */ - static const size_t header_size = 0x04; int i; size_t bytes = read_streamfile(dest, offset, length, sf); if (data->cfg.is_encrypted) { - for (i = 0; i < bytes; i++) { - if (data->cfg.is_header_swap && (offset + i) < header_size) { - dest[i] = header_swap[(offset + i) % header_size]; + int max = bytes; + int header_end = 0x04 + data->cfg.start; + + if (data->cfg.max_offset + data->cfg.start) { + if (offset > data->cfg.max_offset + data->cfg.start) { + max = 0; + } + else { + max = data->cfg.max_offset + data->cfg.start - offset; + if (max > bytes) + max = bytes; + } + } + + for (i = 0; i < max; i++) { + if (data->cfg.is_header_swap && + (offset + i) >= data->cfg.start && + (offset + i) < header_end) { + dest[i] = header_swap[(offset + i) % 0x04]; } else { if (!data->cfg.key_len && !data->cfg.is_nibble_swap) break; - if (data->cfg.key_len) - dest[i] ^= data->cfg.key[(offset + i) % data->cfg.key_len]; + if (data->cfg.key_len && (offset + i) >= data->cfg.start) + dest[i] ^= data->cfg.key[(offset + i - data->cfg.start) % data->cfg.key_len]; if (data->cfg.is_nibble_swap) dest[i] = ((dest[i] << 4) & 0xf0) | ((dest[i] >> 4) & 0x0f); } @@ -44,11 +61,11 @@ static size_t ogg_vorbis_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, si //todo maybe use generic decryption streamfile /* Decrypts Ogg Vorbis streams */ -static STREAMFILE* setup_ogg_vorbis_streamfile(STREAMFILE *sf, ogg_vorbis_io_config_data cfg) { +static STREAMFILE* setup_ogg_vorbis_streamfile(STREAMFILE *sf, ogg_vorbis_io_config_data* cfg) { STREAMFILE *new_sf = NULL; ogg_vorbis_io_data io_data = {0}; - io_data.cfg = cfg; /* memcpy */ + io_data.cfg = *cfg; /* memcpy */ new_sf = open_wrap_streamfile(sf); new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(ogg_vorbis_io_data), ogg_vorbis_io_read, NULL); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c index 454bd07bc..d05af43ed 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -30,7 +30,7 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of /* 0x80000002: 'offset info' chunk (seek table?), not seen */ - /* 'context info' chunk, rare [Famicom Detective Club (Switch)] */ + /* 'context info' chunk, rare [Famicom Detective Club (Switch), SINce Memories (Switch)] */ if (context_offset && read_u32le(offset + context_offset, sf) == 0x80000003) { /* maybe should give priority to external info? */ context_offset += offset; @@ -52,6 +52,12 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of multistream_offset = offset + 0x20; } + /* Opus can only do 48000 but some games store original rate [Grandia HD Collection, Lego Marvel] */ + if (sample_rate != 48000) { + VGM_LOG("OPUS: ignored non-standard sample rate of %i\n", sample_rate); + sample_rate = 48000; + } + /* 'data info' chunk */ data_offset += offset; @@ -69,8 +75,6 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of vgmstream->meta_type = meta_type; vgmstream->sample_rate = sample_rate; - if (vgmstream->sample_rate == 16000) - vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; @@ -124,6 +128,9 @@ VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf) { int num_samples, loop_start, loop_end; /* checks */ + if (read_u32le(0x00,sf) != 0x80000001) /* 'basic info' chunk */ + goto fail; + /* .opus: standard * .bgm: Cotton Reboot (Switch) */ if (!check_extensions(sf,"opus,lopus,bgm")) @@ -157,11 +164,11 @@ VGMSTREAM* init_vgmstream_opus_n1(STREAMFILE* sf) { int num_samples, loop_start, loop_end; /* checks */ - if (!check_extensions(sf,"opus,lopus")) - goto fail; if (!((read_u32be(0x04,sf) == 0x00000000 && read_u32be(0x0c,sf) == 0x00000000) || (read_u32be(0x04,sf) == 0xFFFFFFFF && read_u32be(0x0c,sf) == 0xFFFFFFFF))) goto fail; + if (!check_extensions(sf,"opus,lopus")) + goto fail; offset = 0x10; num_samples = 0; @@ -181,7 +188,7 @@ VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) { int channels; /* checks */ - if ( !check_extensions(sf,"opus,lopus")) + if (!check_extensions(sf,"opus,lopus")) goto fail; channels = read_32bitLE(0x04,sf); @@ -259,10 +266,10 @@ VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf) { int num_samples, loop_start = 0, loop_end = 0, loop_flag; /* checks */ - if (!check_extensions(sf,"nop")) + if (!is_id32be(0x00, sf, "sadf") || + !is_id32be(0x08, sf, "opus")) goto fail; - if (read_32bitBE(0x00, sf) != 0x73616466 || /* "sadf" */ - read_32bitBE(0x08, sf) != 0x6f707573) /* "opus" */ + if (!check_extensions(sf,"nop")) goto fail; offset = read_32bitLE(0x1c, sf); @@ -284,9 +291,9 @@ VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf) { int num_samples, loop_start, loop_end; /* checks */ - if ( !check_extensions(sf,"opus,lopus")) + if (read_u32be(0x08,sf) != 0x01000080) goto fail; - if (read_32bitBE(0x08,sf) != 0x01000080) + if ( !check_extensions(sf,"opus,lopus")) goto fail; offset = 0x08; @@ -308,11 +315,12 @@ VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf) { int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ + if (!is_id32be(0x00, sf, "OPUS")) + goto fail; + /* .opus: header ID (they only exist inside .nus3bank) */ if (!check_extensions(sf, "opus,lopus")) goto fail; - if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */ - goto fail; /* Here's an interesting quirk, OPUS header contains big endian values while the Nintendo Opus header and data that follows remain little endian as usual */ @@ -337,13 +345,13 @@ VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf) { int num_samples, loop_start = 0, loop_end = 0, loop_flag; /* checks */ + if (read_u32be(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */ + goto fail; /* .sps: Labyrinth of Refrain: Coven of Dusk (Switch) * .nlsd: Disgaea Refine (Switch), Ys VIII (Switch) * .at9: void tRrLM(); //Void Terrarium (Switch) */ if (!check_extensions(sf, "sps,nlsd,at9")) goto fail; - if (read_32bitBE(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */ - goto fail; num_samples = read_32bitLE(0x0C, sf); @@ -382,9 +390,9 @@ VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf) { float modifier; /* checks */ - if (!check_extensions(sf, "opusx")) + if (!is_id32be(0x00, sf, "OPUS")) goto fail; - if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */ + if (!check_extensions(sf, "opusx")) goto fail; offset = 0x10; @@ -414,10 +422,11 @@ VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf) { int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ + if (!is_id32be(0x00, sf, "OPUS")) + goto fail; if (!check_extensions(sf, "opus,lopus")) goto fail; - if (read_32bitBE(0x00, sf) != 0x4F505553 || /* "OPUS" */ - read_32bitBE(0x18, sf) != 0x01000080) + if (read_32bitBE(0x18, sf) != 0x01000080) goto fail; offset = 0x18; @@ -441,9 +450,9 @@ VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf) { int num_samples = 0, loop_start = 0, loop_end = 0; /* checks */ - if (!check_extensions(sf, "opus,lopus")) + if (!is_id64be(0x00, sf,"OPUSNX\0\0")) goto fail; - if (read_64bitBE(0x00, sf) != 0x4F5055534E580000) /* "OPUSNX\0\0" */ + if (!check_extensions(sf, "opus,lopus")) goto fail; offset = 0x10; @@ -462,9 +471,9 @@ VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf) { int num_samples = 0, loop_start = 0, loop_end = 0; /* checks */ - if (!check_extensions(sf, "nsopus")) + if (!is_id32be(0x00, sf,"EWNO")) goto fail; - if (read_u32be(0x00, sf) != 0x45574E4F) /* "EWNO" */ + if (!check_extensions(sf, "nsopus")) goto fail; offset = 0x08; @@ -481,12 +490,12 @@ VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf) { int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ - /* .wav: default - * .opus: fake? */ - if (!check_extensions(sf, "wav,lwav,opus,lopus")) - goto fail; if (read_u32be(0x00, sf) != 0x01000000) goto fail; + + /* .wav: original */ + if (!check_extensions(sf, "wav,lwav")) + goto fail; /* 0x04: channels */ /* 0x08: data_size */ offset = read_32bitLE(0x0C, sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ast.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ast.c deleted file mode 100644 index 3c9ef56bf..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ast.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* AST - from Koei and Marvelous games (same internal dev?) */ -VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, variant_type; - - /* check extension */ - if (!check_extensions(streamFile,"ast")) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x41535400) /* "AST\0" */ - goto fail; - - /* determine variant (after 0x10 is garbage/code data in type 1 until 0x800, but consistent in all songs) */ - if (read_32bitBE(0x10,streamFile) == 0x00000000 || read_32bitBE(0x10,streamFile) == 0x20002000) { - variant_type = 1; /* Koei: P.T.O. IV (0x00000000), Naval Ops: Warship Gunner (0x20002000) */ - channel_count = 2; - } - else { - variant_type = 2; /* Marvelous: Katekyoo Hitman Reborn! Dream Hyper Battle!, Binchou-tan: Shiawasegoyomi */ - channel_count = read_32bitLE(0x0C,streamFile); - } - - loop_flag = 0; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - if (variant_type == 1) { - start_offset = 0x800; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x0C,streamFile)-start_offset,channel_count); - vgmstream->interleave_block_size = read_32bitLE(0x08,streamFile); - } - else if (variant_type == 2) { - start_offset = 0x100; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile)-start_offset,channel_count); - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); - } - else { - goto fail; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->coding_type = coding_PSX; - vgmstream->meta_type = meta_PS2_AST; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_lpcm.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_lpcm.c deleted file mode 100644 index 0037cded6..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_lpcm.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* LPCM (from Ah! My Goddess (PS2)) */ -VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("lpcm",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0,streamFile) != 0x4C50434D) /* LPCM */ - goto fail; - - loop_flag = read_32bitLE(0x8,streamFile); - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x10; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 48000; - vgmstream->coding_type = coding_PCM16LE; - vgmstream->num_samples = read_32bitLE(0x4,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x8,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0xc,streamFile); - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 2; - vgmstream->meta_type = meta_PS2_LPCM; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 5e9a4db70..86232ed31 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -353,8 +353,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { * .saf: Whacked! (Xbox) * .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] * .ima: Baja: Edge of Control (PS3/X360) + * .nsa: Studio Ring games that uses NScripter [Hajimete no Otetsudai (PC)] */ - if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima") ) { + if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima,nsa") ) { ; } else if ( check_extensions(sf, "mwv") ) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c index 6c32cbaad..331918bca 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c @@ -272,6 +272,31 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa return 1; } + /* Taz Wanted demo (PC)[2003] */ + if ( read_u32be(0x04,sf_h) == 0x00000900 && + read_u32le(0x24,sf_h) == read_u32le(0xfc,sf_h) && /* sample rate repeat */ + read_u32le(0x10c,sf_h) == header_size + ) { + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32le(0x2c,sf_h); + strwav->loop_end = read_s32le(0x30,sf_h); + strwav->loop_start = read_s32le(0x38,sf_h); + /* 0x58: number of chunks */ + strwav->tracks = read_s32le(0xD8,sf_h); + /* 0xfc: sample rate 2 */ + /* 0x100: ? */ + /* 0x10c: header size */ + + strwav->codec = IMA; + strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000; + ;VGM_LOG("STR+WAV: header TAZd (PC)\n"); + return 1; + } + /* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */ if ( read_u32be(0x04,sf_h) == 0x00000900 && read_u32le(0x24,sf_h) == read_u32le(0xb0,sf_h) && /* sample rate repeat */ @@ -479,12 +504,14 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa return 1; } + /* Taz Wanted (PC)[2002] */ /* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */ if ( read_u32be(0x04,sf_h) == 0x00000900 && read_u32le(0x0c,sf_h) != header_size && read_u32le(0x24,sf_h) != 0 && read_u32le(0x24,sf_h) == read_u32le(0x90,sf_h) && /* sample rate repeat */ - read_u32le(0xa0,sf_h) == header_size /* ~0xC0 */ + (read_u32le(0xa0,sf_h) == header_size || /* Zapper */ + read_u32le(0xa0,sf_h) + 0x50 == header_size) /* Taz */ ) { /* 0x08: null */ /* 0x0c: hashname */ @@ -612,7 +639,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa strwav->codec = DSP; strwav->dsps_table = 0xf0; - strwav->interleave = strwav->tracks > 2 ? 0x8000 : 0x10000; + strwav->interleave = strwav->tracks >= 2 ? 0x8000 : 0x10000; ;VGM_LOG("STR+WAV: header SBCKK (GC)\n"); return 1; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 668ad6cea..dd673084b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -40,6 +40,9 @@ typedef enum { ASF = 30, /* Argonaut ASF 4-bit ADPCM */ EAXA = 31, /* Electronic Arts EA-XA 4-bit ADPCM v1 */ OKI4S = 32, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */ + XA, + XA_EA, + CP_YM, UNKNOWN = 99, } txth_codec_t; @@ -117,11 +120,15 @@ typedef struct { uint32_t chunk_count; uint32_t chunk_header_size; uint32_t chunk_data_size; + uint32_t chunk_value; + uint32_t chunk_size_offset; + uint32_t chunk_be; int chunk_start_set; int chunk_size_set; int chunk_count_set; uint32_t base_offset; + uint32_t is_offset_absolute; uint32_t name_values[16]; int name_values_count; @@ -256,6 +263,9 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { case TGC: coding = coding_TGC; break; case ASF: coding = coding_ASF; break; case EAXA: coding = coding_EA_XA; break; + case XA: coding = coding_XA; break; + case XA_EA: coding = coding_XA_EA; break; + case CP_YM: coding = coding_CP_YM; break; default: goto fail; } @@ -367,6 +377,9 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { case coding_OKI16: case coding_OKI4S: + case coding_XA: + case coding_XA_EA: + case coding_CP_YM: vgmstream->layout_type = layout_none; break; @@ -448,7 +461,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { { int16_t (*read_16bit)(off_t, STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE; int16_t (*get_16bit)(const uint8_t* p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE; - +VGM_LOG("coef=%x\n",txth.coef_offset ); for (i = 0; i < vgmstream->channels; i++) { if (txth.coef_mode == 0) { /* normal coefs */ for (j = 0; j < 16; j++) { @@ -648,7 +661,7 @@ static VGMSTREAM* init_subfile(txth_header* txth) { * - etc * to avoid it we set a particular fake extension and detect it when reading .txth */ - strcpy(extension, "subfile_txth."); + strcpy(extension, ".subfile_txth."); strcat(extension, txth->subfile_extension); sf_sub = setup_subfile_streamfile(txth->sf_body, txth->subfile_offset, txth->subfile_size, extension); @@ -722,50 +735,43 @@ fail: static STREAMFILE* open_txth(STREAMFILE* sf) { - char basename[PATH_LIMIT]; char filename[PATH_LIMIT]; - char fileext[PATH_LIMIT]; - const char *subext; + const char* base_ext; + const char* txth_ext; STREAMFILE* sf_text; - /* try "(path/)(name.ext).txth" */ - get_streamfile_name(sf,filename,PATH_LIMIT); - if (strstr(filename, "subfile_txth") != NULL) + + get_streamfile_name(sf, filename, sizeof(filename)); + if (strstr(filename, ".subfile_txth") != NULL) return NULL; /* detect special case of subfile-within-subfile */ - strcat(filename, ".txth"); - sf_text = open_streamfile(sf,filename); - if (sf_text) return sf_text; - /* try "(path/)(.sub.ext).txth" */ - get_streamfile_basename(sf,basename,PATH_LIMIT); - subext = filename_extension(basename); - if (subext != NULL && subext[0] != '\0') { - get_streamfile_path(sf,filename,PATH_LIMIT); - get_streamfile_ext(sf,fileext,PATH_LIMIT); - strcat(filename,"."); - strcat(filename, subext); - strcat(filename,"."); - strcat(filename, fileext); - strcat(filename, ".txth"); + base_ext = filename_extension(filename); + concatn(sizeof(filename), filename, ".txth"); + txth_ext = filename_extension(filename); - sf_text = open_streamfile(sf,filename); + /* try "(path/)(name.ext).txth" */ + { + /* full filename, already prepared */ + + sf_text = open_streamfile(sf, filename); if (sf_text) return sf_text; } /* try "(path/)(.ext).txth" */ - get_streamfile_path(sf,filename,PATH_LIMIT); - get_streamfile_ext(sf,fileext,PATH_LIMIT); - strcat(filename,"."); - strcat(filename, fileext); - strcat(filename, ".txth"); - sf_text = open_streamfile(sf,filename); - if (sf_text) return sf_text; + if (base_ext) { + base_ext--; //get_streamfile_path(sf, filename, sizeof(filename)); + + sf_text = open_streamfile_by_filename(sf, base_ext); + if (sf_text) return sf_text; + } /* try "(path/).txth" */ - get_streamfile_path(sf,filename,PATH_LIMIT); - strcat(filename, ".txth"); - sf_text = open_streamfile(sf,filename); - if (sf_text) return sf_text; + if (txth_ext) { + txth_ext--; /* points to "txth" due to the concat */ + + sf_text = open_streamfile_by_filename(sf, txth_ext); + if (sf_text) return sf_text; + } /* not found */ return NULL; @@ -788,7 +794,9 @@ static void set_body_chunk(txth_header* txth) { //todo maybe should only be done once, or have some count to retrigger to simplify? if (!txth->chunk_start_set || !txth->chunk_size_set || !txth->chunk_count_set) return; - if (txth->chunk_size == 0 || txth->chunk_start > txth->data_size || txth->chunk_count == 0) + if ((txth->chunk_size == 0 && ! txth->chunk_size_offset) || + txth->chunk_start > txth->data_size || + txth->chunk_count == 0) return; if (!txth->sf_body) return; @@ -804,18 +812,22 @@ static void set_body_chunk(txth_header* txth) { { txth_io_config_data cfg = {0}; - cfg.chunk_start = txth->chunk_start; + cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */ cfg.chunk_header_size = txth->chunk_header_size; cfg.chunk_data_size = txth->chunk_data_size; + + cfg.chunk_value = txth->chunk_value; + cfg.chunk_size_offset = txth->chunk_size_offset; + cfg.chunk_be = txth->chunk_be; + + cfg.chunk_start = txth->chunk_start; cfg.chunk_size = txth->chunk_size; cfg.chunk_count = txth->chunk_count; - cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */ temp_sf = setup_txth_streamfile(txth->sf_body, cfg, txth->sf_body_opened); if (!temp_sf) return; } - /* closing is handled by temp_sf */ //if (txth->sf_body_opened) { // close_streamfile(txth->sf_body); @@ -939,6 +951,9 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) { else if (is_string(val,"GCOM_ADPCM")) return TGC; else if (is_string(val,"ASF")) return ASF; else if (is_string(val,"EAXA")) return EAXA; + else if (is_string(val,"XA")) return XA; + else if (is_string(val,"XA_EA")) return XA_EA; + else if (is_string(val,"CP_YM")) return CP_YM; /* special handling */ else if (is_string(val,"name_value")) return txth->name_values[0]; else if (is_string(val,"name_value1")) return txth->name_values[0]; @@ -949,6 +964,19 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) { return UNKNOWN; } +static int parse_be(txth_header* txth, const char* val, uint32_t* p_value) { + if (is_string(val, "BE")) + *p_value = 1; + else if (is_string(val, "LE")) + *p_value = 0; + else + if (!parse_num(txth->sf_head,txth,val, p_value)) + goto fail; + return 1; +fail: + return 0; +} + static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, char* val) { //;VGM_LOG("TXTH: key=%s, val=%s\n", key, val); @@ -1169,23 +1197,26 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha goto fail; } + else if (is_string(key,"offset_absolute")) { + if (!parse_num(txth->sf_head,txth,val, &txth->is_offset_absolute)) goto fail; + } + /* COEFS */ else if (is_string(key,"coef_offset")) { if (!parse_num(txth->sf_head,txth,val, &txth->coef_offset)) goto fail; /* special adjustments */ + VGM_LOG("coef norm=%x\n",txth->coef_offset ); txth->coef_offset += txth->base_offset; - if (txth->subsong_spacing) + VGM_LOG("coef+base=%x\n",txth->coef_offset ); + if (txth->subsong_spacing && !txth->is_offset_absolute) txth->coef_offset += txth->subsong_spacing * (txth->target_subsong - 1); + VGM_LOG("coef+spac=%x\n",txth->coef_offset ); } else if (is_string(key,"coef_spacing")) { if (!parse_num(txth->sf_head,txth,val, &txth->coef_spacing)) goto fail; } else if (is_string(key,"coef_endianness")) { - if (is_string(val, "BE")) - txth->coef_big_endian = 1; - else if (is_string(val, "LE")) - txth->coef_big_endian = 0; - else if (!parse_num(txth->sf_head,txth,val, &txth->coef_big_endian)) goto fail; + if (!parse_be(txth, val, &txth->coef_big_endian)) goto fail; } else if (is_string(key,"coef_mode")) { if (!parse_num(txth->sf_head,txth,val, &txth->coef_mode)) goto fail; @@ -1201,18 +1232,14 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha txth->hist_set = 1; /* special adjustment */ txth->hist_offset += txth->hist_offset; - if (txth->subsong_spacing) + if (txth->subsong_spacing && !txth->is_offset_absolute) txth->hist_offset += txth->subsong_spacing * (txth->target_subsong - 1); } else if (is_string(key,"hist_spacing")) { if (!parse_num(txth->sf_head,txth,val, &txth->hist_spacing)) goto fail; } else if (is_string(key,"hist_endianness")) { - if (is_string(val, "BE")) - txth->hist_big_endian = 1; - else if (is_string(val, "LE")) - txth->hist_big_endian = 0; - else if (!parse_num(txth->sf_head,txth,val, &txth->hist_big_endian)) goto fail; + if (!parse_be(txth, val, &txth->hist_big_endian)) goto fail; } /* SUBSONGS */ @@ -1227,10 +1254,10 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha txth->name_offset_set = 1; /* special adjustment */ txth->name_offset += txth->base_offset; - if (txth->subsong_spacing) + if (txth->subsong_spacing && !txth->is_offset_absolute) txth->name_offset += txth->subsong_spacing * (txth->target_subsong - 1); } - else if (is_string(key,"name_offset_absolute")) { + else if (is_string(key,"name_offset_absolute")) { //TODO: remove if (!parse_num(txth->sf_head,txth,val, &txth->name_offset)) goto fail; txth->name_offset_set = 1; /* special adjustment */ @@ -1336,34 +1363,41 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha } /* CHUNKS */ - else if (is_string(key,"chunk_number")) { - if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail; + else if (is_string(key,"chunk_count")) { + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail; + txth->chunk_count_set = 1; + set_body_chunk(txth); } else if (is_string(key,"chunk_start")) { if (!parse_num(txth->sf_head,txth,val, &txth->chunk_start)) goto fail; txth->chunk_start_set = 1; set_body_chunk(txth); } - else if (is_string(key,"chunk_header_size")) { - if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail; - //txth->chunk_header_size_set = 1; - //set_body_chunk(txth); /* optional and should go before chunk_size */ - } - else if (is_string(key,"chunk_data_size")) { - if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail; - //txth->chunk_data_size_set = 1; - //set_body_chunk(txth); /* optional and should go before chunk_size */ - } else if (is_string(key,"chunk_size")) { if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size)) goto fail; txth->chunk_size_set = 1; set_body_chunk(txth); } - else if (is_string(key,"chunk_count")) { - if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail; - txth->chunk_count_set = 1; - set_body_chunk(txth); + /* optional and should go before the above */ + else if (is_string(key,"chunk_number")) { + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail; } + else if (is_string(key,"chunk_header_size")) { + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail; + } + else if (is_string(key,"chunk_data_size")) { + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail; + } + else if (is_string(key,"chunk_value")) { + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_value)) goto fail; + } + else if (is_string(key,"chunk_size_offset")) { + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size_offset)) goto fail; + } + else if (is_string(key,"chunk_endianness")) { + if (!parse_be(txth, val, &txth->chunk_be)) goto fail; + } + /* BASE OFFSET */ else if (is_string(key,"base_offset")) { @@ -1564,6 +1598,16 @@ fail: return 0; } +static void string_trim(char* str) { + int str_len = strlen(str); + int i; + for (i = str_len - 1; i >= 0; i--) { + if (str[i] != ' ') + break; + str[i] = '\0'; + } +} + static int read_name_table_keyval(txth_header* txth, const char* line, char* key, char* val) { int ok; int subsong; @@ -1576,8 +1620,10 @@ static int read_name_table_keyval(txth_header* txth, const char* line, char* key return 0; /* try "(name): (val))" */ + ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val); if (ok == 2) { + string_trim(key); /* otherwise includes end spaces before : */ //;VGM_LOG("TXTH: name %s get\n", key); return 1; } @@ -1628,32 +1674,27 @@ fail: return 0; } -static int parse_name_table(txth_header* txth, char* name_list) { +static int parse_name_table(txth_header* txth, char* set_name) { STREAMFILE* sf_names = NULL; off_t txt_offset, file_size; char fullname[PATH_LIMIT]; char filename[PATH_LIMIT]; char basename[PATH_LIMIT]; + const char* table_name; /* just in case */ if (!txth->sf_text || !txth->sf_body) goto fail; - /* trim name_list just in case */ - { - int name_list_len = strlen(name_list); - int i; - for (i = name_list_len - 1; i >= 0; i--) { - if (name_list[i] != ' ') - break; - name_list[i] = '\0'; - } - } - - //;VGM_LOG("TXTH: name_list='%s'\n", name_list); + /* trim just in case */ + string_trim(set_name); + if (is_string(set_name,"*")) + table_name = ".names.txt"; + else + table_name = set_name; /* open companion file near .txth */ - sf_names = open_streamfile_by_filename(txth->sf_text, name_list); + sf_names = open_streamfile_by_filename(txth->sf_text, table_name); if (!sf_names) goto fail; get_streamfile_name(txth->sf_body, fullname, sizeof(filename)); @@ -1893,6 +1934,8 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_ else if ((n = is_string_field(val,"subfile_offset"))) value = txth->subfile_offset; else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size; else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset; + else if ((n = is_string_field(val,"coef_offset"))) value = txth->coef_offset; + else if ((n = is_string_field(val,"hist_offset"))) value = txth->hist_offset; //todo whatever, improve else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0]; else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0]; @@ -2001,6 +2044,9 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) { return asf_bytes_to_samples(bytes, txth->channels); case EAXA: return ea_xa_bytes_to_samples(bytes, txth->channels); + case XA: + case XA_EA: + return xa_bytes_to_samples(bytes, txth->channels, 0, 0, 4); /* XMA bytes-to-samples is done at the end as the value meanings are a bit different */ case XMA1: @@ -2011,6 +2057,7 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) { case DVI_IMA: return ima_bytes_to_samples(bytes, txth->channels); case AICA: + case CP_YM: return yamaha_bytes_to_samples(bytes, txth->channels); case PCFX: case OKI16: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/txth_streamfile.h index 5e4b0f2a3..322c359a4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth_streamfile.h @@ -1,30 +1,36 @@ #ifndef _TXTH_STREAMFILE_H_ #define _TXTH_STREAMFILE_H_ #include "../streamfile.h" +#include "../util/endianness.h" typedef struct { - off_t chunk_start; - size_t chunk_size; - size_t chunk_header_size; - size_t chunk_data_size; + uint32_t chunk_start; + uint32_t chunk_size; + uint32_t chunk_header_size; + uint32_t chunk_data_size; + int chunk_count; int chunk_number; + + uint32_t chunk_value; + uint32_t chunk_size_offset; + int chunk_be; } txth_io_config_data; typedef struct { /* config */ txth_io_config_data cfg; - size_t stream_size; + uint32_t stream_size; /* state */ - off_t logical_offset; /* fake offset */ - off_t physical_offset; /* actual offset */ - size_t block_size; /* current size */ - size_t skip_size; /* size from block start to reach data */ - size_t data_size; /* usable size in a block */ + uint32_t logical_offset; /* fake offset */ + uint32_t physical_offset; /* actual offset */ + uint32_t block_size; /* current size */ + uint32_t skip_size; /* size from block start to reach data */ + uint32_t data_size; /* usable size in a block */ - size_t logical_size; + uint32_t logical_size; } txth_io_data; @@ -64,6 +70,22 @@ static size_t txth_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t l data->data_size = data->cfg.chunk_data_size; } + /* chunk size reader (overwrites the above) */ + if (data->cfg.chunk_header_size && data->cfg.chunk_size_offset) { + read_u32_t read_u32 = data->cfg.chunk_be ? read_u32be : read_u32le; + + data->block_size = read_u32(data->physical_offset + data->cfg.chunk_size_offset, sf); + data->data_size = data->block_size - data->cfg.chunk_header_size; + + /* skip chunk if doesn't match expected header value */ + if (data->cfg.chunk_value) { + uint32_t value = read_u32(data->physical_offset + 0x00, sf); + if (value != data->cfg.chunk_value) { + data->data_size = 0; + } + } + } + /* clamp for games where last block is smaller */ //todo not correct for all cases if (data->physical_offset + data->block_size > data->cfg.chunk_start + data->stream_size) { data->block_size = (data->cfg.chunk_start + data->stream_size) - data->physical_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index 4820f028f..b97827715 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -645,7 +645,7 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_ if (!sf_data) { /* play silence if external file is not found since Rayman 2 seems to rely on this behavior */ vgm_logi("UBI DAT: external file '%s' not found (put together)\n", sb->resource_name); - strncat(sb->readable_name, " (missing)", sizeof(sb->readable_name)); + concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)"); sb->duration = (float)pcm_bytes_to_samples(sb->stream_size, sb->channels, 16) / (float)sb->sample_rate; return init_vgmstream_ubi_sb_silence(sb); } @@ -1301,8 +1301,11 @@ static VGMSTREAM* init_vgmstream_ubi_sb_audio(ubi_sb_header* sb, STREAMFILE* sf_ if (sb->is_external) { sf_data = open_streamfile_by_filename(sf, sb->resource_name); if (sf_data == NULL) { + /* play silence if external file is not found */ vgm_logi("UBI SB: external file '%s' not found (put together)\n", sb->resource_name); - goto fail; + concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)"); + sb->duration = 1.0f; + return init_vgmstream_ubi_sb_silence(sb); } } else { @@ -1342,8 +1345,11 @@ static VGMSTREAM* init_vgmstream_ubi_sb_layer(ubi_sb_header* sb, STREAMFILE* sf_ if (sb->is_external) { sf_data = open_streamfile_by_filename(sf,sb->resource_name); if (sf_data == NULL) { + /* play silence if external file is not found */ vgm_logi("UBI SB: external file '%s' not found (put together)\n", sb->resource_name); - goto fail; + concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)"); + sb->duration = 1.0f; + return init_vgmstream_ubi_sb_silence(sb); } } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xa.c b/Frameworks/vgmstream/vgmstream/src/meta/xa.c index 5f17f21d8..0227e4400 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xa.c @@ -6,53 +6,45 @@ static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2); static int xa_check_format(STREAMFILE* sf, off_t offset, int is_blocked); -/* XA - from Sony PS1 and Philips CD-i CD audio, also Saturn streams */ +/* XA - from Sony PS1 and Philips CD-i CD audio */ VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset; int loop_flag = 0, channels, sample_rate, bps; - int is_riff = 0, is_blocked = 0, is_form2 = 0; + int is_riff = 0, is_form2 = 0, is_blocked; size_t stream_size = 0; int total_subsongs = 0, target_subsong = sf->stream_index; uint16_t target_config = 0; - /* checks */ - /* .xa: common - * .str: often videos and sometimes speech/music - * .adp: Phantasy Star Collection (SAT) raw XA - * .pxa: Mortal Kombat 4 (PS1) - * .grn: Micro Machines (CDi) - * (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */ - if (!check_extensions(sf,"xa,str,adp,pxa,grn,")) - goto fail; - - /* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders. - * Also has minimal support for headerless (ISO 2048 mode1/data) mode. */ - - /* check RIFF header = raw (optional, added when ripping and not part of the CD data) */ - if (read_u32be(0x00,sf) == 0x52494646 && /* "RIFF" */ - read_u32be(0x08,sf) == 0x43445841 && /* "CDXA" */ - read_u32be(0x0C,sf) == 0x666D7420) { /* "fmt " */ + if (read_u32be(0x00,sf) == 0x00FFFFFF && read_u32be(0x04,sf) == 0xFFFFFFFF && read_u32be(0x08,sf) == 0xFFFFFF00) { + /* sector sync word = raw data */ + is_blocked = 1; + start_offset = 0x00; + } + else if (is_id32be(0x00,sf, "RIFF") && is_id32be(0x08,sf, "CDXA") && is_id32be(0x0C,sf, "fmt ")) { + /* RIFF header = raw with header (optional, added by CD drivers when copying and not part of the CD data) */ is_blocked = 1; is_riff = 1; start_offset = 0x2c; /* after "data", ignore RIFF values as often are wrong */ } else { - /* sector sync word = raw */ - if (read_u32be(0x00,sf) == 0x00FFFFFF && - read_u32be(0x04,sf) == 0xFFFFFFFF && - read_u32be(0x08,sf) == 0xFFFFFF00) { - is_blocked = 1; - start_offset = 0x00; - } - else { - /* headerless or possibly incorrectly ripped */ - start_offset = 0x00; - } + /* non-blocked (ISO 2048 mode1/data) or incorrectly ripped: use TXTH */ + goto fail; } + /* .xa: common + * .str: often videos and sometimes speech/music + * .pxa: Mortal Kombat 4 (PS1) + * .grn: Micro Machines (CDi) + * (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */ + if (!check_extensions(sf,"xa,str,pxa,grn,")) + goto fail; + + /* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders. + * For headerless XA (ISO 2048 mode1/data) mode use TXTH. */ + /* test for XA data, since format is raw-ish (with RIFF it's assumed to be ok) */ if (!is_riff && !xa_check_format(sf, start_offset, is_blocked)) goto fail; @@ -89,42 +81,18 @@ VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) { switch((xa_header >> 6) & 1) { /* 6: emphasis (applies a filter) */ case 0: break; default: /* shouldn't be used by games */ - VGM_LOG("XA: unknown emphasis found\n"); - break; + vgm_logi("XA: unknown emphasis found\n"); + goto fail; } switch((xa_header >> 7) & 1) { /* 7: reserved */ case 0: break; default: - VGM_LOG("XA: unknown reserved bit found\n"); - break; + vgm_logi("XA: unknown reserved bit found\n"); + goto fail; } } else { - /* headerless */ - if (check_extensions(sf,"adp")) { - /* Phantasy Star Collection (SAT) raw files */ - /* most are stereo, though a few (mainly sfx banks, sometimes using .bin) are mono */ - - char filename[PATH_LIMIT] = {0}; - get_streamfile_filename(sf, filename,PATH_LIMIT); - - /* detect PS1 mono files, very lame but whatevs, no way to detect XA mono/stereo */ - if (filename[0]=='P' && filename[1]=='S' && filename[2]=='1' && filename[3]=='S') { - channels = 1; - sample_rate = 22050; - } - else { - channels = 2; - sample_rate = 44100; - } - bps = 4; - } - else { - /* incorrectly ripped standard XA */ - channels = 2; - sample_rate = 37800; - bps = 4; - } + goto fail; } /* untested */ @@ -161,7 +129,9 @@ fail: return NULL; } + static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) { + uint8_t frame_hdr[0x10]; int i, j, sector = 0, skip = 0; off_t test_offset = offset; const size_t sector_size = (is_blocked ? 0x900 : 0x800); @@ -172,23 +142,27 @@ static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) { /* test frames inside CD sectors */ while (sector < sector_max) { - uint8_t xa_submode = read_u8(test_offset + 0x12, sf); - int is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02); + if (is_blocked) { + uint8_t xa_submode = read_u8(test_offset + 0x12, sf); + int is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02); - if (is_blocked && !is_audio) { - skip++; - if (sector == 0 && skip > skip_max) /* no a single audio sector found */ - goto fail; - test_offset += sector_size + extra_size + extra_size; - continue; + if (is_blocked && !is_audio) { + skip++; + if (sector == 0 && skip > skip_max) /* no a single audio sector found */ + goto fail; + test_offset += sector_size + extra_size + extra_size; + continue; + } } test_offset += extra_size; /* header */ for (i = 0; i < (sector_size / frame_size); i++) { + read_streamfile(frame_hdr, test_offset, sizeof(frame_hdr), sf); + /* XA frame checks: filter indexes should be 0..3, and shifts 0..C */ for (j = 0; j < 16; j++) { - uint8_t header = read_u8(test_offset + j, sf); + uint8_t header = get_u8(frame_hdr + j); if (((header >> 4) & 0xF) > 0x03) goto fail; if (((header >> 0) & 0xF) > 0x0c) @@ -196,14 +170,14 @@ static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) { } /* XA headers pairs are repeated */ - if (read_u32be(test_offset+0x00, sf) != read_u32be(test_offset+0x04, sf) || - read_u32be(test_offset+0x08, sf) != read_u32be(test_offset+0x0c, sf)) + if (get_u32be(frame_hdr+0x00) != get_u32be(frame_hdr+0x04) || + get_u32be(frame_hdr+0x08) != get_u32be(frame_hdr+0x0c)) goto fail; /* blank frames should always use 0x0c0c0c0c (due to how shift works) */ - if (read_u32be(test_offset+0x00, sf) == 0 && - read_u32be(test_offset+0x04, sf) == 0 && - read_u32be(test_offset+0x08, sf) == 0 && - read_u32be(test_offset+0x0c, sf) == 0) + if (get_u32be(frame_hdr+0x00) == 0 && + get_u32be(frame_hdr+0x04) == 0 && + get_u32be(frame_hdr+0x08) == 0 && + get_u32be(frame_hdr+0x0c) == 0) goto fail; test_offset += 0x80; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index da43bcef0..4843f824e 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -1,12 +1,22 @@ #include "streamfile.h" #include "util.h" #include "vgmstream.h" +#include /* for dup/fdopen in some systems */ #ifndef _MSC_VER #include #endif +//TODO: move +#ifndef DIR_SEPARATOR + #if defined (_WIN32) || defined (WIN32) + #define DIR_SEPARATOR '\\' + #else + #define DIR_SEPARATOR '/' + #endif +#endif + /* For (rarely needed) +2GB file support we use fseek64/ftell64. Those are usually available * but may depend on compiler. * - MSVC: +VS2008 should work @@ -108,7 +118,7 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size #ifdef VGM_DEBUG_OUTPUT if (offset < sf->buf_offset && length > 0) { - VGM_LOG("stdio: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf); + //VGM_LOG("stdio: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf); //sf->rebuffer++; //if (rebuffer > N) ... } @@ -341,7 +351,7 @@ static size_t buffer_read(BUFFER_STREAMFILE* sf, uint8_t* dst, offv_t offset, si #ifdef VGM_DEBUG_OUTPUT if (offset < sf->buf_offset) { - VGM_LOG("buffer: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf); + //VGM_LOG("buffer: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf); } #endif @@ -1263,7 +1273,6 @@ STREAMFILE* read_filemap_file_pos(STREAMFILE* sf, int file_num, int* p_pos) { /* better way? */ if (strcmp(line, "#@reset-pos") == 0) { file_pos = 0; - VGM_LOG("pos =%i\n", file_pos); } continue; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index f29be96ac..5ca8f4cf4 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -1,7 +1,6 @@ /* * streamfile.h - definitions for buffered file reading with STREAMFILE */ - #ifndef _STREAMFILE_H #define _STREAMFILE_H @@ -9,9 +8,14 @@ #define _CRT_SECURE_NO_DEPRECATE #endif +//TODO cleanup +//NULL, allocs #include +//FILE #include +//string functions in meta and so on #include +//off_t #include #include "streamtypes.h" #include "util.h" @@ -22,14 +26,6 @@ #include #endif -#ifndef DIR_SEPARATOR - #if defined (_WIN32) || defined (WIN32) - #define DIR_SEPARATOR '\\' - #else - #define DIR_SEPARATOR '/' - #endif -#endif - /* 64-bit offset is needed for banks that hit +2.5GB (like .fsb or .ktsl2stbin). * Leave as typedef to toggle since it's theoretically slower when compiled as 32-bit. * ATM it's only used in choice places until more performance tests are done. diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index 06fffed1c..8f8c2dd04 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -12,19 +12,19 @@ /* host endian independent multi-byte integer reading */ static inline int16_t get_16bitBE(const uint8_t* p) { - return (p[0]<<8) | (p[1]); + return ((uint16_t)p[0]<<8) | ((uint16_t)p[1]); } static inline int16_t get_16bitLE(const uint8_t* p) { - return (p[0]) | (p[1]<<8); + return ((uint16_t)p[0]) | ((uint16_t)p[1]<<8); } static inline int32_t get_32bitBE(const uint8_t* p) { - return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | (p[3]); + return ((uint32_t)p[0]<<24) | ((uint32_t)p[1]<<16) | ((uint32_t)p[2]<<8) | ((uint32_t)p[3]); } static inline int32_t get_32bitLE(const uint8_t* p) { - return (p[0]) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + return ((uint32_t)p[0]) | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24); } static inline int64_t get_64bitBE(const uint8_t* p) { diff --git a/Frameworks/vgmstream/vgmstream/src/util/endianness.h b/Frameworks/vgmstream/vgmstream/src/util/endianness.h index ce5abe9a5..d4a2e49c4 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/endianness.h +++ b/Frameworks/vgmstream/vgmstream/src/util/endianness.h @@ -6,5 +6,10 @@ typedef uint32_t (*read_u32_t)(off_t, STREAMFILE*); typedef int32_t (*read_s32_t)(off_t, STREAMFILE*); typedef uint16_t (*read_u16_t)(off_t, STREAMFILE*); +typedef float (*read_f32_t)(off_t, STREAMFILE*); + +//todo move here +#define guess_endian32 guess_endianness32bit +#define guess_endian16 guess_endianness16bit #endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index c42bced87..8c1e71a01 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -92,9 +92,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_musx, init_vgmstream_leg, init_vgmstream_filp, - init_vgmstream_ikm_ps2, - init_vgmstream_ikm_pc, - init_vgmstream_ikm_psp, + init_vgmstream_ikm, init_vgmstream_sfs, init_vgmstream_bg00, init_vgmstream_sat_dvi, @@ -214,7 +212,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_xbox_hlwav, init_vgmstream_myspd, init_vgmstream_his, - init_vgmstream_ps2_ast, + init_vgmstream_ast_mmv, + init_vgmstream_ast_mv, init_vgmstream_dmsg, init_vgmstream_ngc_dsp_aaap, init_vgmstream_ngc_dsp_konami, @@ -237,7 +236,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_dsp_xiii, init_vgmstream_dsp_cabelas, init_vgmstream_ps2_adm, - init_vgmstream_ps2_lpcm, + init_vgmstream_lpcm_shade, init_vgmstream_dsp_bdsp, init_vgmstream_ps2_vms, init_vgmstream_xau, diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 7e36a84ab..527fdaa77 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -96,6 +96,7 @@ typedef enum { coding_XA, /* CD-ROM XA 4-bit */ coding_XA8, /* CD-ROM XA 8-bit */ + coding_XA_EA, /* EA's Saturn XA (not to be confused with EA-XA) */ coding_PSX, /* Sony PS ADPCM (VAG) */ coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */ coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (int math) */ @@ -147,6 +148,7 @@ typedef enum { coding_AICA, /* Yamaha AICA ADPCM (stereo) */ coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */ + coding_CP_YM, /* Capcom's Yamaha ADPCM (stereo/mono) */ coding_ASKA, /* Aska ADPCM */ coding_NXAP, /* NXAP ADPCM */ @@ -516,7 +518,8 @@ typedef enum { meta_PONA_3DO, /* Policenauts (3DO) */ meta_PONA_PSX, /* Policenauts (PSX) */ meta_XBOX_HLWAV, /* Half Life 2 (XBOX) */ - meta_PS2_AST, /* Some KOEI game (PS2) */ + meta_AST_MV, + meta_AST_MMV, meta_DMSG, /* Nightcaster II - Equinox (XBOX) */ meta_NGC_DSP_AAAP, /* Turok: Evolution (NGC), Vexx (NGC) */ meta_PS2_STER, /* Juuni Kokuki: Kakukaku Taru Ou Michi Beni Midori no Uka */ @@ -539,7 +542,7 @@ typedef enum { meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */ meta_DSP_CABELAS, /* Cabelas games */ meta_PS2_ADM, /* Dragon Quest V (PS2) */ - meta_PS2_LPCM, /* Ah! My Goddess */ + meta_LPCM_SHADE, meta_DSP_BDSP, /* Ah! My Goddess */ meta_PS2_VMS, /* Autobahn Raser - Police Madness */ meta_XAU, /* XPEC Entertainment (Beat Down (PS2 Xbox), Spectral Force Chronicle (PS2)) */