diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 6309bc4df..06a288d03 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -116,7 +116,6 @@ 832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */ = {isa = PBXBuildFile; fileRef = 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */; }; 832BF82D21E0514B006F50F1 /* nus3audio.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81B21E0514B006F50F1 /* nus3audio.c */; }; 832C70BF1E9335E400BD7B4E /* Vorbis.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F4128F1E932F9A002E37D0 /* Vorbis.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 83345A4F1F8AEB2800B2EAA4 /* nub_xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */; }; 83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */; }; 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; @@ -199,7 +198,6 @@ 834FE107215C79ED000A5D3D /* mib_mih.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DE215C79EB000A5D3D /* mib_mih.c */; }; 834FE108215C79ED000A5D3D /* hd3_bd3.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */; }; 834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E0215C79EB000A5D3D /* idsp_ie.c */; }; - 834FE10A215C79ED000A5D3D /* nub_idsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E1215C79EB000A5D3D /* nub_idsp.c */; }; 834FE10C215C79ED000A5D3D /* fsb5_interleave_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */; }; 834FE10D215C79ED000A5D3D /* vag.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E4215C79EC000A5D3D /* vag.c */; }; 834FE10E215C79ED000A5D3D /* ahv.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E5215C79EC000A5D3D /* ahv.c */; }; @@ -324,7 +322,6 @@ 836F6FC018BDC2190095E648 /* p3d.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8418BDC2180095E648 /* p3d.c */; }; 836F6FC118BDC2190095E648 /* pc_adp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8518BDC2180095E648 /* pc_adp.c */; }; 836F6FC218BDC2190095E648 /* pc_mxst.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8618BDC2180095E648 /* pc_mxst.c */; }; - 836F6FC418BDC2190095E648 /* pc_snds.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8818BDC2180095E648 /* pc_snds.c */; }; 836F6FC718BDC2190095E648 /* pona.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8B18BDC2180095E648 /* pona.c */; }; 836F6FC818BDC2190095E648 /* pos.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8C18BDC2180095E648 /* pos.c */; }; 836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */; }; @@ -346,9 +343,7 @@ 836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9F18BDC2180095E648 /* ps2_hsf.c */; }; 836F6FDC18BDC2190095E648 /* ps2_iab.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA018BDC2180095E648 /* ps2_iab.c */; }; 836F6FDE18BDC2190095E648 /* ps2_ild.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA218BDC2180095E648 /* ps2_ild.c */; }; - 836F6FDF18BDC2190095E648 /* ps2_int.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA318BDC2180095E648 /* ps2_int.c */; }; 836F6FE018BDC2190095E648 /* ps2_joe.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA418BDC2180095E648 /* ps2_joe.c */; }; - 836F6FE118BDC2190095E648 /* ps2_jstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA518BDC2180095E648 /* ps2_jstm.c */; }; 836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA618BDC2180095E648 /* ps2_kces.c */; }; 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 */; }; @@ -385,8 +380,6 @@ 836F701118BDC2190095E648 /* ps3_cps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED518BDC2190095E648 /* ps3_cps.c */; }; 836F701218BDC2190095E648 /* ps3_ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED618BDC2190095E648 /* ps3_ivag.c */; }; 836F701518BDC2190095E648 /* ps3_past.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED918BDC2190095E648 /* ps3_past.c */; }; - 836F701B18BDC2190095E648 /* psx_gms.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EDF18BDC2190095E648 /* psx_gms.c */; }; - 836F701D18BDC2190095E648 /* raw.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE118BDC2190095E648 /* raw.c */; }; 836F701E18BDC2190095E648 /* redspark.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE218BDC2190095E648 /* redspark.c */; }; 836F701F18BDC2190095E648 /* riff.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE318BDC2190095E648 /* riff.c */; }; 836F702018BDC2190095E648 /* rkv.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE418BDC2190095E648 /* rkv.c */; }; @@ -427,9 +420,6 @@ 836F704618BDC2190095E648 /* x360_tra.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0A18BDC2190095E648 /* x360_tra.c */; }; 836F704718BDC2190095E648 /* xbox_hlwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0B18BDC2190095E648 /* xbox_hlwav.c */; }; 836F704818BDC2190095E648 /* xbox_ims.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0C18BDC2190095E648 /* xbox_ims.c */; }; - 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0E18BDC2190095E648 /* xbox_wavm.c */; }; - 836F704B18BDC2190095E648 /* xbox_xmu.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0F18BDC2190095E648 /* xbox_xmu.c */; }; - 836F704C18BDC2190095E648 /* xbox_xvas.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1018BDC2190095E648 /* xbox_xvas.c */; }; 836F704E18BDC2190095E648 /* xss.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1218BDC2190095E648 /* xss.c */; }; 836F704F18BDC2190095E648 /* xwb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1318BDC2190095E648 /* xwb.c */; }; 836F705018BDC2190095E648 /* ydsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1418BDC2190095E648 /* ydsp.c */; }; @@ -452,6 +442,35 @@ 8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8375737221F9507D00F01AF5 /* oki_decoder.c */; }; 8375737621F950ED00F01AF5 /* gin.c in Sources */ = {isa = PBXBuildFile; fileRef = 8375737421F950EC00F01AF5 /* gin.c */; }; 8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */; }; + 837CEA7823487E2500E62A4A /* ptadpcm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEA7523487E2400E62A4A /* ptadpcm_decoder.c */; }; + 837CEA7923487E2500E62A4A /* ubi_adpcm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */; }; + 837CEA7A23487E2500E62A4A /* ffmpeg_decoder_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEA7723487E2400E62A4A /* ffmpeg_decoder_utils.c */; }; + 837CEAD823487E8300E62A4A /* bmp_konami.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAD423487E8300E62A4A /* bmp_konami.c */; }; + 837CEAD923487E8300E62A4A /* acb_utf.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAD523487E8300E62A4A /* acb_utf.h */; }; + 837CEADA23487E8300E62A4A /* acb.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAD623487E8300E62A4A /* acb.c */; }; + 837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAD723487E8300E62A4A /* bgw_streamfile.h */; }; + 837CEAF123487F2C00E62A4A /* xvas.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADC23487F2900E62A4A /* xvas.c */; }; + 837CEAF223487F2C00E62A4A /* raw_pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADD23487F2A00E62A4A /* raw_pcm.c */; }; + 837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */; }; + 837CEAF423487F2C00E62A4A /* xa_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADF23487F2A00E62A4A /* xa_xa30.c */; }; + 837CEAF523487F2C00E62A4A /* ubi_hx.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE023487F2A00E62A4A /* ubi_hx.c */; }; + 837CEAF623487F2C00E62A4A /* mzrt.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE123487F2A00E62A4A /* mzrt.c */; }; + 837CEAF723487F2C00E62A4A /* nub.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE223487F2A00E62A4A /* nub.c */; }; + 837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE323487F2A00E62A4A /* xmv_valve.c */; }; + 837CEAF923487F2C00E62A4A /* xavs.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE423487F2A00E62A4A /* xavs.c */; }; + 837CEAFA23487F2C00E62A4A /* xa_04sw.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE523487F2B00E62A4A /* xa_04sw.c */; }; + 837CEAFB23487F2C00E62A4A /* ima.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE623487F2B00E62A4A /* ima.c */; }; + 837CEAFC23487F2C00E62A4A /* raw_wavm.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE723487F2B00E62A4A /* raw_wavm.c */; }; + 837CEAFD23487F2C00E62A4A /* psf.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE823487F2B00E62A4A /* psf.c */; }; + 837CEAFE23487F2C00E62A4A /* jstm_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAE923487F2B00E62A4A /* jstm_streamfile.h */; }; + 837CEAFF23487F2C00E62A4A /* raw_snds.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAEA23487F2B00E62A4A /* raw_snds.c */; }; + 837CEB0023487F2C00E62A4A /* smk.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAEB23487F2B00E62A4A /* smk.c */; }; + 837CEB0123487F2C00E62A4A /* xmu.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAEC23487F2C00E62A4A /* xmu.c */; }; + 837CEB0223487F2C00E62A4A /* raw_int.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAED23487F2C00E62A4A /* raw_int.c */; }; + 837CEB0323487F2C00E62A4A /* xavs_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAEE23487F2C00E62A4A /* xavs_streamfile.h */; }; + 837CEB0423487F2C00E62A4A /* jstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAEF23487F2C00E62A4A /* jstm.c */; }; + 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */; }; + 837CEB072348809400E62A4A /* seb.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEB062348809400E62A4A /* seb.c */; }; 838BDB641D3AF08C0022CA6F /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB611D3AF08C0022CA6F /* libavcodec.a */; }; 838BDB651D3AF08C0022CA6F /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB621D3AF08C0022CA6F /* libavformat.a */; }; 838BDB661D3AF08C0022CA6F /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB631D3AF08C0022CA6F /* libavutil.a */; }; @@ -502,7 +521,6 @@ 83AA5D241F6E2F9C0020821C /* awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D201F6E2F9B0020821C /* awc.c */; }; 83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA5D211F6E2F9C0020821C /* hca_keys.h */; }; 83AA5D271F6E2F9C0020821C /* stm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D231F6E2F9C0020821C /* stm.c */; }; - 83AB8C751E8072A100086084 /* nub_vag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AB8C731E8072A100086084 /* nub_vag.c */; }; 83AB8C761E8072A100086084 /* x360_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AB8C741E8072A100086084 /* x360_ast.c */; }; 83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BAFB6B19F45EB3005DAB60 /* bfstm.c */; }; 83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C727FB22BC893800678B4A /* xwb_xsb.h */; }; @@ -529,8 +547,6 @@ 83C7282822BC8C1500678B4A /* mixing.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C7282422BC8C1400678B4A /* mixing.h */; }; 83C7282922BC8C1500678B4A /* mixing.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C7282522BC8C1400678B4A /* mixing.c */; }; 83C7282A22BC8C1500678B4A /* plugins.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C7282622BC8C1400678B4A /* plugins.c */; }; - 83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CAB8E11F0B0745001BC993 /* wii_04sw.c */; }; - 83CAB8E31F0B0755001BC993 /* pc_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */; }; 83CD428A1F787879000F77BE /* libswresample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83CD42851F787878000F77BE /* libswresample.a */; }; 83D731101A7394BF00CA1366 /* g7221.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D730EB1A738EB300CA1366 /* g7221.framework */; }; 83D731111A7394D300CA1366 /* g7221.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D730EB1A738EB300CA1366 /* g7221.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -783,7 +799,6 @@ 832BF81921E0514A006F50F1 /* xopus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xopus.c; sourceTree = ""; }; 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys_awb.h; sourceTree = ""; }; 832BF81B21E0514B006F50F1 /* nus3audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3audio.c; sourceTree = ""; }; - 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_xma.c; sourceTree = ""; }; 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = ""; }; 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = ""; }; 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; @@ -865,7 +880,6 @@ 834FE0DE215C79EB000A5D3D /* mib_mih.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mib_mih.c; sourceTree = ""; }; 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hd3_bd3.c; sourceTree = ""; }; 834FE0E0215C79EB000A5D3D /* idsp_ie.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idsp_ie.c; sourceTree = ""; }; - 834FE0E1215C79EB000A5D3D /* nub_idsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_idsp.c; sourceTree = ""; }; 834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb5_interleave_streamfile.h; sourceTree = ""; }; 834FE0E4215C79EC000A5D3D /* vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vag.c; sourceTree = ""; }; 834FE0E5215C79EC000A5D3D /* ahv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ahv.c; sourceTree = ""; }; @@ -992,7 +1006,6 @@ 836F6E8418BDC2180095E648 /* p3d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = p3d.c; sourceTree = ""; }; 836F6E8518BDC2180095E648 /* pc_adp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_adp.c; sourceTree = ""; }; 836F6E8618BDC2180095E648 /* pc_mxst.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_mxst.c; sourceTree = ""; }; - 836F6E8818BDC2180095E648 /* pc_snds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_snds.c; sourceTree = ""; }; 836F6E8B18BDC2180095E648 /* pona.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pona.c; sourceTree = ""; }; 836F6E8C18BDC2180095E648 /* pos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pos.c; sourceTree = ""; }; 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_2pfs.c; sourceTree = ""; }; @@ -1014,9 +1027,7 @@ 836F6E9F18BDC2180095E648 /* ps2_hsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_hsf.c; sourceTree = ""; }; 836F6EA018BDC2180095E648 /* ps2_iab.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_iab.c; sourceTree = ""; }; 836F6EA218BDC2180095E648 /* ps2_ild.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ild.c; sourceTree = ""; }; - 836F6EA318BDC2180095E648 /* ps2_int.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_int.c; sourceTree = ""; }; 836F6EA418BDC2180095E648 /* ps2_joe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_joe.c; sourceTree = ""; }; - 836F6EA518BDC2180095E648 /* ps2_jstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_jstm.c; sourceTree = ""; }; 836F6EA618BDC2180095E648 /* ps2_kces.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_kces.c; sourceTree = ""; }; 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 = ""; }; @@ -1053,8 +1064,6 @@ 836F6ED518BDC2190095E648 /* ps3_cps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_cps.c; sourceTree = ""; }; 836F6ED618BDC2190095E648 /* ps3_ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_ivag.c; sourceTree = ""; }; 836F6ED918BDC2190095E648 /* ps3_past.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_past.c; sourceTree = ""; }; - 836F6EDF18BDC2190095E648 /* psx_gms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psx_gms.c; sourceTree = ""; }; - 836F6EE118BDC2190095E648 /* raw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw.c; sourceTree = ""; }; 836F6EE218BDC2190095E648 /* redspark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = redspark.c; sourceTree = ""; }; 836F6EE318BDC2190095E648 /* riff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = riff.c; sourceTree = ""; }; 836F6EE418BDC2190095E648 /* rkv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rkv.c; sourceTree = ""; }; @@ -1095,9 +1104,6 @@ 836F6F0A18BDC2190095E648 /* x360_tra.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_tra.c; sourceTree = ""; }; 836F6F0B18BDC2190095E648 /* xbox_hlwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_hlwav.c; sourceTree = ""; }; 836F6F0C18BDC2190095E648 /* xbox_ims.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_ims.c; sourceTree = ""; }; - 836F6F0E18BDC2190095E648 /* xbox_wavm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_wavm.c; sourceTree = ""; }; - 836F6F0F18BDC2190095E648 /* xbox_xmu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xmu.c; sourceTree = ""; }; - 836F6F1018BDC2190095E648 /* xbox_xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xvas.c; sourceTree = ""; }; 836F6F1218BDC2190095E648 /* xss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xss.c; sourceTree = ""; }; 836F6F1318BDC2190095E648 /* xwb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwb.c; sourceTree = ""; }; 836F6F1418BDC2190095E648 /* ydsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ydsp.c; sourceTree = ""; }; @@ -1120,6 +1126,35 @@ 8375737221F9507D00F01AF5 /* oki_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = oki_decoder.c; sourceTree = ""; }; 8375737421F950EC00F01AF5 /* gin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gin.c; sourceTree = ""; }; 8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_sb_streamfile.h; sourceTree = ""; }; + 837CEA7523487E2400E62A4A /* ptadpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ptadpcm_decoder.c; sourceTree = ""; }; + 837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_adpcm_decoder.c; sourceTree = ""; }; + 837CEA7723487E2400E62A4A /* ffmpeg_decoder_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils.c; sourceTree = ""; }; + 837CEAD423487E8300E62A4A /* bmp_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bmp_konami.c; sourceTree = ""; }; + 837CEAD523487E8300E62A4A /* acb_utf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acb_utf.h; sourceTree = ""; }; + 837CEAD623487E8300E62A4A /* acb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acb.c; sourceTree = ""; }; + 837CEAD723487E8300E62A4A /* bgw_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bgw_streamfile.h; sourceTree = ""; }; + 837CEADC23487F2900E62A4A /* xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvas.c; sourceTree = ""; }; + 837CEADD23487F2A00E62A4A /* raw_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_pcm.c; sourceTree = ""; }; + 837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mzrt_streamfile.h; sourceTree = ""; }; + 837CEADF23487F2A00E62A4A /* xa_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_xa30.c; sourceTree = ""; }; + 837CEAE023487F2A00E62A4A /* ubi_hx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_hx.c; sourceTree = ""; }; + 837CEAE123487F2A00E62A4A /* mzrt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mzrt.c; sourceTree = ""; }; + 837CEAE223487F2A00E62A4A /* nub.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub.c; sourceTree = ""; }; + 837CEAE323487F2A00E62A4A /* xmv_valve.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmv_valve.c; sourceTree = ""; }; + 837CEAE423487F2A00E62A4A /* xavs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xavs.c; sourceTree = ""; }; + 837CEAE523487F2B00E62A4A /* xa_04sw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_04sw.c; sourceTree = ""; }; + 837CEAE623487F2B00E62A4A /* ima.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ima.c; sourceTree = ""; }; + 837CEAE723487F2B00E62A4A /* raw_wavm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_wavm.c; sourceTree = ""; }; + 837CEAE823487F2B00E62A4A /* psf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psf.c; sourceTree = ""; }; + 837CEAE923487F2B00E62A4A /* jstm_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jstm_streamfile.h; sourceTree = ""; }; + 837CEAEA23487F2B00E62A4A /* raw_snds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_snds.c; sourceTree = ""; }; + 837CEAEB23487F2B00E62A4A /* smk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smk.c; sourceTree = ""; }; + 837CEAEC23487F2C00E62A4A /* xmu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmu.c; sourceTree = ""; }; + 837CEAED23487F2C00E62A4A /* raw_int.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_int.c; sourceTree = ""; }; + 837CEAEE23487F2C00E62A4A /* xavs_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xavs_streamfile.h; sourceTree = ""; }; + 837CEAEF23487F2C00E62A4A /* jstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = jstm.c; sourceTree = ""; }; + 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqex_sead_streamfile.h; sourceTree = ""; }; + 837CEB062348809400E62A4A /* seb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = seb.c; sourceTree = ""; }; 838BDB611D3AF08C0022CA6F /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = ""; }; 838BDB621D3AF08C0022CA6F /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = ""; }; 838BDB631D3AF08C0022CA6F /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = ""; }; @@ -1169,7 +1204,6 @@ 83AA5D201F6E2F9B0020821C /* awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awc.c; sourceTree = ""; }; 83AA5D211F6E2F9C0020821C /* hca_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys.h; sourceTree = ""; }; 83AA5D231F6E2F9C0020821C /* stm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stm.c; sourceTree = ""; }; - 83AB8C731E8072A100086084 /* nub_vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_vag.c; sourceTree = ""; }; 83AB8C741E8072A100086084 /* x360_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_ast.c; sourceTree = ""; }; 83BAFB6B19F45EB3005DAB60 /* bfstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfstm.c; sourceTree = ""; }; 83C727FB22BC893800678B4A /* xwb_xsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xwb_xsb.h; sourceTree = ""; }; @@ -1196,8 +1230,6 @@ 83C7282422BC8C1400678B4A /* mixing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mixing.h; sourceTree = ""; }; 83C7282522BC8C1400678B4A /* mixing.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mixing.c; sourceTree = ""; }; 83C7282622BC8C1400678B4A /* plugins.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = plugins.c; sourceTree = ""; }; - 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pc_xa30.c; sourceTree = ""; }; - 83CAB8E11F0B0745001BC993 /* wii_04sw.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wii_04sw.c; sourceTree = ""; }; 83CD42851F787878000F77BE /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswresample.a; path = ../../ThirdParty/ffmpeg/lib/libswresample.a; sourceTree = ""; }; 83D730E51A738EB200CA1366 /* g7221.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = g7221.xcodeproj; path = ../g7221/g7221.xcodeproj; sourceTree = ""; }; 83D731381A74968900CA1366 /* g719.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = g719.xcodeproj; path = ../g719/g719.xcodeproj; sourceTree = ""; }; @@ -1388,6 +1420,7 @@ 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */, 8306B08320984517000302D4 /* fadpcm_decoder.c */, 834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */, + 837CEA7723487E2400E62A4A /* ffmpeg_decoder_utils.c */, 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */, 836F6DE918BDC2180095E648 /* g72x_state.h */, 83D7318B1A749EEE00CA1366 /* g719_decoder.c */, @@ -1420,8 +1453,10 @@ 836F6DF918BDC2180095E648 /* pcm_decoder.c */, 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */, 836F6DFA18BDC2180095E648 /* psx_decoder.c */, + 837CEA7523487E2400E62A4A /* ptadpcm_decoder.c */, 836F6DFB18BDC2180095E648 /* SASSC_decoder.c */, 836F6DFC18BDC2180095E648 /* sdx2_decoder.c */, + 837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */, 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */, 839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */, 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */, @@ -1500,6 +1535,8 @@ 834FE0C8215C79E7000A5D3D /* a2m.c */, 8306B0C82098458D000302D4 /* aax_utf.h */, 836F6E2A18BDC2180095E648 /* aax.c */, + 837CEAD523487E8300E62A4A /* acb_utf.h */, + 837CEAD623487E8300E62A4A /* acb.c */, 836F6E2B18BDC2180095E648 /* acm.c */, 834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */, 836F6E2C18BDC2180095E648 /* ads.c */, @@ -1531,8 +1568,10 @@ 836F6E3718BDC2180095E648 /* bcstm.c */, 83BAFB6B19F45EB3005DAB60 /* bfstm.c */, 83A5F75E198DF021009AF94C /* bfwav.c */, + 837CEAD723487E8300E62A4A /* bgw_streamfile.h */, 836F6E3818BDC2180095E648 /* bgw.c */, 83299FCE1E7660C7003A3242 /* bik.c */, + 837CEAD423487E8300E62A4A /* bmp_konami.c */, 834FE0CA215C79E7000A5D3D /* bnk_sony.c */, 836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3A18BDC2180095E648 /* brstm.c */, @@ -1594,11 +1633,14 @@ 836F6E5318BDC2180095E648 /* his.c */, 834FE0E0215C79EB000A5D3D /* idsp_ie.c */, 83C7280922BC893C00678B4A /* ikm.c */, + 837CEAE623487F2B00E62A4A /* ima.c */, 832BF81121E05149006F50F1 /* imc.c */, 836F6E5518BDC2180095E648 /* ios_psnd.c */, 836F6E5618BDC2180095E648 /* ish_isd.c */, 836F6E5718BDC2180095E648 /* ivaud.c */, 836F6E5818BDC2180095E648 /* ivb.c */, + 837CEAE923487F2B00E62A4A /* jstm_streamfile.h */, + 837CEAEF23487F2C00E62A4A /* jstm.c */, 834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */, 83A21F83201D8981000F04B9 /* kma9.c */, 836F6E5918BDC2180095E648 /* kraw.c */, @@ -1630,6 +1672,8 @@ 836F6E6318BDC2180095E648 /* musc.c */, 836F6E6418BDC2180095E648 /* musx.c */, 836F6E6518BDC2180095E648 /* myspd.c */, + 837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */, + 837CEAE123487F2A00E62A4A /* mzrt.c */, 8349A9061FE6258100E26435 /* naac.c */, 836F6E6618BDC2180095E648 /* naomi_adpcm.c */, 836F6E6718BDC2180095E648 /* naomi_spsd.c */, @@ -1659,9 +1703,7 @@ 836F6E7E18BDC2180095E648 /* ngc_ymf.c */, 836F6E7F18BDC2180095E648 /* ngca.c */, 83C727FC22BC893900678B4A /* nps.c */, - 834FE0E1215C79EB000A5D3D /* nub_idsp.c */, - 83AB8C731E8072A100086084 /* nub_vag.c */, - 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */, + 837CEAE223487F2A00E62A4A /* nub.c */, 832BF81B21E0514B006F50F1 /* nus3audio.c */, 834FE0D1215C79E9000A5D3D /* nus3bank.c */, 836F6E8118BDC2180095E648 /* nwa.c */, @@ -1683,8 +1725,6 @@ 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */, 8349A8F01FE6257C00E26435 /* pc_ast.c */, 836F6E8618BDC2180095E648 /* pc_mxst.c */, - 836F6E8818BDC2180095E648 /* pc_snds.c */, - 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */, 8306B0D12098458F000302D4 /* pcm_sre.c */, 836F6E8B18BDC2180095E648 /* pona.c */, 836F6E8C18BDC2180095E648 /* pos.c */, @@ -1710,9 +1750,7 @@ 836F6E9F18BDC2180095E648 /* ps2_hsf.c */, 836F6EA018BDC2180095E648 /* ps2_iab.c */, 836F6EA218BDC2180095E648 /* ps2_ild.c */, - 836F6EA318BDC2180095E648 /* ps2_int.c */, 836F6EA418BDC2180095E648 /* ps2_joe.c */, - 836F6EA518BDC2180095E648 /* ps2_jstm.c */, 836F6EA618BDC2180095E648 /* ps2_kces.c */, 836F6EA818BDC2180095E648 /* ps2_leg.c */, 836F6EA918BDC2180095E648 /* ps2_lpcm.c */, @@ -1754,9 +1792,12 @@ 836F6ED518BDC2190095E648 /* ps3_cps.c */, 836F6ED618BDC2190095E648 /* ps3_ivag.c */, 836F6ED918BDC2190095E648 /* ps3_past.c */, - 836F6EDF18BDC2190095E648 /* psx_gms.c */, + 837CEAE823487F2B00E62A4A /* psf.c */, 83997F5722D9569E00633184 /* rad.c */, - 836F6EE118BDC2190095E648 /* raw.c */, + 837CEAED23487F2C00E62A4A /* raw_int.c */, + 837CEADD23487F2A00E62A4A /* raw_pcm.c */, + 837CEAEA23487F2B00E62A4A /* raw_snds.c */, + 837CEAE723487F2B00E62A4A /* raw_wavm.c */, 836F6EE218BDC2190095E648 /* redspark.c */, 834FE0D9215C79EA000A5D3D /* rfrm.c */, 836F6EE318BDC2190095E648 /* riff.c */, @@ -1776,6 +1817,7 @@ 836F6EEE18BDC2190095E648 /* sd9.c */, 834FE0E6215C79EC000A5D3D /* sdf.c */, 836F6EEF18BDC2190095E648 /* sdt.c */, + 837CEB062348809400E62A4A /* seb.c */, 836F6EF018BDC2190095E648 /* seg.c */, 83C7280422BC893B00678B4A /* sfh_streamfile.h */, 83C7280822BC893C00678B4A /* sfh.c */, @@ -1784,6 +1826,7 @@ 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, 836F6EF218BDC2190095E648 /* sli.c */, 8306B0D32098458F000302D4 /* smc_smh.c */, + 837CEAEB23487F2B00E62A4A /* smk.c */, 83F0AA5C21E2028B004BBC04 /* smp.c */, 8306B0C72098458D000302D4 /* smv.c */, 83A21F82201D8981000F04B9 /* sps_n1.c */, @@ -1791,6 +1834,7 @@ 834FE0D6215C79E9000A5D3D /* sqex_scd_sscf.c */, 8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */, 836F6EF418BDC2190095E648 /* sqex_scd.c */, + 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */, 83A21F84201D8981000F04B9 /* sqex_sead.c */, 8306B0C12098458C000302D4 /* sthd.c */, 83AA5D231F6E2F9C0020821C /* stm.c */, @@ -1811,6 +1855,7 @@ 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */, 8306B0D420984590000302D4 /* ubi_bao.c */, 836F6EFC18BDC2190095E648 /* ubi_ckd.c */, + 837CEAE023487F2A00E62A4A /* ubi_hx.c */, 8306B0D720984590000302D4 /* ubi_jade.c */, 8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */, 8306B0CA2098458E000302D4 /* ubi_lyn.c */, @@ -1837,7 +1882,6 @@ 8306B0D02098458F000302D4 /* wave_segmented.c */, 8306B0C92098458E000302D4 /* wave.c */, 834FE0D0215C79E8000A5D3D /* wavebatch.c */, - 83CAB8E11F0B0745001BC993 /* wii_04sw.c */, 836F6F0018BDC2190095E648 /* wii_bns.c */, 836F6F0118BDC2190095E648 /* wii_mus.c */, 836F6F0218BDC2190095E648 /* wii_ras.c */, @@ -1854,16 +1898,19 @@ 831BA6151EAC61A500CF89B0 /* x360_cxs.c */, 831BA6171EAC61A500CF89B0 /* x360_pasx.c */, 836F6F0A18BDC2190095E648 /* x360_tra.c */, + 837CEAE523487F2B00E62A4A /* xa_04sw.c */, + 837CEADF23487F2A00E62A4A /* xa_xa30.c */, 832BF81521E0514A006F50F1 /* xa.c */, 834FE0D2215C79E9000A5D3D /* xau_konami.c */, 833A7A2D1ED11961003EC53E /* xau.c */, + 837CEAEE23487F2C00E62A4A /* xavs_streamfile.h */, + 837CEAE423487F2A00E62A4A /* xavs.c */, 836F6F0B18BDC2190095E648 /* xbox_hlwav.c */, 836F6F0C18BDC2190095E648 /* xbox_ims.c */, - 836F6F0E18BDC2190095E648 /* xbox_wavm.c */, - 836F6F0F18BDC2190095E648 /* xbox_xmu.c */, - 836F6F1018BDC2190095E648 /* xbox_xvas.c */, 8350C0541E071881009E0A93 /* xma.c */, 834FE0DC215C79EA000A5D3D /* xmd.c */, + 837CEAEC23487F2C00E62A4A /* xmu.c */, + 837CEAE323487F2A00E62A4A /* xmv_valve.c */, 830EBE112004656E0023AA10 /* xnb.c */, 832BF81921E0514A006F50F1 /* xopus.c */, 832BF80A21E05148006F50F1 /* xpcm.c */, @@ -1871,6 +1918,7 @@ 836F6F1218BDC2190095E648 /* xss.c */, 834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */, 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, + 837CEADC23487F2900E62A4A /* xvas.c */, 83C727FB22BC893800678B4A /* xwb_xsb.h */, 836F6F1318BDC2190095E648 /* xwb.c */, 83A21F7D201D8980000F04B9 /* xwc.c */, @@ -1935,6 +1983,7 @@ 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */, 835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */, + 837CEAFE23487F2C00E62A4A /* jstm_streamfile.h in Headers */, 832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */, 8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */, 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */, @@ -1947,6 +1996,9 @@ 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */, 834FE10C215C79ED000A5D3D /* fsb5_interleave_streamfile.h in Headers */, 839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */, + 837CEAD923487E8300E62A4A /* acb_utf.h in Headers */, + 837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */, + 837CEB0323487F2C00E62A4A /* xavs_streamfile.h in Headers */, 8306B0E320984590000302D4 /* aax_utf.h in Headers */, 8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */, 836F705918BDC2190095E648 /* vgmstream.h in Headers */, @@ -1961,10 +2013,12 @@ 836F705718BDC2190095E648 /* util.h in Headers */, 836F6F9A18BDC2190095E648 /* meta.h in Headers */, 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, + 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */, 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */, 83C7281822BC893D00678B4A /* sfh_streamfile.h in Headers */, 834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */, 8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */, + 837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */, 8349A91B1FE6258200E26435 /* adx_keys.h in Headers */, 836F6F4D18BDC2190095E648 /* layout.h in Headers */, 83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */, @@ -2146,10 +2200,10 @@ 839E21EB1F2EDB0600EE54D7 /* sk_aud.c in Sources */, 834FE0F1215C79ED000A5D3D /* a2m.c in Sources */, 8301659A1F256BD000CA0941 /* txth.c in Sources */, + 837CEA7823487E2500E62A4A /* ptadpcm_decoder.c in Sources */, 8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */, 8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */, - 83CAB8E31F0B0755001BC993 /* pc_xa30.c in Sources */, - 83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */, + 837CEA7923487E2500E62A4A /* ubi_adpcm_decoder.c in Sources */, 834FE0EB215C79ED000A5D3D /* str_wav.c in Sources */, 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */, 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */, @@ -2171,9 +2225,11 @@ 834FE0F4215C79ED000A5D3D /* wsi.c in Sources */, 836F703218BDC2190095E648 /* str_asr.c in Sources */, 836F6FB218BDC2190095E648 /* ngc_gcub.c in Sources */, + 837CEB0223487F2C00E62A4A /* raw_int.c in Sources */, 836F702818BDC2190095E648 /* sat_dvi.c in Sources */, 832BF82D21E0514B006F50F1 /* nus3audio.c in Sources */, 83997F5B22D9569E00633184 /* rad.c in Sources */, + 837CEAF423487F2C00E62A4A /* xa_xa30.c in Sources */, 836F6F2F18BDC2190095E648 /* mtaf_decoder.c in Sources */, 83AA5D161F6E2F600020821C /* ea_xa_decoder.c in Sources */, 836F6F9B18BDC2190095E648 /* mn_str.c in Sources */, @@ -2183,6 +2239,7 @@ 8349A9121FE6258200E26435 /* vsf_tta.c in Sources */, 836F6FCA18BDC2190095E648 /* ps2_adm.c in Sources */, 836F6FA118BDC2190095E648 /* myspd.c in Sources */, + 837CEB0123487F2C00E62A4A /* xmu.c in Sources */, 836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */, 836F703418BDC2190095E648 /* stx.c in Sources */, 83FF0EBC1E93282100C58054 /* wwise.c in Sources */, @@ -2190,6 +2247,7 @@ 836F700018BDC2190095E648 /* ps2_svag.c in Sources */, 836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */, 836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */, + 837CEB072348809400E62A4A /* seb.c in Sources */, 8306B0DD20984590000302D4 /* waf.c in Sources */, 8306B0B320984552000302D4 /* blocked_thp.c in Sources */, 832BF80921E05135006F50F1 /* fag.c in Sources */, @@ -2222,6 +2280,7 @@ 8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */, 83AB8C761E8072A100086084 /* x360_ast.c in Sources */, 834FE105215C79ED000A5D3D /* xmd.c in Sources */, + 837CEADA23487E8300E62A4A /* acb.c in Sources */, 834FE0F6215C79ED000A5D3D /* derf.c in Sources */, 836F6F8B18BDC2190095E648 /* genh.c in Sources */, 83C7281922BC893D00678B4A /* fsb5_fev.c in Sources */, @@ -2258,12 +2317,14 @@ 834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */, 83F0AA6121E2028C004BBC04 /* vsv.c in Sources */, 8351F32F2212B57000A606E4 /* dsf.c in Sources */, + 837CEAF723487F2C00E62A4A /* nub.c in Sources */, 83F0AA5F21E2028C004BBC04 /* smp.c in Sources */, 833A7A2E1ED11961003EC53E /* xau.c in Sources */, 836F6FB518BDC2190095E648 /* ngc_pdt.c in Sources */, 832BF81E21E0514B006F50F1 /* xps.c in Sources */, + 837CEAFD23487F2C00E62A4A /* psf.c in Sources */, 836F6F6C18BDC2190095E648 /* ahx.c in Sources */, - 83AB8C751E8072A100086084 /* nub_vag.c in Sources */, + 837CEAFB23487F2C00E62A4A /* ima.c in Sources */, 836F702D18BDC2190095E648 /* sfl.c in Sources */, 83D7318C1A749EEE00CA1366 /* g719_decoder.c in Sources */, 836F701118BDC2190095E648 /* ps3_cps.c in Sources */, @@ -2279,6 +2340,8 @@ 836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */, 8306B0BC20984552000302D4 /* blocked_vs.c in Sources */, 834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */, + 837CEA7A23487E2500E62A4A /* ffmpeg_decoder_utils.c in Sources */, + 837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */, 836F6F6718BDC2190095E648 /* acm.c in Sources */, 834FE0FD215C79ED000A5D3D /* vpk.c in Sources */, 8306B0DF20984590000302D4 /* ea_wve_ad10.c in Sources */, @@ -2309,10 +2372,10 @@ 834FE0FC215C79ED000A5D3D /* vai.c in Sources */, 83AA5D171F6E2F600020821C /* mpeg_custom_utils_ealayer3.c in Sources */, 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */, - 836F704C18BDC2190095E648 /* xbox_xvas.c in Sources */, 836F6F3918BDC2190095E648 /* SASSC_decoder.c in Sources */, 8306B0A920984552000302D4 /* blocked_adm.c in Sources */, 836F703A18BDC2190095E648 /* vs.c in Sources */, + 837CEB0423487F2C00E62A4A /* jstm.c in Sources */, 83C7282022BC893D00678B4A /* dcs_wav.c in Sources */, 8306B0F220984590000302D4 /* ubi_jade.c in Sources */, 836F6FF918BDC2190095E648 /* ps2_snd.c in Sources */, @@ -2328,6 +2391,7 @@ 834FE10D215C79ED000A5D3D /* vag.c in Sources */, 8349A9131FE6258200E26435 /* ngc_vid1.c in Sources */, 8350C0551E071881009E0A93 /* xma.c in Sources */, + 837CEAF523487F2C00E62A4A /* ubi_hx.c in Sources */, 836F6F3218BDC2190095E648 /* ngc_dsp_decoder.c in Sources */, 836F704218BDC2190095E648 /* wii_sts.c in Sources */, 83C7281722BC893D00678B4A /* mtaf.c in Sources */, @@ -2348,6 +2412,7 @@ 836F6FE018BDC2190095E648 /* ps2_joe.c in Sources */, 83C7282A22BC8C1500678B4A /* plugins.c in Sources */, 832BF82721E0514B006F50F1 /* xa.c in Sources */, + 837CEAD823487E8300E62A4A /* bmp_konami.c in Sources */, 8306B0A220984552000302D4 /* blocked_bdsp.c in Sources */, 836F700118BDC2190095E648 /* ps2_tec.c in Sources */, 832BF82121E0514B006F50F1 /* zsnd.c in Sources */, @@ -2381,6 +2446,7 @@ 834FE0BF215C79A9000A5D3D /* flat.c in Sources */, 836F6F6B18BDC2190095E648 /* agsc.c in Sources */, 836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */, + 837CEB0023487F2C00E62A4A /* smk.c in Sources */, 83C7281022BC893D00678B4A /* nps.c in Sources */, 83C7281E22BC893D00678B4A /* msf.c in Sources */, 836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */, @@ -2404,6 +2470,7 @@ 836F6F3718BDC2190095E648 /* pcm_decoder.c in Sources */, 834FE106215C79ED000A5D3D /* utk.c in Sources */, 831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */, + 837CEAFF23487F2C00E62A4A /* raw_snds.c in Sources */, 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */, 83AA5D271F6E2F9C0020821C /* stm.c in Sources */, 831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */, @@ -2429,6 +2496,7 @@ 831BA61B1EAC61A500CF89B0 /* sgxd.c in Sources */, 838BDB6C1D3AFAB10022CA6F /* ffmpeg_decoder.c in Sources */, 8349A8E11FE6251F00E26435 /* ea_mt_decoder.c in Sources */, + 837CEAF223487F2C00E62A4A /* raw_pcm.c in Sources */, 836F6FA518BDC2190095E648 /* nds_rrds.c in Sources */, 836F702F18BDC2190095E648 /* spt_spd.c in Sources */, 836F704618BDC2190095E648 /* x360_tra.c in Sources */, @@ -2490,9 +2558,9 @@ 8306B0A620984552000302D4 /* blocked_ea_wve_ad10.c in Sources */, 83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */, 834FE0FA215C79ED000A5D3D /* nus3bank.c in Sources */, - 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */, 834FE0B9215C798C000A5D3D /* xmd_decoder.c in Sources */, 8306B0B220984552000302D4 /* blocked_mxch.c in Sources */, + 837CEAFA23487F2C00E62A4A /* xa_04sw.c in Sources */, 836F6F8618BDC2190095E648 /* excitebots.c in Sources */, 836F6FF418BDC2190095E648 /* rws.c in Sources */, 834FE100215C79ED000A5D3D /* svg.c in Sources */, @@ -2500,7 +2568,6 @@ 836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */, 836F6F3C18BDC2190095E648 /* xa_decoder.c in Sources */, 832BF82521E0514B006F50F1 /* ogg_opus.c in Sources */, - 834FE10A215C79ED000A5D3D /* nub_idsp.c in Sources */, 83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */, 836F6F9118BDC2190095E648 /* ios_psnd.c in Sources */, 836F700618BDC2190095E648 /* ps2_vgs.c in Sources */, @@ -2520,12 +2587,13 @@ 8375737621F950ED00F01AF5 /* gin.c in Sources */, 836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */, 836F704818BDC2190095E648 /* xbox_ims.c in Sources */, + 837CEAF623487F2C00E62A4A /* mzrt.c in Sources */, 836F6F7518BDC2190095E648 /* bnsf.c in Sources */, 836F704318BDC2190095E648 /* wpd.c in Sources */, 8349A9081FE6258200E26435 /* ezw.c in Sources */, - 836F6FE118BDC2190095E648 /* ps2_jstm.c in Sources */, 836F6FD918BDC2190095E648 /* ps2_gcm.c in Sources */, 83A21F88201D8981000F04B9 /* ogg_vorbis.c in Sources */, + 837CEAF923487F2C00E62A4A /* xavs.c in Sources */, 836F6F8E18BDC2190095E648 /* halpst.c in Sources */, 836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */, 836F702618BDC2190095E648 /* s14_sss.c in Sources */, @@ -2533,14 +2601,12 @@ 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, 8323894A1D22419B00482226 /* clHCA.c in Sources */, 836F702E18BDC2190095E648 /* sli.c in Sources */, - 836F701D18BDC2190095E648 /* raw.c in Sources */, 836F6FDE18BDC2190095E648 /* ps2_ild.c in Sources */, 836F703E18BDC2190095E648 /* wii_ras.c in Sources */, 834FE0EE215C79ED000A5D3D /* ue4opus.c in Sources */, 836F6FEA18BDC2190095E648 /* ps2_msa.c in Sources */, 836F6F3618BDC2190095E648 /* ogg_vorbis_decoder.c in Sources */, 836F704718BDC2190095E648 /* xbox_hlwav.c in Sources */, - 83345A4F1F8AEB2800B2EAA4 /* nub_xma.c in Sources */, 8306B0E520984590000302D4 /* ubi_lyn.c in Sources */, 836F6F7618BDC2190095E648 /* brstm.c in Sources */, 836F700718BDC2190095E648 /* ps2_vgv.c in Sources */, @@ -2558,14 +2624,15 @@ 836F6F2618BDC2190095E648 /* g7221_decoder.c in Sources */, 834FE0F2215C79ED000A5D3D /* wv6.c in Sources */, 836F6FA218BDC2190095E648 /* naomi_adpcm.c in Sources */, + 837CEAF123487F2C00E62A4A /* xvas.c in Sources */, 836F6FBF18BDC2190095E648 /* otm.c in Sources */, 8306B0B420984552000302D4 /* blocked_tra.c in Sources */, 834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */, + 837CEAFC23487F2C00E62A4A /* raw_wavm.c in Sources */, 834FE0F7215C79ED000A5D3D /* vis.c in Sources */, 83A21F8C201D8982000F04B9 /* kma9.c in Sources */, 8342469420C4D23000926E48 /* h4m.c in Sources */, - 836F6FC418BDC2190095E648 /* pc_snds.c in Sources */, 834FE111215C79ED000A5D3D /* ck.c in Sources */, 836F704E18BDC2190095E648 /* xss.c in Sources */, 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, @@ -2587,13 +2654,10 @@ 836F705018BDC2190095E648 /* ydsp.c in Sources */, 8306B0B720984552000302D4 /* blocked_str_snds.c in Sources */, 836F702718BDC2190095E648 /* sat_baka.c in Sources */, - 836F704B18BDC2190095E648 /* xbox_xmu.c in Sources */, 832389501D2246C300482226 /* hca.c in Sources */, - 836F701B18BDC2190095E648 /* psx_gms.c in Sources */, 8306B0F020984590000302D4 /* atsl.c in Sources */, 832BF82421E0514B006F50F1 /* mul.c in Sources */, 836F700518BDC2190095E648 /* ps2_vbk.c in Sources */, - 836F6FDF18BDC2190095E648 /* ps2_int.c in Sources */, 8306B0AC20984552000302D4 /* blocked_xa.c in Sources */, 8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */, 836F6F7B18BDC2190095E648 /* dc_idvi.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index 324e8b168..3a70c5c64 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -7,7 +7,6 @@ #include "libatrac9.h" #endif - /* opaque struct */ struct atrac9_codec_data { uint8_t *data_buffer; @@ -53,7 +52,8 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) { /* must hold at least one superframe and its samples */ data->data_buffer_size = data->info.superframeSize; - data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size); + /* extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though) */ + data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size + 0x10); data->sample_buffer = calloc(sizeof(sample_t), data->info.channels * data->info.frameSamples * data->info.framesInSuperframe); data->samples_to_discard = cfg->encoder_delay; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 532b6d85d..9b1f378da 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -42,6 +42,7 @@ void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa size_t ima_bytes_to_samples(size_t bytes, int channels); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); size_t xbox_ima_bytes_to_samples(size_t bytes, int channels); +size_t dat4_ima_bytes_to_samples(size_t bytes, int channels); size_t apple_ima4_bytes_to_samples(size_t bytes, int channels); /* ngc_dsp_decoder */ @@ -82,11 +83,13 @@ size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); /* psx_decoder */ void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags); void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); +void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); size_t ps_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty); size_t ps_bytes_to_samples(size_t bytes, int channels); size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels); +int ps_check_format(STREAMFILE *streamFile, off_t offset, size_t max); /* psv_decoder */ void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -106,10 +109,10 @@ void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* sdx2_decoder */ -void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_sdx2_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_cbd2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_cbd2_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_sdx2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_sdx2_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_cbd2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_cbd2_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* ws_decoder */ void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -130,7 +133,7 @@ void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspac long msadpcm_bytes_to_samples(long bytes, int block_size, int channels); /* yamaha_decoder */ -void decode_yamaha(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +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_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); 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); @@ -178,8 +181,21 @@ void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel /* oki_decoder */ void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode); void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t oki_bytes_to_samples(size_t bytes, int channels); +/* ptadpcm_decoder */ +void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); +size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size); + +/* ubi_adpcm_decoder */ +ubi_adpcm_codec_data *init_ubi_adpcm(STREAMFILE *streamFile, off_t offset, int channels); +void decode_ubi_adpcm(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do); +void reset_ubi_adpcm(ubi_adpcm_codec_data *data); +void seek_ubi_adpcm(ubi_adpcm_codec_data *data, int32_t num_sample); +void free_ubi_adpcm(ubi_adpcm_codec_data *data); +int ubi_adpcm_get_samples(ubi_adpcm_codec_data *data); + /* ea_mt_decoder*/ ea_mt_codec_data *init_ea_mt(int channels, int type); ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets); @@ -293,7 +309,24 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data); void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap); + +/* ffmpeg_decoder_utils.c (helper-things) */ +ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay); +ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* out_samples); + + /* ffmpeg_decoder_custom_opus.c (helper-things) */ +typedef struct { + int channels; + int skip; + int sample_rate; + /* multichannel-only */ + int coupled_count; + int stream_count; + int channel_mapping[8]; +} opus_config; + +ffmpeg_codec_data * init_ffmpeg_switch_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config* cfg); ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); @@ -304,13 +337,11 @@ size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *str size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); - #endif /* coding_utils */ int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, size_t chunk_size, uint16_t codec); -int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay); int ffmpeg_make_riff_atrac3plus(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay); int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode); int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size); @@ -349,10 +380,9 @@ void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample); void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); +void xma_fix_raw_samples_hb(VGMSTREAM *vgmstream, STREAMFILE *headerFile, STREAMFILE *bodyFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, int channel_per_stream, int fix_num_samples, int fix_loop_samples); -int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset); - size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align); size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align); size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c index 6201d6bad..422a74188 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c @@ -37,45 +37,6 @@ static uint32_t read_bitsBE_b(off_t bit_offset, int num_bits, STREAMFILE *stream /* ******************************************** */ /* All helpers copy a RIFF header to buf and returns the number of bytes in buf or -1 when buf is not big enough */ -int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) { - uint16_t codec_ATRAC3 = 0x0270; - size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4; - - if (buf_size < riff_size) - return -1; - - memcpy(buf+0x00, "RIFF", 4); - put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ - memcpy(buf+0x08, "WAVE", 4); - - memcpy(buf+0x0c, "fmt ", 4); - put_32bitLE(buf+0x10, 0x20);/*fmt size*/ - put_16bitLE(buf+0x14, codec_ATRAC3); - put_16bitLE(buf+0x16, channels); - put_32bitLE(buf+0x18, sample_rate); - put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */ - put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */ - - put_16bitLE(buf+0x24, 0x0e); /* extra data size */ - put_16bitLE(buf+0x26, 1); /* unknown, always 1 */ - put_16bitLE(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */ - put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */ - put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000); - put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */ - put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */ - put_16bitLE(buf+0x32, 0); /* unknown, always 0 */ - - memcpy(buf+0x34, "fact", 4); - put_32bitLE(buf+0x38, 0x8); /* fact size */ - put_32bitLE(buf+0x3c, sample_count); - put_32bitLE(buf+0x40, encoder_delay); - - memcpy(buf+0x44, "data", 4); - put_32bitLE(buf+0x48, data_size); /* data size */ - - return riff_size; -} - int ffmpeg_make_riff_atrac3plus(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay) { uint16_t codec_ATRAC3plus = 0xfffe; /* wave format extensible */ size_t riff_size = 4+4+ 4 + 0x3c + 0x14 + 4+4; @@ -871,6 +832,11 @@ void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t s #endif } +void xma_fix_raw_samples_hb(VGMSTREAM *vgmstream, STREAMFILE *headerFile, STREAMFILE *bodyFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) { + int channels_per_stream = xma_get_channels_per_stream(headerFile, chunk_offset, vgmstream->channels); + xma_fix_raw_samples_ch(vgmstream, bodyFile, stream_offset, stream_size, channels_per_stream, fix_num_samples, fix_loop_samples); +} + void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) { int channels_per_stream = xma_get_channels_per_stream(streamFile, chunk_offset, vgmstream->channels); xma_fix_raw_samples_ch(vgmstream, streamFile, stream_offset, stream_size, channels_per_stream, fix_num_samples, fix_loop_samples); @@ -983,24 +949,6 @@ void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * out if(out_loop_flag) *out_loop_flag = loop_flag; } -/* manually read from "fact" chunk */ -int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset) { - off_t chunk_offset; - size_t chunk_size, fact_skip_samples = 0; - if (!find_chunk_le(streamFile, 0x66616374, start_offset + 0x0c, 0, &chunk_offset, &chunk_size)) /* find "fact" */ - goto fail; - if (chunk_size == 0x8) { - fact_skip_samples = read_32bitLE(chunk_offset + 0x4, streamFile); - } - else if (chunk_size == 0xc) { - fact_skip_samples = read_32bitLE(chunk_offset + 0x8, streamFile); - } - - return fact_skip_samples; -fail: - return 0; /* meh */ -} - /* ******************************************** */ /* OTHER STUFF */ /* ******************************************** */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index a2c5b1402..569b8c616 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -9,6 +9,11 @@ static volatile int g_ffmpeg_initialized = 0; +static void free_ffmpeg_config(ffmpeg_codec_data *data); +static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int reset); + +static void reset_ffmpeg_internal(ffmpeg_codec_data *data); +static void seek_ffmpeg_internal(ffmpeg_codec_data *data, int32_t num_sample); /* ******************************************** */ /* INTERNAL UTILS */ @@ -47,6 +52,14 @@ static void remap_audio(sample_t *outbuf, int sample_count, int channels, int ch } } +static void invert_audio(sample_t *outbuf, int sample_count, int channels) { + int i; + + for (i = 0; i < sample_count*channels; i++) { + outbuf[i] = -outbuf[i]; + } +} + /* converts codec's samples (can be in any format, ex. Ogg's float32) to PCM16 */ static void convert_audio_pcm16(sample_t *outbuf, const uint8_t *inbuf, int fullSampleCount, int bitsPerSample, int floatingPoint) { int s; @@ -114,7 +127,7 @@ static void convert_audio_pcm16(sample_t *outbuf, const uint8_t *inbuf, int full * Some formats may not seek to 0 even with this, though. */ static int init_seek(ffmpeg_codec_data * data) { - int ret, ts_index, found_first = 0; + int ret, ts_index, packet_count = 0; int64_t ts = 0; /* seek timestamp */ int64_t pos = 0; /* data offset */ int size = 0; /* data size (block align) */ @@ -140,6 +153,7 @@ static int init_seek(ffmpeg_codec_data * data) { /* find the first + second packets to get pos/size */ + packet_count = 0; while (1) { av_packet_unref(pkt); ret = av_read_frame(data->formatCtx, pkt); @@ -148,8 +162,9 @@ static int init_seek(ffmpeg_codec_data * data) { if (pkt->stream_index != data->streamIndex) continue; /* ignore non-selected streams */ - if (!found_first) { - found_first = 1; + //;VGM_LOG("FFMPEG: packet %i, ret=%i, pos=%i, dts=%i\n", packet_count, ret, (int32_t)pkt->pos, (int32_t)pkt->dts); + packet_count++; + if (packet_count == 1) { pos = pkt->pos; ts = pkt->dts; continue; @@ -158,8 +173,13 @@ static int init_seek(ffmpeg_codec_data * data) { break; } } - if (!found_first) + if (packet_count == 0) goto fail; + + /* happens in unseekable formats where FFmpeg doesn't even know its own position */ + if (pos < 0) + goto fail; + /* in rare cases there is only one packet */ //if (size == 0) size = data_end - pos; /* no easy way to know, ignore (most formats don's need size) */ @@ -183,8 +203,10 @@ static int init_seek(ffmpeg_codec_data * data) { test_seek: /* seek to 0 test + move back to beginning, since we just consumed packets */ ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY); - if ( ret < 0 ) + if ( ret < 0 ) { + //char test[1000] = {0}; av_strerror(ret, test, 1000); VGM_LOG("FFMPEG: ret=%i %s\n", ret, test); return ret; /* we can't even reset_vgmstream the file */ + } avcodec_flush_buffers(data->codecCtx); @@ -234,11 +256,6 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int read_size) { return bytes + max_to_copy; } -/* AVIO callback: write stream not needed */ -static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size) { - return -1; -} - /* AVIO callback: seek stream, handling custom data */ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; @@ -295,6 +312,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * return init_ffmpeg_header_offset_subsong(streamFile, header, header_size, start, size, 0); } + /** * Manually init FFmpeg, from a fake header / offset. * @@ -306,29 +324,35 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * */ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong) { char filename[PATH_LIMIT]; - ffmpeg_codec_data * data; - int errcode, i; - int streamIndex, streamCount; + ffmpeg_codec_data * data = NULL; + int errcode; AVStream *stream; - AVCodecParameters *codecPar = NULL; AVRational tb; - /* basic setup */ + /* check values */ + if ((header && !header_size) || (!header && header_size)) + goto fail; + + if (size == 0 || start + size > get_streamfile_size(streamFile)) { + VGM_LOG("FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(streamFile)); + size = get_streamfile_size(streamFile) - start; + } + + + /* ffmpeg global setup */ g_init_ffmpeg(); - data = ( ffmpeg_codec_data * ) calloc(1, sizeof(ffmpeg_codec_data)); + + /* basic setup */ + data = calloc(1, sizeof(ffmpeg_codec_data)); if (!data) return NULL; streamFile->get_name( streamFile, filename, sizeof(filename) ); data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); if (!data->streamfile) goto fail; - /* ignore bad combos */ - if ((header && !header_size) || (!header && header_size)) - goto fail; - /* fake header to trick FFmpeg into demuxing/decoding the stream */ if (header_size > 0) { data->header_size = header_size; @@ -337,91 +361,24 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui } data->start = start; - data->offset = start; + data->offset = data->start; data->size = size; - if (data->size == 0 || data->start + data->size > get_streamfile_size(streamFile)) { - VGM_LOG("FFPMEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(streamFile)); - data->size = get_streamfile_size(streamFile) - data->start; - } data->logical_offset = 0; data->logical_size = data->header_size + data->size; - /* setup IO, attempt to autodetect format and gather some info */ - data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE); - if (!data->buffer) goto fail; - data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek); - if (!data->ioCtx) goto fail; + /* setup FFmpeg's internals, attempt to autodetect format and gather some info */ + errcode = init_ffmpeg_config(data, target_subsong, 0); + if (errcode < 0) goto fail; - data->formatCtx = avformat_alloc_context(); - if (!data->formatCtx) goto fail; - - data->formatCtx->pb = data->ioCtx; - - if ((errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL)) < 0) goto fail; /* autodetect */ - - if ((errcode = avformat_find_stream_info(data->formatCtx, NULL)) < 0) goto fail; + stream = data->formatCtx->streams[data->streamIndex]; - /* find valid audio stream */ - streamIndex = -1; - streamCount = 0; - - for (i = 0; i < data->formatCtx->nb_streams; ++i) { - stream = data->formatCtx->streams[i]; - - if (stream->codecpar && stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - streamCount++; - - /* select Nth audio stream if specified, or first one */ - if (streamIndex < 0 || (target_subsong > 0 && streamCount == target_subsong)) { - codecPar = stream->codecpar; - streamIndex = i; - } - } - - if (i != streamIndex) - stream->discard = AVDISCARD_ALL; /* disable demuxing for other streams */ - } - if (streamCount < target_subsong) goto fail; - if (streamIndex < 0 || !codecPar) goto fail; - - data->streamIndex = streamIndex; - stream = data->formatCtx->streams[streamIndex]; - data->streamCount = streamCount; - - - /* prepare codec and frame/packet buffers */ - data->codecCtx = avcodec_alloc_context3(NULL); - if (!data->codecCtx) goto fail; - - if ((errcode = avcodec_parameters_to_context(data->codecCtx, codecPar)) < 0) goto fail; - - //av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); /* deprecated and seemingly not needed */ - - data->codec = avcodec_find_decoder(data->codecCtx->codec_id); - if (!data->codec) goto fail; - - if ((errcode = avcodec_open2(data->codecCtx, data->codec, NULL)) < 0) goto fail; - - data->lastDecodedFrame = av_frame_alloc(); - if (!data->lastDecodedFrame) goto fail; - av_frame_unref(data->lastDecodedFrame); - - data->lastReadPacket = malloc(sizeof(AVPacket)); - if (!data->lastReadPacket) goto fail; - av_new_packet(data->lastReadPacket, 0); - - data->readNextPacket = 1; - data->bytesConsumedFromDecodedFrame = INT_MAX; - - - - /* other setup */ + /* derive info */ data->sampleRate = data->codecCtx->sample_rate; data->channels = data->codecCtx->channels; + data->bitrate = (int)(data->codecCtx->bit_rate); data->floatingPoint = 0; - switch (data->codecCtx->sample_fmt) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P: @@ -454,9 +411,11 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui goto fail; } - data->bitrate = (int)(data->codecCtx->bit_rate); - data->endOfStream = 0; - data->endOfAudio = 0; + /* setup decode buffer */ + data->sampleBufferBlock = FFMPEG_DEFAULT_SAMPLE_BUFFER_SIZE; + data->sampleBuffer = av_malloc(data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels); + if (!data->sampleBuffer) goto fail; + /* try to guess frames/samples (duration isn't always set) */ tb.num = 1; tb.den = data->codecCtx->sample_rate; @@ -469,19 +428,21 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */ data->frameSize = av_get_audio_frame_duration(data->codecCtx,0); - /* setup decode buffer */ - data->sampleBufferBlock = FFMPEG_DEFAULT_SAMPLE_BUFFER_SIZE; - data->sampleBuffer = av_malloc( data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels ); - if (!data->sampleBuffer) - goto fail; + + /* reset */ + data->readNextPacket = 1; + data->bytesConsumedFromDecodedFrame = INT_MAX; + data->endOfStream = 0; + data->endOfAudio = 0; - /* setup decent seeking for faulty formats */ - errcode = init_seek(data); - if (errcode < 0) { - VGM_LOG("FFMPEG: can't init_seek\n"); - goto fail; - } + /* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc) + * get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */ + if (stream->start_skip_samples) /* samples to skip in the first packet */ + data->skipSamples = stream->start_skip_samples; + else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */ + data->skipSamples = stream->skip_samples; + /* check ways to skip encoder delay/padding, for debugging purposes (some may be old/unused/encoder only/etc) */ VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS @@ -498,21 +459,111 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui //todo: double check Opus behavior - /* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc) - * get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */ - if (stream->start_skip_samples) /* samples to skip in the first packet */ - data->skipSamples = stream->start_skip_samples; - else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */ - data->skipSamples = stream->skip_samples; + /* setup decent seeking for faulty formats */ + errcode = init_seek(data); + if (errcode < 0) { + VGM_LOG("FFMPEG: can't init_seek, error=%i\n", errcode); + /* some formats like Smacker are so buggy that any seeking is impossible (even on video players) + * whatever, we'll just kill and reconstruct FFmpeg's config every time */ + data->force_seek = 1; + reset_ffmpeg_internal(data); /* reset state from trying to seek */ + //stream = data->formatCtx->streams[data->streamIndex]; + } return data; - fail: free_ffmpeg(data); - return NULL; } +static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int reset) { + int errcode = 0; + + /* basic IO/format setup */ + data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE); + if (!data->buffer) goto fail; + + data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, 0, ffmpeg_seek); + if (!data->ioCtx) goto fail; + + data->formatCtx = avformat_alloc_context(); + if (!data->formatCtx) goto fail; + + data->formatCtx->pb = data->ioCtx; + + //data->inputFormatCtx = av_find_input_format("h264"); /* set directly? */ + /* on reset could use AVFormatContext.iformat to reload old format too */ + + errcode = avformat_open_input(&data->formatCtx, NULL /*""*/, NULL, NULL); + if (errcode < 0) goto fail; + + errcode = avformat_find_stream_info(data->formatCtx, NULL); + if (errcode < 0) goto fail; + + /* find valid audio stream and set other streams to discard */ + { + int i, streamIndex, streamCount; + + streamIndex = -1; + streamCount = 0; + if (reset) + streamIndex = data->streamIndex; + + for (i = 0; i < data->formatCtx->nb_streams; ++i) { + AVStream *stream = data->formatCtx->streams[i]; + + if (stream->codecpar && stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + streamCount++; + + /* select Nth audio stream if specified, or first one */ + if (streamIndex < 0 || (target_subsong > 0 && streamCount == target_subsong)) { + streamIndex = i; + } + } + + if (i != streamIndex) + stream->discard = AVDISCARD_ALL; /* disable demuxing for other streams */ + } + if (streamCount < target_subsong) goto fail; + if (streamIndex < 0) goto fail; + + data->streamIndex = streamIndex; + data->streamCount = streamCount; + } + + /* setup codec with stream info */ + data->codecCtx = avcodec_alloc_context3(NULL); + if (!data->codecCtx) goto fail; + + errcode = avcodec_parameters_to_context(data->codecCtx, ((AVStream*)data->formatCtx->streams[data->streamIndex])->codecpar); + if (errcode < 0) goto fail; + + //av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); /* deprecated and seemingly not needed */ + + data->codec = avcodec_find_decoder(data->codecCtx->codec_id); + if (!data->codec) goto fail; + + errcode = avcodec_open2(data->codecCtx, data->codec, NULL); + if (errcode < 0) goto fail; + + /* prepare codec and frame/packet buffers */ + data->lastDecodedFrame = av_frame_alloc(); + if (!data->lastDecodedFrame) goto fail; + av_frame_unref(data->lastDecodedFrame); + + data->lastReadPacket = av_malloc(sizeof(AVPacket)); /* av_packet_alloc? */ + if (!data->lastReadPacket) goto fail; + av_new_packet(data->lastReadPacket, 0); + //av_packet_unref? + + return 0; +fail: + if (errcode < 0) + return errcode; + return -1; +} + + /* decode samples of any kind of FFmpeg format */ void decode_ffmpeg(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels) { ffmpeg_codec_data *data = vgmstream->codec_data; @@ -523,17 +574,22 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_d AVCodecContext *codecCtx = data->codecCtx; AVPacket *packet = data->lastReadPacket; AVFrame *frame = data->lastDecodedFrame; - int planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt); int readNextPacket = data->readNextPacket; int endOfStream = data->endOfStream; int endOfAudio = data->endOfAudio; int bytesConsumedFromDecodedFrame = data->bytesConsumedFromDecodedFrame; + int planar = 0; int bytesPerSample = data->bitsPerSample / 8; int bytesRead, bytesToRead; + if (data->bad_init) { + memset(outbuf, 0, samples_to_do * channels * sizeof(sample)); + return; + } + /* ignore once file is done (but not at endOfStream as FFmpeg can still output samples until endOfAudio) */ if (/*endOfStream ||*/ endOfAudio) { VGM_LOG("FFMPEG: decode after end of audio\n"); @@ -541,6 +597,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_d return; } + planar = av_sample_fmt_is_planar(codecCtx->sample_fmt); bytesRead = 0; bytesToRead = samples_to_do * (bytesPerSample * codecCtx->channels); @@ -587,7 +644,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_d goto end; } } - + readNextPacket = 0; /* got compressed data */ } @@ -677,6 +734,8 @@ end: convert_audio_pcm16(outbuf, data->sampleBuffer, samplesReadNow * channels, data->bitsPerSample, data->floatingPoint); if (data->channel_remap_set) remap_audio(outbuf, samplesReadNow, data->channels, data->channel_remap); + if (data->invert_audio_set) + invert_audio(outbuf, samplesReadNow, data->channels); /* clean buffer when requested more samples than possible */ if (endOfAudio && samplesReadNow < samples_to_do) { @@ -696,21 +755,44 @@ end: /* UTILS */ /* ******************************************** */ +void reset_ffmpeg_internal(ffmpeg_codec_data *data) { + seek_ffmpeg_internal(data, 0); +} void reset_ffmpeg(VGMSTREAM *vgmstream) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; + reset_ffmpeg_internal(vgmstream->codec_data); +} + +void seek_ffmpeg_internal(ffmpeg_codec_data *data, int32_t num_sample) { if (!data) return; - if (data->formatCtx) { - avformat_seek_file(data->formatCtx, data->streamIndex, 0, 0, 0, AVSEEK_FLAG_ANY); + /* Start from 0 and discard samples until sample (slower but not too noticeable). + * Due to various FFmpeg quirks seeking to a sample is erratic in many formats (would need extra steps). */ + + if (data->force_seek) { + int errcode; + + /* kill+redo everything to allow seeking for extra-buggy formats, + * kinda horrid but seems fast enough and very few formats need this */ + + free_ffmpeg_config(data); + + data->offset = data->start; + data->logical_offset = 0; + + errcode = init_ffmpeg_config(data, 0, 1); + if (errcode < 0) goto fail; } - if (data->codecCtx) { + else { + avformat_seek_file(data->formatCtx, data->streamIndex, 0, 0, 0, AVSEEK_FLAG_ANY); avcodec_flush_buffers(data->codecCtx); } + + data->samplesToDiscard = num_sample; + data->readNextPacket = 1; data->bytesConsumedFromDecodedFrame = INT_MAX; data->endOfStream = 0; data->endOfAudio = 0; - data->samplesToDiscard = 0; /* consider skip samples (encoder delay), if manually set (otherwise let FFmpeg handle it) */ if (data->skipSamplesSet) { @@ -721,71 +803,64 @@ void reset_ffmpeg(VGMSTREAM *vgmstream) { data->samplesToDiscard += data->skipSamples; } + + return; +fail: + VGM_LOG("FFMPEG: error during force_seek\n"); + data->bad_init = 1; /* internals were probably free'd */ } void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - int64_t ts; - if (!data) - return; - - /* Start from 0 and discard samples until loop_start (slower but not too noticeable). - * Due to various FFmpeg quirks seeking to a sample is erratic in many formats (would need extra steps). */ - data->samplesToDiscard = num_sample; - ts = 0; - - avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY); - avcodec_flush_buffers(data->codecCtx); - - data->readNextPacket = 1; - data->bytesConsumedFromDecodedFrame = INT_MAX; - data->endOfStream = 0; - data->endOfAudio = 0; - - /* consider skip samples (encoder delay), if manually set (otherwise let FFmpeg handle it) */ - if (data->skipSamplesSet) { - AVStream *stream = data->formatCtx->streams[data->streamIndex]; - /* sometimes (ex. AAC) after seeking to the first packet skip_samples is restored, but we want our value */ - stream->skip_samples = 0; - stream->start_skip_samples = 0; - - data->samplesToDiscard += data->skipSamples; - } + seek_ffmpeg_internal(vgmstream->codec_data, num_sample); } -void free_ffmpeg(ffmpeg_codec_data *data) { + +static void free_ffmpeg_config(ffmpeg_codec_data *data) { if (data == NULL) return; if (data->lastReadPacket) { av_packet_unref(data->lastReadPacket); - free(data->lastReadPacket); + av_free(data->lastReadPacket); data->lastReadPacket = NULL; } if (data->lastDecodedFrame) { + av_frame_unref(data->lastDecodedFrame); av_free(data->lastDecodedFrame); data->lastDecodedFrame = NULL; } if (data->codecCtx) { avcodec_close(data->codecCtx); - avcodec_free_context(&(data->codecCtx)); + avcodec_free_context(&data->codecCtx); data->codecCtx = NULL; } if (data->formatCtx) { - avformat_close_input(&(data->formatCtx)); + avformat_close_input(&data->formatCtx); + //avformat_free_context(data->formatCtx); /* done in close_input */ data->formatCtx = NULL; } if (data->ioCtx) { - // buffer passed in is occasionally freed and replaced. - // the replacement must be freed as well. + /* buffer passed in is occasionally freed and replaced. + // the replacement must be free'd as well (below) */ data->buffer = data->ioCtx->buffer; - av_free(data->ioCtx); + avio_context_free(&data->ioCtx); + //av_free(data->ioCtx); /* done in context_free (same thing) */ data->ioCtx = NULL; } if (data->buffer) { av_free(data->buffer); data->buffer = NULL; } + + //todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option (not happening in gcc builds) +} + +void free_ffmpeg(ffmpeg_codec_data *data) { + if (data == NULL) + return; + + free_ffmpeg_config(data); + if (data->sampleBuffer) { av_free(data->sampleBuffer); data->sampleBuffer = NULL; @@ -794,10 +869,8 @@ void free_ffmpeg(ffmpeg_codec_data *data) { av_free(data->header_insert_block); data->header_insert_block = NULL; } - if (data->streamfile) { - close_streamfile(data->streamfile); - data->streamfile = NULL; - } + + close_streamfile(data->streamfile); free(data); } @@ -813,7 +886,7 @@ void free_ffmpeg(ffmpeg_codec_data *data) { */ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) { AVStream *stream = NULL; - if (!data->formatCtx) + if (!data || !data->formatCtx) return; /* overwrite FFmpeg's skip samples */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c index 8f992b863..ed5b57b92 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c @@ -2,18 +2,22 @@ #include "../streamfile.h" #include +#ifdef VGM_USE_FFMPEG + /** * Transmogrifies custom Opus (no Ogg layer and custom packet headers) into is Xiph Opus, creating * valid Ogg pages with single Opus packets. * Uses an intermediate buffer to make full Ogg pages, since checksums are calculated with the whole page. * + * Mostly as an experiment/demonstration, until some details are sorted out before adding actual libopus. + * * Info, CRC and stuff: * https://www.opus-codec.org/docs/ * https://tools.ietf.org/html/rfc7845.html * https://github.com/hcs64/ww2ogg */ -static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate); +static size_t make_oggs_first(uint8_t * buf, int buf_size, opus_config *cfg); static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule); static size_t opus_get_packet_samples(const uint8_t * buf, int len); static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile); @@ -230,7 +234,7 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { /* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */ -static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, int channels, int skip, int sample_rate, off_t stream_offset, size_t stream_size, opus_type_t type) { +static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) { STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; opus_io_data io_data = {0}; size_t io_data_size = sizeof(opus_io_data); @@ -239,7 +243,7 @@ static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, int channels, i io_data.stream_offset = stream_offset; io_data.stream_size = stream_size; io_data.physical_offset = stream_offset; - io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), channels, skip, sample_rate); + io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), cfg); if (!io_data.head_size) goto fail; io_data.sequence = 2; io_data.logical_size = opus_io_size(streamFile, &io_data); /* force init */ @@ -405,26 +409,16 @@ fail: return 0; } -static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) { +static size_t make_opus_header(uint8_t * buf, int buf_size, opus_config *cfg) { size_t header_size = 0x13; - int mapping_family = 0; /* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */ -#if 0 - int stream_count = 1; /* number of internal mono/stereo streams (N mono/stereo streams form M channels) */ - int coupled_count = channels - 1; /* number of stereo streams (packet has which one is mono or stereo) */ + int mapping_family = 0; /* special multichannel config */ - if (channels > 2) { - header_size += 0x01+0x01+channels; - switch(type) { - - case ...: - ... - break; - default: - goto fail; - } + if (cfg->channels > 2) { + /* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */ + mapping_family = 1; + header_size += 0x01+0x01+cfg->channels; } -#endif if (header_size > buf_size) { VGM_LOG("OPUS: buffer can't hold header\n"); @@ -434,23 +428,24 @@ static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int sk put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ put_32bitBE(buf+0x04, 0x48656164); /* "Head" header magic */ put_8bit (buf+0x08, 1); /* version */ - put_8bit (buf+0x09, channels); - put_16bitLE(buf+0x0A, skip); - put_32bitLE(buf+0x0c, sample_rate); + put_8bit (buf+0x09, cfg->channels); + put_16bitLE(buf+0x0A, cfg->skip); + put_32bitLE(buf+0x0c, cfg->sample_rate); put_16bitLE(buf+0x10, 0); /* output gain */ put_8bit (buf+0x12, mapping_family); -#if 0 if (mapping_family > 0) { - int 0; + int i; - put_8bit(buf+0x13, stream_count); - put_8bit(buf+0x14, coupled_count); - for (i = 0; i < channels; i++) { - put_8bit(buf+0x15+i, i); /* channel mapping (may need to change per family) */ + /* internal mono/stereo streams (N mono/stereo streams form M channels) */ + put_8bit(buf+0x13, cfg->stream_count); + /* joint stereo streams (rest would be mono, so 6ch can be 2ch+2ch+1ch+1ch = 2 coupled */ + put_8bit(buf+0x14, cfg->coupled_count); + /* mapping bits per channel? */ + for (i = 0; i < cfg->channels; i++) { + put_8bit(buf+0x15+i, cfg->channel_mapping[i]); } } -#endif return header_size; fail: @@ -485,7 +480,7 @@ fail: return 0; } -static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) { +static size_t make_oggs_first(uint8_t * buf, int buf_size, opus_config *cfg) { int buf_done = 0; size_t bytes; @@ -493,7 +488,7 @@ static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int ski goto fail; /* make header */ - bytes = make_opus_header(buf+buf_done + 0x1c,buf_size, channels, skip, sample_rate); + bytes = make_opus_header(buf+buf_done + 0x1c,buf_size, cfg); make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 0, 0); buf_done += 0x1c + bytes; @@ -521,8 +516,6 @@ static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile) { } -#ifdef VGM_USE_FFMPEG - static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *streamFile, opus_type_t type) { size_t num_samples = 0; off_t end_offset = offset + stream_size; @@ -610,14 +603,16 @@ size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { } - /* ******************************************************* */ -static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { +/* actual FFmpeg only-code starts here (the above is universal enough but no point to compile) */ +//#ifdef VGM_USE_FFMPEG + +static ffmpeg_codec_data * init_ffmpeg_custom_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config *cfg, opus_type_t type) { ffmpeg_codec_data * ffmpeg_data = NULL; STREAMFILE *temp_streamFile = NULL; - temp_streamFile = setup_opus_streamfile(streamFile, channels, skip, sample_rate, start_offset, data_size, type); + temp_streamFile = setup_opus_streamfile(streamFile, cfg, start_offset, data_size, type); if (!temp_streamFile) goto fail; ffmpeg_data = init_ffmpeg_offset(temp_streamFile, 0x00,get_streamfile_size(temp_streamFile)); @@ -637,7 +632,18 @@ fail: close_streamfile(temp_streamFile); return NULL; } +static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { + opus_config cfg = {0}; + cfg.channels = channels; + cfg.skip = skip; + cfg.sample_rate = sample_rate; + return init_ffmpeg_custom_opus_config(streamFile, start_offset, data_size, &cfg, type); +} + +ffmpeg_codec_data * init_ffmpeg_switch_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config* cfg) { + return init_ffmpeg_custom_opus_config(streamFile, start_offset, data_size, cfg, OPUS_SWITCH); +} ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c new file mode 100644 index 000000000..83a94c22c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c @@ -0,0 +1,189 @@ +#include "coding.h" + +#ifdef VGM_USE_FFMPEG + +static int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) { + uint16_t codec_ATRAC3 = 0x0270; + size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4; + + if (buf_size < riff_size) + return -1; + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "WAVE", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, 0x20);/*fmt size*/ + put_16bitLE(buf+0x14, codec_ATRAC3); + put_16bitLE(buf+0x16, channels); + put_32bitLE(buf+0x18, sample_rate); + put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */ + put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */ + + put_16bitLE(buf+0x24, 0x0e); /* extra data size */ + put_16bitLE(buf+0x26, 1); /* unknown, always 1 */ + put_16bitLE(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */ + put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */ + put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000); + put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */ + put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */ + put_16bitLE(buf+0x32, 0); /* unknown, always 0 */ + + memcpy(buf+0x34, "fact", 4); + put_32bitLE(buf+0x38, 0x8); /* fact size */ + put_32bitLE(buf+0x3c, sample_count); + put_32bitLE(buf+0x40, encoder_delay); + + memcpy(buf+0x44, "data", 4); + put_32bitLE(buf+0x48, data_size); /* data size */ + + return riff_size; +} + +ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay) { + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[0x100]; + int bytes; + int joint_stereo = (block_align == 0x60*channels) && channels > 1; /* only lowest block size does joint stereo */ + int is_at3 = 1; /* could detect using block size */ + + /* create fake header + init ffmpeg + apply fixes to FFmpeg decoding */ + bytes = ffmpeg_make_riff_atrac3(buf,sizeof(buf), sample_count, data_size, channels, sample_rate, block_align, joint_stereo, encoder_delay); + ffmpeg_data = init_ffmpeg_header_offset(sf, buf,bytes, offset,data_size); + if (!ffmpeg_data) goto fail; + + /* unlike with RIFF ATRAC3 we don't set implicit delay, as raw ATRAC3 headers often give loop/samples + * in offsets, so calcs are expected to be handled externally (presumably the game would call raw decoding API + * and any skips would be handled manually) */ + + /* FFmpeg reads this but just in case they fiddle with it in the future */ + ffmpeg_data->totalSamples = sample_count; + + /* encoder delay: encoder introduces some garbage (not always silent) samples to skip at the beginning (at least 1 frame) + * FFmpeg doesn't set this, and even if it ever does it's probably better to force it for the implicit skip. */ + ffmpeg_set_skip_samples(ffmpeg_data, encoder_delay); + + /* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */ + if (is_at3) { + ffmpeg_data->invert_audio_set = 1; + } + + return ffmpeg_data; +fail: + free_ffmpeg(ffmpeg_data); + return NULL; +} + +/* init ATRAC3/plus while adding some fixes */ +ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* out_samples) { + ffmpeg_codec_data *ffmpeg_data = NULL; + int is_at3 = 0, is_at3p = 0, codec; + size_t riff_size; + int fact_samples, skip_samples, implicit_skip; + off_t fact_offset = 0; + size_t fact_size = 0; + + + /* some simplified checks just in case */ + if (read_32bitBE(offset + 0x00,sf) != 0x52494646) /* "RIFF" */ + goto fail; + + riff_size = read_32bitLE(offset + 0x04,sf) + 0x08; + codec = (uint16_t)read_16bitLE(offset + 0x14, sf); + switch(codec) { + case 0x0270: is_at3 = 1; break; + case 0xFFFE: is_at3p = 1; break; + default: goto fail; + } + + + /* init ffmpeg + apply fixes to FFmpeg decoding (with these fixes should be + * sample-accurate vs official tools, except usual +-1 float-to-pcm conversion) */ + ffmpeg_data = init_ffmpeg_offset(sf, offset, riff_size); + if (!ffmpeg_data) goto fail; + + + /* well behaved .at3 define "fact" but official tools accept files without it */ + if (find_chunk_le(sf,0x66616374,offset + 0x0c,0, &fact_offset, &fact_size)) { /* "fact" */ + if (fact_size == 0x08) { /* early AT3 (mainly PSP games) */ + fact_samples = read_32bitLE(fact_offset + 0x00, sf); + skip_samples = read_32bitLE(fact_offset + 0x04, sf); /* base skip samples */ + } + else if (fact_size == 0x0c) { /* late AT3 (mainly PS3 games and few PSP games) */ + fact_samples = read_32bitLE(fact_offset + 0x00, sf); + /* 0x04: base skip samples, ignored by decoder */ + skip_samples = read_32bitLE(fact_offset + 0x08, sf); /* skip samples with implicit skip of 184 added */ + } + else { + VGM_LOG("ATRAC3: unknown fact size\n"); + goto fail; + } + } + else { + fact_samples = 0; /* tools output 0 samples in this case unless loop end is defined */ + if (is_at3) + skip_samples = 1024; /* 1 frame */ + else if (is_at3p) + skip_samples = 2048; /* 1 frame */ + else + skip_samples = 0; + } + + /* implicit skip: official tools skip this even with encoder delay forced to 0. Maybe FFmpeg decodes late, + * but when forcing tools to decode all frame samples it always ends a bit before last frame, so maybe it's + * really an internal skip, since encoder adds extra frames so fact num_samples + encoder delay + implicit skip + * never goes past file. Same for all bitrate/channels, not added to loops. */ + if (is_at3) { + implicit_skip = 69; + } + else if (is_at3p && fact_size == 0x08) { + implicit_skip = 184*2; + } + else if (is_at3p && fact_size == 0x0c) { + implicit_skip = 184; /* first 184 is already added to delay vs field at 0x08 */ + } + else if (is_at3p) { + implicit_skip = 184; /* default for unknown sizes */ + } + else { + implicit_skip = 0; + } + + /* FFmpeg reads this but just in case they fiddle with it in the future */ + ffmpeg_data->totalSamples = fact_samples; + + /* encoder delay: encoder introduces some garbage (not always silent) samples to skip at the beginning (at least 1 frame) + * FFmpeg doesn't set this, and even if it ever does it's probably better to force it for the implicit skip. */ + ffmpeg_set_skip_samples(ffmpeg_data, skip_samples + implicit_skip); + + /* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */ + if (is_at3) { + ffmpeg_data->invert_audio_set = 1; + } + + /* multichannel fix: LFE channel should be reordered on decode (ATRAC3Plus only, only 1/2/6/8ch exist): + * - 6ch: FL FR FC BL BR LFE > FL FR FC LFE BL BR + * - 8ch: FL FR FC BL BR SL SR LFE > FL FR FC LFE BL BR SL SR */ + if (is_at3p && ffmpeg_data->channels == 6) { + /* LFE BR BL > LFE BL BR > same */ + int channel_remap[] = { 0, 1, 2, 5, 5, 5, }; + ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap); + } + else if (is_at3p && ffmpeg_data->channels == 8) { + /* LFE BR SL SR BL > LFE BL SL SR BR > LFE BL BR SR SL > LFE BL BR SL SR > same */ + int channel_remap[] = { 0, 1, 2, 7, 7, 7, 7, 7}; + ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap); + } + + + if (out_samples) + *out_samples = fact_samples; + + return ffmpeg_data; +fail: + free_ffmpeg(ffmpeg_data); + return NULL; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index fe56cd62d..ba82ac8ce 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -255,8 +255,8 @@ static void blitz_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset delta = (step >> 1) + delta * step; /* custom */ sample_decoded += delta; - /* somehow the exe tries to clamp hist, but actually doesn't (bug?), - * not sure if pcm buffer would be clamped outside though */ + /* in Zapper somehow the exe tries to clamp hist but actually doesn't (bug? not in Lilo & Stitch), + * seems the pcm buffer must be clamped outside though to fix some scratchiness */ *hist1 = sample_decoded;//clamp16(sample_decoded); *step_index += IMA_IndexTable[sample_nibble]; if (*step_index < 0) *step_index=0; @@ -445,7 +445,7 @@ void decode_blitz_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channels int nibble_shift = (i&1?4:0); //low nibble first blitz_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); - outbuf[sample_count] = (short)(hist1); + outbuf[sample_count] = (short)clamp16(hist1); } stream->adpcm_history1_32 = hist1; @@ -1131,6 +1131,14 @@ size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) { + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */ } +size_t dat4_ima_bytes_to_samples(size_t bytes, int channels) { + int block_align = 0x20 * channels; + if (channels <= 0) return 0; + /* DAT4 IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ + return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels + + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */ +} + size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) { int block_align = 0x22 * channels; if (channels <= 0) return 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index f0c3a913a..79d0502f2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -90,6 +90,8 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m case MPEG_XVAG: /* set in header and needed for gapless looping */ data->skip_samples = data->config.skip_samples; break; + case MPEG_STANDARD: + data->skip_samples = data->config.skip_samples; break; default: break; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c index 399497c76..4416a77bb 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -29,19 +29,19 @@ typedef struct { /* EALayer3 v1 header */ uint32_t v1_pcm_flag; - uint32_t v1_pcm_decode_discard; - uint32_t v1_pcm_number; + uint32_t v1_offset_samples; + uint32_t v1_pcm_samples; uint32_t v1_pcm_unknown; /* EALayer3 v2 header */ uint32_t v2_extended_flag; uint32_t v2_stereo_flag; - uint32_t v2_unknown; /* unused? */ + uint32_t v2_reserved; uint32_t v2_frame_size; /* full size including headers and pcm block */ - uint32_t v2_mode; /* discard mode */ - uint32_t v2_mode_value; /* samples to use depending on mode */ - uint32_t v2_pcm_number; - uint32_t v2_common_size; /* common header+data size; can be zero */ + uint32_t v2_offset_mode; /* discard mode */ + uint32_t v2_offset_samples; /* use depends on mode */ + uint32_t v2_pcm_samples; + uint32_t v2_common_size; /* granule size: common header+data size; can be zero */ /* EALayer3 common header + side info */ uint32_t version_index; @@ -275,11 +275,11 @@ static int ealayer3_parse_frame_v1(vgm_bitstream *is, ealayer3_frame_info * eaf, /* check PCM block */ if (eaf->v1_pcm_flag == 0xEE) { - r_bits(is, 16,&eaf->v1_pcm_decode_discard); /* samples to discard of the next decoded (not PCM block) samples */ - r_bits(is, 16,&eaf->v1_pcm_number); /* number of PCM samples, can be 0 */ + r_bits(is, 16,&eaf->v1_offset_samples); /* samples to discard of the next decoded (not PCM block) samples */ + r_bits(is, 16,&eaf->v1_pcm_samples); /* number of PCM samples, can be 0 */ eaf->pre_size += 2+2; /* 16b+16b */ - eaf->pcm_size = (2*eaf->v1_pcm_number * channels_per_frame); + eaf->pcm_size = (2*eaf->v1_pcm_samples * channels_per_frame); if (is_v1b) { /* extra 32b in v1b */ r_bits(is, 32,&eaf->v1_pcm_unknown); @@ -303,15 +303,15 @@ static int ealayer3_parse_frame_v2(vgm_bitstream *is, ealayer3_frame_info * eaf) /* read EA-frame V2 header */ r_bits(is, 1,&eaf->v2_extended_flag); r_bits(is, 1,&eaf->v2_stereo_flag); - r_bits(is, 2,&eaf->v2_unknown); + r_bits(is, 2,&eaf->v2_reserved); r_bits(is, 12,&eaf->v2_frame_size); eaf->pre_size = 2; /* 16b */ if (eaf->v2_extended_flag) { - r_bits(is, 2,&eaf->v2_mode); - r_bits(is, 10,&eaf->v2_mode_value); - r_bits(is, 10,&eaf->v2_pcm_number); + r_bits(is, 2,&eaf->v2_offset_mode); + r_bits(is, 10,&eaf->v2_offset_samples); + r_bits(is, 10,&eaf->v2_pcm_samples); r_bits(is, 10,&eaf->v2_common_size); eaf->pre_size += 4; /* 32b */ @@ -323,10 +323,10 @@ static int ealayer3_parse_frame_v2(vgm_bitstream *is, ealayer3_frame_info * eaf) if (!ok) goto fail; } VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_common_size == 0, "EA EAL3: v2 empty frame\n"); /* seen in V2S */ - VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_mode_value > 0, "EA EAL3: v2_mode=%x with value=0x%x\n", eaf->v2_mode, eaf->v2_mode_value); - //VGM_ASSERT(eaf->v2_pcm_number > 0, "EA EAL3: v2_pcm_number 0x%x\n", eaf->v2_pcm_number); + VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_offset_samples > 0, "EA EAL3: v2_offset_mode=%x with value=0x%x\n", eaf->v2_offset_mode, eaf->v2_offset_samples); + //VGM_ASSERT(eaf->v2_pcm_samples > 0, "EA EAL3: v2_pcm_samples 0x%x\n", eaf->v2_pcm_samples); - eaf->pcm_size = (2*eaf->v2_pcm_number * eaf->channels); + eaf->pcm_size = (2*eaf->v2_pcm_samples * eaf->channels); eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size; @@ -614,43 +614,44 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d goto fail; } - if (eaf->v1_pcm_number && !eaf->pcm_size) { - VGM_LOG("MPEG EAL3: pcm size without pcm number\n"); - goto fail; //todo ??? first block? + if (eaf->v1_pcm_samples && !eaf->pcm_size) { + VGM_LOG("MPEG EAL3: pcm_size without pcm_samples\n"); + goto fail; } - if (eaf->v1_pcm_number) { + if (eaf->v1_pcm_samples || eaf->v1_offset_samples) { uint8_t* outbuf = ms->output_buffer + bytes_filled; off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size; - VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%x\n", eaf->v1_pcm_decode_discard, (uint32_t)stream->offset); - VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%x\n", eaf->v1_pcm_number, (uint32_t)stream->offset); + VGM_ASSERT(eaf->v1_offset_samples > 576, "MPEG EAL3: big discard %i at 0x%x\n", eaf->v1_offset_samples, (uint32_t)stream->offset); + VGM_ASSERT(eaf->v1_pcm_samples > 0x100, "MPEG EAL3: big samples %i at 0x%x\n", eaf->v1_pcm_samples, (uint32_t)stream->offset); + VGM_ASSERT(eaf->v1_offset_samples > 0 && eaf->v1_pcm_samples == 0, "EAL3: offset_samples without pcm_samples\n"); /* not seen but could work */ //;VGM_LOG("EA EAL3 v1: off=%lx, discard=%x, pcm=%i, pcm_o=%lx\n", - // stream->offset, eaf->v1_pcm_decode_discard, eaf->v1_pcm_number, pcm_offset); + // stream->offset, eaf->v1_offset_samples, eaf->v1_pcm_samples, pcm_offset); /* V1 usually discards + copies samples at the same time * V1b PCM block is interleaved/'planar' format (ex. NFS:U PS3) */ - ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v1_pcm_number, channels_per_frame, (data->type == MPEG_EAL31), stream->streamfile); - ms->samples_filled += eaf->v1_pcm_number; + ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v1_pcm_samples, channels_per_frame, (data->type == MPEG_EAL31), stream->streamfile); + ms->samples_filled += eaf->v1_pcm_samples; /* skip decoded samples as PCM block 'overwrites' them w/ special meanings */ { - size_t decode_to_discard = eaf->v1_pcm_decode_discard; + size_t decode_to_discard = eaf->v1_offset_samples; if (data->type == MPEG_EAL31) { - //todo should also discard v1_pcm_number, but block layout samples may be exhausted + //todo should also discard v1_pcm_samples, but block layout samples may be exhausted // and won't move (maybe new block if offset = new offset detected) if (decode_to_discard == 576) - decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number; + decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_samples; } else { - //todo maybe should be (576 or samples_per_frame - decode_to_discard) but V1b doesn't seem to set discard + VGM_ASSERT(decode_to_discard > 0, "EAL3: found offset_samples in V1b\n"); + /* probably (576 or samples_per_frame - eaf->v1_offset_samples) but V1b seems to always use 0 and ~47 samples */ if (decode_to_discard == 0) /* seems ok (ex. comparing NFS:UC PS3 vs PC gets correct waveform this way) */ - decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number; /* musn't discard pcm_number */ - else if (decode_to_discard == 576) //todo unsure - decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number; + decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_samples; /* musn't discard pcm_number */ } + ms->decode_to_discard += decode_to_discard; } } @@ -658,37 +659,56 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d if (eaf->v2_extended_flag) { uint8_t* outbuf = ms->output_buffer + bytes_filled; off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size; + size_t usable_samples, decode_to_discard; /* V2P usually only copies big PCM, while V2S discards then copies few samples (similar to V1b). * Unlike V1b, both modes seem to use 'packed' PCM block */ - ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v2_pcm_number, channels_per_frame, 1, stream->streamfile); - ms->samples_filled += eaf->v2_pcm_number; + ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v2_pcm_samples, channels_per_frame, 1, stream->streamfile); + ms->samples_filled += eaf->v2_pcm_samples; //;VGM_LOG("EA EAL3 v2: off=%lx, mode=%x, value=%i, pcm=%i, c-size=%x, pcm_o=%lx\n", - // stream->offset, eaf->v2_mode, eaf->v2_mode_value, eaf->v2_pcm_number, eaf->v2_common_size, pcm_offset); + // stream->offset, eaf->v2_offset_mode, eaf->v2_offset_samples, eaf->v2_pcm_samples, eaf->v2_common_size, pcm_offset); - /* modify decoded samples depending on flag (looks correct in V2P loops, ex. NFS:W) */ - if (eaf->v2_mode == 0x00) { - size_t decode_to_discard = eaf->v2_mode_value; /* (usually 0 in V2P, varies in V2S) */ - decode_to_discard = 576 - decode_to_discard; + //todo improve how discarding works since there could exists a subtle-but-unlikely PCM+granule usage + //todo test other modes (only seen IGNORE) - ms->decode_to_discard += decode_to_discard; + /* get how many samples we can use in this granule + pcm block (thus how many we can't) */ + if (eaf->v2_offset_mode == 0x00) { /* IGNORE (looks correct in V2P loops, ex. NFS:W) */ + /* offset_samples is usually 0 in V2P (no discard), varies in V2S and may be 576 (full discard). + * If block has pcm_samples then usable_samples will be at least that value (for all known files), + * and is assumed PCM isn't discarded so only discards the decoded granule. */ + usable_samples = 576 - eaf->v2_offset_samples; + if (eaf->common_size == 0) + usable_samples = eaf->v2_pcm_samples; + + if (usable_samples == eaf->v2_pcm_samples) { + decode_to_discard = 576; + } + else { + VGM_LOG("EAL3: unknown discard\n"); + decode_to_discard = 0; + } + } + else if (eaf->v2_offset_mode == 0x01) { /* PRESERVE */ + usable_samples = 576; + if (eaf->common_size == 0) + usable_samples = eaf->v2_pcm_samples; + decode_to_discard = 0; /* all preserved */ + } + else if (eaf->v2_offset_mode == 0x02) { /* MUTE */ + usable_samples = 576; + if (eaf->common_size == 0) + usable_samples = eaf->v2_pcm_samples * 2; // why 2? + decode_to_discard = 0; /* not discarded but muted */ + //mute_samples = eaf->v2_offset_samples; //todo must 0 first N decoded samples + } + else { + VGM_LOG("EAL3: unknown mode\n"); /* not defined */ + usable_samples = 576; + decode_to_discard = 0; } - /* todo supposed skip modes (only seen 0x00): - * - * AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG - * D = BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3 - * E = samples to discard (mode == 0) or skip (mode == 1 or 2) before outputting the uncompressed samples - * (when mode == 3 this is ignored) - * F = number of uncompressed sample frames (pcm block) - * G = MPEG granule size (can be zero) - * - * if 0: 576 - E if G == 0 then F - * if 1: 576 if G == 0 then F - * if 2: 576 if G == 0 then F * 2 - * if 3: 576 - */ + ms->decode_to_discard += decode_to_discard; } return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index e8db73459..32da9dd14 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -238,18 +238,19 @@ void decode_mpeg(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do */ static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample_t * outbuf, int32_t samples_to_do, int channels) { int samples_done = 0; - mpg123_handle *m = data->m; + unsigned char *outbytes = (unsigned char *)outbuf; while (samples_done < samples_to_do) { size_t bytes_done; - int rc; + int rc, bytes_to_do; /* read more raw data */ if (!data->buffer_full) { data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->buffer_size,stream->streamfile); /* end of stream, fill rest with 0s */ - if (!data->bytes_in_buffer) { + if (data->bytes_in_buffer <= 0) { + VGM_ASSERT(samples_to_do < samples_done, "MPEG: end of stream, filling %i\n", (samples_to_do - samples_done)); memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample)); break; } @@ -260,30 +261,26 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * dat stream->offset += data->bytes_in_buffer; } + bytes_to_do = (samples_to_do-samples_done)*sizeof(sample)*channels; + /* feed new raw data to the decoder if needed, copy decoded results to output */ if (!data->buffer_used) { - rc = mpg123_decode(m, - data->buffer,data->bytes_in_buffer, - (unsigned char *)(outbuf+samples_done*channels), - (samples_to_do-samples_done)*sizeof(sample)*channels, - &bytes_done); + rc = mpg123_decode(data->m, data->buffer,data->bytes_in_buffer, outbytes, bytes_to_do, &bytes_done); data->buffer_used = 1; } else { - rc = mpg123_decode(m, - NULL,0, - (unsigned char *)(outbuf+samples_done*channels), - (samples_to_do-samples_done)*sizeof(sample)*channels, - &bytes_done); + rc = mpg123_decode(data->m, NULL,0, outbytes, bytes_to_do, &bytes_done); } /* not enough raw data, request more */ if (rc == MPG123_NEED_MORE) { data->buffer_full = 0; } + VGM_ASSERT(rc != MPG123_NEED_MORE && rc != MPG123_OK, "MPEG: error %i\n", rc); /* update copied samples */ samples_done += bytes_done/sizeof(sample)/channels; + outbytes += bytes_done; } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c index 5ca7f47de..198d14aa6 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c @@ -99,6 +99,7 @@ void reset_ogg_vorbis(VGMSTREAM *vgmstream) { ogg_vorbis_codec_data *data = vgmstream->codec_data; if (!data) return; + /* this seek cleans internal buffers */ ov_pcm_seek(&data->ogg_vorbis_file, 0); } @@ -106,6 +107,8 @@ void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { ogg_vorbis_codec_data *data = vgmstream->codec_data; if (!data) return; + /* this seek crosslaps to avoid possible clicks, so seeking to 0 will + * decode a bit differently than ov_pcm_seek */ ov_pcm_seek_lap(&data->ogg_vorbis_file, num_sample); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c index 1fc07b346..2151c476d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c @@ -2,10 +2,13 @@ static const int step_sizes[49] = { /* OKI table (subsection of IMA's table) */ - 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, - 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, - 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, - 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552 }; static const int stex_indexes[16] = { /* OKI table (also from IMA) */ @@ -60,8 +63,9 @@ static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; step = step_sizes[*step_index]; + /* IMA 'mul' style (standard OKI uses 'shift-add') */ delta = (code & 0x7); - delta = (((delta * 2) + 1) * step) >> 3; /* IMA 'mul' style (standard OKI uses 'shift-add') */ + delta = (((delta * 2) + 1) * step) >> 3; if (code & 0x8) delta = -delta; *hist1 += delta; @@ -75,6 +79,31 @@ static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in *out_sample = *hist1; } +static void oki4s_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) { + int code, step, delta; + + code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; + step = step_sizes[*step_index]; + + step = step << 4; /* original table has precomputed step_sizes so that this isn't done */ + + /* IMA 'shift-add' style (like standard OKI) */ + delta = step >> 3; + if (code & 1) delta += step >> 2; + if (code & 2) delta += step >> 1; + if (code & 4) delta += step; + if (code & 8) delta = -delta; + *hist1 += delta; + + *hist1 = clamp16(*hist1); /* standard OKI clamps hist to 2047,-2048 here */ + + *step_index += stex_indexes[code]; + if (*step_index < 0) *step_index = 0; + if (*step_index > 48) *step_index = 48; + + *out_sample = *hist1; +} + /* PC-FX ADPCM decoding, variation of OKI/Dialogic/VOX ADPCM. Based on mednafen/pcfx-music-dump. * Apparently most ADPCM was made with a buggy encoder, resulting in incorrect sound in real hardware * and sound clipped at half. Decoding can be controlled with modes: @@ -138,6 +167,37 @@ void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspaci stream->adpcm_step_index = step_index; } +/* OKI variation with 16-bit output (vs standard's 12-bit) and pre-adjusted tables (shifted by 4), found in Jubeat Clan (AC). + * Reverse engineered from the DLLs. */ +void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + int16_t out_sample; + int is_stereo = channelspacing > 1; + + + /* external interleave */ + + /* no header (external setup), pre-clamp for wrong values */ + if (step_index < 0) step_index=0; + if (step_index > 48) step_index=48; + + /* decode nibbles (layout: varies) */ + for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { + off_t byte_offset = is_stereo ? + stream->offset + i : /* stereo: one nibble per channel */ + stream->offset + i/2; /* mono: consecutive nibbles (assumed) */ + int nibble_shift = + is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */ + + oki4s_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample); + outbuf[sample_count] = (out_sample); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} size_t oki_bytes_to_samples(size_t bytes, int channels) { if (channels <= 0) return 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c index 884e419dc..f5e7ebd41 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c @@ -3,11 +3,11 @@ /* PS-ADPCM table, defined as rational numbers (as in the spec) */ static const double ps_adpcm_coefs_f[5][2] = { - { 0.0 , 0.0 }, - { 60.0 / 64.0 , 0.0 }, - { 115.0 / 64.0 , -52.0 / 64.0 }, - { 98.0 / 64.0 , -55.0 / 64.0 }, - { 122.0 / 64.0 , -60.0 / 64.0 }, + { 0.0 , 0.0 }, //{ 0.0 , 0.0 }, + { 0.9375 , 0.0 }, //{ 60.0 / 64.0 , 0.0 }, + { 1.796875 , -0.8125 }, //{ 115.0 / 64.0 , -52.0 / 64.0 }, + { 1.53125 , -0.859375 }, //{ 98.0 / 64.0 , -55.0 / 64.0 }, + { 1.90625 , -0.9375 }, //{ 122.0 / 64.0 , -60.0 / 64.0 }, }; /* PS-ADPCM table, defined as spec_coef*64 (for int implementations) */ @@ -101,7 +101,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing /* PS-ADPCM with configurable frame size and no flag (int math version). - * Found in some PC/PS3 games (FF XI in sizes 3/5/9/41, Afrika in size 4, Blur/James Bond in size 33, etc). + * Found in some PC/PS3 games (FF XI in sizes 0x3/0x5/0x9/0x41, Afrika in size 0x4, Blur/James Bond in size 0x33, etc). * * Uses int math to decode, which seems more likely (based on FF XI PC's code in Moogle Toolbox). */ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) { @@ -114,7 +114,7 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int c /* external interleave (variable size), mono */ bytes_per_frame = frame_size; - samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 28 */ + samples_per_frame = (bytes_per_frame - 0x01) * 2; frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; @@ -152,6 +152,56 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int c stream->adpcm_history2_32 = hist2; } +/* PS-ADPCM from Pivotal games, exactly like psx_cfg but with float math (reverse engineered from the exe) */ +void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) { + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t coef_index, shift_factor; + int32_t hist1 = stream->adpcm_history1_32; + int32_t hist2 = stream->adpcm_history2_32; + float scale; + + /* external interleave (variable size), mono */ + bytes_per_frame = frame_size; + samples_per_frame = (bytes_per_frame - 0x01) * 2; + 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; + coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; + + VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %x\n", (uint32_t)frame_offset); + if (coef_index > 5) /* just in case */ + coef_index = 5; + if (shift_factor > 12) /* same */ + shift_factor = 12; + scale = (float)(1.0 / (double)(1 << shift_factor)); + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t sample = 0; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01+i/2,stream->streamfile); + + sample = !(i&1) ? /* low nibble first */ + (nibbles >> 0) & 0x0f : + (nibbles >> 4) & 0x0f; + sample = (int16_t)((sample << 12) & 0xf000); /* 16b sign extend + default scale */ + sample = sample*scale + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2; /* actually substracts negative coefs but whatevs */ + + outbuf[sample_count] = clamp16(sample); + sample_count += channelspacing; + + hist2 = hist1; + hist1 = sample; /* not clamped but no difference */ + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_history2_32 = hist2; +} + /* Find loop samples in PS-ADPCM data and return if the file loops. * @@ -339,6 +389,25 @@ size_t ps_bytes_to_samples(size_t bytes, int channels) { } size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels) { - return bytes / channels / frame_size * 28; + int samples_per_frame = (frame_size - 0x01) * 2; + return bytes / channels / frame_size * samples_per_frame; } +/* test PS-ADPCM frames for correctness */ +int ps_check_format(STREAMFILE *streamFile, off_t offset, size_t max) { + off_t max_offset = offset + max; + if (max_offset > get_streamfile_size(streamFile)) + max_offset = get_streamfile_size(streamFile); + + while (offset < max_offset) { + uint8_t predictor = (read_8bit(offset+0x00,streamFile) >> 4) & 0x0f; + uint8_t flags = read_8bit(offset+0x01,streamFile); + + if (predictor > 5 || flags > 7) { + return 0; + } + offset += 0x10; + } + + return 1; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c new file mode 100644 index 000000000..e128433ce --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c @@ -0,0 +1,98 @@ +#include "coding.h" + + +/* a somewhat IMA-like mix of step+next index all in one (maybe an array[][] originally?) */ +static const int32_t ptadpcm_table[(16+16)*16] = { /* 16 of (step+index) + 16 values in a nibble */ + -14, 2, -10, 2, -7, 1, -5, 1, -3, 0, -2, 0, -1, 0, 0, 0, + 0, 0, 1, 0, 2, 0, 3, 0, 5, 1, 7, 1, 10, 2, 14, 2, + -28, 3, -20, 3, -14, 2, -10, 2, -7, 1, -5, 1, -3, 1, -1, 0, + 1, 0, 3, 1, 5, 1, 7, 1, 10, 2, 14, 2, 20, 3, 28, 3, + -56, 4, -40, 4, -28, 3, -20, 3, -14, 2, -10, 2, -6, 2, -2, 1, + 2, 1, 6, 2, 10, 2, 14, 2, 20, 3, 28, 3, 40, 4, 56, 4, + -112, 5, -80, 5, -56, 4, -40, 4, -28, 3, -20, 3, -12, 3, -4, 2, + 4, 2, 12, 3, 20, 3, 28, 3, 40, 4, 56, 4, 80, 5, 112, 5, + -224, 6, -160, 6, -112, 5, -80, 5, -56, 4, -40, 4, -24, 4, -8, 3, + 8, 3, 24, 4, 40, 4, 56, 4, 80, 5, 112, 5, 160, 6, 224, 6, + -448, 7, -320, 7, -224, 6, -160, 6, -112, 5, -80, 5, -48, 5, -16, 4, + 16, 4, 48, 5, 80, 5, 112, 5, 160, 6, 224, 6, 320, 7, 448, 7, + -896, 8, -640, 8, -448, 7, -320, 7, -224, 6, -160, 6, -96, 6, -32, 5, + 32, 5, 96, 6, 160, 6, 224, 6, 320, 7, 448, 7, 640, 8, 896, 8, + -1792, 9, -1280, 9, -896, 8, -640, 8, -448, 7, -320, 7, -192, 7, -64, 6, + 64, 6, 192, 7, 320, 7, 448, 7, 640, 8, 896, 8, 1280, 9, 1792, 9, + -3584, 10, -2560, 10, -1792, 9, -1280, 9, -896, 8, -640, 8, -384, 8, -128, 7, + 128, 7, 384, 8, 640, 8, 896, 8, 1280, 9, 1792, 9, 2560, 10, 3584, 10, + -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1792, 9, -1280, 9, -768, 9, -256, 8, + 256, 8, 768, 9, 1280, 9, 1792, 9, 2560, 10, 3584, 10, 5120, 11, 7168, 11, + -14336, 11, -10240, 11, -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1536, 10, -512, 9, + 512, 9, 1536, 10, 2560, 10, 3584, 10, 5120, 11, 7168, 11, 10240, 11, 14336, 11, + -28672, 11, -20480, 11, -14336, 11, -10240, 11, -7168, 11, -5120, 11, -3072, 11, -1024, 10, + 1024, 10, 3072, 11, 5120, 11, 7168, 11, 10240, 11, 14336, 11, 20480, 11, 28672, 11, + /* rest is 0s */ +}; + +/* Platinum "PtADPCM" custom ADPCM for Wwise (reverse engineered from .exes). */ +void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) { + off_t frame_offset; + int i, frames_in, sample_count = 0, samples_done = 0; + size_t bytes_per_frame, samples_per_frame; + int16_t hist1, hist2; + int index, step; + + /* external interleave (variable size), mono */ + bytes_per_frame = frame_size; + samples_per_frame = 2 + (frame_size - 0x05) * 2; + 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; + hist2 = read_16bitLE(frame_offset+0x00,stream->streamfile); + hist1 = read_16bitLE(frame_offset+0x02,stream->streamfile); + index = (uint8_t)read_8bit(frame_offset+0x04,stream->streamfile); + + VGM_ASSERT_ONCE(index > 12, "PTADPCM: incorrect index at %x\n", (uint32_t)frame_offset); + + /* write header samples (needed) */ + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist2; + samples_done++; + } + sample_count++; + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist1; + samples_done++; + } + sample_count++; + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t new_sample; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x05 + i/2,stream->streamfile); + uint8_t nibble; + + nibble = !(i&1) ? /* low nibble first */ + (nibbles >> 0) & 0xF : + (nibbles >> 4) & 0xF; + + step = ptadpcm_table[2*(nibble + 16*index) + 0]; + index = ptadpcm_table[2*(nibble + 16*index) + 1]; + new_sample = clamp16(step + 2*hist1 - hist2); + + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = new_sample; + samples_done++; + } + sample_count++; + + hist2 = hist1; + hist1 = new_sample; + } + + //stream->adpcm_history1_32 = hist1; + //stream->adpcm_history2_32 = hist2; +} + +size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size) { + if (channels <= 0 || frame_size < 0x06) return 0; + return (bytes / channels / frame_size) * ((frame_size-0x05) * 2); +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/sdx2_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/sdx2_decoder.c index b911f511e..ff6022c38 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/sdx2_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/sdx2_decoder.c @@ -5,114 +5,112 @@ /* SDX2 - 2:1 Squareroot-delta-exact compression */ /* CBD2 - 2:1 Cuberoot-delta-exact compression (from the unreleased 3DO M2) */ -/* for (i=-128;i<128;i++) squares[i+128]=i<0?(-i*i)*2:(i*i)*2; */ +/* for (i=-128;i<128;i++) { squares[i+128] = i<0?(-i*i)*2:(i*i)*2; } */ static int16_t squares[256] = { --32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322,-27848, --27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762,-23328,-22898, --22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602,-19208,-18818,-18432, --18050,-17672,-17298,-16928,-16562,-16200,-15842,-15488,-15138,-14792,-14450, --14112,-13778,-13448,-13122,-12800,-12482,-12168,-11858,-11552,-11250,-10952, --10658,-10368,-10082, -9800, -9522, -9248, -8978, -8712, -8450, -8192, -7938, - -7688, -7442, -7200, -6962, -6728, -6498, -6272, -6050, -5832, -5618, -5408, - -5202, -5000, -4802, -4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, - -3200, -3042, -2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, - -1682, -1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722, - -648, -578, -512, -450, -392, -338, -288, -242, -200, -162, -128, - -98, -72, -50, -32, -18, -8, -2, 0, 2, 8, 18, - 32, 50, 72, 98, 128, 162, 200, 242, 288, 338, 392, - 450, 512, 578, 648, 722, 800, 882, 968, 1058, 1152, 1250, - 1352, 1458, 1568, 1682, 1800, 1922, 2048, 2178, 2312, 2450, 2592, - 2738, 2888, 3042, 3200, 3362, 3528, 3698, 3872, 4050, 4232, 4418, - 4608, 4802, 5000, 5202, 5408, 5618, 5832, 6050, 6272, 6498, 6728, - 6962, 7200, 7442, 7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, - 9800, 10082, 10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, - 13122, 13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562, - 16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402, 20808, - 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642, 25088, 25538, - 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282, 29768, 30258, 30752, - 31250, 31752, 32258 + -32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322,-27848, + -27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762,-23328,-22898, + -22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602,-19208,-18818,-18432, + -18050,-17672,-17298,-16928,-16562,-16200,-15842,-15488,-15138,-14792,-14450, + -14112,-13778,-13448,-13122,-12800,-12482,-12168,-11858,-11552,-11250,-10952, + -10658,-10368,-10082, -9800, -9522, -9248, -8978, -8712, -8450, -8192, -7938, + -7688, -7442, -7200, -6962, -6728, -6498, -6272, -6050, -5832, -5618, -5408, + -5202, -5000, -4802, -4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, + -3200, -3042, -2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, + -1682, -1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722, + -648, -578, -512, -450, -392, -338, -288, -242, -200, -162, -128, + -98, -72, -50, -32, -18, -8, -2, 0, 2, 8, 18, + 32, 50, 72, 98, 128, 162, 200, 242, 288, 338, 392, + 450, 512, 578, 648, 722, 800, 882, 968, 1058, 1152, 1250, + 1352, 1458, 1568, 1682, 1800, 1922, 2048, 2178, 2312, 2450, 2592, + 2738, 2888, 3042, 3200, 3362, 3528, 3698, 3872, 4050, 4232, 4418, + 4608, 4802, 5000, 5202, 5408, 5618, 5832, 6050, 6272, 6498, 6728, + 6962, 7200, 7442, 7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, + 9800, 10082, 10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, + 13122, 13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562, + 16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402, 20808, + 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642, 25088, 25538, + 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282, 29768, 30258, 30752, + 31250, 31752, 32258 }; -//for (i=-128;i<128;i++) -//{ -// double j = (i/2)/2.0; -// cubes[i+128]=floor(j*j*j); -//} -static int16_t cubes[256]={ --32768,-31256,-31256,-29791,-29791,-28373,-28373,-27000,-27000,-25672,-25672, --24389,-24389,-23149,-23149,-21952,-21952,-20797,-20797,-19683,-19683,-18610, --18610,-17576,-17576,-16581,-16581,-15625,-15625,-14706,-14706,-13824,-13824, --12978,-12978,-12167,-12167,-11391,-11391,-10648,-10648, -9938, -9938, -9261, - -9261, -8615, -8615, -8000, -8000, -7415, -7415, -6859, -6859, -6332, -6332, - -5832, -5832, -5359, -5359, -4913, -4913, -4492, -4492, -4096, -4096, -3724, - -3724, -3375, -3375, -3049, -3049, -2744, -2744, -2460, -2460, -2197, -2197, - -1953, -1953, -1728, -1728, -1521, -1521, -1331, -1331, -1158, -1158, -1000, - -1000, -857, -857, -729, -729, -614, -614, -512, -512, -422, -422, - -343, -343, -275, -275, -216, -216, -166, -166, -125, -125, -91, - -91, -64, -64, -43, -43, -27, -27, -16, -16, -8, -8, - -3, -3, -1, -1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 3, 3, 8, 8, 16, 16, 27, 27, 43, - 43, 64, 64, 91, 91, 125, 125, 166, 166, 216, 216, - 275, 275, 343, 343, 422, 422, 512, 512, 614, 614, 729, - 729, 857, 857, 1000, 1000, 1158, 1158, 1331, 1331, 1521, 1521, - 1728, 1728, 1953, 1953, 2197, 2197, 2460, 2460, 2744, 2744, 3049, - 3049, 3375, 3375, 3724, 3724, 4096, 4096, 4492, 4492, 4913, 4913, - 5359, 5359, 5832, 5832, 6332, 6332, 6859, 6859, 7415, 7415, 8000, - 8000, 8615, 8615, 9261, 9261, 9938, 9938, 10648, 10648, 11391, 11391, - 12167, 12167, 12978, 12978, 13824, 13824, 14706, 14706, 15625, 15625, 16581, - 16581, 17576, 17576, 18610, 18610, 19683, 19683, 20797, 20797, 21952, 21952, - 23149, 23149, 24389, 24389, 25672, 25672, 27000, 27000, 28373, 28373, 29791, - 29791, 31256, 31256}; -static void decode_delta_exact(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int16_t * table) { +/* for (i=-128;i<128;i++) { double j = (i/2)/2.0; cubes[i+128] = floor(j*j*j); } */ +static int16_t cubes[256] = { + -32768,-31256,-31256,-29791,-29791,-28373,-28373,-27000,-27000,-25672,-25672, + -24389,-24389,-23149,-23149,-21952,-21952,-20797,-20797,-19683,-19683,-18610, + -18610,-17576,-17576,-16581,-16581,-15625,-15625,-14706,-14706,-13824,-13824, + -12978,-12978,-12167,-12167,-11391,-11391,-10648,-10648, -9938, -9938, -9261, + -9261, -8615, -8615, -8000, -8000, -7415, -7415, -6859, -6859, -6332, -6332, + -5832, -5832, -5359, -5359, -4913, -4913, -4492, -4492, -4096, -4096, -3724, + -3724, -3375, -3375, -3049, -3049, -2744, -2744, -2460, -2460, -2197, -2197, + -1953, -1953, -1728, -1728, -1521, -1521, -1331, -1331, -1158, -1158, -1000, + -1000, -857, -857, -729, -729, -614, -614, -512, -512, -422, -422, + -343, -343, -275, -275, -216, -216, -166, -166, -125, -125, -91, + -91, -64, -64, -43, -43, -27, -27, -16, -16, -8, -8, + -3, -3, -1, -1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 3, 3, 8, 8, 16, 16, 27, 27, 43, + 43, 64, 64, 91, 91, 125, 125, 166, 166, 216, 216, + 275, 275, 343, 343, 422, 422, 512, 512, 614, 614, 729, + 729, 857, 857, 1000, 1000, 1158, 1158, 1331, 1331, 1521, 1521, + 1728, 1728, 1953, 1953, 2197, 2197, 2460, 2460, 2744, 2744, 3049, + 3049, 3375, 3375, 3724, 3724, 4096, 4096, 4492, 4492, 4913, 4913, + 5359, 5359, 5832, 5832, 6332, 6332, 6859, 6859, 7415, 7415, 8000, + 8000, 8615, 8615, 9261, 9261, 9938, 9938, 10648, 10648, 11391, 11391, + 12167, 12167, 12978, 12978, 13824, 13824, 14706, 14706, 15625, 15625, 16581, + 16581, 17576, 17576, 18610, 18610, 19683, 19683, 20797, 20797, 21952, 21952, + 23149, 23149, 24389, 24389, 25672, 25672, 27000, 27000, 28373, 28373, 29791, + 29791, 31256, 31256 +}; - int32_t hist = stream->adpcm_history1_32; +static void decode_delta_exact(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int16_t * table) { + int32_t hist = stream->adpcm_history1_32; - int i; - int32_t sample_count; - - for (i=first_sample,sample_count=0; ioffset+i,stream->streamfile); int16_t sample; if (!(sample_byte & 1)) hist = 0; sample = hist + table[sample_byte+128]; - hist = outbuf[sample_count] = clamp16(sample); - } - stream->adpcm_history1_32=hist; + hist = outbuf[sample_count] = clamp16(sample); + } + + stream->adpcm_history1_32 = hist; } -static void decode_delta_exact_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int16_t * table) { +static void decode_delta_exact_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int16_t * table) { + int32_t hist = stream->adpcm_history1_32; - int32_t hist = stream->adpcm_history1_32; + int i; + int32_t sample_count = 0; - int i; - int32_t sample_count; - - for (i=first_sample,sample_count=0; ioffset+i*channelspacing,stream->streamfile); int16_t sample; if (!(sample_byte & 1)) hist = 0; sample = hist + table[sample_byte+128]; - hist = outbuf[sample_count] = clamp16(sample); - } - stream->adpcm_history1_32=hist; + hist = outbuf[sample_count] = clamp16(sample); + } + + stream->adpcm_history1_32 = hist; } -void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_sdx2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { decode_delta_exact(stream, outbuf, channelspacing, first_sample, samples_to_do, squares); } -void decode_sdx2_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_sdx2_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { decode_delta_exact_int(stream, outbuf, channelspacing, first_sample, samples_to_do, squares); } -void decode_cbd2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_cbd2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { decode_delta_exact(stream, outbuf, channelspacing, first_sample, samples_to_do, cubes); } -void decode_cbd2_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_cbd2_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { decode_delta_exact_int(stream, outbuf, channelspacing, first_sample, samples_to_do, cubes); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c new file mode 100644 index 000000000..ddd628f7a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c @@ -0,0 +1,588 @@ +#include "coding.h" + + +/* Decodes Ubisoft ADPCM, a rather complex codec with 4-bit (usually music) and 6-bit (usually voices/sfx) + * mono or stereo modes, using multiple tables and temp step/delta values. + * + * Base reverse engineering by Zench: https://bitbucket.org/Zenchreal/decubisnd + * Original ASM MMX/intrinsics to C++ by sigsegv; adapted by bnnm; special thanks to Nicknine. + * + * Data always starts with a 0x30 main header (some games have extra data before too), then frames of + * fixed size: 0x34 ADPCM setup per channel, 1 subframe + 1 padding byte, then another subframe and 1 byte. + * Subframes have 1536 samples or less (like 1024), typical sizes are 0x600 for 4-bit or 0x480 for 6-bit. + * Last frame can contain only one subframe, with less codes than normal (may use padding). Nibbles/codes + * are packed as 32-bit LE with 6-bit or 4-bit codes for all channels (processes kinda like joint stereo). + */ + +#define UBI_CHANNELS_MIN 1 +#define UBI_CHANNELS_MAX 2 +#define UBI_SUBFRAMES_PER_FRAME_MAX 2 +#define UBI_CODES_PER_SUBFRAME_MAX 1536 /* for all channels */ +#define UBI_FRAME_SIZE_MAX (0x34 * UBI_CHANNELS_MAX + (UBI_CODES_PER_SUBFRAME_MAX * 6 / 8 + 0x1) * UBI_SUBFRAMES_PER_FRAME_MAX) +#define UBI_SAMPLES_PER_FRAME_MAX (UBI_CODES_PER_SUBFRAME_MAX * UBI_SUBFRAMES_PER_FRAME_MAX) + + +typedef struct { + uint32_t signature; + uint32_t sample_count; + uint32_t subframe_count; + uint32_t codes_per_subframe_last; + uint32_t codes_per_subframe; + uint32_t subframes_per_frame; + uint32_t sample_rate; + uint32_t unknown1c; + uint32_t unknown20; + uint32_t bits_per_sample; + uint32_t unknown28; + uint32_t channels; +} ubi_adpcm_header_data; + +typedef struct { + uint32_t signature; + int32_t step1; + int32_t next1; + int32_t next2; + + int16_t coef1; + int16_t coef2; + int16_t unused1; + int16_t unused2; + + int16_t mod1; + int16_t mod2; + int16_t mod3; + int16_t mod4; + + int16_t hist1; + int16_t hist2; + int16_t unused3; + int16_t unused4; + + int16_t delta1; + int16_t delta2; + int16_t delta3; + int16_t delta4; + + int16_t delta5; + int16_t unused5; +} ubi_adpcm_channel_data; + +struct ubi_adpcm_codec_data { + ubi_adpcm_header_data header; + ubi_adpcm_channel_data ch[UBI_CHANNELS_MAX]; + + off_t start_offset; + off_t offset; + int subframe_number; + + uint8_t frame[UBI_FRAME_SIZE_MAX]; + uint8_t codes[UBI_CODES_PER_SUBFRAME_MAX]; + int16_t samples[UBI_SAMPLES_PER_FRAME_MAX]; /* for all channels, saved in L-R-L-R form */ + + size_t samples_filled; + size_t samples_consumed; + size_t samples_to_discard; +}; + +/* *********************************************************************** */ + +static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data *data, off_t offset); +static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data *data); + +ubi_adpcm_codec_data *init_ubi_adpcm(STREAMFILE *sf, off_t offset, int channels) { + ubi_adpcm_codec_data *data = NULL; + + data = calloc(1, sizeof(ubi_adpcm_codec_data)); + if (!data) goto fail; + + if (!parse_header(sf, data, offset)) { + VGM_LOG("UBI ADPCM: wrong header\n"); + goto fail; + } + + if (data->header.channels != channels) { + VGM_LOG("UBI ADPCM: wrong number of channels: %i vs %i\n", data->header.channels, channels); + goto fail; + } + + data->start_offset = offset + 0x30; + data->offset = data->start_offset; + + return data; +fail: + free_ubi_adpcm(data); + return NULL; +} + +void decode_ubi_adpcm(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do) { + STREAMFILE* sf = vgmstream->ch[0].streamfile; + ubi_adpcm_codec_data *data = vgmstream->codec_data; + uint32_t channels = data->header.channels; + int samples_done = 0; + + + /* Ubi ADPCM frames are rather big, so we decode then copy to outbuf until done */ + while (samples_done < samples_to_do) { + if (data->samples_filled) { + int samples_to_get = data->samples_filled; + + if (data->samples_to_discard) { + /* discard samples for looping */ + if (samples_to_get > data->samples_to_discard) + samples_to_get = data->samples_to_discard; + data->samples_to_discard -= samples_to_get; + } + else { + /* get max samples and copy */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + + memcpy(outbuf + samples_done*channels, + data->samples + data->samples_consumed*channels, + samples_to_get*channels * sizeof(sample)); + samples_done += samples_to_get; + } + + /* mark consumed samples */ + data->samples_consumed += samples_to_get; + data->samples_filled -= samples_to_get; + } + else { + decode_frame(sf, data); + } + } +} + +void reset_ubi_adpcm(ubi_adpcm_codec_data *data) { + if (!data) return; + + data->offset = data->start_offset; + data->subframe_number = 0; +} + +void seek_ubi_adpcm(ubi_adpcm_codec_data *data, int32_t num_sample) { + if (!data) return; + + //todo improve by seeking to closest frame + + reset_ubi_adpcm(data); + data->samples_to_discard = num_sample; +} + +void free_ubi_adpcm(ubi_adpcm_codec_data *data) { + if (!data) + return; + free(data); +} + + +/* ************************************************************************ */ + +static void read_header_state(uint8_t *data, ubi_adpcm_header_data *header) { + header->signature = get_32bitLE(data + 0x00); + header->sample_count = get_32bitLE(data + 0x04); + header->subframe_count = get_32bitLE(data + 0x08); + header->codes_per_subframe_last= get_32bitLE(data + 0x0c); + header->codes_per_subframe = get_32bitLE(data + 0x10); + header->subframes_per_frame = get_32bitLE(data + 0x14); + header->sample_rate = get_32bitLE(data + 0x18); /* optional? */ + header->unknown1c = get_32bitLE(data + 0x1c); /* variable */ + header->unknown20 = get_32bitLE(data + 0x20); /* null? */ + header->bits_per_sample = get_32bitLE(data + 0x24); + header->unknown28 = get_32bitLE(data + 0x28); /* 1~3? */ + header->channels = get_32bitLE(data + 0x2c); +} + +static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data *data, off_t offset) { + uint8_t buf[0x30]; + size_t bytes; + + bytes = read_streamfile(buf, offset, 0x30, sf); + if (bytes != 0x30) goto fail; + + read_header_state(buf, &data->header); + + if (data->header.signature != 0x08) + goto fail; + if (data->header.codes_per_subframe_last > UBI_CODES_PER_SUBFRAME_MAX || + data->header.codes_per_subframe > UBI_CODES_PER_SUBFRAME_MAX) + goto fail; + if (data->header.subframes_per_frame != UBI_SUBFRAMES_PER_FRAME_MAX) + goto fail; + if (data->header.bits_per_sample != 4 && data->header.bits_per_sample != 6) + goto fail; + if (data->header.channels > UBI_CHANNELS_MAX || data->header.channels < UBI_CHANNELS_MIN) + goto fail; + + return 1; +fail: + return 0; +} + +/* *********************************************************************** */ + +int32_t adpcm6_table1[64] = { + -100000000, -369, -245, -133, -33, 56, 135, 207, + 275, 338, 395, 448, 499, 548, 593, 635, + 676, 717, 755, 791, 825, 858, 889, 919, + 948, 975, 1003, 1029, 1054, 1078, 1103, 1132, + /* probably unused (partly spilled from next table) */ + 1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056, + 5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040, + 20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832, + 51200, 56320, 63488, 67704, 75776, 89088, 102400, 0, +}; + +int32_t adpcm6_table2[64] = { + 1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056, + 5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040, + 20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832, + 51200, 56320, 63488, 67704, 75776, 89088, 102400, 0, + /* probably unused */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, 5, 6, 6, 6, 7, +}; + +int32_t adpcm4_table1[16] = { + -100000000, 8, 269, 425, 545, 645, 745, 850, + /* probably unused */ + -1082465976, 1058977874, 1068540887, 1072986849, 1075167887, 1076761723, 1078439444, 1203982336, +}; + +int32_t adpcm4_table2[16] = { + -1536, 2314, 5243, 8192, 14336, 25354, 45445, 143626, + /* probably unused */ + 0, 0, 0, 1, 1, 1, 3, 7, +}; + +int32_t delta_table[33+33] = { + 1024, 1031, 1053, 1076, 1099, 1123, 1148, 1172, + 1198, 1224, 1251, 1278, 1306, 1334, 1363, 1393, + 1423, 1454, 1485, 1518, 1551, 1584, 1619, 1654, + 1690, 1726, 1764, 1802, 1841, 1881, 1922, 1964, + 2007, + -1024,-1031,-1053,-1076,-1099,-1123,-1148,-1172, + -1198,-1224,-1251,-1278,-1306,-1334,-1363,-1393, + -1423,-1454,-1485,-1518,-1551,-1584,-1619,-1654, + -1690,-1726,-1764,-1802,-1841,-1881,-1922,-1964, + -2007 +}; + + +static int sign16(int16_t test) { + return (test < 0 ? -1 : 1); +} +static int sign32(int32_t test) { + return (test < 0 ? -1 : 1); +} +static int16_t absmax16(int16_t val, int16_t absmax) { + if (val < 0) { + if (val < -absmax) return -absmax; + } else { + if (val > absmax) return absmax; + } + return val; +} +static int32_t clamp_val(int32_t val, int32_t min, int32_t max) { + if (val < min) return min; + else if (val > max) return max; + else return val; +} + + +static int16_t expand_code_6bit(uint8_t code, ubi_adpcm_channel_data* state) { + int step0_index; + int32_t code_signed, step0_next, step0, delta0; + int32_t sample_new; + + code_signed = (int32_t)code - 31; /* convert coded 0..63 value to signed value, where 0=-31 .. 31=0 .. 63=32 */ + step0_index = abs(code_signed); /* 0..32, but should only go up to 31 */ + step0_next = adpcm6_table1[step0_index] + state->step1; + step0 = (state->step1 & 0xFFFF) * 246; + step0 = (step0 + adpcm6_table2[step0_index]) >> 8; + step0 = clamp_val(step0, 271, 2560); + + delta0 = 0; + if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) { + int delta0_index = ((step0_next >> 3) & 0x1F) + (code_signed < 0 ? 33 : 0); + int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31); + delta0 = (delta_table[delta0_index] << delta0_shift) >> 10; + } + + sample_new = (int16_t)(delta0 + state->delta1 + state->hist1); + + state->hist1 = sample_new; + + state->step1 = step0; + + state->delta1 = delta0; + + VGM_ASSERT(step0_index > 31, "UBI ADPCM: index over 31\n"); + return sample_new; +} + +/* may be simplified (masks, saturation, etc) as some values should never happen in the encoder */ +static int16_t expand_code_4bit(uint8_t code, ubi_adpcm_channel_data* state) { + int step0_index; + int32_t code_signed, step0_next, step0, delta0, next0, coef1_next, coef2_next; + int32_t sample_new; + + + code_signed = (int32_t)code - 7; /* convert coded 0..15 value to signed value, where 0=-7 .. 7=0 .. 15=8 */ + step0_index = abs(code_signed); /* 0..8, but should only go up to 7 */ + step0_next = adpcm4_table1[step0_index] + state->step1; + step0 = (state->step1 & 0xFFFF) * 246; + step0 = (step0 + adpcm4_table2[step0_index]) >> 8; + step0 = clamp_val(step0, 271, 2560); + + delta0 = 0; + if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) { + int delta0_index = ((step0_next >> 3) & 0x1F) + (code_signed < 0 ? 33 : 0); + int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31); + delta0 = (delta_table[delta0_index] << delta0_shift) >> 10; + } + + next0 = (int16_t)(( + (state->mod1 * state->delta1) + (state->mod2 * state->delta2) + + (state->mod3 * state->delta3) + (state->mod4 * state->delta4) ) >> 10); + + sample_new = ((state->coef1 * state->hist1) + (state->coef2 * state->hist2)) >> 10; + sample_new = (int16_t)(delta0 + next0 + sample_new); + + coef1_next = state->coef1 * 255; + coef2_next = state->coef2 * 254; + delta0 = (int16_t)delta0; + if (delta0 + next0 != 0) { + int32_t sign1, sign2, coef_delta; + + sign1 = sign32(delta0 + next0) * sign32(state->delta1 + state->next1); + sign2 = sign32(delta0 + next0) * sign32(state->delta2 + state->next2); + + coef_delta = (int16_t)((((sign1 * 3072) + coef1_next) >> 6) & ~0x3); + coef_delta = clamp16(clamp16(coef_delta + 30719) - 30719); //??? + coef_delta = clamp16(clamp16(coef_delta + -30720) - -30720); //??? + coef_delta = ((int16_t)(sign2 * 1024) - (int16_t)(sign1 * coef_delta)) * 2; + + coef1_next += sign1 * 3072; + coef2_next += coef_delta; + } + + + state->hist2 = state->hist1; + state->hist1 = sample_new; + + state->coef2 = absmax16((int16_t)(coef2_next >> 8), 768); + state->coef1 = absmax16((int16_t)(coef1_next >> 8), 960 - state->coef2); + + state->next2 = state->next1; + state->next1 = next0; + state->step1 = step0; + + state->delta5 = state->delta4; + state->delta4 = state->delta3; + state->delta3 = state->delta2; + state->delta2 = state->delta1; + state->delta1 = delta0; + + state->mod4 = clamp16(state->mod4 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta5)) >> 8; + state->mod3 = clamp16(state->mod3 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta4)) >> 8; + state->mod2 = clamp16(state->mod2 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta3)) >> 8; + state->mod1 = clamp16(state->mod1 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta2)) >> 8; + + VGM_ASSERT(step0_index > 7, "UBI ADPCM: index over 7\n"); + return sample_new; +} + +static void decode_subframe_mono(ubi_adpcm_channel_data* ch_state, uint8_t* codes, int16_t* samples, int code_count, int bps) { + int i; + int16_t (*expand_code)(uint8_t,ubi_adpcm_channel_data*) = NULL; + + if (bps == 6) + expand_code = expand_code_6bit; + else + expand_code = expand_code_4bit; + + for (i = 0; i < code_count; i++) { + samples[i] = expand_code(codes[i], ch_state); + } +} + +static void decode_subframe_stereo(ubi_adpcm_channel_data* ch0_state, ubi_adpcm_channel_data* ch1_state, uint8_t* codes, int16_t* samples, int code_count, int bps) { + int i; + int16_t (*expand_code)(uint8_t,ubi_adpcm_channel_data*) = NULL; + + if (bps == 6) + expand_code = expand_code_6bit; + else + expand_code = expand_code_4bit; + + for (i = 0; i < code_count; i += 8) { + samples[i + 0] = expand_code(codes[i + 0], ch0_state); + samples[i + 1] = expand_code(codes[i + 2], ch0_state); + samples[i + 2] = expand_code(codes[i + 4], ch0_state); + samples[i + 3] = expand_code(codes[i + 6], ch0_state); + samples[i + 4] = expand_code(codes[i + 1], ch1_state); + samples[i + 5] = expand_code(codes[i + 3], ch1_state); + samples[i + 6] = expand_code(codes[i + 5], ch1_state); + samples[i + 7] = expand_code(codes[i + 7], ch1_state); + } + + for (i = 0; i < code_count; i += 8) { + int16_t samples_old[8]; + memcpy(samples_old, samples, sizeof(samples_old)); + + samples[0] = clamp16(samples_old[0] + samples_old[4]); + samples[1] = clamp16(samples_old[0] - samples_old[4]); + samples[2] = clamp16(samples_old[1] + samples_old[5]); + samples[3] = clamp16(samples_old[1] - samples_old[5]); + + samples[4] = clamp16(samples_old[2] + samples_old[6]); + samples[5] = clamp16(samples_old[2] - samples_old[6]); + samples[6] = clamp16(samples_old[3] + samples_old[7]); + samples[7] = clamp16(samples_old[3] - samples_old[7]); + + samples += 8; + } +} + +/* unpack uint32 LE data into 4/6-bit codes: + * - for 4-bit, 32b contain 8 codes + * ex. uint8_t 0x98576787DB5725A8... becomes 0x87675798 LE = 8 7 6 7 5 7 9 8 ... + * - for 6-bit, 32b contain ~5 codes with leftover bits used in following 32b + * ex. uint8_t 0x98576787DB5725A8... becomes 0x87675798 LE = 100001 110110 011101 010111 100110 00, + * 0xA82557DB LE = 1010 100000 100101 010101 111101 1011 ... (where last 00 | first 1010 = 001010), etc + * Codes aren't signed but rather have a particular meaning (see decoding). + */ +void unpack_codes(uint8_t *data, uint8_t* codes, int code_count, int bps) { + int i; + size_t pos = 0; + uint64_t bits = 0, input = 0; + const uint64_t mask = (bps == 6) ? 0x3f : 0x0f; + + for (i = 0; i < code_count; i++) { + if (bits < bps) { + uint32_t source32le = (uint32_t)get_32bitLE(data + pos); + pos += 0x04; + + input = (input << 32) | (uint64_t)source32le; + bits += 32; + } + + bits -= bps; + codes[i] = (uint8_t)((input >> bits) & mask); + } +} + +static void read_channel_state(uint8_t *data, ubi_adpcm_channel_data *ch) { + /* ADPCM frame state, some fields are unused and contain repeated garbage in all frames but + * probably exist for padding (original code uses MMX to operate in multiple 16b at the same time) + * or reserved for other bit modes */ + + ch->signature = get_32bitLE(data + 0x00); + ch->step1 = get_32bitLE(data + 0x04); + ch->next1 = get_32bitLE(data + 0x08); + ch->next2 = get_32bitLE(data + 0x0c); + + ch->coef1 = get_16bitLE(data + 0x10); + ch->coef2 = get_16bitLE(data + 0x12); + ch->unused1 = get_16bitLE(data + 0x14); + ch->unused2 = get_16bitLE(data + 0x16); + ch->mod1 = get_16bitLE(data + 0x18); + ch->mod2 = get_16bitLE(data + 0x1a); + ch->mod3 = get_16bitLE(data + 0x1c); + ch->mod4 = get_16bitLE(data + 0x1e); + + ch->hist1 = get_16bitLE(data + 0x20); + ch->hist2 = get_16bitLE(data + 0x22); + ch->unused3 = get_16bitLE(data + 0x24); + ch->unused4 = get_16bitLE(data + 0x26); + ch->delta1 = get_16bitLE(data + 0x28); + ch->delta2 = get_16bitLE(data + 0x2a); + ch->delta3 = get_16bitLE(data + 0x2c); + ch->delta4 = get_16bitLE(data + 0x2e); + + ch->delta5 = get_16bitLE(data + 0x30); + ch->unused5 = get_16bitLE(data + 0x32); + + VGM_ASSERT(ch->signature != 0x02, "UBI ADPCM: incorrect channel header\n"); + VGM_ASSERT(ch->unused3 != 0x00, "UBI ADPCM: found unused3 used\n"); + VGM_ASSERT(ch->unused4 != 0x00, "UBI ADPCM: found unused4 used\n"); +} + +static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data *data) { + int code_count_a, code_count_b; + size_t subframe_size_a, subframe_size_b, frame_size, bytes; + int bps = data->header.bits_per_sample; + int channels = data->header.channels; + + + /* last frame is shorter (subframe A or B may not exist), avoid over-reads in bigfiles */ + if (data->subframe_number + 1 == data->header.subframe_count) { + code_count_a = data->header.codes_per_subframe_last; + code_count_b = 0; + } else if (data->subframe_number + 2 == data->header.subframe_count) { + code_count_a = data->header.codes_per_subframe; + code_count_b = data->header.codes_per_subframe_last; + } else { + code_count_a = data->header.codes_per_subframe; + code_count_b = data->header.codes_per_subframe; + } + + subframe_size_a = (bps * code_count_a / 8); + if (subframe_size_a) subframe_size_a += 0x01; + subframe_size_b = (bps * code_count_b / 8); + if (subframe_size_b) subframe_size_b += 0x01; + + frame_size = 0x34 * channels + subframe_size_a + subframe_size_b; + + //todo check later games (ex. Myst IV) if they handle this + /* last frame can have an odd number of codes, with data ending not aligned to 32b, + * but RE'd code unpacking and stereo decoding always assume to be aligned, causing clicks in some cases + * (if data ends in 0xEE it'll try to do 0x000000EE, but only unpack codes 0 0, thus ignoring actual last 2) */ + //memset(data->frame, 0, sizeof(data->frame)); + //memset(data->codes, 0, sizeof(data->codes)); + //memset(data->samples, 0, sizeof(data->samples)); + + + bytes = read_streamfile(data->frame, data->offset, frame_size, sf); + if (bytes != frame_size) { + VGM_LOG("UBI ADPCM: wrong bytes read %x vs %x at %lx\n", bytes, frame_size, data->offset); + //goto fail; //? + } + + if (channels == 1) { + read_channel_state(data->frame + 0x00, &data->ch[0]); + + unpack_codes(data->frame + 0x34, data->codes, code_count_a, bps); + decode_subframe_mono(&data->ch[0], data->codes, &data->samples[0], code_count_a, bps); + + unpack_codes(data->frame + 0x34 + subframe_size_a, data->codes, code_count_b, bps); + decode_subframe_mono(&data->ch[0], data->codes, &data->samples[code_count_a], code_count_b, bps); + } + else if (channels == 2) { + read_channel_state(data->frame + 0x00, &data->ch[0]); + read_channel_state(data->frame + 0x34, &data->ch[1]); + + unpack_codes(data->frame + 0x68, data->codes, code_count_a, bps); + decode_subframe_stereo(&data->ch[0], &data->ch[1], data->codes, &data->samples[0], code_count_a, bps); + + unpack_codes(data->frame + 0x68 + subframe_size_a, data->codes, code_count_b, bps); + decode_subframe_stereo(&data->ch[0], &data->ch[1], data->codes, &data->samples[code_count_a], code_count_b, bps); + } + + /* frame done */ + data->offset += frame_size; + data->subframe_number += 2; + data->samples_consumed = 0; + data->samples_filled = (code_count_a + code_count_b) / channels; +} + + +int ubi_adpcm_get_samples(ubi_adpcm_codec_data *data) { + if (!data) + return 0; + + return data->header.sample_count / data->header.channels; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c index f0c2d2879..d04d199fd 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c @@ -195,10 +195,11 @@ void free_vorbis_custom(vorbis_custom_codec_data * data) { if (!data) return; - /* internal decoder cleanp */ - vorbis_info_clear(&data->vi); - vorbis_comment_clear(&data->vc); + /* internal decoder cleanup */ + vorbis_block_clear(&data->vb); vorbis_dsp_clear(&data->vd); + vorbis_comment_clear(&data->vc); + vorbis_info_clear(&data->vi); free(data->buffer); free(data); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c index 2f4261dc4..3374f505b 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c @@ -1,30 +1,88 @@ #include "../util.h" #include "coding.h" -/* fixed point (.8) amount to scale the current step size by */ -/* part of the same series as used in MS ADPCM "ADPCMTable" */ -static const unsigned int scale_step[16] = { +/* fixed point amount to scale the current step size */ +static const unsigned int scale_step_aica[16] = { 230, 230, 230, 230, 307, 409, 512, 614, 230, 230, 230, 230, 307, 409, 512, 614 }; -/* actually implemented with if-else/switchs but that's too goofy */ -static const int scale_step_aska[8] = { +static const int scale_step_adpcmb[16] = { + 57, 57, 57, 57, 77, 102, 128, 153, 57, 57, 57, 57, 77, 102, 128, 153, }; -/* expand an unsigned four bit delta to a wider signed range */ +/* 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, -1, -3, -5, -7, -9,-11,-13,-15 }; +/* Yamaha ADPCM-B (aka DELTA-T) expand used in YM2608/YM2610/etc (cross referenced with various sources and .so) */ +static void yamaha_adpcmb_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) { + int code, delta, sample; -/* raw Yamaha ADPCM a.k.a AICA as it's prominently used in Naomi/Dreamcast's Yamaha AICA sound chip, - * also found in Windows RIFF and older Yamaha's arcade sound chips. */ -void decode_yamaha(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; + code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; + delta = ((((code & 0x7) * 2) + 1) * (*step_size)) >> 3; /* like 'mul' IMA */ + if (code & 8) + delta = -delta; + sample = *hist1 + delta; + sample = clamp16(sample); /* this may not be needed (not done in Aska) but no byte changes */ + + *step_size = ((*step_size) * scale_step_adpcmb[code]) >> 6; + if (*step_size < 0x7f) *step_size = 0x7f; + else if (*step_size > 0x6000) *step_size = 0x6000; + + *out_sample = sample; + *hist1 = sample; +} + +/* Yamaha AICA expand, slightly filtered vs "ACM" Yamaha ADPCM, same as Creative ADPCM + * (some info from https://github.com/vgmrips/vgmplay, https://wiki.multimedia.cx/index.php/Creative_ADPCM) */ +static void yamaha_aica_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) { + int code, delta, sample; + + *hist1 = *hist1 * 254 / 256; /* hist filter is vital to get correct waveform but not done in many emus */ + + code = ((read_8bit(byte_offset,stream->streamfile) >> nibble_shift))&0xf; + delta = (*step_size * scale_delta[code]) / 8; /* 'mul' IMA with table (not sure if part of encoder) */ + sample = *hist1 + delta; + + sample = clamp16(sample); /* apparently done by official encoder */ + + *step_size = ((*step_size) * scale_step_aica[code]) >> 8; + if (*step_size < 0x7f) *step_size = 0x7f; + else if (*step_size > 0x6000) *step_size = 0x6000; + + *out_sample = sample; + *hist1 = sample; +} + + +/* info about Yamaha ADPCM as created by official yadpcm.acm (in 'Yamaha-ADPCM-ACM-Driver-100-j') + * - possibly RIFF codec 0x20 + * - simply called "Yamaha ADPCM Codec" (even though not quite like Yamaha ADPCM-B) + * - block_align = (sample_rate / 0x3C + 0x04) * channels (ex. 0x2E6 for 22050 stereo, probably given in RIFF) + * - low nibble first, stereo or mono modes (no interleave) + * - expand (old IMA 'shift+add' style, not 'mul' style): + * delta = step_size >> 3; + * if (code & 1) delta += step_size >> 2; + * if (code & 2) delta += step_size >> 1; + * if (code & 4) delta += step_size; + * if (code & 8) delta = -delta; + * sample = hist + clamp16(delta); + * though compiled more like: + * sample = hist + (1-2*(code & 8) * (step_size/8 + step_size/2 * (code&2) + step_size/4 * (code&1) + step_size * (code&4)) + * - step_size update: + * step_size = clamp_range(step_size * scale_step_aica[code] >> 8, 0x7F, 0x6000) + */ + + +/* Yamaha AICA ADPCM (also used in YMZ280B with high nibble first) */ +void decode_aica(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; @@ -32,8 +90,7 @@ void decode_yamaha(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspac if (step_size < 0x7f) step_size = 0x7f; if (step_size > 0x6000) step_size = 0x6000; - for (i=first_sample,sample_count=0; ioffset + i : /* stereo: one nibble per channel */ stream->offset + i/2; /* mono: consecutive nibbles */ @@ -41,26 +98,20 @@ void decode_yamaha(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspac (!(channel&1) ? 0:4) : /* even = low/L, odd = high/R */ (!(i&1) ? 0:4); /* low nibble first */ - /* Yamaha/AICA expand, but same result as IMA's (((delta * 2 + 1) * step) >> 3) */ - sample_nibble = ((read_8bit(byte_offset,stream->streamfile) >> nibble_shift))&0xf; - sample_delta = (step_size * scale_delta[sample_nibble]) / 8; - sample_decoded = hist1 + sample_delta; - - outbuf[sample_count] = clamp16(sample_decoded); - hist1 = outbuf[sample_count]; - - step_size = (step_size * scale_step[sample_nibble]) >> 8; - if (step_size < 0x7f) step_size = 0x7f; - if (step_size > 0x6000) step_size = 0x6000; + yamaha_aica_expand_nibble(stream, byte_offset, nibble_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, same-ish with modified step table (reversed from Android SO's .so) */ +/* 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) { - int i, sample_count, num_frame; + int i, sample_count = 0, num_frame; + int16_t out_sample; int32_t hist1 = stream->adpcm_history1_32; int step_size = stream->adpcm_step_index; @@ -75,13 +126,13 @@ void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); step_size = read_16bitLE(header_offset+0x02,stream->streamfile); - if (step_size < 0x7f) step_size = 0x7f; - if (step_size > 0x6000) step_size = 0x6000; + /* in most files 1st frame has step 0 but it seems ok and accounted for */ + //if (step_size < 0x7f) step_size = 0x7f; + //else if (step_size > 0x6000) step_size = 0x6000; } /* decode nibbles (layout: varies) */ - for (i=first_sample,sample_count=0; ioffset + 0x40*num_frame + 0x04*channelspacing) + i : /* stereo: one nibble per channel */ (stream->offset + 0x40*num_frame + 0x04*channelspacing) + i/2; /* mono: consecutive nibbles */ @@ -89,26 +140,19 @@ void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */ - sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; - sample_delta = ((((sample_nibble & 0x7) * 2) | 1) * step_size) >> 3; /* like 'mul' IMA with 'or' */ - if (sample_nibble & 8) sample_delta = -sample_delta; - sample_decoded = hist1 + sample_delta; - - outbuf[sample_count] = sample_decoded; /* not clamped */ - hist1 = outbuf[sample_count]; - - step_size = (step_size * scale_step_aska[sample_nibble & 0x07]) >> 6; - if (step_size < 0x7f) step_size = 0x7f; - if (step_size > 0x6000) step_size = 0x6000; + yamaha_adpcmb_expand_nibble(stream, byte_offset, nibble_shift, &hist1, &step_size, &out_sample); + outbuf[sample_count] = out_sample; + sample_count += channelspacing; } stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_size; } + /* Yamaha ADPCM with unknown expand variation (noisy), step size is double of normal Yamaha? */ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i, sample_count, num_frame; + int i, sample_count = 0, num_frame; int32_t hist1 = stream->adpcm_history1_32; int step_size = stream->adpcm_step_index; @@ -124,26 +168,27 @@ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); step_size = read_16bitLE(header_offset+0x02,stream->streamfile); if (step_size < 0x7f) step_size = 0x7f; - if (step_size > 0x6000) step_size = 0x6000; + else if (step_size > 0x6000) step_size = 0x6000; } /* decode nibbles (layout: all nibbles from one channel) */ - for (i=first_sample,sample_count=0; ioffset + 0x40*num_frame + 0x04) + i/2; - int nibble_shift = (i&1?4:0); /* low nibble first */ + int nibble_shift = (i&1?4:0); /* low nibble first? */ - /* Yamaha expand? */ - sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; - sample_delta = (step_size * scale_delta[sample_nibble] / 4) / 8; //todo not ok - sample_decoded = hist1 + sample_delta; + code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + delta = (step_size * scale_delta[code]) / 8; //todo wrong + sample = hist1 + delta; - outbuf[sample_count] = clamp16(sample_decoded); + outbuf[sample_count] = clamp16(sample); hist1 = outbuf[sample_count]; - step_size = (step_size * scale_step[sample_nibble]) >> 8; + step_size = (step_size * scale_step_aica[code]) / 260.0; //todo wrong if (step_size < 0x7f) step_size = 0x7f; - if (step_size > 0x6000) step_size = 0x6000; + else if (step_size > 0x6000) step_size = 0x6000; + + sample_count += channelspacing; } stream->adpcm_history1_32 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 2b45dd1a2..4f55ac7e2 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -2,7 +2,8 @@ /* Defines the list of accepted extensions. vgmstream doesn't use it internally so it's here - * to inform plugins that need it. Common extensions are commented out to avoid stealing them. */ + * to inform plugins that need it. Common extensions are commented out to avoid stealing them + * and possibly adding an unwanted association to the player. */ /* Some extensions require external libraries and could be #ifdef, not worth. */ @@ -17,6 +18,7 @@ static const char* extension_list[] = { "208", "2dx9", "2pfs", + "8", //txth/reserved [Gungage (PS1)] "800", "9tav", @@ -26,6 +28,7 @@ static const char* extension_list[] = { "aax", "abk", //"ac3", //common, FFmpeg/not parsed (AC3) + "acb", "ace", //fake extension for tri-Ace's .aac (renamed, to be removed) "acm", "ad", //txth/reserved [Xenosaga Freaks (PS2)] @@ -91,6 +94,7 @@ static const char* extension_list[] = { "bik", "bika", "bik2", + //"bin", //common "bk2", "bmdx", "bms", @@ -123,6 +127,7 @@ static const char* extension_list[] = { "cxs", "da", + "data", "dax", "dbm", "dcs", @@ -182,18 +187,29 @@ static const char* extension_list[] = { "hlwav", "hps", "hsf", + "hx2", + "hx3", + "hxc", + "hxd", + "hxg", + "hxx", "hwas", "iab", "iadp", + "idmsf", "idsp", "idvi", //fake extension/header id for .pcm (renamed, to be removed) + "idwav", "idx", + "idxma", "ikm", "ild", "ilv", //txth/reserved [Star Wars Episode III (PS2)] + "ima", "imc", "int", + "is14", "isd", "isws", "itl", @@ -223,6 +239,7 @@ static const char* extension_list[] = { "laifc", //fake extension for .aifc "lac3", //fake extension for .ac3, FFmpeg/not parsed "lasf", //fake extension for .asf (various) + "lbin", //fake extension for .bin (various) "leg", "lflac", //fake extension for .flac, FFmpeg/not parsed "lin", @@ -299,6 +316,7 @@ static const char* extension_list[] = { "nop", "nps", "npsf", //fake extension/header id for .nps (in bigfiles) + "nub", "nus3audio", "nus3bank", "nwa", @@ -312,9 +330,11 @@ static const char* extension_list[] = { //"opus", //common "opusx", "otm", + "oto", //txth/reserved [Vampire Savior (SAT)] "ovb", "p04", //txth/reserved [Psychic Force 2012 (DC)] + "p16", //txth/reserved [Astal (SAT)] "p1d", //txth/reserved [Farming Simulator 18 (3DS)] "p2a", //txth/reserved [Thunderhawk Operation Phoenix (PS2)] "p2bt", @@ -327,6 +347,7 @@ static const char* extension_list[] = { "pona", "pos", "ps2stm", //fake extension for .stm (renamed? to be removed?) + "psf", "psh", //fake extension for .vsv (to be removed) "psnd", "psw", //fake extension for .wam (renamed, to be removed) @@ -381,9 +402,11 @@ static const char* extension_list[] = { "sbin", "sc", "scd", + "sch", "sd9", "sdf", "sdt", + "seb", "seg", "sf0", "sfl", @@ -396,6 +419,7 @@ static const char* extension_list[] = { "slb", //txth/reserved [THE Nekomura no Hitobito (PS2)] "sli", "smc", + "smk", "smp", "smpl", //fake extension/header id for .v0/v1 (renamed, to be removed) "smv", @@ -431,7 +455,7 @@ static const char* extension_list[] = { "swag", "swav", "swd", - "switch_audio" + "switch_audio", "sx", "sxd", "sxd2", @@ -459,6 +483,7 @@ static const char* extension_list[] = { "va3", "vag", "vai", + "vam", //txth/reserved [Rocket Power: Beach Bandits (PS2)] "vas", "vawx", "vb", @@ -516,6 +541,7 @@ static const char* extension_list[] = { "xa30", "xag", "xau", + "xav", "xen", "xma", "xma2", @@ -526,7 +552,7 @@ static const char* extension_list[] = { "xss", "xvag", "xvas", - "xwav",//fake extension for .wav (renamed, to be removed) + "xwav", //fake extension for .wav (renamed, to be removed) "xwb", "xmd", "xopus", @@ -550,12 +576,36 @@ static const char* extension_list[] = { //, NULL //end mark }; +static const char* common_extension_list[] = { + "aac", //common + "ac3", //common, FFmpeg/not parsed (AC3) + "aif", //common + "aiff", //common + "bin", //common + "flac", //common + "gsf", //conflicts with GBA gsf plugins? + "mp2", //common + "mp3", //common + "mp4", //common + "mpc", //common + "ogg", //common + "opus", //common + "stm", //common + "wav", //common +}; + + /* List supported formats and return elements in the list, for plugins that need to know. */ const char ** vgmstream_get_formats(size_t * size) { *size = sizeof(extension_list) / sizeof(char*); return extension_list; } +const char ** vgmstream_get_common_formats(size_t * size) { + *size = sizeof(common_extension_list) / sizeof(char*); + return common_extension_list; +} + /* internal description info */ @@ -608,6 +658,7 @@ static const coding_info coding_info_list[] = { {coding_PSX, "Playstation 4-bit ADPCM"}, {coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"}, {coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"}, + {coding_PSX_pivotal, "Playstation 4-bit ADPCM (Pivotal)"}, {coding_HEVAG, "Sony HEVAG 4-bit ADPCM"}, {coding_EA_XA, "Electronic Arts EA-XA 4-bit ADPCM v1"}, @@ -650,8 +701,8 @@ static const coding_info coding_info_list[] = { {coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"}, {coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"}, {coding_WS, "Westwood Studios VBR ADPCM"}, - {coding_YAMAHA, "Yamaha 4-bit ADPCM"}, - {coding_YAMAHA_int, "Yamaha 4-bit ADPCM (mono/interleave)"}, + {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, + {coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"}, {coding_ASKA, "tri-Ace Aska 4-bit ADPCM"}, {coding_NXAP, "Nex NXAP 4-bit ADPCM"}, {coding_NDS_PROCYON, "Procyon Studio Digital Sound Elements NDS 4-bit APDCM"}, @@ -666,6 +717,8 @@ static const coding_info coding_info_list[] = { {coding_XMD, "Konami XMD 4-bit ADPCM"}, {coding_PCFX, "PC-FX 4-bit ADPCM"}, {coding_OKI16, "OKI 4-bit ADPCM (16-bit output)"}, + {coding_OKI4S, "OKI 4-bit ADPCM (4-shift)"}, + {coding_PTADPCM, "Platinum 4-bit ADPCM"}, {coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"}, {coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, @@ -676,6 +729,7 @@ static const coding_info coding_info_list[] = { {coding_ACM, "InterPlay ACM"}, {coding_NWA, "VisualArt's NWA DPCM"}, {coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"}, + {coding_UBI_ADPCM, "Ubisoft 4/6-bit ADPCM"}, {coding_EA_MT, "Electronic Arts MicroTalk"}, @@ -779,7 +833,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_RS03, "Retro Studios RS03 header"}, {meta_DSP_STD, "Nintendo DSP header"}, {meta_DSP_CSTR, "Namco Cstr header"}, - {meta_GCSW, "GCSW header"}, + {meta_GCSW, "MileStone GCSW header"}, {meta_PS2_SShd, "Sony ADS header"}, {meta_NPS, "Namco NPSF header"}, {meta_RWSD, "Nintendo RWSD header (single stream)"}, @@ -789,7 +843,7 @@ static const meta_info meta_info_list[] = { {meta_FWAV, "Nintendo FWAV header"}, {meta_XA, "Sony XA header"}, {meta_PS2_RXWS, "Sony RXWS header"}, - {meta_PS2_RAW, ".int PCM raw header"}, + {meta_RAW_INT, "PS2 .int raw header"}, {meta_PS2_OMU, "Alter Echo OMU Header"}, {meta_DSP_STM, "Intelligent Systems STM header"}, {meta_PS2_EXST, "Sony EXST header"}, @@ -803,15 +857,15 @@ static const meta_info meta_info_list[] = { {meta_DSP_GCM, "Double DSP header stereo by .gcm extension"}, {meta_IDSP_TT, "Traveller's Tales IDSP header"}, {meta_RSTM_SPM, "Nintendo RSTM header (brstmspm)"}, - {meta_RAW, "assumed RAW PCM file by .raw extension"}, + {meta_RAW_PCM, "PC .raw raw header"}, {meta_PS2_VAGi, "Sony VAGi header"}, {meta_PS2_VAGp, "Sony VAGp header"}, {meta_PS2_pGAV, "Sony pGAV header"}, - {meta_PSX_GMS, "assumed Grandia GMS file by .gms extension"}, - {meta_STR_WAV, "Blitz Games STR+WAV header"}, + {meta_SEB, "Game Arts .SEB header"}, + {meta_STR_WAV, "Blitz Games .STR+WAV header"}, {meta_PS2_ILD, "ILD header"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, - {meta_XBOX_WAVM, "Xbox WAVM raw header"}, + {meta_RAW_WAVM, "Xbox .wavm raw header"}, {meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"}, {meta_EA_SCHL, "Electronic Arts SCHl header (variable)"}, {meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"}, @@ -824,7 +878,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_WSI, "Alone in the Dark .WSI header"}, {meta_AIFC, "Apple AIFF-C (Audio Interchange File Format) header"}, {meta_AIFF, "Apple AIFF (Audio Interchange File Format) header"}, - {meta_STR_SNDS, ".str SNDS SHDR chunk"}, + {meta_STR_SNDS, "3DO .str header"}, {meta_WS_AUD, "Westwood Studios .aud header"}, {meta_WS_AUD_old, "Westwood Studios .aud (old) header"}, {meta_PS2_IVB, "IVB/BVII header"}, @@ -850,11 +904,7 @@ static const meta_info meta_info_list[] = { {meta_XWB, "Microsoft XWB header"}, {meta_PS2_XA30, "Reflections XA30 PS2 header"}, {meta_MUSC, "Krome MUSC header"}, - {meta_MUSX_V004, "MUSX / Version 004 Header"}, - {meta_MUSX_V005, "MUSX / Version 005 Header"}, - {meta_MUSX_V006, "MUSX / Version 006 Header"}, - {meta_MUSX_V010, "MUSX / Version 010 Header"}, - {meta_MUSX_V201, "MUSX / Version 201 Header"}, + {meta_MUSX, "Eurocom MUSX header"}, {meta_LEG, "Legaia 2 - Duel Saga LEG Header"}, {meta_FILP, "Bio Hazard - Gun Survivor FILp Header"}, {meta_IKM, "MiCROViSiON IKM header"}, @@ -877,7 +927,7 @@ static const meta_info meta_info_list[] = { {meta_SCD_PCM, "Lunar: Eternal Blue .PCM header"}, {meta_PS2_PCM, "Konami KCEJ East .PCM header"}, {meta_PS2_RKV, "Legacy of Kain - Blood Omen 2 RKV PS2 header"}, - {meta_PS2_VAS, "Pro Baseball Spirits 5 VAS Header"}, + {meta_PS2_VAS, "Konami .VAS header"}, {meta_PS2_TEC, "assumed TECMO badflagged stream by .tec extension"}, {meta_PS2_ENTH, ".enth Header"}, {meta_SDT, "High Voltage .sdt header"}, @@ -889,8 +939,8 @@ static const meta_info meta_info_list[] = { {meta_VS, "Melbourne House .VS header"}, {meta_DC_STR, "Sega Stream Asset Builder header"}, {meta_DC_STR_V2, "variant of Sega Stream Asset Builder header"}, - {meta_XBOX_XMU, "XMU header"}, - {meta_XBOX_XVAS, "Konami .XVAS header"}, + {meta_XMU, "Outrage XMU header"}, + {meta_XVAS, "Konami .XVAS header"}, {meta_PS2_XA2, "Acclaim XA2 Header"}, {meta_DC_IDVI, "Capcom IDVI header"}, {meta_KRAW, "Geometry Wars: Galaxies KRAW header"}, @@ -900,30 +950,13 @@ static const meta_info meta_info_list[] = { {meta_PS2_MIHB, "Sony MultiStream MIC header"}, {meta_DSP_WII_MUS, "mus header"}, {meta_WII_SNG, "SNG DSP Header"}, - {meta_RSD2VAG, "Radical RSD2/VAG header"}, - {meta_RSD2PCMB, "Radical RSD2/PCMB header"}, - {meta_RSD2XADP, "Radical RSD2/XADP header"}, - {meta_RSD3VAG, "Radical RSD3/VAG header"}, - {meta_RSD3GADP, "Radical RSD3/GADP header"}, - {meta_RSD3PCM, "Radical RSD3/PCM header"}, - {meta_RSD3PCMB, "Radical RSD3/PCMB header"}, - {meta_RSD4PCMB, "Radical RSD4/PCMB header"}, - {meta_RSD4PCM, "Radical RSD4/PCM header"}, - {meta_RSD4RADP, "Radical RSD4/RADP header"}, - {meta_RSD4VAG, "Radical RSD4/VAG header"}, - {meta_RSD6XADP, "Radical RSD6/XADP header"}, - {meta_RSD6VAG, "Radical RSD6/VAG header"}, - {meta_RSD6WADP, "Radical RSD6/WADP header"}, - {meta_RSD6RADP, "Radical RSD6/RADP header"}, - {meta_RSD6XMA, "Radical RSD6/XMA header"}, - {meta_RSD6AT3P, "Radical RSD6/AT3+ header"}, - {meta_RSD6WMA, "Radical RSD6/WMA header"}, + {meta_RSD, "Radical RSD header"}, {meta_DC_ASD, "ASD Header"}, {meta_NAOMI_SPSD, "Naomi SPSD header"}, {meta_FFXI_BGW, "Square Enix .BGW header"}, {meta_FFXI_SPW, "Square Enix .SPW header"}, {meta_PS2_ASS, "SystemSoft .ASS header"}, - {meta_NUB_IDSP, "Namco NUB IDSP header"}, + {meta_NUB, "Namco NUB header"}, {meta_IDSP_NL, "Next Level IDSP header"}, {meta_IDSP_IE, "Inevitable Entertainment IDSP Header"}, {meta_UBI_JADE, "Ubisoft Jade RIFF header"}, @@ -976,7 +1009,6 @@ static const meta_info meta_info_list[] = { {meta_PS2_VGV, "Rune: Viking Warlord VGV Header"}, {meta_NGC_GCUB, "GCub Header"}, {meta_NGC_SCK_DSP, "The Scorpion King SCK Header"}, - {meta_NGC_SWD, "PSF + Standard DSP Headers"}, {meta_CAFF, "Apple Core Audio Format File header"}, {meta_PC_MXST, "Lego Island MxSt Header"}, {meta_SAB, "Team17 SAB header"}, @@ -987,7 +1019,7 @@ static const meta_info meta_info_list[] = { {meta_XBOX_HLWAV, "Half Life 2 bgm header"}, {meta_STX, "Nintendo .stx header"}, {meta_MYSPD, "U-Sing .MYSPD header"}, - {meta_HIS, "Her Interactive Sound header"}, + {meta_HIS, "Her Interactive HIS header"}, {meta_PS2_AST, "KOEI AST header"}, {meta_CAPDSP, "Capcom DSP header"}, {meta_DMSG, "RIFF/DMSGsegh header"}, @@ -1029,7 +1061,6 @@ static const meta_info meta_info_list[] = { {meta_NGC_NST_DSP, "Animaniacs NST header"}, {meta_BAF, "Bizarre Creations .baf header"}, {meta_MSF, "Sony MSF header"}, - {meta_NUB_VAG, "Namco NUB VAG header"}, {meta_PS3_PAST, "SNDP header"}, {meta_SGXD, "Sony SGXD header"}, {meta_NGCA, "NGCA header"}, @@ -1041,7 +1072,7 @@ static const meta_info meta_info_list[] = { {meta_VS_STR, "Square .VS STR* header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, {meta_VAWX, "feelplus VAWX header"}, - {meta_PC_SNDS, "assumed Heavy Iron IMA by .snds extension"}, + {meta_RAW_SNDS, "PC .snds raw header"}, {meta_PS2_WMUS, "assumed The Warriors Sony ADPCM by .wmus extension"}, {meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"}, {meta_IOS_PSND, "PSND Header"}, @@ -1059,7 +1090,6 @@ static const meta_info meta_info_list[] = { {meta_PS2_HSF, "Lowrider 'HSF' header"}, {meta_PS3_IVAG, "PS3 'IVAG' Header"}, {meta_PS2_2PFS, "Konami 2PFS header"}, - {meta_RSD6OOGV, "RSD6/OOGV Header"}, {meta_UBI_CKD, "Ubisoft CKD RIFF header"}, {meta_PS2_VBK, "PS2 VBK Header"}, {meta_OTM, "Otomedius OTM Header"}, @@ -1077,7 +1107,6 @@ static const meta_info meta_info_list[] = { {meta_FFMPEG, "FFmpeg supported file format"}, {meta_X360_CXS, "tri-Crescendo CXS header"}, {meta_AKB, "Square-Enix AKB header"}, - {meta_NUB_XMA, "Namco NUB XMA header"}, {meta_X360_PASX, "Namco PASX header"}, {meta_XMA_RIFF, "Microsoft XMA RIFF header"}, {meta_X360_AST, "Capcom AST (X360) header"}, @@ -1092,8 +1121,8 @@ static const meta_info meta_info_list[] = { {meta_TA_AAC_MOBILE, "tri-Ace AAC (Mobile) header"}, {meta_MTA2, "Konami MTA2 header"}, {meta_NGC_ULW, "Criterion ULW raw header"}, - {meta_PC_XA30, "Reflections XA30 PC header"}, - {meta_WII_04SW, "Reflections 04SW header"}, + {meta_XA_XA30, "Reflections XA30 header"}, + {meta_XA_04SW, "Reflections 04SW header"}, {meta_TXTH, "TXTH generic header"}, {meta_EA_BNK, "Electronic Arts BNK header"}, {meta_SK_AUD, "Silicon Knights AUD header"}, @@ -1183,7 +1212,8 @@ static const meta_info meta_info_list[] = { {meta_MSF_TAMASOFT, "Tama-Soft MSF header"}, {meta_XPS_DAT, "From Software .XPS+DAT header"}, {meta_ZSND, "Vicarious Visions ZSND header"}, - {meta_DSP_ADPCMX, "AQUASTYLE ADPY header"}, + {meta_DSP_ADPY, "AQUASTYLE ADPY header"}, + {meta_DSP_ADPX, "AQUASTYLE ADPX header"}, {meta_OGG_OPUS, "Ogg Opus header"}, {meta_IMC, "iNiS .IMC header"}, {meta_GIN, "Electronic Arts Gnsu header"}, @@ -1196,41 +1226,120 @@ static const meta_info meta_info_list[] = { {meta_XWMA_KONAMI, "Konami XWMA header"}, {meta_9TAV, "Konami 9TAV header"}, {meta_BWAV, "Nintendo BWAV header"}, - {meta_RAD, "Traveller's Tales RAD header"}, + {meta_RAD, "Traveller's Tales .RAD header"}, + {meta_SMACKER, "RAD Game Tools SMACKER header"}, + {meta_MZRT, "id Software MZRT header"}, + {meta_XAVS, "Reflections XAVS header"}, + {meta_PSF, "Pivotal PSF header"}, + {meta_DSP_ITL_i, "Infernal .ITL DSP header"}, + {meta_IMA, "Blitz Games .IMA header"}, + {meta_XMV_VALVE, "Valve XMV header"}, + {meta_UBI_HX, "Ubisoft HXx header"}, + {meta_BMP_KONAMI, "Konami BMP header"}, }; - -const char * get_vgmstream_coding_description(coding_t coding_type) { +void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { int i, list_length; + const char *description; - list_length = sizeof(coding_info_list) / sizeof(coding_info); - for (i=0; i < list_length; i++) { - if (coding_info_list[i].type == coding_type) - return coding_info_list[i].description; + /* we need to recurse down because of FFmpeg */ + if (vgmstream->layout_type == layout_layered) { + layered_layout_data* layout_data = vgmstream->layout_data; + get_vgmstream_coding_description(layout_data->layers[0], out, out_size); + return; + } else if (vgmstream->layout_type == layout_segmented) { + segmented_layout_data* layout_data = vgmstream->layout_data; + get_vgmstream_coding_description(layout_data->segments[0], out, out_size); + return; } - return NULL; + description = "CANNOT DECODE"; + + switch (vgmstream->coding_type) { +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: + { + ffmpeg_codec_data *data = vgmstream->codec_data; + + if (data) { + if (data->codec && data->codec->long_name) { + description = data->codec->long_name; + } else if (data->codec && data->codec->name) { + description = data->codec->name; + } else { + description = "FFmpeg (unknown codec)"; + } + } else { + description = "FFmpeg"; + } + break; + } +#endif + default: + list_length = sizeof(coding_info_list) / sizeof(coding_info); + for (i = 0; i < list_length; i++) { + if (coding_info_list[i].type == vgmstream->coding_type) + description = coding_info_list[i].description; + } + break; + } + + strncpy(out, description, out_size); } -const char * get_vgmstream_layout_description(layout_t layout_type) { +const char * get_vgmstream_layout_name(layout_t layout_type) { int i, list_length; list_length = sizeof(layout_info_list) / sizeof(layout_info); - for (i=0; i < list_length; i++) { + for (i = 0; i < list_length; i++) { if (layout_info_list[i].type == layout_type) return layout_info_list[i].description; } return NULL; } -const char * get_vgmstream_meta_description(meta_t meta_type) { +void get_vgmstream_layout_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { + char temp[256]; + VGMSTREAM* vgmstreamsub = NULL; + const char* description; + + description = get_vgmstream_layout_name(vgmstream->layout_type); + if (!description) description = "INCONCEIVABLE"; + + if (vgmstream->layout_type == layout_layered) { + vgmstreamsub = ((layered_layout_data*)vgmstream->layout_data)->layers[0]; + snprintf(temp, sizeof(temp), "%s (%i layers)", description, ((layered_layout_data*)vgmstream->layout_data)->layer_count); + } else if (vgmstream->layout_type == layout_segmented) { + snprintf(temp, sizeof(temp), "%s (%i segments)", description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); + vgmstreamsub = ((segmented_layout_data*)vgmstream->layout_data)->segments[0]; + } else { + snprintf(temp, sizeof(temp), "%s", description); + } + strncpy(out, temp, out_size); + + /* layouts can contain layouts infinitely let's leave it at one level deep (most common) */ + /* TODO: improve this somehow */ + if (vgmstreamsub && vgmstreamsub->layout_type == layout_layered) { + description = get_vgmstream_layout_name(vgmstreamsub->layout_type); + snprintf(temp, sizeof(temp), " + %s (%i layers)", description, ((layered_layout_data*)vgmstreamsub->layout_data)->layer_count); + concatn(out_size, out, temp); + } else if (vgmstreamsub && vgmstreamsub->layout_type == layout_segmented) { + description = get_vgmstream_layout_name(vgmstreamsub->layout_type); + snprintf(temp, sizeof(temp), " + %s (%i segments)", description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); + concatn(out_size, out, temp); + } +} +void get_vgmstream_meta_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { int i, list_length; + const char *description; + + description = "THEY SHOULD HAVE SENT A POET"; list_length = sizeof(meta_info_list) / sizeof(meta_info); for (i=0; i < list_length; i++) { - if (meta_info_list[i].type == meta_type) - return meta_info_list[i].description; + if (meta_info_list[i].type == vgmstream->meta_type) + description = meta_info_list[i].description; } - return NULL; + strncpy(out, description, out_size); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c index d61cc3e2b..94715a3f8 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c @@ -8,19 +8,48 @@ * Incompatible with decoders that move offsets. */ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { int samples_written = 0; - int frame_size, samples_per_frame, samples_this_block; + int samples_per_frame, samples_this_block; /* used */ + int samples_per_frame_d = 0, samples_this_block_d = 0; /* default */ + int samples_per_frame_f = 0, samples_this_block_f = 0; /* first */ + int samples_per_frame_l = 0, samples_this_block_l = 0; /* last */ + int has_interleave_first = vgmstream->interleave_first_block_size && vgmstream->channels > 1; int has_interleave_last = vgmstream->interleave_last_block_size && vgmstream->channels > 1; - frame_size = get_vgmstream_frame_size(vgmstream); - samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; - if (has_interleave_last && - vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block > vgmstream->num_samples) { - /* adjust values again if inside last interleave */ - frame_size = get_vgmstream_shortframe_size(vgmstream); - samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; + /* setup */ + { + int frame_size_d = get_vgmstream_frame_size(vgmstream); + samples_per_frame_d = get_vgmstream_samples_per_frame(vgmstream); + if (frame_size_d == 0 || samples_per_frame_d == 0) goto fail; + samples_this_block_d = vgmstream->interleave_block_size / frame_size_d * samples_per_frame_d; + } + if (has_interleave_first) { + int frame_size_f = get_vgmstream_frame_size(vgmstream); + samples_per_frame_f = get_vgmstream_samples_per_frame(vgmstream); //todo samples per shortframe + if (frame_size_f == 0 || samples_per_frame_f == 0) goto fail; + samples_this_block_f = vgmstream->interleave_first_block_size / frame_size_f * samples_per_frame_f; + } + if (has_interleave_last) { + int frame_size_l = get_vgmstream_shortframe_size(vgmstream); + samples_per_frame_l = get_vgmstream_samples_per_shortframe(vgmstream); + if (frame_size_l == 0 || samples_per_frame_l == 0) goto fail; + samples_this_block_l = vgmstream->interleave_last_block_size / frame_size_l * samples_per_frame_l; + } + + /* set current values */ + if (has_interleave_first && + vgmstream->current_sample < samples_this_block_f) { + samples_per_frame = samples_per_frame_f; + samples_this_block = samples_this_block_f; + } + else if (has_interleave_last && + vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block_d > vgmstream->num_samples) { + samples_per_frame = samples_per_frame_l; + samples_this_block = samples_this_block_l; + } + else { + samples_per_frame = samples_per_frame_d; + samples_this_block = samples_this_block_d; } /* mono interleaved stream with no layout set, just behave like flat layout */ @@ -28,18 +57,28 @@ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTR samples_this_block = vgmstream->num_samples; + /* write samples */ while (samples_written < sample_count) { int samples_to_do; if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { /* handle looping, restore standard interleave sizes */ - if (has_interleave_last) { /* assumes that won't loop back into a interleave_last */ - frame_size = get_vgmstream_frame_size(vgmstream); - samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; + + if (has_interleave_first && + vgmstream->current_sample < samples_this_block_f) { + /* use first interleave*/ + samples_per_frame = samples_per_frame_f; + samples_this_block = samples_this_block_f; if (samples_this_block == 0 && vgmstream->channels == 1) samples_this_block = vgmstream->num_samples; } + else if (has_interleave_last) { /* assumes that won't loop back into a interleave_last */ + samples_per_frame = samples_per_frame_d; + samples_this_block = samples_this_block_d; + if (samples_this_block == 0 && vgmstream->channels == 1) + samples_this_block = vgmstream->num_samples; + } + continue; } @@ -48,9 +87,7 @@ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTR samples_to_do = sample_count - samples_written; if (samples_to_do == 0) { /* happens when interleave is not set */ - VGM_LOG("layout_interleave: wrong samples_to_do found\n"); - memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t)); - break; + goto fail; } decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); @@ -64,18 +101,34 @@ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTR if (vgmstream->samples_into_block == samples_this_block) { int ch; - if (has_interleave_last && - vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { - /* adjust values again if inside last interleave */ - frame_size = get_vgmstream_shortframe_size(vgmstream); - samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; + if (has_interleave_first && + vgmstream->current_sample == samples_this_block_f) { + /* restore standard frame size after going past first interleave */ + samples_per_frame = samples_per_frame_d; + samples_this_block = samples_this_block_d; if (samples_this_block == 0 && vgmstream->channels == 1) samples_this_block = vgmstream->num_samples; for (ch = 0; ch < vgmstream->channels; ch++) { - off_t skip = vgmstream->interleave_block_size*(vgmstream->channels-ch) + - vgmstream->interleave_last_block_size*ch;; + off_t skip = + vgmstream->interleave_first_skip*(vgmstream->channels-1-ch) + + vgmstream->interleave_first_block_size*(vgmstream->channels-ch) + + vgmstream->interleave_block_size*ch; + vgmstream->ch[ch].offset += skip; + } + } + else if (has_interleave_last && + vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { + /* adjust values again if inside last interleave */ + samples_per_frame = samples_per_frame_l; + samples_this_block = samples_this_block_l; + if (samples_this_block == 0 && vgmstream->channels == 1) + samples_this_block = vgmstream->num_samples; + + for (ch = 0; ch < vgmstream->channels; ch++) { + off_t skip = + vgmstream->interleave_block_size*(vgmstream->channels-ch) + + vgmstream->interleave_last_block_size*ch; vgmstream->ch[ch].offset += skip; } } @@ -88,6 +141,9 @@ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTR vgmstream->samples_into_block = 0; } - } + return; +fail: + VGM_LOG("layout_interleave: wrong values found\n"); + memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t)); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layered.c b/Frameworks/vgmstream/vgmstream/src/layout/layered.c index af4ebd1ef..24b8f6d66 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layered.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/layered.c @@ -152,6 +152,7 @@ void free_layout_layered(layered_layout_data *data) { } free(data->layers); } + free(data->buffer); free(data); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 2c2e18c8c..1b4c953ed 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" #include "../mixing.h" -#define VGMSTREAM_MAX_SEGMENTS 255 +#define VGMSTREAM_MAX_SEGMENTS 512 #define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192 @@ -203,17 +203,28 @@ fail: } void free_layout_segmented(segmented_layout_data *data) { - int i; + int i, j; if (!data) return; if (data->segments) { for (i = 0; i < data->segment_count; i++) { + int is_repeat = 0; + + /* segments are allowed to be repeated so don't close the same thing twice */ + for (j = 0; j < i; j++) { + if (data->segments[i] == data->segments[j]) + is_repeat = 1; + } + if (is_repeat) + continue; + close_vgmstream(data->segments[i]); } free(data->segments); } + free(data->buffer); free(data); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acb.c b/Frameworks/vgmstream/vgmstream/src/meta/acb.c new file mode 100644 index 000000000..6207099fc --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/acb.c @@ -0,0 +1,585 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "acb_utf.h" + + +/* ACB (Atom Cue sheet Binary) - CRI container of memory audio, often together with a .awb wave bank */ +VGMSTREAM * init_vgmstream_acb(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset; + size_t subfile_size; + utf_context *utf = NULL; + + + /* checks */ + if (!check_extensions(streamFile, "acb")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */ + goto fail; + + /* .acb is a cue sheet that uses @UTF (CRI's generic table format) to store row/columns + * with complex info (cues, sequences, spatial info, etc). it can store a memory .awb + * (our target here), or reference external/streamed .awb (loaded elsewhere) + * we only want .awb with actual waves but may use .acb to get names */ + { + int rows; + const char* name; + uint32_t offset = 0, size = 0; + uint32_t table_offset = 0x00; + + utf = utf_open(streamFile, table_offset, &rows, &name); + if (!utf) goto fail; + + if (rows != 1 || strcmp(name, "Header") != 0) + goto fail; + + //todo acb+cpk is also possible + + if (!utf_query_data(streamFile, utf, 0, "AwbFile", &offset, &size)) + goto fail; + + subfile_offset = table_offset + offset; + subfile_size = size; + + /* column exists but can be empty */ + if (subfile_size == 0) + goto fail; + } + + //;VGM_LOG("ACB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); + + temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, "awb"); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_awb_memory(temp_streamFile, streamFile); + if (!vgmstream) goto fail; + + /* name-loading for this for memory .awb will be called from init_vgmstream_awb_memory */ + + utf_close(utf); + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + utf_close(utf); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +/* ************************************** */ + +typedef struct { + /* keep track of these tables so they can be closed when done */ + utf_context *Header; + utf_context *CueNameTable; + utf_context *CueTable; + utf_context *BlockTable; + utf_context *SequenceTable; + utf_context *TrackTable; + utf_context *TrackEventTable; + utf_context *CommandTable; + utf_context *SynthTable; + utf_context *WaveformTable; + + /* config */ + int is_memory; + int target_waveid; + int has_TrackEventTable; + int has_CommandTable; + + /* to avoid infinite/circular references (AtomViewer crashes otherwise) */ + int synth_depth; + int sequence_depth; + + /* name stuff */ + int16_t cuename_index; + const char * cuename_name; + int awbname_count; + int16_t awbname_list[255]; + char name[1024]; + +} acb_header; + +static int load_utf_subtable(STREAMFILE *acbFile, acb_header* acb, utf_context* *Table, const char* TableName, int* rows) { + uint32_t offset = 0; + + /* already loaded */ + if (*Table != NULL) + return 1; + + if (!utf_query_data(acbFile, acb->Header, 0, TableName, &offset, NULL)) + goto fail; + *Table = utf_open(acbFile, offset, rows, NULL); + if (!*Table) goto fail; + + //;VGM_LOG("ACB: loaded table %s\n", TableName); + return 1; +fail: + return 0; +} + + +static void add_acb_name(STREAMFILE *acbFile, acb_header* acb, int8_t Waveform_Streaming) { + //todo safe string ops + + /* ignore name repeats */ + if (acb->awbname_count) { + int i; + for (i = 0; i < acb->awbname_count; i++) { + if (acb->awbname_list[i] == acb->cuename_index) + return; + } + } + + /* since waveforms can be reused by cues multiple names are a thing */ + if (acb->awbname_count) { + strcat(acb->name, "; "); + strcat(acb->name, acb->cuename_name); + } + else { + strcpy(acb->name, acb->cuename_name); + } + if (Waveform_Streaming == 2 && acb->is_memory) { + strcat(acb->name, " [pre]"); + } + + acb->awbname_list[acb->awbname_count] = acb->cuename_index; + acb->awbname_count++; + if (acb->awbname_count >= 254) + acb->awbname_count = 254; /* ??? */ + + //;VGM_LOG("ACB: found cue for waveid=%i: %s\n", acb->target_waveid, acb->cuename_name); +} + + +static int load_acb_waveform(STREAMFILE *acbFile, acb_header* acb, int16_t Index) { + int16_t Waveform_Id; + int8_t Waveform_Streaming; + + /* read Waveform[Index] */ + if (!load_utf_subtable(acbFile, acb, &acb->WaveformTable, "WaveformTable", NULL)) + goto fail; + if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */ + if (acb->is_memory) { + if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id)) + goto fail; + } else { + if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id)) + goto fail; + } + } + if (!utf_query_s8(acbFile, acb->WaveformTable, Index, "Streaming", &Waveform_Streaming)) + goto fail; + //;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Waveform_Id, Waveform_Streaming); + + /* not found but valid */ + if (Waveform_Id != acb->target_waveid) + return 1; + /* must match our target's (0=memory, 1=streaming, 2=memory (prefetch)+stream) */ + if ((acb->is_memory && Waveform_Streaming == 1) || (!acb->is_memory && Waveform_Streaming == 0)) + return 1; + + /* aaand finally get name (phew) */ + add_acb_name(acbFile, acb, Waveform_Streaming); + + return 1; +fail: + return 0; +} + +/* define here for Synths pointing to Sequences */ +static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index); + +static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb, int16_t Index) { + int i, count; + int8_t Synth_Type; + uint32_t Synth_ReferenceItems_offset; + uint32_t Synth_ReferenceItems_size; + + + /* read Synth[Index] */ + if (!load_utf_subtable(acbFile, acb, &acb->SynthTable, "SynthTable", NULL)) + goto fail; + if (!utf_query_s8(acbFile, acb->SynthTable, Index, "Type", &Synth_Type)) + goto fail; + if (!utf_query_data(acbFile, acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size)) + goto fail; + //;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Synth_Type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size); + + acb->synth_depth++; + + if (acb->synth_depth > 2) { + VGM_LOG("ACB: Synth depth too high\n"); + goto fail; /* max Synth > Synth > Waveform (ex. Yakuza 6) */ + } + + /* Cue.ReferenceType 2 uses Synth.Type, while 3 always sets it to 0 and uses Sequence.Type instead + * Both look the same and probably affect which item in the ReferenceItems list is picked: + * - 0: polyphonic (1 item) + * - 1: sequential (1 to N?) + * - 2: shuffle (1 from N?) + * - 3: random (1 from N?) + * - 4: no repeat + * - 5: switch game variable + * - 6: combo sequential + * - 7: switch selector + * - 8: track transition by selector + * - other: undefined? + * Since we want to find all possible Waveforms that could match our id, we ignore Type and just parse all ReferenceItems. + */ + + count = Synth_ReferenceItems_size / 0x04; + for (i = 0; i < count; i++) { + uint16_t Synth_ReferenceItem_type = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x00, acbFile); + uint16_t Synth_ReferenceItem_index = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x02, acbFile); + //;VGM_LOG("ACB: Synth.ReferenceItem: type=%x, index=%x\n", Synth_ReferenceItem_type, Synth_ReferenceItem_index); + + switch(Synth_ReferenceItem_type) { + case 0x00: /* no reference */ + count = 0; + break; + + case 0x01: /* Waveform (most common) */ + if (!load_acb_waveform(acbFile, acb, Synth_ReferenceItem_index)) + goto fail; + break; + + case 0x02: /* Synth, possibly random (rare, found in Sonic Lost World with ReferenceType 2) */ + if (!load_acb_synth(acbFile, acb, Synth_ReferenceItem_index)) + goto fail; + break; + + case 0x03: /* Sequence of Synths w/ % in Synth.TrackValues (rare, found in Sonic Lost World with ReferenceType 2) */ + if (!load_acb_sequence(acbFile, acb, Synth_ReferenceItem_index)) + goto fail; + break; + + case 0x06: /* this seems to point to Synth but results don't make sense (rare, from Sonic Lost World) */ + default: /* undefined/crashes AtomViewer */ + VGM_LOG("ACB: unknown Synth.ReferenceItem type %x at %x + %x\n", Synth_ReferenceItem_type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size); + count = 0; /* force end without failing */ + break; + } + } + + acb->synth_depth--; + + return 1; +fail: + return 0; +} + +static int load_acb_track_event_command(STREAMFILE *acbFile, acb_header* acb, int16_t Index) { + int16_t Track_EventIndex; + uint32_t Track_Command_offset; + uint32_t Track_Command_size; + + + /* read Track[Index] */ + if (!load_utf_subtable(acbFile, acb, &acb->TrackTable, "TrackTable", NULL)) + goto fail; + if (!utf_query_s16(acbFile, acb->TrackTable, Index, "EventIndex", &Track_EventIndex)) + goto fail; + //;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, Track_EventIndex); + + /* next link varies with version, check by table existence */ + if (acb->has_CommandTable) { /* <=v1.27 */ + /* read Command[EventIndex] */ + if (!load_utf_subtable(acbFile, acb, &acb->CommandTable, "CommandTable", NULL)) + goto fail; + if (!utf_query_data(acbFile, acb->CommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) + goto fail; + //;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size); + } + else if (acb->has_TrackEventTable) { /* >=v1.28 */ + /* read TrackEvent[EventIndex] */ + if (!load_utf_subtable(acbFile, acb, &acb->TrackEventTable, "TrackEventTable", NULL)) + goto fail; + if (!utf_query_data(acbFile, acb->TrackEventTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) + goto fail; + //;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size); + } + else { + VGM_LOG("ACB: unknown command table\n"); + goto fail; + } + + /* read Command (some kind of multiple TLVs, this seems ok) */ + { + uint32_t offset = Track_Command_offset; + uint32_t max_offset = Track_Command_offset + Track_Command_size; + uint16_t tlv_code, tlv_type, tlv_index; + uint8_t tlv_size; + + + while (offset < max_offset) { + tlv_code = read_u16be(offset + 0x00, acbFile); + tlv_size = read_u8 (offset + 0x02, acbFile); + offset += 0x03; + + if (tlv_code == 0x07D0) { + if (tlv_size < 0x04) { + VGM_LOG("ACB: TLV with unknown size\n"); + break; + } + + tlv_type = read_u16be(offset + 0x00, acbFile); + tlv_index = read_u16be(offset + 0x02, acbFile); + //;VGM_LOG("ACB: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index); + + /* probably same as Synth_ReferenceItem_type */ + switch(tlv_type) { + + case 0x02: /* Synth (common) */ + if (!load_acb_synth(acbFile, acb, tlv_index)) + goto fail; + break; + + case 0x03: /* Sequence of Synths (common, ex. Yakuza 6, Yakuza Kiwami 2) */ + if (!load_acb_sequence(acbFile, acb, tlv_index)) + goto fail; + break; + + default: + VGM_LOG("ACB: unknown TLV type %x at %x + %x\n", tlv_type, offset, tlv_size); + max_offset = 0; /* force end without failing */ + break; + } + } + + /* 0x07D1 comes suspiciously often paired with 0x07D0 too */ + + offset += tlv_size; + } + } + + return 1; +fail: + return 0; +} + +static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index) { + int i; + int16_t Sequence_NumTracks; + uint32_t Sequence_TrackIndex_offset; + uint32_t Sequence_TrackIndex_size; + + + /* read Sequence[Index] */ + if (!load_utf_subtable(acbFile, acb, &acb->SequenceTable, "SequenceTable", NULL)) + goto fail; + if (!utf_query_s16(acbFile, acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks)) + goto fail; + if (!utf_query_data(acbFile, acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size)) + goto fail; + //;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Sequence_NumTracks, Sequence_TrackIndex_offset,Sequence_TrackIndex_size); + + acb->sequence_depth++; + + if (acb->sequence_depth > 3) { + VGM_LOG("ACB: Sequence depth too high\n"); + goto fail; /* max Sequence > Sequence > Sequence > Synth > Waveform (ex. Yakuza 6) */ + } + + if (Sequence_NumTracks * 0x02 > Sequence_TrackIndex_size) { /* padding may exist */ + VGM_LOG("ACB: wrong Sequence.TrackIndex size\n"); + goto fail; + } + + /* read Tracks inside Sequence */ + for (i = 0; i < Sequence_NumTracks; i++) { + int16_t Sequence_TrackIndex_index = read_s16be(Sequence_TrackIndex_offset + i*0x02, acbFile); + + if (!load_acb_track_event_command(acbFile, acb, Sequence_TrackIndex_index)) + goto fail; + } + + acb->sequence_depth--; + + return 1; +fail: + return 0; +} + +static int load_acb_block(STREAMFILE *acbFile, acb_header* acb, int16_t Index) { + int i; + int16_t Block_NumTracks; + uint32_t Block_TrackIndex_offset; + uint32_t Block_TrackIndex_size; + + + /* read Block[Index] */ + if (!load_utf_subtable(acbFile, acb, &acb->BlockTable, "BlockTable", NULL)) + goto fail; + if (!utf_query_s16(acbFile, acb->BlockTable, Index, "NumTracks", &Block_NumTracks)) + goto fail; + if (!utf_query_data(acbFile, acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size)) + goto fail; + //;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Block_NumTracks, Block_TrackIndex_offset,Block_TrackIndex_size); + + if (Block_NumTracks * 0x02 > Block_TrackIndex_size) { /* padding may exist */ + VGM_LOG("ACB: wrong Block.TrackIndex size\n"); + goto fail; + } + + /* read Tracks inside Block */ + for (i = 0; i < Block_NumTracks; i++) { + int16_t Block_TrackIndex_index = read_s16be(Block_TrackIndex_offset + i*0x02, acbFile); + + if (!load_acb_track_event_command(acbFile, acb, Block_TrackIndex_index)) + goto fail; + } + + return 1; +fail: + return 0; + +} + +static int load_acb_cue(STREAMFILE *acbFile, acb_header* acb, int16_t Index) { + int8_t Cue_ReferenceType; + int16_t Cue_ReferenceIndex; + + + /* read Cue[Index] */ + if (!load_utf_subtable(acbFile, acb, &acb->CueTable, "CueTable", NULL)) + goto fail; + if (!utf_query_s8 (acbFile, acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType)) + goto fail; + if (!utf_query_s16(acbFile, acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex)) + goto fail; + //;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, Cue_ReferenceType, Cue_ReferenceIndex); + + + /* usually older games use older references but not necessarily */ + switch(Cue_ReferenceType) { + + case 1: /* Cue > Waveform (ex. PES 2015) */ + if (!load_acb_waveform(acbFile, acb, Cue_ReferenceIndex)) + goto fail; + break; + + case 2: /* Cue > Synth > Waveform (ex. Ukiyo no Roushi) */ + if (!load_acb_synth(acbFile, acb, Cue_ReferenceIndex)) + goto fail; + break; + + case 3: /* Cue > Sequence > Track > Command > Synth > Waveform (ex. Valkyrie Profile anatomia, Yakuza Kiwami 2) */ + if (!load_acb_sequence(acbFile, acb, Cue_ReferenceIndex)) + goto fail; + break; + + case 8: /* Cue > Block > Track > Command > Synth > Waveform (ex. Sonic Lost World, rare) */ + if (!load_acb_block(acbFile, acb, Cue_ReferenceIndex)) + goto fail; + break; + + default: + VGM_LOG("ACB: unknown Cue.ReferenceType=%x, Cue.ReferenceIndex=%x\n", Cue_ReferenceType, Cue_ReferenceIndex); + break; /* ignore and continue */ + } + + + return 1; +fail: + return 0; + +} + +static int load_acb_cuename(STREAMFILE *acbFile, acb_header* acb, int16_t Index) { + int16_t CueName_CueIndex; + const char* CueName_CueName; + + + /* read CueName[Index] */ + if (!load_utf_subtable(acbFile, acb, &acb->CueNameTable, "CueNameTable", NULL)) + goto fail; + if (!utf_query_s16(acbFile, acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex)) + goto fail; + if (!utf_query_string(acbFile, acb->CueNameTable, Index, "CueName", &CueName_CueName)) + goto fail; + //;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueName_CueIndex, CueName_CueName); + + + /* save as will be needed if references waveform */ + acb->cuename_index = Index; + acb->cuename_name = CueName_CueName; + + if (!load_acb_cue(acbFile, acb, CueName_CueIndex)) + goto fail; + + return 1; +fail: + return 0; +} + + +void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int is_memory) { + acb_header acb = {0}; + int i, CueName_rows; + + + if (!acbFile || !vgmstream || waveid < 0) + return; + + /* Normally games load a .acb + .awb, and asks the .acb to play a cue by name or index. + * Since we only care for actual waves, to get its name we need to find which cue uses our wave. + * Multiple cues can use the same wave (meaning multiple names), and one cue may use multiple waves. + * There is no easy way to map cue name <> wave name so basically we parse the whole thing. + * + * .acb are created in CRI Atom Craft, where user defines N Cues with CueName each, then link somehow + * to a Waveform (.awb=streamed or memory .acb=internal, data 'material' encoded in some format), + * depending on reference types. Typical links are: + * - CueName > Cue > Waveform (type 1) + * - CueName > Cue > Synth > Waveform (type 2) + * - CueName > Cue > Sequence > Track > Command > Synth > Waveform (type 3, <=v1.27) + * - CueName > Cue > Sequence > Track > Command > Synth > Synth > Waveform (type 3, <=v1.27) + * - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Waveform (type 3, >=v1.28) + * - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Synth > Waveform (type 3, >=v1.28) + * - CueName > Cue > Sequence > Track > TrackEvent > Command > Sequence > (...) > Synth > Waveform (type 3, >=v1.28) + * - CueName > Cue > Block > Track > Command > Synth > Synth > Waveform (type 8) + * - others should be possible but haven't been observed + * Atom Craft may only target certain .acb versions so some links are later removed + * Not all cues to point to though Waveforms, as some are just config events/commands. + * .acb link to .awb by name (loaded manually), though they have a checksum/hash to validate. + */ + + //;VGM_LOG("ACB: find waveid=%i\n", waveid); + + acb.Header = utf_open(acbFile, 0x00, NULL, NULL); + if (!acb.Header) goto fail; + + acb.target_waveid = waveid; + acb.is_memory = is_memory; + acb.has_TrackEventTable = utf_query_data(acbFile, acb.Header, 0, "TrackEventTable", NULL,NULL); + acb.has_CommandTable = utf_query_data(acbFile, acb.Header, 0, "CommandTable", NULL,NULL); + + + /* read all possible cue names and find which waveids are referenced by it */ + if (!load_utf_subtable(acbFile, &acb, &acb.CueNameTable, "CueNameTable", &CueName_rows)) + goto fail; + for (i = 0; i < CueName_rows; i++) { + + if (!load_acb_cuename(acbFile, &acb, i)) + goto fail; + } + + /* meh copy */ + if (acb.awbname_count > 0) { + strncpy(vgmstream->stream_name, acb.name, STREAM_NAME_SIZE); + vgmstream->stream_name[STREAM_NAME_SIZE - 1] = '\0'; + } + +fail: + utf_close(acb.Header); + utf_close(acb.CueNameTable); + utf_close(acb.CueTable); + utf_close(acb.SequenceTable); + utf_close(acb.TrackTable); + utf_close(acb.TrackEventTable); + utf_close(acb.CommandTable); + utf_close(acb.SynthTable); + utf_close(acb.WaveformTable); +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h b/Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h new file mode 100644 index 000000000..e081ca40e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h @@ -0,0 +1,423 @@ +#ifndef _ACB_UTF_H_ +#define _ACB_UTF_H_ + +/* CRI @UTF (Universal Table Format?) is a generic database-like table made of + * rows/columns that contain numbers/strings/ binarydata, which also can be other tables. + * + * A table starts with "@UTF" and defines some values (row/data/string offsets, counts, etc) + * then schema (columns type+name), then rows, string table and binary data. Formats using @UTF + * store and read data by row number + column name. Being a generic table with no fixed schema + * CRI uses it for different purposes (.acf: cues, .cpk: files, .aax: bgm, .usm: video, etc). + * + * (adapted from hcs's code to do multiple querys in the same table) + */ + +// todo divide into some .c file and use for other @UTF parsing + +/* API */ +typedef struct utf_context utf_context; /* opaque struct */ +/*static*/ utf_context* utf_open(STREAMFILE *streamfile, uint32_t table_offset, int* rows, const char* *row_name); +/*static*/ void utf_close(utf_context *utf); + +/*static*/ int utf_query_s8(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, int8_t* value); +/*static*/ int utf_query_s16(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, int16_t* value); +/*static*/ int utf_query_string(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, const char* *value); +/*static*/ int utf_query_data(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, uint32_t *offset, uint32_t *size); + +/* ************************************************* */ +/* INTERNALS */ + +/* possibly 3b+5b from clUTF decompilation */ +#define COLUMN_BITMASK_STORAGE 0xf0 +#define COLUMN_BITMASK_TYPE 0x0f + +#define COLUMN_STORAGE_ZERO 0x10 +#define COLUMN_STORAGE_CONSTANT 0x30 +#define COLUMN_STORAGE_ROW 0x50 +//#define COLUMN_STORAGE_CONSTANT2 0x70 /* from vgmtoolbox */ + +#define COLUMN_TYPE_SINT8 0x00 +#define COLUMN_TYPE_UINT8 0x01 +#define COLUMN_TYPE_SINT16 0x02 +#define COLUMN_TYPE_UINT16 0x03 +#define COLUMN_TYPE_SINT32 0x04 +#define COLUMN_TYPE_UINT32 0x05 +#define COLUMN_TYPE_SINT64 0x06 +//#define COLUMN_TYPE_UINT64 0x07 +#define COLUMN_TYPE_FLOAT 0x08 +//#define COLUMN_TYPE_DOUBLE 0x09 +#define COLUMN_TYPE_STRING 0x0a +#define COLUMN_TYPE_DATA 0x0b + + +typedef struct { + uint32_t offset; + uint32_t size; +} utf_data_t; +typedef struct { + int valid; /* table is valid */ + int found; + int type; /* one of COLUMN_TYPE_* */ + union { + int8_t value_s8; + uint8_t value_u8; + int16_t value_s16; + uint16_t value_u16; + int32_t value_s32; + uint32_t value_u32; + int64_t value_s64; + uint64_t value_u64; + float value_float; + double value_double; + utf_data_t value_data; + const char *value_string; + } value; +} utf_result; + + +typedef struct { + uint8_t flags; + const char *name; + uint32_t offset; +} utf_column; + +struct utf_context { + uint32_t table_offset; + + /* header */ + uint32_t table_size; + uint16_t version; + uint16_t rows_offset; + uint32_t strings_offset; + uint32_t data_offset; + uint32_t name_offset; + uint16_t columns; + uint16_t row_width; + uint32_t rows; + /*const*/ utf_column *schema; + + /* derived */ + uint32_t schema_offset; + uint32_t strings_size; + /*const*/ char *string_table; + const char *table_name; +}; + + +/* @UTF table reading, abridged */ +/*static*/ utf_context* utf_open(STREAMFILE *streamfile, uint32_t table_offset, int* rows, const char* *row_name) { + utf_context* utf = NULL; + + + utf = calloc(1, sizeof(utf_context)); + if (!utf) goto fail; + + utf->table_offset = table_offset; + + /* check header */ + if (read_32bitBE(table_offset + 0x00, streamfile) != 0x40555446) /* "@UTF" */ + goto fail; + + /* load table header */ + utf->table_size = read_32bitBE(table_offset + 0x04, streamfile) + 0x08; + utf->version = read_16bitBE(table_offset + 0x08, streamfile); + utf->rows_offset = read_16bitBE(table_offset + 0x0a, streamfile) + 0x08; + utf->strings_offset = read_32bitBE(table_offset + 0x0c, streamfile) + 0x08; + utf->data_offset = read_32bitBE(table_offset + 0x10, streamfile) + 0x08; + utf->name_offset = read_32bitBE(table_offset + 0x14, streamfile); /* within string table */ + utf->columns = read_16bitBE(table_offset + 0x18, streamfile); + utf->row_width = read_16bitBE(table_offset + 0x1a, streamfile); + utf->rows = read_32bitBE(table_offset + 0x1c, streamfile); + + utf->schema_offset = 0x20; + utf->strings_size = utf->data_offset - utf->strings_offset; + + /* 00: early (32b rows_offset?), 01: +2017 (no apparent differences) */ + if (utf->version != 0x00 && utf->version != 0x01) { + VGM_LOG("@UTF: unknown version\n"); + } + if (utf->table_offset + utf->table_size > get_streamfile_size(streamfile)) + goto fail; + if (utf->rows == 0 || utf->rows_offset > utf->table_size || utf->data_offset > utf->table_size) + goto fail; + if (utf->name_offset > utf->strings_size) + goto fail; + + + /* load string table */ + { + size_t read; + + utf->string_table = calloc(utf->strings_size + 1, sizeof(char)); + if (!utf->string_table) goto fail; + + utf->table_name = utf->string_table + utf->name_offset; + + read = read_streamfile((unsigned char*)utf->string_table, utf->table_offset + utf->strings_offset, utf->strings_size, streamfile); + if (utf->strings_size != read) goto fail; + } + + + /* load column schema */ + { + int i; + uint32_t value_size, column_offset = 0; + uint32_t schema_offset = utf->table_offset + utf->schema_offset; + + + utf->schema = malloc(sizeof(utf_column) * utf->columns); + if (!utf->schema) goto fail; + + for (i = 0; i < utf->columns; i++) { + uint8_t flags = read_8bit(schema_offset + 0x00, streamfile); + uint32_t name_offset = read_32bitBE(schema_offset + 0x01, streamfile); + if (name_offset > utf->strings_size) + goto fail; + + utf->schema[i].flags = flags; + utf->schema[i].name = utf->string_table + name_offset; + schema_offset += 0x01 + 0x04; + + switch (utf->schema[i].flags & COLUMN_BITMASK_TYPE) { + case COLUMN_TYPE_SINT8: + case COLUMN_TYPE_UINT8: + value_size = 0x01; + break; + case COLUMN_TYPE_SINT16: + case COLUMN_TYPE_UINT16: + value_size = 0x02; + break; + case COLUMN_TYPE_SINT32: + case COLUMN_TYPE_UINT32: + case COLUMN_TYPE_FLOAT: + case COLUMN_TYPE_STRING: + value_size = 0x04; + break; + case COLUMN_TYPE_SINT64: + //case COLUMN_TYPE_UINT64: + //case COLUMN_TYPE_DOUBLE: + case COLUMN_TYPE_DATA: + value_size = 0x08; + break; + default: + VGM_LOG("@UTF: unknown column type\n"); + goto fail; + } + + switch (utf->schema[i].flags & COLUMN_BITMASK_STORAGE) { + case COLUMN_STORAGE_ROW: + utf->schema[i].offset = column_offset; + column_offset += value_size; + break; + case COLUMN_STORAGE_CONSTANT: + //case COLUMN_STORAGE_CONSTANT2: + utf->schema[i].offset = schema_offset - (utf->table_offset + utf->schema_offset); /* relative to schema */ + schema_offset += value_size; + break; + case COLUMN_STORAGE_ZERO: + utf->schema[i].offset = 0; /* ? */ + break; + default: + VGM_LOG("@UTF: unknown column storage\n"); + goto fail; + } + } + } + + + /* write info */ + if (rows) *rows = utf->rows; + if (row_name) *row_name = utf->string_table + utf->name_offset; + + return utf; +fail: + utf_close(utf); + return NULL; +} + +/*static*/ void utf_close(utf_context *utf) { + if (!utf) return; + + free(utf->string_table); + free(utf->schema); + free(utf); +} + + +static int utf_query(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, utf_result* result) { + int i; + + + /* fill in the default stuff */ + result->valid = 0; + result->found = 0; + + if (row >= utf->rows || row < 0) + goto fail; + + /* find target column */ + for (i = 0; i < utf->columns; i++) { + utf_column* col = &utf->schema[i]; + uint32_t data_offset; + + if (strcmp(col->name, column) != 0) + continue; + + result->found = 1; + result->type = col->flags & COLUMN_BITMASK_TYPE; + + switch (col->flags & COLUMN_BITMASK_STORAGE) { + case COLUMN_STORAGE_ROW: + data_offset = utf->table_offset + utf->rows_offset + row * utf->row_width + col->offset; + break; + case COLUMN_STORAGE_CONSTANT: + //case COLUMN_STORAGE_CONSTANT2: + data_offset = utf->table_offset + utf->schema_offset + col->offset; + break; + case COLUMN_STORAGE_ZERO: + data_offset = 0; + memset(&result->value, 0, sizeof(result->value)); + break; + default: + goto fail; + } + + /* ignore zero value */ + if (!data_offset) + break; + + /* read row/constant value */ + switch (col->flags & COLUMN_BITMASK_TYPE) { + case COLUMN_TYPE_SINT8: + result->value.value_u8 = read_8bit(data_offset, streamfile); + break; + case COLUMN_TYPE_UINT8: + result->value.value_u8 = (uint8_t)read_8bit(data_offset, streamfile); + break; + case COLUMN_TYPE_SINT16: + result->value.value_s16 = read_16bitBE(data_offset, streamfile); + break; + case COLUMN_TYPE_UINT16: + result->value.value_u16 = (uint16_t)read_16bitBE(data_offset, streamfile); + break; + case COLUMN_TYPE_SINT32: + result->value.value_s32 = read_32bitBE(data_offset, streamfile); + break; + case COLUMN_TYPE_UINT32: + result->value.value_u32 = (uint32_t)read_32bitBE(data_offset, streamfile); + break; + case COLUMN_TYPE_SINT64: + result->value.value_s64 = read_64bitBE(data_offset, streamfile); + break; +#if 0 + case COLUMN_TYPE_UINT64: + result->value.value_u64 = read_64bitBE(data_offset, streamfile); + break; +#endif + case COLUMN_TYPE_FLOAT: { + union { //todo inline function? + float float_value; + uint32_t int_value; + } cnv; + + if (sizeof(float) != 4) { + VGM_LOG("@UTF: can't convert float\n"); + goto fail; + } + + cnv.int_value = (uint32_t)read_32bitBE(data_offset, streamfile); + result->value.value_float = cnv.float_value; + break; + } +#if 0 + case COLUMN_TYPE_DOUBLE: { + union { + double float_value; + uint64_t int_value; + } cnv; + + if (sizeof(double) != 8) { + VGM_LOG("@UTF: can't convert double\n"); + goto fail; + } + + cnv.int_value = (uint64_t)read_64bitBE(data_offset, streamfile); + result->value.value_float = cnv.float_value; + break; + } +#endif + case COLUMN_TYPE_STRING: { + uint32_t name_offset = read_32bitBE(data_offset, streamfile); + if (name_offset > utf->strings_size) + goto fail; + result->value.value_string = utf->string_table + name_offset; + break; + } + + case COLUMN_TYPE_DATA: + result->value.value_data.offset = read_32bitBE(data_offset + 0x00, streamfile); + result->value.value_data.size = read_32bitBE(data_offset + 0x04, streamfile); + break; + + default: + goto fail; + } + + break; /* column found and read */ + } + + result->valid = 1; + return 1; +fail: + return 0; +} + +//////////////////////////////////////////////////////////// + +static int utf_query_value(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, void* value, int type) { + utf_result result = {0}; + + utf_query(streamfile, utf, row, column, &result); + if (!result.valid || !result.found || result.type != type) + return 0; + + switch(result.type) { + case COLUMN_TYPE_SINT8: (*(int8_t*)value) = result.value.value_s8; break; + case COLUMN_TYPE_UINT8: (*(uint8_t*)value) = result.value.value_u8; break; + case COLUMN_TYPE_SINT16: (*(int16_t*)value) = result.value.value_s16; break; + case COLUMN_TYPE_UINT16: (*(uint16_t*)value) = result.value.value_u16; break; + case COLUMN_TYPE_SINT32: (*(int32_t*)value) = result.value.value_s32; break; + case COLUMN_TYPE_UINT32: (*(uint32_t*)value) = result.value.value_u32; break; + case COLUMN_TYPE_SINT64: (*(int64_t*)value) = result.value.value_s64; break; + //case COLUMN_TYPE_UINT64: (*(uint64_t*)value) = result.value.value_u64; break; + case COLUMN_TYPE_STRING: (*(const char**)value) = result.value.value_string; break; + default: + return 0; + } + + return 1; +} + +/*static*/ int utf_query_s8(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, int8_t* value) { + return utf_query_value(streamfile, utf, row, column, (void*)value, COLUMN_TYPE_SINT8); +} +/*static*/ int utf_query_s16(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, int16_t* value) { + return utf_query_value(streamfile, utf, row, column, (void*)value, COLUMN_TYPE_SINT16); +} +/*static*/ int utf_query_string(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, const char* *value) { + return utf_query_value(streamfile, utf, row, column, (void*)value, COLUMN_TYPE_STRING); +} + +/*static*/ int utf_query_data(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, uint32_t *offset, uint32_t *size) { + utf_result result = {0}; + + utf_query(streamfile, utf, row, column, &result); + if (!result.valid || !result.found || result.type != COLUMN_TYPE_DATA) + return 0; + + if (offset) *offset = utf->table_offset + utf->data_offset + result.value.value_data.offset; + if (size) *size = result.value.value_data.size; + return 1; +} + +#endif /* _ACB_UTF_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c b/Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c index e55ee8d2d..c6e83a51c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c @@ -11,7 +11,8 @@ VGMSTREAM * init_vgmstream_adpcm_capcom(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile,"adpcm")) + /* .mca: Monster Hunter Generations Ultimate / XX */ + if (!check_extensions(streamFile,"adpcm,mca")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x02000000) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 290ef15c1..5252b641b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -256,14 +256,44 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star /* try to find key in external file first */ { - uint8_t keybuf[6]; + uint8_t keybuf[0x20+1] = {0}; /* +1 extra null for keystrings */ + size_t key_size; - if (read_key_file(keybuf, 6, streamFile) == 6) { - *xor_start = get_16bitBE(keybuf+0); - *xor_mult = get_16bitBE(keybuf+2); - *xor_add = get_16bitBE(keybuf+4); - return 1; + /* handle type8 keystrings, key9 keycodes and derived keys too */ + key_size = read_key_file(keybuf,0x20, streamFile); + + if (key_size > 0) { + int i, is_ascii = 0; + + /* keystrings should be ASCII, also needed to tell apart 0x06 strings from derived keys */ + if (type == 8) { + is_ascii = 1; + for (i = 0; i < key_size; i++) { + if (keybuf[i] < 0x20 || keybuf[i] > 0x7f) { + is_ascii = 0; + break; + } + } + } + + if (key_size == 0x06 && !is_ascii) { + *xor_start = get_16bitBE(keybuf + 0x00); + *xor_mult = get_16bitBE(keybuf + 0x02); + *xor_add = get_16bitBE(keybuf + 0x04); + return 1; + } + else if (type == 8 && is_ascii) { + const char * keystring = (const char *)keybuf; + derive_adx_key8(keystring, xor_start, xor_mult, xor_add); + return 1; + } + else if (type == 9 && key_size == 0x08) { + uint64_t keycode = (uint64_t)get_64bitBE(keybuf); + derive_adx_key9(keycode, xor_start, xor_mult, xor_add); + return 1; + } } + /* no key set or unknown format, try list */ } /* setup totals */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index bee23ec61..72f23b202 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -10,187 +10,180 @@ typedef struct { /** * List of known keys, cracked from the sound files. - * Keystrings (type 8) and keycodes (type 9) from VGAudio / game's executables / 2ch.net. + * Keystrings (type 8) and keycodes (type 9) from executables / VGAudio / game's executables / 2ch.net. * Multiple keys may work for a game due to how they are derived. * start/mult/add are optional (0,0,0) if key8/9 are provided, but take priority if given. */ static const adxkey_info adxkey8_list[] = { - /* Clover Studio (GOD HAND, Okami) */ + /* GOD HAND (PS2), Okami (PS2) [Clover Studio] */ {0x49e1,0x4a57,0x553d, "karaage",0}, - /* Grasshopper Manufacture 0 (Blood+) */ - {0x5f5d,0x58bd,0x55ed, NULL,0}, // estimated (keystring not in ELF?) + /* Blood+ (PS2) [Grasshopper Manufacture] */ + {0x5f5d,0x58bd,0x55ed, NULL,0}, // keystring not in ELF? - /* Grasshopper Manufacture 1 (Killer7) */ + /* Killer7 (PS2) [Grasshopper Manufacture] */ {0x50fb,0x5803,0x5701, "GHM",0}, - /* Grasshopper Manufacture 2 (Samurai Champloo) */ + /* Samurai Champloo (PS2) [Grasshopper Manufacture] */ {0x4f3f,0x472f,0x562f, "GHMSC",0}, - /* Moss Ltd (Raiden III) */ + /* Raiden III (PS2) [Moss] */ {0x66f5,0x58bd,0x4459, "(C)2005 MOSS LTD. BMW Z4",0}, - /* Sonic Team 0 (Phantasy Star Universe) */ + /* Phantasy Star Universe (PC), Phantasy Star Universe: Ambition of the Illuminus (PS2) [Sonic Team] */ {0x5deb,0x5f27,0x673f, "3x5k62bg9ptbwy",0}, - /* G.rev 0 (Senko no Ronde) */ + /* Senko no Ronde [G.rev] */ {0x46d3,0x5ced,0x474d, "ranatus",0}, - /* Sonic Team 1 (NiGHTS: Journey of Dreams) */ + /* NiGHTS: Journey of Dreams (Wii) [Sonic Team] */ {0x440b,0x6539,0x5723, "sakakit4649",0}, /* unknown source */ {0x586d,0x5d65,0x63eb, NULL,0}, // from guessadx (unique?) - /* Navel (Shuffle! On the Stage (PS2)) */ + /* Shuffle! On the Stage (PS2) [Navel] */ {0x4969,0x5deb,0x467f, "SHUF",0}, - /* Success (Aoishiro (PS2)) */ + /* Aoishiro (PS2) [Success] */ {0x4d65,0x5eb7,0x5dfd, "wakasugi",0}, - /* Sonic Team 2 (Sonic and the Black Knight) */ + /* Sonic and the Black Knight (Wii) [Sonic Team] */ {0x55b7,0x6191,0x5a77, "morio",0}, - /* Enterbrain (Amagami) */ + /* Amagami (PS2) [Enterbrain] */ {0x5a17,0x509f,0x5bfd, "mituba",0}, /* also AHX key */ - /* Yamasa (Yamasa Digi Portable: Matsuri no Tatsujin) */ - {0x4c01,0x549d,0x676f, NULL,0}, // confirmed unique with guessadx + /* Yamasa Digi Portable: Matsuri no Tatsujin (PSP) [Yamasa] */ + {0x4c01,0x549d,0x676f, "7fa0xB9tw3",0}, - /* Kadokawa Shoten (Fragments Blue) */ - {0x5803,0x4555,0x47bf, NULL,0}, // confirmed unique with guessadx + /* Fragments Blue (PS2) [Kadokawa Shoten] */ + {0x5803,0x4555,0x47bf, "PIETA",0}, - /* Namco (Soulcalibur IV) */ - {0x59ed,0x4679,0x46c9, NULL,0}, // confirmed unique with guessadx + /* Soulcalibur IV (PS3) [Namco] */ + {0x59ed,0x4679,0x46c9, "SC4Test",0}, - /* G.rev 1 (Senko no Ronde DUO) */ + /* Senko no Ronde DUO (X360) [G.rev] */ {0x6157,0x6809,0x4045, NULL,0}, // from guessadx - /* ASCII Media Works 0 (Nogizaka Haruka no Himitsu: Cosplay Hajimemashita) */ - {0x45af,0x5f27,0x52b1, NULL,0}, // 2nd from guessadx, other was {0x45ad,0x5f27,0x10fd} + /* Nogizaka Haruka no Himitsu: Cosplay Hajimemashita (PS2) [Vridge] */ + {0x45af,0x5f27,0x52b1, "SKFHSIA",0}, - /* D3 Publisher 0 (Little Anchor) */ + /* Little Anchor (PS2) [D3 Publisher] */ {0x5f65,0x5b3d,0x5f65, NULL,0}, // confirmed unique with guessadx - /* Marvelous 0 (Hanayoi Romanesque: Ai to Kanashimi) */ + /* Hanayoi Romanesque: Ai to Kanashimi (PS2) [Marvelous] */ {0x5563,0x5047,0x43ed, NULL,0}, // 2nd from guessadx, other was {0x5562,0x5047,0x1433} - /* Capcom (Mobile Suit Gundam: Gundam vs. Gundam NEXT PLUS) */ + /* Mobile Suit Gundam: Gundam vs. Gundam NEXT PLUS (PSP) [Capcom] */ {0x4f7b,0x4fdb,0x5cbf, "CS-GGNX+",0}, - /* Developer: Bridge NetShop - * Publisher: Kadokawa Shoten (Shoukan Shoujo: Elemental Girl Calling) */ - {0x4f7b,0x5071,0x4c61, NULL,0}, // confirmed unique with guessadx + /* Shoukan Shoujo: Elemental Girl Calling (PS2) [Bridge NetShop] */ + {0x4f7b,0x5071,0x4c61, "ELEMENGAL",0}, - /* Developer: Net Corporation - * Publisher: Tecmo (Rakushou! Pachi-Slot Sengen 6: Rio 2 Cruising Vanadis) */ + /* Rakushou! Pachi-Slot Sengen 6: Rio 2 Cruising Vanadis (PS2) [Net Corporation] */ {0x53e9,0x586d,0x4eaf, NULL,0}, // confirmed unique with guessadx - /* Developer: Aquaplus - * Tears to Tiara Gaiden Avalon no Kagi (PS3) */ + /* Tears to Tiara Gaiden Avalon no Nazo (PS3) [Aquaplus] */ {0x47e1,0x60e9,0x51c1, NULL,0}, // confirmed unique with guessadx - /* Developer: Broccoli - * Neon Genesis Evangelion: Koutetsu no Girlfriend 2nd (PS2) */ - {0x481d,0x4f25,0x5243, NULL,0}, // confirmed unique with guessadx + /* Neon Genesis Evangelion: Koutetsu no Girlfriend 2nd (PS2) [Broccoli] */ + {0x481d,0x4f25,0x5243, "eva2",0}, - /* Developer: Marvelous - * Futakoi Alternative (PS2) */ - {0x413b,0x543b,0x57d1, NULL,0}, // confirmed unique with guessadx + /* Futakoi Alternative (PS2) [Marvelous] */ + {0x413b,0x543b,0x57d1, "LOVLOV",0}, - /* Developer: Marvelous - * Gakuen Utopia - Manabi Straight! KiraKira Happy Festa! (PS2) */ + /* Gakuen Utopia: Manabi Straight! KiraKira Happy Festa! (PS2) [Marvelous] */ {0x440b,0x4327,0x564b, "MANABIST",0}, - /* Developer: Datam Polystar - * Soshite Kono Uchuu ni Kirameku Kimi no Shi XXX (PS2) */ - {0x5f5d,0x552b,0x5507, NULL,0}, // confirmed unique with guessadx + /* Soshite Kono Uchuu ni Kirameku Kimi no Shi XXX (PS2) [Datam Polystar] */ + {0x5f5d,0x552b,0x5507, "DATAM-KK2",0}, - /* Developer: Sega - * Sakura Taisen: Atsuki Chishio Ni (PS2) */ + /* Sakura Taisen: Atsuki Chishio Ni (PS2) [Sega] */ {0x645d,0x6011,0x5c29, NULL,0}, // confirmed unique with guessadx - /* Developer: Sega - * Sakura Taisen 3 ~Paris wa Moeteiru ka~ (PS2) */ + /* Sakura Taisen 3 ~Paris wa Moeteiru ka~ (PS2) [Sega] */ {0x62ad,0x4b13,0x5957, NULL,0}, // confirmed unique with guessadx - /* Developer: Jinx - * Sotsugyou 2nd Generation (PS2) */ + /* Sotsugyou 2nd Generation (PS2) [Jinx] */ {0x6305,0x509f,0x4c01, NULL,0}, // First guess from guessadx, other was {0x6307,0x509f,0x2ac5} - /* La Corda d'Oro (2005)(-)(Koei)[PSP] */ - {0x55b7,0x67e5,0x5387, NULL,0}, // confirmed unique with guessadx + /* La Corda d'Oro (PSP) [Koei] */ + {0x55b7,0x67e5,0x5387, NULL,0}, // keystring not in ELF? - /* Nanatsuiro * Drops Pure!! (2007)(Media Works)[PS2] */ + /* Nanatsuiro * Drops Pure!! (PS2) [Media Works] */ {0x6731,0x645d,0x566b, NULL,0}, // confirmed unique with guessadx - /* Shakugan no Shana (2006)(Vridge)(Media Works)[PS2] */ + /* Shakugan no Shana (PS2) [Vridge] */ {0x5fc5,0x63d9,0x599f, "FUZETSU",0}, - /* Uragiri wa Boku no Namae o Shitteiru (2010)(Kadokawa Shoten)[PS2] */ + /* Uragiri wa Boku no Namae o Shitteiru (PS2) [Kadokawa Shoten] */ {0x4c73,0x4d8d,0x5827, NULL,0}, // confirmed unique with guessadx - /* StormLover Kai!! (2012)(D3 Publisher)[PSP] */ - {0x5a11,0x67e5,0x6751, NULL,0}, // confirmed unique with guessadx + /* StormLover!! (PSP), StormLover Kai!! (PSP) [Vridge] */ + {0x5a11,0x67e5,0x6751, "HEXDPFMDKPQW",0}, /* unknown AHX key */ - /* Sora no Otoshimono - DokiDoki Summer Vacation (2010)(Kadokawa Shoten)[PSP] */ - {0x5e75,0x4a89,0x4c61, NULL,0}, // confirmed unique with guessadx + /* Sora no Otoshimono: DokiDoki Summer Vacation (PSP) [Kadokawa Shoten] */ + {0x5e75,0x4a89,0x4c61, "funen-gomi",0}, - /* Boku wa Koukuu Kanseikan - Airport Hero Naha (2006)(Sonic Powered)(Electronic Arts)[PSP] */ - {0x64ab,0x5297,0x632f, NULL,0}, // confirmed unique with guessadx + /* Boku wa Koukuu Kanseikan: Airport Hero Naha (PSP) [Sonic Powered] */ + {0x64ab,0x5297,0x632f, "sonic",0}, - /* Lucky Star - Net Idol Meister (2009)(Kadokawa Shoten)[PSP] */ - {0x4d82,0x5243,0x0685, NULL,0}, // confirmed unique with guessadx + /* Lucky Star: Net Idol Meister (PSP) [Vridge, Kadokawa Shoten] */ + {0x4d81,0x5243,0x58c7, "JJOLIFJLE",0}, /* unknown AHX key */ - /* Ishin Renka: Ryouma Gaiden (2010-11-25)(-)(D3 Publisher)[PSP] */ - {0x54d1,0x526d,0x5e8b, NULL,0}, // ? + /* Ishin Renka: Ryouma Gaiden (PSP) [Vridge] */ + {0x54d1,0x526d,0x5e8b, "LQAFJOIEJ",0}, /* unknown AHX key */ - /* Lucky Star - Ryouou Gakuen Outousai Portable (2010-12-22)(-)(Kadokawa Shoten)[PSP] */ - {0x4d06,0x663b,0x7d09, NULL,0}, // ? + /* Lucky Star: Ryouou Gakuen Outousai Portable (PSP) [Vridge] */ + {0x4d05,0x663b,0x6343, "IUNOIRU",0}, /* unknown AHX key */ - /* Marriage Royale - Prism Story (2010-04-28)(-)(ASCII Media Works)[PSP] */ - {0x40a9,0x46b1,0x62ad, NULL,0}, // ? + /* Marriage Royale: Prism Story (PSP) [Vridge] */ + {0x40a9,0x46b1,0x62ad, "ROYMAR",0}, /* unknown AHX key */ - /* Nogizaka Haruka no Himitsu - Doujinshi Hajime Mashita (2010-10-28)(-)(ASCII Media Works)[PSP] */ - {0x4601,0x671f,0x0455, NULL,0}, // ? + /* Nogizaka Haruka no Himitsu: Doujinshi Hajimemashita (PSP) [Vridge] */ + {0x4609,0x671f,0x4b65, "CLKMEOUHFLIE",0}, /* unknown AHX key */ - /* Slotter Mania P - Mach Go Go Go III (2011-01-06)(-)(Dorart)[PSP] */ - {0x41ef,0x463d,0x5507, NULL,0}, // ? + /* Slotter Mania P: Mach Go Go Go III (PSP) [Dorart] */ + {0x41ef,0x463d,0x5507, "SGGK",0}, - /* Nichijou - Uchuujin (2011-07-28)(-)(Kadokawa Shoten)[PSP] */ - {0x4369,0x486d,0x5461, NULL,0}, // ? + /* Nichijou: Uchuujin (PSP) [Vridge] */ + {0x4369,0x486d,0x5461, "LJLOUHIU787",0}, /* unknown AHX key */ - /* R-15 Portable (2011-10-27)(-)(Kadokawa Shoten)[PSP] */ - {0x6809,0x5fd5,0x5bb1, NULL,0}, // ? + /* R-15 Portable (PSP) [Kadokawa Shoten] */ + {0x6809,0x5fd5,0x5bb1, "R-15(Heart)Love",0}, - /* Suzumiya Haruhi-chan no Mahjong (2011-07-07)(-)(Kadokawa Shoten)[PSP] */ - {0x5c33,0x4133,0x4ce7, NULL,0}, // ? + /* Suzumiya Haruhi-chan no Mahjong (PSP) [Kadokawa Shoten] */ + {0x5c33,0x4133,0x4ce7, "bi88a#fas",0}, - /* Storm Lover Natsu Koi!! (2011-08-04)(Vridge)(D3 Publisher)[PSP] */ - {0x4133,0x5a01,0x5723, NULL,0}, // ? + /* StormLover Natsu Koi!! (PSP) [Vridge] */ + {0x4133,0x5a01,0x5723, "LIKDFJUIDJOQ",0}, /* unknown AHX key */ - /* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */ + /* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere (PS2) [Kadokawa Shoten] */ {0x55d9,0x46d3,0x5b01, "SONMYOJI",0}, - /* Girls Bravo: Romance 15's [PS2] */ + /* Girls Bravo: Romance 15's (PS2) [Kadokawa Shoten] */ {0x658f,0x4a89,0x5213, "GBRAVO",0}, - /* Kashimashi! Girl Meets Girl - Hajimete no Natsu Monogatari (PS2) */ + /* Kashimashi! Girl Meets Girl: Hajimete no Natsu Monogatari (PS2) [Vridge] */ {0x6109,0x5135,0x673f, "KASHIM",0}, - /* Bakumatsu Renka - Karyuu Kenshiden (PS2) */ + /* Bakumatsu Renka: Karyuu Kenshiden (PS2) [Vridge] */ {0x4919,0x612d,0x4919, "RENRENKA22",0}, - /* Tensei Hakkenshi - Fuumaroku (PS2) */ + /* Tensei Hakkenshi: Fuumaroku (PS2) [Vridge] */ {0x5761,0x6283,0x4531, "HAKKEN",0}, - /* Lucky Star - Ryouou Gakuen Outousai (PS2) */ + /* Lucky Star: Ryouou Gakuen Outousai (PS2) [Vridge] */ {0x481D,0x44F9,0x4E35, "LSTARPS2",0}, - /* Bakumatsu Renka: Shinsengumi (PS2) */ + /* Bakumatsu Renka: Shinsengumi (PS2) [Vridge] */ {0x5381,0x5701,0x665B, "SHINN",0}, + /* Gintama Gin-san to Issho! Boku no Kabukichou Nikki (PS2) [Bandai Namco?] */ + {0x67CD,0x5CA7,0x655F, "gt25809",0}, + }; static const adxkey_info adxkey9_list[] = { @@ -213,7 +206,7 @@ static const adxkey_info adxkey9_list[] = { /* Fallen Princess (iOS/Android) */ {0x5e4b,0x190d,0x76bb, NULL,145552191146490718}, // 02051AF25990FB5E - /* Yuuki Yuuna wa Yuusha de aru - Hanayui no Kirameki / Yuyuyui (iOS/Android) */ + /* Yuuki Yuuna wa Yuusha de aru: Hanayui no Kirameki / Yuyuyui (iOS/Android) */ {0x3f10,0x3651,0x6d31, NULL,4867249871962584729}, // 438BF1F883653699 /* Super Robot Wars X-Omega (iOS/Android) voices */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c index c51a408d7..d9b2a46a0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c @@ -73,8 +73,9 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { * .bgm: Super Street Fighter II Turbo (3DO) * .acm: Crusader - No Remorse (SAT) * .adp: Sonic Jam (SAT) - * .ai: Dragon Force (SAT) */ - if (check_extensions(streamFile, "aif,laif")) { + * .ai: Dragon Force (SAT) + * (extensionless: Doom (3DO) */ + if (check_extensions(streamFile, "aif,laif,")) { is_aifc_ext = 1; is_aiff_ext = 1; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awb.c b/Frameworks/vgmstream/vgmstream/src/meta/awb.c index a9e546a62..9d21b09f6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awb.c @@ -1,19 +1,27 @@ #include "meta.h" #include "../coding/coding.h" -typedef enum { ADX, HCA, AT9, VAG } awb_type; +typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type; -/* CRI AFS2, container of streaming ADX or HCA, often (but not always) together with a .acb CUE */ +static void load_awb_name(STREAMFILE *streamFile, STREAMFILE *acbFile, VGMSTREAM *vgmstream, int waveid); + +/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */ VGMSTREAM * init_vgmstream_awb(STREAMFILE *streamFile) { + return init_vgmstream_awb_memory(streamFile, NULL); +} + +VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE *streamFile, STREAMFILE *acbFile) { VGMSTREAM *vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; off_t offset, subfile_offset, subfile_next; size_t subfile_size; int total_subsongs, target_subsong = streamFile->stream_index; //uint32_t flags; + uint8_t offset_size; uint16_t alignment, subkey; awb_type type; char *extension = NULL; + int waveid; /* checks @@ -21,62 +29,94 @@ VGMSTREAM * init_vgmstream_awb(STREAMFILE *streamFile) { * .afs2: sometimes [Okami HD (PS4)] */ if (!check_extensions(streamFile, "awb,afs2")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41465332) /* "AFS2" */ + if (read_u32be(0x00,streamFile) != 0x41465332) /* "AFS2" */ goto fail; //flags = read_32bitLE(0x08,streamFile); - total_subsongs = read_32bitLE(0x08,streamFile); - alignment = (uint16_t)read_16bitLE(0x0c,streamFile); - subkey = (uint16_t)read_16bitLE(0x0e,streamFile); + /* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */ + offset_size = read_u8(0x05,streamFile); + /* 0x06(2): always 0x0002? */ + total_subsongs = read_s32le(0x08,streamFile); + alignment = read_u16le(0x0c,streamFile); + subkey = read_u16le(0x0e,streamFile); if (target_subsong == 0) target_subsong = 1; if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; offset = 0x10; - /* id(?) table: skip */ - offset += total_subsongs * 0x02; + /* id(?) table: read target */ + { + off_t waveid_offset = offset + (target_subsong-1) * 0x02; - /* offset table: find target - * offset are absolute but sometimes misaligned (specially first that just points to offset table end) */ + waveid = read_u16le(waveid_offset,streamFile); + + offset += total_subsongs * 0x02; + } + + /* offset table: find target */ { off_t file_size = get_streamfile_size(streamFile); - offset += (target_subsong-1) * 0x04; - /* last offset is always file end, so table entries = total_subsongs+1 */ - subfile_offset = read_32bitLE(offset+0x00,streamFile); - subfile_next = read_32bitLE(offset+0x04,streamFile); + /* last sub-offset is always file end, so table entries = total_subsongs+1 */ + offset += (target_subsong-1) * offset_size; + switch(offset_size) { + case 0x04: /* common */ + subfile_offset = read_u32le(offset+0x00,streamFile); + subfile_next = read_u32le(offset+0x04,streamFile); + break; + case 0x02: /* mostly sfx in .acb */ + subfile_offset = read_u16le(offset+0x00,streamFile); + subfile_next = read_u16le(offset+0x02,streamFile); + break; + default: + VGM_LOG("AWB: unknown offset size\n"); + goto fail; + } + + /* offset are absolute but sometimes misaligned (specially first that just points to offset table end) */ subfile_offset += (subfile_offset % alignment) ? alignment - (subfile_offset % alignment) : 0; subfile_next += (subfile_next % alignment) && subfile_next < file_size ? alignment - (subfile_next % alignment) : 0; subfile_size = subfile_next - subfile_offset; - - //todo: flags & 0x200 are uint16 offsets? } - //;VGM_LOG("TXTH: subfile offset=%lx + %x\n", subfile_offset, subfile_size); + //;VGM_LOG("AWB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); /* autodetect as there isn't anything, plus can mix types * (waveid<>codec info is usually in the companion .acb) */ - if ((uint16_t)read_16bitBE(subfile_offset, streamFile) == 0x8000) { /* ADX id */ + if (read_u16be(subfile_offset, streamFile) == 0x8000) { /* ADX id (type 0) */ type = ADX; extension = "adx"; } - else if (((uint32_t)read_32bitBE(subfile_offset,streamFile) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" */ + else if ((read_u32be(subfile_offset,streamFile) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" (type 2=HCA, 6=HCA-MX) */ type = HCA; extension = "hca"; } - else if (read_32bitBE(subfile_offset,streamFile) == 0x52494646) { /* "RIFF" */ - type = AT9; - extension = "at9"; - } - else if (read_32bitBE(subfile_offset,streamFile) == 0x56414770) { /* "VAGp" */ + else if (read_u32be(subfile_offset,streamFile) == 0x56414770) { /* "VAGp" (type 7=VAG, 10=HEVAG) */ type = VAG; extension = "vag"; } + else if (read_u32be(subfile_offset,streamFile) == 0x52494646) { /* "RIFF" (type 8=ATRAC3, 11=ATRAC9) */ + type = RIFF; + extension = "wav"; + } + else if (read_u32be(subfile_offset,streamFile) == 0x43574156) { /* "CWAV" (type 9) */ + type = CWAV; + extension = "bcwav"; + } + else if (read_u32be(subfile_offset + 0x08,streamFile) >= 8000 && + read_u32be(subfile_offset + 0x08,streamFile) <= 48000 && + read_u16be(subfile_offset + 0x0e,streamFile) == 0 && + read_u32be(subfile_offset + 0x18,streamFile) == 2 && + read_u32be(subfile_offset + 0x50,streamFile) == 0) { /* probably should call some check function (type 13) */ + type = DSP; + extension = "dsp"; + } else { + VGM_LOG("AWB: unknown codec\n"); goto fail; } @@ -93,22 +133,31 @@ VGMSTREAM * init_vgmstream_awb(STREAMFILE *streamFile) { vgmstream = init_vgmstream_adx(temp_streamFile); if (!vgmstream) goto fail; break; - case AT9: /* Ukiyo no Roushi (Vita) */ + case VAG: /* Ukiyo no Roushi (Vita) */ + vgmstream = init_vgmstream_vag(temp_streamFile); + if (!vgmstream) goto fail; + break; + case RIFF: /* Ukiyo no Roushi (Vita) */ vgmstream = init_vgmstream_riff(temp_streamFile); if (!vgmstream) goto fail; break; - case VAG: /* Ukiyo no Roushi (Vita) */ - vgmstream = init_vgmstream_vag(temp_streamFile); + case CWAV: /* Sonic: Lost World (3DS) */ + vgmstream = init_vgmstream_rwsd(temp_streamFile); + if (!vgmstream) goto fail; + break; + case DSP: /* Sonic: Lost World (WiiU) */ + vgmstream = init_vgmstream_ngc_dsp_std(temp_streamFile); if (!vgmstream) goto fail; break; default: goto fail; } - //todo: could try to get name in .acb for this waveid - vgmstream->num_streams = total_subsongs; + /* try to load cue names */ + load_awb_name(streamFile, acbFile, vgmstream, waveid); + close_streamfile(temp_streamFile); return vgmstream; @@ -117,3 +166,55 @@ fail: close_vgmstream(vgmstream); return NULL; } + + +static void load_awb_name(STREAMFILE *streamFile, STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid) { + int is_memory = (acbFile != NULL); + + /* .acb is passed when loading memory .awb inside .acb */ + if (!is_memory) { + /* load companion .acb using known pairs */ //todo improve, see xsb code + char filename[PATH_LIMIT]; + int len_name, len_cmp; + + + /* try (name).awb + (name).awb */ + acbFile = open_streamfile_by_ext(streamFile, "acb"); + + /* try (name)_streamfiles.awb + (name).acb */ + if (!acbFile) { + char *cmp = "_streamfiles"; + get_streamfile_basename(streamFile, filename, sizeof(filename)); + len_name = strlen(filename); + len_cmp = strlen(cmp); + + if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { + filename[len_name - len_cmp] = '\0'; + strcat(filename, ".acb"); + acbFile = open_streamfile_by_filename(streamFile, filename); + } + } + + /* try (name)_STR.awb + (name).acb */ + if (!acbFile) { + char *cmp = "_STR"; + get_streamfile_basename(streamFile, filename, sizeof(filename)); + len_name = strlen(filename); + len_cmp = strlen(cmp); + + if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { + filename[len_name - len_cmp] = '\0'; + strcat(filename, ".acb"); + acbFile = open_streamfile_by_filename(streamFile, filename); + } + } + + /* probably loaded */ + load_acb_wave_name(acbFile, vgmstream, waveid, is_memory); + + close_streamfile(acbFile); + } + else { + load_acb_wave_name(acbFile, vgmstream, waveid, is_memory); + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c index 98eaf6609..a73cdbd47 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c @@ -1,8 +1,6 @@ #include "meta.h" #include "../coding/coding.h" - - -static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels); +#include "bgw_streamfile.h" /* BGW - from Final Fantasy XI (PC) music files */ @@ -27,13 +25,13 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { codec = read_32bitLE(0x0c,streamFile); file_size = read_32bitLE(0x10,streamFile); - /*file_id = read_32bitLE(0x14,streamFile);*/ + /* file_id = read_32bitLE(0x14,streamFile); */ block_size = read_32bitLE(0x18,streamFile); loop_start = read_32bitLE(0x1c,streamFile); sample_rate = (read_32bitLE(0x20,streamFile) + read_32bitLE(0x24,streamFile)) & 0x7FFFFFFF; /* bizarrely obfuscated sample rate */ start_offset = read_32bitLE(0x28,streamFile); - /*0x2c: unk (vol?) */ - /*0x2d: unk (0x10?) */ + /* 0x2c: unk (vol?) */ + /* 0x2d: unk (0x10?) */ channel_count = read_8bit(0x2e,streamFile); block_align = (uint8_t)read_8bit(0x2f,streamFile); @@ -65,31 +63,26 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 3: { /* ATRAC3 (encrypted) */ - uint8_t buf[0x100]; - int bytes, joint_stereo, skip_samples; size_t data_size = file_size - start_offset; + int encoder_delay, block_align; - vgmstream->num_samples = block_size; /* atrac3_bytes_to_samples gives the same value */ - if (loop_flag) { - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - block_align = 0xC0 * vgmstream->channels; /* 0x00 in header */ - joint_stereo = 0; - skip_samples = 0; - - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); - if (bytes <= 0) goto fail; + encoder_delay = 1024*2 + 69*2; /* observed value, all files start at +2200 (PS-ADPCM also starts around 50-150 samples in) */ + block_align = 0xC0 * vgmstream->channels; /* 0x00 in header */ + vgmstream->num_samples = block_size - encoder_delay; /* atrac3_bytes_to_samples gives block_size */ temp_streamFile = setup_bgw_atrac3_streamfile(streamFile, start_offset,data_size, 0xC0,channel_count); if (!temp_streamFile) goto fail; - vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0,data_size); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(temp_streamFile, 0x00,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + if (loop_flag) { + vgmstream->loop_start_sample = loop_start - encoder_delay; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + close_streamfile(temp_streamFile); break; } @@ -132,13 +125,13 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { file_size = read_32bitLE(0x08,streamFile); codec = read_32bitLE(0x0c,streamFile); - /*file_id = read_32bitLE(0x10,streamFile);*/ + /* file_id = read_32bitLE(0x10,streamFile);*/ block_size = read_32bitLE(0x14,streamFile); loop_start = read_32bitLE(0x18,streamFile); sample_rate = (read_32bitLE(0x1c,streamFile) + read_32bitLE(0x20,streamFile)) & 0x7FFFFFFF; /* bizarrely obfuscated sample rate */ start_offset = read_32bitLE(0x24,streamFile); - /*0x2c: unk (0x00?) */ - /*0x2d: unk (0x00/01?) */ + /* 0x2c: unk (0x00?) */ + /* 0x2d: unk (0x00/01?) */ channel_count = read_8bit(0x2a,streamFile); /*0x2b: unk (0x01 when PCM, 0x10 when VAG?) */ block_align = read_8bit(0x2c,streamFile); @@ -184,31 +177,26 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 3: { /* ATRAC3 (encrypted) */ - uint8_t buf[0x100]; - int bytes, joint_stereo, skip_samples; size_t data_size = file_size - start_offset; + int encoder_delay, block_align; - vgmstream->num_samples = block_size; /* atrac3_bytes_to_samples gives the same value */ - if (loop_flag) { - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - block_align = 0xC0 * vgmstream->channels; /* 0x00 in header */ - joint_stereo = 0; - skip_samples = 0; - - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); - if (bytes <= 0) goto fail; + encoder_delay = 1024*2 + 69*2; /* observed value, all files start at +2200 (PS-ADPCM also starts around 50-150 samples in) */ + block_align = 0xC0 * vgmstream->channels; /* 0x00 in header */ + vgmstream->num_samples = block_size - encoder_delay; /* atrac3_bytes_to_samples gives block_size */ temp_streamFile = setup_bgw_atrac3_streamfile(streamFile, start_offset,data_size, 0xC0,channel_count); if (!temp_streamFile) goto fail; - vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0,data_size); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(temp_streamFile, 0x00,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + if (loop_flag) { + vgmstream->loop_start_sample = loop_start - encoder_delay; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + close_streamfile(temp_streamFile); break; } @@ -230,61 +218,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - - -#define BGW_KEY_MAX (0xC0*2) - -typedef struct { - uint8_t key[BGW_KEY_MAX]; - size_t key_size; -} bgw_decryption_data; - -/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */ -static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) { - size_t bytes_read; - int i; - - bytes_read = streamfile->read(streamfile, dest, offset, length); - - /* decrypt data (xor) */ - for (i = 0; i < bytes_read; i++) { - dest[i] ^= data->key[(offset + i) % data->key_size]; - } - - return bytes_read; -} - -static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; - bgw_decryption_data io_data = {0}; - size_t io_data_size = sizeof(bgw_decryption_data); - int ch; - - /* setup decryption with key (first frame + modified channel header) */ - if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail; - - io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile); - for (ch = 0; ch < channels; ch++) { - uint32_t xor = get_32bitBE(io_data.key + frame_size*ch); - put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F); - } - - /* setup subfile */ - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read,NULL); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - return temp_streamFile; - -fail: - close_streamfile(temp_streamFile); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bgw_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/bgw_streamfile.h new file mode 100644 index 000000000..f734fdce6 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/bgw_streamfile.h @@ -0,0 +1,66 @@ +#ifndef _BGW_STREAMFILE_H_ +#define _BGW_STREAMFILE_H_ +#include "../streamfile.h" + + +#define BGW_KEY_MAX (0xC0*2) + +typedef struct { + uint8_t key[BGW_KEY_MAX]; + size_t key_size; +} bgw_decryption_data; + +/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */ +static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + dest[i] ^= data->key[(offset + i) % data->key_size]; + } + + //todo: a few files (music069.bgw, music071.bgw, music900.bgw) have the last frames unencrypted, + // though they are blank and encoder ignores wrongly decrypted frames and outputs blank samples as well + + return bytes_read; +} + +static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + bgw_decryption_data io_data = {0}; + size_t io_data_size = sizeof(bgw_decryption_data); + int ch; + + /* setup decryption with key (first frame + modified channel header) */ + if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail; + + io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile); + for (ch = 0; ch < channels; ch++) { + uint32_t xor = get_32bitBE(io_data.key + frame_size*ch); + put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F); + } + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _BGW_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c b/Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c new file mode 100644 index 000000000..32c6e49b7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c @@ -0,0 +1,45 @@ +#include "meta.h" + + +/* BMP - from Jubeat series (AC) */ +VGMSTREAM * init_vgmstream_bmp_konami(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + /* .bin: actual extension + * .lbin: for plugins */ + if (!check_extensions(streamFile, "bin,lbin")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x424D5000) /* "BMP\0" "*/ + goto fail; + + channel_count = read_8bit(0x10,streamFile); /* assumed */ + if (channel_count != 2) goto fail; + loop_flag = 0; + start_offset = 0x20; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_BMP_KONAMI; + + vgmstream->num_samples = read_32bitBE(0x04,streamFile); + vgmstream->sample_rate = read_32bitBE(0x14, streamFile); + + vgmstream->coding_type = coding_OKI4S; + vgmstream->layout_type = layout_none; + + 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/bwav.c b/Frameworks/vgmstream/vgmstream/src/meta/bwav.c index c07805664..5ee7fc299 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bwav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bwav.c @@ -6,7 +6,6 @@ VGMSTREAM * init_vgmstream_bwav(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int channel_count, loop_flag; - size_t interleave = 0; int32_t coef_start_offset, coef_spacing; /* checks */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dc_str.c b/Frameworks/vgmstream/vgmstream/src/meta/dc_str.c index 709f5204a..d9be078db 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dc_str.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dc_str.c @@ -39,7 +39,7 @@ VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile) { /* fill in the vital statistics */ switch (samples) { case 4: - vgmstream->coding_type = coding_YAMAHA_int; + vgmstream->coding_type = coding_AICA_int; vgmstream->num_samples = read_32bitLE(0x14,streamFile); if (loop_flag) { vgmstream->loop_start_sample = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dcs_wav.c b/Frameworks/vgmstream/vgmstream/src/meta/dcs_wav.c index 871c6fe75..a148ce806 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dcs_wav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dcs_wav.c @@ -42,7 +42,7 @@ VGMSTREAM * init_vgmstream_dcs_wav(STREAMFILE *streamFile) { vgmstream->meta_type = meta_DCS_WAV; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = yamaha_bytes_to_samples(get_streamfile_size(streamFile), channel_count); - vgmstream->coding_type = coding_YAMAHA_int; + vgmstream->coding_type = coding_AICA_int; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x4000; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 9acdef8c0..09b4f5674 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -1,3 +1,4 @@ +#include #include "meta.h" #include "../layout/layout.h" #include "../coding/coding.h" @@ -8,23 +9,24 @@ #define EAAC_VERSION_V0 0x00 /* SNR/SNS */ #define EAAC_VERSION_V1 0x01 /* SPS */ -#define EAAC_CODEC_NONE 0x00 /* internal 'codec not set' */ -#define EAAC_CODEC_RESERVED 0x01 /* not used/reserved? /MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */ -#define EAAC_CODEC_PCM 0x02 +#define EAAC_CODEC_NONE 0x00 /* XAS v0? */ +#define EAAC_CODEC_RESERVED 0x01 /* EALAYER3 V1a? MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */ +#define EAAC_CODEC_PCM16BE 0x02 #define EAAC_CODEC_EAXMA 0x03 -#define EAAC_CODEC_XAS 0x04 +#define EAAC_CODEC_XAS1 0x04 #define EAAC_CODEC_EALAYER3_V1 0x05 #define EAAC_CODEC_EALAYER3_V2_PCM 0x06 #define EAAC_CODEC_EALAYER3_V2_SPIKE 0x07 -#define EAAC_CODEC_DSP 0x08 +#define EAAC_CODEC_GCADPCM 0x08 #define EAAC_CODEC_EASPEEX 0x09 #define EAAC_CODEC_EATRAX 0x0a #define EAAC_CODEC_EAMP3 0x0b #define EAAC_CODEC_EAOPUS 0x0c -#define EAAC_FLAG_NONE 0x00 -#define EAAC_FLAG_LOOPED 0x02 -#define EAAC_FLAG_STREAMED 0x04 +#define EAAC_TYPE_RAM 0x00 +#define EAAC_TYPE_STREAM 0x01 + +#define EAAC_LOOP_SET 0x01 #define EAAC_BLOCKID0_DATA 0x00 #define EAAC_BLOCKID0_END 0x80 /* maybe meant to be a bitflag? */ @@ -337,8 +339,8 @@ VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE *streamFile) { num_metas = read_16bitBE(entry_offset + 0x04, streamFile); metas_offset = read_32bitBE(entry_offset + 0x06, streamFile); - snr_offset = 0xFFFFFFFF; - sns_offset = 0xFFFFFFFF; + snr_offset = 0; + sns_offset = 0; for (i = 0; i < num_metas; i++) { entry_offset = metas_offset + 0x06 * i; @@ -359,10 +361,10 @@ VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE *streamFile) { } } - if (snr_offset == 0xFFFFFFFF && sns_offset == 0xFFFFFFFF) + if (snr_offset == 0 && sns_offset == 0) goto fail; - if (snr_offset == 0xFFFFFFFF) { + if (snr_offset == 0) { /* SPS file */ sbsFile = open_streamfile_by_ext(streamFile, "sbs"); if (!sbsFile) @@ -374,10 +376,10 @@ VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE *streamFile) { snr_offset = sns_offset; sns_offset = snr_offset + (read_32bitBE(snr_offset, sbsFile) & 0x00FFFFFF); snr_offset += 0x04; - vgmstream = init_vgmstream_eaaudiocore_header(sbsFile, sbsFile, snr_offset, sns_offset, meta_EA_SNR_SNS); + vgmstream = init_vgmstream_eaaudiocore_header(sbsFile, sbsFile, snr_offset, sns_offset, meta_EA_SPS); if (!vgmstream) goto fail; - } else if (sns_offset == 0xFFFFFFFF) { + } else if (sns_offset == 0) { /* RAM asset */ sns_offset = snr_offset + get_snr_size(streamFile, snr_offset); vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, snr_offset, sns_offset, meta_EA_SNR_SNS); @@ -409,12 +411,12 @@ fail: /* EA HDR/STH/DAT - seen in older 7th gen games, used for storing speech */ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { int target_stream = streamFile->stream_index; - uint32_t i; uint8_t userdata_size, total_sounds, block_id; - off_t snr_offset, sns_offset; - size_t file_size, block_size; + off_t snr_offset, sns_offset, sth_offset, sth_offset2; + size_t dat_size, block_size; STREAMFILE *datFile = NULL, *sthFile = NULL; VGMSTREAM *vgmstream; + int32_t(*read_32bit)(off_t, STREAMFILE*); /* 0x00: ID */ /* 0x02: userdata size */ @@ -422,10 +424,20 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { /* 0x04: sub-ID (used for different police voices in NFS games) */ /* 0x08: alt number of files? */ /* 0x09: zero */ - /* 0x0A: ??? */ + /* 0x0A: related to size? */ /* 0x0C: zero */ /* 0x10: table start */ + if (read_8bit(0x09, streamFile) != 0) + goto fail; + + if (read_32bitBE(0x0c, streamFile) != 0) + goto fail; + + /* first offset is always zero */ + if (read_16bitBE(0x10, streamFile) != 0) + goto fail; + sthFile = open_streamfile_by_ext(streamFile, "sth"); if (!sthFile) goto fail; @@ -435,7 +447,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { goto fail; /* STH always starts with the first offset of zero */ - sns_offset = read_32bitLE(0x00, sthFile); + sns_offset = read_32bitBE(0x00, sthFile); if (sns_offset != 0) goto fail; @@ -446,6 +458,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { userdata_size = read_8bit(0x02, streamFile); total_sounds = read_8bit(0x03, streamFile); + if (read_8bit(0x08, streamFile) > total_sounds) goto fail; @@ -454,23 +467,25 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { goto fail; /* offsets in HDR are always big endian */ - //snr_offset = (off_t)read_16bitBE(0x10 + (0x02+userdata_size) * (target_stream-1), streamFile) + 0x04; - //sns_offset = read_32bit(snr_offset, sthFile); + sth_offset = (uint16_t)read_16bitBE(0x10 + (0x02 + userdata_size) * (target_stream - 1), streamFile); +#if 0 + snr_offset = sth_offset + 0x04; + sns_offset = read_32bit(sth_offset + 0x00, sthFile); +#else /* we can't reliably detect byte endianness so we're going to find the sound the hacky way */ - /* go through blocks until we reach the goal sound */ - file_size = get_streamfile_size(datFile); + dat_size = get_streamfile_size(datFile); snr_offset = 0; sns_offset = 0; - for (i = 0; i < total_sounds; i++) { - snr_offset = (uint16_t)read_16bitBE(0x10 + (0x02+userdata_size) * i, streamFile) + 0x04; - - if (i == target_stream - 1) - break; - + if (total_sounds == 1) { + /* always 0 */ + snr_offset = sth_offset + 0x04; + sns_offset = 0x00; + } else { + /* find the first sound size and match it up with the second sound offset to detect endianness */ while (1) { - if (sns_offset >= file_size) + if (sns_offset >= dat_size) goto fail; block_id = read_8bit(sns_offset, datFile); @@ -486,7 +501,20 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { if (block_id == EAAC_BLOCKID0_END) break; } + + sth_offset2 = (uint16_t)read_16bitBE(0x10 + (0x02 + userdata_size) * 1, streamFile); + if (sns_offset == read_32bitBE(sth_offset2, sthFile)) { + read_32bit = read_32bitBE; + } else if (sns_offset == read_32bitLE(sth_offset2, sthFile)) { + read_32bit = read_32bitLE; + } else { + goto fail; + } + + snr_offset = sth_offset + 0x04; + sns_offset = read_32bit(sth_offset + 0x00, sthFile); } +#endif block_id = read_8bit(sns_offset, datFile); if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END) @@ -670,7 +698,7 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *streamFile) { goto fail; total_sounds = 0; - sound_offset = 0xFFFFFFFF; + sound_offset = 0; /* The bank is split into DSET sections each of which references one or multiple sounds. */ /* Each set can contain RAM sounds (stored in SBR in data section) or streamed sounds (stored separately in SBS file). */ @@ -760,7 +788,7 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *streamFile) { } } - if (sound_offset == 0xFFFFFFFF) + if (sound_offset == 0) goto fail; if (!is_streamed) { @@ -812,7 +840,8 @@ typedef struct { int codec; int channel_config; int sample_rate; - int flags; + int type; + int loop; int streamed; int channels; @@ -844,34 +873,45 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST /* EA SNR/SPH header */ header1 = (uint32_t)read_32bitBE(header_offset + 0x00, streamHead); header2 = (uint32_t)read_32bitBE(header_offset + 0x04, streamHead); - eaac.version = (header1 >> 28) & 0x0F; /* 4 bits */ - eaac.codec = (header1 >> 24) & 0x0F; /* 4 bits */ + eaac.version = (header1 >> 28) & 0x0F; /* 4 bits */ + eaac.codec = (header1 >> 24) & 0x0F; /* 4 bits */ eaac.channel_config = (header1 >> 18) & 0x3F; /* 6 bits */ - eaac.sample_rate = (header1 & 0x03FFFF); /* 18 bits (some Dead Space 2 (PC) do use 96000) */ - eaac.flags = (header2 >> 28) & 0x0F; /* 4 bits *//* TODO: maybe even 3 bits and not 4? */ - eaac.num_samples = (header2 & 0x0FFFFFFF); /* 28 bits */ + eaac.sample_rate = (header1 >> 0) & 0x03FFFF; /* 18 bits */ + eaac.type = (header2 >> 30) & 0x03; /* 2 bits */ + eaac.loop = (header2 >> 29) & 0x01; /* 1 bits */ + eaac.num_samples = (header2 >> 0) & 0x1FFFFFFF; /* 29 bits */ /* rest is optional, depends on used flags and codec (handled below) */ eaac.stream_offset = start_offset; + /* common channel configs are mono/stereo/quad/5.1/7.1 (from debug strings), while others are quite rare + * [Battlefield 4 (X360)-EAXMA: 3/5/7ch, Army of Two: The Devil's Cartel (PS3)-EALayer3v2P: 11ch] */ + eaac.channels = eaac.channel_config + 1; + /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than block flags) */ if (eaac.version != EAAC_VERSION_V0 && eaac.version != EAAC_VERSION_V1) { VGM_LOG("EA EAAC: unknown version\n"); goto fail; } - /* catch unknown/garbage values just in case */ - if (eaac.flags != EAAC_FLAG_NONE && !(eaac.flags & (EAAC_FLAG_LOOPED | EAAC_FLAG_STREAMED))) { - VGM_LOG("EA EAAC: unknown flags 0x%02x\n", eaac.flags); + /* accepted max (some Dead Space 2 (PC) do use 96000) */ + if (eaac.sample_rate > 200000) { + VGM_LOG("EA EAAC: unknown sample rate\n"); + goto fail; + } + + /* catch unknown values (0x02: "gigasample"? some kind of memory+stream thing?) */ + if (eaac.type != EAAC_TYPE_RAM && eaac.type != EAAC_TYPE_STREAM) { + VGM_LOG("EA EAAC: unknown type 0x%02x\n", eaac.type); goto fail; } /* Non-streamed sounds are stored as a single block (may not set block end flags) */ - eaac.streamed = (eaac.flags & EAAC_FLAG_STREAMED) != 0; + eaac.streamed = (eaac.type == EAAC_TYPE_STREAM); /* get loops (fairly involved due to the multiple layouts and mutant streamfiles) * full loops aren't too uncommon [Dead Space (PC) stream sfx/ambiance, FIFA 98 (PS3) RAM sfx], * while actual looping is very rare [Need for Speed: World (PC)-EAL3, The Simpsons Game (X360)-EAXMA] */ - if (eaac.flags & EAAC_FLAG_LOOPED) { + if (eaac.loop == EAAC_LOOP_SET) { eaac.loop_flag = 1; eaac.loop_start = read_32bitBE(header_offset+0x08, streamHead); eaac.loop_end = eaac.num_samples; @@ -911,28 +951,14 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM || eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE || eaac.codec == EAAC_CODEC_EAXMA || - eaac.codec == EAAC_CODEC_XAS)) { + eaac.codec == EAAC_CODEC_XAS1)) { VGM_LOG("EA EAAC: unknown actual looping for codec %x\n", eaac.codec); goto fail; } } - /* common channel configs are mono/stereo/quad/5.1/7.1 (from debug strings) */ - switch(eaac.channel_config) { - case 0x00: eaac.channels = 1; break; - case 0x01: eaac.channels = 2; break; - case 0x02: eaac.channels = 3; break; /* rare [Battlefield 4 (X360)-EAXMA] */ - case 0x03: eaac.channels = 4; break; - case 0x04: eaac.channels = 5; break; /* rare [Battlefield 4 (X360)-EAXMA] */ - case 0x05: eaac.channels = 6; break; - case 0x06: eaac.channels = 7; break; /* rare [Battlefield 4 (X360)-EAXMA] */ - case 0x07: eaac.channels = 8; break; - case 0x0a: eaac.channels = 11; break; /* rare [Army of Two: The Devil's Cartel (PS3)-EALayer3v2P] */ - default: - /* surely channels = channel_config+1 but fail just in case for now */ - VGM_LOG("EA EAAC: unknown channel config 0x%02x\n", eaac.channel_config); - goto fail; - } + /* if type is gigasample there seems to be a field with number of "gigasamples in ram", + * that goes after loop_start but before streamed/gigasamples' eaac.loop_offset) */ /* build the VGMSTREAM */ @@ -948,7 +974,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST /* EA decoder list and known internal FourCCs */ switch(eaac.codec) { - case EAAC_CODEC_PCM: /* "P6B0": PCM16BE [NBA Jam (Wii)] */ + case EAAC_CODEC_PCM16BE: /* "P6B0": PCM16BE [NBA Jam (Wii)] */ vgmstream->coding_type = coding_PCM16_int; vgmstream->codec_endian = 1; vgmstream->layout_type = layout_blocked_ea_sns; @@ -976,7 +1002,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } #endif - case EAAC_CODEC_XAS: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */ + case EAAC_CODEC_XAS1: /* "Xas1": EA-XAS v1 [Dead Space (PC/PS3)] */ /* special (if hacky) loop handling, see comments */ if (eaac.loop_start > 0) { @@ -1026,7 +1052,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } #endif - case EAAC_CODEC_DSP: /* "Gca0"?: DSP [Need for Speed: Nitro (Wii) sfx] */ + case EAAC_CODEC_GCADPCM: /* "Gca0": DSP [Need for Speed: Nitro (Wii) sfx] */ vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_blocked_ea_sns; /* DSP coefs are read in the blocks */ @@ -1121,9 +1147,11 @@ fail: } static size_t get_snr_size(STREAMFILE *streamFile, off_t offset) { - switch (read_8bit(offset + 0x04, streamFile) >> 4 & 0x0F) { /* flags */ - case EAAC_FLAG_LOOPED | EAAC_FLAG_STREAMED: return 0x10; - case EAAC_FLAG_LOOPED: return 0x0C; + //const int EAAC_FLAG_LOOPED = 0x02; + //const int EAAC_FLAG_STREAMED = 0x04; + switch (read_8bit(offset + 0x04, streamFile) >> 4 & 0x0F) { /* flags */ //todo improve + case 0x02 | 0x04: return 0x10; + case 0x02: return 0x0C; default: return 0x08; } } @@ -1215,8 +1243,7 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st } #endif - case EAAC_CODEC_XAS: - { + case EAAC_CODEC_XAS1: { start_offset = offsets[i]; data->segments[i]->coding_type = coding_EA_XAS_V1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 303ea5d89..792a0d68f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -101,6 +101,8 @@ typedef struct { int big_endian; int loop_flag; int codec_config; + + size_t stream_size; } ea_header; static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone); @@ -118,14 +120,15 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* they don't seem enforced by EA's tools but usually: * .asf: ~early (audio stream file?) [ex. Need for Speed II (PC)] * .lasf: fake for plugins - * .str: ~early [ex. FIFA 2002 (PS1)] - * .eam: ~mid (fake?) + * .str: ~early [ex. FIFA 98 (PS1), FIFA 2002 (PS1)] + * .eam: ~mid? * .exa: ~mid [ex. 007 - From Russia with Love] * .sng: ~late (FIFA games) * .aud: ~late [ex. FIFA 14 (3DS)] * .strm: MySims Kingdom (Wii) * .stm: FIFA 12 (3DS) - * .sx/xa: fake? + * .sx: FIFA 98 (SAT) + * .xa: ? * .hab: GoldenEye - Rogue Agent (inside .big) * .xsf: 007 - Agent Under Fire (Xbox) * .gsf: 007 - Everything or Nothing (GC) @@ -457,26 +460,35 @@ fail: return NULL; } -/* EA HDR/DAT combo - seen in late 6th-gen games, used for storing speech and other streamed sounds (except for music) */ +/* EA HDR/DAT v1 (2004-2005) - used for storing speech and other streamed sounds (except for music) */ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { int target_stream = streamFile->stream_index; uint8_t userdata_size, total_sounds; + size_t dat_size; off_t schl_offset, offset_mult; STREAMFILE *datFile = NULL; VGMSTREAM *vgmstream; - /* main header's endianness is platform-native but we only care about one byte values */ + /* main header is machine endian but it's not important here */ /* 0x00: ID */ /* 0x02: sub-ID (used for different police voices in NFS games) */ /* 0x04: (low nibble) userdata size */ /* 0x04: (high nibble) ??? */ /* 0x05: number of files */ - /* 0x06: ??? */ + /* 0x06: alt number of files? */ /* 0x07: offset multiplier flag */ /* 0x08: combined size of all sounds without padding divided by offset mult */ - /* 0x0C: table start */ + /* 0x0a: zero */ + /* 0x0c: table start */ /* no nice way to validate these so we do what we can */ + if (read_16bitBE(0x0a, streamFile) != 0) + goto fail; + + /* first offset is always zero */ + if (read_16bitBE(0x0c, streamFile) != 0) + goto fail; + /* must be accompanied by DAT file with SCHl sounds */ datFile = open_streamfile_by_ext(streamFile, "dat"); if (!datFile) @@ -489,12 +501,90 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { total_sounds = read_8bit(0x05, streamFile); offset_mult = (uint8_t)read_8bit(0x07, streamFile) * 0x0100 + 0x0100; + if (read_8bit(0x06, streamFile) > total_sounds) + goto fail; + + dat_size = get_streamfile_size(datFile); + if ((uint16_t)read_16bitLE(0x08, streamFile) * offset_mult > dat_size && + (uint16_t)read_16bitBE(0x08, streamFile) * offset_mult > dat_size) + goto fail; + if (target_stream == 0) target_stream = 1; if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) goto fail; /* offsets are always big endian */ - schl_offset = (uint16_t)read_16bitBE(0x0C + (0x02+userdata_size) * (target_stream-1), streamFile) * offset_mult; + schl_offset = (uint16_t)read_16bitBE(0x0C + (0x02 + userdata_size) * (target_stream - 1), streamFile) * offset_mult; + if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER) + goto fail; + + vgmstream = parse_schl_block(datFile, schl_offset, 0); + if (!vgmstream) + goto fail; + + vgmstream->num_streams = total_sounds; + close_streamfile(datFile); + return vgmstream; + +fail: + close_streamfile(datFile); + return NULL; +} + +/* EA HDR/DAT v2 (2006-2014) */ +VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE *streamFile) { + int target_stream = streamFile->stream_index; + uint8_t userdata_size, total_sounds; + size_t dat_size; + off_t schl_offset, offset_mult; + STREAMFILE *datFile = NULL; + VGMSTREAM *vgmstream; + + /* main header is machine endian but it's not important here */ + /* 0x00: ID */ + /* 0x02: userdata size */ + /* 0x03: number of files */ + /* 0x04: sub-ID (used for different police voices in NFS games) */ + /* 0x08: alt number of files? */ + /* 0x09: offset mult */ + /* 0x0a: DAT size divided by offset mult */ + /* 0x0c: zero */ + /* 0x10: table start */ + + /* no nice way to validate these so we do what we can */ + if (read_32bitBE(0x0c, streamFile) != 0) + goto fail; + + /* first offset is always zero */ + if (read_16bitBE(0x10, streamFile) != 0) + goto fail; + + /* must be accompanied by DAT file with SCHl sounds */ + datFile = open_streamfile_by_ext(streamFile, "dat"); + if (!datFile) + goto fail; + + if (read_32bitBE(0x00, datFile) != EA_BLOCKID_HEADER) + goto fail; + + userdata_size = read_8bit(0x02, streamFile); + total_sounds = read_8bit(0x03, streamFile); + offset_mult = (uint8_t)read_8bit(0x09, streamFile) * 0x0100 + 0x0100; + + if (read_8bit(0x08, streamFile) > total_sounds) + goto fail; + + dat_size = get_streamfile_size(datFile); + if ((uint16_t)read_16bitLE(0x0a, streamFile) * offset_mult != dat_size && + (uint16_t)read_16bitBE(0x0a, streamFile) * offset_mult != dat_size) + goto fail; + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) + goto fail; + + /* offsets are always big endian */ + schl_offset = (uint16_t)read_16bitBE(0x10 + (0x02 + userdata_size) * (target_stream - 1), streamFile) * offset_mult; if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER) goto fail; @@ -728,6 +818,8 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { /* we need to go through the first two sections to find the sound table */ sec1_num = read_16bit(0x12, streamFile); sec2_num = read_8bit(0x0f, streamFile); + sec3_num = read_8bit(0x10, streamFile); + sec4_num = read_8bit(0x11, streamFile); /* get the last entry offset */ section_offset = 0x20; @@ -739,6 +831,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { } section_offset = entry_offset + 0x10 + subentry_num * 0x04; + entry_offset = (uint16_t)read_16bit(section_offset + (sec2_num - 1) * 0x02, streamFile) * 0x04; if (big_endian) { subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 10) & 0xFF; @@ -749,6 +842,9 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { section_offset = entry_offset + 0x10 + subentry_num * 0x10; entry_offset = read_32bit(section_offset, streamFile) * 0x04; + entry_offset += sec3_num * 0x04; + entry_offset += sec4_num * 0x04; + section_offset = read_32bit(entry_offset + 0x00, streamFile) * 0x04; eof_offset = read_32bit(entry_offset + 0x04, streamFile) * 0x04; total_streams = (eof_offset - section_offset) / 0x08; @@ -1073,8 +1169,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ #ifdef VGM_USE_FFMPEG case EA_CODEC2_ATRAC3PLUS: { - ffmpeg_codec_data *ffmpeg_data; - /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header [Medal of Honor Heroes 2 (PSP)] */ if (!is_bnk) { STREAMFILE* temp_streamFile = NULL; @@ -1083,23 +1177,19 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ if (!temp_streamFile) goto fail; start_offset = 0x00; /* must point to the custom streamfile's beginning */ + ea->stream_size = get_streamfile_size(temp_streamFile); - ffmpeg_data = init_ffmpeg_offset(temp_streamFile, start_offset, get_streamfile_size(temp_streamFile)); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(temp_streamFile, start_offset, NULL); close_streamfile(temp_streamFile); - if (!ffmpeg_data) goto fail; } else { - size_t riff_size = read_32bitLE(start_offset + 0x04, streamFile) + 0x08; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, riff_size); - if (!ffmpeg_data) goto fail; + /* memory file without blocks */ + vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, NULL); } - vgmstream->codec_data = ffmpeg_data; + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - - if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ - ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); break; } #endif @@ -1109,6 +1199,8 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ goto fail; } + vgmstream->stream_size = ea->stream_size; + /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; @@ -1541,16 +1633,23 @@ fail: static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream, int standalone) { uint32_t block_id; - int32_t num_samples; - size_t stream_size, file_size; - int multiple_schl; + int32_t num_samples = 0; + size_t stream_size = 0, file_size; + int multiple_schl = 0; - stream_size = 0, num_samples = 0, multiple_schl = 0; file_size = get_streamfile_size(streamFile); - vgmstream->next_block_offset = start_offset; + /* formats with custom codecs */ + if (vgmstream->layout_type != layout_blocked_ea_schl) { + return; + } + + /* manually read totals */ + block_update(start_offset, vgmstream); while (vgmstream->next_block_offset < file_size) { block_update_ea_schl(vgmstream->next_block_offset, vgmstream); + if (vgmstream->current_block_samples < 0) + break; block_id = read_32bitBE(vgmstream->current_block_offset + 0x00, streamFile); if (block_id == EA_BLOCKID_END) { /* banks should never contain movie "SHxx" */ @@ -1584,7 +1683,8 @@ static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t star } } - vgmstream->stream_size = stream_size; + if (vgmstream->stream_size == 0) + 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/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index c968ae865..3784b7dd4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -15,6 +15,7 @@ VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { VGMSTREAM *vgmstream = NULL; + ffmpeg_codec_data *data = NULL; int loop_flag = 0; int32_t loop_start = 0, loop_end = 0, num_samples = 0; int total_subsongs, target_subsong = streamFile->stream_index; @@ -23,9 +24,13 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, //if (!check_extensions(streamFile, "...")) // goto fail; + /* don't try to open headers and other mini files */ + if (get_streamfile_size(streamFile) <= 0x100) + goto fail; + /* init ffmpeg */ - ffmpeg_codec_data *data = init_ffmpeg_offset(streamFile, start, size); + data = init_ffmpeg_offset(streamFile, start, size); if (!data) return NULL; total_subsongs = data->streamCount; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 80156dfd1..730e896e2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -148,6 +148,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { fsb.loop_start = read_32bitLE(header_offset+0x38,streamFile); fsb.loop_end = read_32bitLE(header_offset+0x3c,streamFile); + VGM_ASSERT(fsb.loop_end > fsb.num_samples, "FSB: loop end over samples (%i vs %i)\n", fsb.loop_end, fsb.num_samples); fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1; if (fsb.loop_end > fsb.num_samples) /* this seems common... */ fsb.num_samples = fsb.loop_end; @@ -264,25 +265,6 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { } - /* XOR encryption for some FSB4, though the flag is only seen after decrypting */ - //;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 != streamFile->get_size(streamFile), - "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, streamFile->get_size(streamFile)); - - /* Loops unless disabled. FMOD default seems to be full loops (0/num_samples-1) without flags, for repeating tracks - * that should loop and jingles/sfx that shouldn't. We'll try to disable looping if it looks jingly enough. */ - fsb.loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); - if(!(fsb.mode & FSOUND_LOOP_NORMAL) /* rarely set */ - && fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */ - && fsb.num_samples < 20*fsb.sample_rate) /* in seconds (lame but no other way to know) */ - fsb.loop_flag = 0; - - /* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */ - VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n"); - /* convert to clean some code */ if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG; else if (fsb.mode & FSOUND_IMAADPCM) fsb.codec = IMA; @@ -293,6 +275,55 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { else if (fsb.mode & FSOUND_8BITS) fsb.codec = PCM8; else fsb.codec = PCM16; + /* correct compared to FMOD's tools */ + if (fsb.loop_end) + fsb.loop_end += 1; + + /* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */ + VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n"); + VGM_ASSERT(fsb.mode & FSOUND_LOOP_OFF, "FSB LOOP OFF found\n"); /* sometimes used */ + VGM_ASSERT(fsb.mode & FSOUND_LOOP_NORMAL, "FSB LOOP NORMAL found\n"); /* very rarely set */ + /* XOR encryption for some FSB4, though the flag is only seen after decrypting */ + //;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 != streamFile->get_size(streamFile), + "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, streamFile->get_size(streamFile)); + + /* autodetect unwanted loops */ + { + /* FMOD tool's default behaviour is creating files with full loops and no flags unless disabled + * manually (can be overriden during program too), for all FSB versions. This makes jingles/sfx/voices + * loop when they shouldn't, but most music does full loops seamlessly, so we only want to disable + * if it looks jingly enough. Incidentally, their tools can only make files with full loops. */ + int enable_loop, full_loop, is_small; + + /* seems to mean forced loop */ + enable_loop = (fsb.mode & FSOUND_LOOP_NORMAL); + + /* for MPEG and CELT sometimes full loops are given with around/exact 1 frame less than num_samples, + * probably to account for encoder/decoder delay (ex. The Witcher 2, Hard Reset, Timeshift) */ + if (fsb.codec == CELT) + full_loop = fsb.loop_start - 512 <= 0 && fsb.loop_end >= fsb.num_samples - 512; /* aproximate */ + else if (fsb.codec == MPEG) + full_loop = fsb.loop_start - 1152 <= 0 && fsb.loop_end >= fsb.num_samples - 1152; /* WWF Legends of Wrestlemania uses 2 frames? */ + else + full_loop = fsb.loop_start == 0 && fsb.loop_end == fsb.num_samples; + + /* in seconds (lame but no better way) */ + is_small = fsb.num_samples < 20 * fsb.sample_rate; + + //;VGM_LOG("FSB: loop start=%i, loop end=%i, samples=%i, mode=%x\n", fsb.loop_start, fsb.loop_end, fsb.num_samples, fsb.mode); + //;VGM_LOG("FSB: enable=%i, full=%i, small=%i\n",enable_loop,full_loop,is_small ); + + 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"); + fsb.loop_flag = 0; + } + } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(fsb.channels,fsb.loop_flag); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 5be34b805..4ba6e5520 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -147,23 +147,39 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; case 0x03: /* loop info */ fsb5.loop_start = read_32bitLE(extraflag_offset+0x04,streamFile); - if (extraflag_size > 0x04) /* probably not needed */ + if (extraflag_size > 0x04) { /* probably not needed */ fsb5.loop_end = read_32bitLE(extraflag_offset+0x08,streamFile); + fsb5.loop_end += 1; /* correct compared to FMOD's tools */ + } - /* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ - fsb5.loop_flag = (fsb5.loop_start != 0x00); + /* autodetect unwanted loops */ + { + /* like FSB4 jingles/sfx/music do full loops for no reason, but happens a lot less. + * Most songs loop normally now with proper values [ex. Shantae, FFX] */ + int full_loop, ajurika_loops; - /* ignore wrong loops in some files [Pac-Man CE2 Plus (Switch) pce2p_bgm_ajurika_*.fsb] */ - if (fsb5.loop_start == 0x3c && fsb5.loop_end == 0x007F007F && - fsb5.num_samples > fsb5.loop_end + 100000) { /* arbitrary limit */ - fsb5.loop_flag = 0; + /* could use the same checks as FSB4 but simplified (ex. Sonic Boom Fire & Ice jingles) */ + full_loop = fsb5.loop_start != 0x00; + + /* wrong values in some files [Pac-Man CE2 Plus (Switch) pce2p_bgm_ajurika_*.fsb] */ + ajurika_loops = fsb5.loop_start == 0x3c && fsb5.loop_end == 0x007F007F && + fsb5.num_samples > fsb5.loop_end + 100000; /* arbitrary limit */ + + //;VGM_LOG("FSB5: loop start=%i, loop end=%i, samples=%i\n", fsb5.loop_start, fsb5.loop_end, fsb5.num_samples); + + fsb5.loop_flag = 1; + if (!full_loop || ajurika_loops) { + VGM_LOG("FSB5: disabled unwanted loop\n"); + fsb5.loop_flag = 0; + } } break; case 0x04: /* free comment, or maybe SFX info */ break; - //case 0x05: /* Unknown (32b) */ //todo multistream marker? - // /* found in Tearaway Vita, value 0, first stream only */ - // break; + case 0x05: /* unknown 32b */ //todo multistream marker? + /* found in Tearaway Vita, value 0, first stream only */ + VGM_LOG("FSB5: flag %x with value %08x\n", extraflag_type, read_32bitLE(extraflag_offset+0x04,streamFile)); + break; case 0x06: /* XMA seek table */ /* no need for it */ break; @@ -186,9 +202,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { * (xN entries) */ break; - //case 0x0d: /* Unknown (32b) */ - // /* found in some XMA2/Vorbis/FADPCM */ - // break; + case 0x0d: /* unknown 32b (config? usually 0x3fnnnn00 BE) */ + /* found in some XMA2/Vorbis/FADPCM */ + VGM_LOG("FSB5: flag %x with value %08x\n", extraflag_type, read_32bitLE(extraflag_offset+0x04,streamFile)); + break; default: VGM_LOG("FSB5: unknown extraflag 0x%x at %x + 0x04 (size 0x%x)\n", extraflag_type, (uint32_t)extraflag_offset, extraflag_size); break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gcsw.c b/Frameworks/vgmstream/vgmstream/src/meta/gcsw.c index c46954d10..6d6122353 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gcsw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gcsw.c @@ -1,62 +1,45 @@ #include "meta.h" -#include "../util.h" + +/* GCSW - from Radirgy GeneriC (GC) */ VGMSTREAM * init_vgmstream_gcsw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + int channel_count, loop_flag; + off_t start_offset; - int channel_count; - int loop_flag; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("gcw",filename_extension(filename))) goto fail; - - /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)!=0x47435357) /* "GCSW" */ + /* checks */ + if (!check_extensions(streamFile, "gcw")) goto fail; - /* check type details */ - /* guess */ + if (read_32bitBE(0,streamFile) != 0x47435357) /* "GCSW" */ + goto fail; + + start_offset = 0x20; + channel_count = read_32bitBE(0x0c,streamFile); loop_flag = read_32bitBE(0x1c,streamFile); - channel_count = read_32bitBE(0xc,streamFile); + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ + vgmstream->meta_type = meta_GCSW; + + vgmstream->sample_rate = read_32bitBE(0x08,streamFile); vgmstream->num_samples = read_32bitBE(0x10,streamFile); - vgmstream->sample_rate = read_32bitBE(0x8,streamFile); - /* channels and loop flag are set by allocate_vgmstream */ vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile); vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile); vgmstream->coding_type = coding_PCM16BE; vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_GCSW; - vgmstream->interleave_block_size = 0x8000; - /* open the file for reading by each channel */ - { - int i; - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - 0x20+0x8000*i; - } - } - + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/genh.c b/Frameworks/vgmstream/vgmstream/src/meta/genh.c index 3fe328881..eedba22ed 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/genh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/genh.c @@ -16,7 +16,7 @@ typedef enum { DVI_IMA = 7, /* DVI IMA ADPCM (high nibble first) */ MPEG = 8, /* MPEG (MP3) */ IMA = 9, /* IMA ADPCM (low nibble first) */ - YAMAHA = 10, /* YAMAHA (AICA) ADPCM (Dreamcast games) */ + AICA = 10, /* YAMAHA AICA ADPCM (Dreamcast games) */ MSADPCM = 11, /* MS ADPCM (Windows games) */ NGC_DSP = 12, /* NGC DSP (Nintendo games) */ PCM8_U_int = 13, /* 8-bit unsigned PCM (interleaved) */ @@ -102,7 +102,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */ #endif case IMA: coding = coding_IMA; break; - case YAMAHA: coding = coding_YAMAHA; break; + case AICA: coding = coding_AICA; break; case MSADPCM: coding = coding_MSADPCM; break; case NGC_DSP: coding = coding_NGC_DSP; break; case PCM8_U_int: coding = coding_PCM8_U_int; break; @@ -153,7 +153,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case coding_PSX_badflags: case coding_DVI_IMA: case coding_IMA: - case coding_YAMAHA: + case coding_AICA: case coding_APPLE_IMA4: vgmstream->interleave_block_size = genh.interleave; vgmstream->interleave_last_block_size = genh.interleave_last; @@ -172,8 +172,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { coding = coding_DVI_IMA_int; if (coding == coding_IMA) coding = coding_IMA_int; - if (coding == coding_YAMAHA) - coding = coding_YAMAHA_int; + if (coding == coding_AICA) + coding = coding_AICA_int; } /* to avoid endless loops */ @@ -190,11 +190,11 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { } /* to avoid problems with dual stereo files (_L+_R) for codecs with stereo modes */ - if (coding == coding_YAMAHA && genh.channels == 1) - coding = coding_YAMAHA_int; + if (coding == coding_AICA && genh.channels == 1) + coding = coding_AICA_int; /* setup adpcm */ - if (coding == coding_YAMAHA || coding == coding_YAMAHA_int) { + if (coding == coding_AICA || coding == coding_AICA_int) { int ch; for (ch = 0; ch < vgmstream->channels; ch++) { vgmstream->ch[ch].adpcm_step_index = 0x7f; @@ -313,39 +313,41 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { int32_t bytes; if (genh.codec == ATRAC3) { - int block_size = genh.interleave; - int joint_stereo; - switch(genh.codec_mode) { - case 0: joint_stereo = vgmstream->channels > 1 && genh.interleave/vgmstream->channels==0x60 ? 1 : 0; break; /* autodetect */ - case 1: joint_stereo = 1; break; /* force joint stereo */ - case 2: joint_stereo = 0; break; /* force stereo */ - default: goto fail; - } + int block_align, encoder_delay; - bytes = ffmpeg_make_riff_atrac3(buf, 200, vgmstream->num_samples, genh.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, genh.skip_samples); + block_align = genh.interleave; + encoder_delay = genh.skip_samples; + + ffmpeg_data = init_ffmpeg_atrac3_raw(streamFile, genh.start_offset,genh.data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!ffmpeg_data) goto fail; } else if (genh.codec == ATRAC3PLUS) { int block_size = genh.interleave; bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, genh.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, genh.skip_samples); + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, genh.start_offset,genh.data_size); + if ( !ffmpeg_data ) goto fail; } else if (genh.codec == XMA1) { int xma_stream_mode = genh.codec_mode == 1 ? 1 : 0; bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, genh.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode); + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, genh.start_offset,genh.data_size); + if ( !ffmpeg_data ) goto fail; } else if (genh.codec == XMA2) { - int block_size = genh.interleave ? genh.interleave : 2048; - int block_count = genh.data_size / block_size; + int block_count, block_size; + + block_size = genh.interleave ? genh.interleave : 2048; + block_count = genh.data_size / block_size; bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, genh.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, genh.start_offset,genh.data_size); + if ( !ffmpeg_data ) goto fail; } else { goto fail; } - - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, genh.start_offset,genh.data_size); - if ( !ffmpeg_data ) goto fail; } vgmstream->codec_data = ffmpeg_data; @@ -353,7 +355,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { if (genh.codec == XMA1 || genh.codec == XMA2) { xma_fix_raw_samples(vgmstream, streamFile, genh.start_offset,genh.data_size, 0, 0,0); - } else if (genh.skip_samples_mode && genh.skip_samples >= 0) { /* force encoder delay */ + } else if (genh.skip_samples_mode && genh.skip_samples >= 0 && genh.codec != ATRAC3) { /* force encoder delay */ ffmpeg_set_skip_samples(ffmpeg_data, genh.skip_samples); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c index 8a5236aae..71f5bbf1c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c @@ -6,10 +6,10 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; - int loop_flag, channel_count; + int loop_flag, channel_count, sample_rate, num_samples, loop_start, loop_end; off_t start_offset, chunk_offset, first_offset; - size_t datasize; - int codec_id; + size_t data_size; + int codec; /* checks */ @@ -19,16 +19,43 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { streamHeader = open_streamfile_by_ext(streamFile, "gsp"); if (!streamHeader) goto fail; - /* check header */ if (read_32bitBE(0x00,streamHeader) != 0x47534E44) /* "GSND" */ goto fail; - /* 0x04: version?, 0x08: 1?, 0x0c: 0?, */ - first_offset = read_32bitBE(0x10,streamHeader); /* usually 0x14*/ + /* 0x04: version? */ + /* 0x08: 1? */ + /* 0x0c: 0? */ + first_offset = read_32bitBE(0x10,streamHeader); /* usually 0x14 */ + + if (!find_chunk_be(streamHeader, 0x48454144,first_offset,1, &chunk_offset,NULL)) /* "HEAD" */ + goto fail; + /* 0x00: header size */ + /* 0x04: num_chunks */ + + if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) /* "DATA" */ + goto fail; + data_size = read_32bitBE(chunk_offset + 0x00,streamHeader); + codec = read_32bitBE(chunk_offset + 0x04,streamHeader); + sample_rate = read_32bitBE(chunk_offset + 0x08,streamHeader); + /* 0x0c: always 16? */ + channel_count = read_16bitBE(chunk_offset + 0x0e,streamHeader); + /* 0x10: always 0? */ + num_samples = read_32bitBE(chunk_offset + 0x14,streamHeader); + /* 0x18: always 0? */ + /* 0x1c: unk (varies with codec_id) */ + + if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) /* "BSIC" */ + goto fail; + /* 0x00/0x04: probably volume/pan/etc floats (1.0) */ + /* 0x08: null? */ + loop_flag = read_8bit(chunk_offset+0x0c,streamHeader); + loop_start = read_32bitBE(chunk_offset+0x10,streamHeader); + loop_end = read_32bitBE(chunk_offset+0x14,streamHeader); + + //if (!find_chunk_be(streamHeader, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) /* "NAME" */ + // goto fail; + /* 0x00: name_size */ + /* 0x04+: name (same as filename) */ - if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ - channel_count = read_16bitBE(chunk_offset+0x0e,streamHeader); - if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ - loop_flag = read_8bit(chunk_offset+0x0c,streamHeader); start_offset = 0x00; @@ -39,27 +66,12 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { vgmstream->meta_type = meta_GSP_GSB; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; - if (!find_chunk_be(streamHeader, 0x48454144,first_offset,1, &chunk_offset,NULL)) goto fail; /*"HEAD"*/ - /* 0x00: header_size, 0x04: num_chunks */ - - if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ - /* 0x00: filesize, 0x0c: always 10?, 0x10: always 0?, 0x18: always 0? */ - datasize = read_32bitBE(chunk_offset+0x00,streamHeader); - codec_id = read_32bitBE(chunk_offset+0x04,streamHeader); - vgmstream->sample_rate = read_32bitBE(chunk_offset+0x08,streamHeader); - vgmstream->num_samples = read_32bitBE(chunk_offset+0x14,streamHeader); - /* 0x1c: unk (varies with codec_id) */ - - if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ - /* 0x00+: probably volume/pan/etc */ - vgmstream->loop_start_sample = read_32bitBE(chunk_offset+0x10,streamHeader); - vgmstream->loop_end_sample = read_32bitBE(chunk_offset+0x14,streamHeader); - - //if (!find_chunk_be(streamHeader, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) goto fail; /*"NAME"*/ - /* 0x00: name_size, 0x04+: name*/ - - switch (codec_id) { + switch (codec) { case 0x04: { /* DSP [Super Swing Golf (Wii)] */ size_t block_header_size; size_t num_blocks; @@ -67,12 +79,13 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_blocked_gsb; - if (!find_chunk_be(streamHeader, 0x47434558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"GCEX"*/ + if (!find_chunk_be(streamHeader, 0x47434558,first_offset,1, &chunk_offset,NULL)) /* "GCEX" */ + goto fail; //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamHeader); block_header_size = read_32bitBE(chunk_offset+0x04,streamHeader); num_blocks = read_32bitBE(chunk_offset+0x08,streamHeader); - vgmstream->num_samples = (datasize - block_header_size * num_blocks) / 8 / vgmstream->channels * 14; + vgmstream->num_samples = (data_size - block_header_size * num_blocks) / 8 / vgmstream->channels * 14; /* 0x0c+: unk */ dsp_read_coefs_be(vgmstream, streamHeader, chunk_offset+0x18, 0x30); @@ -80,30 +93,21 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { } #ifdef VGM_USE_FFMPEG case 0x08: { /* ATRAC3 [Quantum Theory (PS3)] */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - int32_t bytes, block_size, encoder_delay, joint_stereo, max_samples; + int block_align, encoder_delay; - block_size = 0x98 * vgmstream->channels; - joint_stereo = 0; - max_samples = atrac3_bytes_to_samples(datasize, block_size);; - encoder_delay = max_samples - vgmstream->num_samples; /* todo guessed */ + block_align = 0x98 * vgmstream->channels; + encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; + /* fix num_samples as header samples seem to be modified to match altered (49999/48001) sample rates somehow */ - vgmstream->num_samples += encoder_delay; - /* make a fake riff so FFmpeg can parse the ATRAC3 */ - bytes = ffmpeg_make_riff_atrac3(buf,100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); - if (bytes <= 0) - goto fail; - - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->loop_start_sample = (vgmstream->loop_start_sample / ffmpeg_data->blockAlign) * ffmpeg_data->frameSize; - vgmstream->loop_end_sample = (vgmstream->loop_end_sample / ffmpeg_data->blockAlign) * ffmpeg_data->frameSize; - + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; break; } @@ -112,17 +116,19 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { uint8_t buf[200]; int32_t bytes; - if (!find_chunk_be(streamHeader, 0x584D4558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"XMEX"*/ - /* 0x00: fmt0x166 header (BE), 0x34: seek table */ + if (!find_chunk_be(streamHeader, 0x584D4558,first_offset,1, &chunk_offset,NULL)) /* "XMEX" */ + goto fail; + /* 0x00: fmt0x166 header (BE) */ + /* 0x34: seek table */ - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, datasize, streamHeader, 1); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, data_size, streamHeader, 1); + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - xma_fix_raw_samples(vgmstream, streamFile, start_offset,datasize, 0, 0,0); /* samples are ok */ + xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* samples are ok */ break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index fa87c62eb..42c56ec5c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -32,11 +32,14 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) { keysize = read_key_file(keybuf, 0x08+0x04, streamFile); if (keysize == 0x08) { /* standard */ keycode = (uint64_t)get_64bitBE(keybuf+0x00); + if (subkey) { + keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) ); + } } else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */ - uint64_t key = (uint64_t)get_64bitBE(keybuf+0x00); - uint16_t sub = (uint16_t)get_16bitBE(keybuf+0x08); - keycode = key * ( ((uint64_t)sub << 16u) | ((uint16_t)~sub + 2u) ); + uint64_t file_key = (uint64_t)get_64bitBE(keybuf+0x00); + uint16_t file_sub = (uint16_t)get_16bitBE(keybuf+0x08); + keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) ); } else { find_hca_key(hca_data, &keycode, subkey); @@ -65,10 +68,34 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) { //if (vgmstream->loop_end_sample && vgmstream->num_samples > vgmstream->loop_end_sample) // vgmstream->num_samples = vgmstream->loop_end_sample; + /* this can happen in preloading HCA from memory AWB */ + if (hca_data->info.blockCount * hca_data->info.blockSize > get_streamfile_size(streamFile)) { + unsigned int max_block = get_streamfile_size(streamFile) / hca_data->info.blockSize; + vgmstream->num_samples = max_block * hca_data->info.samplesPerBlock - + hca_data->info.encoderDelay - hca_data->info.encoderPadding; + } + vgmstream->coding_type = coding_CRI_HCA; vgmstream->layout_type = layout_none; vgmstream->codec_data = hca_data; + /* assumed mappings */ + { + static const uint32_t hca_mappings[] = { + 0, + mapping_MONO, + mapping_STEREO, + mapping_2POINT1, + mapping_QUAD, + mapping_5POINT0, + mapping_5POINT1, + mapping_7POINT0, + mapping_7POINT1, + }; + + vgmstream->channel_layout = hca_mappings[vgmstream->channels]; + } + return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 8710a4335..1ae76b85c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -291,6 +291,9 @@ static const hcakey_info hcakey_list[] = { /* DAME x PRINCE (Android) */ {217019410378917901}, // 030302010100080D + /* Uta Macross SmaPho De Culture (Android) */ + {396798934275978741}, // 0581B68744C5F5F5 + /* Dragalia Lost (Cygames) [iOS/Android] */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/his.c b/Frameworks/vgmstream/vgmstream/src/meta/his.c index 9ed5d0787..121141e93 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/his.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/his.c @@ -1,112 +1,99 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* Her Interactive Sound .his (Nancy Drew) */ -/* A somewhat transformed RIFF WAVE */ +/* HIS - Her Interactive games [Nancy Drew series (PC)] */ VGMSTREAM * init_vgmstream_his(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int channel_count; - int loop_flag = 0; - int bps = 0; + int channel_count, loop_flag = 0, bps, sample_rate, num_samples, version; off_t start_offset; - const uint8_t header_magic_expected[0x16] = "Her Interactive Sound\x1a"; - uint8_t header_magic[0x16]; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("his",filename_extension(filename))) goto fail; - /* check header magic */ - if (0x16 != streamFile->read(streamFile, header_magic, 0, 0x16)) goto fail; - if (memcmp(header_magic_expected, header_magic, 0x16)) goto fail; + /* checks */ + if (!check_extensions(streamFile, "his")) + goto fail; - /* data chunk label */ - if (0x64617461 != read_32bitBE(0x24,streamFile)) goto fail; + if (read_32bitBE(0x00,streamFile) == 0x48657220) { /* "Her Interactive Sound\x1a" */ + /* Nancy Drew: Secrets Can Kill (PC) */ + version = 0; + channel_count = read_16bitLE(0x16,streamFile); + sample_rate = read_32bitLE(0x18,streamFile); + /* 0x1c: bitrate */ + /* 0x20: block size */ + bps = read_16bitLE(0x22,streamFile); - start_offset = 0x2c; + if (read_32bitBE(0x24,streamFile) != 0x64617461) /* "data" */ + goto fail; + num_samples = pcm_bytes_to_samples(read_32bitLE(0x28,streamFile), channel_count, bps); - channel_count = read_16bitLE(0x16,streamFile); + start_offset = 0x2c; + } + else if (read_32bitBE(0x00,streamFile) == 0x48495300) { /* HIS\0 */ + /* most(?) others */ + version = read_32bitLE(0x04,streamFile); + /* 0x08: codec */ + channel_count = read_16bitLE(0x0a,streamFile); + sample_rate = read_32bitLE(0x0c,streamFile); + /* 0x10: bitrate */ + /* 0x14: block size */ + bps = read_16bitLE(0x16,streamFile); - /* 8-bit or 16-bit expected */ - switch (read_16bitLE(0x22,streamFile)) - { + num_samples = pcm_bytes_to_samples(read_32bitLE(0x18,streamFile), channel_count, bps); /* true even for Ogg */ + + /* later games use "OggS" */ + if (version == 1) + start_offset = 0x1c; /* Nancy Drew: The Final Scene (PC) */ + else if (version == 2 && read_32bitBE(0x1e,streamFile) == 0x4F676753) + start_offset = 0x1e; /* Nancy Drew: The Haunted Carousel (PC) */ + else if (version == 2 && read_32bitBE(0x20,streamFile) == 0x4F676753) + start_offset = 0x20; /* Nancy Drew: The Silent Spy (PC) */ + else + goto fail; + } + else { + goto fail; + } + + + if (version == 2) { +#ifdef VGM_USE_VORBIS + ogg_vorbis_meta_info_t ovmi = {0}; + + ovmi.meta_type = meta_HIS; + return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); +#else + goto fail; +#endif + } + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_HIS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + switch (bps) { case 8: - bps = 1; + vgmstream->coding_type = coding_PCM8_U; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; break; case 16: - bps = 2; + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; break; default: goto fail; } - /* check bytes per frame */ - if (read_16bitLE(0x20,streamFile) != channel_count*bps) goto fail; - - /* check size */ - /* file size -8 */ - if ((read_32bitLE(0x1c,streamFile)+8) != get_streamfile_size(streamFile)) - goto fail; - /* data chunk size, assume it occupies the rest of the file */ - //if ((read_32bitLE(0x28,streamFile)+start_offset) != get_streamfile_size(streamFile)) - // goto fail; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitLE(0x28,streamFile) / channel_count / bps; - vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - - vgmstream->meta_type = meta_HIS; - vgmstream->layout_type = layout_none; - if (bps == 2) - { - vgmstream->coding_type = coding_PCM16LE; - if (channel_count == 2) - { - vgmstream->coding_type = coding_PCM16_int; - vgmstream->interleave_block_size = 2; - } - } - else // bps == 1 - { - vgmstream->coding_type = coding_PCM8_U; - if (channel_count == 2) - { - vgmstream->coding_type = coding_PCM8_U_int; - vgmstream->interleave_block_size = 1; - } - } - - /* open the file for reading */ - { - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - vgmstream->ch[0].streamfile = file; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset=start_offset; - - if (channel_count == 2) - { - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - vgmstream->ch[1].streamfile = file; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[1].offset=start_offset + vgmstream->interleave_block_size; - } - } - + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ima.c b/Frameworks/vgmstream/vgmstream/src/meta/ima.c new file mode 100644 index 000000000..7ef9349d4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ima.c @@ -0,0 +1,52 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .IMA - Blitz Games early games [Lilo & Stitch: Trouble in Paradise (PC)] */ +VGMSTREAM * init_vgmstream_ima(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, num_samples, sample_rate; + + + /* checks */ + if (!check_extensions(streamFile, "ima")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x02000000) /* version? */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0) + goto fail; + + num_samples = read_32bitLE(0x08, streamFile); + channel_count = read_32bitLE(0x0c,streamFile); + sample_rate = read_32bitLE(0x10, streamFile); + + loop_flag = 0; + start_offset = 0x14; + + if (channel_count > 1) /* unknown interleave */ + goto fail; + if (num_samples != ima_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count)) + goto fail; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_IMA; + vgmstream->sample_rate = sample_rate; + + vgmstream->coding_type = coding_BLITZ_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = num_samples; + + 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_jstm.c b/Frameworks/vgmstream/vgmstream/src/meta/jstm.c similarity index 55% rename from Frameworks/vgmstream/vgmstream/src/meta/ps2_jstm.c rename to Frameworks/vgmstream/vgmstream/src/meta/jstm.c index 9f78093c3..51902acff 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_jstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/jstm.c @@ -1,10 +1,10 @@ #include "meta.h" #include "../coding/coding.h" +#include "jstm_streamfile.h" -static STREAMFILE* setup_jstm_streamfile(STREAMFILE *streamFile, off_t start_offset); /* JSTM - from Tantei Jinguji Saburo - Kind of Blue (PS2) */ -VGMSTREAM * init_vgmstream_ps2_jstm(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_jstm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; off_t start_offset; @@ -55,49 +55,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - - -typedef struct { - off_t start_offset; -} jstm_decryption_data; - -static size_t jstm_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, jstm_decryption_data* data) { - size_t bytes_read; - int i; - - bytes_read = streamfile->read(streamfile, dest, offset, length); - - /* decrypt data (xor) */ - for (i = 0; i < bytes_read; i++) { - if (offset+i >= data->start_offset) { - dest[i] = dest[i] ^ 0x5A; - } - } - - return bytes_read; -} - -static STREAMFILE* setup_jstm_streamfile(STREAMFILE *streamFile, off_t start_offset) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; - jstm_decryption_data io_data = {0}; - size_t io_data_size = sizeof(jstm_decryption_data); - - /* setup decryption */ - io_data.start_offset = start_offset; - - - /* setup custom streamfile */ - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, jstm_decryption_read,NULL); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - return temp_streamFile; - -fail: - close_streamfile(temp_streamFile); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/jstm_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/jstm_streamfile.h new file mode 100644 index 000000000..e28e5a6bb --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/jstm_streamfile.h @@ -0,0 +1,51 @@ +#ifndef _JSTM_STREAMFILE_H_ +#define _JSTM_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + off_t start_offset; +} jstm_decryption_data; + +static size_t jstm_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, jstm_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + if (offset+i >= data->start_offset) { + dest[i] = dest[i] ^ 0x5A; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_jstm_streamfile(STREAMFILE *streamFile, off_t start_offset) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + jstm_decryption_data io_data = {0}; + size_t io_data_size = sizeof(jstm_decryption_data); + + /* setup decryption */ + io_data.start_offset = start_offset; + + + /* setup custom streamfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, jstm_decryption_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _JSTM_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c index 02f36a47a..8b66a8f1f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c @@ -32,53 +32,79 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitLE(0x30, streamFile); vgmstream->sample_rate = read_32bitLE(0x2c, streamFile); + vgmstream->num_samples = read_32bitLE(0x30, streamFile); vgmstream->loop_start_sample = read_32bitLE(0x34, streamFile); vgmstream->loop_end_sample = vgmstream->loop_start_sample + loop_length; vgmstream->meta_type = meta_KTSS; start_offset = read_32bitLE(0x24, streamFile) + 0x20; switch (codec_id) { - case 0x2: /* DSP ADPCM - Hyrule Warriors, Fire Emblem Warriors, and other Koei Tecmo games */ - /* check type details */ - version = read_8bit(0x22, streamFile); - if (version == 1) { - coef_start_offset = 0x40; - coef_spacing = 0x2e; - } - else if (version == 3) { // Fire Emblem Warriors (Switch) - coef_start_offset = 0x5c; - coef_spacing = 0x60; - } - else - goto fail; + case 0x2: /* DSP ADPCM - Hyrule Warriors, Fire Emblem Warriors, and other Koei Tecmo games */ + /* check type details */ + version = read_8bit(0x22, streamFile); + if (version == 1) { + coef_start_offset = 0x40; + coef_spacing = 0x2e; + } + else if (version == 3) { // Fire Emblem Warriors (Switch) + coef_start_offset = 0x5c; + coef_spacing = 0x60; + } + else + goto fail; - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x8; - dsp_read_coefs_le(vgmstream, streamFile, coef_start_offset, coef_spacing); - break; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x8; + dsp_read_coefs_le(vgmstream, streamFile, coef_start_offset, coef_spacing); + break; #ifdef VGM_USE_FFMPEG - case 0x9: /* Opus - Dead or Alive Xtreme 3: Scarlet */ - data_size = read_32bitLE(0x44, streamFile); - { - vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate); + case 0x9: { /* Opus - Dead or Alive Xtreme 3: Scarlet, Fire Emblem: Three Houses */ + opus_config cfg = {0}; + + data_size = read_32bitLE(0x44, streamFile); + + cfg.channels = vgmstream->channels; + cfg.skip = read_32bitLE(0x58, streamFile); + cfg.sample_rate = vgmstream->sample_rate; /* also at 0x54 */ + + /* this info seems always included even for stereo streams */ + if (vgmstream->channels <= 8) { + int i; + cfg.stream_count = read_8bit(0x5a,streamFile); + cfg.coupled_count = read_8bit(0x5b,streamFile); + for (i = 0; i < vgmstream->channels; i++) { + cfg.channel_mapping[i] = read_8bit(0x5c + i,streamFile); + } + } + + vgmstream->codec_data = init_ffmpeg_switch_opus_config(streamFile, start_offset, data_size, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data); + /* apparently KTSS doesn't need standard Opus reordering, so we undo their thing */ + switch(vgmstream->channels) { + case 6: { + /* FL FC FR BL LFE BR > FL FR FC LFE BL BR */ + int channel_remap[] = { 0, 2, 2, 3, 3, 5 }; + ffmpeg_set_channel_remapping(vgmstream->codec_data, channel_remap); + break; + } + default: + break; + } if (vgmstream->num_samples == 0) { vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip; } + break; } - break; - - default: - goto fail; #endif + default: + goto fail; } if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 7789dd098..edf0eefcd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -31,7 +31,6 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile); @@ -48,8 +47,10 @@ VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_dsp_adpx(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_dsp_itl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile); @@ -71,7 +72,7 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_raw_int(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile); @@ -83,17 +84,17 @@ VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_raw(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_raw_pcm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_raw_wavm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile); @@ -202,11 +203,7 @@ VGMSTREAM * init_vgmstream_ps2_xa30(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_musc(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_musx_v004(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_musx_v005(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_musx_v201(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_musx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_leg(STREAMFILE * streamFile); @@ -243,6 +240,7 @@ VGMSTREAM * init_vgmstream_ps2_pcm(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_rkv(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_vas(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ps2_vas_container(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_tec(STREAMFILE * streamFile); @@ -268,9 +266,9 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_xmu(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_xvas(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_bh2pcm(STREAMFILE *streamFile); @@ -286,8 +284,6 @@ VGMSTREAM * init_vgmstream_ps2_omu(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_xa2(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_idsp_ie(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_ymf(STREAMFILE * streamFile); @@ -305,25 +301,7 @@ VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_wii_mus(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd2vag(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd2pcmb(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd2xadp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd3pcm(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd3pcmb(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd3gadp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd3vag(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd4pcmb(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd4pcm(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd4radp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd4vag(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd6vag(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd6wadp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd6xadp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd6radp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd6oogv(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_rsd6xma(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_rsd6at3p(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_rsd6wma(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_rsd(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_dc_asd(STREAMFILE * streamFile); @@ -503,7 +481,7 @@ VGMSTREAM * init_vgmstream_bar(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ffw(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_jstm(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_jstm(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_xvag(STREAMFILE* streamFile); @@ -518,8 +496,6 @@ VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_msf(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ps3_past(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_sgxd(STREAMFILE* streamFile); @@ -540,7 +516,7 @@ VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_vawx(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_pc_snds(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_wmus(STREAMFILE* streamFile); @@ -612,8 +588,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_x360_pasx(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile); @@ -637,9 +611,9 @@ VGMSTREAM * init_vgmstream_mta2_container(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_ulw(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_xa_04sw(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile); @@ -648,6 +622,7 @@ VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE * steeamFile); VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE * steeamFile); @@ -668,6 +643,9 @@ VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_sqex(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile); @@ -857,10 +835,38 @@ VGMSTREAM * init_vgmstream_fsb5_fev_bank(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_bwav(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_awb(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE * streamFile, STREAMFILE *acbFile); + +VGMSTREAM * init_vgmstream_acb(STREAMFILE * streamFile); +void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int is_memory); VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_smk(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_mzrt(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_psf_single(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_sch(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ima(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_nub(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_xmv_valve(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_bmp_konami(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msf.c b/Frameworks/vgmstream/vgmstream/src/meta/msf.c index 8da863fda..e99475985 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/msf.c @@ -99,38 +99,27 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { case 0x04: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) [Silent Hill HD (PS3)] */ case 0x05: /* ATRAC3 mid (105 kbps, frame size 152) [Atelier Rorona (PS3)] */ case 0x06: { /* ATRAC3 high (132 kbps, frame size 192) [Tekken Tag Tournament HD (PS3)] */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - int32_t bytes, block_size, encoder_delay, joint_stereo; - - block_size = (codec==4 ? 0x60 : (codec==5 ? 0x98 : 0xC0)) * vgmstream->channels; - joint_stereo = (codec==4); /* interleaved joint stereo (ch must be even) */ + int block_align, encoder_delay; /* MSF skip samples: from tests with MSEnc and real files (ex. TTT2 eddy.msf v43, v01 demos) seems like 1162 is consistent. * Atelier Rorona bt_normal01 needs it to properly skip the beginning garbage but usually doesn't matter. * (note that encoder may add a fade-in with looping/resampling enabled but should be skipped) */ - encoder_delay = 1162; - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_size) - encoder_delay; + encoder_delay = 1024 + 69*2; + block_align = (codec==4 ? 0x60 : (codec==5 ? 0x98 : 0xC0)) * vgmstream->channels; + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; if (vgmstream->sample_rate == 0xFFFFFFFF) /* some MSFv1 (Digi World SP) */ vgmstream->sample_rate = 44100; /* voice tracks seems to use 44khz, not sure about other tracks */ - bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - - /* manually set skip_samples if FFmpeg didn't do it */ - if (ffmpeg_data->skipSamples <= 0) { - ffmpeg_set_skip_samples(ffmpeg_data, encoder_delay); - } - - /* MSF loop/sample values are offsets so trickier to adjust the skip_samples but this seems correct */ + /* MSF loop/sample values are offsets so trickier to adjust but this seems correct */ if (loop_flag) { - vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_size) /* - encoder_delay*/; - vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_size) - encoder_delay; + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; } break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/musx.c b/Frameworks/vgmstream/vgmstream/src/meta/musx.c index b3f211fdd..9a7dd977b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/musx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/musx.c @@ -1,224 +1,131 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form; +typedef enum { PSX, DSP, XBOX, IMA, DAT } musx_codec; +typedef struct { + int big_endian; + int version; + size_t file_size; -/* MUSX (Version 004) */ -VGMSTREAM * init_vgmstream_musx_v004(STREAMFILE *streamFile) { + int total_subsongs; + int is_old; + + off_t tables_offset; + off_t loops_offset; + off_t stream_offset; + size_t stream_size; + off_t coefs_offset; + + musx_form form; + musx_codec codec; + uint32_t platform; + + int channels; + int sample_rate; + int loop_flag; + int32_t loop_start; + int32_t loop_end; + int32_t num_samples; + int32_t loop_start_sample; + int32_t loop_end_sample; +} musx_header; + +static int parse_musx(STREAMFILE *streamFile, musx_header *musx); + +/* MUSX - from Eurocom's games */ +VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int loop_flag, channel_count; + musx_header musx = {0}; /* checks */ - if (!check_extensions(streamFile, "musx")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; - if (read_32bitBE(0x08,streamFile) != 0x04000000) - goto fail; - - loop_flag = (read_32bitLE(0x840,streamFile) != 0xFFFFFFFF); - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - switch (read_32bitBE(0x10,streamFile)) { - case 0x5053325F: /* PS2_ */ - start_offset = read_32bitLE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x80; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; - } - break; - case 0x47435F5F: /* GC__ */ - start_offset = read_32bitBE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_DAT4_IMA; - vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))/16/channel_count*28; - } - break; - case 0x58425F5F: /* XB__ */ - start_offset = read_32bitLE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_DAT4_IMA; - vgmstream->num_samples = (read_32bitLE(0x2C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; - } - break; - default: - goto fail; - } - - vgmstream->meta_type = meta_MUSX_V004; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/* MUSX (Version 005) [Predator: Concrete Jungle (PS2/Xbox) ] */ -VGMSTREAM * init_vgmstream_musx_v005(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - - /* checks */ - /* .sfx: Batman Begins, .musx: header id */ - if (!check_extensions(streamFile, "musx,sfx")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; - if (read_32bitBE(0x08,streamFile) != 0x05000000) - goto fail; - - loop_flag = (read_32bitLE(0x840,streamFile) != 0xFFFFFFFF); - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - switch (read_32bitBE(0x10,streamFile)) { - case 0x5053325F: /* PS2_ */ - start_offset = read_32bitLE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x80; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; - } - break; - case 0x47435F5F: /* GC__ */ - start_offset = read_32bitBE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_DAT4_IMA; - vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))/16/channel_count*28; - } - break; - case 0x58425F5F: /* XB__ */ - start_offset = read_32bitLE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_DAT4_IMA; - vgmstream->num_samples = (read_32bitLE(0x2C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; - } - break; - default: - goto fail; - } - - vgmstream->meta_type = meta_MUSX_V005; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/* MUSX (Version 006) [Batman Begins (GC)] */ -VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - - /* checks */ - /* .sfx: Batman Begins, .musx: header id */ + /* .sfx: actual extension (extracted from bigfiles with sometimes encrypted names) + * .musx: header id */ if (!check_extensions(streamFile, "sfx,musx")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ goto fail; - if (read_32bitBE(0x08,streamFile) != 0x06000000) + + if (!parse_musx(streamFile, &musx)) goto fail; - loop_flag = (read_32bitLE(0x840,streamFile)!=0xFFFFFFFF); - channel_count = 2; - //todo some files (ex. Batman Begins) have a subsong table at 0x800, not sure what signals it (flags at 0x04/0x14?) + start_offset = musx.stream_offset; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(musx.channels, musx.loop_flag); if (!vgmstream) goto fail; - switch (read_32bitBE(0x10,streamFile)) { - case 0x5053325F: /* PS2_ */ - start_offset = read_32bitLE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; + vgmstream->meta_type = meta_MUSX; + vgmstream->sample_rate = musx.sample_rate; + vgmstream->num_streams = musx.total_subsongs; + vgmstream->stream_size = musx.stream_size; + + switch (musx.codec) { + case PSX: + vgmstream->num_samples = ps_bytes_to_samples(musx.stream_size, musx.channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(musx.loop_start, musx.channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(musx.loop_end, musx.channels); + vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))*28/16/channel_count; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x80; - vgmstream->meta_type = meta_MUSX_V006; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))*28/16/channel_count; - vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))*28/16/channel_count; - } break; - case 0x47435F5F: /* GC__ */ - start_offset = read_32bitBE(0x28,streamFile); - if (start_offset == 0 || start_offset == 0xABABABAB) goto fail; /* some are empty */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; + case DAT: + vgmstream->num_samples = dat4_ima_bytes_to_samples(musx.stream_size, musx.channels); + vgmstream->loop_start_sample = dat4_ima_bytes_to_samples(musx.loop_start, musx.channels); + vgmstream->loop_end_sample = dat4_ima_bytes_to_samples(musx.loop_end, musx.channels); + vgmstream->coding_type = coding_DAT4_IMA; - vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))*28/16/channel_count; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x20; - vgmstream->meta_type = meta_MUSX_V006; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))*28/16/channel_count; - vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))*28/16/channel_count; - } break; + + case IMA: + vgmstream->num_samples = ima_bytes_to_samples(musx.stream_size, musx.channels); + vgmstream->loop_start_sample = musx.loop_start / 4; /* weird but needed */ + vgmstream->loop_end_sample = musx.loop_end / 4; + + vgmstream->coding_type = coding_DVI_IMA_int; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + break; + + case XBOX: + vgmstream->num_samples = xbox_ima_bytes_to_samples(musx.stream_size, musx.channels); + vgmstream->loop_start_sample = xbox_ima_bytes_to_samples(musx.loop_start, musx.channels); + vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(musx.loop_end, musx.channels); + + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + break; + + case DSP: + vgmstream->num_samples = dsp_bytes_to_samples(musx.stream_size, musx.channels); + vgmstream->loop_start_sample = dsp_bytes_to_samples(musx.loop_start, musx.channels); + vgmstream->loop_end_sample = dsp_bytes_to_samples(musx.loop_end, musx.channels); + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; + + dsp_read_coefs(vgmstream,streamFile,musx.coefs_offset+0x1c, 0x60, musx.big_endian); + dsp_read_hist(vgmstream,streamFile,musx.coefs_offset+0x40, 0x60, musx.big_endian); + break; + default: goto fail; } + if (musx.num_samples) + vgmstream->num_samples = musx.num_samples; + if (musx.loop_flag && musx.loop_start_sample) + vgmstream->loop_start_sample = musx.loop_start_sample; + if (musx.loop_flag && musx.loop_end_sample) + vgmstream->loop_end_sample = musx.loop_end_sample; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; @@ -230,258 +137,416 @@ fail: } -/* MUSX (Version 010) [Dead Space: Extraction (Wii), Rio (PS3), Pirates of the Caribbean: At World's End (PSP)] */ -VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - int musx_type; /* determining the decoder by strings like "PS2_", "GC__" and so on */ - //int musx_version; /* 0x08 provides a "version" byte */ - int loop_flag = 0; - int channel_count; +static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int default_channels, default_sample_rate; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("musx",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; - if (read_32bitBE(0x800,streamFile) == 0x53424E4B) /* "SBNK", */ // SoundBank, refuse - goto fail; - if (read_32bitBE(0x08,streamFile) != 0x0A000000) /* "0x0A000000" */ - goto fail; - - loop_flag = ((read_32bitLE(0x34,streamFile)!=0x00000000) && - (read_32bitLE(0x34,streamFile)!=0xABABABAB)); - channel_count = 2; - - musx_type=(read_32bitBE(0x10,streamFile)); - - if (musx_type == 0x5749495F && /* WII_ */ - (read_16bitBE(0x40,streamFile) == 0x4441) && /* DA */ - (read_8bit(0x42,streamFile) == 0x54)) /* T */ - { - channel_count = read_32bitLE(0x48,streamFile); - loop_flag = (read_32bitLE(0x64,streamFile) != -1); - } - if (musx_type == 0x5053335F && /* PS3_ */ - (read_16bitBE(0x40,streamFile) == 0x4441) && /* DA */ - (read_8bit(0x42,streamFile) == 0x54)) /* T */ - { - channel_count = read_32bitLE(0x48,streamFile); - loop_flag = (read_32bitLE(0x64,streamFile) != -1); - } - if (0x58455F5F == musx_type) /* XE__ */ - { - loop_flag = 0; + if (musx->big_endian) { + read_32bit = read_32bitBE; + } else { + read_32bit = read_32bitLE; } - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - /* fill in the vital statistics */ - switch (musx_type) { - case 0x5053325F: /* PS2_ */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x40,streamFile); - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x80; - vgmstream->meta_type = meta_MUSX_V010; - if (loop_flag) - { - vgmstream->loop_start_sample = read_32bitLE(0x44,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x40,streamFile); + /* autodetect for older versions that have no info */ + if (musx->platform == 0) { + if (musx->big_endian) { + musx->platform = 0x47433032; /* "GC02" (fake) */ + } + else { + off_t offset = musx->stream_offset; + size_t max = 0x5000; + if (max > musx->stream_size) + max = musx->stream_size; + + if (ps_check_format(streamFile, offset, max)) { + musx->platform = 0x5053325F; /* "PS2_" */ + } else { + musx->platform = 0x58423032; /* "XB02" (fake) */ } - break; - case 0x5053505F: /* PSP_ */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32768; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0xC,streamFile))*28/32; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x80; - vgmstream->meta_type = meta_MUSX_V010; - break; - case 0x5053335F: /* PS3_ */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->coding_type = coding_DAT4_IMA; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - vgmstream->meta_type = meta_MUSX_V010; + } + } - if (read_32bitBE(0x40,streamFile)==0x44415438){ - vgmstream->num_samples = read_32bitLE(0x60,streamFile); - vgmstream->sample_rate = read_32bitLE(0x4C,streamFile); - if (loop_flag) - { - vgmstream->loop_start_sample = read_32bitLE(0x64,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x60,streamFile); + + /* defaults */ + switch(musx->platform) { + + case 0x5053325F: /* "PS2_" */ + default_channels = 2; + default_sample_rate = 32000; + musx->codec = PSX; + break; + + case 0x47435F5F: /* "GC__" */ + default_channels = 2; + default_sample_rate = 32000; + musx->codec = DAT; + break; + + case 0x47433032: /* "GC02" */ + default_channels = 2; + default_sample_rate = 32000; + if (musx->coefs_offset) + musx->codec = DSP; + else + musx->codec = IMA; + break; + + case 0x58425F5F: /* "XB__" */ + default_channels = 2; + default_sample_rate = 44100; + musx->codec = DAT; + break; + + case 0x58423032: /* "XB02" */ + default_channels = 2; + default_sample_rate = 44100; + musx->codec = XBOX; + break; + + case 0x5053505F: /* "PSP_" */ + default_channels = 2; + default_sample_rate = 32768; + musx->codec = PSX; + break; + + case 0x5749495F: /* "WII_" */ + default_channels = 2; + default_sample_rate = 44100; + musx->codec = DAT; + break; + + case 0x5053335F: /* "PS3_" */ + default_channels = 2; + default_sample_rate = 44100; + musx->codec = DAT; + break; + + case 0x58455F5F: /* "XE__" */ + default_channels = 2; + default_sample_rate = 32000; + musx->codec = DAT; + break; + + default: + VGM_LOG("MUSX: unknown platform %x\n", musx->platform); + goto fail; + } + + if (musx->channels == 0) + musx->channels = default_channels; + if (musx->sample_rate == 0) + musx->sample_rate = default_sample_rate; + + + /* parse loops and other info */ + if (musx->tables_offset && musx->loops_offset) { + int i, cues2_count; + off_t cues2_offset; + + /* cue/stream position table thing */ + /* 0x00: cues1 entries (entry size 0x34 or 0x18) + * 0x04: cues2 entries (entry size 0x20 or 0x14) + * 0x08: header size (always 0x14) + * 0x0c: cues2 start + * 0x10: volume? (usually <= 100) */ + + /* find loops (cues1 also seems to have this info but this looks ok) */ + cues2_count = read_32bit(musx->loops_offset+0x04, streamFile); + cues2_offset = musx->loops_offset + read_32bit(musx->loops_offset+0x0c, streamFile); + for (i = 0; i < cues2_count; i++) { + uint32_t type, offset1, offset2; + + if (musx->is_old) { + offset1 = read_32bit(cues2_offset + i*0x20 + 0x04, streamFile); + type = read_32bit(cues2_offset + i*0x20 + 0x08, streamFile); + offset2 = read_32bit(cues2_offset + i*0x20 + 0x14, streamFile); + } else { + offset1 = read_32bit(cues2_offset + i*0x14 + 0x04, streamFile); + type = read_32bit(cues2_offset + i*0x14 + 0x08, streamFile); + offset2 = read_32bit(cues2_offset + i*0x14 + 0x0c, streamFile); } + + /* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */ + if (type == 0x06 || type == 0x07) { + musx->loop_start = offset2; + musx->loop_end = offset1; + musx->loop_flag = 1; + break; + } + } + } + else if (musx->loops_offset && read_32bitBE(musx->loops_offset, streamFile) != 0xABABABAB) { + /* parse loop table (loop starts are -1 if non-looping) + * 0x00: version? + * 0x04: flags? (&1=loops) + * 0x08: loop start offset? + * 0x0c: loop end offset? + * 0x10: loop end sample + * 0x14: loop start sample + * 0x18: loop end offset + * 0x1c: loop start offset */ + musx->loop_end_sample = read_32bitLE(musx->loops_offset+0x10, streamFile); + musx->loop_start_sample = read_32bitLE(musx->loops_offset+0x14, streamFile); + musx->loop_end = read_32bitLE(musx->loops_offset+0x18, streamFile); + musx->loop_start = read_32bitLE(musx->loops_offset+0x1c, streamFile); + musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */ + musx->loop_flag = (musx->loop_start_sample >= 0); + } + + /* fix some v10 sizes */ + if (musx->stream_size == 0) { + off_t offset; + musx->stream_size = musx->file_size - musx->stream_offset; + + /* remove padding */ + offset = musx->stream_offset + musx->stream_size - 0x04; + while (offset > 0) { + if (read_32bit(offset, streamFile) != 0xABABABAB) + break; + musx->stream_size -= 0x04; + offset -= 0x04; + } + } + + return 1; +fail: + return 0; +} + +static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int target_subsong = streamFile->stream_index; + + + /* base header is LE */ + /* 0x04: file ID (referenced in external .h, sometimes files are named HC(id).sfx too) */ + musx->version = read_32bitLE(0x08,streamFile); + musx->file_size = read_32bitLE(0x0c,streamFile); + + + switch(musx->version) { + case 201: /* Sphinx and the Cursed Mummy (PS2), Buffy the Vampire Slayer: Chaos Bleeds (PS2/GC/Xbox) */ + case 1: /* Athens 2004 (PS2) */ + musx->platform = 0; /* guess later */ + musx->tables_offset = 0x10; + musx->big_endian = guess_endianness32bit(0x10, streamFile); + musx->is_old = 1; + break; + + case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC) */ + case 5: /* Predator: Concrete Jungle (PS2/Xbox), Robots (GC) */ + case 6: /* Batman Begins (GC), Ice Age 2 (PS2) */ + musx->platform = read_32bitBE(0x10,streamFile); + /* 0x14: some id/hash? */ + /* 0x18: platform number? */ + /* 0x1c: null */ + musx->tables_offset = 0x20; + musx->big_endian = (musx->platform == 0x47435F5F); /* "GC__" */ + break; + + case 10: /* 007: Quantum of Solace (PS2), Pirates of the Caribbean: At World's End (PSP), GoldenEye 007 (Wii), Rio (PS3) */ + musx->platform = read_32bitBE(0x10,streamFile); + /* 0x14: null */ + /* 0x18: platform number? */ + /* 0x1c: null */ + /* 0x20: hash */ + musx->tables_offset = 0; /* no tables */ + musx->big_endian = (musx->platform == 0x5749495F || musx->platform == 0x5053335F); /* "GC__" / "PS3_" (only after header) */ + break; + + default: + goto fail; + } + + if (musx->big_endian) { + read_32bit = read_32bitBE; + } else { + read_32bit = read_32bitLE; + } + + + /* MUSX has multiple forms which seem external/implicit info so we need some crude autodetection */ + if (musx->tables_offset) { + off_t table1_offset, table2_offset /*, table3_offset, table4_offset*/; + size_t /*table1_size, table2_size, table3_size,*/ table4_size; + + table1_offset = read_32bit(musx->tables_offset+0x00, streamFile); + //table1_size = read_32bit(musx->tables_offset+0x04, streamFile); + table2_offset = read_32bit(musx->tables_offset+0x08, streamFile); + //table2_size = read_32bit(musx->tables_offset+0x0c, streamFile); + //table3_offset = read_32bit(musx->tables_offset+0x10, streamFile); + //table3_size = read_32bit(musx->tables_offset+0x14, streamFile); + //table4_offset = read_32bit(musx->tables_offset+0x18, streamFile); + table4_size = read_32bit(musx->tables_offset+0x1c, streamFile); + + if (table2_offset == 0 || table2_offset == 0xABABABAB) { + /* possibly a collection of IDs */ + goto fail; + } + else if (table4_size != 0 && table4_size != 0xABABABAB) { + /* sfx banks have table1=id table? table2=header table, table=DSP coefs table (optional), table4=data */ + musx->form = SFX_BANK; + } + else if (read_32bit(table1_offset+0x00, streamFile) < 0x9 && + read_32bit(table1_offset+0x08, streamFile) == 0x14 && + read_32bit(table1_offset+0x10, streamFile) <= 100) { + /* streams have a info table with a certain format */ + musx->form = MFX; + } + else if (read_32bit(table1_offset+0x00, streamFile) == 0 && + read_32bit(table1_offset+0x04, streamFile) > read_32bit(table1_offset+0x00, streamFile) && + read_32bit(table1_offset+0x08, streamFile) > read_32bit(table1_offset+0x04, streamFile)) { + /* a list of offsets starting from 0, each stream then has info table at offset */ + musx->form = MFX_BANK; + } + else { + goto fail; + } + } + else { + if (read_32bitBE(0x800,streamFile) == 0x53424E4B) { /* "SBNK" */ + /* SFX_BANK-like sound bank found on v10 Wii games */ + musx->form = SBNK; + } + else if (read_32bitBE(0x800,streamFile) == 0x464F524D) { /* "FORM" */ + /* RIFF-like sound bank found on v10 PSP games */ + musx->form = FORM; + } + else if (read_32bitBE(0x800,streamFile) == 0x45535044) { /* "ESPD" */ + /* projectdetails.sfx */ + goto fail; + } + else { + musx->form = MFX; + } + } + + + /* parse known forms */ + switch(musx->form) { + case MFX: + + if (musx->tables_offset) { + musx->loops_offset = read_32bit(musx->tables_offset+0x00, streamFile); + + musx->stream_offset = read_32bit(musx->tables_offset+0x08, streamFile); + musx->stream_size = read_32bit(musx->tables_offset+0x0c, streamFile); } else { - vgmstream->sample_rate = 44100; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/2/(0x20)*((0x20-4)*2); - if (loop_flag) - { - vgmstream->loop_start_sample = read_32bitLE(0x44,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x40,streamFile); - } - } - break; - case 0x5749495F: /* WII_ */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x4C,streamFile); - switch (read_32bitBE(0x40,streamFile)) - { - case 0x44415434: /* DAT4 */ - case 0x44415438: /* DAT8 [GoldenEye 007 (Wii)] */ - vgmstream->coding_type = coding_DAT4_IMA; - break; - default: - goto fail; - } - vgmstream->num_samples = read_32bitLE(0x60,streamFile); - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - vgmstream->meta_type = meta_MUSX_V010; - if (loop_flag) - { - vgmstream->loop_start_sample = read_32bitLE(0x64,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x60,streamFile); + if (read_32bitBE(0x30, streamFile) != 0xABABABAB) { + uint32_t miniheader = read_32bitBE(0x40, streamFile); + switch(miniheader) { + case 0x44415434: /* "DAT4" */ + case 0x44415438: /* "DAT8" */ + /* found on PS3/Wii (but not always?) */ + musx->stream_size = read_32bitLE(0x44, streamFile); + musx->channels = read_32bitLE(0x48, streamFile); + musx->sample_rate = read_32bitLE(0x4c, streamFile); + musx->loops_offset = 0x50; + break; + default: + musx->loops_offset = 0x30; + break; + } + } + + musx->stream_offset = 0x800; + musx->stream_size = 0; /* read later */ } + if (!parse_musx_stream(streamFile, musx)) + goto fail; break; - case 0x58455F5F: /* XE__ */ - start_offset = 0x800; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_DAT4_IMA; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/2/(0x20)*((0x20-4)*2); - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - vgmstream->meta_type = meta_MUSX_V010; - + + case MFX_BANK: { + off_t target_offset, base_offset, data_offset; + + musx->total_subsongs = read_32bit(musx->tables_offset+0x04, streamFile) / 0x04; /* size */ + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail; + + base_offset = read_32bit(musx->tables_offset+0x00, streamFile); + data_offset = read_32bit(musx->tables_offset+0x08, streamFile); + + target_offset = read_32bit(base_offset + (target_subsong - 1)*0x04, streamFile) + data_offset; + + /* 0x00: id? */ + musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; + musx->stream_size = read_32bit(target_offset + 0x08, streamFile); + musx->loops_offset = target_offset + 0x0c; + + /* this looks correct for PS2 and Xbox, not sure about GC */ + musx->channels = 1; + musx->sample_rate = 22050; + + if (!parse_musx_stream(streamFile, musx)) + goto fail; break; + } + + case SFX_BANK: { + off_t target_offset, head_offset, coef_offset, data_offset; + size_t coef_size; + + //unk_offset = read_32bit(musx->tables_offset+0x00, streamFile); /* ids and cue-like config? */ + head_offset = read_32bit(musx->tables_offset+0x08, streamFile); + coef_offset = read_32bit(musx->tables_offset+0x10, streamFile); + coef_size = read_32bit(musx->tables_offset+0x14, streamFile); + data_offset = read_32bit(musx->tables_offset+0x18, streamFile); + + musx->total_subsongs = read_32bit(head_offset+0x00, streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail; + + + if (musx->is_old) { + target_offset = head_offset + 0x04 + (target_subsong - 1)*0x28; + + /* 0x00: flag? */ + musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; + musx->stream_size = read_32bit(target_offset + 0x08, streamFile); + musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile); + /* 0x10: size? */ + /* 0x14: channels? */ + /* 0x18: always 4? */ + musx->coefs_offset = read_32bit(target_offset + 0x1c, streamFile) + coef_offset; /* may be set even for non-GC */ + /* 0x20: null? */ + /* 0x24: sub-id? */ + musx->channels = 1; + } + else { + target_offset = head_offset + 0x04 + (target_subsong - 1)*0x20; + + /* 0x00: flag? */ + musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; + musx->stream_size = read_32bit(target_offset + 0x08, streamFile); + musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile); + /* 0x10: size? */ + musx->coefs_offset = read_32bit(target_offset + 0x14, streamFile) + coef_offset; /* may be set even for non-GC */ + /* 0x18: null? */ + /* 0x1c: sub-id? */ + musx->channels = 1; + } + + + VGM_LOG("to=%lx, %lx, %x\n", target_offset, musx->stream_offset, musx->stream_size); + + if (coef_size == 0) + musx->coefs_offset = 0; /* disable for later detection */ + + if (!parse_musx_stream(streamFile, musx)) + goto fail; + break; + } + default: + VGM_LOG("MUSX: unknown form\n"); goto fail; } - /* 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; + return 1; fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/* MUSX (Version 201) */ -VGMSTREAM * init_vgmstream_musx_v201(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - //int musx_version; /* 0x08 provides a "version" byte */ - int loop_flag; - int channel_count; - int loop_detect; - int loop_offsets; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("musx",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; - if ((read_32bitBE(0x08,streamFile) != 0xC9000000) && - (read_32bitLE(0x08,streamFile) != 0xC9000000)) /* "0xC9000000" */ - goto fail; - - channel_count = 2; - - loop_detect = read_32bitBE(0x800,streamFile); - switch (loop_detect) { - case 0x02000000: - loop_offsets = 0x8E0; - break; - case 0x03000000: - loop_offsets = 0x880; - break; - case 0x04000000: - loop_offsets = 0x8B4; - break; - case 0x05000000: - loop_offsets = 0x8E8; - break; - case 0x06000000: - loop_offsets = 0x91C; - break; - default: - goto fail; - } - - loop_flag = (read_32bitLE(loop_offsets+0x04,streamFile) !=0x00000000); - start_offset = read_32bitLE(0x18,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - { - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(loop_offsets,streamFile)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(loop_offsets+0x10,streamFile)*28/16/channel_count; - vgmstream->loop_end_sample = read_32bitLE(loop_offsets,streamFile)*28/16/channel_count; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x80; - vgmstream->meta_type = meta_MUSX_V201; - } - - /* 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: - close_vgmstream(vgmstream); - return NULL; + return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mzrt.c b/Frameworks/vgmstream/vgmstream/src/meta/mzrt.c new file mode 100644 index 000000000..6acb62500 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/mzrt.c @@ -0,0 +1,144 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "mzrt_streamfile.h" + + +/* mzrt - idTech "4.5" audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */ +VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count, codec, sample_rate, block_size = 0, bps = 0, num_samples; + STREAMFILE *temp_streamFile = NULL; + + + /* checks */ + if (!check_extensions(streamFile, "idwav,idmsf,idxma")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x6D7A7274) /* "mzrt" */ + goto fail; + + /* this format is bizarrely mis-aligned (and mis-designed too) */ + + num_samples = read_32bitBE(0x11,streamFile); + codec = read_16bitLE(0x15,streamFile); + switch(codec) { + case 0x0001: + case 0x0002: + case 0x0166: + channel_count = read_16bitLE(0x17,streamFile); + sample_rate = read_32bitLE(0x19, streamFile); + block_size = read_16bitLE(0x21, streamFile); + bps = read_16bitLE(0x23,streamFile); + + start_offset = 0x25; + break; + + case 0x0000: + sample_rate = read_32bitBE(0x1D, streamFile); + channel_count = read_32bitBE(0x21, streamFile); + + start_offset = 0x29; + break; + + default: + goto fail; + } + + /* skip fmt-extra data */ + if (codec == 0x0002 || codec == 0x0166) { + start_offset += 0x02 + read_16bitLE(start_offset, streamFile); + } + + /* skip unknown table */ + if (codec == 0x0000) { + start_offset += 0x04 + read_32bitBE(start_offset, streamFile) * 0x04; + + } + + /* skip unknown table */ + start_offset += 0x04 + read_32bitBE(start_offset, streamFile); + + /* skip block info */ + if (codec != 0x0000) { + /* 0x00: de-blocked size + * 0x04: block count*/ + start_offset += 0x08; + + /* idwav only uses 1 super-block though */ + temp_streamFile = setup_mzrt_streamfile(streamFile, start_offset); + if (!temp_streamFile) goto fail; + } + else { + /* 0x00: de-blocked size */ + start_offset += 0x04; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MZRT; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + switch(codec) { + case 0x0001: + if (bps != 16) goto fail; + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channel_count; + break; + + case 0x0002: + if (bps != 4) goto fail; + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = block_size; + break; + +#ifdef VGM_USE_FFMPEG + case 0x0166: { + uint8_t buf[0x100]; + int bytes; + size_t stream_size = get_streamfile_size(temp_streamFile); + + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, streamFile, 0); + vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples_hb(vgmstream, streamFile, temp_streamFile, 0x00,stream_size, 0x15, 0,0); + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x0000: { + mpeg_custom_config cfg = {0}; + + cfg.skip_samples = 576; /* assumed */ + + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + break; + } +#endif + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream,temp_streamFile == NULL ? streamFile : temp_streamFile,temp_streamFile == NULL ? start_offset : 0x00)) + goto fail; + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mzrt_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/mzrt_streamfile.h new file mode 100644 index 000000000..f9483d37c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/mzrt_streamfile.h @@ -0,0 +1,124 @@ +#ifndef _MZRT_STREAMFILE_H_ +#define _MZRT_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + off_t stream_offset; + size_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 */ + + size_t logical_size; +} mzrt_io_data; + + +static size_t mzrt_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, mzrt_io_data* data) { + size_t total_read = 0; + + + /* re-start when previous offset (can't map logical<>physical offsets) */ + if (data->logical_offset < 0 || offset < data->logical_offset) { + data->physical_offset = data->stream_offset; + data->logical_offset = 0x00; + data->data_size = 0; + } + + /* read blocks */ + while (length > 0) { + + /* ignore EOF */ + if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) { + break; + } + + /* process new block */ + if (data->data_size == 0) { + /* 0x00: samples in this block */ + data->data_size = read_32bitBE(data->stream_offset + 0x04, streamfile); + data->skip_size = 0x08; + data->block_size = data->skip_size + data->data_size; + } + + /* move to next block */ + if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_size = 0; + continue; + } + + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_offset; + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + } + + return total_read; +} + +static size_t mzrt_io_size(STREAMFILE *streamfile, mzrt_io_data* data) { + uint8_t buf[1]; + + if (data->logical_size) + return data->logical_size; + + /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ + mzrt_io_read(streamfile, buf, 0x7FFFFFFF, 1, data); + data->logical_size = data->logical_offset; + + return data->logical_size; +} + +/* Handles deinterleaving of MZRT blocked streams */ +static STREAMFILE* setup_mzrt_streamfile(STREAMFILE *streamFile, off_t stream_offset) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + mzrt_io_data io_data = {0}; + size_t io_data_size = sizeof(mzrt_io_data); + + io_data.stream_offset = stream_offset; + io_data.stream_size = get_streamfile_size(streamFile) - stream_offset; + io_data.logical_offset = -1; /* force phys offset reset */ + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, mzrt_io_read,mzrt_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _MZRT_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/naomi_adpcm.c b/Frameworks/vgmstream/vgmstream/src/meta/naomi_adpcm.c index 27acbeeeb..bacafb069 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/naomi_adpcm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/naomi_adpcm.c @@ -29,7 +29,7 @@ VGMSTREAM * init_vgmstream_naomi_adpcm(STREAMFILE *streamFile) { vgmstream->sample_rate = 44100; vgmstream->num_samples = yamaha_bytes_to_samples(data_size, channel_count); - vgmstream->coding_type = coding_YAMAHA_int; + vgmstream->coding_type = coding_AICA_int; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = data_size / channel_count; vgmstream->meta_type = meta_NAOMI_ADPCM; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/naomi_spsd.c b/Frameworks/vgmstream/vgmstream/src/meta/naomi_spsd.c index 442ad1171..76b1c4ed5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/naomi_spsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/naomi_spsd.c @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_naomi_spsd(STREAMFILE *streamFile) { break; case 0x03: /* standard */ - vgmstream->coding_type = coding_YAMAHA_int; + vgmstream->coding_type = coding_AICA_int; vgmstream->num_samples = yamaha_bytes_to_samples(data_size,channel_count); vgmstream->loop_start_sample = /*read_32bitLE(0x2c,streamFile) +*/ yamaha_bytes_to_samples(0x2000*channel_count,channel_count); vgmstream->loop_end_sample = vgmstream->num_samples; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index 79f2b19b9..e7592a829 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -6,26 +6,25 @@ /* If these variables are packed properly in the struct (one after another) * then this is actually how they are laid out in the file, albeit big-endian */ struct dsp_header { - uint32_t sample_count; - uint32_t nibble_count; - uint32_t sample_rate; - uint16_t loop_flag; - uint16_t format; - uint32_t loop_start_offset; - uint32_t loop_end_offset; - uint32_t ca; - int16_t coef[16]; /* really 8x2 */ - uint16_t gain; - uint16_t initial_ps; - int16_t initial_hist1; - int16_t initial_hist2; - uint16_t loop_ps; - int16_t loop_hist1; - int16_t loop_hist2; - int16_t channel_count; /* DSPADPCM.exe ~v2.7 extension */ - int16_t block_size; - /* padding/reserved up to 0x60 */ - /* DSPADPCM.exe from GC adds some extra data here (uninitialized MSVC memory?) */ + uint32_t sample_count; /* 0x00 */ + uint32_t nibble_count; /* 0x04 */ + uint32_t sample_rate; /* 0x08 */ + uint16_t loop_flag; /* 0x0c */ + uint16_t format; /* 0x0e */ + uint32_t loop_start_offset; /* 0x10 */ + uint32_t loop_end_offset; /* 0x14 */ + uint32_t ca; /* 0x18 */ + int16_t coef[16]; /* 0x1c (really 8x2) */ + uint16_t gain; /* 0x3c */ + uint16_t initial_ps; /* 0x3e */ + int16_t initial_hist1; /* 0x40 */ + int16_t initial_hist2; /* 0x42 */ + uint16_t loop_ps; /* 0x44 */ + int16_t loop_hist1; /* 0x46 */ + int16_t loop_hist2; /* 0x48 */ + int16_t channel_count; /* 0x4a (DSPADPCM.exe ~v2.7 extension) */ + int16_t block_size; /* 0x4c */ + /* padding/reserved up to 0x60, DSPADPCM.exe from GC adds garbage here (uninitialized MSVC memory?) */ }; /* read the above struct; returns nonzero on failure */ @@ -79,6 +78,8 @@ typedef struct { size_t header_spacing; /* distance between DSP header of other channels */ off_t start_offset; /* data start */ size_t interleave; /* distance between data of other channels */ + size_t interleave_first; /* same, in the first block */ + size_t interleave_first_skip; /* extra info */ size_t interleave_last; /* same, in the last block */ meta_t meta_type; @@ -208,6 +209,8 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d if (dspm->interleave == 0 || vgmstream->coding_type == coding_NGC_DSP_subint) vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = dspm->interleave; + vgmstream->interleave_first_block_size = dspm->interleave_first; + vgmstream->interleave_first_skip = dspm->interleave_first_skip; vgmstream->interleave_last_block_size = dspm->interleave_last; { @@ -253,8 +256,9 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { /* checks */ /* .dsp: standard - * .adp: Dr. Muto/Battalion Wars (GC) mono files */ - if (!check_extensions(streamFile, "dsp,adp")) + * .adp: Dr. Muto/Battalion Wars (GC) mono files + * (extensionless): Tony Hawk's Downhill Jam (Wii) */ + if (!check_extensions(streamFile, "dsp,adp,")) goto fail; if (read_dsp_header(&header, 0x00, streamFile)) @@ -414,6 +418,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { vgmstream->meta_type = meta_DSP_STD; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; + vgmstream->allow_dual_stereo = 1; { /* adpcm coeffs/history */ @@ -688,33 +693,6 @@ fail: return NULL; } -/* SWD - PSF chunks + interleaved dsps [Conflict: Desert Storm 1 & 2] */ -VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE *streamFile) { - dsp_meta dspm = {0}; - - /* checks */ - if (!check_extensions(streamFile, "swd")) - goto fail; - - //todo blocked layout when first chunk is 0x50534631 (count + table of 0x0c with offset/sizes) - - if (read_32bitBE(0x00,streamFile) != 0x505346d1) /* PSF\0xd1 */ - goto fail; - - dspm.channel_count = 2; - dspm.max_channels = 2; - - dspm.header_offset = 0x08; - dspm.header_spacing = 0x60; - dspm.start_offset = dspm.header_offset + 0x60 * dspm.channel_count; - dspm.interleave = 0x08; - - dspm.meta_type = meta_NGC_SWD; - return init_vgmstream_dsp_common(streamFile, &dspm); -fail: - return NULL; -} - /* IDSP - Traveller's Tales header + interleaved dsps [Lego Batman (Wii), Lego Dimensions (Wii U)] */ VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { dsp_meta dspm = {0}; @@ -1185,8 +1163,8 @@ fail: return NULL; } -/* .adpcmx - AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile) { +/* ADPY - AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */ +VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile) { dsp_meta dspm = {0}; /* checks */ @@ -1194,6 +1172,7 @@ VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile) { goto fail; if (read_32bitBE(0x00,streamFile) != 0x41445059) /* "ADPY" */ goto fail; + /* 0x04(2): 1? */ /* 0x08: some size? */ /* 0x0c: null */ @@ -1207,7 +1186,36 @@ VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile) { dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; dspm.interleave = 0x08; - dspm.meta_type = meta_DSP_ADPCMX; + dspm.meta_type = meta_DSP_ADPY; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +/* ADPX - AQUASTYLE wrapper [Fushigi no Gensokyo: Lotus Labyrinth (Switch)] */ +VGMSTREAM * init_vgmstream_dsp_adpx(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(streamFile, "adpcmx")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41445058) /* "ADPX" */ + goto fail; + + /* from 0x04 *6 are probably channel sizes, so max would be 6ch; this assumes 2ch */ + if (read_32bitLE(0x04,streamFile) != read_32bitLE(0x08,streamFile) && + read_32bitLE(0x0c,streamFile) != 0) + goto fail; + dspm.channel_count = 2; + dspm.max_channels = 2; + dspm.little_endian = 1; + + dspm.header_offset = 0x1c; + dspm.header_spacing = read_32bitLE(0x04,streamFile); + dspm.start_offset = dspm.header_offset + 0x60; + dspm.interleave = dspm.header_spacing; + + dspm.meta_type = meta_DSP_ADPX; return init_vgmstream_dsp_common(streamFile, &dspm); fail: return NULL; @@ -1246,3 +1254,34 @@ VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile) { fail: return NULL; } + +/* .itl - Incinerator Studios interleaved dsp [Cars Race-o-rama (Wii), MX vs ATV Untamed (Wii)] */ +VGMSTREAM * init_vgmstream_dsp_itl(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + size_t stream_size; + + /* checks */ + /* .itl: standard + * .dsp: default to catch a similar file, not sure which devs */ + if (!check_extensions(streamFile, "itl,dsp")) + goto fail; + + stream_size = get_streamfile_size(streamFile); + dspm.channel_count = 2; + dspm.max_channels = 2; + + dspm.start_offset = 0x60; + dspm.interleave = 0x10000; + dspm.interleave_first_skip = dspm.start_offset; + dspm.interleave_first = dspm.interleave - dspm.interleave_first_skip; + dspm.interleave_last = (stream_size / dspm.channel_count) % dspm.interleave; + dspm.header_offset = 0x00; + dspm.header_spacing = dspm.interleave; + + //todo some files end in half a frame and may click at the very end + //todo when .dsp should refer to Ultimate Board Collection (Wii), not sure about dev + dspm.meta_type = meta_DSP_ITL_i; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nub.c b/Frameworks/vgmstream/vgmstream/src/meta/nub.c new file mode 100644 index 000000000..96e738177 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/nub.c @@ -0,0 +1,557 @@ +#include "meta.h" +#include "../coding/coding.h" + + +static STREAMFILE* make_nub_streamfile(STREAMFILE* streamFile, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext); + +/* .nub - Namco's nu Sound v2 audio container */ +VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t name_offset = 0; + size_t name_size = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + uint32_t version, codec; + const char* fake_ext; + VGMSTREAM*(*init_vgmstream_function)(STREAMFILE *) = NULL; + char name[STREAM_NAME_SIZE] = {0}; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + + + /* checks */ + if (!check_extensions(streamFile, "nub")) + goto fail; + + version = read_32bitBE(0x00,streamFile); + if (version != 0x00020000 && /* v2.0 (rare, ex. Ridge Race 6 (X360)) */ + version != 0x00020100 && /* v2.1 (common) */ + version != 0x01020100) /* same but LE? (seen in PSP games, not PS4) */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x00000000) /* null */ + goto fail; + + /* sometimes LE [Soul Calibur: Broken Destiny (PSP), Tales of Vesperia (PS4) */ + if (guess_endianness32bit(0x10, streamFile)) { + read_32bit = read_32bitBE; + } else{ + read_32bit = read_32bitLE; + } + + /* parse TOC */ + { + off_t offset, data_start, header_start; + off_t header_offset, subheader_start, stream_offset; + size_t header_size, subheader_size, stream_size; + + /* - base header */ + /* 0x08: file id/number (can be 0 = first) */ + total_subsongs = read_32bit(0x0c, streamFile); /* .nub with 0 files do exist */ + data_start = read_32bit(0x10, streamFile); /* exists even with 0 files */ + /* 0x14: data end (may have padding) */ + header_start = read_32bit(0x18, streamFile); + /* 0x1c: header end */ + + /* probably means "header end" in v2.0 */ + if (version == 0x00020000) { + data_start = align_size_to_block(data_start, 0x800); + } + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + offset = read_32bit(header_start + (target_subsong-1)*0x04, streamFile); + + /* .nus have all headers first then all data, but extractors often just paste them together, + * so we'll combine header+data on the fly to make them playable with existing parsers. + * Formats inside .nub don't exist as external files, so they could be extracted in various + * ways that we'll try to match (though BNSF can be found as header+data in some bigfiles too). */ + + header_offset = offset; + + /* - extension (as referenced in companion files with internal filenames, ex. "BGM_MovingDemo1.is14" > "is14") */ + if (version != 0x00020000) + offset += 0x04; /* skip but not in v2.0 */ + + /* - file header */ + /* 0x00: config? */ + /* 0x04: header id/number */ + codec = (uint32_t)read_32bit(offset + 0x08, streamFile); + /* 0x0c: null */ + stream_size = read_32bit(offset + 0x10, streamFile); /* 0x10 aligned */ + stream_offset = read_32bit(offset + 0x14, streamFile) + data_start; + subheader_size = read_32bit(offset + 0x18, streamFile); + /* rest looks like config/volumes/etc */ + + if (version == 0x00020000) + subheader_start = 0xAC; + else + subheader_start = 0xBC; + header_size = align_size_to_block(subheader_start + subheader_size, 0x10); + + switch(codec) { + case 0x00: /* (none) (xma1) */ + fake_ext = "xma"; + init_vgmstream_function = init_vgmstream_nub_xma; + break; + + case 0x01: /* "wav\0" */ + fake_ext = "wav"; + init_vgmstream_function = init_vgmstream_nub_wav; + break; + + case 0x02: /* "vag\0" */ + fake_ext = "vag"; + init_vgmstream_function = init_vgmstream_nub_vag; + break; + + case 0x03: /* "at3\0" */ + fake_ext = "at3"; + init_vgmstream_function = init_vgmstream_nub_at3; + break; + + case 0x04: /* "xma\0" (xma2 old) */ + case 0x08: /* "xma\0" (xma2 new) */ + fake_ext = "xma"; + init_vgmstream_function = init_vgmstream_nub_xma; + break; + + case 0x06: /* "idsp" */ + fake_ext = "idsp"; + init_vgmstream_function = init_vgmstream_nub_idsp; + break; + + case 0x07: /* "is14" */ + fake_ext = "is14"; + init_vgmstream_function = init_vgmstream_nub_is14; + break; + + case 0x05: + default: + VGM_LOG("NUB: unknown codec %x\n", codec); + goto fail; + } + + //;VGM_LOG("NUB: subfile header=%lx + %x, offset=%lx + %x\n", header_offset, header_size, stream_offset, stream_size); + + temp_streamFile = make_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, fake_ext); + if (!temp_streamFile) goto fail; + } + + /* get names */ + { + /* file names are in a companion file, rarely [Noby Noby Boy (PS3)] */ + STREAMFILE *nameFile = NULL; + char filename[PATH_LIMIT]; + char basename[255]; + + get_streamfile_basename(streamFile, basename, sizeof(basename)); + snprintf(filename,sizeof(filename), "nuSound2ToneStr%s.bin", basename); + + nameFile = open_streamfile_by_filename(streamFile, filename); + if (nameFile && read_32bit(0x08, nameFile) == total_subsongs) { + off_t header_start = 0x40; /* first name is bank name */ + char name1[0x20+1] = {0}; + char name2[0x20+1] = {0}; + + name_size = 0x20; + name_offset = header_start + (target_subsong-1)*(name_size*2); + + read_string(name1,name_size, name_offset + 0x00, nameFile); /* internal name */ + read_string(name2,name_size, name_offset + 0x20, nameFile); /* file name */ + //todo some filenames use shift-jis, not sure what to do + + snprintf(name,sizeof(name), "%s/%s", name1,name2); + } + close_streamfile(nameFile); + } + + /* init the VGMSTREAM */ + vgmstream = init_vgmstream_function(temp_streamFile); + if (!vgmstream) goto fail; + + vgmstream->stream_size = get_streamfile_size(temp_streamFile); + vgmstream->num_streams = total_subsongs; + if (name[0] != '\0') + strcpy(vgmstream->stream_name, name); + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +/* *********************************************************** */ + +static STREAMFILE* make_nub_streamfile(STREAMFILE* streamFile, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + STREAMFILE *segment_streamFiles[2] = {0}; + int i; + + + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + segment_streamFiles[0] = new_streamFile; + + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + segment_streamFiles[1] = new_streamFile; + + new_streamFile = open_clamp_streamfile(segment_streamFiles[0], header_offset,header_size); + if (!new_streamFile) goto fail; + segment_streamFiles[0] = new_streamFile; + + new_streamFile = open_clamp_streamfile(segment_streamFiles[1], stream_offset,stream_size); + if (!new_streamFile) goto fail; + segment_streamFiles[1] = new_streamFile; + + new_streamFile = open_multifile_streamfile(segment_streamFiles, 2); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL, fake_ext); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + if (!temp_streamFile) { + for (i = 0; i < 2; i++) { + close_streamfile(segment_streamFiles[i]); + } + } else { + close_streamfile(temp_streamFile); /* closes all segments */ + } + return NULL; +} + +/* *********************************************************** */ + +//todo could be simplified + +/* .nub wav - from Namco NUB archives [Ridge Racer 7 (PS3)] */ +VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "wav,lwav")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x77617600) /* "wav\0" "*/ + goto fail; + + if (read_16bitBE(0xBC+0x00,streamFile) != 0x0001) /* mini "fmt" chunk */ + goto fail; + + loop_flag = read_32bitBE(0x24,streamFile); + channel_count = read_16bitBE(0xBC+0x02,streamFile); + start_offset = 0xD0; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_NUB; + vgmstream->sample_rate = read_32bitBE(0xBC+0x04,streamFile); + vgmstream->num_samples = pcm_bytes_to_samples(read_32bitBE(0x14,streamFile), channel_count, 16); + vgmstream->loop_start_sample = pcm_bytes_to_samples(read_32bitBE(0x20,streamFile), channel_count, 16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(read_32bitBE(0x24,streamFile), channel_count, 16); + + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* .nub vag - from Namco NUB archives [Ridge Racer 7 (PS3), Noby Noby Boy (PS3)] */ +VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile, "vag")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x76616700) /* "vag\0" */ + goto fail; + + loop_flag = read_32bitBE(0x24,streamFile); + channel_count = 1; /* dual file stereo */ + start_offset = 0xC0; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_NUB; + vgmstream->sample_rate = read_32bitBE(0xBC,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(read_32bitBE(0x14,streamFile), channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitBE(0x20,streamFile), channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitBE(0x24,streamFile), channel_count); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_none; + vgmstream->allow_dual_stereo = 1; + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* .nub at3 - from Namco NUB archives [Ridge Racer 7 (PS3), Katamari Forever (PS3)] */ +VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset = 0; + size_t subfile_size = 0; + + + /* checks */ + if (!check_extensions(streamFile,"at3")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x61743300) /* "at3\0" */ + goto fail; + + /* mini fmt+fact header, we can use RIFF anyway */ + subfile_offset = 0x100; + subfile_size = read_32bitLE(subfile_offset + 0x04, streamFile) + 0x08; /* RIFF size */ + + temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +/* .nub xma - from Namco NUB archives [Ridge Racer 6 (X360), Tekken 6 (X360), Galaga Legions DX (X360)] */ +VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, chunk_offset; + size_t data_size, chunk_size, header_size; + int loop_flag, channel_count, sample_rate, nus_codec; + int num_samples, loop_start_sample, loop_end_sample; + + + /* checks */ + if (!check_extensions(streamFile,"xma")) + goto fail; + + if (read_32bitBE(0x00,streamFile) == 0x786D6100) { /* "xma\0" */ + /* nub v2.1 */ + nus_codec = read_32bitBE(0x0C,streamFile); + data_size = read_32bitBE(0x14,streamFile); + header_size = read_32bitBE(0x1c,streamFile); + chunk_offset = 0xBC; + chunk_size = read_32bitBE(0x24,streamFile); + } + else if (read_32bitBE(0x08,streamFile) == 0 && read_32bitBE(0x0c,streamFile) == 0) { + /* nub v2.0 from Ridge Racer 6 */ + nus_codec = read_32bitBE(0x08,streamFile); + data_size = read_32bitBE(0x10,streamFile); + header_size = read_32bitBE(0x18,streamFile); + chunk_offset = 0xAC; + chunk_size = header_size; + } + else { + goto fail; + } + + start_offset = align_size_to_block(chunk_offset + header_size, 0x10); + + if (nus_codec == 0x00) { /* XMA1 "fmt " */ + int loop_start_b, loop_end_b, loop_subframe; + + xma1_parse_fmt_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, 1); + + { + ms_sample_data msd = {0}; + + msd.xma_version = 1; + msd.channels = channel_count; + msd.data_offset = start_offset; + msd.data_size = data_size; + msd.loop_flag = loop_flag; + msd.loop_start_b= loop_start_b; + msd.loop_end_b = loop_end_b; + msd.loop_start_subframe = loop_subframe & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */ + msd.loop_end_subframe = loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ + msd.chunk_offset= chunk_offset; + + xma_get_samples(&msd, streamFile); + + num_samples = msd.num_samples; + loop_start_sample = msd.loop_start_sample; + loop_end_sample = msd.loop_end_sample; + } + } + else if (nus_codec == 0x04) { /* "XMA2" */ + xma2_parse_xma2_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample); + } + else if (nus_codec == 0x08) { /* XMA2 "fmt " */ + channel_count = read_16bitBE(chunk_offset+0x02,streamFile); + sample_rate = read_32bitBE(chunk_offset+0x04,streamFile); + xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1); + } + else { + goto fail; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_NUB; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + +#ifdef VGM_USE_FFMPEG + { + uint8_t buf[0x100]; + size_t bytes; + + if (nus_codec == 0x04) { + bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile); + } else { + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1); + } + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */ + } +#else + goto fail; +#endif + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* .nub idsp - from Namco NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */ +VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + /* checks */ + if ( !check_extensions(streamFile,"idsp") ) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x69647370) /* "idsp" */ + goto fail; + if (read_32bitBE(0xBC,streamFile) != 0x49445350) /* "IDSP" (actual header) */ + goto fail; + + loop_flag = read_32bitBE(0x20,streamFile); + channel_count = read_32bitBE(0xC4,streamFile); + if (channel_count > 8) goto fail; + start_offset = 0x100 + (channel_count * 0x60); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_NUB; + vgmstream->sample_rate = read_32bitBE(0xC8,streamFile); + vgmstream->num_samples = dsp_bytes_to_samples(read_32bitBE(0x14,streamFile),channel_count); + vgmstream->loop_start_sample = read_32bitBE(0xD0,streamFile); + vgmstream->loop_end_sample = read_32bitBE(0xD4,streamFile); + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0xD8,streamFile); + if (vgmstream->interleave_block_size == 0) + vgmstream->interleave_block_size = read_32bitBE(0x14,streamFile) / channel_count; + + dsp_read_coefs_be(vgmstream,streamFile,0x118,0x60); + + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* .nub is14 - from Namco NUB archives [Tales of Vesperia (PS3)] */ +VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t header_offset, stream_offset; + size_t header_size, stream_size, sdat_size; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + + + /* checks */ + if (!check_extensions(streamFile,"is14")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x69733134) /* "is14" */ + goto fail; + + if (guess_endianness32bit(0x1c, streamFile)) { + read_32bit = read_32bitBE; + } else{ + read_32bit = read_32bitLE; + } + + + /* paste header+data together and pass to meta */ + header_offset = 0xBC; + header_size = read_32bit(0x1c, streamFile); + + /* size at 0x14 is padded, find "sdat" size BE (may move around) */ + if (!find_chunk_riff_be(streamFile, 0x73646174, 0xbc+0x0c, header_size - 0x0c, NULL, &sdat_size)) + goto fail; + stream_offset = align_size_to_block(header_offset + header_size, 0x10); + stream_size = sdat_size; + + + temp_streamFile = make_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "bnsf"); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_bnsf(temp_streamFile); + if (!vgmstream) goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nub_idsp.c b/Frameworks/vgmstream/vgmstream/src/meta/nub_idsp.c deleted file mode 100644 index 8ece72d2f..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/nub_idsp.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* "idsp" - from Namco's Wii NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */ -VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - /* checks */ - if ( !check_extensions(streamFile,"idsp") ) - goto fail; - - /* actual header starts at "IDSP", while "idsp" is mostly nub bank stuff */ - if (read_32bitBE(0x00,streamFile) != 0x69647370) /* "idsp" */ - goto fail; - if (read_32bitBE(0xBC,streamFile) != 0x49445350) /* "IDSP" */ - goto fail; - - loop_flag = read_32bitBE(0x20,streamFile); - channel_count = read_32bitBE(0xC4,streamFile); - if (channel_count > 8) goto fail; - - start_offset = 0x100 + (channel_count * 0x60); - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_NUB_IDSP; - vgmstream->sample_rate = read_32bitBE(0xC8,streamFile); - vgmstream->num_samples = dsp_bytes_to_samples(read_32bitBE(0x14,streamFile),channel_count); - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitBE(0xD0,streamFile)); - vgmstream->loop_end_sample = (read_32bitBE(0xD4,streamFile)); - } - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0xD8,streamFile); - if (vgmstream->interleave_block_size == 0) - vgmstream->interleave_block_size = (get_streamfile_size(streamFile) - start_offset) / channel_count; - - dsp_read_coefs_be(vgmstream,streamFile,0x118,0x60); - - 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/nub_vag.c b/Frameworks/vgmstream/vgmstream/src/meta/nub_vag.c deleted file mode 100644 index 6d68746c0..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/nub_vag.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* vag - from Namco's PS3 NUB archives (Ridge Racer 7) */ -VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - - /* check extension, case insensitive */ - if ( !check_extensions(streamFile, "vag")) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x76616700) /* "vag\0" */ - goto fail; - - loop_flag = read_32bitBE(0x30,streamFile)==0x3F800000; - channel_count = 1; /* dual file stereo */ - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitBE(0xBC,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitBE(0x14,streamFile)*28/32*2; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x20,streamFile)*28/32*2; - vgmstream->loop_end_sample = read_32bitBE(0x24,streamFile)*28/32*2; - } - - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_NUB_VAG; - vgmstream->allow_dual_stereo = 1; - - start_offset = 0xC0; - - /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nub_xma.c b/Frameworks/vgmstream/vgmstream/src/meta/nub_xma.c deleted file mode 100644 index 8ce8fa62c..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/nub_xma.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* Namco NUB xma - from Tekken 6, Galaga Legions DX */ -VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, chunk_offset; - size_t data_size, chunk_size; - int loop_flag, channel_count, sample_rate, chunk_type; - int num_samples, loop_start_sample, loop_end_sample; - - - /* checks */ - if ( !check_extensions(streamFile,"xma")) /* (probably meant to be .nub) */ - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x786D6100) /* "xma\0" */ - goto fail; - - - /* custom header with a "XMA2" or "fmt " chunk inside; most other values are unknown */ - chunk_type = read_32bitBE(0xC,streamFile); - start_offset = 0x100; - data_size = read_32bitBE(0x14,streamFile); - chunk_offset = 0xBC; - chunk_size = read_32bitBE(0x24,streamFile); - if (chunk_type == 0x4) { /* "XMA2" */ - xma2_parse_xma2_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample); - } else if (chunk_type == 0x8) { /* "fmt " */ - channel_count = read_16bitBE(chunk_offset+0x02,streamFile); - sample_rate = read_32bitBE(chunk_offset+0x04,streamFile); - xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1); - } else { - goto fail; - } - - - /* 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_sample; - vgmstream->loop_end_sample = loop_end_sample; - vgmstream->meta_type = meta_NUB_XMA; - -#ifdef VGM_USE_FFMPEG - { - uint8_t buf[0x100]; - size_t bytes; - - if (chunk_type == 0x4) { /* "XMA2" */ - bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile); - } else { /* "fmt " */ - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1); - } - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); - if ( !vgmstream->codec_data ) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */ - } -#else - goto fail; -#endif - - 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/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index c7cd4896d..7f90b1798 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -108,12 +108,16 @@ static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v } static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; size_t bytes_read = size*nmemb; + uint8_t key[6] = { 0x23,0x31,0x20,0x2e,0x2e,0x28 }; int i; - /* bytes add 0x23 ('#') */ //todo incorrect, add changes every 0x64 bytes + //todo incorrect, picked value changes (fixed order for all files), or key is bigger + /* bytes add key that changes every 0x64 bytes */ for (i = 0; i < bytes_read; i++) { - ((uint8_t*)ptr)[i] += 0x23; + int pos = (ov_streamfile->offset + i) / 0x64; + ((uint8_t*)ptr)[i] += key[pos % sizeof(key)]; } } @@ -172,6 +176,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { int is_gwm = 0; int is_mus = 0; int is_lse = 0; + int is_bgm = 0; /* check extension */ @@ -201,6 +206,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { is_mus = 1; } else if (check_extensions(streamFile,"lse")) { /* .lse: Labyrinth of Refrain: Coven of Dusk (PC) */ is_lse = 1; + } else if (check_extensions(streamFile,"bgm")) { /* .bgm: Fortissimo (PC) */ + is_bgm = 1; } else { goto fail; } @@ -208,7 +215,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (is_ogg) { if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */ ovmi.decryption_callback = psychic_ogg_decryption_callback; - ovmi.meta_type = meta_OGG_encrypted; } else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */ cfg.is_header_swap = 1; @@ -227,7 +233,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { cfg.is_encrypted = 1; } else if (read_32bitBE(0x00,streamFile) == 0x4f676753) { /* "OggS" (standard) */ - ovmi.meta_type = meta_OGG_VORBIS; + ; } else { goto fail; /* unknown/not Ogg Vorbis (ex. Wwise) */ @@ -238,7 +244,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */ ovmi.decryption_callback = um3_ogg_decryption_callback; } - ovmi.meta_type = meta_OGG_encrypted; } if (is_kovs) { /* Koei Tecmo PC games */ @@ -321,8 +326,15 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (isl_name) { STREAMFILE *islFile = NULL; - //todo could try in ../(file) first since that's how the .isl is stored islFile = open_streamfile_by_filename(streamFile, isl_name); + + if (!islFile) { + /* try in ../(file) too since that's how the .isl is stored on disc */ + char isl_path[PATH_LIMIT]; + snprintf(isl_path, sizeof(isl_path), "../%s", isl_name); + islFile = open_streamfile_by_filename(streamFile, isl_path); + } + if (islFile) { STREAMFILE *dec_sf = NULL; @@ -364,7 +376,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { goto fail; } ovmi.decryption_callback = rpgmvo_ogg_decryption_callback; - ovmi.meta_type = meta_OGG_encrypted; start_offset = 0x10; } @@ -413,13 +424,36 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } } - if (cfg.is_encrypted) { - ovmi.meta_type = meta_OGG_encrypted; + if (is_bgm) { /* [Fortissimo (PC)] */ + size_t file_size = get_streamfile_size(streamFile); + uint8_t key[0x04]; + uint32_t xor_be; + put_32bitLE(key, (uint32_t)file_size); + xor_be = (uint32_t)get_32bitBE(key); + if ((read_32bitBE(0x00,streamFile) ^ xor_be) == 0x4F676753) { /* "OggS" */ + int i; + cfg.key_len = 4; + for (i = 0; i < cfg.key_len; i++) { + cfg.key[i] = key[i]; + } + cfg.is_encrypted = 1; + } + } + + + if (cfg.is_encrypted) { temp_streamFile = setup_ogg_vorbis_streamfile(streamFile, cfg); if (!temp_streamFile) 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_callbacks(temp_streamFile != NULL ? temp_streamFile : streamFile, NULL, start_offset, &ovmi); close_streamfile(temp_streamFile); @@ -435,6 +469,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb ogg_vorbis_codec_data * data = NULL; OggVorbis_File *ovf = NULL; vorbis_info *vi; + char name[STREAM_NAME_SIZE] = {0}; int loop_flag = ovmi->loop_flag; int32_t loop_start = ovmi->loop_start; @@ -606,6 +641,10 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb data->disable_reordering = 1; } + if (strstr(user_comment, "TITLE=") == user_comment) { + strncpy(name, user_comment + 6, sizeof(name) - 1); + } + ;VGM_LOG("OGG: user_comment=%s\n", user_comment); } } @@ -618,9 +657,14 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb vgmstream->codec_data = data; /* store our fun extra datas */ vgmstream->channels = vi->channels; vgmstream->sample_rate = vi->rate; - vgmstream->num_streams = ovmi->total_subsongs; vgmstream->stream_size = stream_size; + if (ovmi->total_subsongs) /* not setting it has some effect when showing stream names */ + vgmstream->num_streams = ovmi->total_subsongs; + + if (name[0] != '\0') + strcpy(vgmstream->stream_name, name); + vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */ if (loop_flag) { vgmstream->loop_start_sample = loop_start; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c index 24c1d8939..5007e758b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -9,7 +9,7 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag = 0, channel_count; - off_t data_offset; + off_t data_offset, multichannel_offset = 0; size_t data_size, skip = 0; @@ -22,6 +22,11 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, skip = read_16bitLE(offset + 0x1c, streamFile); /* 0x1e: ? (seen in Lego Movie 2 (Switch)) */ + /* recent >2ch info [Clannad (Switch)] */ + if ((uint32_t)read_32bitLE(offset + 0x20, streamFile) == 0x80000005) { + multichannel_offset = offset + 0x20; + } + if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) goto fail; @@ -37,7 +42,8 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, vgmstream->meta_type = meta_type; vgmstream->sample_rate = read_32bitLE(offset + 0x0c,streamFile); - + 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; @@ -45,10 +51,26 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, #ifdef VGM_USE_FFMPEG { - vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); + opus_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.skip = skip; + cfg.sample_rate = vgmstream->sample_rate; + + if (multichannel_offset && vgmstream->channels <= 8) { + int i; + cfg.stream_count = read_8bit(multichannel_offset + 0x08,streamFile); + cfg.coupled_count = read_8bit(multichannel_offset + 0x09,streamFile); + for (i = 0; i < vgmstream->channels; i++) { + cfg.channel_mapping[i] = read_8bit(multichannel_offset + 0x0a + i,streamFile); + } + } + + vgmstream->codec_data = init_ffmpeg_switch_opus_config(streamFile, start_offset,data_size, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data); if (vgmstream->num_samples == 0) { vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip; @@ -368,3 +390,50 @@ VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE *streamFile) { fail: return NULL; } + +/* Edelweiss variation [Astebreed (Switch)] */ +VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE *streamFile) { + off_t offset = 0; + int num_samples = 0, loop_start = 0, loop_end = 0; + + /* checks */ + if (!check_extensions(streamFile, "opus,lopus")) + goto fail; + if (read_64bitBE(0x00, streamFile) != 0x4F5055534E580000) /* "OPUSNX\0\0" */ + goto fail; + + offset = 0x10; + num_samples = 0; //read_32bitLE(0x08, streamFile); /* samples with encoder delay */ + if (read_32bitLE(0x0c, streamFile) != 0) + goto fail; + + return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); +fail: + return NULL; +} + +/* Square Enix variation [Dragon Quest I-III (Switch)] */ +VGMSTREAM * init_vgmstream_opus_sqex(STREAMFILE *streamFile) { + off_t offset = 0; + int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; + + /* checks */ + if (!check_extensions(streamFile, "opus,lopus")) + goto fail; + if (read_64bitBE(0x00, streamFile) != 0x0100000002000000) + goto fail; + + offset = read_32bitLE(0x0C, streamFile); + num_samples = read_32bitLE(0x1C, streamFile); + + /* Check if there's a loop end value to determine loop_flag*/ + loop_flag = read_32bitLE(0x18, streamFile); + if (loop_flag) { + loop_start = read_32bitLE(0x14, streamFile); + loop_end = read_32bitLE(0x18, streamFile); + } + + return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); +fail: + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_snds.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_snds.c deleted file mode 100644 index 03b1d23d7..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_snds.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* .snds - from Incredibles PC */ - -VGMSTREAM * init_vgmstream_pc_snds(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - size_t file_size; - int i; - - /* check extension, case insensitive */ - /* this is all we have to go on, snds is completely headerless */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("snds",filename_extension(filename))) goto fail; - - file_size = get_streamfile_size(streamFile); - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(2,0); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = 48000; - - /* file seems to be mistakenly 1/8 too long */ - vgmstream->num_samples = file_size*8/9; - - /* check for 32 0 bytes where the padding should start */ - for (i = 0; i < 8; i++) - { - if (read_32bitBE(vgmstream->num_samples+i*4,streamFile) != 0) - { - /* not padding? just play the whole file */ - vgmstream->num_samples = file_size; - break; - } - } - - vgmstream->coding_type = coding_SNDS_IMA; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_PC_SNDS; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = vgmstream->ch[1].streamfile = - streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[0].streamfile) goto fail; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset= - vgmstream->ch[1].channel_start_offset= - vgmstream->ch[1].offset=0; - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c index 4d7872d3f..dda438df5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c @@ -1,13 +1,15 @@ #include "meta.h" #include "../coding/coding.h" +static size_t joe_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave); + /* .JOE - from Asobo Studio games [Up (PS2), Wall-E (PS2)] */ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int channel_count, loop_flag, sample_rate; int32_t num_samples, loop_start = 0, loop_end = 0; - size_t file_size, data_size, unknown1, unknown2, interleave; + size_t file_size, data_size, unknown1, unknown2, interleave, padding_size; /* checks */ @@ -20,44 +22,43 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { unknown2 = read_32bitLE(0x0c,streamFile); /* detect version */ - if (data_size/2 == file_size - 0x10 - && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm (PS2) */ + if (data_size / 2 == file_size - 0x10 + && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm (PS2) */ data_size = data_size / 2; interleave = 0x4000; start_offset = 0x10; - } - else if (data_size/2 == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks (PS2) */ + } else if (data_size / 2 == file_size - 0x10 + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks (PS2) */ data_size = data_size / 2; interleave = 0x8000; start_offset = 0x10; - } - else if (data_size == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series (PS2) */ + } else if (data_size == file_size - 0x10 + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series (PS2) */ interleave = 0x8000; start_offset = 0x10; - } - else if (data_size == file_size - 0x4020) { /* CT Special Forces (PS2), and all games beyond */ + } else if (data_size == file_size - 0x4020) { /* CT Special Forces (PS2), and all games beyond */ interleave = unknown1; /* always 0? */ if (!interleave) interleave = 0x10; start_offset = 0x4020; /* header padding contains garbage */ - } - else { + } else { goto fail; } //start_offset = file_size - data_size; /* also ok */ channel_count = 2; + loop_flag = 0; sample_rate = read_32bitLE(0x00,streamFile); + + /* the file's end is padded with either 0xcdcdcdcd or zeroes */ + padding_size = joe_find_padding(streamFile, start_offset, data_size, channel_count, interleave); + if (padding_size == SIZE_MAX) + goto fail; + + data_size -= padding_size; num_samples = ps_bytes_to_samples(data_size, channel_count); - loop_flag = ps_find_loop_offsets(streamFile, start_offset, data_size, channel_count, interleave,&loop_start, &loop_end); - /* most songs simply repeat except a few jingles (PS-ADPCM flags are always set) */ - loop_flag = loop_flag && (num_samples > 20*sample_rate); /* in seconds */ - - /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; @@ -66,6 +67,7 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; + vgmstream->stream_size = data_size; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; @@ -80,3 +82,38 @@ fail: close_vgmstream(vgmstream); return NULL; } + +static size_t joe_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave) { + uint8_t flag; + off_t min_offset, offset; + size_t frame_size = 0x10; + size_t padding_size = 0; + size_t interleave_consumed = 0; + + if (data_size == 0 || channels == 0 || (channels > 0 && interleave == 0)) + return SIZE_MAX; + + offset = start_offset + data_size - interleave * (channels - 1); + min_offset = start_offset; + + while (offset > min_offset) { + offset -= frame_size; + flag = read_8bit(offset + 0x01, streamFile); + if (flag == 0x03) + break; + + padding_size += frame_size * channels; + + /* skip other channels */ + interleave_consumed += 0x10; + if (interleave_consumed == interleave) { + interleave_consumed = 0; + offset -= interleave * (channels - 1); + } + } + + if (padding_size >= data_size) + return SIZE_MAX; + + return padding_size; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c index 2cffb9946..80daabfd4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c @@ -9,17 +9,19 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { off_t start_offset, chunk_offset, name_offset = 0; size_t stream_size, chunk_size; int loop_flag = 0, channel_count, is_separate = 0, type, sample_rate; - int32_t loop_start, loop_end; + int32_t num_samples, loop_start; int total_subsongs, target_subsong = streamFile->stream_index; - /* check extensions */ - /* .xws: header and data, .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */ - if (!check_extensions(streamFile,"xws,xwb")) goto fail; + /* checks */ + /* .xws: header and data + * .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */ + if (!check_extensions(streamFile,"xws,xwb")) + goto fail; is_separate = check_extensions(streamFile,"xwb"); /* xwh+xwb: use xwh as header; otherwise use the current file */ if (is_separate) { - /* extra check to avoid hijacking Microsoft's XWB */ + /* extra check to reject Microsoft's XWB faster */ if ((read_32bitBE(0x00,streamFile) == 0x57424E44) || /* "WBND" (LE) */ (read_32bitBE(0x00,streamFile) == 0x444E4257)) /* "DNBW" (BE) */ goto fail; @@ -62,7 +64,7 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { channel_count = read_8bit(header_offset+0x09, streamHeader); sample_rate = (uint16_t)read_16bitLE(header_offset+0x0a,streamHeader); stream_offset = read_32bitLE(header_offset+0x10,streamHeader); - loop_end = read_32bitLE(header_offset+0x14,streamHeader); + num_samples = read_32bitLE(header_offset+0x14,streamHeader); loop_start = read_32bitLE(header_offset+0x18,streamHeader); loop_flag = (loop_start != 0xFFFFFFFF); @@ -84,7 +86,7 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { } if (target_subsong == total_subsongs) { - next_stream_offset = data_offset + get_streamfile_size(is_separate ? streamFile : streamHeader); + next_stream_offset = get_streamfile_size(is_separate ? streamFile : streamHeader) - data_offset; } else { off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong); next_stream_offset = read_32bitLE(next_header_offset+0x10,streamHeader); @@ -120,41 +122,36 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; - vgmstream->num_samples = ps_bytes_to_samples(loop_end, channel_count); + vgmstream->num_samples = ps_bytes_to_samples(num_samples, channel_count); vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count); - vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; break; case 0x01: /* PCM */ vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = channel_count==1 ? layout_none : layout_interleave; + vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x2; - vgmstream->num_samples = pcm_bytes_to_samples(loop_end, channel_count, 16); + vgmstream->num_samples = pcm_bytes_to_samples(num_samples, channel_count, 16); vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16); - vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channel_count, 16); + vgmstream->loop_end_sample = vgmstream->num_samples; break; #ifdef VGM_USE_FFMPEG case 0x02: { /* ATRAC3 */ - uint8_t buf[0x100]; - int32_t bytes, block_size, encoder_delay, joint_stereo; + int block_align, encoder_delay; - block_size = 0xc0 * channel_count; - joint_stereo = 0; - encoder_delay = 0x0; + block_align = 0xc0 * channel_count; + encoder_delay = 1024 + 69*2; /* observed default */ + vgmstream->num_samples = num_samples - encoder_delay; - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); - if (bytes <= 0) goto fail; - - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = loop_end; vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; + vgmstream->loop_end_sample = vgmstream->num_samples; break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_va3.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_va3.c index 616b37742..bd911bc27 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_va3.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_va3.c @@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count; - uint32_t data_size, loop_start, loop_end; + uint32_t data_size; /* check extension, case insensitive */ if (!check_extensions(streamFile, "va3")) @@ -29,41 +29,23 @@ VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile) { /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - - + + vgmstream->meta_type = meta_VA3; vgmstream->sample_rate = read_32bitLE(0x14, streamFile); vgmstream->num_samples = read_32bitLE(0x08, streamFile); vgmstream->channels = channel_count; - vgmstream->meta_type = meta_VA3; - loop_start = 0; - loop_end = 0; #ifdef VGM_USE_FFMPEG { - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[200]; - int32_t bytes, samples_size = 1024, block_size, encoder_delay, joint_stereo; - block_size = 0xC0 * vgmstream->channels; - //max_samples = (data_size / block_size) * samples_size; - encoder_delay = 0x0; - joint_stereo = 0; + int block_align, encoder_delay; - /* make a fake riff so FFmpeg can parse the ATRAC3 */ - bytes = ffmpeg_make_riff_atrac3(buf, 200, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); - if (bytes <= 0) goto fail; + block_align = 0xC0 * vgmstream->channels; + encoder_delay = 0; //todo - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - //vgmstream->num_samples = max_samples; - - if (loop_flag) { - vgmstream->loop_start_sample = (loop_start / block_size) * samples_size; - vgmstream->loop_end_sample = (loop_end / block_size) * samples_size; - } - } #else goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vas.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vas.c index b4e279e35..f2f3ed55d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vas.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vas.c @@ -1,66 +1,154 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* VAS (from Pro Baseball Spirits 5) */ + +/* .VAS - from Konami Jikkyou Powerful Pro Yakyuu games */ VGMSTREAM * init_vgmstream_ps2_vas(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int loop_flag; - int channel_count; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vas",filename_extension(filename))) goto fail; - /* check header */ -#if 0 - if (read_32bitBE(0x00,streamFile) != 0x00000000) /* 0x0 */ + /* checks */ + if (!check_extensions(streamFile, "vas")) + goto fail; + if (read_32bitLE(0x00,streamFile) + 0x800 != get_streamfile_size(streamFile)) goto fail; -#endif - loop_flag = (read_32bitLE(0x10,streamFile)!=0); + loop_flag = (read_32bitLE(0x10,streamFile) != 0); channel_count = 2; - - /* build the VGMSTREAM */ + start_offset = 0x800; + + /* header is too simple so test a bit */ + if (!ps_check_format(streamFile, start_offset, 0x1000)) + goto fail; + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; + vgmstream->meta_type = meta_PS2_VAS; vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x00,streamFile)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile)*28/16/channel_count; - vgmstream->loop_end_sample = read_32bitLE(0x00,streamFile)*28/16/channel_count; - } + vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x200; - vgmstream->meta_type = meta_PS2_VAS; - /* 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; - - } - } + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x00,streamFile), channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x14,streamFile), channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); + return NULL; +} + + +/* .VAS in containers */ +VGMSTREAM * init_vgmstream_ps2_vas_container(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset = 0; + size_t subfile_size = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "vas")) + goto fail; + + if (read_32bitBE(0x00, streamFile) == 0xAB8A5A00) { /* fixed value */ + + /* just in case */ + if (read_32bitLE(0x04, streamFile)*0x800 + 0x800 != get_streamfile_size(streamFile)) + goto fail; + + total_subsongs = read_32bitLE(0x08, streamFile); /* also at 0x10 */ + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* check offset table flag, 0x98 has table size */ + if (read_32bitLE(0x94, streamFile)) { + off_t header_offset = 0x800 + 0x10*(target_subsong-1); + + /* some values are repeats found in the file sub-header */ + subfile_offset = read_32bitLE(header_offset + 0x00,streamFile) * 0x800; + subfile_size = read_32bitLE(header_offset + 0x08,streamFile) + 0x800; + } + else { + /* a bunch of files */ + off_t offset = 0x800; + int i; + + for (i = 0; i < total_subsongs; i++) { + size_t size = read_32bitLE(offset, streamFile) + 0x800; + + if (i + 1 == target_subsong) { + subfile_offset = offset; + subfile_size = size; + break; + } + + offset += size; + } + if (i == total_subsongs) + goto fail; + } + } + else { + /* some .vas are just files pasted together, better extracted externally but whatevs */ + size_t file_size = get_streamfile_size(streamFile); + off_t offset = 0; + + /* must have multiple .vas */ + if (read_32bitLE(0x00,streamFile) + 0x800 >= file_size) + goto fail; + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + while (offset < file_size) { + size_t size = read_32bitLE(offset,streamFile) + 0x800; + + /* some files can be null, ignore */ + if (size > 0x800) { + total_subsongs++; + + if (total_subsongs == target_subsong) { + subfile_offset = offset; + subfile_size = size; + } + } + + offset += size; + } + + /* should end exactly at file_size */ + if (offset > file_size) + goto fail; + + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + } + + + temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_ps2_vas(temp_streamFile); + if (!vgmstream) goto fail; + + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps_headerless.c b/Frameworks/vgmstream/vgmstream/src/meta/ps_headerless.c index 8a4a7d974..30dd869e1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps_headerless.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps_headerless.c @@ -1,7 +1,5 @@ #include "meta.h" -#include "../util.h" - -static int check_psadpcm(STREAMFILE *streamFile); +#include "../coding/coding.h" /* headerless PS-ADPCM - from Katamary Damacy (PS2), Air (PS2), Aladdin: Nasira's Revenge (PS1) @@ -51,7 +49,7 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) { goto fail; /* test if raw PS-ADPCM */ - if (!check_psadpcm(streamFile)) + if (!ps_check_format(streamFile, 0x00, 0x2000)) goto fail; @@ -286,26 +284,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - -/* tests some PS-ADPCM frames */ -static int check_psadpcm(STREAMFILE *streamFile) { - off_t offset, max_offset; - - max_offset = get_streamfile_size(streamFile); - if (max_offset > 0x2000) - max_offset = 0x2000; - - offset = 0x00; - while (offset < max_offset) { - uint8_t predictor = (read_8bit(offset+0x00,streamFile) >> 4) & 0x0f; - uint8_t flags = read_8bit(offset+0x01,streamFile); - - if (predictor > 5 || flags > 7) - goto fail; - offset += 0x10; - } - - return 1; -fail: - return 0; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psf.c b/Frameworks/vgmstream/vgmstream/src/meta/psf.c new file mode 100644 index 000000000..809793353 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/psf.c @@ -0,0 +1,760 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* PSF single - Pivotal games single segment (external in some PC/Xbox or inside bigfiles) [The Great Escape, Conflict series] */ +VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate, rate_value, interleave; + uint32_t psf_config; + uint8_t flags; + size_t data_size; + coding_t codec; + + + /* checks */ + /* .psf: actual extension + * .swd: bigfile extension */ + if (!check_extensions(streamFile, "psf,swd")) + goto fail; + if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x50534600) /* "PSF\00" */ + goto fail; + + flags = read_8bit(0x03,streamFile); + switch(flags) { + case 0xC0: /* [The Great Escape (PS2), Conflict: Desert Storm (PS2)] */ + case 0x40: /* [The Great Escape (PS2)] */ + case 0xA1: /* [Conflict: Desert Storm 2 (PS2)] */ + case 0x21: /* [Conflict: Desert Storm 2 (PS2), Conflict: Global Storm (PS2)] */ + //case 0x22: /* [Conflict: Vietman (PS2)] */ //todo weird size value, stereo, only one found + codec = coding_PSX; + interleave = 0x10; + + channel_count = 2; + if (flags == 0x21 || flags == 0x40) + channel_count = 1; + start_offset = 0x08; + break; + + case 0x80: /* [The Great Escape (PC/Xbox), Conflict: Desert Storm (Xbox/GC), Conflict: Desert Storm 2 (Xbox)] */ + case 0x81: /* [Conflict: Desert Storm 2 (Xbox), Conflict: Vietnam (Xbox)] */ + case 0x01: /* [Conflict: Global Storm (Xbox)] */ + codec = coding_PSX_pivotal; + interleave = 0x10; + + channel_count = 2; + if (flags == 0x01) + channel_count = 1; + start_offset = 0x08; + break; + + case 0xD1: /* [Conflict: Desert Storm 2 (GC)] */ + codec = coding_NGC_DSP; + interleave = 0x08; + + channel_count = 2; + start_offset = 0x08 + 0x60 * channel_count; + break; + + default: + goto fail; + } + + loop_flag = 0; + + psf_config = read_32bitLE(0x04, streamFile); + + /* pitch/cents? */ + rate_value = (psf_config >> 20) & 0xFFF; + switch(rate_value) { + case 3763: sample_rate = 44100; break; + case 1365: sample_rate = 16000; break; + case 940: sample_rate = 11050; break; + case 460: sample_rate = 5000; break; + default: + VGM_LOG("PSF: unknown rate value %x\n", rate_value); + sample_rate = rate_value * 11.72; /* not exact but works well enough */ + break; + } + + data_size = (psf_config & 0xFFFFF) * (interleave * channel_count); /* in blocks */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PSF; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case coding_PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + break; + + case coding_PSX_pivotal: + vgmstream->coding_type = coding_PSX_pivotal; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count); + break; + + case coding_NGC_DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + /* has standard DSP headers at 0x08 */ + dsp_read_coefs_be(vgmstream,streamFile,0x08+0x1c,0x60); + dsp_read_hist_be (vgmstream,streamFile,0x08+0x40,0x60); + + vgmstream->num_samples = read_32bitBE(0x08, streamFile);//dsp_bytes_to_samples(data_size, channel_count); + break; + + default: + goto fail; + } + + vgmstream->stream_size = data_size; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + + +/* PSF segmented - Pivotal games multiple segments (external in some PC/Xbox or inside bigfiles) [The Great Escape, Conflict series] */ +VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE* temp_streamFile = NULL; + segmented_layout_data *data = NULL; + int i,j, sequence_count = 0, loop_flag = 0, loop_start = 0, loop_end = 0; + int sequence[512] = {0}; + off_t offsets[512] = {0}; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + char stream_name[STREAM_NAME_SIZE] = {0}; + size_t stream_size = 0; + + + /* checks */ + /* .psf: actual extension + * .swd: bigfile extension */ + if (!check_extensions(streamFile, "psf,swd")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x50534660 && /* "PSF\60" [The Great Escape (PC/Xbox/PS2), Conflict: Desert Storm (Xbox/GC)] */ + read_32bitBE(0x00,streamFile) != 0x50534631) /* "PSF\31" [Conflict: Desert Storm 2 (Xbox/GC/PS2), Conflict: Global Terror (Xbox)] */ + goto fail; + + + /* transition table info: + * 0x00: offset + * 0x04: 0x02*4 next segment points (one per track) + * (xN segments) + * + * There are 4 possible tracks, like: normal, tension, action, high action. Segment 0 has tracks' + * entry segments (where 1=first, right after segment 0), and each segment has a link point to next + * (or starting) segment of any of other tracks. Thus, follow point 1/2/3/4 to playtrack 1/2/3/4 + * (points also loop back). It's designed to go smoothly between any tracks (1>3, 4>1, etc), + * so sometimes "step" segments (that aren't normally played) are used. + * If a track doesn't exist it may keep repeating silent segments, but still defines points. + * + * ex. sequence could go like this: + * (read segment 0 track1 entry): 1 + * - track0: 1>2>3>4>5>6>7>8>1>2>3, then to track2 goes 3>15>9 + * - track1: 9>10>11>12>13>14>9>10, then to track4 goes 10>33 + * - track2: 33>34>35>36>30>31>32>33, then to track1 goes 33>3 + * - track3: 3>4>5>6... (etc) + * + * Well make a sequence based on target subsong: + * - 1: tracks mixed with transitions, looping back to track1 (only first is used in .sch so we want all) + * - 2~5: track1~4 looping back to themselves + * - 6+: single segment (where 6=first segment) to allow free mixes + */ + { + int track[4][255] = {0}; + int count[4] = {0}; + int current_track, current_point, next_point, repeat_point; + int transition_count = read_32bitLE(0x04, streamFile); + + total_subsongs = 1 + 4 + (transition_count - 1); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + + if (target_subsong == 1) { + current_track = 0; /* start from first track, will move to others automatically */ + + snprintf(stream_name,sizeof(stream_name), "full"); + } + else if (target_subsong <= 1+4) { + current_track = (target_subsong-1) - 1; /* where 0 = first track */ + + snprintf(stream_name,sizeof(stream_name), "track%i", (current_track+1)); + } + else { + int segment = target_subsong - 1 - 4; /* where 1 = first segment */ + + sequence[0] = segment; + sequence_count = 1; + current_track = -1; + + /* show transitions to help with ordering */ + track[0][0] = read_16bitLE(0x08 + 0x0c*segment + 0x04, streamFile); + track[1][0] = read_16bitLE(0x08 + 0x0c*segment + 0x06, streamFile); + track[2][0] = read_16bitLE(0x08 + 0x0c*segment + 0x08, streamFile); + track[3][0] = read_16bitLE(0x08 + 0x0c*segment + 0x0a, streamFile); + snprintf(stream_name,sizeof(stream_name), "segment%03i to %03i/%03i/%03i/%03i", segment,track[0][0],track[1][0],track[2][0],track[3][0]); + } + + /* find target sequence */ + current_point = 0; /* segment 0 has track entry points */ + while (sequence_count < 512 && current_track >= 0) { + + next_point = read_16bitLE(0x08 + 0x0c*current_point + 0x04 + 0x02*current_track, streamFile); + + /* find if next point repeats in our current track */ + repeat_point = -1; + for (i = 0; i < count[current_track]; i++) { + + if (track[current_track][i] == next_point) { + repeat_point = i; + break; + } + } + + /* set loops and end sequence */ + if (repeat_point >= 0) { + if (target_subsong == 1) { + /* move to next track and change transition to next track too */ + current_track++; + + /* to loop properly we set loop end in track3 and loop start in track0 + * when track3 ends and move to track0 could have a transition segment + * before actually looping track0, so we do this in 2 steps */ + + if (loop_flag) { /* 2nd time repeat is found = loop start in track0 */ + loop_start = repeat_point; + break; /* sequence fully done */ + } + + if (current_track > 3) { /* 1st time repeat is found = loop end in track3 */ + current_track = 0; + loop_flag = 1; + } + + next_point = read_16bitLE(0x08 + 0x0c*current_point + 0x04 + 0x02*current_track, streamFile); + + if (loop_flag) { + loop_end = sequence_count; /* this points to the next_point that will be added below */ + } + } + else { + /* end track N */ + loop_flag = 1; + loop_start = repeat_point; + loop_end = sequence_count - 1; + break; + } + } + + + /* separate track info to find repeated points (since some transitions are common for all tracks) */ + track[current_track][count[current_track]] = next_point; + count[current_track]++; + + sequence[sequence_count] = next_point; + sequence_count++; + + current_point = next_point; + } + + if (sequence_count >= 512 || count[current_track] >= 512) + goto fail; + } + + + /* build segments */ + data = init_layout_segmented(sequence_count); + if (!data) goto fail; + + for (i = 0; i < sequence_count; i++) { + off_t psf_offset; + size_t psf_size; + int old_psf = -1; + + psf_offset = read_32bitLE(0x08 + sequence[i]*0x0c + 0x00, streamFile); + psf_size = get_streamfile_size(streamFile) - psf_offset; /* not ok but meh */ + + /* find repeated sections (sequences often repeat PSFs) */ + offsets[i] = psf_offset; + for (j = 0; j < i; j++) { + if (offsets[j] == psf_offset) { + old_psf = j; + break; + } + } + + /* reuse repeated VGMSTREAMs to improve memory and bitrate calcs a bit */ + if (old_psf >= 0) { + data->segments[i] = data->segments[old_psf]; + } + else { + temp_streamFile = setup_subfile_streamfile(streamFile, psf_offset, psf_size, "psf"); + if (!temp_streamFile) goto fail; + + data->segments[i] = init_vgmstream_psf_single(temp_streamFile); + if (!data->segments[i]) goto fail; + + stream_size += data->segments[i]->stream_size; /* only non-repeats */ + } + } + + /* setup VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + vgmstream = allocate_segmented_vgmstream(data,loop_flag, loop_start, loop_end); + if (!vgmstream) goto fail; + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + strcpy(vgmstream->stream_name, stream_name); + + return vgmstream; +fail: + if (!vgmstream) free_layout_segmented(data); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +/* ***************************************************** */ + +static VGMSTREAM * init_vgmstream_psf_pfsm(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate = 0, rate_value = 0, interleave, big_endian; + size_t data_size; + coding_t codec; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + /* standard: + * 0x00: -1/number (lang?) + * 0x04: config/size? + * 0x08: channel size? only ok for PSX-pivotal + * 0x0c: sample rate or rate_value + * 0x0e: 0x4=PSX-pivotal or 0xFF=PSX + * 0x0f: name size (0xCC/FF=null) + * 0x10: data + * + * GC is similar with 0x20-align between some fields + */ + + /* checks */ + //if (!check_extensions(streamFile, "psf")) + // goto fail; + if (read_32bitBE(0x00,streamFile) != 0x5046534D && /* "PFSM" */ + read_32bitLE(0x00,streamFile) != 0x5046534D) /* "PFSM" (BE) */ + goto fail; + + big_endian = (read_32bitLE(0x00,streamFile) == 0x5046534D); + if (big_endian) { + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } + else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + loop_flag = 0; + + + if (big_endian && read_32bit(0x50, streamFile) != 0) { /* GC */ + codec = coding_NGC_DSP; + interleave = 0x08; + channel_count = 1; + rate_value = (uint16_t)read_16bit(0x48, streamFile); + + start_offset = 0x60 + 0x60 * channel_count; + } + else if (big_endian) { /* GC */ + codec = coding_PCM16BE; + interleave = 0x02; + channel_count = 1; + rate_value = (uint16_t)read_16bit(0x48, streamFile); + + start_offset = 0x60; + } + else if ((uint8_t)read_8bit(0x16, streamFile) == 0xFF) { /* PS2 */ + codec = coding_PSX; + interleave = 0x10; + rate_value = (uint16_t)read_16bit(0x14, streamFile); + channel_count = 1; + + start_offset = 0x18; + } + else { /* PC/Xbox, some PS2/GC */ + codec = coding_PSX_pivotal; + interleave = 0x10; + sample_rate = (uint16_t)read_16bit(0x14, streamFile); + channel_count = 1; + + start_offset = 0x18; + } + + data_size = get_streamfile_size(streamFile) - start_offset; + + /* pitch/cents? */ + if (sample_rate == 0) { + /* pitch/cents? */ + switch(rate_value) { + case 3763: sample_rate = 44100; break; + case 1365: sample_rate = 16000; break; + case 940: sample_rate = 11050; break; + case 460: sample_rate = 5000; break; + default: + VGM_LOG("PSF: unknown rate value %x\n", rate_value); + sample_rate = rate_value * 11.72; /* not exact but works well enough */ + break; + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PSF; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case coding_PCM16BE: + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); + break; + + case coding_PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + break; + + case coding_PSX_pivotal: + vgmstream->coding_type = coding_PSX_pivotal; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count); + break; + + case coding_NGC_DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + /* has standard DSP headers at 0x08 */ + dsp_read_coefs_be(vgmstream,streamFile,0x60+0x1c,0x60); + dsp_read_hist_be (vgmstream,streamFile,0x60+0x40,0x60); + + vgmstream->num_samples = read_32bitBE(0x60, streamFile);//dsp_bytes_to_samples(data_size, channel_count); + break; + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + + +typedef enum { UNKNOWN, IMUS, PFST, PFSM } sch_type; + + + +/* SCH - Pivotal games multi-audio container [The Great Escape, Conflict series] */ +VGMSTREAM * init_vgmstream_sch(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *external_streamFile = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t skip = 0, chunk_offset, target_offset = 0, header_offset, subfile_offset = 0; + size_t file_size, chunk_padding, target_size = 0, subfile_size = 0; + int big_endian; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + sch_type target_type = UNKNOWN; + char stream_name[STREAM_NAME_SIZE] ={0}; + + + /* checks */ + if (!check_extensions(streamFile, "sch")) + goto fail; + + if (read_32bitBE(0x00,streamFile) == 0x48445253) /* "HDRSND" (found on later games) */ + skip = 0x0E; + if (read_32bitBE(skip + 0x00,streamFile) != 0x53434800 && /* "SCH\0" */ + read_32bitLE(skip + 0x00,streamFile) != 0x53434800) /* "SCH\0" (BE consoles) */ + goto fail; + + + /* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids): + * no other info so total subsongs would be count of usable chunks + * (offsets are probably in level .dat files) */ + big_endian = (read_32bitLE(skip + 0x00,streamFile) == 0x53434800); + if (big_endian) { + read_32bit = read_32bitBE; + chunk_padding = 0x18; + } + else { + read_32bit = read_32bitLE; + chunk_padding = 0; + } + + file_size = get_streamfile_size(streamFile); + if (read_32bit(skip + 0x04,streamFile) + skip + 0x08 + chunk_padding < file_size) /* sometimes padded */ + goto fail; + + if (target_subsong == 0) target_subsong = 1; + + chunk_offset = skip + 0x08 + chunk_padding; + + /* get all files*/ + while (chunk_offset < file_size) { + uint32_t chunk_id = read_32bitBE(chunk_offset + 0x00,streamFile); + uint32_t chunk_size = read_32bit(chunk_offset + 0x04,streamFile); + sch_type current_type = UNKNOWN; + + switch(chunk_id) { + case 0x494D5553: /* "IMUS" (TGE PC/Xbox only) */ + current_type = IMUS; + break; + + case 0x54534650: + case 0x50465354: /* "PFST" */ + current_type = PFST; + break; + + case 0x4D534650: + case 0x5046534D: /* "PFSM" */ + current_type = PFSM; + break; + + case 0x4B4E4142: + case 0x42414E4B: /* "BANK" */ + /* unknown format (variable size), maybe config for entry numbers */ + break; + case 0x424C4F4B: /* "BLOK" [Conflict: Desert Storm (Xbox)] */ + /* some ids or something? */ + break; + + default: + VGM_LOG("SCH: unknown chunk at %lx\n", chunk_offset); + goto fail; + } + + if (current_type != UNKNOWN) + total_subsongs++; + + if (total_subsongs == target_subsong && target_type == UNKNOWN) { + target_type = current_type; + target_offset = chunk_offset; + target_size = 0x08 + chunk_padding + chunk_size; + } + + chunk_offset += 0x08 + chunk_padding + chunk_size; + } + + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + if (target_size == 0) goto fail; + + header_offset = target_offset + 0x08 + chunk_padding; + + //;VGM_LOG("SCH: offset=%lx, size=%x\n",target_offset, target_size); + + switch(target_type) { + case IMUS: { /* external segmented track */ + STREAMFILE *psf_streamFile; + uint8_t name_size; + char name[255]; + + /* 0x00: config/size? + * 0x04: name size + * 0x05: segments + * 0x06: ? + * 0x08: relative path to .psf + * 0xNN: segment table (same as .psf) + */ + + name_size = read_8bit(header_offset + 0x04, streamFile); + read_string(name,name_size, header_offset + 0x08, streamFile); + + /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ + if ((uint8_t)read_8bit(header_offset + 0x07, streamFile) == 0xCC) { + external_streamFile = open_streamfile_by_filename(streamFile, "Stream.swd"); + if (!external_streamFile) goto fail; + + subfile_offset = read_32bit(header_offset + 0x08 + name_size, streamFile); + subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ + + temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); + if (!temp_streamFile) goto fail; + + psf_streamFile = temp_streamFile; + } + else { + external_streamFile = open_streamfile_by_filename(streamFile, name); + if (!external_streamFile) goto fail; + + psf_streamFile = external_streamFile; + } + + vgmstream = init_vgmstream_psf_segmented(psf_streamFile); + if (!vgmstream) { + vgmstream = init_vgmstream_psf_single(psf_streamFile); + if (!vgmstream) goto fail; + } + + snprintf(stream_name,sizeof(stream_name), "%s-%s" , "IMUS", name); + break; + } + + case PFST: { /* external track */ + STREAMFILE *psf_streamFile; + uint8_t name_size; + char name[255]; + + if (chunk_padding == 0 && target_size > 0x08 + 0x0c) { /* TGE PC/Xbox version */ + /* 0x00: -1/0 + * 0x04: config/size? + * 0x08: channel size + * 0x0c: sample rate? (differs vs PSF) + * 0x0e: 4? + * 0x0f: name size + * 0x10: name + */ + + /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ + if ((read_32bitBE(header_offset + 0x14, streamFile) & 0x0000FFFF) == 0xCCCC) { + name_size = read_8bit(header_offset + 0x13, streamFile); + read_string(name,name_size, header_offset + 0x18, streamFile); + + external_streamFile = open_streamfile_by_filename(streamFile, "Stream.swd"); + if (!external_streamFile) goto fail; + + subfile_offset = read_32bit(header_offset + 0x0c, streamFile); + subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ + + temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); + if (!temp_streamFile) goto fail; + + psf_streamFile = temp_streamFile; + } + else { + name_size = read_8bit(header_offset + 0x0f, streamFile); + read_string(name,name_size, header_offset + 0x10, streamFile); + + external_streamFile = open_streamfile_by_filename(streamFile, name); + if (!external_streamFile) goto fail; + + psf_streamFile = external_streamFile; + } + } + else if (chunk_padding) { + strcpy(name, "STREAM.SWD"); /* fixed */ + + /* 0x00: -1 + * 0x04: config/size? + * 0x08: .swd offset + */ + external_streamFile = open_streamfile_by_filename(streamFile, name); + if (!external_streamFile) goto fail; + + subfile_offset = read_32bit(header_offset + 0x24, streamFile); + subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ + + temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); + if (!temp_streamFile) goto fail; + + psf_streamFile = temp_streamFile; + } + else { /* others */ + strcpy(name, "STREAM.SWD"); /* fixed */ + + /* 0x00: -1 + * 0x04: config/size? + * 0x08: .swd offset + */ + external_streamFile = open_streamfile_by_filename(streamFile, name); + if (!external_streamFile) goto fail; + + subfile_offset = read_32bit(header_offset + 0x08, streamFile); + subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ + + temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); + if (!temp_streamFile) goto fail; + + psf_streamFile = temp_streamFile; + } + + vgmstream = init_vgmstream_psf_segmented(psf_streamFile); + if (!vgmstream) { + vgmstream = init_vgmstream_psf_single(psf_streamFile); + if (!vgmstream) goto fail; + } + + snprintf(stream_name,sizeof(stream_name), "%s-%s" , "PFST", name); + break; + } + + case PFSM: + /* internal sound */ + + temp_streamFile = setup_subfile_streamfile(streamFile, target_offset,target_size, NULL); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_psf_pfsm(temp_streamFile); + if (!vgmstream) goto fail; + + snprintf(stream_name,sizeof(stream_name), "%s" , "PFSM"); + break; + + default: /* target not found */ + goto fail; + } + + vgmstream->num_streams = total_subsongs; + strcpy(vgmstream->stream_name, stream_name); + + close_streamfile(temp_streamFile); + close_streamfile(external_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_streamfile(external_streamFile); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psx_gms.c b/Frameworks/vgmstream/vgmstream/src/meta/psx_gms.c deleted file mode 100644 index c48341484..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/psx_gms.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* GMS - - PSX GMS format has no recognition ID. - This format was used essentially in Grandia Games but - can be easily used by other header format as the format of the header is very simple - - known extensions : GMS - - 2008-05-19 - Fastelbja : First version ... -*/ - -VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - int loop_flag=0; - int channel_count; - off_t start_offset; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("gms",filename_extension(filename))) goto fail; - - /* check loop */ - loop_flag = (read_32bitLE(0x20,streamFile)==0); - - /* Always stereo files */ - channel_count=read_32bitLE(0x00,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x1C,streamFile); - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x1C,streamFile); - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x800; - vgmstream->meta_type = meta_PSX_GMS; - - start_offset = 0x800; - - /* open the file for reading by each channel */ - { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(start_offset+vgmstream->interleave_block_size*i); - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/raw.c b/Frameworks/vgmstream/vgmstream/src/meta/raw.c deleted file mode 100644 index e9e21c14e..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/raw.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* RAW - - RAW format is native 44khz PCM file - Nothing more :P ... - - 2008-05-17 - Fastelbja : First version ... -*/ - -VGMSTREAM * init_vgmstream_raw(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("raw",filename_extension(filename))) goto fail; - - /* No check to do as they are raw pcm */ - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(2,0); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = 2; - vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_PCM16LE; - vgmstream->num_samples = (int32_t)(get_streamfile_size(streamFile)/4); - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 2; - vgmstream->meta_type = meta_RAW; - - /* open the file for reading by each channel */ - { - STREAMFILE *chstreamfile; - - /* have both channels use the same buffer, as interleave is so small */ - chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!chstreamfile) goto fail; - - for (i=0;i<2;i++) { - vgmstream->ch[i].streamfile = chstreamfile; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=(off_t)(i*vgmstream->interleave_block_size); - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_int.c b/Frameworks/vgmstream/vgmstream/src/meta/raw_int.c similarity index 57% rename from Frameworks/vgmstream/vgmstream/src/meta/ps2_int.c rename to Frameworks/vgmstream/vgmstream/src/meta/raw_int.c index a89af591c..d0ba9719b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_int.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/raw_int.c @@ -2,7 +2,7 @@ #include "../coding/coding.h" /* raw PCM file assumed by extension [PaRappa The Rapper 2 (PS2)? , Amplitude (PS2)?] */ -VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_raw_int(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int channel_count; @@ -16,18 +16,9 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) { else channel_count = 4; - /* try to skip known .int (a horrible idea this parser exists) */ - { - /* ignore A2M .int */ - if (read_32bitBE(0x00,streamFile) == 0x41324D00) /* "A2M\0" */ - goto fail; - /* ignore EXST .int */ - if (read_32bitBE(0x10,streamFile) == 0x0C020000 && - read_32bitBE(0x20,streamFile) == 0x0C020000 && - read_32bitBE(0x30,streamFile) == 0x0C020000 && - read_32bitBE(0x40,streamFile) == 0x0C020000) /* check a few empty PS-ADPCM frames */ - goto fail; - } + /* ignore .int PS-ADPCM */ + if (ps_check_format(streamFile, 0x00, 0x10000)) + goto fail; start_offset = 0x00; @@ -36,8 +27,8 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,0); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_RAW_INT; vgmstream->sample_rate = 48000; - vgmstream->meta_type = meta_PS2_RAW; vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), vgmstream->channels, 16); vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; @@ -48,6 +39,6 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) { return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/raw_pcm.c b/Frameworks/vgmstream/vgmstream/src/meta/raw_pcm.c new file mode 100644 index 000000000..dbfda6391 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/raw_pcm.c @@ -0,0 +1,38 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* RAW - RAW format is native 44khz PCM file */ +VGMSTREAM * init_vgmstream_raw_pcm(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "raw")) + goto fail; + + channel_count = 2; + loop_flag = 0; + start_offset = 0x00; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_RAW_PCM; + vgmstream->sample_rate = 44100; + vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), channel_count, 16); + + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + 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/raw_snds.c b/Frameworks/vgmstream/vgmstream/src/meta/raw_snds.c new file mode 100644 index 000000000..6cf68c0c5 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/raw_snds.c @@ -0,0 +1,51 @@ +#include "meta.h" + + +/* .snds - from Heavy Iron's The Incredibles (PC) */ +VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + size_t file_size; + int i; + + + /* checks */ + if (!check_extensions(streamFile, "snds")) + goto fail; + + loop_flag = 0; + channel_count = 2; + start_offset = 0; + file_size = get_streamfile_size(streamFile); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_RAW_SNDS; + vgmstream->sample_rate = 48000; + + /* file seems to be mistakenly 1/8 too long, check for 32 0 bytes where the padding should start */ + vgmstream->num_samples = file_size*8/9; + for (i = 0; i < 8; i++) { + if (read_32bitBE(vgmstream->num_samples+i*4,streamFile) != 0) { + vgmstream->num_samples = file_size; /* no padding? just play the whole file */ + break; + } + } + + vgmstream->coding_type = coding_SNDS_IMA; + vgmstream->layout_type = layout_none; + + + 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/xbox_wavm.c b/Frameworks/vgmstream/vgmstream/src/meta/raw_wavm.c similarity index 86% rename from Frameworks/vgmstream/vgmstream/src/meta/xbox_wavm.c rename to Frameworks/vgmstream/vgmstream/src/meta/raw_wavm.c index ec1c41062..6be895304 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xbox_wavm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/raw_wavm.c @@ -2,7 +2,7 @@ #include "../coding/coding.h" /* WAVM - headerless format which can be found on XBOX */ -VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_raw_wavm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset = 0; int loop_flag, channel_count; @@ -19,13 +19,12 @@ VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_RAW_WAVM; vgmstream->sample_rate = 44100; vgmstream->num_samples = xbox_ima_bytes_to_samples(get_streamfile_size(streamFile), vgmstream->channels); vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_XBOX_WAVM; - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 6006785ab..aad822b84 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -94,6 +94,7 @@ typedef struct { int interleave; int is_at3; + int is_at3p; int is_at9; } riff_fmt_chunk; @@ -122,17 +123,22 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->channel_layout = read_32bit(current_chunk+0x1c,streamFile); /* 0x10 guid at 0x20 */ - /* happens in .at3/at9, may be a bug in their encoder b/c MS's defs set mono as FC */ + /* happens in various .at3/at9, may be a bug in their encoder b/c MS's defs set mono as FC */ if (fmt->channel_count == 1 && fmt->channel_layout == speaker_FL) { /* other channels are fine */ fmt->channel_layout = speaker_FC; } + + /* happens in few at3p, may be a bug in older tools as other games have ok flags [Ridge Racer 7 (PS3)] */ + if (fmt->channel_count == 6 && fmt->channel_layout == 0x013f) { + fmt->channel_layout = 0x3f; + } } switch (fmt->codec) { - case 0x00: /* Yamaha ADPCM (raw) [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */ + case 0x00: /* Yamaha AICA ADPCM (raw) [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */ if (fmt->bps != 4) goto fail; if (fmt->block_size != 0x02*fmt->channel_count) goto fail; - fmt->coding_type = coding_YAMAHA_int; + fmt->coding_type = coding_AICA_int; fmt->interleave = 0x01; break; @@ -143,7 +149,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->interleave = 0x02; break; case 8: - fmt->coding_type = coding_PCM8_U_int; + fmt->coding_type = coding_PCM8_U; fmt->interleave = 0x01; break; default: @@ -168,9 +174,11 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->coding_type = coding_MS_IMA; break; - case 0x20: /* Yamaha ADPCM (raw) [Takuyo/Dynamix/etc DC games] */ + case 0x20: /* Yamaha AICA ADPCM (raw) [Takuyo/Dynamix/etc DC games] */ if (fmt->bps != 4) goto fail; - fmt->coding_type = coding_YAMAHA; + fmt->coding_type = coding_AICA; + /* official RIFF spec has 0x20 as 'Yamaha ADPCM', but data is probably not pure AICA + * (maybe with headered frames and would need extra detection?) */ break; case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */ @@ -242,11 +250,11 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk bztmp = (bztmp >> 8) | (bztmp << 8); fmt->coding_type = coding_AT3plus; fmt->block_size = (bztmp & 0x3FF) * 8 + 8; /* should match fmt->block_size */ - fmt->is_at3 = 1; + fmt->is_at3p = 1; break; #elif defined(VGM_USE_FFMPEG) fmt->coding_type = coding_FFmpeg; - fmt->is_at3 = 1; + fmt->is_at3p = 1; break; #else goto fail; @@ -442,13 +450,22 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case 0x66616374: /* "fact" */ if (chunk_size == 0x04) { /* standard, usually found with ADPCM */ fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); - } else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, streamFile) == 0x4C794E20) { /* "LyN " */ + } + else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, streamFile) == 0x4C794E20) { /* "LyN " */ goto fail; /* parsed elsewhere */ - } else if ((fmt.is_at3 || fmt.is_at9) && chunk_size == 0x08) { + } + else if ((fmt.is_at3 || fmt.is_at3p) && chunk_size == 0x08) { /* early AT3 (mainly PSP games) */ fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); - fact_sample_skip = read_32bitLE(current_chunk+0x0c, streamFile); - } else if ((fmt.is_at3 || fmt.is_at9) && chunk_size == 0x0c) { + fact_sample_skip = read_32bitLE(current_chunk+0x0c, streamFile); /* base skip samples */ + } + else if ((fmt.is_at3 || fmt.is_at3p) && chunk_size == 0x0c) { /* late AT3 (mainly PS3 games and few PSP games) */ fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + /* 0x0c: base skip samples, ignored by decoder */ + fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); /* skip samples with extra 184 */ + } + else if (fmt.is_at9 && chunk_size == 0x0c) { + fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + /* 0x0c: base skip samples (same as next field) */ fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); } break; @@ -535,13 +552,39 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->sample_rate = fmt.sample_rate; vgmstream->channel_layout = fmt.channel_layout; - /* init, samples */ + /* coding, layout, interleave */ + vgmstream->coding_type = fmt.coding_type; + switch (fmt.coding_type) { + case coding_MSADPCM: + case coding_MS_IMA: + case coding_AICA: + case coding_XBOX_IMA: + case coding_IMA: +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: +#endif +#ifdef VGM_USE_MAIATRAC3PLUS + case coding_AT3plus: +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: +#endif + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = fmt.block_size; + break; + default: + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = fmt.interleave; + break; + } + + /* samples, codec init (after setting coding to ensure proper close on failure) */ switch (fmt.coding_type) { case coding_PCM16LE: vgmstream->num_samples = pcm_bytes_to_samples(data_size, fmt.channel_count, 16); break; - case coding_PCM8_U_int: + case coding_PCM8_U: vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 8); break; @@ -583,8 +626,8 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = fact_sample_count; break; - case coding_YAMAHA: - case coding_YAMAHA_int: + case coding_AICA: + case coding_AICA_int: vgmstream->num_samples = yamaha_bytes_to_samples(data_size, fmt.channel_count); break; @@ -600,40 +643,22 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case coding_FFmpeg: { - ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, 0x00, streamFile->get_size(streamFile)); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + if (!fmt.is_at3 && !fmt.is_at3p) goto fail; - vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact_sample_count */ + vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, 0x00, NULL); + if (!vgmstream->codec_data) goto fail; - if (fmt.is_at3) { - /* the encoder introduces some garbage (not always silent) samples to skip before the stream */ - /* manually set skip_samples if FFmpeg didn't do it */ - if (ffmpeg_data->skipSamples <= 0) { - ffmpeg_set_skip_samples(ffmpeg_data, fact_sample_skip); - } + vgmstream->num_samples = fact_sample_count; + if (loop_flag) { + /* adjust RIFF loop/sample absolute values (with skip samples) */ + loop_start_smpl -= fact_sample_skip; + loop_end_smpl -= fact_sample_skip; - /* LFE channel should be reordered on decode, but FFmpeg doesn't do it automatically: - * - 6ch: FL FR FC BL BR LFE > FL FR FC LFE BL BR - * - 8ch: FL FR FC BL BR SL SR LFE > FL FR FC LFE BL BR SL SR - * (ATRAC3Plus only, 5/7ch can't be encoded) */ - if (ffmpeg_data->channels == 6) { - /* LFE BR BL > LFE BL BR > same */ - int channel_remap[] = { 0, 1, 2, 5, 5, 5, }; - ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap); - } - else if (ffmpeg_data->channels == 8) { - /* LFE BR SL SR BL > LFE BL SL SR BR > LFE BL BR SR SL > LFE BL BR SL SR > same */ - int channel_remap[] = { 0, 1, 2, 7, 7, 7, 7, 7}; - ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap); - } - - /* RIFF loop/sample values are absolute (with skip samples), adjust */ - if (loop_flag) { - loop_start_smpl -= (int32_t)ffmpeg_data->skipSamples; - loop_end_smpl -= (int32_t)ffmpeg_data->skipSamples; - } + /* happens with official tools when "fact" is not found */ + if (vgmstream->num_samples == 0) + vgmstream->num_samples = loop_end_smpl; } + break; } #endif @@ -673,32 +698,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { goto fail; } - /* coding, layout, interleave */ - vgmstream->coding_type = fmt.coding_type; - switch (fmt.coding_type) { - case coding_MSADPCM: - case coding_MS_IMA: - case coding_YAMAHA: - case coding_XBOX_IMA: - case coding_IMA: -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: -#endif -#ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: -#endif -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: -#endif - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = fmt.block_size; - break; - default: - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = fmt.interleave; - break; - } - /* Dynasty Warriors 5 (Xbox) 6ch interleaves stereo frames, probably not official */ if (vgmstream->coding_type == coding_XBOX_IMA && vgmstream->channels > 2) { vgmstream->layout_type = layout_interleave; @@ -753,7 +752,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->coding_type = coding_MSADPCM_int; - /* only works with half-interleave as frame_size and interleave are merged ATM*/ + /* only works with half-interleave as frame_size and interleave are merged ATM */ for (ch = 0; ch < vgmstream->channels; ch++) { vgmstream->ch[ch].channel_start_offset = vgmstream->ch[ch].offset = start_offset + half_interleave*ch; @@ -902,7 +901,7 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { case coding_PCM16BE: vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 16); break; - case coding_PCM8_U_int: + case coding_PCM8_U: vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 8); break; default: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c index e40b3b746..96a8947d3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c @@ -1,1197 +1,224 @@ #include "meta.h" -#include "../util.h" #include "../coding/coding.h" -/* RSD */ -/* RSD2VAG */ -VGMSTREAM * init_vgmstream_rsd2vag(STREAMFILE *streamFile) { + +/* RSD - from Radical Entertainment games */ +VGMSTREAM * init_vgmstream_rsd(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534432) /* RSD2 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x56414720) /* VAG\0x20 */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); - vgmstream->meta_type = meta_RSD2VAG; - - /* 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; -} - - -/* RSD2PCMB - Big Endian */ -VGMSTREAM * init_vgmstream_rsd2pcmb(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534432) /* RSD2 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x50434D42) /* PCMB */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x18,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PCM16BE; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/2/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-start_offset)/2/channel_count; - } - - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else if (channel_count == 2) { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - } - - - vgmstream->meta_type = meta_RSD2PCMB; - - /* 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; -} - - -/* RSD2XADP */ -VGMSTREAM * init_vgmstream_rsd2xadp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; + off_t start_offset, name_offset; size_t data_size; - - /* check extension */ - if (!check_extensions(streamFile,"rsd")) - goto fail; - - if (read_32bitBE(0x00,streamFile) != 0x52534432) /* RSD2 */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x58414450) /* XADP */ - goto fail; - - start_offset = read_32bitLE(0x18,streamFile); /* not sure about this */ - data_size = get_streamfile_size(streamFile); - loop_flag = 0; - channel_count = read_32bitLE(0x08,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); - - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_RSD2XADP; - - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/* RSD3VAG */ -VGMSTREAM * init_vgmstream_rsd3vag(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534433) /* RSD3 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x56414720) /* VAG\0x20 */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); - vgmstream->meta_type = meta_RSD3VAG; - - /* 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; -} - - -/* RSD3GADP */ -VGMSTREAM * init_vgmstream_rsd3gadp(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534433) /* RSD3 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x47414450) /* WADP */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x18,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - } - - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_RSD3GADP; - - if (vgmstream->coding_type == coding_NGC_DSP) { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x1D+i*2,streamFile); - } - } - /* 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; -} - -/* RSD3PCM - Little Endian */ -VGMSTREAM * init_vgmstream_rsd3pcm(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534433) /* RSD3 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x50434D20) /* PCM\0x20 */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x18,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PCM16LE; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/2/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-start_offset)/2/channel_count; - } - - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else if (channel_count == 2) { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - } - - vgmstream->meta_type = meta_RSD3PCM; - - - /* 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; -} - - - -/* RSD3PCMB - Big Endian */ -VGMSTREAM * init_vgmstream_rsd3pcmb(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534433) /* RSD3 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x50434D42) /* PCMB */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x18,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PCM16BE; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/2/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-start_offset)/2/channel_count; - } - - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else if (channel_count == 2) { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - } - - - vgmstream->meta_type = meta_RSD3PCMB; - - /* 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; -} - - - -/* RSD4VAG */ -VGMSTREAM * init_vgmstream_rsd4vag(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534434) /* RSD4 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x56414720) /* VAG\0x20 */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0xC,streamFile); - vgmstream->meta_type = meta_RSD4VAG; - - /* 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; -} - - -/* RSD4PCM - Little Endian */ -VGMSTREAM * init_vgmstream_rsd4pcm(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534434) /* RSD4 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x50434D20) /* PCM\0x20 */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PCM16LE; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/2/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-0x800)/2/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - vgmstream->meta_type = meta_RSD4PCM; - - /* 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; -} - - - -/* RSD4PCMB - Big Endian */ -VGMSTREAM * init_vgmstream_rsd4pcmb(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534434) /* RSD4 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x50434D42) /* PCMB */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x80; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PCM16BE; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/2/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-0x800)/2/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - vgmstream->meta_type = meta_RSD4PCMB; - - /* 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; -} - -/* RSD4RADP */ -VGMSTREAM * init_vgmstream_rsd4radp(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534434) /* RSD4 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x52414450) /* RADP */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_RAD_IMA; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/0x14/channel_count*32; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-start_offset)*28/16/channel_count; - } - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x14*channel_count; - vgmstream->meta_type = meta_RSD4RADP; - - /* 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].offset=vgmstream->ch[i].channel_start_offset=start_offset; - } - } - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -/* RSD6RADP */ -VGMSTREAM * init_vgmstream_rsd6radp(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534436) /* RSD6 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x52414450) /* RADP */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_RAD_IMA; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/0x14/channel_count*32; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-start_offset)*28/16/channel_count; - } - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x14*channel_count; - vgmstream->meta_type = meta_RSD6RADP; - - /* 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].offset=vgmstream->ch[i].channel_start_offset=start_offset; - } - } - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -/* RSD6VAG */ -VGMSTREAM * init_vgmstream_rsd6vag(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("rsd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534436) /* RSD6 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x56414720) /* VAG\0x20 */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0xC,streamFile); - vgmstream->meta_type = meta_RSD6VAG; - - /* 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; -} - - -/* RSD6WADP */ -VGMSTREAM * init_vgmstream_rsd6wadp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - /* check extension, case insensitive */ - if (!check_extensions(streamFile,"rsd")) - goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534436) /* RSD6 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x57414450) /* WADP */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - start_offset = 0x800; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; - - vgmstream->coding_type = coding_NGC_DSP_subint; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x02; //read_32bitLE(0xC,streamFile); - vgmstream->meta_type = meta_RSD6WADP; - - dsp_read_coefs_be(vgmstream,streamFile,0x1a4,0x28); - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -/* RSD6OGG */ -VGMSTREAM * init_vgmstream_rsd6oogv(STREAMFILE *streamFile) { -#ifdef VGM_USE_VORBIS - off_t start_offset; - - /* check extension */ - if (!check_extensions(streamFile, "rsd")) - goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x52534436) /* RSD6 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x4F4F4756) /* OOGV */ - goto fail; - - { - ogg_vorbis_meta_info_t ovmi = {0}; - VGMSTREAM * result = NULL; - - ovmi.meta_type = meta_RSD6OOGV; - - start_offset = 0x800; - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); - - if (result != NULL) { - return result; - } - } - -fail: - /* clean up anything we may have opened */ -#endif - return NULL; -} - -/* RSD6XADP - from Crash Tag Team Racing (Xbox), Scarface (Xbox) */ -VGMSTREAM * init_vgmstream_rsd6xadp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - size_t data_size; - - /* check extension */ - if (!check_extensions(streamFile,"rsd")) - goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x52534436) /* RSD6 */ - goto fail; - if (read_32bitBE(0x4,streamFile) != 0x58414450) /* XADP */ - goto fail; - - start_offset = 0x800; - data_size = get_streamfile_size(streamFile) - start_offset; - loop_flag = 0; - channel_count = read_32bitLE(0x8,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); - - vgmstream->meta_type = meta_RSD6XADP; - vgmstream->coding_type = (channel_count > 2) ? coding_XBOX_IMA_mch : coding_XBOX_IMA; - vgmstream->layout_type = layout_none; - - - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/* RSD6XMA */ -VGMSTREAM * init_vgmstream_rsd6xma(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - uint32_t version; - - /* check extension, case insensitive */ - if (!check_extensions(streamFile,"rsd")) - goto fail; - - /* check header */ - if (read_32bitBE(0x0, streamFile) != 0x52534436) /* RSD6 */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x584D4120) /* XMA */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x8, streamFile); - version = read_32bitBE(0x80C, streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = read_32bitBE(0x800, streamFile) + read_32bitBE(0x804, streamFile) + 0xc; /* assumed, seek table always at 0x800 */ - vgmstream->channels = channel_count; - vgmstream->meta_type = meta_RSD6XMA; - vgmstream->sample_rate = read_32bitBE(0x818, streamFile); - - switch (version) { - case 0x03010000: { - vgmstream->num_samples = read_32bitBE(0x824, streamFile); - - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - size_t bytes, datasize, block_size, block_count; - - block_count = read_32bitBE(0x828, streamFile); - block_size = 0x10000; - datasize = read_32bitBE(0x808, streamFile); - - bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, datasize); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */ - //xma_fix_raw_samples(vgmstream, streamFile, start_offset,datasize, 0, 0,0); - ffmpeg_set_skip_samples(ffmpeg_data, 512+64); - } -#else - goto fail; -#endif - break; - } - case 0x04010000: { - vgmstream->num_samples = read_32bitBE(0x814, streamFile); - - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - size_t bytes, datasize, block_size, block_count; - - block_count = read_32bitBE(0x830, streamFile); - block_size = 0x10000; - datasize = read_32bitBE(0x808, streamFile); - - bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, datasize); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */ - //xma_fix_raw_samples(vgmstream, streamFile, start_offset,datasize, 0, 0,0); - ffmpeg_set_skip_samples(ffmpeg_data, 512+64); - } -#else - goto fail; -#endif - break; - } - default: - goto fail; - } - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -/* RSD6AT3+ [Crash of the Titans (PSP)] */ -VGMSTREAM * init_vgmstream_rsd6at3p(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - size_t data_size; - int loop_flag, channel_count; - - - /* check extension, case insensitive */ - if (!check_extensions(streamFile,"rsd")) - goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52534436) /* "RSD6" */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x4154332B) /* "AT3+" */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x08, streamFile); - start_offset = 0x800; - data_size = get_streamfile_size(streamFile) - start_offset; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_RSD6AT3P; - vgmstream->sample_rate = read_32bitLE(0x10, streamFile); - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - - /* full RIFF header at start_offset */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - if (channel_count != ffmpeg_data->channels) goto fail; - - vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact samples */ - - if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ - ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); - } -#else - goto fail; -#endif - - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/* RSD6WMA [Scarface (Xbox)] */ -VGMSTREAM * init_vgmstream_rsd6wma(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - size_t data_size; - int loop_flag, channel_count; + int loop_flag, channel_count, sample_rate, interleave; + uint32_t codec; + uint8_t version; /* checks */ if (!check_extensions(streamFile,"rsd")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x52534436) /* "RSD6" */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x574D4120) /* "WMA " */ + if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */ goto fail; loop_flag = 0; + + codec = read_32bitBE(0x04,streamFile); channel_count = read_32bitLE(0x08, streamFile); - start_offset = 0x800; + /* 0x0c: always 16? */ + sample_rate = read_32bitLE(0x10, streamFile); + + version = read_8bit(0x03, streamFile); + switch(version) { + case '2': /* known codecs: VAG/XADP/PCMB [The Simpsons: Road Rage] */ + case '3': /* known codecs: VAG/PCM/PCMB/GADP? [Dark Summit] */ + interleave = read_32bitLE(0x14,streamFile); /* VAG only, 0x04 otherwise */ + start_offset = read_32bitLE(0x18,streamFile); + name_offset = 0; + break; + + case '4': /* known codecs: VAG/PCM/RADP/PCMB [The Simpsons: Hit & Run, Tetris Worlds, Hulk] */ + /* 0x14: padding */ + /* 0x18: padding */ + interleave = 0; + start_offset = 0x800; + name_offset = 0; + + /* PCMB/PCM/GADP normally start early but sometimes have padding [The Simpsons: Hit & Run (GC/Xbox)] */ + if ((codec == 0x50434D20 || codec == 0x550434D42 || codec == 0x47414450) + && read_32bitLE(0x80,streamFile) != 0x2D2D2D2D) + start_offset = 0x80; + break; + + case '6': /* known codecs: VAG/XADP/WADP/RADP/OOGV/AT3+/XMA [Hulk 2, Crash Tag Team Racing, Crash: Mind over Mutant, Scarface] */ + /* 0x14: padding */ + name_offset = 0x18; /* dev file path */ + interleave = 0; + start_offset = 0x800; + break; + + default: + goto fail; + } + data_size = get_streamfile_size(streamFile) - start_offset; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->meta_type = meta_RSD6WMA; - //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile) / channel_count / 2; /* may be PCM data size, but not exact */ - vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, streamFile); + vgmstream->meta_type = meta_RSD; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case 0x50434D20: /* "PCM " [Dark Summit (Xbox), Hulk (PC)] */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); + break; + + case 0x50434D42: /* "PCMB" [The Simpsons: Road Rage (GC), Dark Summit (GC)] */ + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); + break; + + case 0x56414720: /* "VAG " [The Simpsons: Road Rage (PS2), Crash Tag Team Racing (PSP)] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = (interleave == 0) ? 0x10 : interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + break; + + case 0x58414450: /* "XADP" [The Simpsons: Road Rage (Xbox)], Crash Tag Team Racing (Xbox)] */ + vgmstream->coding_type = (channel_count > 2) ? coding_XBOX_IMA_mch : coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); + break; + + case 0x47414450: /* "GADP" [Hulk (GC)] */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; /* assumed, known files are mono */ + dsp_read_coefs_le(vgmstream,streamFile,0x14,0x2e); /* LE! */ + dsp_read_hist_le (vgmstream,streamFile,0x38,0x2e); + + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + break; + + case 0x57414450: /* "WADP" [Crash: Mind Over Mutant (Wii)] */ + vgmstream->coding_type = coding_NGC_DSP_subint; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x02; + dsp_read_coefs_be(vgmstream,streamFile,0x1a4,0x28); + dsp_read_hist_be (vgmstream,streamFile,0x1c8,0x28); + + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + break; + + case 0x52414450: /* "RADP" [The Simpsons: Hit & Run (GC), Scarface (Wii)] */ + vgmstream->coding_type = coding_RAD_IMA; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x14*channel_count; + + vgmstream->num_samples = data_size / 0x14 / channel_count * 32; /* bytes-to-samples */ + break; + +#ifdef VGM_USE_VORBIS + case 0x4F4F4756: { /* "OOGV" [Scarface (PC)] */ + ogg_vorbis_meta_info_t ovmi = {0}; + + ovmi.meta_type = meta_RSD; + close_vgmstream(vgmstream); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); + if (!vgmstream) goto fail; + break; + } +#endif #ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; + case 0x574D4120: { /* "WMA " [Scarface (Xbox)] */ + ffmpeg_codec_data *ffmpeg_data = NULL; - /* mini header + WMA header at start_offset */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset+0x08,data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; + /* mini header + WMA header at start_offset */ + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset+0x08,data_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; - vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */ - } -#else - goto fail; + vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */ + //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile) / channel_count / 2; /* may be PCM data size, but not exact */ + vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, streamFile); + break; + } + + case 0x4154332B: { /* "AT3+" [Crash of the Titans (PSP)] */ + int fact_samples = 0; + + vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, &fact_samples); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = fact_samples; + break; + } + + case 0x584D4120: { /* "XMA " [Crash of the Titans (X360)-v1, Crash: Mind over Mutant (X360)-v2] */ + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[0x100]; + size_t bytes, xma_size, block_size, block_count; + int xma_version; + + + /* skip mini header */ + start_offset = read_32bitBE(0x800, streamFile) + read_32bitBE(0x804, streamFile) + 0xc; /* assumed, seek table always at 0x800 */ + xma_size = read_32bitBE(0x808, streamFile); + xma_version = read_32bitBE(0x80C, streamFile); + + switch (xma_version) { + case 0x03010000: + vgmstream->sample_rate = read_32bitBE(0x818, streamFile); + vgmstream->num_samples = read_32bitBE(0x824, streamFile); + block_count = read_32bitBE(0x828, streamFile); + block_size = 0x10000; + break; + case 0x04010000: + vgmstream->num_samples = read_32bitBE(0x814, streamFile); + vgmstream->sample_rate = read_32bitBE(0x818, streamFile); + block_count = read_32bitBE(0x830, streamFile); + block_size = 0x10000; + break; + default: + goto fail; + } + + bytes = ffmpeg_make_riff_xma2(buf,sizeof(buf), vgmstream->num_samples, xma_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, xma_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */ + //xma_fix_raw_samples(vgmstream, streamFile, start_offset,xma_size, 0, 0,0); + ffmpeg_set_skip_samples(ffmpeg_data, 512+64); + break; + } #endif + default: + goto fail; + } + + if (name_offset) + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rws.c b/Frameworks/vgmstream/vgmstream/src/meta/rws.c index 563a70584..ba67b565e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rws.c @@ -7,20 +7,24 @@ static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile); typedef struct { + int big_endian; + + uint32_t codec; int channel_count; - int codec; int sample_rate; + off_t file_name_offset; + int total_segments; int target_segment; off_t segment_offset; - size_t segment_size; + size_t segment_layers_size; off_t segment_name_offset; int total_layers; int target_layer; - off_t layer_offset; - size_t layer_size; + off_t layer_start; + //size_t layer_size; off_t layer_name_offset; size_t file_size; @@ -28,14 +32,13 @@ typedef struct { size_t data_size; off_t data_offset; - //size_t stream_size; + size_t usable_size; size_t block_size; - size_t block_size_total; - size_t stream_size_full; + size_t block_layers_size; off_t coefs_offset; - int use_segment_subsongs; /* otherwise play the whole thing */ + char readable_name[STREAM_NAME_SIZE]; } rws_header; @@ -43,7 +46,6 @@ typedef struct { VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, offset; - off_t stream_offset, name_offset; size_t stream_size; int loop_flag; int i; @@ -56,99 +58,112 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (!check_extensions(streamFile,"rws")) goto fail; - /* parse chunks (always LE) */ - /* RWS are made of a file chunk with header and data chunks (other chunks exist for non-audio .RWS). - * A chunk is: id, size, RW version (no real diffs), data of size (version is repeated but same for all chunks). - * Version: 16b main + 16b build (can vary between files) ex: 0c02, 1003, 1400 = 3.5, 1803 = 3.6, 1C02 = 3.7. */ + /* Audio .RWS is made of file + header + data chunks (non-audio .RWS with other chunks exist). + * Chunk format (LE): id, size, RW version, data of size (version is repeated but same for all chunks). + * Version is 16b main + 16b build (possibly shifted), no known differences between versions, + * and can vary between files of a game. ex: 0c02, 1003, 1400 = 3.5, 1803 = 3.6, 1C02 = 3.7. */ - if (read_32bitLE(0x00,streamFile) != 0x0000080d) /* audio file chunk id */ + /* parse audio chunks */ + if (read_32bitLE(0x00,streamFile) != 0x0000080d) /* audio file id */ goto fail; - rws.file_size = read_32bitLE(0x04,streamFile); /* audio file chunk size */ + rws.file_size = read_32bitLE(0x04, streamFile); /* audio file size */ if (rws.file_size + 0x0c != get_streamfile_size(streamFile)) goto fail; - if (read_32bitLE(0x0c,streamFile) != 0x0000080e) /* header chunk id */ + if (read_32bitLE(0x0c,streamFile) != 0x0000080e) /* header id */ goto fail; - rws.header_size = read_32bitLE(0x10,streamFile); /* header chunk size */ + rws.header_size = read_32bitLE(0x10, streamFile); /* header size */ rws.data_offset = 0x0c + 0x0c + rws.header_size; /* usually 0x800 but not always */ - if (read_32bitLE(rws.data_offset+0x00,streamFile) != 0x0000080f) /* data chunk id */ + if (read_32bitLE(rws.data_offset + 0x00, streamFile) != 0x0000080f) /* data chunk id */ goto fail; - rws.data_size = read_32bitLE(rws.data_offset+0x04,streamFile); /* data chunk size */ + rws.data_size = read_32bitLE(rws.data_offset + 0x04, streamFile); /* data chunk size */ if (rws.data_size+0x0c + rws.data_offset != get_streamfile_size(streamFile)) goto fail; - /* inside header chunk (many unknown fields are probably IDs/config, as two same-sized files vary a lot) */ + /* inside header chunk (many unknown fields are probably IDs/config/garbage, + * as two files of the same size vary a lot) */ offset = 0x0c + 0x0c; + rws.big_endian = guess_endianness32bit(offset + 0x00, streamFile); /* GC/Wii/X360 */ + read_32bit = rws.big_endian ? read_32bitBE : read_32bitLE; /* base header */ - /* 0x00: actual header size (less than chunk size), 0x04/08/10: sizes of various sections?, 0x14/18/24/2C: commands? - * 0x1c: null? 0x30: 0x800?, 0x34: block_size_total?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid */ - read_32bit = (read_32bitLE(offset+0x00,streamFile) > rws.header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */ - rws.total_segments = read_32bit(offset+0x20,streamFile); - rws.total_layers = read_32bit(offset+0x28,streamFile); - if (rws.total_segments > 1 && rws.total_layers > 1) { - VGM_LOG("RWS: unknown segments+layers layout\n"); + { + /* 0x00: actual header size (less than chunk size) */ + /* 0x04/08/10: sizes of various sections? */ + /* 0x14/18: config? */ + /* 0x1c: null? */ + rws.total_segments = read_32bit(offset + 0x20, streamFile); + /* 0x24: config? */ + rws.total_layers = read_32bit(offset + 0x28, streamFile); + /* 0x2c: config? */ + /* 0x30: 0x800? */ + /* 0x34: block_layers_size? */ + /* 0x38: data offset */ + /* 0x3c: 0? */ + /* 0x40-50: file uuid */ + offset += 0x50; } /* audio file name */ - offset += 0x50 + get_rws_string_size(offset+0x50, streamFile); - + { + rws.file_name_offset = offset; + offset += get_rws_string_size(offset, streamFile); + } /* RWS data can be divided in two ways: + * - "substreams" (layers): interleaved blocks, for fake multichannel L/R+C/S+LS/RS [Burnout 2 (GC/Xbox)] + * or song variations [Neighbours From Hell (Xbox/GC)]. Last layer may have padding to keep chunks aligned: + * ex.- 0x1700 data of substream_0 2ch, 0x1700 data + 0x200 pad of substream1 2ch, repeat until end * - "segments": cues/divisions within data, like intro+main/loop [[Max Payne 2 (PS2), Nana (PS2)] * or voice1+2+..N, song1+2+..N [Madagascar (PS2), The Legend of Spyro: Dawn of the Dragon (X360)] - * - "streams" (layers): interleaved blocks, like L/R+C/S+LS/RS [Burnout 2 (GC/Xbox)]. Last stream has padding: - * ex.- 1 block: 0x1800 data of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch. * - * Layers seem only used to fake multichannel, but as they are given sample rate/channel/codec/coefs/etc - * they are treated as subsongs. Similarly segments can be treated as subsongs in some cases. - * They don't seem used at the same time, though could be possible. */ + * As each layer is given sample rate/channel/codec/etc they are treated as full subsongs, though usually + * all layers are the same. Segments are just divisions and can be played one after another, but are useful + * to play as subsongs. Since both can exist at the same time (rarely) we make layers*segments=subsongs. + * Ex.- subsong1=layer1 blocks in segment1, subsong2=layer2 blocks in segment1, subsong3=layer1 blocks in segment2, ... + * + * Segment1 Segment2 + * +-------------------------------------------+----------------- + * |Layer1|Layer2|(pad)|...|Layer1|Layer2|(pad)|Layer1|Layer2|... + * -------------------------------------------------------------- + */ - /* Use either layers or segments as subsongs (with layers having priority). This divides Nana (PS2) - * or Max Payne 2 (PS2) intro+main, so it could be adjusted to >2 (>4 for Max Payne 2) if undesired */ if (target_subsong == 0) target_subsong = 1; - rws.use_segment_subsongs = (rws.total_layers == 1 && rws.total_segments > 1); - if (rws.use_segment_subsongs) { - rws.target_layer = 1; - rws.target_segment = target_subsong; - total_subsongs = rws.total_segments; - } - else { - rws.target_layer = target_subsong; - rws.target_segment = 0; /* none = play all */ - total_subsongs = rws.total_layers; - } + rws.target_layer = ((target_subsong-1) % rws.total_layers) + 1; + rws.target_segment = ((target_subsong-1) / rws.total_layers) + 1; + total_subsongs = rws.total_layers * rws.total_segments; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - /* segment info, for all layers */ - /* 0x00/04/0c: command?, 0x18: full segment size (including padding), 0x1c: offset, others: ?) */ + /* segment info */ for (i = 0; i < rws.total_segments; i++) { if (i+1 == rws.target_segment) { - rws.segment_offset = read_32bit(offset + 0x20*i + 0x1c,streamFile); + /* 0x00/04/0c: config? */ + /* others: ? */ + rws.segment_layers_size = read_32bit(offset + 0x18, streamFile); /* sum of all including padding */ + rws.segment_offset = read_32bit(offset + 0x1c, streamFile); } - rws.stream_size_full += read_32bit(offset + 0x20*i + 0x18,streamFile); + offset += 0x20; } - offset += 0x20 * rws.total_segments; - /* usable segment/layer sizes (assumed either one, sometimes incorrect size?) */ - for (i = 0; i < (rws.total_segments * rws.total_layers); i++) { /* sum usable segment sizes (no padding) */ - size_t usable_size = read_32bit(offset + 0x04*i,streamFile); /* size without padding */ - if (i+1 == rws.target_segment) { - rws.segment_size = usable_size; - } - if (i+1 == rws.target_layer || rws.total_layers == 1) { - rws.layer_size += usable_size; + /* usable layer sizes per segment */ + for (i = 0; i < (rws.total_segments * rws.total_layers); i++) { + size_t usable_size = read_32bit(offset, streamFile); /* without padding */ + /* size order: segment1 layer1 size, ..., segment1 layerN size, segment2 layer1 size, etc */ + if (i+1 == target_subsong) { /* order matches our subsong order */ + rws.usable_size = usable_size; } + offset += 0x04; } - offset += 0x04 * (rws.total_segments * rws.total_layers); /* segment uuids */ - offset += 0x10 * rws.total_segments; + { + offset += 0x10 * rws.total_segments; + } /* segment names */ for (i = 0; i < rws.total_segments; i++) { @@ -158,46 +173,53 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { offset += get_rws_string_size(offset, streamFile); } - /* layer info */ - /* 0x00/04/14: command?, 0x08: null? 0x0c: related to samples per frame? (XADPCM=07, VAG=1C, DSP=0E, PCM=01) - * 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */ for (i = 0; i < rws.total_layers; i++) { /* get block_sizes */ - rws.block_size_total += read_32bit(offset + 0x10 + 0x28*i, streamFile); /* for all layers, to skip during decode */ if (i+1 == rws.target_layer) { - //block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */ - rws.block_size = read_32bit(offset + 0x20 + 0x28*i, streamFile); /* without padding */ - rws.layer_offset = read_32bit(offset + 0x24 + 0x28*i, streamFile); /* within data */ + /* 0x00/04: config? */ + /* 0x08: null? */ + /* 0x0c: related to samples per frame? (XBOX-IMA=07, PSX=1C, DSP=0E, PCM=01) */ + //block_size_pad = read_32bit(offset + 0x10, streamFile); /* with padding, can be different per layer */ + /* 0x14/18: ? */ + /* 0x1c: codec related? */ + rws.block_size = read_32bit(offset + 0x20, streamFile); /* without padding */ + rws.layer_start = read_32bit(offset + 0x24, streamFile); /* skip data */ } + rws.block_layers_size += read_32bit(offset + 0x10, streamFile); /* needed to skip during decode */ + offset += 0x28; } - offset += 0x28 * rws.total_layers; /* layer config */ - /* 0x04: command?, 0x0c(1): bits per sample, others: null? */ - for (i = 0; i < rws.total_layers; i++) { /* size depends on codec so we must parse it */ - int prev_codec = 0; + for (i = 0; i < rws.total_layers; i++) { + uint32_t layer_codec = 0; if (i+1 == rws.target_layer) { - rws.sample_rate = read_32bit(offset+0x00, streamFile); - //unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */ - rws.channel_count = read_8bit(offset+0x0d, streamFile); - rws.codec = read_32bitBE(offset+0x1c, streamFile); /* uuid of 128b but first 32b is enough */ + rws.sample_rate = read_32bit(offset + 0x00, streamFile); + /* 0x04: config? */ + //rws.layer_size = read_32bit(offset + 0x08, streamFile); /* same or close to usable size */ + /* 0x0c: bits per sample */ + rws.channel_count = read_8bit(offset + 0x0d, streamFile); + /* others: ? */ + rws.codec = (uint32_t)read_32bit(offset + 0x1c, streamFile); /* 128b uuid (32b-16b-16b-8b*8) but first 32b is enough */ } - prev_codec = read_32bitBE(offset+0x1c, streamFile); + layer_codec = (uint32_t)read_32bit(offset + 0x1c, streamFile); offset += 0x2c; - if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per layer */ - /* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */ + /* DSP has an extra field per layer */ + if (layer_codec == 0xF86215B0) { + /* 0x00: approx num samples? */ + /* 0x04: approx size/loop related? (can be 0) */ if (i+1 == rws.target_layer) { rws.coefs_offset = offset + 0x1c; } offset += 0x60; } - offset += 0x04; /* padding/garbage */ } /* layer uuids */ - offset += 0x10 * rws.total_layers; + { + offset += 0x10 * rws.total_layers; + } /* layer names */ for (i = 0; i < rws.total_layers; i++) { @@ -207,35 +229,49 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { offset += get_rws_string_size(offset, streamFile); } - - /* rest is padding/garbage until chunk end (may contain strings and weird stuff) */ + /* rest is padding/garbage until chunk end (may contain strings and uninitialized memory) */ // ... + start_offset = rws.data_offset + 0x0c + (rws.segment_offset + rws.layer_start); + stream_size = rws.usable_size; - if (rws.use_segment_subsongs) { - stream_offset = rws.segment_offset; - stream_size = rws.segment_size; - name_offset = rws.segment_name_offset; - } - else { - stream_offset = rws.layer_offset; - stream_size = rws.layer_size; - name_offset = rws.layer_name_offset; - } - start_offset = rws.data_offset + 0x0c + stream_offset; - - - /* sometimes it's wrong in XBOX-IMA for no apparent reason (probably a bug in RWS) */ - if (!rws.use_segment_subsongs) { - size_t stream_size_expected = (rws.stream_size_full / rws.block_size_total) * (rws.block_size * rws.total_layers) / rws.total_layers; - if (stream_size > stream_size_expected) { - VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected); - stream_size = stream_size_expected; + /* sometimes segment/layers go over file size in XBOX-IMA for no apparent reason, with usable_size bigger + * than segment_layers_size yet data_size being correct (bug in RWS header? maybe stops decoding on file end) */ + { + size_t expected_size = (rws.segment_layers_size / rws.block_layers_size) * (rws.block_size * rws.total_layers) / rws.total_layers; + if (stream_size > expected_size) { + VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, expected_size); + stream_size = expected_size; } } + /* build readable name */ + { + char base_name[STREAM_NAME_SIZE], file_name[STREAM_NAME_SIZE], segment_name[STREAM_NAME_SIZE], layer_name[STREAM_NAME_SIZE]; - loop_flag = 0; /* RWX doesn't seem to include actual looping (so devs may fake it with segments) */ + get_streamfile_basename(streamFile, base_name, sizeof(base_name)); + /* null terminated */ + read_string(file_name,STREAM_NAME_SIZE, rws.file_name_offset, streamFile); + read_string(segment_name,STREAM_NAME_SIZE, rws.segment_name_offset, streamFile); + read_string(layer_name,STREAM_NAME_SIZE, rws.layer_name_offset, streamFile); + + /* some internal names aren't very interesting and are stuff like "SubStream" */ + if (strcmp(base_name, file_name) == 0) { + if (rws.total_layers > 1) + snprintf(rws.readable_name,STREAM_NAME_SIZE, "%s/%s", segment_name, layer_name); + else + snprintf(rws.readable_name,STREAM_NAME_SIZE, "%s", segment_name); + } + else { + if (rws.total_layers > 1) + snprintf(rws.readable_name,STREAM_NAME_SIZE, "%s/%s/%s", file_name, segment_name, layer_name); + else + snprintf(rws.readable_name,STREAM_NAME_SIZE, "%s/%s", file_name, segment_name); + } + } + + /* seemingly no actual looping supported (devs may fake it with segments) */ + loop_flag = 0; /* build the VGMSTREAM */ @@ -246,25 +282,23 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { vgmstream->sample_rate = rws.sample_rate; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; - if (name_offset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); + strcpy(vgmstream->stream_name, rws.readable_name); vgmstream->layout_type = layout_blocked_rws; vgmstream->current_block_size = rws.block_size / vgmstream->channels; - vgmstream->full_block_size = rws.block_size_total; + vgmstream->full_block_size = rws.block_layers_size; switch(rws.codec) { - case 0x17D21BD0: /* PCM PC (17D21BD0 8735ED4E B9D9B8E8 6EA9B995) */ - case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */ - /* ex. D.i.R.T. - Origin of the Species (PC), The Legend of Spyro (X360) */ + case 0xD01BD217: /* {D01BD217,3587,4EED,B9,D9,B8,E8,6E,A9,B9,95} PCM PC/X360 */ + /* ex. D.i.R.T.: Origin of the Species (PC), The Legend of Spyro (X360) */ vgmstream->coding_type = coding_PCM16_int; - vgmstream->codec_endian = (rws.codec == 0xD01BD217); /* X360: BE */ - vgmstream->interleave_block_size = 0x02; /* only used to setup channels */ + vgmstream->codec_endian = (rws.big_endian); + vgmstream->interleave_block_size = 0x02; /* only to setup channels */ vgmstream->num_samples = pcm_bytes_to_samples(stream_size, rws.channel_count, 16); break; - case 0x9897EAD9: /* PS-ADPCM PS2 (9897EAD9 BCBB7B44 96B26547 59102E16) */ + case 0xD9EA9798: /* {D9EA9798,BBBC,447B,96,B2,65,47,59,10,2E,16} PS-ADPCM PS2 */ /* ex. Silent Hill Origins (PS2), Ghost Rider (PS2), Max Payne 2 (PS2), Nana (PS2) */ vgmstream->coding_type = coding_PSX; vgmstream->interleave_block_size = rws.block_size / 2; @@ -272,21 +306,21 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { vgmstream->num_samples = ps_bytes_to_samples(stream_size, rws.channel_count); break; - case 0xF86215B0: /* DSP GC/Wii (F86215B0 31D54C29 BD37CDBF 9BD10C53) */ + case 0xF86215B0: /* {F86215B0,31D5,4C29,BD,37,CD,BF,9B,D1,0C,53} DSP GC/Wii */ /* ex. Burnout 2 (GC), Alice in Wonderland (Wii) */ vgmstream->coding_type = coding_NGC_DSP; vgmstream->interleave_block_size = rws.block_size / 2; - /* get coefs (all channels share them so 0 spacing; also seem fixed for all RWS) */ - dsp_read_coefs_be(vgmstream,streamFile,rws.coefs_offset, 0); + /* get coefs (all channels share them; also seem fixed for all RWS) */ + dsp_read_coefs_be(vgmstream, streamFile, rws.coefs_offset, 0); vgmstream->num_samples = dsp_bytes_to_samples(stream_size, rws.channel_count); break; - case 0x936538EF: /* XBOX-IMA PC (936538EF 11B62D43 957FA71A DE44227A) */ - case 0x2BA22F63: /* XBOX-IMA Xbox (2BA22F63 DD118F45 AA27A5C3 46E9790E) */ + case 0xEF386593: /* {EF386593,B611,432D,95,7F,A7,1A,DE,44,22,7A} XBOX-IMA PC */ + case 0x632FA22B: /* {632FA22B,11DD,458F,AA,27,A5,C3,46,E9,79,0E} XBOX-IMA Xbox */ /* ex. Broken Sword 3 (PC), Jacked (PC/Xbox), Burnout 2 (Xbox) */ - vgmstream->coding_type = coding_XBOX_IMA; /* PC and Xbox share the same data */ + vgmstream->coding_type = coding_XBOX_IMA; /* same data though different uuid */ vgmstream->interleave_block_size = 0; vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, rws.channel_count); @@ -298,7 +332,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; return vgmstream; @@ -308,11 +342,11 @@ fail: } -/* rws-strings are null-terminated then padded to 0x10 (weirdly the padding contains garbage) */ +/* rws-strings are null-terminated then padded to 0x10 (weirdly enough the padding contains garbage) */ static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile) { int i; - for (i = 0; i < 0x800; i++) { /* 0x800=arbitrary max */ - if (read_8bit(offset+i,streamFile) == 0) { /* null terminator */ + for (i = 0; i < 255; i++) { /* arbitrary max */ + if (read_8bit(offset+i, streamFile) == 0) { /* null terminator */ return i + (0x10 - (i % 0x10)); /* size is padded */ } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/s14_sss.c b/Frameworks/vgmstream/vgmstream/src/meta/s14_sss.c index e1aed3303..71b87cf0d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/s14_sss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/s14_sss.c @@ -25,11 +25,13 @@ VGMSTREAM * init_vgmstream_s14_sss(STREAMFILE *streamFile) { char filename[PATH_LIMIT]; streamFile->get_name(streamFile,filename,sizeof(filename)); - /* horrid but I ain't losing sleep over it (besides the header must be somewhere as some tracks loop) */ - if (strstr(filename,"S037")==filename || strstr(filename,"b06")==filename) /* Korogashi Puzzle Katamari Damacy */ + /* horrid but I ain't losing sleep over it (besides the header is often incrusted in-code as some tracks loop) */ + if (strstr(filename,"S037")==filename || strstr(filename,"b06")==filename || /* Korogashi Puzzle Katamari Damacy */ + strstr(filename,"_48kbps")!=NULL) /* Taiko no Tatsujin DS 1/2 */ interleave = 0x78; else if (strstr(filename,"32700")==filename || /* Hottarake no Shima - Kanata to Nijiiro no Kagami */ - (strstr(filename,"b0")==filename || strstr(filename,"puzzle")==filename || strstr(filename,"M09")==filename) ) /* Korogashi Puzzle Katamari Damacy */ + strstr(filename,"b0")==filename || strstr(filename,"puzzle")==filename || strstr(filename,"M09")==filename || /* Korogashi Puzzle Katamari Damacy */ + strstr(filename,"_32kbps")!=NULL) /* Taiko no Tatsujin DS 1/2 */ interleave = 0x50; else interleave = 0x3c; /* The Idolm@ster - Dearly Stars */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/seb.c b/Frameworks/vgmstream/vgmstream/src/meta/seb.c new file mode 100644 index 000000000..657a7352c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/seb.c @@ -0,0 +1,53 @@ +#include "meta.h" + + +/* .seb - Game Arts games [Grandia (PS1), Grandia II/III/X (PS2)] */ +VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + /* .seb: found in Grandia II (PS2) .idx */ + /* .gms: fake? (.stz+idx bigfile without names, except in Grandia II) */ + if (!check_extensions(streamFile, "seb,gms,")) + goto fail; + + channel_count = read_32bitLE(0x00,streamFile); + if (channel_count > 2) goto fail; /* mono or stereo */ + /* 0x08/0c: unknown count, possibly related to looping */ + + start_offset = 0x800; + + if (read_32bitLE(0x10,streamFile) > get_streamfile_size(streamFile) || /* loop start offset */ + read_32bitLE(0x18,streamFile) > get_streamfile_size(streamFile)) /* loop end offset */ + goto fail; + /* in Grandia III sometimes there is a value at 0x24/34 */ + + loop_flag = (read_32bitLE(0x20,streamFile) == 0); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SEB; + vgmstream->sample_rate = read_32bitLE(0x04,streamFile); + + vgmstream->num_samples = read_32bitLE(0x1c,streamFile); + vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile); + vgmstream->loop_end_sample = read_32bitLE(0x1c,streamFile); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x800; + + 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/seg.c b/Frameworks/vgmstream/vgmstream/src/meta/seg.c index 9a09210af..87e6f85b2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/seg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/seg.c @@ -67,11 +67,14 @@ VGMSTREAM * init_vgmstream_seg(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x2000; + vgmstream->interleave_first_skip = 0x60; + vgmstream->interleave_first_block_size = vgmstream->interleave_block_size - vgmstream->interleave_first_skip; /* standard dsp header at start_offset */ dsp_read_coefs_be(vgmstream, streamFile, start_offset+0x1c, vgmstream->interleave_block_size); dsp_read_hist_be(vgmstream, streamFile, start_offset+0x40, vgmstream->interleave_block_size); - //todo first_interleave: 0x2000 - 60 + + start_offset += vgmstream->interleave_first_skip; break; case 0x70635F00: /* "pc_\0" */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index 1e4454631..f05bb748f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -125,18 +125,13 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */ - ffmpeg_codec_data *ffmpeg_data; - - /* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, NULL); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ - ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); - /* SGXD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ + /* SGXD's sample rate has priority over RIFF's sample rate (may not match) */ + /* loop/sample values are relative (without skip) vs RIFF (with skip), matching "smpl" otherwise */ break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/smk.c b/Frameworks/vgmstream/vgmstream/src/meta/smk.c new file mode 100644 index 000000000..72113b998 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/smk.c @@ -0,0 +1,187 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util.h" + +static int smacker_get_info(STREAMFILE *streamFile, int target_subsong, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples); + +/* SMK - RAD Game Tools Smacker movies (audio/video format) */ +VGMSTREAM * init_vgmstream_smk(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + size_t stream_size; + + + /* checks */ + if (!check_extensions(streamFile,"smk")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x534D4B32 && /* "SMK2" */ + read_32bitBE(0x00,streamFile) != 0x534D4B34) /* "SMK4" */ + goto fail; + + /* find target stream info */ + if (!smacker_get_info(streamFile, target_subsong, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples)) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->layout_type = layout_none; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->meta_type = meta_SMACKER; + + { +#ifdef VGM_USE_FFMPEG + /* target_subsong should be passed manually */ + vgmstream->codec_data = init_ffmpeg_header_offset_subsong(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), target_subsong); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; +#else + goto fail; +#endif + } + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +typedef enum { + SMK_AUDIO_PACKED = (1<<7), + SMK_AUDIO_PRESENT = (1<<6), + SMK_AUDIO_16BITS = (1<<5), + SMK_AUDIO_STEREO = (1<<4), + SMK_AUDIO_BINK_RDFT = (1<<3), + SMK_AUDIO_BINK_DCT = (1<<2), + //SMK_AUD_UNUSED1 = (1<<1), + //SMK_AUD_UNUSED0 = (1<<0), +} smk_audio_flag; + +//todo test multilang streams and codecs other than SMACKAUD +/* Gets stream info, and number of samples in a file by reading frames + * info: https://wiki.multimedia.cx/index.php/Smacker */ +static int smacker_get_info(STREAMFILE *sf, int target_subsong, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { + STREAMFILE *sf_index = NULL; + uint32_t flags, total_frames, trees_sizes; + off_t size_offset, type_offset, data_offset; + int i, j, sample_rate = 0, channel_count = 0, num_samples = 0; + int total_subsongs, target_stream = 0; + size_t stream_size = 0; + uint8_t stream_flags = 0; + + + /* rough format: + * - header (id, frames, video info/config, audio info) + * - frame sizes table + * - frame info table + * - huffman trees + * - frame data + */ + + /* read header */ + total_frames = read_u32le(0x0c,sf); + if (total_frames <= 0 || total_frames > 0x100000) goto fail; /* something must be off */ + + flags = read_u32le(0x14,sf); + if (flags & 1) /* extra "ring frame" */ + total_frames += 1; + + trees_sizes = read_u32le(0x34,sf); + + if (target_subsong == 0) target_subsong = 1; + total_subsongs = 0; + for (i = 0; i < 7; i++) { /* up to 7 audio (multilang?) streams */ + uint32_t audio_info = read_u32le(0x48 + 0x04*i,sf); + uint8_t audio_flags = (audio_info >> 24) & 0xFF; + int audio_rate = audio_info & 0x00FFFFFF; + + if (audio_flags & SMK_AUDIO_PRESENT) { + total_subsongs++; + + if (target_subsong == total_subsongs) { + target_stream = i; + sample_rate = audio_rate & 0x00FFFFFF; + channel_count = (audio_flags & SMK_AUDIO_STEREO) ? 2 : 1; + stream_flags = audio_flags; + } + } + } + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + if (sample_rate == 0 || channel_count == 0) goto fail; + + /* read size and type tables into buffer */ + sf_index = reopen_streamfile(sf, total_frames*0x04 + total_frames*0x01); + if (!sf_index) goto fail; + + /* read frames and sum all samples, since some codecs are VBR */ + size_offset = 0x68; + type_offset = size_offset + total_frames*0x04; + data_offset = type_offset + total_frames*0x01 + trees_sizes; + for (i=0; i < total_frames; i++) { + uint32_t frame_size = read_u32le(size_offset,sf_index) & 0xFFFFFFFC; /* last 2 bits are keyframe flags */ + uint8_t frame_type = read_u8 (type_offset,sf_index); /* 0: has palette, 1..7: has stream N) */ + off_t offset = data_offset; + + /* skip palette */ + if (frame_type & (1<<0)) { + uint8_t palette_size = read_u8(offset,sf); + offset += palette_size * 4; + } + + /* read target audio packet and ignore rest (though probably all streams are the same) */ + for (j = 0; j < 7; j++) { + uint32_t audio_size; + + /* check if stream N exists in this frame (supposedly streams can be go in separate frames) */ + if ( !(frame_type & (1<<(j+1))) ) + continue; + + audio_size = read_u32le(offset,sf); + + if (j == target_stream) { + + /* resulting PCM bytes to samples */ + if (stream_flags & SMK_AUDIO_PACKED) { /* Smacker and maybe Bink codecs */ + uint32_t unpacked_size = read_u32le(offset+0x04,sf); + num_samples += unpacked_size / (0x02 * channel_count); + } + else if (stream_flags & SMK_AUDIO_16BITS) { /* PCM16 */ + num_samples += (audio_size - 0x04) / (0x02 * channel_count); + } + else { /* PCM8 */ + num_samples += (audio_size - 0x04) / (0x01 * channel_count); + } + } + + stream_size += audio_size; + offset += audio_size; + } + + /* rest is video packet (size = offset - data_offset) */ + + size_offset += 0x04; + type_offset += 0x01; + data_offset += frame_size; + } + + if (out_total_subsongs) *out_total_subsongs = total_subsongs; + if (out_stream_size) *out_stream_size = stream_size; + if (out_sample_rate) *out_sample_rate = sample_rate; + if (out_channel_count) *out_channel_count = channel_count; + if (out_num_samples) *out_num_samples = num_samples; + + close_streamfile(sf_index); + return 1; + +fail: + close_streamfile(sf_index); + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/smp.c b/Frameworks/vgmstream/vgmstream/src/meta/smp.c index 2ddd52999..8549b6787 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/smp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/smp.c @@ -48,17 +48,13 @@ VGMSTREAM * init_vgmstream_smp(STREAMFILE *streamFile) { switch(codec) { #ifdef VGM_USE_FFMPEG case 0x01: { - uint8_t buf[0x100]; - int bytes, block_size, joint_stereo, skip_samples; - + int block_align, encoder_delay; if (bps != 16) goto fail; - block_size = 0x98 * vgmstream->channels; - joint_stereo = 0; - skip_samples = 0; /* unknown */ + block_align = 0x98 * vgmstream->channels; + encoder_delay = 0; /* 1024 looks ok, but num_samples needs to be adjusted too */ - bytes = ffmpeg_make_riff_atrac3(buf,sizeof(buf), vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, skip_samples); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index d2f0d69c8..e17e803de 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -300,7 +300,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->num_samples = read_32bitBE(start_offset+0x00,streamFile); if (loop_flag) { vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end+1; + vgmstream->loop_end_sample = loop_end + 1; } for (i = 1; i < channel_count; i++) { @@ -349,29 +349,24 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->num_samples = ffmpeg_data->totalSamples; vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; + vgmstream->loop_end_sample = loop_end; //todo +1? xma_fix_raw_samples(vgmstream, streamFile, start_offset,stream_size, 0, 0,0); /* samples are ok, loops? */ break; } case 0x0E: { /* ATRAC3/ATRAC3plus [Lord of Arcana (PSP), Final Fantasy Type-0] */ - ffmpeg_codec_data *ffmpeg_data = NULL; + int fact_samples = 0; - /* full RIFF header at start_offset/extradata_offset (same) */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, &fact_samples); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact samples */ + vgmstream->num_samples = fact_samples; vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; - - if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ - ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); - /* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ + vgmstream->loop_end_sample = loop_end + 1; + /* loop/sample values are relative (without skip) vs RIFF (with skip), matching "smpl" otherwise */ break; } #endif @@ -391,8 +386,12 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->num_samples = read_32bit(extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */ - vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start - vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end + vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile); + vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, streamFile) + 1; + if (loop_flag) { + vgmstream->loop_start_sample -= cfg.encoder_delay; + vgmstream->loop_end_sample -= cfg.encoder_delay; + } break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c index be1cafc53..66c3f4e56 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c @@ -1,186 +1,137 @@ #include "meta.h" #include "../coding/coding.h" +#include "sqex_sead_streamfile.h" -static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start); +typedef struct { + int big_endian; + + int version; + int is_sab; + int is_mab; + + int total_subsongs; + int target_subsong; + + uint16_t wave_id; + int loop_flag; + int channel_count; + int codec; + int sample_rate; + int loop_start; + int loop_end; + off_t meta_offset; + off_t extradata_offset; + size_t extradata_size; + size_t stream_size; + size_t special_size; + + off_t descriptor_offset; + size_t descriptor_size; + off_t filename_offset; + size_t filename_size; + off_t cuename_offset; + size_t cuename_size; + off_t modename_offset; + size_t modename_size; + off_t instname_offset; + size_t instname_size; + off_t sndname_offset; + size_t sndname_size; + + off_t sections_offset; + off_t snd_offset; + off_t trk_offset; + off_t musc_offset; + off_t inst_offset; + off_t mtrl_offset; + + char readable_name[STREAM_NAME_SIZE]; + +} sead_header; + +static int parse_sead(sead_header *sead, STREAMFILE *sf); + /* SABF/MABF - Square Enix's "sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, tables_offset, mtrl_offset, meta_offset, extradata_offset; //, info_offset, name_offset = 0; - size_t stream_size, descriptor_size, extradata_size, special_size; //, name_size = 0; - - - int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end; - int is_sab = 0, is_mab = 0; - int total_subsongs, target_subsong = streamFile->stream_index; - + sead_header sead = {0}; + off_t start_offset; + int target_subsong = streamFile->stream_index; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - /* check extensions (.sab: sound/bgm, .mab: music, .sbin: Dissidia Opera Omnia .sab) */ - if ( !check_extensions(streamFile,"sab,mab,sbin")) + /* checks */ + /* .sab: sound/bgm + * .mab: music + * .sbin: Dissidia Opera Omnia .sab */ + if (!check_extensions(streamFile,"sab,mab,sbin")) goto fail; - - /** main header **/ if (read_32bitBE(0x00,streamFile) == 0x73616266) { /* "sabf" */ - is_sab = 1; + sead.is_sab = 1; } else if (read_32bitBE(0x00,streamFile) == 0x6D616266) { /* "mabf" */ - is_mab = 1; + sead.is_mab = 1; } else { + /* there are other SEAD files with other chunks but similar formats too */ goto fail; } - //if (read_8bit(0x04,streamFile) != 0x02) /* version? */ - // goto fail; - /* 0x04(1): version? (usually 0x02, rarely 0x01, ex FF XV title) */ - /* 0x05(1): 0x00/01? */ - /* 0x06(2): version? (usually 0x10, rarely 0x20) */ - if (read_16bitBE(0x06,streamFile) < 0x100) { /* use some value as no apparent flag */ + sead.big_endian = guess_endianness16bit(0x06,streamFile); /* use some value as no apparent flag */ + if (sead.big_endian) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { read_32bit = read_32bitLE; read_16bit = read_16bitLE; } - /* 0x08(1): version 0x04?, 0x0a(2): ? */ - descriptor_size = read_8bit(0x09,streamFile); - if (read_32bit(0x0c,streamFile) != get_streamfile_size(streamFile)) + sead.target_subsong = target_subsong; + + if (!parse_sead(&sead, streamFile)) goto fail; - /* 0x10(n): file descriptor ("BGM", "Music", "SE", etc, long names are ok), padded */ - tables_offset = 0x10 + (descriptor_size + 0x01); /* string null seems counted for padding */ - if (tables_offset % 0x10) - tables_offset += 0x10 - (tables_offset % 0x10); - - - /** offset tables **/ - if (is_sab) { - if (read_32bitBE(tables_offset+0x00,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */ - if (read_32bitBE(tables_offset+0x10,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */ - if (read_32bitBE(tables_offset+0x20,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */ - if (read_32bitBE(tables_offset+0x30,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ - //info_offset = read_32bit(tables_offset+0x08,streamFile); - //seq_offset = read_32bit(tables_offset+0x18,streamFile); - //trk_offset = read_32bit(tables_offset+0x28,streamFile); - mtrl_offset = read_32bit(tables_offset+0x38,streamFile); - } - else if (is_mab) { - if (read_32bitBE(tables_offset+0x00,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */ - if (read_32bitBE(tables_offset+0x10,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */ - if (read_32bitBE(tables_offset+0x20,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ - //info_offset = read_32bit(tables_offset+0x08,streamFile); - //inst_offset = read_32bit(tables_offset+0x18,streamFile); - mtrl_offset = read_32bit(tables_offset+0x28,streamFile); - } - else { - goto fail; - } - /* each section starts with: - * 0x00(2): 0x00/01?, 0x02: size? (0x10), 0x04(2): entries, 0x06+: padded to 0x10 - * 0x10+0x04*entry: offset from section start, also padded to 0x10 at the end */ - - /* find meta_offset in mtrl and total subsongs */ - { - int i; - int entries = read_16bit(mtrl_offset+0x04,streamFile); - off_t entries_offset = mtrl_offset + 0x10; - - if (target_subsong == 0) target_subsong = 1; - total_subsongs = 0; - meta_offset = 0; - - /* manually find subsongs as entries can be dummy (ex. sfx banks in Dissidia Opera Omnia) */ - for (i = 0; i < entries; i++) { - off_t entry_offset = mtrl_offset + read_32bit(entries_offset + i*0x04,streamFile); - - if (read_8bit(entry_offset+0x05,streamFile) == 0) - continue; /* codec 0 when dummy */ - - total_subsongs++; - if (!meta_offset && total_subsongs == target_subsong) - meta_offset = entry_offset; - } - if (meta_offset == 0) goto fail; - /* SAB can contain 0 entries too */ - } - - - /** stream header **/ - /* 0x00(2): 0x00/01? */ - /* 0x02(2): base entry size? (0x20) */ - channel_count = read_8bit(meta_offset+0x04,streamFile); - codec = read_8bit(meta_offset+0x05,streamFile); - //entry_id = read_16bit(meta_offset+0x06,streamFile); - sample_rate = read_32bit(meta_offset+0x08,streamFile); - loop_start = read_32bit(meta_offset+0x0c,streamFile); /* in samples but usually ignored */ - - loop_end = read_32bit(meta_offset+0x10,streamFile); - extradata_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header, can be 0 */ - stream_size = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */ - special_size = read_32bit(meta_offset+0x1c,streamFile); - - loop_flag = (loop_end > 0); - extradata_offset = meta_offset + 0x20; - - - /** info section (get stream name) **/ - //if (is_sab) { //todo load name based on entry id - /* "snd " */ - /* 0x08(2): file number within descriptor */ - /* 0x1a(2): base_entry size (-0x10?) */ - //name_size = read_32bit(snd_offset+0x20,streamFile); - //name_offset = snd_offset+0x70; - /* 0x24(4): unique id? (referenced in "seq" section) */ - //} - //else if (is_mab) { - /* "musc" */ - //looks like a "music cue" section, pointing to one subsection per "material". - // ex. one cue may point to 3 named subsongs/sections. - // some common header info from all materials is repeated (ex. sample rate), while other - // (loops, maybe proper num_samples) are listed per material but don't always match thei header - //} /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(sead.channel_count, sead.loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = stream_size; - vgmstream->meta_type = is_sab ? meta_SQEX_SAB : meta_SQEX_MAB; + vgmstream->meta_type = sead.is_sab ? meta_SQEX_SAB : meta_SQEX_MAB; + vgmstream->sample_rate = sead.sample_rate; + vgmstream->num_streams = sead.total_subsongs; + vgmstream->stream_size = sead.stream_size; + strcpy(vgmstream->stream_name, sead.readable_name); - switch(codec) { + switch(sead.codec) { case 0x01: { /* PCM [Chrono Trigger sfx (PC)] */ - start_offset = extradata_offset + extradata_size; + start_offset = sead.extradata_offset + sead.extradata_size; vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16); - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; + vgmstream->num_samples = pcm_bytes_to_samples(sead.stream_size, vgmstream->channels, 16); + vgmstream->loop_start_sample = sead.loop_start; + vgmstream->loop_end_sample = sead.loop_end; break; } case 0x02: { /* MSADPCM [Dragon Quest Builders (Vita) sfx] */ - start_offset = extradata_offset + extradata_size; + start_offset = sead.extradata_offset + sead.extradata_size; /* 0x00 (2): null?, 0x02(2): entry size? */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = read_16bit(extradata_offset+0x04,streamFile); + vgmstream->interleave_block_size = read_16bit(sead.extradata_offset+0x04,streamFile); /* much like AKBs, there are slightly different loop values here, probably more accurate * (if no loop, loop_end doubles as num_samples) */ - vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); - vgmstream->loop_start_sample = read_32bit(extradata_offset+0x08, streamFile); //loop_start - vgmstream->loop_end_sample = read_32bit(extradata_offset+0x0c, streamFile); //loop_end + vgmstream->num_samples = msadpcm_bytes_to_samples(sead.stream_size, vgmstream->interleave_block_size, vgmstream->channels); + vgmstream->loop_start_sample = read_32bit(sead.extradata_offset+0x08, streamFile); //loop_start + vgmstream->loop_end_sample = read_32bit(sead.extradata_offset+0x0c, streamFile); //loop_end break; } @@ -188,17 +139,18 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { case 0x03: { /* OGG [Final Fantasy XV Benchmark sfx (PC)] */ VGMSTREAM *ogg_vgmstream = NULL; ogg_vorbis_meta_info_t ovmi = {0}; - off_t subfile_offset = extradata_offset + extradata_size; + off_t subfile_offset = sead.extradata_offset + sead.extradata_size; ovmi.meta_type = vgmstream->meta_type; - ovmi.total_subsongs = total_subsongs; - ovmi.stream_size = stream_size; + ovmi.total_subsongs = sead.total_subsongs; + ovmi.stream_size = sead.stream_size; /* post header has some kind of repeated values, config/table? */ ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, subfile_offset, &ovmi); if (ogg_vgmstream) { ogg_vgmstream->num_streams = vgmstream->num_streams; ogg_vgmstream->stream_size = vgmstream->stream_size; + strcpy(ogg_vgmstream->stream_name, vgmstream->stream_name); close_vgmstream(vgmstream); return ogg_vgmstream; @@ -215,21 +167,21 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */ atrac9_config cfg = {0}; - start_offset = extradata_offset + extradata_size; + start_offset = sead.extradata_offset + sead.extradata_size; /* post header has various typical ATRAC9 values */ cfg.channels = vgmstream->channels; - cfg.config_data = read_32bit(extradata_offset+0x0c,streamFile); - cfg.encoder_delay = read_32bit(extradata_offset+0x18,streamFile); + cfg.config_data = read_32bit(sead.extradata_offset+0x0c,streamFile); + cfg.encoder_delay = read_32bit(sead.extradata_offset+0x18,streamFile); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; - vgmstream->sample_rate = read_32bit(extradata_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */ - vgmstream->num_samples = read_32bit(extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */ - vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start - vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end + vgmstream->sample_rate = read_32bit(sead.extradata_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */ + vgmstream->num_samples = read_32bit(sead.extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */ + vgmstream->loop_start_sample = read_32bit(sead.extradata_offset+0x20, streamFile) - (sead.loop_flag ? cfg.encoder_delay : 0); //loop_start + vgmstream->loop_end_sample = read_32bit(sead.extradata_offset+0x24, streamFile) - (sead.loop_flag ? cfg.encoder_delay : 0); //loop_end break; } #endif @@ -239,7 +191,7 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; - start_offset = extradata_offset + extradata_size; + start_offset = sead.extradata_offset + sead.extradata_size; /* post header is a proper MSF, but sample rate/loops are ignored in favor of SAB's */ mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); @@ -247,9 +199,9 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { vgmstream->codec_data = mpeg_data; vgmstream->layout_type = layout_none; - vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; + vgmstream->num_samples = mpeg_bytes_to_samples(sead.stream_size, mpeg_data); + vgmstream->loop_start_sample = sead.loop_start; + vgmstream->loop_end_sample = sead.loop_end; break; } #endif @@ -258,16 +210,16 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { //todo there is no easy way to use the HCA decoder; try subfile hack for now VGMSTREAM *temp_vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; - off_t subfile_offset = extradata_offset + 0x10; - size_t subfile_size = stream_size + extradata_size - 0x10; + off_t subfile_offset = sead.extradata_offset + 0x10; + size_t subfile_size = sead.stream_size + sead.extradata_size - 0x10; /* post header: values from the HCA header, in file endianness + HCA header */ - size_t key_start = special_size & 0xff; - size_t header_size = read_16bit(extradata_offset+0x02, streamFile); - int encryption = read_16bit(extradata_offset+0x0c, streamFile); //maybe 8bit? + size_t key_start = sead.special_size & 0xff; + size_t header_size = read_16bit(sead.extradata_offset+0x02, streamFile); + int encryption = read_16bit(sead.extradata_offset+0x0c, streamFile); //maybe 8bit? /* encryption type 0x01 found in Final Fantasy XII TZA (PS4/PC) */ - temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size, encryption, header_size, key_start); + temp_streamFile = setup_sqex_sead_streamfile(streamFile, subfile_offset, subfile_size, encryption, header_size, key_start); if (!temp_streamFile) goto fail; temp_vgmstream = init_vgmstream_hca(temp_streamFile); @@ -276,6 +228,7 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { temp_vgmstream->num_streams = vgmstream->num_streams; temp_vgmstream->stream_size = vgmstream->stream_size; temp_vgmstream->meta_type = vgmstream->meta_type; + strcpy(temp_vgmstream->stream_name, vgmstream->stream_name); close_streamfile(temp_streamFile); close_vgmstream(vgmstream); @@ -289,10 +242,12 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { case 0x00: /* dummy entry */ default: - VGM_LOG("SQEX SEAD: unknown codec %x\n", codec); + VGM_LOG("SQEX SEAD: unknown codec %x\n", sead.codec); goto fail; } + strcpy(vgmstream->stream_name, sead.readable_name); + /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; @@ -304,79 +259,399 @@ fail: } -typedef struct { - size_t header_size; - size_t key_start; -} sead_decryption_data; +static void build_readable_name(char * buf, size_t buf_size, sead_header *sead, STREAMFILE *sf) { -/* Encrypted HCA */ -static size_t sead_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, sead_decryption_data* data) { - /* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */ - static const uint8_t encryption_key[0x100] = { - 0x3A,0x32,0x32,0x32,0x03,0x7E,0x12,0xF7,0xB2,0xE2,0xA2,0x67,0x32,0x32,0x22,0x32, // 00-0F - 0x32,0x52,0x16,0x1B,0x3C,0xA1,0x54,0x7B,0x1B,0x97,0xA6,0x93,0x1A,0x4B,0xAA,0xA6, // 10-1F - 0x7A,0x7B,0x1B,0x97,0xA6,0xF7,0x02,0xBB,0xAA,0xA6,0xBB,0xF7,0x2A,0x51,0xBE,0x03, // 20-2F - 0xF4,0x2A,0x51,0xBE,0x03,0xF4,0x2A,0x51,0xBE,0x12,0x06,0x56,0x27,0x32,0x32,0x36, // 30-3F - 0x32,0xB2,0x1A,0x3B,0xBC,0x91,0xD4,0x7B,0x58,0xFC,0x0B,0x55,0x2A,0x15,0xBC,0x40, // 40-4F - 0x92,0x0B,0x5B,0x7C,0x0A,0x95,0x12,0x35,0xB8,0x63,0xD2,0x0B,0x3B,0xF0,0xC7,0x14, // 50-5F - 0x51,0x5C,0x94,0x86,0x94,0x59,0x5C,0xFC,0x1B,0x17,0x3A,0x3F,0x6B,0x37,0x32,0x32, // 60-6F - 0x30,0x32,0x72,0x7A,0x13,0xB7,0x26,0x60,0x7A,0x13,0xB7,0x26,0x50,0xBA,0x13,0xB4, // 70-7F - 0x2A,0x50,0xBA,0x13,0xB5,0x2E,0x40,0xFA,0x13,0x95,0xAE,0x40,0x38,0x18,0x9A,0x92, // 80-8F - 0xB0,0x38,0x00,0xFA,0x12,0xB1,0x7E,0x00,0xDB,0x96,0xA1,0x7C,0x08,0xDB,0x9A,0x91, // 90-9F - 0xBC,0x08,0xD8,0x1A,0x86,0xE2,0x70,0x39,0x1F,0x86,0xE0,0x78,0x7E,0x03,0xE7,0x64, // A0-AF - 0x51,0x9C,0x8F,0x34,0x6F,0x4E,0x41,0xFC,0x0B,0xD5,0xAE,0x41,0xFC,0x0B,0xD5,0xAE, // B0-BF - 0x41,0xFC,0x3B,0x70,0x71,0x64,0x33,0x32,0x12,0x32,0x32,0x36,0x70,0x34,0x2B,0x56, // C0-CF - 0x22,0x70,0x3A,0x13,0xB7,0x26,0x60,0xBA,0x1B,0x94,0xAA,0x40,0x38,0x00,0xFA,0xB2, // D0-DF - 0xE2,0xA2,0x67,0x32,0x32,0x12,0x32,0xB2,0x32,0x32,0x32,0x32,0x75,0xA3,0x26,0x7B, // E0-EF - 0x83,0x26,0xF9,0x83,0x2E,0xFF,0xE3,0x16,0x7D,0xC0,0x1E,0x63,0x21,0x07,0xE3,0x01, // F0-FF - }; - size_t bytes_read; - off_t encrypted_offset = data->header_size; - int i; + if (sead->is_sab) { + char descriptor[255], name[255]; - bytes_read = streamfile->read(streamfile, dest, offset, length); + if (sead->descriptor_size > 255 || sead->sndname_size > 255) goto fail; - /* decrypt data (xor) */ - if (offset >= encrypted_offset) { - for (i = 0; i < bytes_read; i++) { - dest[i] ^= encryption_key[(data->key_start + (offset - encrypted_offset) + i) % 0x100]; + read_string(descriptor,sead->descriptor_size+1,sead->descriptor_offset, sf); + read_string(name,sead->sndname_size+1,sead->sndname_offset, sf); + + snprintf(buf,buf_size, "%s/%s", descriptor, name); + } + else { + char descriptor[255], name[255], mode[255]; + + if (sead->descriptor_size > 255 || sead->filename_size > 255 || sead->cuename_size > 255 || sead->modename_size > 255) goto fail; + + read_string(descriptor,sead->descriptor_size+1,sead->descriptor_offset, sf); + //read_string(filename,sead->filename_size+1,sead->filename_offset, sf); /* same as filename, not too interesting */ + if (sead->cuename_offset) + read_string(name,sead->cuename_size+1,sead->cuename_offset, sf); + else if (sead->instname_offset) + read_string(name,sead->instname_size+1,sead->instname_offset, sf); + else + strcpy(name, "?"); + read_string(mode,sead->modename_size+1,sead->modename_offset, sf); + + /* default mode in most files, not very interesting */ + if (strcmp(mode, "Mode") == 0 || strcmp(mode, "Mode0") == 0) + snprintf(buf,buf_size, "%s/%s", descriptor, name); + else + snprintf(buf,buf_size, "%s/%s/%s", descriptor, name, mode); + } + + return; +fail: + VGM_LOG("SEAD: bad name found\n"); +} + +static void parse_sead_mab_name(sead_header *sead, STREAMFILE *sf) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sead->big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = sead->big_endian ? read_16bitBE : read_16bitLE; + int i, entries, cue, mode, cue_count, mode_count; + off_t entry_offset, cue_offset, mode_offset, name_offset, table_offset; + size_t name_size; + //int wave, wave_count; off_t wave_offset, subtable_offset; uint16_t wave_id; + int name = 0; + + + /* find which name corresponds to our song (mabf can have N subsongs + * and X cues + Y modes and also Z instruments, one of which should reference it) */ + //todo exact name matching unknown, assumes subsong N = name N + + /* parse "musc" (music cue?) */ + entries = read_16bit(sead->musc_offset + 0x04, sf); + for (i = 0; i < entries; i++) { + entry_offset = sead->musc_offset + read_32bit(sead->musc_offset + 0x10 + i*0x04, sf); + + /* 0x00: config? */ + sead->filename_offset = entry_offset + read_16bit(entry_offset + 0x02, sf); + cue_count = read_8bit(entry_offset + 0x04, sf); + mode_count = read_8bit(entry_offset + 0x05, sf); + /* 0x06: some low number? */ + /* 0x07: always 0x80? (apparently not an offset/size) */ + /* 0x08: id? */ + /* 0x0a: 0? */ + /* 0x44: sample rate */ + /* others: unknown/null */ + sead->filename_size = read_8bit(entry_offset + 0x48, sf); + + /* table points to all cue offsets first then all modes offsets */ + table_offset = align_size_to_block(sead->filename_offset + sead->filename_size + 0x01, 0x10); + + /* cue name (ex. "bgm_007_take2" / "bgm_007s" / etc subsongs) */ + for (cue = 0; cue < cue_count; cue++) { + cue_offset = sead->musc_offset + 0x20 + read_32bit(table_offset + cue*0x04, sf); + + /* 0x00: id? */ + name_offset = cue_offset + read_16bit(cue_offset + 0x02, sf); + name_size = read_8bit(cue_offset + 0x04, sf); + //wave_count = read_8bit(cue_offset + 0x05, sf); + /* 0x06: ? */ + /* 0x0c: num samples */ + /* 0x10: loop start */ + /* 0x14: loop end */ + /* 0x18: flag? */ + /* others: ? */ + + name++; + if (name == sead->target_subsong || cue_count == 1) { + sead->cuename_offset = name_offset; + sead->cuename_size = name_size; + break; + } + +#if 0 //this works for some games like KH3 but not others like FFXII + /* subtable: first N wave refs + ? unk refs (rarely more than 1 each) */ + subtable_offset = align_size_to_block(name_offset + name_size + 1, 0x10); + + for (wave = 0; wave < wave_count; wave++) { + wave_offset = cue_offset + read_32bit(subtable_offset + wave*0x04, sf); + + /* 0x00: config? */ + /* 0x02: entry size */ + wave_id = read_16bit(wave_offset + 0x04, sf); + /* 0x06: null? */ + /* 0x08: null? */ + /* 0x0c: some id/config? */ + + if (wave_id == sead->wave_id) { + sead->cuename_offset = name_offset; + sead->cuename_size = name_size; + break; + } + } + + if (sead->cuename_offset) + break; +#endif + } + + /* mode name (ex. almost always "Mode" and only 1 entry, rarely "Water" / "Restaurant" / etc) + * no idea how modes are referenced (perhaps manually with in-game events) + * so just a quick hack, only found multiple in FFXV's bgm_gardina */ + if (mode_count == sead->total_subsongs) + mode = sead->target_subsong - 1; + else + mode = 0; + + { //for (mode = 0; mode < mode_count; mode++) { + mode_offset = sead->musc_offset + 0x20 + read_32bit(table_offset + cue_count*0x04 + mode*0x04, sf); + + /* 0x00: id? */ + name_offset = mode_offset + read_16bit(mode_offset + 0x02, sf); + /* 0x04: mode id */ + name_size = read_8bit(mode_offset + 0x06, sf); + /* 0x08: offset? */ + /* others: floats and stuff */ + + sead->modename_offset = name_offset; + sead->modename_size = name_size; } } - return bytes_read; + + /* parse "inst" (instruments) */ + entries = read_16bit(sead->inst_offset + 0x04, sf); + for (i = 0; i < entries; i++) { + entry_offset = sead->inst_offset + read_32bit(sead->inst_offset + 0x10 + i*0x04, sf); + + /* 0x00: id? */ + /* 0x02: base size? */ + /* 0x05: count? */ + //wave_count = read_8bit(entry_offset + 0x06, sf); + /* 0x0c: num samples */ + /* 0x10: loop start */ + /* 0x14: loop end */ + /* 0x18: flag? */ + /* others: ? */ + + /* no apparent fields and inst is very rare (ex. KH3 tut) */ + name_offset = entry_offset + 0x30; + name_size = 0x0F; + + name++; + if (name == sead->target_subsong) { + sead->instname_offset = name_offset; + sead->instname_size = name_size; + break; + } + + +#if 0 //not actually tested + if (wave_count != 1) break; /* ? */ + + /* subtable: N wave refs? */ + subtable_offset = align_size_to_block(name_offset + name_size + 1, 0x10); + + for (wave = 0; wave < wave_count; wave++) { + wave_offset = subtable_offset + read_32bit(subtable_offset + wave*0x04, sf); + + /* 0x00: config? */ + /* 0x02: entry size? */ + wave_id = read_16bit(wave_offset + 0x04, sf); + /* 0x06: ? */ + /* 0x08: id/crc? */ + /* 0x0c: ? */ + /* 0x10: sample rate */ + /* others: null? */ + + if (wave_id == sead->wave_id) { + sead->instname_offset = name_offset; + sead->instname_size = name_size; + break; + } + } + + if (sead->instname_offset) + break; +#endif + } } -static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; +static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sead->big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = sead->big_endian ? read_16bitBE : read_16bitLE; + int i, entries, snd_id, wave_id, snd_found = 0; + size_t size; + off_t entry_offset; - /* setup subfile */ - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + //todo looks mostly correct for many subsongs but in rare cases wave_ids aren't referenced + // or maybe id needs another jump (seq?) (ex. DQB se_break_soil, FFXV aircraftzeroone) - if (encryption) { - sead_decryption_data io_data = {0}; - size_t io_data_size = sizeof(sead_decryption_data); + /* parse "trk" (track info) */ + entries = read_16bit(sead->trk_offset + 0x04, sf); + for (i = 0; i < entries; i++) { + entry_offset = sead->trk_offset + read_32bit(sead->trk_offset + 0x10 + i*0x04, sf); - io_data.header_size = header_size; - io_data.key_start = key_start; + /* 0x00: type/count? */ + size = read_16bit(entry_offset + 0x02, sf); /* bigger if 'type=03' */ + /* 0x04: trk id? */ + /* 0x04: some id? */ - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sead_decryption_read,NULL); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + if (size > 0x10) { + snd_id = read_8bit(entry_offset + 0x10, sf); + wave_id = read_16bit(entry_offset + 0x11, sf); + } + else { + snd_id = read_16bit(entry_offset + 0x08, sf); + wave_id = read_16bit(entry_offset + 0x0a, sf); + } + + + if (wave_id == sead->wave_id) { + snd_found = 1; + break; + } } - new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca"); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + if (!snd_found) { + if (sead->total_subsongs == 1) { + snd_id = 0; /* meh */ + VGM_LOG("SEAD: snd_id not found, using first\n"); + } else { + return; + } + } - return temp_streamFile; -fail: - close_streamfile(temp_streamFile); - return NULL; + /* parse "snd " (sound info) */ + { + off_t entry_offset = sead->snd_offset + read_32bit(sead->snd_offset + 0x10 + snd_id*0x04, sf); + + /* 0x00: config? */ + sead->sndname_offset = entry_offset + read_16bit(entry_offset + 0x02, sf); + /* 0x04: count of ? */ + /* 0x05: count of ? (0 if no sound exist in file) */ + /* 0x06: some low number? */ + /* 0x07: always 0x80? (apparently not an offset/size) */ + /* 0x08: snd id */ + /* 0x0a: 0? */ + /* 0x0c: 1.0? */ + /* 0x1a: header size? */ + /* 0x1c: 30.0? * */ + /* 0x24: crc/id? */ + /* 0x46: header size? */ + /* 0x4c: header size? */ + + if (sead->version == 1) { + sead->sndname_offset -= 0x10; + sead->sndname_size = read_8bit(entry_offset + 0x08, sf); + } + else { + sead->sndname_size = read_8bit(entry_offset + 0x23, sf); + } + + /* 0x24: unique id? (referenced in "seq" section?) */ + /* others: probably sound config like pan/volume (has floats and stuff) */ + } +} + +static int parse_sead(sead_header *sead, STREAMFILE *sf) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sead->big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = sead->big_endian ? read_16bitBE : read_16bitLE; + + /** base header **/ + sead->version = read_8bit(0x04, sf); /* usually 0x02, rarely 0x01 (ex FF XV early songs) */ + /* 0x05(1): 0/1? */ + /* 0x06(2): ? (usually 0x10, rarely 0x20) */ + /* 0x08(1): 3/4? */ + sead->descriptor_size = read_8bit(0x09, sf); + /* 0x0a(2): ? */ + if (read_32bit(0x0c, sf) != get_streamfile_size(sf)) + goto fail; + + if (sead->descriptor_size == 0) /* not set when version == 1 */ + sead->descriptor_size = 0x0f; + sead->descriptor_offset = 0x10; /* file descriptor ("BGM", "Music2", "SE", etc, long names are ok) */ + sead->sections_offset = sead->descriptor_offset + (sead->descriptor_size + 0x01); /* string null matters for padding */ + sead->sections_offset = align_size_to_block(sead->sections_offset, 0x10); + + + /** offsets to sections **/ + if (sead->is_sab) { + if (read_32bitBE(sead->sections_offset + 0x00, sf) != 0x736E6420) goto fail; /* "snd " (sonds) */ + if (read_32bitBE(sead->sections_offset + 0x10, sf) != 0x73657120) goto fail; /* "seq " (unknown) */ + if (read_32bitBE(sead->sections_offset + 0x20, sf) != 0x74726B20) goto fail; /* "trk " (unknown) */ + if (read_32bitBE(sead->sections_offset + 0x30, sf) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + sead->snd_offset = read_32bit(sead->sections_offset + 0x08, sf); + //sead->seq_offset = read_32bit(sead->sections_offset + 0x18, sf); + sead->trk_offset = read_32bit(sead->sections_offset + 0x28, sf); + sead->mtrl_offset = read_32bit(sead->sections_offset + 0x38, sf); + } + else if (sead->is_mab) { + if (read_32bitBE(sead->sections_offset + 0x00, sf) != 0x6D757363) goto fail; /* "musc" (cues) */ + if (read_32bitBE(sead->sections_offset + 0x10, sf) != 0x696E7374) goto fail; /* "inst" (instruments) */ + if (read_32bitBE(sead->sections_offset + 0x20, sf) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + sead->musc_offset = read_32bit(sead->sections_offset + 0x08, sf); + sead->inst_offset = read_32bit(sead->sections_offset + 0x18, sf); + sead->mtrl_offset = read_32bit(sead->sections_offset + 0x28, sf); + } + else { + goto fail; + } + + + /* section format at offset: + * 0x00(2): 0/1? + * 0x02(2): header size? (always 0x10) + * 0x04(2): entries + * 0x06(+): padded to 0x10 + * 0x10 + 0x04*entry: offset to entry from table start (also padded to 0x10 at the end) */ + + + /* find meta_offset in "mtrl" and total subsongs */ + { + int i, entries; + + entries = read_16bit(sead->mtrl_offset+0x04, sf); + + if (sead->target_subsong == 0) sead->target_subsong = 1; + sead->total_subsongs = 0; + sead->meta_offset = 0; + + /* manually find subsongs as entries can be dummy (ex. sfx banks in Dissidia Opera Omnia) */ + for (i = 0; i < entries; i++) { + off_t entry_offset = sead->mtrl_offset + read_32bit(sead->mtrl_offset + 0x10 + i*0x04, sf); + + if (read_8bit(entry_offset + 0x05, sf) == 0) { + continue; /* codec 0 when dummy (see stream header) */ + } + + + sead->total_subsongs++; + if (!sead->meta_offset && sead->total_subsongs == sead->target_subsong) { + sead->meta_offset = entry_offset; + } + } + if (sead->meta_offset == 0) goto fail; + /* SAB can contain 0 entries too */ + } + + + /** stream header **/ + /* 0x00(2): 0x00/01? */ + /* 0x02(2): base entry size? (0x20) */ + sead->channel_count = read_8bit(sead->meta_offset + 0x04, sf); + sead->codec = read_8bit(sead->meta_offset + 0x05, sf); + sead->wave_id = read_16bit(sead->meta_offset + 0x06, sf); /* 0..N */ + sead->sample_rate = read_32bit(sead->meta_offset + 0x08, sf); + sead->loop_start = read_32bit(sead->meta_offset + 0x0c, sf); /* in samples but usually ignored */ + + sead->loop_end = read_32bit(sead->meta_offset + 0x10, sf); + sead->extradata_size = read_32bit(sead->meta_offset + 0x14, sf); /* including subfile header, can be 0 */ + sead->stream_size = read_32bit(sead->meta_offset + 0x18, sf); /* not including subfile header */ + sead->special_size = read_32bit(sead->meta_offset + 0x1c, sf); + + sead->loop_flag = (sead->loop_end > 0); + sead->extradata_offset = sead->meta_offset + 0x20; + + + /** info section (get stream name) **/ + if (sead->is_sab) { + parse_sead_sab_name(sead, sf); + } + else if (sead->is_mab) { + parse_sead_mab_name(sead, sf); + } + + build_readable_name(sead->readable_name, sizeof(sead->readable_name), sead, sf); + + return 1; +fail: + return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead_streamfile.h new file mode 100644 index 000000000..98cad9284 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead_streamfile.h @@ -0,0 +1,86 @@ +#ifndef _SQEX_SEAD_STREAMFILE_H_ +#define _SQEX_SEAD_STREAMFILE_H_ +#include "../streamfile.h" + +static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start); + + +typedef struct { + size_t header_size; + size_t key_start; +} sqex_sead_decryption_data; + + +static size_t sqex_sead_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, sqex_sead_decryption_data* data) { + /* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */ + static const uint8_t encryption_key[0x100] = { + 0x3A,0x32,0x32,0x32,0x03,0x7E,0x12,0xF7,0xB2,0xE2,0xA2,0x67,0x32,0x32,0x22,0x32, // 00-0F + 0x32,0x52,0x16,0x1B,0x3C,0xA1,0x54,0x7B,0x1B,0x97,0xA6,0x93,0x1A,0x4B,0xAA,0xA6, // 10-1F + 0x7A,0x7B,0x1B,0x97,0xA6,0xF7,0x02,0xBB,0xAA,0xA6,0xBB,0xF7,0x2A,0x51,0xBE,0x03, // 20-2F + 0xF4,0x2A,0x51,0xBE,0x03,0xF4,0x2A,0x51,0xBE,0x12,0x06,0x56,0x27,0x32,0x32,0x36, // 30-3F + 0x32,0xB2,0x1A,0x3B,0xBC,0x91,0xD4,0x7B,0x58,0xFC,0x0B,0x55,0x2A,0x15,0xBC,0x40, // 40-4F + 0x92,0x0B,0x5B,0x7C,0x0A,0x95,0x12,0x35,0xB8,0x63,0xD2,0x0B,0x3B,0xF0,0xC7,0x14, // 50-5F + 0x51,0x5C,0x94,0x86,0x94,0x59,0x5C,0xFC,0x1B,0x17,0x3A,0x3F,0x6B,0x37,0x32,0x32, // 60-6F + 0x30,0x32,0x72,0x7A,0x13,0xB7,0x26,0x60,0x7A,0x13,0xB7,0x26,0x50,0xBA,0x13,0xB4, // 70-7F + 0x2A,0x50,0xBA,0x13,0xB5,0x2E,0x40,0xFA,0x13,0x95,0xAE,0x40,0x38,0x18,0x9A,0x92, // 80-8F + 0xB0,0x38,0x00,0xFA,0x12,0xB1,0x7E,0x00,0xDB,0x96,0xA1,0x7C,0x08,0xDB,0x9A,0x91, // 90-9F + 0xBC,0x08,0xD8,0x1A,0x86,0xE2,0x70,0x39,0x1F,0x86,0xE0,0x78,0x7E,0x03,0xE7,0x64, // A0-AF + 0x51,0x9C,0x8F,0x34,0x6F,0x4E,0x41,0xFC,0x0B,0xD5,0xAE,0x41,0xFC,0x0B,0xD5,0xAE, // B0-BF + 0x41,0xFC,0x3B,0x70,0x71,0x64,0x33,0x32,0x12,0x32,0x32,0x36,0x70,0x34,0x2B,0x56, // C0-CF + 0x22,0x70,0x3A,0x13,0xB7,0x26,0x60,0xBA,0x1B,0x94,0xAA,0x40,0x38,0x00,0xFA,0xB2, // D0-DF + 0xE2,0xA2,0x67,0x32,0x32,0x12,0x32,0xB2,0x32,0x32,0x32,0x32,0x75,0xA3,0x26,0x7B, // E0-EF + 0x83,0x26,0xF9,0x83,0x2E,0xFF,0xE3,0x16,0x7D,0xC0,0x1E,0x63,0x21,0x07,0xE3,0x01, // F0-FF + }; + size_t bytes_read; + off_t encrypted_offset = data->header_size; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + if (offset >= encrypted_offset) { + for (i = 0; i < bytes_read; i++) { + dest[i] ^= encryption_key[(data->key_start + (offset - encrypted_offset) + i) % 0x100]; + } + } + + return bytes_read; +} + +/* decrypts subfile if neccessary */ +static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + if (encryption) { + sqex_sead_decryption_data io_data = {0}; + size_t io_data_size = sizeof(sqex_sead_decryption_data); + + io_data.header_size = header_size; + io_data.key_start = key_start; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sqex_sead_decryption_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + } + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _SQEX_SEAD_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_snds.c b/Frameworks/vgmstream/vgmstream/src/meta/str_snds.c index 6a43997aa..b232898ca 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/str_snds.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_snds.c @@ -1,118 +1,98 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" -#include "../util.h" -/* 3DO format, .str extension and possibly a CTRL header, blocks and - * AIFF-C style format specifier. Blocks are not IFF-compliant. Interesting - * blocks are all SNDS - */ +/* .str - 3DO format with CTRL/SNDS/SHDR blocks [Icebreaker (3DO), Battle Pinball (3DO)] */ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + off_t start_offset, shdr_offset = -1; + int loop_flag, channel_count, found_shdr = 0; + size_t file_size, ctrl_size = -1; - int channel_count; - int loop_flag = 0; - off_t SHDR_offset = -1; - int FoundSHDR = 0; - int CTRL_size = -1; - size_t file_size; + /* checks */ + if (!check_extensions(streamFile, "str")) + goto fail; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("str",filename_extension(filename))) goto fail; - - /* check for opening CTRL or SNDS chunk */ - if (read_32bitBE(0x0,streamFile) != 0x4354524c && /* CTRL */ - read_32bitBE(0x0,streamFile) != 0x534e4453 && // SNDS - read_32bitBE(0x0,streamFile) != 0x53484452) // SHDR + if (read_32bitBE(0x00,streamFile) != 0x4354524c && /* "CTRL" */ + read_32bitBE(0x00,streamFile) != 0x534e4453 && /* "SNDS" */ + read_32bitBE(0x00,streamFile) != 0x53484452) /* "SHDR" */ goto fail; file_size = get_streamfile_size(streamFile); + start_offset = 0x00; /* scan chunks until we find a SNDS containing a SHDR */ { - off_t current_chunk; + off_t current_chunk = 0; - current_chunk = 0; - - while (!FoundSHDR && current_chunk < file_size) { + while (!found_shdr && current_chunk < file_size) { if (current_chunk < 0) goto fail; - if (current_chunk+read_32bitBE(current_chunk+4,streamFile) >= - file_size) goto fail; + if (current_chunk+read_32bitBE(current_chunk+0x04,streamFile) >= file_size) + goto fail; - switch (read_32bitBE(current_chunk,streamFile)) - { - case 0x4354524C: /* CTRL */ - /* to distinguish between styles */ - CTRL_size = read_32bitBE(current_chunk+4,streamFile); - break; - case 0x534e4453: /* SNDS */ - switch (read_32bitBE(current_chunk+16,streamFile)) - { - case 0x53484452: /* SHDR */ - FoundSHDR = 1; - SHDR_offset = current_chunk+16; - break; - - default: - break; + switch (read_32bitBE(current_chunk,streamFile)) { + case 0x4354524C: /* "CTRL" */ + ctrl_size = read_32bitBE(current_chunk+4,streamFile); + break; + + case 0x534e4453: /* "SNDS" */ + switch (read_32bitBE(current_chunk+16,streamFile)) { + case 0x53484452: /* SHDR */ + found_shdr = 1; + shdr_offset = current_chunk+16; + break; + default: + break; } break; - case 0x53484452: /* SHDR */ - switch (read_32bitBE(current_chunk+0x7C, streamFile)) - { - case 0x4354524C: /* CTRL */ - // to distinguish between styles - CTRL_size = read_32bitBE(current_chunk + 0x80, streamFile); - break; - - default: - break; + + case 0x53484452: /* "SHDR" */ + switch (read_32bitBE(current_chunk+0x7C, streamFile)) { + case 0x4354524C: /* "CTRL" */ + /* to distinguish between styles */ + ctrl_size = read_32bitBE(current_chunk + 0x80, streamFile); + break; + + default: + break; } - break; - default: + break; + + default: /* ignore others for now */ break; } - current_chunk += read_32bitBE(current_chunk+4,streamFile); + current_chunk += read_32bitBE(current_chunk+0x04,streamFile); } } - if (!FoundSHDR) goto fail; + if (!found_shdr) goto fail; - /* details */ - channel_count = read_32bitBE(SHDR_offset+0x20,streamFile); + channel_count = read_32bitBE(shdr_offset+0x20,streamFile); loop_flag = 0; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - if ((CTRL_size == 0x1C) || - (CTRL_size == 0x0B) || - (CTRL_size == -1)) - { - vgmstream->num_samples = - read_32bitBE(SHDR_offset+0x2c,streamFile)-1; /* sample count? */ + vgmstream->meta_type = meta_STR_SNDS; + vgmstream->sample_rate = read_32bitBE(shdr_offset+0x1c,streamFile); + + if (ctrl_size == 0x1C || ctrl_size == 0x0B || ctrl_size == -1) { + vgmstream->num_samples = read_32bitBE(shdr_offset+0x2c,streamFile) - 1; /* sample count? */ } - else { - vgmstream->num_samples = - read_32bitBE(SHDR_offset+0x2c,streamFile) /* frame count? */ - * 0x10; + else { + vgmstream->num_samples = read_32bitBE(shdr_offset+0x2c,streamFile) * 0x10; /* frame count? */ } + vgmstream->num_samples /= vgmstream->channels; - vgmstream->num_samples/=vgmstream->channels; - - vgmstream->sample_rate = read_32bitBE(SHDR_offset+0x1c,streamFile); - switch (read_32bitBE(SHDR_offset+0x24,streamFile)) { - case 0x53445832: /* SDX2 */ + switch (read_32bitBE(shdr_offset+0x24,streamFile)) { + case 0x53445832: /* "SDX2" */ if (channel_count > 1) { vgmstream->coding_type = coding_SDX2_int; vgmstream->interleave_block_size = 1; @@ -123,33 +103,12 @@ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) { goto fail; } vgmstream->layout_type = layout_blocked_str_snds; - vgmstream->meta_type = meta_STR_SNDS; - - /* channels and loop flag are set by allocate_vgmstream */ - if (loop_flag) { - /* just guessin', no way to set loop flag anyway */ - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - /* open the file for reading by each channel */ - { - int i; - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) goto fail; - for (i=0;ich[i].streamfile = vgmstream->ch[0].streamfile; - } - } - - /* start me up */ - block_update_str_snds(0,vgmstream); + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c index aa72e9e2d..8cfd09763 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c @@ -2,7 +2,7 @@ #include "../coding/coding.h" -typedef enum { PSX, DSP, XBOX, WMA, IMA } strwav_codec; +typedef enum { PSX, DSP, XBOX, WMA, IMA, XMA2 } strwav_codec; typedef struct { int32_t channels; int32_t sample_rate; @@ -32,12 +32,15 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "str")) + if (!check_extensions(streamFile, "str,data")) goto fail; /* get external header (extracted with filenames from bigfiles) */ { - /* try with standard file.wav.str=body + file.wav=header (or file.wma.str + file.wma for Fuzion Frenzy (Xbox)) */ + /* try body+header combos: + * - file.wav.str=body + file.wav=header [common] + * - file.wma.str + file.wma [Fuzion Frenzy (Xbox)] + * - file.data + file (extensionless) [SpongeBob's Surf & Skate Roadtrip (X360)] */ char basename[PATH_LIMIT]; get_streamfile_basename(streamFile,basename,PATH_LIMIT); streamHeader = open_streamfile_by_filename(streamFile, basename); @@ -51,7 +54,8 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) { } } else { - if (!check_extensions(streamHeader, "wav,wma")) + /* header must have known extensions */ + if (!check_extensions(streamHeader, "wav,wma,")) goto fail; } } @@ -66,7 +70,7 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) { goto fail; /* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */ - if (strwav.flags != 0x07 && strwav.flags != 0x06 && strwav.flags != 0x05 && strwav.flags != 0x04 && strwav.flags != 0x02) { + if (strwav.flags > 0x07) { VGM_LOG("STR+WAV: unknown flags\n"); goto fail; } @@ -150,6 +154,27 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) { } #endif +#ifdef VGM_USE_FFMPEG + case XMA2: { + uint8_t buf[0x100]; + size_t stream_size; + size_t bytes, block_size, block_count; + + stream_size = get_streamfile_size(streamFile); + block_size = 0x10000; + block_count = stream_size / block_size; /* not accurate? */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, strwav.num_samples, stream_size, strwav.channels, strwav.sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, 0x00,stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, streamFile, 0x00,stream_size, 0, 0,0); + break; + } +#endif + default: goto fail; } @@ -431,7 +456,7 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) { if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */ read_32bitLE(0x08,streamHeader) != 0x00000000 && - read_32bitBE(0x0c,streamHeader) == header_size && /* variable per DSP header */ + read_32bitBE(0x0c,streamHeader) == header_size && /* variable per header */ read_32bitBE(0x7c,streamHeader) != 0 && /* has DSP header */ read_32bitBE(0x38,streamHeader) == read_32bitBE(read_32bitBE(0x7c,streamHeader)+0x38,streamHeader) /* sample rate vs 1st DSP header */ ) { @@ -455,7 +480,7 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) { if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */ read_32bitLE(0x08,streamHeader) != 0x00000000 && - read_32bitBE(0x0c,streamHeader) == header_size && /* variable per DSP header */ + read_32bitBE(0x0c,streamHeader) == header_size && /* variable per header */ read_32bitBE(0x7c,streamHeader) == 0 /* not DSP header */ ) { strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok? @@ -473,6 +498,28 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) { return 1; } + /* SpongeBob's Surf & Skate Roadtrip (X360)[2011] */ + if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || /* used? */ + read_32bitBE(0x04,streamHeader) == 0x00000700) && + read_32bitLE(0x08,streamHeader) != 0x00000000 && + read_32bitBE(0x0c,streamHeader) == 0x124 && /* variable, not sure about final calc */ + read_32bitBE(0x8c,streamHeader) == 0x180 /* encoder delay actually */ + //0x4c is data_size + 0x210 + ) { + strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok? + strwav->num_samples = read_32bitBE(0x30,streamHeader);//todo sometimes wrong? + strwav->loop_end = read_32bitBE(0x34,streamHeader); + strwav->sample_rate = read_32bitBE(0x38,streamHeader); + strwav->flags = read_32bitBE(0x3c,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader); /* multichannel XMA */ + strwav->loop_flag = strwav->flags & 0x01; + + strwav->codec = XMA2; + //;VGM_LOG("STR+WAV: header SBSSR (X360)\n"); + return 1; + } + /* unknown */ goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c index 49de813e9..8d7ae806d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c @@ -176,30 +176,20 @@ VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG { - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - int32_t bytes, samples_size = 1024, block_size, encoder_delay, joint_stereo, max_samples; - block_size = (codec_id == 4 ? 0x60 : (codec_id == 5 ? 0x98 : 0xC0)) * vgmstream->channels; - max_samples = (data_size / block_size) * samples_size; - encoder_delay = 0x0; - joint_stereo = 0; + int block_align, encoder_delay; - /* make a fake riff so FFmpeg can parse the ATRAC3 */ - bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); - if (bytes <= 0) goto fail; + block_align = (codec_id == 4 ? 0x60 : (codec_id == 5 ? 0x98 : 0xC0)) * vgmstream->channels; + encoder_delay = 1024 + 69; /* approximate, gets good loops */ + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = max_samples; - - if (loop_flag) { - vgmstream->loop_start_sample = (loop_start / block_size) * samples_size; - vgmstream->loop_end_sample = (loop_end / block_size) * samples_size; - } + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); // - encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index fecb8b052..ed43222a7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -17,7 +17,7 @@ typedef enum { DVI_IMA = 7, /* DVI IMA ADPCM (high nibble first) */ MPEG = 8, /* MPEG (MP3) */ IMA = 9, /* IMA ADPCM (low nibble first) */ - YAMAHA = 10, /* YAMAHA (AICA) ADPCM (Dreamcast games) */ + AICA = 10, /* YAMAHA AICA ADPCM (Dreamcast games) */ MSADPCM = 11, /* MS ADPCM (Windows games) */ NGC_DSP = 12, /* NGC DSP (Nintendo games) */ PCM8_U_int = 13, /* 8-bit unsigned PCM (interleaved) */ @@ -81,6 +81,11 @@ typedef struct { int coef_table_set; uint8_t coef_table[0x02*16 * 16]; /* reasonable max */ + int hist_set; + uint32_t hist_offset; + uint32_t hist_spacing; + uint32_t hist_big_endian; + int num_samples_data_size; int target_subsong; @@ -195,7 +200,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */ #endif case IMA: coding = coding_IMA; break; - case YAMAHA: coding = coding_YAMAHA; break; + case AICA: coding = coding_AICA; break; case MSADPCM: coding = coding_MSADPCM; break; case NGC_DSP: coding = coding_NGC_DSP; break; case PCM8_U_int: coding = coding_PCM8_U_int; break; @@ -259,7 +264,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case coding_PSX_badflags: case coding_DVI_IMA: case coding_IMA: - case coding_YAMAHA: + case coding_AICA: case coding_APPLE_IMA4: vgmstream->interleave_block_size = txth.interleave; vgmstream->interleave_last_block_size = txth.interleave_last; @@ -278,8 +283,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding = coding_DVI_IMA_int; if (coding == coding_IMA) coding = coding_IMA_int; - if (coding == coding_YAMAHA) - coding = coding_YAMAHA_int; + if (coding == coding_AICA) + coding = coding_AICA_int; } /* to avoid endless loops */ @@ -289,7 +294,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding == coding_IMA_int || coding == coding_DVI_IMA_int || coding == coding_SDX2_int || - coding == coding_YAMAHA_int) ) { + coding == coding_AICA_int) ) { goto fail; } } else { @@ -297,11 +302,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { } /* to avoid problems with dual stereo files (_L+_R) for codecs with stereo modes */ - if (coding == coding_YAMAHA && txth.channels == 1) - coding = coding_YAMAHA_int; + if (coding == coding_AICA && txth.channels == 1) + coding = coding_AICA_int; /* setup adpcm */ - if (coding == coding_YAMAHA || coding == coding_YAMAHA_int) { + if (coding == coding_AICA || coding == coding_AICA_int) { int ch; for (ch = 0; ch < vgmstream->channels; ch++) { vgmstream->ch[ch].adpcm_step_index = 0x7f; @@ -378,29 +383,41 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { } /* get coefs */ - for (i = 0; i < vgmstream->channels; i++) { + { int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE; int16_t (*get_16bit)(uint8_t * p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE; - /* normal/split coefs */ - if (txth.coef_mode == 0) { /* normal mode */ - for (j = 0; j < 16; j++) { - int16_t coef; - if (txth.coef_table_set) - coef = get_16bit(txth.coef_table + i*txth.coef_spacing + j*2); - else - coef = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2, txth.streamHead); - vgmstream->ch[i].adpcm_coef[j] = coef; + for (i = 0; i < vgmstream->channels; i++) { + if (txth.coef_mode == 0) { /* normal coefs */ + for (j = 0; j < 16; j++) { + int16_t coef; + if (txth.coef_table_set) + coef = get_16bit(txth.coef_table + i*txth.coef_spacing + j*2); + else + coef = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2, txth.streamHead); + vgmstream->ch[i].adpcm_coef[j] = coef; + } + } + else { /* split coefs */ + goto fail; //IDK what is this + /* + for (j = 0; j < 8; j++) { + vgmstream->ch[i].adpcm_coef[j*2] = read_16bit(genh.coef_offset + i*genh.coef_spacing + j*2, txth.streamHead); + vgmstream->ch[i].adpcm_coef[j*2+1] = read_16bit(genh.coef_split_offset + i*genh.coef_split_spacing + j*2, txth.streamHead); + } + */ } } - else { /* split coefs */ - goto fail; //IDK what is this - /* - for (j = 0; j < 8; j++) { - vgmstream->ch[i].adpcm_coef[j*2] = read_16bit(genh.coef_offset + i*genh.coef_spacing + j*2, txth.streamHead); - vgmstream->ch[i].adpcm_coef[j*2+1] = read_16bit(genh.coef_split_offset + i*genh.coef_split_spacing + j*2, txth.streamHead); - } - */ + } + + /* get hist */ + if (txth.hist_set) { + int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.hist_big_endian ? read_16bitBE : read_16bitLE; + + for (i = 0; i < vgmstream->channels; i++) { + off_t offset = txth.hist_offset + i*txth.hist_spacing; + vgmstream->ch[i].adpcm_history1_16 = read_16bit(offset + 0x00, txth.streamHead); + vgmstream->ch[i].adpcm_history2_16 = read_16bit(offset + 0x02, txth.streamHead); } } @@ -432,26 +449,27 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { int32_t bytes; if (txth.codec == ATRAC3) { - int block_size = txth.interleave; - int joint_stereo; - switch(txth.codec_mode) { - case 0: joint_stereo = vgmstream->channels > 1 && txth.interleave/vgmstream->channels==0x60 ? 1 : 0; break; /* autodetect */ - case 1: joint_stereo = 1; break; /* force joint stereo */ - case 2: joint_stereo = 0; break; /* force stereo */ - default: goto fail; - } + int block_align, encoder_delay; - bytes = ffmpeg_make_riff_atrac3(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, txth.skip_samples); + block_align = txth.interleave; + encoder_delay = txth.skip_samples; + + ffmpeg_data = init_ffmpeg_atrac3_raw(txth.streamBody, txth.start_offset,txth.data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!ffmpeg_data) goto fail; } else if (txth.codec == ATRAC3PLUS) { int block_size = txth.interleave; bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples); + ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); + if ( !ffmpeg_data ) goto fail; } else if (txth.codec == XMA1) { int xma_stream_mode = txth.codec_mode == 1 ? 1 : 0; bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode); + ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); + if ( !ffmpeg_data ) goto fail; } else if (txth.codec == XMA2) { int block_count, block_size; @@ -460,13 +478,12 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { block_count = txth.data_size / block_size; bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); + if ( !ffmpeg_data ) goto fail; } else { goto fail; } - - ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); - if ( !ffmpeg_data ) goto fail; } vgmstream->codec_data = ffmpeg_data; @@ -474,7 +491,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { if (txth.codec == XMA1 || txth.codec == XMA2) { xma_fix_raw_samples(vgmstream, txth.streamBody, txth.start_offset,txth.data_size, 0, 0,0); - } else if (txth.skip_samples_set) { /* force encoder delay */ + } else if (txth.skip_samples_set && txth.codec != ATRAC3) { /* force encoder delay */ ffmpeg_set_skip_samples(ffmpeg_data, txth.skip_samples); } @@ -796,8 +813,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char else if (is_string(val,"DVI_IMA")) txth->codec = DVI_IMA; else if (is_string(val,"MPEG")) txth->codec = MPEG; else if (is_string(val,"IMA")) txth->codec = IMA; - else if (is_string(val,"YAMAHA")) txth->codec = YAMAHA; - else if (is_string(val,"AICA")) txth->codec = YAMAHA; + else if (is_string(val,"AICA")) txth->codec = AICA; else if (is_string(val,"MSADPCM")) txth->codec = MSADPCM; else if (is_string(val,"NGC_DSP")) txth->codec = NGC_DSP; else if (is_string(val,"DSP")) txth->codec = NGC_DSP; @@ -1051,6 +1067,22 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char txth->coef_table_set = 1; } + /* HIST */ + else if (is_string(key,"hist_offset")) { + if (!parse_num(txth->streamHead,txth,val, &txth->hist_offset)) goto fail; + txth->hist_set = 1; + } + else if (is_string(key,"hist_spacing")) { + if (!parse_num(txth->streamHead,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->streamHead,txth,val, &txth->hist_big_endian)) goto fail; + } + /* SUBSONGS */ else if (is_string(key,"subsong_count")) { if (!parse_num(txth->streamHead,txth,val, &txth->subsong_count)) goto fail; @@ -1269,7 +1301,7 @@ static int is_string_match(const char * text, const char * pattern) { int t_pos = 0, p_pos = 0; int p_size, t_size; uint16_t p_char, t_char; - ;VGM_LOG("TXTH: match '%s' vs '%s'\n", text,pattern); + //;VGM_LOG("TXTH: match '%s' vs '%s'\n", text,pattern); /* compares 2 strings (case insensitive, to a point) allowing wildcards * ex. for "test": match = "Test*", "*est", "*teSt","T*ES*T"; fail = "tst", "teest" @@ -1288,7 +1320,7 @@ static int is_string_match(const char * text, const char * pattern) { while(text[t_pos] != '\0') { t_char = get_string_wchar(text, t_pos, &t_size); - ;VGM_LOG("TXTH: consume %i '%s'\n", t_size, (text+t_pos) ); + //;VGM_LOG("TXTH: consume %i '%s'\n", t_size, (text+t_pos) ); if (t_char == p_char) break; @@ -1671,7 +1703,7 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { case IMA: case DVI_IMA: return ima_bytes_to_samples(bytes, txth->channels); - case YAMAHA: + case AICA: return yamaha_bytes_to_samples(bytes, txth->channels); case PCFX: case OKI16: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index fa857616f..976d30c27 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -5,10 +5,11 @@ #define TXTP_LINE_MAX 1024 -#define TXTP_MIXING_MAX 128 +#define TXTP_MIXING_MAX 512 #define TXTP_GROUP_MODE_SEGMENTED 'S' #define TXTP_GROUP_MODE_LAYERED 'L' #define TXTP_GROUP_REPEAT 'R' +#define TXTP_POSITION_LOOPS 'L' /* mixing info */ typedef enum { @@ -27,6 +28,7 @@ typedef enum { MACRO_LAYER, MACRO_CROSSTRACK, MACRO_CROSSLAYER, + MACRO_DOWNMIX, } txtp_mix_t; @@ -49,6 +51,8 @@ typedef struct { double time_start; double time_end; double time_post; + double position; + char position_type; /* macros */ int max; @@ -68,21 +72,29 @@ typedef struct { int mixing_count; txtp_mix_data mixing[TXTP_MIXING_MAX]; + int config_loop_count_set; double config_loop_count; + int config_fade_time_set; double config_fade_time; + int config_fade_delay_set; double config_fade_delay; int config_ignore_loop; int config_force_loop; int config_ignore_fade; int sample_rate; - int loop_install; + + int loop_install_set; int loop_end_max; double loop_start_second; int32_t loop_start_sample; double loop_end_second; int32_t loop_end_sample; + int trim_set; + double trim_second; + int32_t trim_sample; + } txtp_entry; @@ -419,18 +431,23 @@ fail: static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { - vgmstream->config_loop_count = current->config_loop_count; - vgmstream->config_fade_time = current->config_fade_time; - vgmstream->config_fade_delay = current->config_fade_delay; - vgmstream->config_ignore_loop = current->config_ignore_loop; - vgmstream->config_force_loop = current->config_force_loop; - vgmstream->config_ignore_fade = current->config_ignore_fade; + if (current->config_loop_count_set) + vgmstream->config_loop_count = current->config_loop_count; + if (current->config_fade_time_set) + vgmstream->config_fade_time = current->config_fade_time; + if (current->config_fade_delay_set) + vgmstream->config_fade_delay = current->config_fade_delay; + if (current->config_ignore_loop) + vgmstream->config_ignore_loop = current->config_ignore_loop; + if (current->config_force_loop) + vgmstream->config_force_loop = current->config_force_loop; + if (current->config_ignore_fade) + vgmstream->config_ignore_fade = current->config_ignore_fade; - if (current->sample_rate > 0) { + if (current->sample_rate > 0) vgmstream->sample_rate = current->sample_rate; - } - if (current->loop_install) { + if (current->loop_install_set) { if (current->loop_start_second > 0 || current->loop_end_second > 0) { current->loop_start_sample = current->loop_start_second * vgmstream->sample_rate; current->loop_end_sample = current->loop_end_second * vgmstream->sample_rate; @@ -443,9 +460,27 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { current->loop_end_sample = vgmstream->num_samples; } - vgmstream_force_loop(vgmstream, current->loop_install, current->loop_start_sample, current->loop_end_sample); + vgmstream_force_loop(vgmstream, current->loop_install_set, current->loop_start_sample, current->loop_end_sample); } + if (current->trim_set) { + if (current->trim_second != 0.0) { + current->trim_sample = current->trim_second * vgmstream->sample_rate; + } + + if (current->trim_sample < 0) { + vgmstream->num_samples += current->trim_sample; /* trim from end (add negative) */ + } + else if (vgmstream->num_samples > current->trim_sample) { + vgmstream->num_samples = current->trim_sample; /* trim to value */ + } + + /* readjust after triming if it went over (could check for more edge cases but eh) */ + if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + /* add macro to mixing list */ if (current->channel_mask) { int ch; @@ -461,43 +496,57 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { /* copy mixing list (should be done last as some mixes depend on config) */ if (current->mixing_count > 0) { - int m; + int m, position_samples; for (m = 0; m < current->mixing_count; m++) { - txtp_mix_data mix = current->mixing[m]; + txtp_mix_data *mix = ¤t->mixing[m]; - switch(mix.command) { + switch(mix->command) { /* base mixes */ - case MIX_SWAP: mixing_push_swap(vgmstream, mix.ch_dst, mix.ch_src); break; - case MIX_ADD: mixing_push_add(vgmstream, mix.ch_dst, mix.ch_src, 1.0); break; - case MIX_ADD_VOLUME: mixing_push_add(vgmstream, mix.ch_dst, mix.ch_src, mix.vol); break; - case MIX_VOLUME: mixing_push_volume(vgmstream, mix.ch_dst, mix.vol); break; - case MIX_LIMIT: mixing_push_limit(vgmstream, mix.ch_dst, mix.vol); break; - case MIX_UPMIX: mixing_push_upmix(vgmstream, mix.ch_dst); break; - case MIX_DOWNMIX: mixing_push_downmix(vgmstream, mix.ch_dst); break; - case MIX_KILLMIX: mixing_push_killmix(vgmstream, mix.ch_dst); break; + case MIX_SWAP: mixing_push_swap(vgmstream, mix->ch_dst, mix->ch_src); break; + case MIX_ADD: mixing_push_add(vgmstream, mix->ch_dst, mix->ch_src, 1.0); break; + case MIX_ADD_VOLUME: mixing_push_add(vgmstream, mix->ch_dst, mix->ch_src, mix->vol); break; + case MIX_VOLUME: mixing_push_volume(vgmstream, mix->ch_dst, mix->vol); break; + case MIX_LIMIT: mixing_push_limit(vgmstream, mix->ch_dst, mix->vol); break; + case MIX_UPMIX: mixing_push_upmix(vgmstream, mix->ch_dst); break; + case MIX_DOWNMIX: mixing_push_downmix(vgmstream, mix->ch_dst); break; + case MIX_KILLMIX: mixing_push_killmix(vgmstream, mix->ch_dst); break; case MIX_FADE: /* Convert from time to samples now that sample rate is final. * Samples and time values may be mixed though, so it's done for every * value (if one is 0 the other will be too, though) */ - if (mix.time_pre > 0.0) mix.sample_pre = mix.time_pre * vgmstream->sample_rate; - if (mix.time_start > 0.0) mix.sample_start = mix.time_start * vgmstream->sample_rate; - if (mix.time_end > 0.0) mix.sample_end = mix.time_end * vgmstream->sample_rate; - if (mix.time_post > 0.0) mix.sample_post = mix.time_post * vgmstream->sample_rate; + if (mix->time_pre > 0.0) mix->sample_pre = mix->time_pre * vgmstream->sample_rate; + if (mix->time_start > 0.0) mix->sample_start = mix->time_start * vgmstream->sample_rate; + if (mix->time_end > 0.0) mix->sample_end = mix->time_end * vgmstream->sample_rate; + if (mix->time_post > 0.0) mix->sample_post = mix->time_post * vgmstream->sample_rate; /* convert special meaning too */ - if (mix.time_pre < 0.0) mix.sample_pre = -1; - if (mix.time_post < 0.0) mix.sample_post = -1; + if (mix->time_pre < 0.0) mix->sample_pre = -1; + if (mix->time_post < 0.0) mix->sample_post = -1; - mixing_push_fade(vgmstream, mix.ch_dst, mix.vol_start, mix.vol_end, mix.shape, - mix.sample_pre, mix.sample_start, mix.sample_end, mix.sample_post); + if (mix->position_type == TXTP_POSITION_LOOPS && vgmstream->loop_flag) { + int loop_pre = vgmstream->loop_start_sample; + int loop_samples = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); + + position_samples = loop_pre + loop_samples * mix->position; + + if (mix->sample_pre >= 0) mix->sample_pre += position_samples; + mix->sample_start += position_samples; + mix->sample_end += position_samples; + if (mix->sample_post >= 0) mix->sample_post += position_samples; + } + + + mixing_push_fade(vgmstream, mix->ch_dst, mix->vol_start, mix->vol_end, mix->shape, + mix->sample_pre, mix->sample_start, mix->sample_end, mix->sample_post); break; /* macro mixes */ - case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix.vol, mix.mask); break; - case MACRO_TRACK: mixing_macro_track(vgmstream, mix.mask); break; - case MACRO_LAYER: mixing_macro_layer(vgmstream, mix.max, mix.mask, mix.mode); break; - case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix.max); break; - case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix.max, mix.mode); break; + case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix->vol, mix->mask); break; + case MACRO_TRACK: mixing_macro_track(vgmstream, mix->mask); break; + case MACRO_LAYER: mixing_macro_layer(vgmstream, mix->max, mix->mask, mix->mode); break; + case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix->max); break; + case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix->max, mix->mode); break; + case MACRO_DOWNMIX: mixing_macro_downmix(vgmstream, mix->max); break; default: break; @@ -543,14 +592,17 @@ static void clean_filename(char * filename) { * - %n: special match (not counted in return value), chars consumed until that point (can appear and be set multiple times) */ -static int get_double(const char * config, double *value) { +static int get_double(const char * config, double *value, int *is_set) { int n, m; double temp; + if (is_set) *is_set = 0; + m = sscanf(config, " %lf%n", &temp,&n); if (m != 1 || temp < 0) return 0; + if (is_set) *is_set = 1; *value = temp; return n; } @@ -567,6 +619,25 @@ static int get_int(const char * config, int *value) { return n; } +static int get_position(const char * config, double *value_f, char *value_type) { + int n,m; + double temp_f; + char temp_c; + + /* test if format is position: N.n(type) */ + m = sscanf(config, " %lf%c%n", &temp_f,&temp_c,&n); + if (m != 2 || temp_f < 0.0) + return 0; + /* test accepted chars as it will capture anything */ + if (temp_c != TXTP_POSITION_LOOPS) + return 0; + + *value_f = temp_f; + *value_type = temp_c; + return n; +} + + static int get_time(const char * config, double *value_f, int32_t *value_i) { int n,m; int temp_i1, temp_i2; @@ -577,7 +648,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { m = sscanf(config, " %i%c%i%n", &temp_i1,&temp_c,&temp_i2,&n); if (m == 3 && (temp_c == ':' || temp_c == '_')) { m = sscanf(config, " %lf%c%lf%n", &temp_f1,&temp_c,&temp_f2,&n); - if (m != 3 || temp_f1 < 0.0 || temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0) + if (m != 3 || /*temp_f1 < 0.0 ||*/ temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0) return 0; *value_f = temp_f1 * 60.0 + temp_f2; @@ -588,7 +659,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { m = sscanf(config, " %i.%i%n", &temp_i1,&temp_i2,&n); if (m == 2) { m = sscanf(config, " %lf%n", &temp_f1,&n); - if (m != 1 || temp_f1 < 0.0) + if (m != 1 /*|| temp_f1 < 0.0*/) return 0; *value_f = temp_f1; return n; @@ -685,14 +756,14 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { char type, separator; m = sscanf(config, " %d %c%n", &mix->ch_dst, &type, &n); - if (n == 0 || m != 2) goto fail; + if (m != 2 || n == 0) goto fail; config += n; tn += n; if (type == '^') { /* full definition */ m = sscanf(config, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n); - if (n == 0 || m != 3) goto fail; + if (m != 3 || n == 0) goto fail; config += n; tn += n; @@ -702,7 +773,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { tn += n; m = sscanf(config, " %c%n", &separator, &n); - if (n == 0 || m != 1 || separator != '~') goto fail; + if ( m != 1 || n == 0 || separator != '~') goto fail; config += n; tn += n; @@ -712,7 +783,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { tn += n; m = sscanf(config, " %c%n", &separator, &n); - if (n == 0 || m != 1 || separator != '+') goto fail; + if (m != 1 || n == 0 || separator != '+') goto fail; config += n; tn += n; @@ -722,7 +793,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { tn += n; m = sscanf(config, " %c%n", &separator, &n); - if (n == 0 || m != 1 || separator != '~') goto fail; + if (m != 1 || n == 0 || separator != '~') goto fail; config += n; tn += n; @@ -750,13 +821,18 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { mix->time_pre = -1.0; mix->sample_pre = -1; + n = get_position(config, &mix->position, &mix->position_type); + //if (n == 0) goto fail; /* optional */ + config += n; + tn += n; + n = get_time(config, &mix->time_start, &mix->sample_start); if (n == 0) goto fail; config += n; tn += n; m = sscanf(config, " %c%n", &separator, &n); - if (n == 0 || m != 1 || separator != '+') goto fail; + if (m != 1 || n == 0 || separator != '+') goto fail; config += n; tn += n; @@ -783,8 +859,8 @@ void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) { return; } - /* parsers reads ch1 = first, but for mixing code ch0 = first - * (if parser reads ch0 here it'll become -1 with special meaning in code) */ + /* parser reads ch1 = first, but for mixing code ch0 = first + * (if parser reads ch0 here it'll become -1 with meaning of "all channels" in mixing code) */ mix->ch_dst--; mix->ch_src--; mix->command = command; @@ -795,14 +871,19 @@ void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) { static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filename) { + + /* don't memcopy to allow list additions and ignore values not set, + * as current can be "default" config */ + //*current = *cfg; + if (filename) strcpy(current->filename, filename); - current->subsong = cfg->subsong; + if (cfg->subsong) + current->subsong = cfg->subsong; - current->channel_mask = cfg->channel_mask; - - //*current = *cfg; /* don't memcopy to allow list additions */ //todo save list first then memcpy + if (cfg->channel_mask) + current->channel_mask = cfg->channel_mask; if (cfg->mixing_count > 0) { int i; @@ -812,20 +893,46 @@ static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filenam } } - current->config_loop_count = cfg->config_loop_count; - current->config_fade_time = cfg->config_fade_time; - current->config_fade_delay = cfg->config_fade_delay; - current->config_ignore_loop = cfg->config_ignore_loop; - current->config_force_loop = cfg->config_force_loop; - current->config_ignore_fade = cfg->config_ignore_fade; + if (cfg->config_loop_count_set) { + current->config_loop_count_set = cfg->config_loop_count_set; + current->config_loop_count = cfg->config_loop_count; + } + if (cfg->config_fade_time_set) { + current->config_fade_time_set = cfg->config_fade_time_set; + current->config_fade_time = cfg->config_fade_time; + } + if (cfg->config_fade_delay_set) { + current->config_fade_delay_set = cfg->config_fade_delay_set; + current->config_fade_delay = cfg->config_fade_delay; + } + if (cfg->config_ignore_loop) { + current->config_ignore_loop = cfg->config_ignore_loop; + } + if (cfg->config_force_loop) { + current->config_force_loop = cfg->config_force_loop; + } + if (cfg->config_ignore_fade) { + current->config_ignore_fade = cfg->config_ignore_fade; + } - current->sample_rate = cfg->sample_rate; - current->loop_install = cfg->loop_install; - current->loop_end_max = cfg->loop_end_max; - current->loop_start_sample = cfg->loop_start_sample; - current->loop_start_second = cfg->loop_start_second; - current->loop_end_sample = cfg->loop_end_sample; - current->loop_end_second = cfg->loop_end_second; + if (cfg->sample_rate > 0) { + current->sample_rate = cfg->sample_rate; + } + + if (cfg->loop_install_set) { + current->loop_install_set = cfg->loop_install_set; + current->loop_end_max = cfg->loop_end_max; + current->loop_start_sample = cfg->loop_start_sample; + current->loop_start_second = cfg->loop_start_second; + current->loop_end_sample = cfg->loop_end_sample; + current->loop_end_second = cfg->loop_end_second; + } + + if (cfg->trim_set) { + current->trim_set = cfg->trim_set; + current->trim_second = cfg->trim_second; + current->trim_sample = cfg->trim_sample; + } } static void parse_config(txtp_entry *cfg, char *config) { @@ -980,15 +1087,15 @@ static void parse_config(txtp_entry *cfg, char *config) { //;VGM_LOG("TXTP: ignore_fade=%i\n", cfg->config_ignore_fade); } else if (strcmp(command,"l") == 0) { - config += get_double(config, &cfg->config_loop_count); + config += get_double(config, &cfg->config_loop_count, &cfg->config_loop_count_set); //;VGM_LOG("TXTP: loop_count=%f\n", cfg->config_loop_count); } else if (strcmp(command,"f") == 0) { - config += get_double(config, &cfg->config_fade_time); + config += get_double(config, &cfg->config_fade_time, &cfg->config_fade_time_set); //;VGM_LOG("TXTP: fade_time=%f\n", cfg->config_fade_time); } else if (strcmp(command,"d") == 0) { - config += get_double(config, &cfg->config_fade_delay); + config += get_double(config, &cfg->config_fade_delay, &cfg->config_fade_delay_set); //;VGM_LOG("TXTP: fade_delay %f\n", cfg->config_fade_delay); } else if (strcmp(command,"h") == 0) { @@ -1006,17 +1113,22 @@ static void parse_config(txtp_entry *cfg, char *config) { } config += n; - cfg->loop_install = 1; + cfg->loop_install_set = 1; } //;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg->loop_install, cfg->loop_end_max, // cfg->loop_start_sample, cfg->loop_end_sample, cfg->loop_start_second, cfg->loop_end_second); } + else if (strcmp(command,"t") == 0) { + n = get_time(config, &cfg->trim_second, &cfg->trim_sample); + cfg->trim_set = (n > 0); + //;VGM_LOG("TXTP: trim %i - %f / %i\n", cfg->trim_set, cfg->trim_second, cfg->trim_sample); + } //todo cleanup else if (strcmp(command,"@volume") == 0) { txtp_mix_data mix = {0}; - nm = get_double(config, &mix.vol); + nm = get_double(config, &mix.vol, NULL); config += nm; if (nm == 0) continue; @@ -1071,6 +1183,16 @@ static void parse_config(txtp_entry *cfg, char *config) { add_mixing(cfg, &mix, type); } + else if (strcmp(command,"@downmix") == 0) { + txtp_mix_data mix = {0}; + + mix.max = 2; /* stereo only for now */ + //nm = get_int(config, &mix.max); + //config += nm; + //if (nm == 0) continue; + + add_mixing(cfg, &mix, MACRO_DOWNMIX); + } else if (config[nc] == ' ') { //;VGM_LOG("TXTP: comment\n"); break; /* comment, ignore rest */ @@ -1305,29 +1427,12 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { txtp->is_segmented = 1; - /* empty file: use filename with config (ex. "song.ext#3.txtp") */ - if (get_streamfile_size(streamFile) == 0) { - char filename[PATH_LIMIT] = {0}; - char* ext; - get_streamfile_filename(streamFile, filename,PATH_LIMIT); - - /* remove ".txtp" */ - ext = strrchr(filename,'.'); - if (!ext) goto fail; /* ??? */ - ext[0] = '\0'; - - if (!add_entry(txtp, filename, 0)) - goto fail; - - return txtp; - } - - /* skip BOM if needed */ - if ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF) + if (file_size > 0 && + ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF)) txt_offset = 0x02; - /* normal file: read and parse lines */ + /* read and parse lines */ while (txt_offset < file_size) { char line[TXTP_LINE_MAX] = {0}; char key[TXTP_LINE_MAX] = {0}, val[TXTP_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */ @@ -1359,6 +1464,16 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { goto fail; } + /* mini-txth: if no entries are set try with filename, ex. from "song.ext#3.txtp" use "song.ext#3" + * (it's possible to have default "commands" inside the .txtp plus filename+config) */ + if (txtp->entry_count == 0) { + char filename[PATH_LIMIT] = {0}; + + get_streamfile_basename(streamFile, filename, sizeof(filename)); + + add_entry(txtp, filename, 0); + } + return txtp; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index 20f4dffe9..dd719cfb5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -360,15 +360,12 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE case RAW_AT3_105: case RAW_AT3: { - uint8_t buf[0x100]; - int32_t bytes, block_size, encoder_delay, joint_stereo; + int block_align, encoder_delay; - block_size = (bao->codec == RAW_AT3_105 ? 0x98 : 0xc0) * vgmstream->channels; - joint_stereo = 0; + block_align = (bao->codec == RAW_AT3_105 ? 0x98 : 0xc0) * vgmstream->channels; encoder_delay = 0; /* num_samples is full bytes-to-samples (unlike FMT_AT3) and comparing X360 vs PS3 games seems ok */ - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, vgmstream->stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); - vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, bao->stream_size); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamData, start_offset,vgmstream->stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -376,16 +373,10 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE } case FMT_AT3: { - ffmpeg_codec_data *ffmpeg_data; - - ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, bao->stream_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamData, start_offset, NULL); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - - if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ - ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamData, start_offset)); break; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c new file mode 100644 index 000000000..705adbcc0 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c @@ -0,0 +1,660 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2 } ubi_hx_codec; + +typedef struct { + int big_endian; + int total_subsongs; + + int codec_id; + ubi_hx_codec codec; /* unified codec */ + int header_index; /* entry number within section2 */ + off_t header_offset; /* entry offset within internal .HXx */ + size_t header_size; /* entry offset within internal .HXx */ + char class_name[255]; + size_t class_size; + size_t stream_mode; + + off_t stream_offset; /* data offset within external stream */ + size_t stream_size; /* data size within external stream */ + uint32_t cuuid1; /* usually "Res" id1: class (1=Event, 3=Wave), id2: group id+sound id, */ + uint32_t cuuid2; /* others have some complex id (not hash), id1: parent id?, id2: file id? */ + + int loop_flag; + int channels; + int sample_rate; + int num_samples; + + int is_external; + char resource_name[0x28]; /* filename to the external stream */ + char internal_name[255]; /* WavRes's assigned name */ + char readable_name[255]; /* final subsong name */ + +} ubi_hx_header; + + +static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong); +static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *sf); + +/* .HXx - banks from Ubisoft's HXAudio engine games [Rayman Arena, Rayman 3, XIII] */ +VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) { + VGMSTREAM* vgmstream = NULL; + ubi_hx_header hx = {0}; + int target_subsong = streamFile->stream_index; + + + /* checks */ + /* .hxd: Rayman M/Arena (all), PK: Out of Shadows (all) + * .hxc: Rayman 3 (PC), XIII (PC) + * .hx2: Rayman 3 (PS2), XIII (PS2) + * .hxg: Rayman 3 (GC), XIII (GC) + * .hxx: Rayman 3 (Xbox), Rayman 3 HD (X360) + * .hx3: Rayman 3 HD (PS3) */ + if (!check_extensions(streamFile, "hxd,hxc,hx2,hxg,hxx,hx3")) + goto fail; + + /* .HXx is a slightly less bizarre bank with various resource classes (events, streams, etc, not unlike other Ubi's engines) + * then an index to those types. Some games leave a companion .bnh with text info, probably leftover from their tools. + * Game seems to play files by calling linked ids: EventResData (play/stop/etc) > Random/Program/Wav ResData (1..N refs) > FileIdObj */ + + /* HX CONFIG */ + hx.big_endian = guess_endianness32bit(0x00, streamFile); + + /* HX HEADER */ + if (!parse_hx(&hx, streamFile, target_subsong)) + goto fail; + + /* CREATE VGMSTREAM */ + vgmstream = init_vgmstream_ubi_hx_header(&hx, streamFile); + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +static void build_readable_name(char * buf, size_t buf_size, ubi_hx_header * hx) { + const char *grp_name; + + if (hx->is_external) + grp_name = hx->resource_name; + else + grp_name = "internal"; + + if (hx->internal_name[0]) + snprintf(buf,buf_size, "%s/%i/%08x-%08x/%s/%s", "hx", hx->header_index, hx->cuuid1,hx->cuuid2, grp_name, hx->internal_name); + else + snprintf(buf,buf_size, "%s/%i/%08x-%08x/%s", "hx", hx->header_index, hx->cuuid1,hx->cuuid2, grp_name); +} + + +/* get referenced name from WavRes, using the index again (abridged) */ +static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE; + off_t index_offset, offset; + int i, index_entries; + char class_name[255]; + + + index_offset = read_32bit(0x00, sf); + index_entries = read_32bit(index_offset + 0x08, sf); + offset = index_offset + 0x0c; + for (i = 0; i < index_entries; i++) { + off_t header_offset; + size_t class_size; + int j, link_count, language_count, is_found = 0; + + + class_size = read_32bit(offset + 0x00, sf); + if (class_size > sizeof(class_name)+1) goto fail; + read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */ + offset += 0x04 + class_size; + + header_offset = read_32bit(offset + 0x08, sf); + offset += 0x10; + + //unknown_count = read_32bit(offset + 0x00, sf); + offset += 0x04; + + link_count = read_32bit(offset + 0x00, sf); + offset += 0x04; + for (j = 0; j < link_count; j++) { + uint32_t link_id1 = (uint32_t)read_32bit(offset + 0x00, sf); + uint32_t link_id2 = (uint32_t)read_32bit(offset + 0x04, sf); + + if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) { + is_found = 1; + } + offset += 0x08; + } + + language_count = read_32bit(offset + 0x00, sf); + offset += 0x04; + for (j = 0; j < language_count; j++) { + uint32_t link_id1 = (uint32_t)read_32bit(offset + 0x08, sf); + uint32_t link_id2 = (uint32_t)read_32bit(offset + 0x0c, sf); + + if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) { + is_found = 1; + } + + offset += 0x10; + } + + /* identify all possible names so unknown platforms fail */ + if (is_found && ( + strcmp(class_name, "CPCWavResData") == 0 || + strcmp(class_name, "CPS2WavResData") == 0 || + strcmp(class_name, "CGCWavResData") == 0 || + strcmp(class_name, "CXBoxWavResData") == 0 || + strcmp(class_name, "CPS3WavResData") == 0)) { + size_t resclass_size, internal_size; + off_t wavres_offset = header_offset; + + /* parse WavRes header */ + resclass_size = read_32bit(wavres_offset, sf); + wavres_offset += 0x04 + resclass_size + 0x08 + 0x04; /* skip class + cuiid + flags */ + + internal_size = read_32bit(wavres_offset + 0x00, sf); /* usually 0 in consoles */ + if (internal_size > sizeof(hx->internal_name)+1) goto fail; + read_string(hx->internal_name,internal_size+1, wavres_offset + 0x04, sf); + return 1; + } + } + +fail: + return 0; +} + + +/* parse a single known header resource at offset */ +static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t size, int index) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = hx->big_endian ? read_16bitBE : read_16bitLE; + off_t riff_offset, riff_size, chunk_offset, stream_adjust = 0, resource_size; + size_t chunk_size; + int cue_flag = 0; + + //todo cleanup/unify common readings + + //;VGM_LOG("UBI HX: header class %s, o=%lx, s=%x\n\n", class_name, header_offset, header_size); + + hx->header_index = index; + hx->header_offset = offset; + hx->header_size = size; + + hx->class_size = read_32bit(offset + 0x00, sf); + if (hx->class_size > sizeof(hx->class_name)+1) goto fail; + read_string(hx->class_name,hx->class_size+1, offset + 0x04, sf); + offset += 0x04 + hx->class_size; + + hx->cuuid1 = (uint32_t)read_32bit(offset + 0x00, sf); + hx->cuuid2 = (uint32_t)read_32bit(offset + 0x04, sf); + offset += 0x08; + + if (strcmp(hx->class_name, "CPCWaveFileIdObj") == 0 || + strcmp(hx->class_name, "CPS2WaveFileIdObj") == 0 || + strcmp(hx->class_name, "CGCWaveFileIdObj") == 0) { + uint32_t flag_type = read_32bit(offset + 0x00, sf); + + if (flag_type == 0x01 || flag_type == 0x02) { /* Rayman Arena */ + if (read_32bit(offset + 0x04, sf) != 0x00) goto fail; + hx->stream_mode = read_32bit(offset + 0x08, sf); /* flag: 0=internal, 1=external */ + /* 0x0c: flag: 0=static, 1=stream */ + offset += 0x10; + } + else if (flag_type == 0x03) { /* others */ + /* 0x04: some kind of parent id shared by multiple Waves, or 0 */ + offset += 0x08; + + if (strcmp(hx->class_name, "CGCWaveFileIdObj") == 0) { + if (read_32bit(offset + 0x00, sf) != read_32bit(offset + 0x04, sf)) goto fail; /* meaning? */ + hx->stream_mode = read_32bit(offset + 0x04, sf); + offset += 0x08; + } + else { + hx->stream_mode = read_8bit(offset, sf); + offset += 0x01; + } + } + else { + VGM_LOG("UBI HX: unknown flag-type\n"); + goto fail; + } + + /* get bizarro adjust (found in XIII external files) */ + if (hx->stream_mode == 0x0a) { + stream_adjust = read_32bit(offset, sf); /* what */ + offset += 0x04; + } + + //todo probably a flag: &1=external, &2=stream, &8=has adjust (XIII), &4=??? (XIII PS2, small, mono) + switch(hx->stream_mode) { + case 0x00: /* memory (internal file) */ + riff_offset = offset; + riff_size = read_32bit(riff_offset + 0x04, sf) + 0x08; + break; + + case 0x01: /* static (smaller external file) */ + case 0x03: /* stream (bigger external file) */ + case 0x07: /* static? */ + case 0x0a: /* static? */ + resource_size = read_32bit(offset + 0x00, sf); + if (resource_size > sizeof(hx->resource_name)+1) goto fail; + read_string(hx->resource_name,resource_size+1, offset + 0x04, sf); + + riff_offset = offset + 0x04 + resource_size; + riff_size = read_32bit(riff_offset + 0x04, sf) + 0x08; + + hx->is_external = 1; + break; + + default: + goto fail; + } + + + /* parse pseudo-RIFF "fmt" */ + if (read_32bit(riff_offset, sf) != 0x46464952) /* "RIFF" in machine endianness */ + goto fail; + + hx->codec_id = read_16bit(riff_offset + 0x14 , sf); + switch(hx->codec_id) { + case 0x01: hx->codec = PCM; break; + case 0x02: hx->codec = UBI; break; + case 0x03: hx->codec = PSX; break; + case 0x04: hx->codec = DSP; break; + default: goto fail; + } + hx->channels = read_16bit(riff_offset + 0x16, sf); + hx->sample_rate = read_32bit(riff_offset + 0x18, sf); + + /* find "datx" (external) or "data" (internal) also in machine endianness */ + if (hx->is_external) { + if (find_chunk_riff_ve(sf, 0x78746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian)) { + hx->stream_size = read_32bit(chunk_offset + 0x00, sf); + hx->stream_offset = read_32bit(chunk_offset + 0x04, sf) + stream_adjust; + } + else if ((flag_type == 0x01 || flag_type == 0x02) && /* Rayman M (not Arena) uses "data" instead */ + find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,&chunk_size, hx->big_endian)) { + hx->stream_size = chunk_size; + hx->stream_offset = read_32bit(chunk_offset + 0x00, sf) + stream_adjust; + } + else { + goto fail; + } + } + else { + if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian)) + goto fail; + hx->stream_offset = chunk_offset; + hx->stream_size = riff_size - (chunk_offset - riff_offset); + } + + /* can contain other RIFF stuff like "cue ", "labl" and "ump3" + * XIII music uses cue/labl to play/loop dynamic sections */ + } + else if (strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 || + strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0 || + strcmp(hx->class_name, "CPS3StaticAC3WaveFileIdObj") == 0 || + strcmp(hx->class_name, "CPS3StreamAC3WaveFileIdObj") == 0) { + + hx->stream_offset = read_32bit(offset + 0x00, sf); + hx->stream_size = read_32bit(offset + 0x04, sf); + offset += 0x08; + + if (read_32bit(offset + 0x00, sf) != 0x01) goto fail; + /* 0x04: some kind of parent id shared by multiple Waves, or 0 */ + offset += 0x08; + + hx->stream_mode = read_8bit(offset, sf); + offset += 0x01; + + if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 || + strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0) && !hx->big_endian) { + /* micro header: some mix of channels + block size + sample rate + flags, unsure of which bits */ + hx->codec = XIMA; + hx->channels = (uint8_t)read_8bit(offset + 0x01, sf); + switch(hx->channels) { /* upper 2 bits? */ + case 0x48: hx->channels = 1; break; + case 0x90: hx->channels = 2; break; + default: goto fail; + } + hx->sample_rate = (uint16_t)(read_16bit(offset + 0x02, sf) & 0x7FFF) << 1; /* ??? */ + cue_flag = (uint8_t) read_8bit (offset + 0x03, sf) & (1<<7); + offset += 0x04; + } + else if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 || + strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0) && hx->big_endian) { + /* fake fmt chunk */ + hx->codec = XMA2; + hx->channels = (uint16_t)read_16bit(offset + 0x02, sf); + hx->sample_rate = read_32bit(offset + 0x04, sf); + hx->num_samples = read_32bit(offset + 0x18, sf) / 0x02 / hx->channels; + cue_flag = read_32bit(offset + 0x34, sf); + offset += 0x38; + } + else { + /* MSFC header */ + hx->codec = ATRAC3; + hx->codec_id = read_32bit(offset + 0x04, sf); + hx->channels = read_32bit(offset + 0x08, sf); + hx->sample_rate = read_32bit(offset + 0x10, sf); + cue_flag = read_32bit(offset + 0x40, sf); + offset += 0x44; + } + + /* skip cue table that sometimes exists in streams */ + if (cue_flag) { + int j; + + size_t cue_count = read_32bit(offset, sf); + offset += 0x04; + for (j = 0; j < cue_count; j++) { + /* 0x00: id? */ + size_t description_size = read_32bit(offset + 0x04, sf); /* for next string */ + offset += 0x08 + description_size; + } + } + + + switch(hx->stream_mode) { + case 0x01: /* static (smaller external file) */ + case 0x03: /* stream (bigger external file) */ + case 0x07: /* stream? */ + resource_size = read_32bit(offset + 0x00, sf); + if (resource_size > sizeof(hx->resource_name)+1) goto fail; + read_string(hx->resource_name,resource_size+1, offset + 0x04, sf); + + hx->is_external = 1; + break; + + default: + goto fail; + } + } + else { + goto fail; + } + + return 1; +fail: + VGM_LOG("UBI HX: error parsing header at %lx\n", hx->header_offset); + return 0; +} + + +/* parse a bank index and its possible audio headers (some info from Droolie's .bms) */ +static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE; + off_t index_offset, offset; + int i, index_entries; + char class_name[255]; + + + index_offset = read_32bit(0x00, sf); + if (read_32bit(index_offset + 0x00, sf) != 0x58444E49) /* "XDNI" (INDX in given endianness) */ + goto fail; + if (read_32bit(index_offset + 0x04, sf) != 0x02) /* type? */ + goto fail; + + if (target_subsong == 0) target_subsong = 1; + + index_entries = read_32bit(index_offset + 0x08, sf); + offset = index_offset + 0x0c; + for (i = 0; i < index_entries; i++) { + off_t header_offset; + size_t class_size, header_size; + int j, unknown_count, link_count, language_count; + + //;VGM_LOG("UBI HX: index %i at %lx\n", i, offset); + + /* parse index entries: offset to actual header plus some extra info also in the header */ + + class_size = read_32bit(offset + 0x00, sf); + if (class_size > sizeof(class_name)+1) goto fail; + + read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */ + offset += 0x04 + class_size; + + /* 0x00: id1+2 */ + header_offset = read_32bit(offset + 0x08, sf); + header_size = read_32bit(offset + 0x0c, sf); + offset += 0x10; + + /* not seen */ + unknown_count = read_32bit(offset + 0x00, sf); + if (unknown_count != 0) { + VGM_LOG("UBI HX: found unknown near %lx\n", offset); + goto fail; + } + offset += 0x04; + + /* ids that this object directly points to (ex. Event > Random) */ + link_count = read_32bit(offset + 0x00, sf); + offset += 0x04 + 0x08 * link_count; + + /* localized id list of WavRes (can use this list instead of the prev one) */ + language_count = read_32bit(offset + 0x00, sf); + offset += 0x04; + for (j = 0; j < language_count; j++) { + /* 0x00: lang code, in reverse endianness: "en ", "fr ", etc */ + /* 0x04: possibly count of ids for this lang */ + /* 0x08: id1+2 */ + + if (read_32bit(offset + 0x04, sf) != 1) { + VGM_LOG("UBI HX: wrong lang count near %lx\n", offset); + goto fail; /* WavRes doesn't have this field */ + } + offset += 0x10; + } + + //todo figure out CProgramResData sequences + /* identify all possible names so unknown platforms fail */ + if (strcmp(class_name, "CEventResData") == 0 || /* play/stop/etc event */ + strcmp(class_name, "CProgramResData") == 0 || /* some kind of map/object-like config to make sequences in some cases? */ + strcmp(class_name, "CActorResData") == 0 || /* same? */ + strcmp(class_name, "CRandomResData") == 0 || /* chooses random WavRes from a list */ + strcmp(class_name, "CTreeBank") == 0 || /* points to TreeRes? */ + strcmp(class_name, "CTreeRes") == 0 || /* points to TreeBank? */ + strcmp(class_name, "CSwitchResData") == 0 || /* big list of WavRes */ + strcmp(class_name, "CPCWavResData") == 0 || /* points to WaveFileIdObj */ + strcmp(class_name, "CPS2WavResData") == 0 || + strcmp(class_name, "CGCWavResData") == 0 || + strcmp(class_name, "CXBoxWavResData") == 0 || + strcmp(class_name, "CPS3WavResData") == 0) { + continue; + } + else if (strcmp(class_name, "CPCWaveFileIdObj") == 0 || + strcmp(class_name, "CPS2WaveFileIdObj") == 0 || + strcmp(class_name, "CGCWaveFileIdObj") == 0 || + strcmp(class_name, "CXBoxStaticHWWaveFileIdObj") == 0 || + strcmp(class_name, "CXBoxStreamHWWaveFileIdObj") == 0 || + strcmp(class_name, "CPS3StaticAC3WaveFileIdObj") == 0 || + strcmp(class_name, "CPS3StreamAC3WaveFileIdObj") == 0) { + ; + } + else { + VGM_LOG("UBI HX: unknown type: %s\n", class_name); + goto fail; + } + + if (link_count != 0) { + VGM_LOG("UBI HX: found links in wav object\n"); + goto fail; + } + + hx->total_subsongs++; + if (hx->total_subsongs != target_subsong) + continue; + + if (!parse_header(hx, sf, header_offset, header_size, i)) + goto fail; + if (!parse_name(hx, sf)) + goto fail; + + build_readable_name(hx->readable_name,sizeof(hx->readable_name), hx); + } + + if (target_subsong < 0 || target_subsong > hx->total_subsongs || hx->total_subsongs < 1) goto fail; + + + return 1; +fail: + return 0; +} + + +static STREAMFILE * open_hx_streamfile(ubi_hx_header *hx, STREAMFILE *sf) { + STREAMFILE *streamData = NULL; + + + if (!hx->is_external) + return NULL; + + streamData = open_streamfile_by_filename(sf, hx->resource_name); + if (streamData == NULL) { + VGM_LOG("UBI HX: external stream '%s' not found\n", hx->resource_name); + goto fail; + } + + /* streams often have a "RIFF" with "fmt" and "data" but stream offset/size is already adjusted to skip them */ + + return streamData; +fail: + return NULL; +} + +static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *streamFile) { + STREAMFILE *streamTemp = NULL; + STREAMFILE *streamData = NULL; + VGMSTREAM* vgmstream = NULL; + + + if (hx->is_external) { + streamTemp = open_hx_streamfile(hx, streamFile); + if (streamTemp == NULL) goto fail; + streamData = streamTemp; + } + else { + streamData = streamFile; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(hx->channels, hx->loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_UBI_HX; + vgmstream->sample_rate = hx->sample_rate; + vgmstream->num_streams = hx->total_subsongs; + vgmstream->stream_size = hx->stream_size; + + switch(hx->codec) { + case PCM: + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + vgmstream->num_samples = pcm_bytes_to_samples(hx->stream_size, hx->channels, 16); + break; + + case UBI: + vgmstream->codec_data = init_ubi_adpcm(streamData, hx->stream_offset, vgmstream->channels); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_UBI_ADPCM; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = ubi_adpcm_get_samples(vgmstream->codec_data); + /* XIII has 6-bit stereo music, Rayman 3 4-bit music, both use 6-bit mono) */ + break; + + case PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->num_samples = ps_bytes_to_samples(hx->stream_size, hx->channels); + break; + + case DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; + + /* dsp header at start offset */ + vgmstream->num_samples = read_32bitBE(hx->stream_offset + 0x00, streamData); + dsp_read_coefs_be(vgmstream, streamData, hx->stream_offset + 0x1c, 0x60); + dsp_read_hist_be (vgmstream, streamData, hx->stream_offset + 0x40, 0x60); + hx->stream_offset += 0x60 * hx->channels; + hx->stream_size -= 0x60 * hx->channels; + break; + + case XIMA: + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = xbox_ima_bytes_to_samples(hx->stream_size, hx->channels); + break; + +#ifdef VGM_USE_FFMPEG + case XMA2: { + int bytes, block_count, block_size; + uint8_t buf[0x200]; + + block_size = 0x800; + block_count = hx->stream_size / block_size; + + bytes = ffmpeg_make_riff_xma2(buf,0x200, hx->num_samples, hx->stream_size, hx->channels, hx->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf,bytes, hx->stream_offset,hx->stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = hx->num_samples; + + xma_fix_raw_samples_ch(vgmstream, streamData, hx->stream_offset,hx->stream_size, hx->channels, 0,0); + break; + } + + case ATRAC3: { + int block_align, encoder_delay; + + encoder_delay = 1024 + 69*2; + switch(hx->codec_id) { + case 4: block_align = 0x60 * vgmstream->channels; break; + case 5: block_align = 0x98 * vgmstream->channels; break; + case 6: block_align = 0xC0 * vgmstream->channels; break; + default: goto fail; + } + + vgmstream->num_samples = atrac3_bytes_to_samples(hx->stream_size, block_align) - encoder_delay; + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamData, hx->stream_offset,hx->stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif + + default: + goto fail; + } + + strcpy(vgmstream->stream_name, hx->readable_name); + + if (!vgmstream_open_stream(vgmstream, streamData, hx->stream_offset)) + goto fail; + close_streamfile(streamTemp); + return vgmstream; + +fail: + close_streamfile(streamTemp); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index 46a5f5227..b1528ee22 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -7,7 +7,7 @@ #define SB_MAX_LAYER_COUNT 16 /* arbitrary max */ #define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */ -typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM } ubi_sb_codec; +typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX } ubi_sb_codec; typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform; typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_sb_type; @@ -36,6 +36,7 @@ typedef struct { int audio_external_and; int audio_loop_and; int audio_group_and; + int num_codec_flags; int audio_has_internal_names; size_t audio_interleave; int audio_fix_psx_samples; @@ -74,6 +75,7 @@ typedef struct { int is_padded_sectionX_offset; int is_padded_sounds_offset; int ignore_layer_error; + int default_codec_for_group0; } ubi_sb_config; typedef struct { @@ -206,17 +208,23 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { /* SBx layout: header, section1, section2, extra section, section3, data (all except header can be null) */ sb.is_bank = 1; sb.version = read_32bit(0x00, streamFile); + + if (!config_sb_version(&sb, streamFile)) + goto fail; + sb.section1_num = read_32bit(0x04, streamFile); sb.section2_num = read_32bit(0x08, streamFile); sb.section3_num = read_32bit(0x0c, streamFile); sb.sectionX_size = read_32bit(0x10, streamFile); sb.flag1 = read_32bit(0x14, streamFile); - sb.flag2 = read_32bit(0x18, streamFile); - if (!config_sb_version(&sb, streamFile)) - goto fail; + if (sb.version <= 0x000A0000) { + sb.section1_offset = 0x18; + } else { + sb.section1_offset = 0x1c; + sb.flag2 = read_32bit(0x18, streamFile); + } - sb.section1_offset = 0x1c; if (sb.cfg.is_padded_section1_offset) sb.section1_offset = align_size_to_block(sb.section1_offset, 0x10); @@ -476,25 +484,24 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str break; case UBI_ADPCM: - /* todo custom Ubi 4/6-bit ADPCM (4b for music/stereo and 6b for voices) - * - ~0x30: stream header (varies with versions). - * contains frame sizes (normal/last), channels, sometimes sample rate/samples/etc. - * - 0x34 per channel: channel header, starts with 0x02, contains ADPCM info - * - 0x602/902: frame data divided into 2 chunks (with a padding byte) - * for each chunk frame data is read as LE int32, then divided into codes - * (some 6b span two int32). stereo interleaves 4 codes per channel. - * data is decoded using 3 tables and ops to adjust sample and calculate next step - * (6b decoding is much simpler than 4b, that applies more extra ops). - * - * used in: - * - Batman: Vengeance (PC) - * - Splinter Cell (PC) - * - some Myst IV (PC/Xbox) banks (ex. puzzle_si_splintercell.sb2) - * - Splinter Cell Essentials (PSP) voices (header has extra 0x08 with fixed value) - */ + /* custom Ubi 4/6-bit ADPCM used in early games: + * - Splinter Cell (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx) + * - Batman: Vengeance (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx) + * - Myst IV (PC/Xbox): 4bit-1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2) + * - possibly others */ - VGM_LOG("UBI SB: unsupported Ubi ADPCM found\n"); - goto fail; + /* skip extra header (some kind of id?) found in Myst IV */ + if (read_32bitBE(start_offset + 0x00, streamData) != 0x08000000 && + read_32bitBE(start_offset + 0x08, streamData) == 0x08000000) { + start_offset += 0x08; + sb->stream_size -= 0x08; + } + + vgmstream->codec_data = init_ubi_adpcm(streamData, start_offset, vgmstream->channels); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_UBI_ADPCM; + vgmstream->layout_type = layout_none; + break; case RAW_PCM: vgmstream->coding_type = coding_PCM16LE; /* always LE */ @@ -559,38 +566,28 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str #ifdef VGM_USE_FFMPEG case FMT_AT3: { - ffmpeg_codec_data *ffmpeg_data; - - /* skip weird value (3, 4) in Brothers in Arms: D-Day (PSP) */ + /* skip weird value (3 or 4) in Brothers in Arms: D-Day (PSP) */ if (read_32bitBE(start_offset+0x04,streamData) == 0x52494646) { VGM_LOG("UBI SB: skipping unknown value 0x%x before RIFF\n", read_32bitBE(start_offset+0x00,streamData)); start_offset += 0x04; sb->stream_size -= 0x04; } - ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, sb->stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamData, start_offset, NULL); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ - ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamData, start_offset)); break; } case RAW_AT3: { - ffmpeg_codec_data *ffmpeg_data; - uint8_t buf[0x100]; - int32_t bytes, block_size, encoder_delay, joint_stereo; + int block_align, encoder_delay; - block_size = 0x98 * sb->channels; - joint_stereo = 0; - encoder_delay = 0x00; /* TODO: this is may be incorrect */ + block_align = 0x98 * sb->channels; + encoder_delay = 0; /* TODO: this is may be incorrect */ - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, sb->num_samples, sb->stream_size, sb->channels, sb->sample_rate, block_size, joint_stereo, encoder_delay); - ffmpeg_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, sb->stream_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamData, start_offset,sb->stream_size, sb->num_samples,sb->channels,sb->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; @@ -733,6 +730,15 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str } break; + case FMT_MPDX: + /* a custom, chunked MPEG format (sigh) + * 0x00: samples? (related to size) + * 0x04: "2RUS" (apparently "1RUS" for mono files) + * Rest is a MPEG-like sync but not an actual MPEG header? (DLLs do refer it as MPEG) + * Files may have multiple "2RUS" or just a big one + * A companion .csb has some not-too-useful info */ + goto fail; + default: VGM_LOG("UBI SB: unknown codec\n"); goto fail; @@ -1038,9 +1044,9 @@ static VGMSTREAM * init_vgmstream_ubi_sb_header(ubi_sb_header *sb, STREAMFILE* s goto fail; } - //;VGM_LOG("UBI SB: target at %x + %x, extra=%x, name=%s, g=%i, t=%i\n", - // (uint32_t)sb->header_offset, sb->cfg.section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name, sb->group_id, sb->stream_type); - //;VGM_LOG("UBI SB: stream offset=%x, size=%x, name=%s\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external ? sb->resource_name : "internal" ); + ;VGM_LOG("UBI SB: target at %x + %x, extra=%x, name=%s, g=%i, t=%i\n", + (uint32_t)sb->header_offset, sb->cfg.section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name, sb->group_id, sb->stream_type); + ;VGM_LOG("UBI SB: stream offset=%x, size=%x, name=%s\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external ? sb->resource_name : "internal" ); switch(sb->type) { case UBI_AUDIO: @@ -1161,21 +1167,25 @@ static int parse_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* stream goto fail; } - if (sb->cfg.audio_external_flag) { + if (sb->cfg.audio_external_flag && sb->cfg.audio_external_and) { sb->is_external = (read_32bit(offset + sb->cfg.audio_external_flag, streamFile) & sb->cfg.audio_external_and); } - if (sb->cfg.audio_group_id) { - sb->group_id = read_32bit(offset + sb->cfg.audio_group_id, streamFile); - if (sb->cfg.audio_group_and) sb->group_id &= sb->cfg.audio_group_and; + if (sb->cfg.audio_group_id && sb->cfg.audio_group_and) { + /* probably means "SW decoded" */ + sb->group_id = read_32bit(offset + sb->cfg.audio_group_id, streamFile); + if (sb->cfg.audio_group_and) sb->group_id &= sb->cfg.audio_group_and; /* normalize bitflag, known groups are only id 0/1 (if needed could calculate * shift-right value here, based on cfg.audio_group_and first 1-bit) */ if (sb->group_id > 1) sb->group_id = 1; + } else { + /* old group flag (HW decoded?) */ + sb->group_id = (int)!(sb->stream_type & 0x01); } - if (sb->cfg.audio_loop_flag) { + if (sb->cfg.audio_loop_flag && sb->cfg.audio_loop_and) { sb->loop_flag = (read_32bit(offset + sb->cfg.audio_loop_flag, streamFile) & sb->cfg.audio_loop_and); } @@ -1445,156 +1455,145 @@ static int parse_stream_codec(ubi_sb_header * sb) { if (sb->type == UBI_SEQUENCE) return 1; - /* happens randomly in internal sounds in early Xbox games */ - if (sb->stream_type > 0xFF) { - //;VGM_LOG("UBI SB: garbage in stream_type\n"); - sb->stream_type = 0; /* probably ignored for non-stream types */ - } + /* in early versions, this is a bitfield with either 1 or 2 rightmost bits being flags */ + sb->stream_type >>= sb->cfg.num_codec_flags; - /* Batman: Rise of Sin Tzu (Xbox) - * (similar but also for some streams, maybe uses a 'hardware flag' possibly at 0x20?) */ - if ((sb->version == 0x000A0003 && sb->platform == UBI_XBOX) && - (!sb->is_external || strncmp(sb->resource_name, "STREAMHW.", 9) == 0)) { - sb->stream_type = 0; + if (sb->cfg.default_codec_for_group0 && sb->type == UBI_AUDIO && sb->group_id == 0) { + /* early Xbox games contain garbage in stream_type field in this case, it seems that 0x00 is assumed */ + sb->stream_type = 0x00; } - /* guess codec */ - switch (sb->stream_type) { - case 0x00: /* platform default (rarely external) */ - switch (sb->platform) { - case UBI_PC: - sb->codec = RAW_PCM; - break; - case UBI_PS2: + if (sb->stream_type == 0x00) { + switch (sb->platform) { + case UBI_PC: + sb->codec = RAW_PCM; + break; + case UBI_PS2: + sb->codec = RAW_PSX; + break; + case UBI_PSP: + if (sb->is_psp_old) + sb->codec = FMT_VAG; + else sb->codec = RAW_PSX; - break; - case UBI_PSP: - if (sb->is_psp_old) - sb->codec = FMT_VAG; - else - sb->codec = RAW_PSX; - break; - case UBI_XBOX: - sb->codec = RAW_XBOX; - break; - case UBI_GC: - case UBI_WII: - sb->codec = RAW_DSP; - break; - case UBI_X360: - sb->codec = RAW_XMA1; - break; + break; + case UBI_XBOX: + sb->codec = RAW_XBOX; + break; + case UBI_GC: + case UBI_WII: + sb->codec = RAW_DSP; + break; + case UBI_X360: + sb->codec = RAW_XMA1; + break; #if 0 - case UBI_PS3: /* assumed, but no games seem to use it */ - sb->codec = RAW_AT3; - break; + case UBI_PS3: /* assumed, but no games seem to use it */ + sb->codec = RAW_AT3; + break; #endif - case UBI_3DS: - sb->codec = FMT_CWAV; - break; - default: - VGM_LOG("UBI SB: unknown internal format\n"); - goto fail; - } - break; + case UBI_3DS: + sb->codec = FMT_CWAV; + break; + default: + VGM_LOG("UBI SB: unknown internal format\n"); + goto fail; + } + } else if (sb->version == 0x00000000) { + /* really old version predating SBx and SMx formats */ + /* Rayman 2: The Great Escape */ + /* Tonic Trouble */ + /* Donald Duck: Goin' Quackers */ + /* Disney's Dinosaur */ - case 0x01: - switch (sb->version) { - case 0x00000003: /* Donald Duck: Goin' Quackers */ - case 0x00000004: /* Myst III: Exile */ - case 0x00000007: /* Splinter Cell */ - switch (sb->platform) { - case UBI_PS2: sb->codec = RAW_PSX; break; - case UBI_GC: sb->codec = RAW_DSP; break; - case UBI_PC: sb->codec = RAW_PCM; break; - case UBI_XBOX: sb->codec = RAW_XBOX; break; - default: VGM_LOG("UBI SB: unknown old internal format\n"); goto fail; - } - break; - default: - sb->codec = RAW_PCM; /* uncommon, ex. Wii/PSP/3DS */ - break; - } - break; + switch (sb->stream_type) { + case 0x01: + sb->codec = FMT_MPDX; + break; + + case 0x02: + sb->codec = FMT_APM; + break; - case 0x02: - switch (sb->version) { - case 0x00000007: /* Splinter Cell, Splinter Cell: Pandora Tomorrow */ - case 0x00120012: /* Myst IV: Exile */ - //todo splinter Cell Essentials - sb->codec = UBI_ADPCM; - break; - default: - sb->codec = RAW_PSX; /* PS3 */ - break; - } - break; + default: + VGM_LOG("Unknown stream_type %02x for version %08x\n", sb->stream_type, sb->version); + goto fail; + } + } else if (sb->version < 0x000A0000) { + switch (sb->stream_type) { + case 0x01: + sb->codec = UBI_ADPCM; + break; - case 0x03: - sb->codec = UBI_IMA; /* Ubi IMA v3+ (versions handled in decoder) */ - break; + case 0x02: + sb->codec = UBI_IMA; /* Ubi IMA v2/v3 */ + break; - case 0x04: - switch (sb->version) { - case 0x00000000: /* Rayman 2, Tonic Trouble */ - sb->codec = FMT_APM; - break; + default: + VGM_LOG("Unknown stream_type %02x for version %08x\n", sb->stream_type, sb->version); + goto fail; + } + } else { + switch (sb->stream_type) { + case 0x01: + sb->codec = RAW_PCM; /* uncommon, ex. Wii/PSP/3DS */ + break; - case 0x00000007: /* Splinter Cell, Splinter Cell: Pandora Tomorrow */ - sb->codec = UBI_IMA; - break; - default: - sb->codec = FMT_OGG; /* later PC games */ - break; - } - break; + case 0x02: + switch (sb->platform) { + case UBI_PS3: + sb->codec = RAW_PSX; /* PS3 */ + break; + case UBI_PSP: + /* TODO: IMA using Ubisoft ADPCM frame layout [Splinter Cell: Essentials (PSP)] */ + VGM_LOG("Unimplemented custom IMA codec.\n"); + goto fail; + default: + sb->codec = UBI_ADPCM; + break; + } + break; - case 0x05: - switch (sb->platform) { - case UBI_X360: - sb->codec = FMT_XMA1; - break; - case UBI_PS3: - case UBI_PSP: - sb->codec = FMT_AT3; - break; - default: - VGM_LOG("UBI SB: unknown codec for stream_type %x\n", sb->stream_type); - goto fail; - } - break; + case 0x03: + sb->codec = UBI_IMA; /* Ubi IMA v3+ (versions handled in decoder) */ + break; - case 0x06: - switch (sb->version) { - case 0x00000003: /* Batman: Vengeance (PC) */ - sb->codec = UBI_ADPCM; - break; - default: - sb->codec = RAW_PSX; /* later PSP and PS3(?) games */ - break; - } - break; + case 0x04: + sb->codec = FMT_OGG; /* later PC games */ + break; - case 0x07: - sb->codec = RAW_AT3; /* PS3 games */ - break; + case 0x05: + switch (sb->platform) { + case UBI_X360: + sb->codec = FMT_XMA1; + break; + case UBI_PS3: + case UBI_PSP: + sb->codec = FMT_AT3; + break; + default: + VGM_LOG("UBI SB: unknown codec for stream_type %x\n", sb->stream_type); + goto fail; + } + break; - case 0x08: - switch (sb->version) { - case 0x00000003: /* Donald Duck: Goin' Quackers */ - case 0x00000004: /* Myst III: Exile */ - sb->codec = UBI_IMA; /* Ubi IMA v2/v3 */ - break; - default: - sb->codec = FMT_AT3; - break; - } - break; + case 0x06: + sb->codec = RAW_PSX; /* later PSP and PS3(?) games */ + break; - default: - VGM_LOG("UBI SB: unknown stream_type %x\n", sb->stream_type); - goto fail; + case 0x07: + sb->codec = RAW_AT3; /* PS3 */ + break; + + case 0x08: + sb->codec = FMT_AT3; + break; + + default: + VGM_LOG("Unknown stream_type %02x\n", sb->stream_type); + goto fail; + } } return 1; @@ -1612,11 +1611,6 @@ static int parse_offsets(ubi_sb_header * sb, STREAMFILE *streamFile) { VGM_ASSERT(!sb->is_map && sb->section3_num > 2, "UBI SB: section3 > 2 found\n"); - if (!(sb->cfg.audio_group_id || sb->is_map) && sb->section3_num > 1) { - VGM_LOG("UBI SB: unexpected number of internal stream groups %i\n", sb->section3_num); - goto fail; - } - if (sb->is_external) return 1; @@ -1653,11 +1647,6 @@ static int parse_offsets(ubi_sb_header * sb, STREAMFILE *streamFile) { int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF; if (index == sb->header_index) { - if (!sb->cfg.audio_group_id && table2_num > 1) { - VGM_LOG("UBI SB: unexpected number of internal stream map groups %i at %x\n", table2_num, (uint32_t)table2_offset); - goto fail; - } - sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, streamFile); for (k = 0; k < table2_num; k++) { uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, streamFile); @@ -1683,7 +1672,7 @@ static int parse_offsets(ubi_sb_header * sb, STREAMFILE *streamFile) { sounds_offset = align_size_to_block(sounds_offset, 0x10); sb->stream_offset = sounds_offset + sb->stream_offset; - if (sb->cfg.audio_group_id && sb->section3_num > 1) { /* maybe should always test this? */ + if (sb->section3_num > 1) { /* maybe should always test this? */ for (i = 0; i < sb->section3_num; i++) { off_t offset = sb->section3_offset + sb->cfg.section3_entry_size * i; @@ -1696,8 +1685,8 @@ static int parse_offsets(ubi_sb_header * sb, STREAMFILE *streamFile) { } return 1; -fail: - return 0; +//fail: +// return 0; } /* parse a single known header resource at offset (see config_sb for info) */ @@ -1769,7 +1758,7 @@ static int parse_sb(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subso /*header_id =*/ read_32bit(offset + 0x00, streamFile); /* forces buffer read */ header_type = read_32bit(offset + 0x04, streamFile); - if (header_type <= 0x00 || header_type >= 0x10 || header_type == 0x09) { + if (header_type <= 0x00 || header_type >= 0x10) { VGM_LOG("UBI SB: unknown type %x at %x\n", header_type, (uint32_t)offset); goto fail; } @@ -1962,6 +1951,7 @@ static void config_sb_random_old(ubi_sb_header * sb, off_t sequence_count, off_t static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { int is_dino_pc = 0; + int is_ttse_pc = 0; int is_bia_ps2 = 0, is_biadd_psp = 0; int is_sc2_ps2_gc = 0; int is_sc4_pc_online = 0; @@ -2051,7 +2041,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { * 9: TYPE_THEME_OLD2 * A: TYPE_RANDOM * B: TYPE_THEME0 (sequence) - * (only 1, 4, A and B are known) + * Only 1, 2, 4, 9, A and B are known. + * 2 is used rarely in Donald Duck's demo and point to a .mdx (midi?) + * 9 is used in Tonic Trouble Special Edition + * Others are common. */ /* All types may contain memory garbage, making it harder to identify fields (platforms @@ -2090,8 +2083,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->cfg.random_extra_offset = 0x10; //sb->cfg.random_extra_size = 0x0c; - } - else if (sb->version <= 0x00000007) { + } else if (sb->version <= 0x00000007) { sb->cfg.audio_stream_size = 0x0c; sb->cfg.audio_extra_offset = 0x10; sb->cfg.audio_stream_offset = 0x14; @@ -2099,8 +2091,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->cfg.sequence_extra_offset = 0x10; sb->cfg.layer_extra_offset = 0x10; - } - else { + } else { sb->cfg.audio_stream_size = 0x08; sb->cfg.audio_extra_offset = 0x0c; sb->cfg.audio_stream_offset = 0x10; @@ -2110,6 +2101,14 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->cfg.layer_extra_offset = 0x0c; } + if (sb->version == 0x00000000 || sb->version == 0x00000200) { + sb->cfg.num_codec_flags = 1; + } else if (sb->version <= 0x00000004) { + sb->cfg.num_codec_flags = 2; + } else if (sb->version < 0x000A0000) { + sb->cfg.num_codec_flags = 1; + } + sb->allowed_types[0x01] = 1; sb->allowed_types[0x05] = 1; sb->allowed_types[0x0c] = 1; @@ -2120,6 +2119,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { if (sb->is_bnm) { //sb->allowed_types[0x0a] = 1; /* only needed inside sequences */ sb->allowed_types[0x0b] = 1; + sb->allowed_types[0x09] = 1; } #if 0 @@ -2151,6 +2151,36 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { is_dino_pc = 1; } + /* Tonic Touble beta has garbage instead of version */ + if (sb->is_bnm && sb->version > 0x00000000 && sb->platform == UBI_PC) { + STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "ED_MAIN.LCB"); + if (streamTest) { + is_ttse_pc = 1; + sb->version = 0x00000000; + close_streamfile(streamTest); + } + } + + + /* Tonic Trouble Special Edition (1999)(PC)-bnm */ + if (sb->version == 0x00000000 && sb->platform == UBI_PC && is_ttse_pc) { + config_sb_entry(sb, 0x20, 0x5c); + + config_sb_audio_fs(sb, 0x2c, 0x00, 0x30); + config_sb_audio_hs(sb, 0x42, 0x3c, 0x38, 0x38, 0x48, 0x44); + sb->cfg.audio_has_internal_names = 1; + + config_sb_sequence(sb, 0x24, 0x18); + + //config_sb_random_old(sb, 0x18, 0x0c); + + /* no layers */ + //todo type 9 needed + //todo MPX don't set stream size? + return 1; + } + + /* Rayman 2: The Great Escape (1999)(PC)-bnm */ /* Tonic Trouble (1999)(PC)-bnm */ /* Donald Duck: Goin' Quackers (2000)(PC)-bnm */ @@ -2158,7 +2188,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { if (sb->version == 0x00000000 && sb->platform == UBI_PC) { config_sb_entry(sb, 0x20, 0x5c); - config_sb_audio_fs(sb, 0x2c, 0x2c, 0x30); /* no group id */ + config_sb_audio_fs(sb, 0x2c, 0x00, 0x30); config_sb_audio_hs(sb, 0x42, 0x3c, 0x34, 0x34, 0x48, 0x44); sb->cfg.audio_has_internal_names = 1; @@ -2179,7 +2209,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { (sb->version == 0x00000003 && sb->platform == UBI_XBOX)) { config_sb_entry(sb, 0x40, 0x68); - config_sb_audio_fs(sb, 0x30, 0x30, 0x34); /* no group id? use external flag */ + config_sb_audio_fs(sb, 0x30, 0x00, 0x34); config_sb_audio_hs(sb, 0x52, 0x4c, 0x38, 0x40, 0x58, 0x54); sb->cfg.audio_has_internal_names = 1; @@ -2196,7 +2226,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { if (sb->version == 0x00000003 && sb->platform == UBI_GC) { config_sb_entry(sb, 0x40, 0x6c); - config_sb_audio_fs(sb, 0x30, 0x30, 0x34); /* no group id? use external flag */ + config_sb_audio_fs(sb, 0x30, 0x00, 0x34); config_sb_audio_hs(sb, 0x56, 0x50, 0x48, 0x48, 0x5c, 0x58); /* 0x38 may be num samples too? */ config_sb_sequence(sb, 0x2c, 0x1c); @@ -2226,6 +2256,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { } #endif +#if 0 //todo group flags and maybe num_samples for sfx are off /* Myst III: Exile (2001)(PS2)-map */ if (sb->version == 0x00000004 && sb->platform == UBI_PS2) { @@ -2238,13 +2269,14 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { config_sb_sequence(sb, 0x2c, 0x24); return 1; } +#endif /* Splinter Cell (2002)(PC)-map */ /* Splinter Cell: Pandora Tomorrow (2004)(PC)-map */ if (sb->version == 0x00000007 && sb->platform == UBI_PC) { config_sb_entry(sb, 0x58, 0x80); - config_sb_audio_fs(sb, 0x28, 0x28, 0x2c); /* no group id? use external flag */ + config_sb_audio_fs(sb, 0x28, 0x00, 0x2c); config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); sb->cfg.audio_has_internal_names = 1; @@ -2260,7 +2292,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { if (sb->version == 0x00000007 && sb->platform == UBI_XBOX) { config_sb_entry(sb, 0x58, 0x78); - config_sb_audio_fs(sb, 0x28, 0x28, 0x2c); /* no group id? use external flag */ + config_sb_audio_fs(sb, 0x28, 0x00, 0x2c); config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); sb->cfg.audio_has_internal_names = 1; @@ -2288,7 +2320,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { if (sb->version == 0x00000007 && sb->platform == UBI_PS2) { config_sb_entry(sb, 0x40, 0x70); - config_sb_audio_fb(sb, 0x1c, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_fb(sb, 0x1c, (1 << 2), 0, (1 << 4)); config_sb_audio_hs(sb, 0x24, 0x28, 0x34, 0x3c, 0x44, 0x6c); /* num_samples may be null */ config_sb_sequence(sb, 0x2c, 0x30); @@ -2309,7 +2341,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { if (sb->version == 0x00000007 && sb->platform == UBI_GC) { config_sb_entry(sb, 0x58, 0x78); - config_sb_audio_fs(sb, 0x24, 0x24, 0x28); /* no group id? use external flag */ + config_sb_audio_fs(sb, 0x24, 0x00, 0x28); config_sb_audio_hs(sb, 0x4a, 0x44, 0x2c, 0x34, 0x50, 0x4c); config_sb_sequence(sb, 0x2c, 0x34); @@ -2324,6 +2356,30 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { return 1; } + /* Prince of Persia: The Sands of Time Demo (2003)(Xbox)-bank 0x0000000D */ + if (sb->version == 0x0000000D && sb->platform == UBI_XBOX) { + config_sb_entry(sb, 0x5c, 0x74); + + config_sb_audio_fs(sb, 0x24, 0x00, 0x28); + config_sb_audio_hs(sb, 0x46, 0x40, 0x2c, 0x34, 0x4c, 0x48); + sb->cfg.audio_has_internal_names = 1; + return 1; + } + + /* Prince of Persia: The Sands of Time Demo (2003)(Xbox)-bank 0x000A0000 */ + if (sb->version == 0x000A0000 && sb->platform == UBI_XBOX) { + config_sb_entry(sb, 0x64, 0x78); + + config_sb_audio_fs(sb, 0x24, 0x28, 0x2c); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); + + config_sb_sequence(sb, 0x28, 0x14); + + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); + return 1; + } + /* Prince of Persia: Sands of Time (2003)(PC)-bank 0x000A0004 / 0x000A0002 (just in case) */ if ((sb->version == 0x000A0002 && sb->platform == UBI_PC) || (sb->version == 0x000A0004 && sb->platform == UBI_PC)) { @@ -2343,21 +2399,25 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { /* two configs with same id; use project file as identifier */ if (sb->version == 0x000A0007 && sb->platform == UBI_PS2) { STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP1"); + if (!streamTest) /* try again for localized subfolders */ + streamTest = open_streamfile_by_filename(streamFile, "../BIAAUDIO.SP1"); if (streamTest) { is_bia_ps2 = 1; close_streamfile(streamTest); } } - /* Prince of Persia: The Sands of Time (2003)(PS2)-bank 0x000A0004 / 0x000A0002 (POP1 port) */ + /* Prince of Persia: The Sands of Time (2003)(PS2)-bank 0x000A0004 / 0x000A0002 (POP1 port/Demo) */ /* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank 0x000A0007 */ /* Tom Clancy's Ghost Recon 2 (2004)(PS2)-bank 0x000A0007 */ /* Splinter Cell: Pandora Tomorrow (2006)(PS2)-bank 0x000A0008 (separate banks from main map) */ + /* Prince of Persia: Warrior Within Demo (2004)(PS2)-bank 0x00100000 */ /* Prince of Persia: Warrior Within (2004)(PS2)-bank 0x00120009 */ if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) || (sb->version == 0x000A0004 && sb->platform == UBI_PS2) || (sb->version == 0x000A0007 && sb->platform == UBI_PS2 && !is_bia_ps2) || (sb->version == 0x000A0008 && sb->platform == UBI_PS2) || + (sb->version == 0x00100000 && sb->platform == UBI_PS2) || (sb->version == 0x00120009 && sb->platform == UBI_PS2)) { config_sb_entry(sb, 0x48, 0x6c); @@ -2399,9 +2459,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { if (sb->version == 0x000A0003 && sb->platform == UBI_XBOX) { config_sb_entry(sb, 0x64, 0x80); - config_sb_audio_fs(sb, 0x24, 0x28, 0x2c); - config_sb_audio_hs(sb, 0x52, 0x4c, 0x38, 0x40, 0x58, 0x54); /* stream_type may contain garbage */ + config_sb_audio_fs(sb, 0x24, 0x28, 0x34); + config_sb_audio_hs(sb, 0x52, 0x4c, 0x38, 0x40, 0x58, 0x54); sb->cfg.audio_has_internal_names = 1; + sb->cfg.default_codec_for_group0 = 1; config_sb_sequence(sb, 0x28, 0x14); @@ -2411,14 +2472,15 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { return 1; } - /* Prince of Persia: The Sands of Time (2003)(Xbox)-bank 0x000A0004 / 0x000A0002 (POP1 port) */ + /* Prince of Persia: The Sands of Time (2003)(Xbox)-bank 0x000A0004 / 0x000A0002 (POP1 port/Demo) */ if ((sb->version == 0x000A0002 && sb->platform == UBI_XBOX) || (sb->version == 0x000A0004 && sb->platform == UBI_XBOX)) { config_sb_entry(sb, 0x64, 0x78); config_sb_audio_fs(sb, 0x24, 0x28, 0x2c); - config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); /* stream_type may contain garbage */ + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); sb->cfg.audio_has_internal_names = 1; + sb->cfg.default_codec_for_group0 = 1; config_sb_sequence(sb, 0x28, 0x14); @@ -2452,8 +2514,9 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { config_sb_entry(sb, 0x64, 0x8c); config_sb_audio_fs(sb, 0x24, 0x28, 0x40); - config_sb_audio_hs(sb, 0x5e, 0x58, 0x44, 0x4c, 0x64, 0x60); /* stream_type may contain garbage */ + config_sb_audio_hs(sb, 0x5e, 0x58, 0x44, 0x4c, 0x64, 0x60); sb->cfg.audio_has_internal_names = 1; + sb->cfg.default_codec_for_group0 = 1; config_sb_sequence(sb, 0x28, 0x14); @@ -2474,8 +2537,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { return 1; } - /* Prince of Persia: Warrior Within (2004)(PC)-bank */ - if (sb->version == 0x00120009 && sb->platform == UBI_PC) { + /* Prince of Persia: Warrior Within Demo (2004)(PC)-bank 0x00120006 */ + /* Prince of Persia: Warrior Within (2004)(PC)-bank 0x00120009 */ + if ((sb->version == 0x00120006 && sb->platform == UBI_PC) || + (sb->version == 0x00120009 && sb->platform == UBI_PC)) { config_sb_entry(sb, 0x6c, 0x84); config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); @@ -2491,8 +2556,9 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { config_sb_entry(sb, 0x6c, 0x90); config_sb_audio_fs(sb, 0x24, 0x28, 0x40); - config_sb_audio_hs(sb, 0x60, 0x58, 0x44, 0x4c, 0x68, 0x64); /* stream_type may contain garbage */ + config_sb_audio_hs(sb, 0x60, 0x58, 0x44, 0x4c, 0x68, 0x64); sb->cfg.audio_has_internal_names = 1; + sb->cfg.default_codec_for_group0 = 1; config_sb_sequence(sb, 0x28, 0x14); return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c index bc351c801..a34c632ef 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -6,7 +6,7 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - size_t file_size, channel_size, interleave; + size_t file_size, channel_size, interleave, interleave_first = 0, interleave_first_skip = 0; meta_t meta_type; int channel_count = 0, loop_flag, sample_rate; uint32_t vag_id, version; @@ -80,7 +80,7 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { case 0x70474156: /* pGAV (little endian / stereo) [Jak 3 (PS2), Jak X (PS2)] */ meta_type = meta_PS2_pGAV; - start_offset = 0x00; //todo 0x30, requires interleave_first + start_offset = 0x30; if (read_32bitBE(0x20,streamFile) == 0x53746572) { /* "Ster" */ channel_count = 2; @@ -91,7 +91,8 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { interleave = 0x1000; /* Jak X interleave, includes header */ else interleave = 0x2000; /* Jak 3 interleave in rare files, no header */ - //todo interleave_first = interleave - start_offset; /* interleave includes header */ + interleave_first = interleave - start_offset; /* interleave includes header */ + interleave_first_skip = start_offset; } else { channel_count = 1; @@ -127,21 +128,21 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { } else if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */ /* The Simpsons Wrestling (PS1) */ - start_offset = 0x00; //todo 0x30, requires interleave_first + start_offset = 0x30; channel_count = 2; interleave = 0x6000; - //todo interleave_first = interleave - start_offset; /* includes header */ - channel_size += 0x30; + interleave_first = interleave - start_offset; /* includes header */ + interleave_first_skip = start_offset; loop_flag = 0; } else if (read_32bitBE(0x1000,streamFile) == 0x56414770) { /* "VAGp" */ /* Shikigami no Shiro (PS2) */ - start_offset = 0x00; //todo 0x30, requires interleave_first + start_offset = 0x30; channel_count = 2; interleave = 0x1000; - //todo interleave_first = interleave - start_offset; /* includes header */ - channel_size += 0x30; + interleave_first = interleave - start_offset; /* includes header */ + interleave_first_skip = start_offset; loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); } @@ -244,6 +245,9 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; vgmstream->interleave_block_size = interleave; + vgmstream->interleave_first_block_size = interleave_first; + vgmstream->interleave_first_skip = interleave_first_skip; + read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); /* always, can be null */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c b/Frameworks/vgmstream/vgmstream/src/meta/vawx.c index 8771cc6be..fceddac19 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vawx.c @@ -66,34 +66,28 @@ VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) { vgmstream->loop_start_sample = read_32bitBE(0x44,streamFile); vgmstream->loop_end_sample = read_32bitBE(0x48,streamFile); + //todo fix loops/samples vs ATRAC3 /* may be only applying end_skip to num_samples? */ xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); break; } case 7: { /* ATRAC3 */ - uint8_t buf[0x100]; - int32_t bytes, block_size, encoder_delay, joint_stereo, max_samples; + int block_align, encoder_delay; data_size = read_32bitBE(0x54,streamFile); - block_size = 0x98 * vgmstream->channels; - joint_stereo = 0; - max_samples = atrac3_bytes_to_samples(data_size, block_size); - encoder_delay = 0x0; //max_samples - vgmstream->num_samples; /* todo not correct */ - vgmstream->num_samples = max_samples; /* use calc samples since loop points are too, breaks looping in some files otherwise */ + block_align = 0x98 * vgmstream->channels; + encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; /* original samples break looping in some files otherwise */ - /* make a fake riff so FFmpeg can parse the ATRAC3 */ - bytes = ffmpeg_make_riff_atrac3(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->loop_start_sample = atrac3_bytes_to_samples(read_32bitBE(0x44,streamFile), block_size); - vgmstream->loop_end_sample = atrac3_bytes_to_samples(read_32bitBE(0x48,streamFile), block_size); - //vgmstream->loop_start_sample -= encoder_delay; - //vgmstream->loop_end_sample -= encoder_delay; - + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(read_32bitBE(0x44,streamFile), block_align); //- encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(read_32bitBE(0x48,streamFile), block_align) - encoder_delay; break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 462de2a95..f35718751 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -8,7 +8,7 @@ * * Some info: https://www.audiokinetic.com/en/library/edge/ */ -typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUS } wwise_codec; +typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, PTADPCM } wwise_codec; typedef struct { int big_endian; size_t file_size; @@ -72,9 +72,16 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { ww.file_size = streamFile->get_size(streamFile); #if 0 - /* sometimes uses a RIFF size that doesn't count chunks/sizes, has LE size in RIFX, or is just wrong...? */ - if (4+4+read_32bit(0x04,streamFile) != ww.file_size) { - VGM_LOG("WWISE: bad riff size (real=0x%x vs riff=0x%x)\n", 4+4+read_32bit(0x04,streamFile), ww.file_size); + /* Wwise's RIFF size is often wonky, seemingly depending on codec: + * - PCM, IMA/PTADPCM, VORBIS, AAC, OPUSNX/OPUS: correct + * - DSP, XWMA, ATRAC9: almost always slightly smaller (around 0x50) + * - HEVAG: very off + * - XMA2: exact file size + * - some RIFX have LE size + * (later we'll validate "data" which fortunately is correct) + */ + if (read_32bit(0x04,streamFile)+0x04+0x04 != ww.file_size) { + VGM_LOG("WWISE: bad riff size (real=0x%x vs riff=0x%x)\n", read_32bit(0x04,streamFile)+0x04+0x04, ww.file_size); goto fail; } #endif @@ -165,43 +172,54 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x0162: ww.codec = XWMA; break; /* WMAPro */ case 0x0165: ww.codec = XMA2; break; /* always with the "XMA2" chunk, Wwise doesn't use XMA1 */ case 0x0166: ww.codec = XMA2; break; - case 0x3039: ww.codec = OPUS; break; /* later renamed to "OPUSNX" */ - //case 0x3040: ww.codec = OPUS; break; /* same for other platforms, supposedly */ case 0xAAC0: ww.codec = AAC; break; case 0xFFF0: ww.codec = DSP; break; case 0xFFFB: ww.codec = HEVAG; break; case 0xFFFC: ww.codec = ATRAC9; break; case 0xFFFE: ww.codec = PCM; break; /* "PCM for Wwise Authoring" */ case 0xFFFF: ww.codec = VORBIS; break; + case 0x3039: ww.codec = OPUSNX; break; /* later renamed from "OPUS" */ +#if 0 + case 0x3040: ww.codec = OPUS; break; + case 0x8311: ww.codec = PTADPCM; break; +#endif default: goto fail; } - /* fix for newer Wwise DSP with coefs: Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS) */ - if (ww.format == 0x0002 && ww.extra_size == 0x0c + ww.channels * 0x2e) { - ww.codec = DSP; - } - else if (ww.format == 0x0002 && ww.block_align == 0x104 * ww.channels) { - //ww.codec = SWITCH_ADPCM; - /* unknown codec, found in Bayonetta 2 (Switch) - * frames of 0x104 per ch, possibly frame header is hist1(2)/hist2(2)/index(1) - * (may write 2 header samples + FF*2 nibbles = 0x200 samples per block?) - * index only goes up to ~0xb, may be a shift/scale value */ - goto fail; + /* identify system's ADPCM */ + if (ww.format == 0x0002) { + if (ww.extra_size == 0x0c + ww.channels * 0x2e) { + /* newer Wwise DSP with coefs [Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS)] */ + ww.codec = DSP; + } else if (ww.extra_size == 0x0a && find_chunk(streamFile, 0x57696948, first_offset,0, NULL,NULL, ww.big_endian, 0)) { /* WiiH */ + /* few older Wwise DSP with num_samples in extra_size [Tony Hawk: Shred (Wii)] */ + ww.codec = DSP; + } else if (ww.block_align == 0x104 * ww.channels) { + ww.codec = PTADPCM; /* Bayonetta 2 (Switch) */ + } } /* Some Wwise files (ex. Oddworld PSV, Bayonetta 2 WiiU, often in BGM.bnk) are truncated mirrors of another file. - * They come in RAM banks, probably to play the beginning while the rest of the real stream loads. + * They come in RAM banks, prefetch to play the beginning while the rest of the real stream loads. * We'll add basic support to avoid complaints of this or that .wem not playing */ - if (ww.data_size > ww.file_size) { + if (ww.data_offset + ww.data_size > ww.file_size) { //VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size); - if (ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2) /* only seen those, probably others exist */ - ww.truncated = 1; + + /* catch wrong rips as truncated tracks' file_size should be much smaller than data_size */ + if (ww.data_offset + ww.data_size - ww.file_size < 0x5000) { + VGM_LOG("WWISE: wrong expected data_size\n"); + goto fail; + } + + if (ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX) + ww.truncated = 1; /* only seen those, probably all exist */ else goto fail; } + start_offset = ww.data_offset; /* build the VGMSTREAM */ @@ -284,7 +302,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ break; - case 0x2a: /* uncommon (mid 2011) [inFamous 2 (PS3)] */ + case 0x2a: /* uncommon (mid 2011) [inFamous 2 (PS3), Captain America: Super Soldier (X360)] */ data_offsets = 0x10; block_offsets = 0x28; cfg.header_type = WWV_TYPE_2; @@ -306,6 +324,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } ww.data_size -= audio_offset; + + /* detect normal packets */ + if (vorb_size == 0x2a) { + /* almost all blocksizes are 0x08+0x0B except a few with 0x0a+0x0a [Captain America: Super Soldier (X360) voices/sfx] */ + if (cfg.blocksize_0_exp == cfg.blocksize_1_exp) + cfg.packet_type = WWV_STANDARD; + } + /* detect setup type: * - full inline: ~2009, ex. The King of Fighters XII (X360), The Saboteur (PC) * - trimmed inline: ~2010, ex. Army of Two: 40 days (X360) some multiplayer files @@ -359,11 +385,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { cfg.blocksize_0_exp = read_8bit(extra_offset + block_offsets + 0x01, streamFile); /* big */ ww.data_size -= audio_offset; - /* Normal packets are used rarely (ex. Oddworld New 'n' Tasty! (PSV)). They are hard to detect (decoding - * will mostly work with garbage results) but we'll try. Setup size and "fmt" bitrate fields may matter too. */ + /* detect normal packets */ if (ww.extra_size == 0x30) { - /* all blocksizes I've seen are 0x08+0x0B except Oddworld (PSV), that uses 0x09+0x09 - * (maybe lower spec machines = needs simpler packets) */ + /* almost all blocksizes are 0x08+0x0B except some with 0x09+0x09 [Oddworld New 'n' Tasty! (PSV)] */ if (cfg.blocksize_0_exp == cfg.blocksize_1_exp) cfg.packet_type = WWV_STANDARD; } @@ -419,6 +443,15 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { goto fail; } + /* for some reason all(?) DSP .wem do full loops (even mono/jingles/etc) but + * several tracks do loop like this, so disable it for short-ish tracks */ + if (ww.loop_flag && vgmstream->loop_start_sample == 0 && + vgmstream->loop_end_sample < 20*ww.sample_rate) { /* in seconds */ + vgmstream->loop_flag = 0; + } + + + /* get coefs and default history */ dsp_read_coefs(vgmstream,streamFile,wiih_offset, 0x2e, ww.big_endian); for (i=0; i < ww.channels; i++) { @@ -526,7 +559,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { break; } - case OPUS: { /* Switch */ + case OPUSNX: { /* Switch */ size_t skip; /* values up to 0x14 seem fixed and similar to HEVAG's (block_align 0x02/04, bits_per_sample 0x10) */ @@ -546,12 +579,38 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { skip = switch_opus_get_encoder_delay(start_offset, streamFile); /* should be 120 */ + /* OPUS is VBR so this is very approximate percent, meh */ + if (ww.truncated) { + vgmstream->num_samples = (int32_t)(vgmstream->num_samples * + (double)(ww.file_size - start_offset) / (double)ww.data_size); + ww.data_size = ww.file_size - start_offset; + } + vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,ww.data_size, vgmstream->channels, skip, vgmstream->sample_rate); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } + +#if 0 + case OPUS: { /* PC/etc */ + ffmpeg_codec_data * ffmpeg_data = NULL; + if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; + + /* extra: size 0x12, unknown values, maybe num_samples/etc */ + + ffmpeg_data = init_ffmpeg_offset(streamFile, ww.data_offset,ww.data_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; + break; + } +#endif + #endif case HEVAG: /* PSV */ /* changed values, another bizarre Wwise quirk */ @@ -591,12 +650,24 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { break; } #endif + case PTADPCM: /* substitutes IMA as default ADPCM codec */ + if (ww.bits_per_sample != 4) goto fail; + if (ww.block_align != 0x24 * ww.channels && ww.block_align != 0x104 * ww.channels) goto fail; + + vgmstream->coding_type = coding_PTADPCM; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = ww.block_align / ww.channels; + //vgmstream->codec_endian = ww.big_endian; //? + + vgmstream->num_samples = ptadpcm_bytes_to_samples(ww.data_size, ww.channels, vgmstream->interleave_block_size); + break; default: goto fail; } + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wii_04sw.c b/Frameworks/vgmstream/vgmstream/src/meta/xa_04sw.c similarity index 92% rename from Frameworks/vgmstream/vgmstream/src/meta/wii_04sw.c rename to Frameworks/vgmstream/vgmstream/src/meta/xa_04sw.c index e1559ca98..3db1a6923 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wii_04sw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xa_04sw.c @@ -2,7 +2,7 @@ #include "../coding/coding.h" /* 04SW - found in Driver: Parallel Lines (Wii) */ -VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_xa_04sw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count; @@ -44,7 +44,7 @@ VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE *streamFile) { dsp_read_coefs_be(vgmstream,streamFile,0x20, 0x60); /* the initial history offset seems different thatn standard DSP and possibly always zero */ - vgmstream->meta_type = meta_WII_04SW; + vgmstream->meta_type = meta_XA_04SW; /* the rest of the header has unknown values (several repeats) and the filename */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_xa30.c b/Frameworks/vgmstream/vgmstream/src/meta/xa_xa30.c similarity index 93% rename from Frameworks/vgmstream/vgmstream/src/meta/pc_xa30.c rename to Frameworks/vgmstream/vgmstream/src/meta/xa_xa30.c index 7b615bcd2..e6f390f79 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_xa30.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xa_xa30.c @@ -2,7 +2,7 @@ #include "../coding/coding.h" /* XA30 - found in Reflections games [Driver: Parallel Lines (PC), Driver 3 (PC)] */ -VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count, codec; @@ -41,7 +41,7 @@ VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) { /* 0x20: always IMA=00016000, PCM=00056000 PCM?, rest of the header is null */ vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; - vgmstream->meta_type = meta_PC_XA30; + vgmstream->meta_type = meta_XA_XA30; switch(codec) { case 0x00: /* PCM (rare, seen in Driver 3) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xavs.c b/Frameworks/vgmstream/vgmstream/src/meta/xavs.c new file mode 100644 index 000000000..6e4643264 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xavs.c @@ -0,0 +1,87 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" +#include "xavs_streamfile.h" + +/* XAVS - Reflections audio and video+audio container [Stuntman (PS2)] */ +VGMSTREAM * init_vgmstream_xavs(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + int total_subsongs, target_subsong = streamFile->stream_index; + STREAMFILE *temp_streamFile = NULL; + + + /* checks */ + if (!check_extensions(streamFile, "xav")) + goto fail; + if (read_32bitBE(0x00, streamFile) != 0x58415653) /* "XAVS" */ + goto fail; + + loop_flag = 0; + channel_count = 2; + start_offset = 0x00; + + /* 0x04: 16b width + height (0 if file has no video) */ + /* 0x08: related to video (0 if file has no video) */ + total_subsongs = read_16bitLE(0x0c, streamFile); + /* 0x0c: volume? (0x50, 0x4e) */ + /* 0x10: biggest video chunk? (0 if file has no video) */ + /* 0x14: biggest audio chunk? */ + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + + /* could use a blocked layout, but this needs interleaved PCM within blocks which can't be done ATM */ + temp_streamFile = setup_xavs_streamfile(streamFile, 0x18, target_subsong - 1); + if (!temp_streamFile) goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XAVS; + vgmstream->num_streams = total_subsongs; + + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + + /* no apparent flags, most videos use 0x41 but not all */ + { + off_t offset = 0x18; + while (offset < get_streamfile_size(streamFile)) { + uint32_t chunk_id = read_32bitLE(offset+0x00, streamFile) & 0xFF; + uint32_t chunk_size = read_32bitLE(offset+0x00, streamFile) >> 8; + + if ((chunk_id & 0xF0) == 0x40) { + vgmstream->sample_rate = 48000; + vgmstream->interleave_block_size = 0x200; + break; + } else if ((chunk_id & 0xF0) == 0x60) { + vgmstream->sample_rate = 24000; + vgmstream->interleave_block_size = 0x100; + break; + } else if (chunk_id == 0x56) { + offset += 0x04 + chunk_size; + } else if (chunk_id == 0x21) { + offset += 0x04; + } else { + goto fail; + } + } + } + + vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(temp_streamFile), channel_count, 16); + + if (!vgmstream_open_stream(vgmstream,temp_streamFile,start_offset)) + goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xavs_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/xavs_streamfile.h new file mode 100644 index 000000000..ac5e7d3b3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xavs_streamfile.h @@ -0,0 +1,160 @@ +#ifndef _XAVS_STREAMFILE_H_ +#define _XAVS_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + off_t stream_offset; + size_t stream_size; + int stream_number; + + /* 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 */ + + size_t logical_size; +} xavs_io_data; + + +static size_t xavs_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xavs_io_data* data) { + size_t total_read = 0; + + + /* re-start when previous offset (can't map logical<>physical offsets) */ + if (data->logical_offset < 0 || offset < data->logical_offset) { + data->physical_offset = data->stream_offset; + data->logical_offset = 0x00; + data->data_size = 0; + } + + /* read blocks */ + while (length > 0) { + + /* ignore EOF */ + if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) { + break; + } + + /* process new block */ + if (data->data_size == 0) { + uint32_t chunk_id = read_32bitLE(data->physical_offset+0x00, streamfile) & 0xFF; + uint32_t chunk_size = read_32bitLE(data->physical_offset+0x00, streamfile) >> 8; + + data->skip_size = 0x04; + + switch(chunk_id) { + /* audio */ + case 0x41: + case 0x61: + case 0x62: + case 0x63: + data->block_size = 0x04 + chunk_size; + if (data->stream_number + 1 == (chunk_id & 0x0F)) { + data->data_size = chunk_size; + } else { + data->data_size = 0; /* ignore other subsongs */ + } + break; + + /* video */ + case 0x56: + data->block_size = 0x04 + chunk_size; + data->data_size = 0; + break; + + /* empty */ + case 0x21: /* related to video */ + case 0x5F: /* "_EOS" */ + data->block_size = 0x04; + data->data_size = 0; + break; + + default: + VGM_LOG("XAVS: unknown type at %lx\n", data->physical_offset); + data->block_size = 0x04; + data->data_size = 0; + break; + } + } + + /* move to next block */ + if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_size = 0; + continue; + } + + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_offset; + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + } + + return total_read; +} + +static size_t xavs_io_size(STREAMFILE *streamfile, xavs_io_data* data) { + uint8_t buf[1]; + + if (data->logical_size) + return data->logical_size; + + /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ + xavs_io_read(streamfile, buf, 0x7FFFFFFF, 1, data); + data->logical_size = data->logical_offset; + + return data->logical_size; +} + +/* Handles deinterleaving of XAVS blocked streams */ +static STREAMFILE* setup_xavs_streamfile(STREAMFILE *streamFile, off_t stream_offset, int stream_number) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + xavs_io_data io_data = {0}; + size_t io_data_size = sizeof(xavs_io_data); + + io_data.stream_offset = stream_offset; + io_data.stream_size = get_streamfile_size(streamFile) - stream_offset; + io_data.stream_number = stream_number; + io_data.logical_offset = -1; /* force phys offset reset */ + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, xavs_io_read,xavs_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _XAVS_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xbox_xmu.c b/Frameworks/vgmstream/vgmstream/src/meta/xmu.c similarity index 87% rename from Frameworks/vgmstream/vgmstream/src/meta/xbox_xmu.c rename to Frameworks/vgmstream/vgmstream/src/meta/xmu.c index 63f5faea5..690416c94 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xbox_xmu.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xmu.c @@ -1,8 +1,8 @@ #include "meta.h" #include "../coding/coding.h" -/* XMU- found in Alter Echo (Xbox) */ -VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) { +/* XMU - found in Alter Echo (Xbox) */ +VGMSTREAM * init_vgmstream_xmu(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; size_t start_offset; int loop_flag, channel_count; @@ -25,6 +25,7 @@ VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_XMU; vgmstream->sample_rate = read_32bitLE(0x10,streamFile); vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); vgmstream->loop_start_sample = 0; @@ -32,7 +33,6 @@ VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) { vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_XBOX_XMU; if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c b/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c new file mode 100644 index 000000000..6bc81a28b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c @@ -0,0 +1,118 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .360.WAV, .PS3.WAV - from Valve games running on Source Engine */ +/* [The Orange Box (X360), Portal 2 (PS3/X360), Counter-Strike: Global Offensive (PS3/X360)] */ +VGMSTREAM* init_vgmstream_xmv_valve(STREAMFILE* streamFile) { + VGMSTREAM* vgmstream = NULL; + int32_t loop_start; + uint32_t start_offset, data_size, sample_rate, num_samples; + uint16_t /*loop_block, loop_start_skip,*/ loop_end_skip; + uint8_t format, freq_mode, channels; + int loop_flag; + + /* checks */ + if (!check_extensions(streamFile, "wav,lwav")) + goto fail; + + /* check header magic */ + if (read_32bitBE(0x00, streamFile) != 0x58575620) /* "XMV " */ + goto fail; + + /* only version 4 is known */ + if (read_32bitBE(0x04, streamFile) != 0x04) + goto fail; + + start_offset = read_32bitBE(0x10, streamFile); + data_size = read_32bitBE(0x14, streamFile); + num_samples = read_32bitBE(0x18, streamFile); + loop_start = read_32bitBE(0x1c, streamFile); + + /* XMA only */ + //loop_block = read_16bitBE(0x20, streamFile); + //loop_start_skip = read_16bitBE(0x22, streamFile); + loop_end_skip = read_16bitBE(0x24, streamFile); + + format = read_8bit(0x28, streamFile); + freq_mode = read_8bit(0x2a, streamFile); + channels = read_8bit(0x2b, streamFile); + + switch (freq_mode) { + case 0x00: sample_rate = 11025; break; + case 0x01: sample_rate = 22050; break; + case 0x02: sample_rate = 44100; break; + default: goto fail; + } + + loop_flag = (loop_start != -1); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XMV_VALVE; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = num_samples; /* always loops from the end */ + + switch (format) { + case 0x00: /* PCM */ + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; +#ifdef VGM_USE_FFMPEG + case 0x01: { /* XMA */ + ffmpeg_codec_data* ffmpeg_data; + uint8_t buf[0x100]; + int block_count, block_size; + size_t bytes; + + block_size = 0x800; + block_count = data_size / block_size; + + bytes = ffmpeg_make_riff_xma2(buf, 0x100, num_samples, data_size, channels, sample_rate, block_count, block_size); + + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, data_size); + if (!ffmpeg_data) goto fail; + + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + vgmstream->loop_end_sample -= loop_end_skip; + + xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, 0, 1, 1); + break; + } +#endif +#ifdef VGM_USE_MPEG + case 0x03: { /* MP3 */ + mpeg_codec_data *mpeg_data; + coding_t mpeg_coding; + + mpeg_data = init_mpeg(streamFile, start_offset, &mpeg_coding, channels); + if (!mpeg_data) goto fail; + + vgmstream->codec_data = mpeg_data; + vgmstream->coding_type = mpeg_coding; + vgmstream->layout_type = layout_none; + + /* strangely, number of samples is stored incorrectly for MP3, there's PCM size in this field instead */ + vgmstream->num_samples = pcm_bytes_to_samples(num_samples, channels, 16); + break; + } +#endif + case 0x02: /* ADPCM (not actually implemented, was probably supposed to be Microsoft ADPCM) */ + default: + goto fail; + } + + 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/xnb.c b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c index 384a17f83..961c25c75 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xnb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c @@ -18,7 +18,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { goto fail; /* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360 - * MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P': PS4, etc */ + * MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */ platform = read_8bit(0x03,streamFile); big_endian = (platform == 'x'); @@ -58,7 +58,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { if ( strcmp(reader_name, type_sound) != 0 ) goto fail; current_offset += reader_string_len + 1; - current_offset += 4; /* reader version */ + current_offset += 0x04; /* reader version */ /* shared resource count */ if (read_8bit(current_offset++, streamFile) != 1) @@ -66,13 +66,13 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { /* shared resource: partial "fmt" chunk */ fmt_chunk_size = read_32bitLE(current_offset, streamFile); - current_offset += 4; + current_offset += 0x04; { int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; - codec = read_16bit(current_offset+0x00, streamFile); + codec = (uint16_t)read_16bit(current_offset+0x00, streamFile); channel_count = read_16bit(current_offset+0x02, streamFile); sample_rate = read_32bit(current_offset+0x04, streamFile); /* 0x08: byte rate */ @@ -83,12 +83,17 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { xma2_parse_fmt_chunk_extra(streamFile, current_offset, &loop_flag, &num_samples, &loop_start, &loop_end, big_endian); xma_chunk_offset = current_offset; } + + if (codec == 0xFFFF) { + if (platform != 'S') goto fail; + sample_rate = read_32bit(current_offset+fmt_chunk_size+0x04+0x08, streamFile); + } } current_offset += fmt_chunk_size; data_size = read_32bitLE(current_offset, streamFile); - current_offset += 4; + current_offset += 0x04; start_offset = current_offset; } @@ -107,7 +112,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { if (!block_align) block_align = (bps == 8 ? 0x01 : 0x02) * channel_count; - vgmstream->coding_type = bps == 8 ? coding_PCM8_U_int : coding_PCM16LE; + vgmstream->coding_type = bps == 8 ? coding_PCM8_U : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = block_align / channel_count; vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps); @@ -151,6 +156,16 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { break; } #endif + case 0xFFFF: /* Eagle Island (Switch) */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = data_size / channel_count; + vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile); + //vgmstream->num_samples = dsp_bytes_to_samples(data_size - 0x60*channel_count, channel_count); + + dsp_read_coefs(vgmstream,streamFile,start_offset + 0x1c,vgmstream->interleave_block_size,big_endian); + dsp_read_hist (vgmstream,streamFile,start_offset + 0x3c,vgmstream->interleave_block_size,big_endian); + break; default: VGM_LOG("XNB: unknown codec 0x%x\n", codec); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c index 1fd658502..4cf0247f9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c @@ -307,15 +307,14 @@ static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_head goto fail; } - if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) { + if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) goto fail; - } + close_streamfile(temp_streamFile); } /* setup layered VGMSTREAMs */ if (!setup_layout_layered(data)) goto fail; - close_streamfile(temp_streamFile); return data; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xbox_xvas.c b/Frameworks/vgmstream/vgmstream/src/meta/xvas.c similarity index 91% rename from Frameworks/vgmstream/vgmstream/src/meta/xbox_xvas.c rename to Frameworks/vgmstream/vgmstream/src/meta/xvas.c index acd09c8b3..5aad6b280 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xbox_xvas.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xvas.c @@ -3,7 +3,7 @@ #include "../coding/coding.h" /* XVAS - found in TMNT 2 & TMNT 3 (Xbox) */ -VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_xvas(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count; @@ -28,7 +28,7 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->meta_type = meta_XBOX_XVAS; + vgmstream->meta_type = meta_XVAS; vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); if(loop_flag) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 84a26ce87..faa9237aa 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -326,8 +326,11 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { } else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 - && xwb.data_size == 0x55951c1c) { /* some kind of id? */ - /* Stardew Valley (Switch), full interleaved DSPs (including headers) */ + && read_32bitLE(xwb.stream_offset + 0x08, streamFile) == xwb.sample_rate /* DSP header */ + && read_16bitLE(xwb.stream_offset + 0x0e, streamFile) == 0 + && read_32bitLE(xwb.stream_offset + 0x18, streamFile) == 2 + /*&& xwb.data_size == 0x55951c1c*/) { /* some kind of id in Stardew Valley? */ + /* Stardew Valley (Switch), Skulls of the Shogun (Switch): full interleaved DSPs (including headers) */ xwb.codec = DSP; } else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 @@ -342,7 +345,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start) && !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP); - /* Oddworld OGG the data_size value is size of uncompressed bytes instead; DSP uses some id/config as value */ + /* Oddworld OGG the data_size value is size of uncompressed bytes instead; DSP uses some id/config as value */ if (xwb.codec != OGG && xwb.codec != DSP && xwb.codec != ATRAC9_RIFF) { /* some low-q rips don't remove padding, relax validation a bit */ if (xwb.data_offset + xwb.stream_size > get_streamfile_size(streamFile)) @@ -525,16 +528,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { } case ATRAC3: { /* Techland PS3 extension [Sniper Ghost Warrior (PS3)] */ - uint8_t buf[0x100]; - int bytes; + int block_align, encoder_delay; - int block_size = xwb.block_align * vgmstream->channels; - int joint_stereo = xwb.block_align == 0x60; /* untested, ATRAC3 default */ - int skip_samples = 0; /* unknown */ + block_align = xwb.block_align * vgmstream->channels; + encoder_delay = 1024; /* assumed */ + vgmstream->num_samples -= encoder_delay; - bytes = ffmpeg_make_riff_atrac3(buf,0x100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, skip_samples); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !vgmstream->codec_data ) goto fail; + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, xwb.stream_offset,xwb.stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; @@ -632,10 +633,11 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head } //;VGM_LOG("XSB: name found=%i at %lx\n", xsb.parse_found, xsb.name_offset); - if (!xsb.parse_found || xsb.name_offset == 0) + if (!xsb.name_len || xsb.name[0] == '\0') goto fail; - read_string(buf,maxsize, xsb.name_offset,streamFile); /* null-terminated */ + strncpy(buf,xsb.name,maxsize); + buf[maxsize-1] = '\0'; return 1; fail: return 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h index f42b24dc2..b254a7fe6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h @@ -1,5 +1,6 @@ #ifndef _XWB_XSB_H_ #define _XWB_XSB_H_ +#include "meta.h" #define XSB_XACT1_0_MAX 5 /* Unreal Championship (Xbox) */ #define XSB_XACT1_1_MAX 8 /* Die Hard: Vendetta (Xbox) */ @@ -30,9 +31,9 @@ typedef struct { off_t cue_names_offset; /* output */ - int parse_found; int parse_done; - off_t name_offset; + char name[STREAM_NAME_SIZE]; + int name_len; } xsb_header; @@ -41,24 +42,30 @@ static void xsb_check_stream(xsb_header * xsb, int stream_index, int wavebank_in if (xsb->parse_done) return; - /* multiple names may correspond to a stream, so commenting parse_done - * will allow to search for other names instead of first only */ + /* multiple names may correspond to a stream (ex. Blue Dragon), so we concat all */ if (xsb->selected_stream == stream_index && (xsb->selected_wavebank == wavebank_index || wavebank_index == -1 || wavebank_index == 255)) { - xsb->name_offset = name_offset; - xsb->parse_found = 1; - xsb->parse_done = 1; + char name[STREAM_NAME_SIZE]; + size_t name_size; + + name_size = read_string(name,sizeof(name), name_offset,sf); /* null-terminated */ + + if (xsb->name_len) { + const char *cat = "; "; + int cat_len = 2; + + if (xsb->name_len + cat_len + name_size + 1 < STREAM_NAME_SIZE) { + strcat(xsb->name + xsb->name_len, cat); + strcat(xsb->name + xsb->name_len, name); + } + } + else { + strcpy(xsb->name, name); + } + xsb->name_len += name_size; + //xsb->parse_done = 1; /* uncomment this to stop reading after first name */ //;VGM_LOG("XSB: parse found stream=%i, wavebank=%i, name_offset=%lx\n", stream_index, wavebank_index, name_offset); } - -#if 0 // for debugging purposes - { - char stream_name[255]; - read_string(stream_name,255, name_offset,sf); /* null-terminated */ - ;VGM_LOG("XSB: stream=%i, wavebank=%i, name=%lx=%s vs s=%i, w=%i\n", stream_index, wavebank_index, name_offset, stream_name, xsb->selected_stream, xsb->selected_wavebank); - xsb->parse_done = 0; /* keep parsing */ - } -#endif } /* old XACT1 is a bit different and much of it is unknown but this seems to work. diff --git a/Frameworks/vgmstream/vgmstream/src/mixing.c b/Frameworks/vgmstream/vgmstream/src/mixing.c index 81ceffbcc..ab2b820b0 100644 --- a/Frameworks/vgmstream/vgmstream/src/mixing.c +++ b/Frameworks/vgmstream/vgmstream/src/mixing.c @@ -48,7 +48,8 @@ * (and maybe improve memory cache?), though maybe it should call one function per operation. */ -#define VGMSTREAM_MAX_MIXING 128 +#define VGMSTREAM_MAX_MIXING 512 +#define MIXING_PI 3.14159265358979323846f /* mixing info */ @@ -98,15 +99,15 @@ static int is_active(mixing_data *data, int32_t current_start, int32_t current_e int32_t fade_start, fade_end; for (i = 0; i < data->mixing_count; i++) { - mix_command_data mix = data->mixing_chain[i]; + mix_command_data *mix = &data->mixing_chain[i]; - if (mix.command != MIX_FADE) + if (mix->command != MIX_FADE) return 1; /* has non-fades = active */ /* check is current range falls within a fade * (assuming fades were already optimized on add) */ - fade_start = mix.time_pre < 0 ? 0 : mix.time_pre; - fade_end = mix.time_post < 0 ? INT_MAX : mix.time_post; + fade_start = mix->time_pre < 0 ? 0 : mix->time_pre; + fade_end = mix->time_post < 0 ? INT_MAX : mix->time_post; if (current_start < fade_end && current_end > fade_start) return 1; @@ -115,24 +116,72 @@ static int is_active(mixing_data *data, int32_t current_start, int32_t current_e return 0; } -static int32_t get_current_pos(VGMSTREAM* vgmstream) { +static int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) { int32_t current_pos; - if (vgmstream->loop_flag && vgmstream->current_sample > vgmstream->loop_start_sample) { - int loop_pre = vgmstream->loop_start_sample; - int loop_into = vgmstream->current_sample - vgmstream->loop_start_sample; - int loop_samples = vgmstream->loop_end_sample - vgmstream->loop_start_sample; - current_pos = loop_pre + loop_into + loop_samples*vgmstream->loop_count; + if (vgmstream->loop_flag && vgmstream->loop_count > 0) { + int loop_pre = vgmstream->loop_start_sample; /* samples before looping */ + int loop_into = (vgmstream->current_sample - vgmstream->loop_start_sample); /* samples after loop */ + int loop_samples = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); /* looped section */ + + current_pos = loop_pre + (loop_samples * vgmstream->loop_count) + loop_into - sample_count; } else { - current_pos = vgmstream->current_sample; + current_pos = (vgmstream->current_sample - sample_count); } return current_pos; } +static float get_fade_gain_curve(char shape, float index) { + float gain; + + /* don't bother doing calcs near 0.0/1.0 */ + if (index <= 0.0001f || index >= 0.9999f) { + return index; + } + + //todo optimizations: interleave calcs, maybe use cosf, powf, etc? (with extra defines) + + /* (curve math mostly from SoX/FFmpeg) */ + switch(shape) { + /* 2.5f in L/E 'pow' is the attenuation factor, where 5.0 (100db) is common but a bit fast + * (alt calculations with 'exp' from FFmpeg use (factor)*ln(0.1) = -NN.N... */ + + case 'E': /* exponential (for fade-outs, closer to natural decay of sound) */ + //gain = pow(0.1f, (1.0f - index) * 2.5f); + gain = exp(-5.75646273248511f * (1.0f - index)); + break; + case 'L': /* logarithmic (inverse of the above, maybe for crossfades) */ + //gain = 1 - pow(0.1f, (index) * 2.5f); + gain = 1 - exp(-5.75646273248511f * (index)); + break; + + case 'H': /* raised sine wave or cosine wave (for more musical crossfades) */ + gain = (1.0f - cos(index * MIXING_PI)) / 2.0f; + break; + + case 'Q': /* quarter of sine wave (for musical fades) */ + gain = sin(index * MIXING_PI / 2.0f); + break; + + case 'p': /* parabola (maybe for crossfades) */ + gain = 1.0f - sqrt(1.0f - index); + break; + case 'P': /* inverted parabola (maybe for fades) */ + gain = (1.0f - (1.0f - index) * (1.0f - index)); + break; + + case 'T': /* triangular/linear (simpler/sharper fades) */ + default: + gain = index; + break; + } + + return gain; +} + static int get_fade_gain(mix_command_data *mix, float *out_cur_vol, int32_t current_subpos) { - //todo optimizations: interleave calcs, maybe use cosf, powf, etc? float cur_vol = 0.0f; if ((current_subpos >= mix->time_pre || mix->time_pre < 0) && current_subpos < mix->time_start) { @@ -173,41 +222,7 @@ static int get_fade_gain(mix_command_data *mix, float *out_cur_vol, int32_t curr * curves are complementary (exponential fade-in ~= logarithmic fade-out); the following * are described taking fade-in = normal. */ - - /* (curve math mostly from SoX/FFmpeg) */ - switch(mix->shape) { - /* 2.5f in L/E 'pow' is the attenuation factor, where 5.0 (100db) is common but a bit fast - * (alt calculations with 'exp' from FFmpeg use (factor)*ln(0.1) = -NN.N... */ - - case 'E': /* exponential (for fade-outs, closer to natural decay of sound) */ - //gain = pow(0.1f, (1.0f - index) * 2.5f); - gain = exp(-5.75646273248511f * (1.0f - index)); - break; - case 'L': /* logarithmic (inverse of the above, maybe for crossfades) */ - //gain = 1 - pow(0.1f, (index) * 2.5f); - gain = 1 - exp(-5.75646273248511f * (index)); - break; - - case 'H': /* raised sine wave or cosine wave (for more musical crossfades) */ - gain = (1.0f - cos(index * M_PI )) / 2.0f; - break; - - case 'Q': /* quarter of sine wave (for musical fades) */ - gain = sin(index * M_PI / 2.0f); - break; - - case 'p': /* parabola (maybe for crossfades) */ - gain = 1.0f - sqrt(1.0f - index); - break; - case 'P': /* inverted parabola (maybe for fades) */ - gain = (1.0f - (1.0f - index) * (1.0f - index)); - break; - - case 'T': /* triangular/linear (simpler/sharper fades) */ - default: - gain = index; - break; - } + gain = get_fade_gain_curve(mix->shape, index); if (mix->vol_start < mix->vol_end) { /* fade in */ cur_vol = mix->vol_start + range_vol * gain; @@ -216,6 +231,7 @@ static int get_fade_gain(mix_command_data *mix, float *out_cur_vol, int32_t curr } } else { + /* fade is outside reach */ goto fail; } @@ -230,20 +246,19 @@ void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) int ch, s, m, ok; int32_t current_pos, current_subpos; - float temp_f, temp_min, temp_max, cur_vol; + float temp_f, temp_min, temp_max, cur_vol = 0.0f; float *temp_mixbuf; sample_t *temp_outbuf; const float limiter_max = 32767.0f; const float limiter_min = -32768.0f; - /* no support or not need to apply */ if (!data || !data->mixing_on || data->mixing_count == 0) return; /* try to skip if no ops apply (for example if fade set but does nothing yet) */ - current_pos = get_current_pos(vgmstream); + current_pos = get_current_pos(vgmstream, sample_count); if (!is_active(data, current_pos, current_pos + sample_count)) return; @@ -252,6 +267,8 @@ void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) temp_mixbuf = data->mixbuf; temp_outbuf = outbuf; + current_subpos = current_pos; + /* apply mixes in order per channel */ for (s = 0; s < sample_count; s++) { /* reset after new sample 'step'*/ @@ -263,7 +280,7 @@ void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) } for (m = 0; m < data->mixing_count; m++) { - mix_command_data mix = data->mixing_chain[m]; + mix_command_data *mix = &data->mixing_chain[m]; /* mixing ops are designed to apply in order, all channels per 1 sample 'step'. Since some ops change * total channels, channel number meaning varies as ops move them around, ex: @@ -274,34 +291,34 @@ void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) * - 2ch w/ "1-2,1d" = ch1<>ch2, ch1(drop and move ch2(old ch1) to ch1) = ch1 * - 2ch w/ "1d,1-2" = ch1(drop and pull rest), ch1(do nothing, ch2 doesn't exist now) = ch2 */ - switch(mix.command) { + switch(mix->command) { case MIX_SWAP: - temp_f = stpbuf[mix.ch_dst]; - stpbuf[mix.ch_dst] = stpbuf[mix.ch_src]; - stpbuf[mix.ch_src] = temp_f; + temp_f = stpbuf[mix->ch_dst]; + stpbuf[mix->ch_dst] = stpbuf[mix->ch_src]; + stpbuf[mix->ch_src] = temp_f; break; case MIX_ADD: - stpbuf[mix.ch_dst] = stpbuf[mix.ch_dst] + stpbuf[mix.ch_src] * mix.vol; + stpbuf[mix->ch_dst] = stpbuf[mix->ch_dst] + stpbuf[mix->ch_src] * mix->vol; break; case MIX_VOLUME: - if (mix.ch_dst < 0) { + if (mix->ch_dst < 0) { for (ch = 0; ch < step_channels; ch++) { - stpbuf[ch] = stpbuf[ch] * mix.vol; + stpbuf[ch] = stpbuf[ch] * mix->vol; } } else { - stpbuf[mix.ch_dst] = stpbuf[mix.ch_dst] * mix.vol; + stpbuf[mix->ch_dst] = stpbuf[mix->ch_dst] * mix->vol; } break; case MIX_LIMIT: - temp_max = limiter_max * mix.vol; - temp_min = limiter_min * mix.vol; + temp_max = limiter_max * mix->vol; + temp_min = limiter_min * mix->vol; - if (mix.ch_dst < 0) { + if (mix->ch_dst < 0) { for (ch = 0; ch < step_channels; ch++) { if (stpbuf[ch] > temp_max) stpbuf[ch] = temp_max; @@ -310,47 +327,45 @@ void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) } } else { - if (stpbuf[mix.ch_dst] > temp_max) - stpbuf[mix.ch_dst] = temp_max; - else if (stpbuf[mix.ch_dst] < temp_min) - stpbuf[mix.ch_dst] = temp_min; + if (stpbuf[mix->ch_dst] > temp_max) + stpbuf[mix->ch_dst] = temp_max; + else if (stpbuf[mix->ch_dst] < temp_min) + stpbuf[mix->ch_dst] = temp_min; } break; case MIX_UPMIX: step_channels += 1; - for (ch = step_channels - 1; ch > mix.ch_dst; ch--) { + for (ch = step_channels - 1; ch > mix->ch_dst; ch--) { stpbuf[ch] = stpbuf[ch-1]; /* 'push' channels forward (or pull backwards) */ } - stpbuf[mix.ch_dst] = 0; /* inserted as silent */ + stpbuf[mix->ch_dst] = 0; /* inserted as silent */ break; case MIX_DOWNMIX: step_channels -= 1; - for (ch = mix.ch_dst; ch < step_channels; ch++) { + for (ch = mix->ch_dst; ch < step_channels; ch++) { stpbuf[ch] = stpbuf[ch+1]; /* 'pull' channels back */ } break; case MIX_KILLMIX: - step_channels = mix.ch_dst; /* clamp channels */ + step_channels = mix->ch_dst; /* clamp channels */ break; case MIX_FADE: - current_subpos = current_pos + s; - - ok = get_fade_gain(&mix, &cur_vol, current_subpos); + ok = get_fade_gain(mix, &cur_vol, current_subpos); if (!ok) { - break; + break; /* fade doesn't apply right now */ } - if (mix.ch_dst < 0) { + if (mix->ch_dst < 0) { for (ch = 0; ch < step_channels; ch++) { stpbuf[ch] = stpbuf[ch] * cur_vol; } } else { - stpbuf[mix.ch_dst] = stpbuf[mix.ch_dst] * cur_vol; + stpbuf[mix->ch_dst] = stpbuf[mix->ch_dst] * cur_vol; } break; @@ -359,6 +374,8 @@ void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) } } + current_subpos++; + temp_mixbuf += step_channels; temp_outbuf += vgmstream->channels; } @@ -925,6 +942,118 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) { mixing_push_killmix(vgmstream, max); } + +typedef enum { + pos_FL = 0, + pos_FR = 1, + pos_FC = 2, + pos_LFE = 3, + pos_BL = 4, + pos_BR = 5, + pos_FLC = 6, + pos_FRC = 7, + pos_BC = 8, + pos_SL = 9, + pos_SR = 10, +} mixing_position_t; + +void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/) { + mixing_data *data = vgmstream->mixing_data; + int ch, output_channels, mp_in, mp_out, ch_in, ch_out; + mapping_t input_mapping, output_mapping; + const double vol_max = 1.0; + const double vol_sqrt = 1 / sqrt(2); + const double vol_half = 1 / 2; + double matrix[16][16] = {{0}}; + + + if (!data) + return; + if (max <= 1 || data->output_channels <= max || max >= 8) + return; + + /* assume WAV defaults if not set */ + input_mapping = vgmstream->channel_layout; + if (input_mapping == 0) { + switch(data->output_channels) { + case 1: input_mapping = mapping_MONO; break; + case 2: input_mapping = mapping_STEREO; break; + case 3: input_mapping = mapping_2POINT1; break; + case 4: input_mapping = mapping_QUAD; break; + case 5: input_mapping = mapping_5POINT0; break; + case 6: input_mapping = mapping_5POINT1; break; + case 7: input_mapping = mapping_7POINT0; break; + case 8: input_mapping = mapping_7POINT1; break; + default: return; + } + } + + /* build mapping matrix[input channel][output channel] = volume, + * using standard WAV/AC3 downmix formulas + * - https://www.audiokinetic.com/library/edge/?source=Help&id=downmix_tables + * - https://www.audiokinetic.com/library/edge/?source=Help&id=standard_configurations + */ + switch(max) { + case 1: + output_mapping = mapping_MONO; + matrix[pos_FL][pos_FC] = vol_sqrt; + matrix[pos_FR][pos_FC] = vol_sqrt; + matrix[pos_FC][pos_FC] = vol_max; + matrix[pos_SL][pos_FC] = vol_half; + matrix[pos_SR][pos_FC] = vol_half; + matrix[pos_BL][pos_FC] = vol_half; + matrix[pos_BR][pos_FC] = vol_half; + break; + case 2: + output_mapping = mapping_STEREO; + matrix[pos_FL][pos_FL] = vol_max; + matrix[pos_FR][pos_FR] = vol_max; + matrix[pos_FC][pos_FL] = vol_sqrt; + matrix[pos_FC][pos_FR] = vol_sqrt; + matrix[pos_SL][pos_FL] = vol_sqrt; + matrix[pos_SR][pos_FR] = vol_sqrt; + matrix[pos_BL][pos_FL] = vol_sqrt; + matrix[pos_BR][pos_FR] = vol_sqrt; + break; + default: + /* not sure if +3ch would use FC/LFE, SL/BR and whatnot without passing extra config, so ignore for now */ + return; + } + + /* save and make N fake channels at the beginning for easier calcs */ + output_channels = data->output_channels; + for (ch = 0; ch < max; ch++) { + mixing_push_upmix(vgmstream, 0); + } + + /* downmix */ + ch_in = 0; + for (mp_in = 0; mp_in < 16; mp_in++) { + /* read input mapping (ex. 5.1) and find channel */ + if (!(input_mapping & (1< max) + break; + } + + ch_in++; + if (ch_in >= output_channels) + break; + } + + /* remove unneeded channels */ + mixing_push_killmix(vgmstream, max); +} + /* ******************************************************************* */ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { diff --git a/Frameworks/vgmstream/vgmstream/src/mixing.h b/Frameworks/vgmstream/vgmstream/src/mixing.h index 98e0b2e18..9bb49b97e 100644 --- a/Frameworks/vgmstream/vgmstream/src/mixing.h +++ b/Frameworks/vgmstream/vgmstream/src/mixing.h @@ -36,6 +36,7 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask); void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode); void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max); void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode); +void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/); #endif /* _MIXING_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.c b/Frameworks/vgmstream/vgmstream/src/plugins.c index 4baf602f8..524132632 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.c +++ b/Frameworks/vgmstream/vgmstream/src/plugins.c @@ -2,6 +2,71 @@ #include "plugins.h" #include "mixing.h" + +/* ****************************************** */ +/* CONTEXT: simplifies plugin code */ +/* ****************************************** */ + +int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) { + const char ** extension_list; + size_t extension_list_len; + const char *extension; + int i; + + + if (cfg->is_extension) { + extension = filename; + } else { + extension = filename_extension(filename); + } + + /* some metas accept extensionless files */ + if (strlen(extension) <= 0) { + return !cfg->reject_extensionless; + } + + /* try in default list */ + if (!cfg->skip_standard) { + extension_list = vgmstream_get_formats(&extension_list_len); + for (i = 0; i < extension_list_len; i++) { + if (strcasecmp(extension, extension_list[i]) == 0) { + return 1; + } + } + } + + /* try in common extensions */ + if (cfg->accept_common) { + extension_list = vgmstream_get_common_formats(&extension_list_len); + for (i = 0; i < extension_list_len; i++) { + if (strcasecmp(extension, extension_list[i]) == 0) + return 1; + } + } + + /* allow anything not in the normal list but not in common extensions */ + if (cfg->accept_unknown) { + int is_common = 0; + + extension_list = vgmstream_get_common_formats(&extension_list_len); + for (i = 0; i < extension_list_len; i++) { + if (strcasecmp(extension, extension_list[i]) == 0) { + is_common = 1; + break; + } + } + + if (!is_common) + return 1; + } + + return 0; +} + +/* ****************************************** */ +/* TAGS: loads key=val tags from a file */ +/* ****************************************** */ + #define VGMSTREAM_TAGS_LINE_MAX 2048 /* opaque tag state */ @@ -11,6 +76,7 @@ struct VGMSTREAM_TAGS { char val[VGMSTREAM_TAGS_LINE_MAX]; /* file to find tags for */ + int targetname_len; char targetname[VGMSTREAM_TAGS_LINE_MAX]; /* path of targetname */ char targetpath[VGMSTREAM_TAGS_LINE_MAX]; @@ -59,17 +125,21 @@ void vgmstream_tags_close(VGMSTREAM_TAGS *tags) { free(tags); } -/* Tags are divided in two: "global" @TAGS and "file" %TAGS for target filename. To extract both - * we find the filename's tag "section": (other_filename) ..(#tag section).. (target_filename). - * When a new "other_filename" is found that offset is marked as section_start, and when target_filename - * is found it's marked as section_end. Then we can begin extracting tags within that section, until - * all tags are exhausted. Global tags are extracted while searching, so they always go first, and - * also meaning any tags after the section is found are ignored. */ +/* Find next tag and return 1 if found. + * + * Tags can be "global" @TAGS, "command" $TAGS, and "file" %TAGS for a target filename. + * To extract tags we must find either global tags, or the filename's tag "section" + * where tags apply: (# @TAGS ) .. (other_filename) ..(# %TAGS section).. (target_filename). + * When a new "other_filename" is found that offset is marked as section_start, and when + * target_filename is found it's marked as section_end. Then we can begin extracting tags + * within that section, until all tags are exhausted. Global tags are extracted as found, + * so they always go first, also meaning any tags after file's section are ignored. + * Command tags have special meanings and are output after all section tags. */ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) { off_t file_size = get_streamfile_size(tagfile); char currentname[VGMSTREAM_TAGS_LINE_MAX] = {0}; char line[VGMSTREAM_TAGS_LINE_MAX] = {0}; - int ok, bytes_read, line_done; + int ok, bytes_read, line_done, n1,n2; if (!tags) return 0; @@ -92,7 +162,7 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) { /* read lines */ while (tags->offset <= file_size) { - /* no more tags to extract */ + /* after section: no more tags to extract */ if (tags->section_found && tags->offset >= tags->section_end) { /* write extra tags after all regular tags */ @@ -163,10 +233,32 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) { continue; /* next line */ } - /* find possible filename and section start/end */ - ok = sscanf(line, " %[^\r\n] ", currentname); + /* find possible filename and section start/end + * (.m3u seem to allow filenames with whitespaces before, make sure to trim) */ + ok = sscanf(line, " %n%[^\r\n]%n ", &n1, currentname, &n2); if (ok == 1) { - if (strcasecmp(tags->targetname,currentname) == 0) { /* looks ok even for UTF-8 */ + int currentname_len = n2 - n1; + int filename_found = 0; + + /* we want to find file with the same name (case insensitive), OR a virtual .txtp with + * the filename inside (so 'file.adx' gets tags from 'file.adx#i.txtp', reading + * tags even if we don't open !tags.m3u with virtual .txtp directly) */ + + /* strcasecmp works ok even for UTF-8 */ + if (currentname_len >= tags->targetname_len && /* starts with targetname */ + strncasecmp(currentname, tags->targetname, tags->targetname_len) == 0) { + + if (currentname_len == tags->targetname_len) { /* exact match */ + filename_found = 1; + } + else if (vgmstream_is_virtual_filename(currentname)) { /* ends with .txth */ + char c = currentname[tags->targetname_len]; + /* tell apart the unlikely case of having both 'bgm01.ad.txtp' and 'bgm01.adp.txtp' */ + filename_found = (c==' ' || c == '.' || c == '#'); + } + } + + if (filename_found) { /* section ok, start would be set before this (or be 0) */ tags->section_end = tags->offset; tags->section_found = 1; @@ -223,8 +315,13 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) { tags->targetpath[0] = '\0'; strcpy(tags->targetname, target_filename); } + tags->targetname_len = strlen(tags->targetname); } +/* ****************************************** */ +/* MIXING: modifies vgmstream output */ +/* ****************************************** */ + void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) { mixing_setup(vgmstream, max_sample_count); mixing_info(vgmstream, input_channels, output_channels); @@ -234,12 +331,14 @@ void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) { if (max_channels <= 0) return; - /* guess mixing the best we can */ - //todo: could use standard downmixing for known max_channels <> vgmstream->channels combos: - // https://www.audiokinetic.com/library/edge/?source=Help&id=downmix_tables#tbl_mono - // https://www.audiokinetic.com/library/edge/?source=Help&id=standard_configurations - - mixing_macro_layer(vgmstream, max_channels, 0, 'e'); + /* guess mixing the best we can, using standard downmixing if possible + * (without mapping we can't be sure if format is using a standard layout) */ + if (vgmstream->channel_layout && max_channels <= 2) { + mixing_macro_downmix(vgmstream, max_channels); + } + else { + mixing_macro_layer(vgmstream, max_channels, 0, 'e'); + } return; } diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.h b/Frameworks/vgmstream/vgmstream/src/plugins.h index 630214235..94b8d69a0 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.h +++ b/Frameworks/vgmstream/vgmstream/src/plugins.h @@ -6,46 +6,57 @@ #include "streamfile.h" +/* ****************************************** */ +/* CONTEXT: simplifies plugin code */ +/* ****************************************** */ + +typedef struct { + int is_extension; /* set if filename is already an extension */ + int skip_standard; /* set if shouldn't check standard formats */ + int reject_extensionless; /* set if player can't play extensionless files */ + int accept_unknown; /* set to allow any extension (for txth) */ + int accept_common; /* set to allow known-but-common extension (when player has plugin priority) */ +} vgmstream_ctx_valid_cfg; + +/* returns if vgmstream can parse file by extension */ +int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg); #if 0 -/* ****************************************** */ -/* PLAYER: simplifies plugin code */ -/* ****************************************** */ /* opaque player state */ -typedef struct VGMSTREAM_PLAYER VGMSTREAM_PLAYER; +typedef struct VGMSTREAM_CTX VGMSTREAM_CTX; typedef struct { //... -} VGMSTREAM_PLAYER_INFO; +} VGMSTREAM_CTX_INFO; -VGMSTREAM_PLAYER* vgmstream_player_init(...); +VGMSTREAM_CTX* vgmstream_ctx_init(...); -VGMSTREAM_PLAYER* vgmstream_player_format_check(...); -VGMSTREAM_PLAYER* vgmstream_player_set_format_whilelist(...); -VGMSTREAM_PLAYER* vgmstream_player_set_format_blacklist(...); +VGMSTREAM_CTX* vgmstream_ctx_format_check(...); +VGMSTREAM_CTX* vgmstream_ctx_set_format_whilelist(...); +VGMSTREAM_CTX* vgmstream_ctx_set_format_blacklist(...); -VGMSTREAM_PLAYER* vgmstream_player_set_file(...); +VGMSTREAM_CTX* vgmstream_ctx_set_file(...); -VGMSTREAM_PLAYER* vgmstream_player_get_config(...); +VGMSTREAM_CTX* vgmstream_ctx_get_config(...); -VGMSTREAM_PLAYER* vgmstream_player_set_config(...); +VGMSTREAM_CTX* vgmstream_ctx_set_config(...); -VGMSTREAM_PLAYER* vgmstream_player_get_buffer(...); +VGMSTREAM_CTX* vgmstream_ctx_get_buffer(...); -VGMSTREAM_PLAYER* vgmstream_player_get_info(...); +VGMSTREAM_CTX* vgmstream_ctx_get_info(...); -VGMSTREAM_PLAYER* vgmstream_player_describe(...); +VGMSTREAM_CTX* vgmstream_ctx_describe(...); -VGMSTREAM_PLAYER* vgmstream_player_get_title(...); +VGMSTREAM_CTX* vgmstream_ctx_get_title(...); -VGMSTREAM_PLAYER* vgmstream_player_get_tagfile(...); +VGMSTREAM_CTX* vgmstream_ctx_get_tagfile(...); -VGMSTREAM_PLAYER* vgmstream_player_play(...); +VGMSTREAM_CTX* vgmstream_ctx_play(...); -VGMSTREAM_PLAYER* vgmstream_player_seek(...); +VGMSTREAM_CTX* vgmstream_ctx_seek(...); -VGMSTREAM_PLAYER* vgmstream_player_close(...); +VGMSTREAM_CTX* vgmstream_ctx_close(...); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 472726e93..bdbb2412d 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -18,15 +18,15 @@ typedef struct { size_t buffersize; /* max buffer size */ size_t validsize; /* current buffer size */ size_t filesize; /* buffered file size */ -} STDIOSTREAMFILE; +} STDIO_STREAMFILE; static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize); static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize); -static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) { +static size_t read_stdio(STDIO_STREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) { size_t length_read_total = 0; - if (!streamfile || !dest || length <= 0 || offset < 0) + if (!streamfile->infile || !dest || length <= 0 || offset < 0) return 0; /* is the part of the requested length in the buffer? */ @@ -45,6 +45,11 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse dest += length_to_read; } +#ifdef VGM_DEBUG_OUTPUT + if (offset < streamfile->buffer_offset) { + VGM_LOG("STDIO: rebuffer, requested %lx vs %lx (sf %x)\n", offset, streamfile->buffer_offset, (uint32_t)streamfile); + } +#endif /* read the rest of the requested length */ while (length > 0) { @@ -99,35 +104,35 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse streamfile->offset = offset; /* last fread offset */ return length_read_total; } -static size_t get_size_stdio(STDIOSTREAMFILE * streamfile) { +static size_t get_size_stdio(STDIO_STREAMFILE * streamfile) { return streamfile->filesize; } -static off_t get_offset_stdio(STDIOSTREAMFILE *streamfile) { +static off_t get_offset_stdio(STDIO_STREAMFILE *streamfile) { return streamfile->offset; } -static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t length) { +static void get_name_stdio(STDIO_STREAMFILE *streamfile,char *buffer,size_t length) { strncpy(buffer,streamfile->name,length); buffer[length-1]='\0'; } -static void close_stdio(STDIOSTREAMFILE * streamfile) { - fclose(streamfile->infile); +static void close_stdio(STDIO_STREAMFILE * streamfile) { + if (streamfile->infile) + fclose(streamfile->infile); free(streamfile->buffer); free(streamfile); } -static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const filename,size_t buffersize) { - int newfd; - FILE *newfile; - STREAMFILE *newstreamFile; - +static STREAMFILE *open_stdio(STDIO_STREAMFILE *streamFile,const char * const filename,size_t buffersize) { if (!filename) return NULL; + #if !defined (__ANDROID__) // if same name, duplicate the file pointer we already have open - if (!strcmp(streamFile->name,filename)) { - if (((newfd = dup(fileno(streamFile->infile))) >= 0) && - (newfile = fdopen( newfd, "rb" ))) - { + if (streamFile->infile && !strcmp(streamFile->name,filename)) { + int newfd; + FILE *newfile; + STREAMFILE *newstreamFile; + + if ( ((newfd = dup(fileno(streamFile->infile))) >= 0) && (newfile = fdopen( newfd, "rb")) ) { newstreamFile = open_stdio_streamfile_buffer_by_file(newfile,filename,buffersize); if (newstreamFile) { return newstreamFile; @@ -141,14 +146,14 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil return open_stdio_streamfile_buffer(filename,buffersize); } -static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize) { +static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile, const char * const filename, size_t buffersize) { uint8_t * buffer = NULL; - STDIOSTREAMFILE * streamfile = NULL; + STDIO_STREAMFILE * streamfile = NULL; buffer = calloc(buffersize,1); if (!buffer) goto fail; - streamfile = calloc(1,sizeof(STDIOSTREAMFILE)); + streamfile = calloc(1,sizeof(STDIO_STREAMFILE)); if (!streamfile) goto fail; streamfile->sf.read = (void*)read_stdio; @@ -166,8 +171,13 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char streamfile->name[sizeof(streamfile->name)-1] = '\0'; /* cache filesize */ - fseeko(streamfile->infile,0,SEEK_END); - streamfile->filesize = ftello(streamfile->infile); + if (infile) { + fseeko(streamfile->infile,0,SEEK_END); + streamfile->filesize = ftello(streamfile->infile); + } + else { + streamfile->filesize = 0; /* allow virtual, non-existing files */ + } /* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF * (happens in banks like FSB, though rarely). Can be remedied with the @@ -190,11 +200,15 @@ static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, si STREAMFILE *streamFile; infile = fopen(filename,"rb"); - if (!infile) return NULL; + if (!infile) { + /* allow non-existing files in some cases */ + if (!vgmstream_is_virtual_filename(filename)) + return NULL; + } streamFile = open_stdio_streamfile_buffer_by_file(infile,filename,buffersize); if (!streamFile) { - fclose(infile); + if (infile) fclose(infile); } return streamFile; @@ -227,7 +241,7 @@ typedef struct { static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { size_t length_read_total = 0; - if (!streamfile || !dest || length <= 0 || offset < 0) + if (!dest || length <= 0 || offset < 0) return 0; /* is the part of the requested length in the buffer? */ @@ -246,6 +260,11 @@ static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t o dest += length_to_read; } +#ifdef VGM_DEBUG_OUTPUT + if (offset < streamfile->buffer_offset) { + VGM_LOG("BUFFER: rebuffer, requested %lx vs %lx (sf %x)\n", offset, streamfile->buffer_offset, (uint32_t)streamfile); + } +#endif /* read the rest of the requested length */ while (length > 0) { @@ -694,6 +713,7 @@ static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * new_sf = open_multifile_streamfile(new_inner_sfs, streamfile->inner_sfs_size); if (!new_sf) goto fail; + free(new_inner_sfs); return new_sf; } else { @@ -771,35 +791,72 @@ STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname) { } STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) { - char filename_ext[PATH_LIMIT]; - - streamFile->get_name(streamFile,filename_ext,sizeof(filename_ext)); - strcpy(filename_ext + strlen(filename_ext) - strlen(filename_extension(filename_ext)), ext); - - return streamFile->open(streamFile,filename_ext,STREAMFILE_DEFAULT_BUFFER_SIZE); -} - -STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * name) { - char foldername[PATH_LIMIT]; char filename[PATH_LIMIT]; - const char *path; + int filename_len, fileext_len; - streamFile->get_name(streamFile,foldername,sizeof(foldername)); + streamFile->get_name(streamFile,filename,sizeof(filename)); - path = strrchr(foldername,DIR_SEPARATOR); - if (path!=NULL) path = path+1; + filename_len = strlen(filename); + fileext_len = strlen(filename_extension(filename)); - if (path) { - strcpy(filename, foldername); - filename[path-foldername] = '\0'; - strcat(filename, name); - } else { - strcpy(filename, name); + if (fileext_len == 0) {/* extensionless */ + strcat(filename,"."); + strcat(filename,ext); + } + else { + strcpy(filename + filename_len - fileext_len, ext); } return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); } +STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename) { + char fullname[PATH_LIMIT]; + char partname[PATH_LIMIT]; + char *path, *name; + + streamFile->get_name(streamFile, fullname, sizeof(fullname)); + + //todo normalize separators in a better way, safeops, improve copying + path = strrchr(fullname,DIR_SEPARATOR); + if (path) { + path[1] = '\0'; /* remove name after separator */ + + strcpy(partname, filename); + fix_dir_separators(partname); + + /* normalize relative paths as don't work ok in some plugins */ + if (partname[0]=='.' && partname[1] == DIR_SEPARATOR) { /* './name' */ + name = partname + 2; /* ignore './' */ + } + else if (partname[0]=='.' && partname[1]=='.' && partname[2] == DIR_SEPARATOR) { /* '../name' */ + char *pathprev; + + path[0] = '\0'; /* remove last separator so next call works */ + pathprev = strrchr(fullname,DIR_SEPARATOR); + if (pathprev) { + pathprev[1] = '\0'; /* remove prev dir after separator */ + name = partname + 3; /* ignore '../' */ + } + else { /* let plugin handle? */ + path[0] = DIR_SEPARATOR; + name = partname; + } + /* could work with more relative paths but whatevs */ + } + else { + name = partname; + } + + strcat(fullname, name); + } + else { + strcpy(fullname, filename); + } + + return streamFile->open(streamFile, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE); +} + STREAMFILE * reopen_streamfile(STREAMFILE *streamFile, size_t buffer_size) { char pathname[PATH_LIMIT]; @@ -879,7 +936,7 @@ size_t read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamF if (buf) buf[pos] = '\0'; return maxsize; } - if (c < 0x20 || c > 0xA5) + if (c < 0x20 || (uint8_t)c > 0xA5) goto fail; } @@ -1019,26 +1076,29 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { * * returns 0 on failure */ -int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { - return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0); -} -int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { - return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0); -} -int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end) { - size_t filesize; - off_t current_chunk = start_offset; +static int find_chunk_internal(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_type, int big_endian_size, int zero_size_end) { + int32_t (*read_32bit_type)(off_t,STREAMFILE*) = big_endian_type ? read_32bitBE : read_32bitLE; + int32_t (*read_32bit_size)(off_t,STREAMFILE*) = big_endian_size ? read_32bitBE : read_32bitLE; + off_t offset, max_offset; + size_t file_size = get_streamfile_size(streamFile); + + if (max_size == 0) + max_size = file_size; + + offset = start_offset; + max_offset = offset + max_size; + if (max_offset > file_size) + max_offset = file_size; + - filesize = get_streamfile_size(streamFile); /* read chunks */ - while (current_chunk < filesize) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = size_big_endian ? - read_32bitBE(current_chunk+4,streamFile) : - read_32bitLE(current_chunk+4,streamFile); + while (offset < max_offset) { + uint32_t chunk_type = read_32bit_type(offset + 0x00,streamFile); + uint32_t chunk_size = read_32bit_size(offset + 0x04,streamFile); + //;VGM_LOG("CHUNK: type=%x, size=%x at %lx\n", chunk_type, chunk_size, offset); if (chunk_type == chunk_id) { - if (out_chunk_offset) *out_chunk_offset = current_chunk+8; + if (out_chunk_offset) *out_chunk_offset = offset + 0x08; if (out_chunk_size) *out_chunk_size = chunk_size; return 1; } @@ -1047,11 +1107,29 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in if (chunk_size == 0 && zero_size_end) return 0; - current_chunk += full_chunk_size ? chunk_size : 4+4+chunk_size; + offset += full_chunk_size ? chunk_size : 0x08 + chunk_size; } return 0; } +int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { + return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0); +} +int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { + return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0); +} +int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end) { + return find_chunk_internal(streamFile, chunk_id, start_offset, 0, full_chunk_size, out_chunk_offset, out_chunk_size, 1, big_endian_size, zero_size_end); +} +int find_chunk_riff_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) { + return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 0, 0); +} +int find_chunk_riff_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) { + return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 1, 0); +} +int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian) { + return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, big_endian, big_endian, 0); +} /* copies name as-is (may include full path included) */ void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 3e31a60e5..9dbee9dd0 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -119,8 +119,9 @@ STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname); * Can be used to get companion headers. */ STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext); -/* Opens a STREAMFILE from a base path + new filename - * Can be used to get companion files. */ +/* Opens a STREAMFILE from a base path + new filename. + * Can be used to get companion files. Relative paths like + * './filename', '../filename', 'dir/filename' also work. */ STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename); /* Reopen a STREAMFILE with a different buffer size, for fine-tuned bigfile parsing. @@ -267,7 +268,13 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); -int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end); +int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end); +/* find a RIFF-style chunk (with chunk_size not including id and size) */ +int find_chunk_riff_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size); +int find_chunk_riff_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size); +/* same with chunk ids in variable endianess (so instead of "fmt " has " tmf" */ +int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian); + void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size); diff --git a/Frameworks/vgmstream/vgmstream/src/util.c b/Frameworks/vgmstream/vgmstream/src/util.c index bda83e8b4..95b58a4f9 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.c +++ b/Frameworks/vgmstream/vgmstream/src/util.c @@ -101,14 +101,22 @@ void put_32bitBE(uint8_t * buf, int32_t i) { } void swap_samples_le(sample_t *buf, int count) { + /* Windows can't be BE... I think */ +#if !defined(_WIN32) +#if !defined(__BYTE_ORDER__) || __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ int i; for (i = 0; i < count; i++) { + /* 16b sample in memory: aabb where aa=MSB, bb=LSB */ uint8_t b0 = buf[i] & 0xff; uint8_t b1 = buf[i] >> 8; uint8_t *p = (uint8_t*)&(buf[i]); + /* 16b sample in buffer: bbaa where bb=LSB, aa=MSB */ p[0] = b0; p[1] = b1; + /* when endianness is LE, buffer has bbaa already so this function can be skipped */ } +#endif +#endif } /* length is maximum length of dst. dst will always be null-terminated if diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index 4f2d03684..8b4239472 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -62,9 +62,9 @@ static inline int get_low_nibble_signed(uint8_t n) { } static inline int clamp16(int32_t val) { - if (val>32767) return 32767; - if (val<-32768) return -32768; - return val; + if (val > 32767) return 32767; + else if (val < -32768) return -32768; + else return val; } static inline int round10(int val) { @@ -79,7 +79,8 @@ static inline int round10(int val) { * extension in the original filename or the ending null byte if no extension */ const char * filename_extension(const char * filename); -void swap_samples_le(sample *buf, int count); +/* swap samples in machine endianness to little endian (useful to write .wav) */ +void swap_samples_le(sample_t *buf, int count); void concatn(int length, char * dst, const char * src); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 5fbfb0411..b72235224 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -51,10 +51,9 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_mic, init_vgmstream_ngc_dsp_std_int, init_vgmstream_vag, - init_vgmstream_psx_gms, + init_vgmstream_seb, init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, - init_vgmstream_xbox_wavm, init_vgmstream_ngc_str, init_vgmstream_ea_schl, init_vgmstream_caf, @@ -98,11 +97,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_xwb, init_vgmstream_ps2_xa30, init_vgmstream_musc, - init_vgmstream_musx_v004, - init_vgmstream_musx_v005, - init_vgmstream_musx_v006, - init_vgmstream_musx_v010, - init_vgmstream_musx_v201, + init_vgmstream_musx, init_vgmstream_leg, init_vgmstream_filp, init_vgmstream_ikm_ps2, @@ -122,12 +117,12 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_pcm, init_vgmstream_ps2_rkv, init_vgmstream_ps2_vas, + init_vgmstream_ps2_vas_container, init_vgmstream_ps2_tec, init_vgmstream_ps2_enth, init_vgmstream_sdt, init_vgmstream_aix, init_vgmstream_ngc_tydsp, - init_vgmstream_ngc_swd, init_vgmstream_capdsp, init_vgmstream_xbox_wvs, init_vgmstream_ngc_wvs, @@ -138,8 +133,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_vs, init_vgmstream_dc_str, init_vgmstream_dc_str_v2, - init_vgmstream_xbox_xmu, - init_vgmstream_xbox_xvas, + init_vgmstream_xmu, + init_vgmstream_xvas, init_vgmstream_ngc_bh2pcm, init_vgmstream_sat_sap, init_vgmstream_dc_idvi, @@ -148,7 +143,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_kraw, init_vgmstream_ps2_omu, init_vgmstream_ps2_xa2, - init_vgmstream_nub_idsp, init_vgmstream_idsp_nl, init_vgmstream_idsp_ie, init_vgmstream_ngc_ymf, @@ -161,21 +155,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_wii_mus, init_vgmstream_dc_asd, init_vgmstream_naomi_spsd, - init_vgmstream_rsd2vag, - init_vgmstream_rsd2pcmb, - init_vgmstream_rsd2xadp, - init_vgmstream_rsd3vag, - init_vgmstream_rsd3gadp, - init_vgmstream_rsd3pcm, - init_vgmstream_rsd3pcmb, - init_vgmstream_rsd4pcmb, - init_vgmstream_rsd4pcm, - init_vgmstream_rsd4radp, - init_vgmstream_rsd4vag, - init_vgmstream_rsd6vag, - init_vgmstream_rsd6wadp, - init_vgmstream_rsd6xadp, - init_vgmstream_rsd6radp, + init_vgmstream_rsd, init_vgmstream_bgw, init_vgmstream_spw, init_vgmstream_ps2_ass, @@ -274,7 +254,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_bar, init_vgmstream_ffw, init_vgmstream_dsp_dspw, - init_vgmstream_ps2_jstm, + init_vgmstream_jstm, init_vgmstream_xvag, init_vgmstream_ps3_cps, init_vgmstream_sqex_scd, @@ -282,7 +262,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_baf, init_vgmstream_baf_badrip, init_vgmstream_msf, - init_vgmstream_nub_vag, init_vgmstream_ps3_past, init_vgmstream_sgxd, init_vgmstream_ngca, @@ -309,7 +288,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps3_ivag, init_vgmstream_ps2_2pfs, init_vgmstream_xnb, - init_vgmstream_rsd6oogv, init_vgmstream_ubi_ckd, init_vgmstream_ps2_vbk, init_vgmstream_otm, @@ -333,13 +311,11 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_wwise, init_vgmstream_ubi_raki, init_vgmstream_x360_pasx, - init_vgmstream_nub_xma, init_vgmstream_xma, init_vgmstream_sxd, init_vgmstream_ogl, init_vgmstream_mc3, init_vgmstream_gtd, - init_vgmstream_rsd6xma, init_vgmstream_ta_aac_x360, init_vgmstream_ta_aac_ps3, init_vgmstream_ta_aac_mobile, @@ -349,11 +325,12 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_mta2, init_vgmstream_mta2_container, init_vgmstream_ngc_ulw, - init_vgmstream_pc_xa30, - init_vgmstream_wii_04sw, + init_vgmstream_xa_xa30, + init_vgmstream_xa_04sw, init_vgmstream_ea_bnk, init_vgmstream_ea_abk, init_vgmstream_ea_hdr_dat, + init_vgmstream_ea_hdr_dat_v2, init_vgmstream_ea_map_mus, init_vgmstream_ea_mpf_mus, init_vgmstream_ea_schl_fixed, @@ -398,8 +375,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_waf, init_vgmstream_wave, init_vgmstream_wave_segmented, - init_vgmstream_rsd6at3p, - init_vgmstream_rsd6wma, init_vgmstream_smv, init_vgmstream_nxap, init_vgmstream_ea_wve_au00, @@ -461,7 +436,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_xps, init_vgmstream_zsnd, init_vgmstream_opus_opusx, - init_vgmstream_dsp_adpcmx, + init_vgmstream_dsp_adpy, + init_vgmstream_dsp_adpx, init_vgmstream_ogg_opus, init_vgmstream_nus3audio, init_vgmstream_imc, @@ -483,15 +459,37 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_bwav, init_vgmstream_opus_prototype, init_vgmstream_awb, + init_vgmstream_acb, init_vgmstream_rad, + init_vgmstream_smk, + init_vgmstream_mzrt, + init_vgmstream_xavs, + init_vgmstream_psf_single, + init_vgmstream_psf_segmented, + init_vgmstream_dsp_itl, + init_vgmstream_sch, + init_vgmstream_ima, + init_vgmstream_nub, + init_vgmstream_nub_wav, + init_vgmstream_nub_vag, + init_vgmstream_nub_at3, + init_vgmstream_nub_xma, + init_vgmstream_nub_idsp, + init_vgmstream_nub_is14, + init_vgmstream_xmv_valve, + init_vgmstream_ubi_hx, + init_vgmstream_bmp_konami, + init_vgmstream_opus_opusnx, + init_vgmstream_opus_sqex, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ - init_vgmstream_ps2_int, /* .int raw PS-ADPCM */ + init_vgmstream_raw_int, /* .int raw PCM */ init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */ - init_vgmstream_pc_snds, /* .snds PC, after ps_headerless */ - init_vgmstream_s14_sss, /* .raw siren14 */ - init_vgmstream_raw, /* .raw PCM */ + init_vgmstream_raw_snds, /* .snds raw SNDS IMA (*after* ps_headerless) */ + init_vgmstream_raw_wavm, /* .wavm raw xbox */ + init_vgmstream_raw_pcm, /* .raw raw PCM */ + init_vgmstream_s14_sss, /* .s14/sss raw siren14 */ #ifdef VGM_USE_FFMPEG init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ #endif @@ -507,7 +505,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { fcns_size = (sizeof(init_vgmstream_functions)/sizeof(init_vgmstream_functions[0])); /* try a series of formats, see which works */ - for (i =0; i < fcns_size; i++) { + for (i = 0; i < fcns_size; i++) { /* call init function and see if valid VGMSTREAM was returned */ VGMSTREAM * vgmstream = (init_vgmstream_functions[i])(streamFile); if (!vgmstream) @@ -657,6 +655,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { reset_hca(vgmstream->codec_data); } + if (vgmstream->coding_type == coding_UBI_ADPCM) { + reset_ubi_adpcm(vgmstream->codec_data); + } + if (vgmstream->coding_type == coding_EA_MT) { reset_ea_mt(vgmstream); } @@ -821,6 +823,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) { vgmstream->codec_data = NULL; } + if (vgmstream->coding_type == coding_UBI_ADPCM) { + free_ubi_adpcm(vgmstream->codec_data); + vgmstream->codec_data = NULL; + } + if (vgmstream->coding_type == coding_EA_MT) { free_ea_mt(vgmstream->codec_data, vgmstream->channels); vgmstream->codec_data = NULL; @@ -1025,7 +1032,7 @@ void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target) { /* Decode data into sample buffer */ -void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { +void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { switch (vgmstream->layout_type) { case layout_interleave: render_vgmstream_interleave(buffer,sample_count,vgmstream); @@ -1146,6 +1153,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_OTNS_IMA: case coding_UBI_IMA: case coding_OKI16: + case coding_OKI4S: return 1; case coding_PCM4: case coding_PCM4_U: @@ -1188,7 +1196,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_HEVAG: return 28; case coding_PSX_cfg: - return (vgmstream->interleave_block_size - 1) * 2; /* decodes 1 byte into 2 bytes */ + case coding_PSX_pivotal: + return (vgmstream->interleave_block_size - 0x01) * 2; /* size 0x01 header */ case coding_EA_XA: case coding_EA_XA_int: @@ -1207,9 +1216,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return (vgmstream->interleave_block_size - 0x07)*2 + 2; case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ return vgmstream->ws_output_size; - case coding_YAMAHA: + case coding_AICA: return 1; - case coding_YAMAHA_int: + case coding_AICA_int: return 2; case coding_ASKA: return (0x40-0x04*vgmstream->channels) * 2 / vgmstream->channels; @@ -1255,6 +1264,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 14; /* (0x08 - 0x1) * 2 */ case coding_XMD: return (vgmstream->interleave_block_size - 0x06)*2 + 2; + case coding_PTADPCM: + return (vgmstream->interleave_block_size - 0x05)*2 + 2; + case coding_UBI_ADPCM: + return 0; /* varies per mode */ case coding_EA_MT: return 0; /* 432, but variable in looped files */ case coding_CRI_HCA: @@ -1339,6 +1352,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_BLITZ_IMA: case coding_PCFX: case coding_OKI16: + case coding_OKI4S: return 0x01; case coding_MS_IMA: case coding_RAD_IMA: @@ -1376,6 +1390,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_HEVAG: return 0x10; case coding_PSX_cfg: + case coding_PSX_pivotal: return vgmstream->interleave_block_size; case coding_EA_XA: @@ -1397,8 +1412,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return vgmstream->interleave_block_size; case coding_WS: return vgmstream->current_block_size; - case coding_YAMAHA: - case coding_YAMAHA_int: + case coding_AICA: + case coding_AICA_int: return 0x01; case coding_ASKA: case coding_NXAP: @@ -1436,6 +1451,10 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x08; case coding_XMD: return vgmstream->interleave_block_size; + case coding_PTADPCM: + return vgmstream->interleave_block_size; + case coding_UBI_ADPCM: + return 0; /* varies per mode? */ case coding_EA_MT: return 0; /* variable (frames of bit counts or PCM frames) */ #ifdef VGM_USE_ATRAC9 @@ -1691,6 +1710,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; + case coding_PSX_pivotal: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx_pivotal(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, + vgmstream->interleave_block_size); + } + break; case coding_HEVAG: for (ch = 0; ch < vgmstream->channels; ch++) { decode_hevag(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, @@ -1986,12 +2012,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); } break; - case coding_YAMAHA: - case coding_YAMAHA_int: + case coding_AICA: + case coding_AICA_int: for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_YAMAHA); + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA); - decode_yamaha(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + decode_aica(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, is_stereo); } @@ -2076,6 +2102,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->interleave_block_size); } break; + case coding_PTADPCM: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ptadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, + vgmstream->interleave_block_size); + } + break; case coding_PCFX: for (ch = 0; ch < vgmstream->channels; ch++) { decode_pcfx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, @@ -2089,6 +2122,17 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to } break; + case coding_OKI4S: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_oki4s(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); + } + break; + + case coding_UBI_ADPCM: + decode_ubi_adpcm(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do); + break; + case coding_EA_MT: for (ch = 0; ch < vgmstream->channels; ch++) { decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+ch, @@ -2170,6 +2214,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { loop_hca(vgmstream->codec_data, vgmstream->loop_sample); } + if (vgmstream->coding_type == coding_UBI_ADPCM) { + seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_sample); + } + if (vgmstream->coding_type == coding_EA_MT) { seek_ea_mt(vgmstream, vgmstream->loop_sample); } @@ -2264,7 +2312,6 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { #define TEMPSIZE (256+32) char temp[TEMPSIZE]; - const char* description; double time_mm, time_ss, seconds; if (!vgmstream) { @@ -2344,89 +2391,25 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE, "encoding: "); concatn(length,desc,temp); - switch (vgmstream->coding_type) { -#ifdef VGM_USE_FFMPEG - - case coding_FFmpeg: { - //todo codec bugs with layout inside layouts (ex. TXTP) - ffmpeg_codec_data *data = NULL; - - if (vgmstream->layout_type == layout_layered) { - layered_layout_data* layout_data = vgmstream->layout_data; - if (layout_data->layers[0]->coding_type == coding_FFmpeg) - data = layout_data->layers[0]->codec_data; - } - else if (vgmstream->layout_type == layout_segmented) { - segmented_layout_data* layout_data = vgmstream->layout_data; - if (layout_data->segments[0]->coding_type == coding_FFmpeg) - data = layout_data->segments[0]->codec_data; - } - else { - data = vgmstream->codec_data; - } - - if (data) { - if (data->codec && data->codec->long_name) { - snprintf(temp,TEMPSIZE, "%s",data->codec->long_name); - } else if (data->codec && data->codec->name) { - snprintf(temp,TEMPSIZE, "%s",data->codec->name); - } else { - snprintf(temp,TEMPSIZE, "FFmpeg (unknown codec)"); - } - } else { - snprintf(temp,TEMPSIZE, "FFmpeg"); - } - break; - } -#endif - default: - description = get_vgmstream_coding_description(vgmstream->coding_type); - if (!description) description = "CANNOT DECODE"; - snprintf(temp,TEMPSIZE, "%s",description); - break; - } + get_vgmstream_coding_description(vgmstream, temp, TEMPSIZE); concatn(length,desc,temp); concatn(length,desc,"\n"); snprintf(temp,TEMPSIZE, "layout: "); concatn(length,desc,temp); - { - VGMSTREAM* vgmstreamsub = NULL; - - description = get_vgmstream_layout_description(vgmstream->layout_type); - if (!description) description = "INCONCEIVABLE"; - - if (vgmstream->layout_type == layout_layered) { - vgmstreamsub = ((layered_layout_data*)vgmstream->layout_data)->layers[0]; - snprintf(temp,TEMPSIZE, "%s (%i layers)", description, ((layered_layout_data*)vgmstream->layout_data)->layer_count); - } - else if (vgmstream->layout_type == layout_segmented) { - snprintf(temp,TEMPSIZE, "%s (%i segments)", description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); - vgmstreamsub = ((segmented_layout_data*)vgmstream->layout_data)->segments[0]; - } - else { - snprintf(temp,TEMPSIZE, "%s",description); - } - concatn(length,desc,temp); - - /* layouts can contain layouts infinitely let's leave it at one level deep (most common) */ - if (vgmstreamsub && vgmstreamsub->layout_type == layout_layered) { - description = get_vgmstream_layout_description(vgmstreamsub->layout_type); - snprintf(temp,TEMPSIZE, " + %s (%i layers)",description, ((layered_layout_data*)vgmstreamsub->layout_data)->layer_count); - concatn(length,desc,temp); - } - else if (vgmstreamsub && vgmstreamsub->layout_type == layout_segmented) { - description = get_vgmstream_layout_description(vgmstreamsub->layout_type); - snprintf(temp,TEMPSIZE, " + %s (%i segments)",description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); - concatn(length,desc,temp); - } - } + get_vgmstream_layout_description(vgmstream, temp, TEMPSIZE); + concatn(length, desc, temp); concatn(length,desc,"\n"); if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) { snprintf(temp,TEMPSIZE, "interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size); concatn(length,desc,temp); + if (vgmstream->interleave_first_block_size) { + snprintf(temp,TEMPSIZE, "interleave first block: %#x bytes\n", (int32_t)vgmstream->interleave_first_block_size); + concatn(length,desc,temp); + } + if (vgmstream->interleave_last_block_size) { snprintf(temp,TEMPSIZE, "interleave last block: %#x bytes\n", (int32_t)vgmstream->interleave_last_block_size); concatn(length,desc,temp); @@ -2454,13 +2437,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE, "metadata from: "); concatn(length,desc,temp); - switch (vgmstream->meta_type) { - default: - description = get_vgmstream_meta_description(vgmstream->meta_type); - if (!description) description = "THEY SHOULD HAVE SENT A POET"; - snprintf(temp,TEMPSIZE, "%s", description); - break; - } + get_vgmstream_meta_description(vgmstream, temp, TEMPSIZE); concatn(length,desc,temp); concatn(length,desc,"\n"); @@ -2496,14 +2473,15 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea {"Left","Right"}, {".V0",".V1"}, /* Homura (PS2) */ {".L",".R"}, /* Crash Nitro Racing (PS2), Gradius V (PS2) */ - {"_0","_1"}, //fake for Homura/unneeded? + {"_0","_1"}, /* fake for Homura/unneeded? */ + {".adpcm","_NxEncoderOut_.adpcm"}, /* Kill la Kill: IF (Switch) */ //todo can't match R>L }; char new_filename[PATH_LIMIT]; - char * ext; + char * extension; int dfs_pair = -1; /* -1=no stereo, 0=opened_vgmstream is left, 1=opened_vgmstream is right */ VGMSTREAM *new_vgmstream = NULL; STREAMFILE *dual_streamFile = NULL; - int i,j, dfs_pair_count; + int i,j, dfs_pair_count, extension_len, filename_len; if (opened_vgmstream->channels != 1) return; @@ -2512,48 +2490,54 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea if (opened_vgmstream->codec_data || opened_vgmstream->layout_data) return; - /* vgmstream's layout stuff currently assumes a single file */ - // fastelbja : no need ... this one works ok with dual file + //todo other layouts work but some stereo codecs do weird things //if (opened_vgmstream->layout != layout_none) return; - //todo force layout_none if layout_interleave? get_streamfile_name(streamFile,new_filename,sizeof(new_filename)); - if (strlen(new_filename) < 2) return; /* we need at least a base and a name ending to replace */ + filename_len = strlen(new_filename); + if (filename_len < 2) + return; - ext = (char *)filename_extension(new_filename); - if (ext-new_filename >= 1 && ext[-1]=='.') ext--; /* including "." */ + extension = (char *)filename_extension(new_filename); + if (extension - new_filename >= 1 && extension[-1] == '.') /* [-1] is ok, yeah */ + extension--; /* must include "." */ + extension_len = strlen(extension); - /* find pair from base name and modify new_filename with the opposite */ + + /* find pair from base name and modify new_filename with the opposite (tries L>R then R>L) */ dfs_pair_count = (sizeof(dfs_pairs)/sizeof(dfs_pairs[0])); for (i = 0; dfs_pair == -1 && i < dfs_pair_count; i++) { for (j = 0; dfs_pair == -1 && j < 2; j++) { const char * this_suffix = dfs_pairs[i][j]; + const char * that_suffix = dfs_pairs[i][j^1]; size_t this_suffix_len = strlen(dfs_pairs[i][j]); - const char * other_suffix = dfs_pairs[i][j^1]; - size_t other_suffix_len = strlen(dfs_pairs[i][j^1]); + size_t that_suffix_len = strlen(dfs_pairs[i][j^1]); - /* if suffix matches copy opposite to ext pointer (thus to new_filename) */ - if (this_suffix[0] == '.' && strlen(ext) == this_suffix_len) { /* dual extension (ex. Homura PS2) */ - if ( !memcmp(ext,this_suffix,this_suffix_len) ) { + //;VGM_LOG("DFS: l=%s, r=%s\n", this_suffix,that_suffix); + + /* if suffix matches paste opposite suffix (+ terminator) to extension pointer, thus to new_filename */ + if (this_suffix[0] == '.' && extension_len == this_suffix_len) { /* same extension */ + //;VGM_LOG("DFS: same ext %s vs %s len %i\n", extension, this_suffix, this_suffix_len); + if (memcmp(extension,this_suffix,this_suffix_len) == 0) { dfs_pair = j; - memcpy (ext, other_suffix,other_suffix_len); /* overwrite with new extension */ + memcpy (extension, that_suffix,that_suffix_len+1); } } - else { /* dual suffix */ - if ( !memcmp(ext - this_suffix_len,this_suffix,this_suffix_len) ) { + else if (filename_len - extension_len > this_suffix_len) { /* same suffix (without extension) */ + //;VGM_LOG("DFS: same suf %s vs %s len %i\n", extension - this_suffix_len, this_suffix, this_suffix_len); + if (memcmp(extension - this_suffix_len, this_suffix,this_suffix_len) == 0) { dfs_pair = j; - memmove(ext + other_suffix_len - this_suffix_len, ext,strlen(ext)+1); /* move the extension and terminator, too */ - memcpy (ext - this_suffix_len, other_suffix,other_suffix_len); /* overwrite with new suffix */ + memmove(extension + that_suffix_len - this_suffix_len, extension,extension_len+1); /* move old extension to end */ + memcpy (extension - this_suffix_len, that_suffix,that_suffix_len); /* overwrite with new suffix */ } } - } } /* see if the filename had a suitable L/R-pair name */ if (dfs_pair == -1) goto fail; - + //;VGM_LOG("DFS: match %i filename=%s\n", dfs_pair, new_filename); /* try to init other channel (new_filename now has the opposite name) */ dual_streamFile = open_streamfile(streamFile,new_filename); @@ -2640,6 +2624,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea opened_vgmstream->channels = 2; /* discard the second VGMSTREAM */ + mixing_close(new_vgmstream); + free(new_vgmstream->start_vgmstream); free(new_vgmstream); mixing_update_channel(opened_vgmstream); /* notify of new channel hacked-in */ @@ -2815,6 +2801,12 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s vgmstream->coding_type == coding_CRI_HCA) return 1; +#ifdef VGM_USE_VORBIS + /* stream/offsets not needed, managed by decoder */ + if (vgmstream->coding_type == coding_OGG_VORBIS) + return 1; +#endif + #ifdef VGM_USE_FFMPEG /* stream/offsets not needed, managed by decoder */ if (vgmstream->coding_type == coding_FFmpeg) @@ -2894,3 +2886,20 @@ fail: /* open streams will be closed in close_vgmstream(), hopefully called by the meta */ return 0; } + +int vgmstream_is_virtual_filename(const char* filename) { + int len = strlen(filename); + if (len < 6) + return 0; + + /* vgmstream can play .txtp files that have size 0 but point to another file with config + * based only in the filename (ex. "file.fsb #2.txtp" plays 2nd subsong of file.fsb). + * + * Also, .m3u playlist can include files that don't exist, and players often allow filenames + * pointing to nothing (since could be some protocol/url). + * + * Plugins can use both quirks to allow "virtual files" (.txtp) in .m3u that don't need + * to exist but allow config. Plugins with this function if the filename is virtual, + * and their STREAMFILEs should be modified as to ignore null FILEs and report size 0. */ + return strcmp(&filename[len-5], ".txtp") == 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 1ae890325..536374fe5 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -116,7 +116,8 @@ typedef enum { coding_XA, /* CD-ROM 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 (FF XI, SGXD type 5, Bizarre Creations) */ + coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (int math) */ + coding_PSX_pivotal, /* Sony PS ADPCM with configurable frame size (float math) */ coding_HEVAG, /* Sony PSVita ADPCM */ coding_EA_XA, /* Electronic Arts EA-XA ADPCM v1 (stereo) aka "EA ADPCM" */ @@ -159,8 +160,8 @@ typedef enum { coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */ coding_WS, /* Westwood Studios VBR ADPCM */ - coding_YAMAHA, /* Yamaha ADPCM (stereo) */ - coding_YAMAHA_int, /* Yamaha ADPCM (mono/interleave) */ + coding_AICA, /* Yamaha AICA ADPCM (stereo) */ + coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */ coding_ASKA, /* Aska ADPCM */ coding_NXAP, /* NXAP ADPCM */ @@ -175,7 +176,9 @@ typedef enum { coding_DSA, /* Ocean DSA 4-bit ADPCM */ coding_XMD, /* Konami XMD 4-bit ADPCM */ coding_PCFX, /* PC-FX 4-bit ADPCM */ - coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output */ + coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output and modified expand */ + coding_OKI4S, /* OKI 4-bit ADPCM with 16-bit output and cuadruple step */ + coding_PTADPCM, /* Platinum 4-bit ADPCM */ /* others */ coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ @@ -187,6 +190,7 @@ typedef enum { coding_ACM, /* InterPlay ACM */ coding_NWA, /* VisualArt's NWA */ coding_CIRCUS_ADPCM, /* Circus 8-bit ADPCM */ + coding_UBI_ADPCM, /* Ubisoft 4/6-bit ADPCM */ coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */ @@ -351,7 +355,7 @@ typedef enum { meta_PS2_SShd, /* .ADS with SShd header */ meta_NPS, meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */ - meta_PS2_RAW, /* RAW Interleaved Format */ + meta_RAW_INT, meta_PS2_EXST, /* Shadow of Colossus EXST */ meta_PS2_SVAG, /* Konami SVAG */ meta_PS_HEADERLESS, /* headerless PS-ADPCM */ @@ -360,7 +364,7 @@ typedef enum { meta_PS2_VAGi, /* VAGi Interleaved File */ meta_PS2_VAGp, /* VAGp Mono File */ meta_PS2_pGAV, /* VAGp with Little Endian Header */ - meta_PSX_GMS, /* GMS File (used in PS1 & PS2) [no header_id] */ + meta_SEB, meta_STR_WAV, /* Blitz Games STR+WAV files */ meta_PS2_ILD, /* ILD File */ meta_PS2_PNB, /* PsychoNauts Bgm File */ @@ -383,11 +387,7 @@ typedef enum { meta_XWB, /* Microsoft XACT framework (Xbox, X360, Windows) */ meta_PS2_XA30, /* Driver - Parallel Lines (PS2) */ meta_MUSC, /* Krome PS2 games */ - meta_MUSX_V004, /* Spyro Games, possibly more */ - meta_MUSX_V005, /* Spyro Games, possibly more */ - meta_MUSX_V006, /* Spyro Games, possibly more */ - meta_MUSX_V010, /* Spyro Games, possibly more */ - meta_MUSX_V201, /* Sphinx and the cursed Mummy */ + meta_MUSX, meta_LEG, /* Legaia 2 [no header_id] */ meta_FILP, /* Resident Evil - Dead Aim */ meta_IKM, @@ -405,7 +405,6 @@ typedef enum { meta_PS2_ENTH, /* Enthusia */ meta_SDT, /* Baldur's Gate - Dark Alliance */ meta_NGC_TYDSP, /* Ty - The Tasmanian Tiger */ - meta_NGC_SWD, /* Conflict - Desert Storm 1 & 2 */ meta_CAPDSP, /* Capcom DSP Header [no header_id] */ meta_DC_STR, /* SEGA Stream Asset Builder */ meta_DC_STR_V2, /* variant of SEGA Stream Asset Builder */ @@ -415,7 +414,7 @@ typedef enum { meta_KRAW, /* Geometry Wars - Galaxies */ meta_PS2_OMU, /* PS2 Int file with Header */ meta_PS2_XA2, /* XG3 Extreme-G Racing */ - meta_NUB_IDSP, /* Soul Calibur Legends (Wii) */ + meta_NUB, meta_IDSP_NL, /* Mario Strikers Charged (Wii) */ meta_IDSP_IE, /* Defencer (GC) */ meta_SPT_SPD, /* Various (SPT+SPT DSP) */ @@ -436,27 +435,7 @@ typedef enum { meta_NGC_PDT, /* Mario Party 6 */ meta_DC_ASD, /* Miss Moonligh */ meta_NAOMI_SPSD, /* Guilty Gear X */ - - meta_RSD2VAG, /* RSD2VAG */ - meta_RSD2PCMB, /* RSD2PCMB */ - meta_RSD2XADP, /* RSD2XADP */ - meta_RSD3VAG, /* RSD3VAG */ - meta_RSD3GADP, /* RSD3GADP */ - meta_RSD3PCM, /* RSD3PCM */ - meta_RSD3PCMB, /* RSD3PCMB */ - meta_RSD4PCMB, /* RSD4PCMB */ - meta_RSD4PCM, /* RSD4PCM */ - meta_RSD4RADP, /* RSD4RADP */ - meta_RSD4VAG, /* RSD4VAG */ - meta_RSD6VAG, /* RSD6VAG */ - meta_RSD6WADP, /* RSD6WADP */ - meta_RSD6XADP, /* RSD6XADP */ - meta_RSD6RADP, /* RSD6RADP */ - meta_RSD6OOGV, /* RSD6OOGV */ - meta_RSD6XMA, /* RSD6XMA */ - meta_RSD6AT3P, /* RSD6AT3+ */ - meta_RSD6WMA, /* RSD6WMA */ - + meta_RSD, meta_PS2_ASS, /* ASS */ meta_SEG, /* Eragon */ meta_NDS_STRM_FFTA2, /* Final Fantasy Tactics A2 */ @@ -475,24 +454,19 @@ typedef enum { meta_PS2_XA2_RRP, /* RC Revenge Pro */ meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */ meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ - - meta_XBOX_WAVM, /* XBOX WAVM File */ + meta_RAW_WAVM, meta_XBOX_WVS, /* XBOX WVS */ meta_NGC_WVS, /* Metal Arms - Glitch in the System */ meta_XBOX_MATX, /* XBOX MATX */ - meta_XBOX_XMU, /* XBOX XMU */ - meta_XBOX_XVAS, /* XBOX VAS */ - + meta_XMU, + meta_XVAS, meta_EA_SCHL, /* Electronic Arts SCHl with variable header */ meta_EA_SCHL_fixed, /* Electronic Arts SCHl with fixed header */ meta_EA_BNK, /* Electronic Arts BNK */ meta_EA_1SNH, /* Electronic Arts 1SNh/EACS */ meta_EA_EACS, - - meta_RAW, /* RAW PCM file */ - + meta_RAW_PCM, meta_GENH, /* generic header */ - meta_AIFC, /* Audio Interchange File Format AIFF-C */ meta_AIFF, /* Audio Interchange File Format */ meta_STR_SNDS, /* .str with SNDS blocks and SHDR header */ @@ -581,7 +555,6 @@ typedef enum { meta_XVAG, /* Ratchet & Clank Future: Quest for Booty (PS3) */ meta_PS3_CPS, /* Eternal Sonata (PS3) */ meta_MSF, - meta_NUB_VAG, /* Namco VAG from NUB archives */ meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */ meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */ meta_NGCA, /* GoldenEye 007 (Wii) */ @@ -593,7 +566,7 @@ typedef enum { meta_VS_STR, /* The Bouncer */ meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ meta_VAWX, /* feelplus: No More Heroes Heroes Paradise, Moon Diver */ - meta_PC_SNDS, /* Incredibles PC .snds */ + meta_RAW_SNDS, meta_PS2_WMUS, /* The Warriors (PS2) */ meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */ meta_IOS_PSND, /* Crash Bandicoot Nitro Kart 2 (iOS) */ @@ -626,7 +599,6 @@ typedef enum { meta_FFMPEG, /* any file supported by FFmpeg */ meta_X360_CXS, /* Eternal Sonata (Xbox 360) */ meta_AKB, /* SQEX iOS */ - meta_NUB_XMA, /* Namco XMA from NUB archives */ meta_X360_PASX, /* Namco PASX (Soul Calibur II HD X360) */ meta_XMA_RIFF, /* Microsoft RIFF XMA */ meta_X360_AST, /* Dead Rising (X360) */ @@ -641,8 +613,8 @@ typedef enum { meta_TA_AAC_MOBILE, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */ meta_MTA2, meta_NGC_ULW, /* Burnout 1 (GC only) */ - meta_PC_XA30, /* Driver - Parallel Lines (PC) */ - meta_WII_04SW, /* Driver - Parallel Lines (Wii) */ + meta_XA_XA30, + meta_XA_04SW, meta_TXTH, /* generic text header */ meta_SK_AUD, /* Silicon Knights .AUD (Eternal Darkness GC) */ meta_AHX, /* CRI AHX header */ @@ -730,7 +702,8 @@ typedef enum { meta_MSF_TAMASOFT, meta_XPS_DAT, meta_ZSND, - meta_DSP_ADPCMX, + meta_DSP_ADPY, + meta_DSP_ADPX, meta_OGG_OPUS, meta_IMC, meta_GIN, @@ -744,6 +717,15 @@ typedef enum { meta_9TAV, meta_BWAV, meta_RAD, + meta_SMACKER, + meta_MZRT, + meta_XAVS, + meta_PSF, + meta_DSP_ITL_i, + meta_IMA, + meta_XMV_VALVE, + meta_UBI_HX, + meta_BMP_KONAMI, } meta_t; @@ -855,6 +837,8 @@ typedef struct { /* layouts/block config */ size_t interleave_block_size; /* interleave, or block/frame size (depending on the codec) */ + size_t interleave_first_block_size; /* different interleave for first block */ + size_t interleave_first_skip; /* data skipped before interleave first (needed to skip other channels) */ size_t interleave_last_block_size; /* smaller interleave for last block */ /* subsong config */ @@ -1220,11 +1204,13 @@ typedef struct { int64_t skipSamples; // number of start samples that will be skipped (encoder delay), for looping adjustments int streamCount; // number of FFmpeg audio streams + /*** internal state ***/ + // config int channel_remap_set; int channel_remap[32]; /* map of channel > new position */ + int invert_audio_set; - /*** internal state ***/ - // Intermediate byte buffer + // intermediate byte buffer uint8_t *sampleBuffer; // max samples we can held (can be less or more than frameSize) size_t sampleBufferBlock; @@ -1249,6 +1235,9 @@ typedef struct { // Seeking is not ideal, so rollback is necessary int samplesToDiscard; + // Flags for special seeking in faulty formats + int force_seek; + int bad_init; } ffmpeg_codec_data; #endif @@ -1275,6 +1264,8 @@ typedef struct { #endif #endif +typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data; + typedef struct ea_mt_codec_data ea_mt_codec_data; @@ -1337,7 +1328,7 @@ void close_vgmstream(VGMSTREAM * vgmstream); int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM * vgmstream); /* Decode data into sample buffer */ -void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); /* Write a description of the stream into array pointed by desc, which must be length bytes long. * Will always be null-terminated if length > 0 */ @@ -1350,6 +1341,9 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream); * The list disables some common formats that may conflict (.wav, .ogg, etc). */ const char ** vgmstream_get_formats(size_t * size); +/* same, but for common-but-disabled formats in the above list. */ +const char ** vgmstream_get_common_formats(size_t * size); + /* Force enable/disable internal looping. Should be done before playing anything (or after reset), * and not all codecs support arbitrary loop values ATM. */ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sample, int loop_end_sample); @@ -1357,6 +1351,9 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa /* Set number of max loops to do, then play up to stream end (for songs with proper endings) */ void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target); +/* Return 1 if vgmstream detects from the filename that said file can be used even if doesn't physically exist */ +int vgmstream_is_virtual_filename(const char* filename); + /* -------------------------------------------------------------------------*/ /* vgmstream "private" API */ /* -------------------------------------------------------------------------*/ @@ -1390,8 +1387,8 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream); int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset); /* Get description info */ -const char * get_vgmstream_coding_description(coding_t coding_type); -const char * get_vgmstream_layout_description(layout_t layout_type); -const char * get_vgmstream_meta_description(meta_t meta_type); +void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t out_size); +void get_vgmstream_layout_description(VGMSTREAM *vgmstream, char *out, size_t out_size); +void get_vgmstream_meta_description(VGMSTREAM *vgmstream, char *out, size_t out_size); #endif