Updated VGMStream to r1050-2526-g8b6b5b9e

CQTexperiment
Christopher Snowhill 2019-10-05 00:44:22 -07:00
parent fd7ab684c2
commit 14c6ce431a
122 changed files with 10647 additions and 4933 deletions

View File

@ -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 = "<group>"; };
832BF81A21E0514A006F50F1 /* hca_keys_awb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys_awb.h; sourceTree = "<group>"; };
832BF81B21E0514B006F50F1 /* nus3audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3audio.c; sourceTree = "<group>"; };
83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_xma.c; sourceTree = "<group>"; };
83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = "<group>"; };
83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = "<group>"; };
833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = "<group>"; };
@ -865,7 +880,6 @@
834FE0DE215C79EB000A5D3D /* mib_mih.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mib_mih.c; sourceTree = "<group>"; };
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hd3_bd3.c; sourceTree = "<group>"; };
834FE0E0215C79EB000A5D3D /* idsp_ie.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idsp_ie.c; sourceTree = "<group>"; };
834FE0E1215C79EB000A5D3D /* nub_idsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_idsp.c; sourceTree = "<group>"; };
834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb5_interleave_streamfile.h; sourceTree = "<group>"; };
834FE0E4215C79EC000A5D3D /* vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vag.c; sourceTree = "<group>"; };
834FE0E5215C79EC000A5D3D /* ahv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ahv.c; sourceTree = "<group>"; };
@ -992,7 +1006,6 @@
836F6E8418BDC2180095E648 /* p3d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = p3d.c; sourceTree = "<group>"; };
836F6E8518BDC2180095E648 /* pc_adp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_adp.c; sourceTree = "<group>"; };
836F6E8618BDC2180095E648 /* pc_mxst.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_mxst.c; sourceTree = "<group>"; };
836F6E8818BDC2180095E648 /* pc_snds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_snds.c; sourceTree = "<group>"; };
836F6E8B18BDC2180095E648 /* pona.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pona.c; sourceTree = "<group>"; };
836F6E8C18BDC2180095E648 /* pos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pos.c; sourceTree = "<group>"; };
836F6E8D18BDC2180095E648 /* ps2_2pfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_2pfs.c; sourceTree = "<group>"; };
@ -1014,9 +1027,7 @@
836F6E9F18BDC2180095E648 /* ps2_hsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_hsf.c; sourceTree = "<group>"; };
836F6EA018BDC2180095E648 /* ps2_iab.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_iab.c; sourceTree = "<group>"; };
836F6EA218BDC2180095E648 /* ps2_ild.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ild.c; sourceTree = "<group>"; };
836F6EA318BDC2180095E648 /* ps2_int.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_int.c; sourceTree = "<group>"; };
836F6EA418BDC2180095E648 /* ps2_joe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_joe.c; sourceTree = "<group>"; };
836F6EA518BDC2180095E648 /* ps2_jstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_jstm.c; sourceTree = "<group>"; };
836F6EA618BDC2180095E648 /* ps2_kces.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_kces.c; sourceTree = "<group>"; };
836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = "<group>"; };
836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = "<group>"; };
@ -1053,8 +1064,6 @@
836F6ED518BDC2190095E648 /* ps3_cps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_cps.c; sourceTree = "<group>"; };
836F6ED618BDC2190095E648 /* ps3_ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_ivag.c; sourceTree = "<group>"; };
836F6ED918BDC2190095E648 /* ps3_past.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_past.c; sourceTree = "<group>"; };
836F6EDF18BDC2190095E648 /* psx_gms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psx_gms.c; sourceTree = "<group>"; };
836F6EE118BDC2190095E648 /* raw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw.c; sourceTree = "<group>"; };
836F6EE218BDC2190095E648 /* redspark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = redspark.c; sourceTree = "<group>"; };
836F6EE318BDC2190095E648 /* riff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = riff.c; sourceTree = "<group>"; };
836F6EE418BDC2190095E648 /* rkv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rkv.c; sourceTree = "<group>"; };
@ -1095,9 +1104,6 @@
836F6F0A18BDC2190095E648 /* x360_tra.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_tra.c; sourceTree = "<group>"; };
836F6F0B18BDC2190095E648 /* xbox_hlwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_hlwav.c; sourceTree = "<group>"; };
836F6F0C18BDC2190095E648 /* xbox_ims.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_ims.c; sourceTree = "<group>"; };
836F6F0E18BDC2190095E648 /* xbox_wavm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_wavm.c; sourceTree = "<group>"; };
836F6F0F18BDC2190095E648 /* xbox_xmu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xmu.c; sourceTree = "<group>"; };
836F6F1018BDC2190095E648 /* xbox_xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xvas.c; sourceTree = "<group>"; };
836F6F1218BDC2190095E648 /* xss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xss.c; sourceTree = "<group>"; };
836F6F1318BDC2190095E648 /* xwb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwb.c; sourceTree = "<group>"; };
836F6F1418BDC2190095E648 /* ydsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ydsp.c; sourceTree = "<group>"; };
@ -1120,6 +1126,35 @@
8375737221F9507D00F01AF5 /* oki_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = oki_decoder.c; sourceTree = "<group>"; };
8375737421F950EC00F01AF5 /* gin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gin.c; sourceTree = "<group>"; };
8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_sb_streamfile.h; sourceTree = "<group>"; };
837CEA7523487E2400E62A4A /* ptadpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ptadpcm_decoder.c; sourceTree = "<group>"; };
837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_adpcm_decoder.c; sourceTree = "<group>"; };
837CEA7723487E2400E62A4A /* ffmpeg_decoder_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils.c; sourceTree = "<group>"; };
837CEAD423487E8300E62A4A /* bmp_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bmp_konami.c; sourceTree = "<group>"; };
837CEAD523487E8300E62A4A /* acb_utf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acb_utf.h; sourceTree = "<group>"; };
837CEAD623487E8300E62A4A /* acb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acb.c; sourceTree = "<group>"; };
837CEAD723487E8300E62A4A /* bgw_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bgw_streamfile.h; sourceTree = "<group>"; };
837CEADC23487F2900E62A4A /* xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvas.c; sourceTree = "<group>"; };
837CEADD23487F2A00E62A4A /* raw_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_pcm.c; sourceTree = "<group>"; };
837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mzrt_streamfile.h; sourceTree = "<group>"; };
837CEADF23487F2A00E62A4A /* xa_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_xa30.c; sourceTree = "<group>"; };
837CEAE023487F2A00E62A4A /* ubi_hx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_hx.c; sourceTree = "<group>"; };
837CEAE123487F2A00E62A4A /* mzrt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mzrt.c; sourceTree = "<group>"; };
837CEAE223487F2A00E62A4A /* nub.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub.c; sourceTree = "<group>"; };
837CEAE323487F2A00E62A4A /* xmv_valve.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmv_valve.c; sourceTree = "<group>"; };
837CEAE423487F2A00E62A4A /* xavs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xavs.c; sourceTree = "<group>"; };
837CEAE523487F2B00E62A4A /* xa_04sw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_04sw.c; sourceTree = "<group>"; };
837CEAE623487F2B00E62A4A /* ima.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ima.c; sourceTree = "<group>"; };
837CEAE723487F2B00E62A4A /* raw_wavm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_wavm.c; sourceTree = "<group>"; };
837CEAE823487F2B00E62A4A /* psf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psf.c; sourceTree = "<group>"; };
837CEAE923487F2B00E62A4A /* jstm_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jstm_streamfile.h; sourceTree = "<group>"; };
837CEAEA23487F2B00E62A4A /* raw_snds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_snds.c; sourceTree = "<group>"; };
837CEAEB23487F2B00E62A4A /* smk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smk.c; sourceTree = "<group>"; };
837CEAEC23487F2C00E62A4A /* xmu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmu.c; sourceTree = "<group>"; };
837CEAED23487F2C00E62A4A /* raw_int.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_int.c; sourceTree = "<group>"; };
837CEAEE23487F2C00E62A4A /* xavs_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xavs_streamfile.h; sourceTree = "<group>"; };
837CEAEF23487F2C00E62A4A /* jstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = jstm.c; sourceTree = "<group>"; };
837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqex_sead_streamfile.h; sourceTree = "<group>"; };
837CEB062348809400E62A4A /* seb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = seb.c; sourceTree = "<group>"; };
838BDB611D3AF08C0022CA6F /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = "<group>"; };
838BDB621D3AF08C0022CA6F /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = "<group>"; };
838BDB631D3AF08C0022CA6F /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = "<group>"; };
@ -1169,7 +1204,6 @@
83AA5D201F6E2F9B0020821C /* awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awc.c; sourceTree = "<group>"; };
83AA5D211F6E2F9C0020821C /* hca_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys.h; sourceTree = "<group>"; };
83AA5D231F6E2F9C0020821C /* stm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stm.c; sourceTree = "<group>"; };
83AB8C731E8072A100086084 /* nub_vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_vag.c; sourceTree = "<group>"; };
83AB8C741E8072A100086084 /* x360_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_ast.c; sourceTree = "<group>"; };
83BAFB6B19F45EB3005DAB60 /* bfstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfstm.c; sourceTree = "<group>"; };
83C727FB22BC893800678B4A /* xwb_xsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xwb_xsb.h; sourceTree = "<group>"; };
@ -1196,8 +1230,6 @@
83C7282422BC8C1400678B4A /* mixing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mixing.h; sourceTree = "<group>"; };
83C7282522BC8C1400678B4A /* mixing.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mixing.c; sourceTree = "<group>"; };
83C7282622BC8C1400678B4A /* plugins.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = plugins.c; sourceTree = "<group>"; };
83CAB8DC1F0B0744001BC993 /* pc_xa30.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pc_xa30.c; sourceTree = "<group>"; };
83CAB8E11F0B0745001BC993 /* wii_04sw.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wii_04sw.c; sourceTree = "<group>"; };
83CD42851F787878000F77BE /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswresample.a; path = ../../ThirdParty/ffmpeg/lib/libswresample.a; sourceTree = "<group>"; };
83D730E51A738EB200CA1366 /* g7221.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = g7221.xcodeproj; path = ../g7221/g7221.xcodeproj; sourceTree = "<group>"; };
83D731381A74968900CA1366 /* g719.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = g719.xcodeproj; path = ../g719/g719.xcodeproj; sourceTree = "<group>"; };
@ -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 */,

View File

@ -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;

View File

@ -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);

View File

@ -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 */
/* ******************************************** */

View File

@ -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 */

View File

@ -2,18 +2,22 @@
#include "../streamfile.h"
#include <string.h>
#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);
}

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int i;
int32_t sample_count = 0;
for (i=first_sample; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int8_t sample_byte = read_8bit(stream->offset+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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
for (i=first_sample; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int8_t sample_byte = read_8bit(stream->offset+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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int sample_nibble, sample_decoded, sample_delta;
for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = is_stereo ?
stream->offset + 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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int sample_nibble, sample_decoded, sample_delta;
for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = (channelspacing == 2) ?
(stream->offset + 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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int sample_nibble, sample_decoded, sample_delta;
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int code, delta, sample;
off_t byte_offset = (stream->offset + 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;

View File

@ -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);
}

View File

@ -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));
}

View File

@ -152,6 +152,7 @@ void free_layout_layered(layered_layout_data *data) {
}
free(data->layers);
}
free(data->buffer);
free(data);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -1,3 +1,4 @@
#include <math.h>
#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;

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;i<channel_count;i++) {
vgmstream->ch[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;
}

View File

@ -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);
}

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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))

View File

@ -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*/

View File

@ -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;

View File

@ -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;i<channel_count;i++) {
vgmstream->ch[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;i<channel_count;i++) {
vgmstream->ch[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;
}

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;i<channel_count;i++) {
vgmstream->ch[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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;i<channel_count;i++) {
vgmstream->ch[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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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:

File diff suppressed because it is too large Load Diff

View File

@ -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 */
}
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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" */

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;i<channel_count;i++) {
vgmstream->ch[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;
}

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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 = &current->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:

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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 */

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More