Updated VGMStream to r1050-2526-g8b6b5b9e
This commit is contained in:
parent
fd7ab684c2
commit
14c6ce431a
122 changed files with 10647 additions and 4933 deletions
|
@ -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 */,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
/* ******************************************** */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
189
Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c
Normal file
189
Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
98
Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c
Normal file
98
Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
588
Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c
Normal file
588
Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -152,6 +152,7 @@ void free_layout_layered(layered_layout_data *data) {
|
|||
}
|
||||
free(data->layers);
|
||||
}
|
||||
free(data->buffer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
585
Frameworks/vgmstream/vgmstream/src/meta/acb.c
Normal file
585
Frameworks/vgmstream/vgmstream/src/meta/acb.c
Normal 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);
|
||||
}
|
423
Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h
Normal file
423
Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h
Normal 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_ */
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
66
Frameworks/vgmstream/vgmstream/src/meta/bgw_streamfile.h
Normal file
66
Frameworks/vgmstream/vgmstream/src/meta/bgw_streamfile.h
Normal 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_ */
|
45
Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c
Normal file
45
Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c
Normal 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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
52
Frameworks/vgmstream/vgmstream/src/meta/ima.c
Normal file
52
Frameworks/vgmstream/vgmstream/src/meta/ima.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
51
Frameworks/vgmstream/vgmstream/src/meta/jstm_streamfile.h
Normal file
51
Frameworks/vgmstream/vgmstream/src/meta/jstm_streamfile.h
Normal 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_ */
|
|
@ -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))
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
144
Frameworks/vgmstream/vgmstream/src/meta/mzrt.c
Normal file
144
Frameworks/vgmstream/vgmstream/src/meta/mzrt.c
Normal 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;
|
||||
}
|
124
Frameworks/vgmstream/vgmstream/src/meta/mzrt_streamfile.h
Normal file
124
Frameworks/vgmstream/vgmstream/src/meta/mzrt_streamfile.h
Normal 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_ */
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
557
Frameworks/vgmstream/vgmstream/src/meta/nub.c
Normal file
557
Frameworks/vgmstream/vgmstream/src/meta/nub.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
760
Frameworks/vgmstream/vgmstream/src/meta/psf.c
Normal file
760
Frameworks/vgmstream/vgmstream/src/meta/psf.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
38
Frameworks/vgmstream/vgmstream/src/meta/raw_pcm.c
Normal file
38
Frameworks/vgmstream/vgmstream/src/meta/raw_pcm.c
Normal 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;
|
||||
}
|
51
Frameworks/vgmstream/vgmstream/src/meta/raw_snds.c
Normal file
51
Frameworks/vgmstream/vgmstream/src/meta/raw_snds.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
|
@ -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
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
53
Frameworks/vgmstream/vgmstream/src/meta/seb.c
Normal file
53
Frameworks/vgmstream/vgmstream/src/meta/seb.c
Normal 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;
|
||||
}
|
|
@ -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" */
|
||||
|
|
|
@ -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
|
||||
|
|
187
Frameworks/vgmstream/vgmstream/src/meta/smk.c
Normal file
187
Frameworks/vgmstream/vgmstream/src/meta/smk.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
|
||||
|
||||
#define TXTP_LINE_MAX 1024
|
||||
#define TXTP_MIXING_MAX 128
|
||||
#define TXTP_MIXING_MAX 512
|
||||
#define TXTP_GROUP_MODE_SEGMENTED 'S'
|
||||
#define TXTP_GROUP_MODE_LAYERED 'L'
|
||||
#define TXTP_GROUP_REPEAT 'R'
|
||||
#define TXTP_POSITION_LOOPS 'L'
|
||||
|
||||
/* mixing info */
|
||||
typedef enum {
|
||||
|
@ -27,6 +28,7 @@ typedef enum {
|
|||
MACRO_LAYER,
|
||||
MACRO_CROSSTRACK,
|
||||
MACRO_CROSSLAYER,
|
||||
MACRO_DOWNMIX,
|
||||
|
||||
} txtp_mix_t;
|
||||
|
||||
|
@ -49,6 +51,8 @@ typedef struct {
|
|||
double time_start;
|
||||
double time_end;
|
||||
double time_post;
|
||||
double position;
|
||||
char position_type;
|
||||
|
||||
/* macros */
|
||||
int max;
|
||||
|
@ -68,21 +72,29 @@ typedef struct {
|
|||
int mixing_count;
|
||||
txtp_mix_data mixing[TXTP_MIXING_MAX];
|
||||
|
||||
int config_loop_count_set;
|
||||
double config_loop_count;
|
||||
int config_fade_time_set;
|
||||
double config_fade_time;
|
||||
int config_fade_delay_set;
|
||||
double config_fade_delay;
|
||||
int config_ignore_loop;
|
||||
int config_force_loop;
|
||||
int config_ignore_fade;
|
||||
|
||||
int sample_rate;
|
||||
int loop_install;
|
||||
|
||||
int loop_install_set;
|
||||
int loop_end_max;
|
||||
double loop_start_second;
|
||||
int32_t loop_start_sample;
|
||||
double loop_end_second;
|
||||
int32_t loop_end_sample;
|
||||
|
||||
int trim_set;
|
||||
double trim_second;
|
||||
int32_t trim_sample;
|
||||
|
||||
} txtp_entry;
|
||||
|
||||
|
||||
|
@ -419,18 +431,23 @@ fail:
|
|||
|
||||
static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
|
||||
|
||||
vgmstream->config_loop_count = current->config_loop_count;
|
||||
vgmstream->config_fade_time = current->config_fade_time;
|
||||
vgmstream->config_fade_delay = current->config_fade_delay;
|
||||
vgmstream->config_ignore_loop = current->config_ignore_loop;
|
||||
vgmstream->config_force_loop = current->config_force_loop;
|
||||
vgmstream->config_ignore_fade = current->config_ignore_fade;
|
||||
if (current->config_loop_count_set)
|
||||
vgmstream->config_loop_count = current->config_loop_count;
|
||||
if (current->config_fade_time_set)
|
||||
vgmstream->config_fade_time = current->config_fade_time;
|
||||
if (current->config_fade_delay_set)
|
||||
vgmstream->config_fade_delay = current->config_fade_delay;
|
||||
if (current->config_ignore_loop)
|
||||
vgmstream->config_ignore_loop = current->config_ignore_loop;
|
||||
if (current->config_force_loop)
|
||||
vgmstream->config_force_loop = current->config_force_loop;
|
||||
if (current->config_ignore_fade)
|
||||
vgmstream->config_ignore_fade = current->config_ignore_fade;
|
||||
|
||||
if (current->sample_rate > 0) {
|
||||
if (current->sample_rate > 0)
|
||||
vgmstream->sample_rate = current->sample_rate;
|
||||
}
|
||||
|
||||
if (current->loop_install) {
|
||||
if (current->loop_install_set) {
|
||||
if (current->loop_start_second > 0 || current->loop_end_second > 0) {
|
||||
current->loop_start_sample = current->loop_start_second * vgmstream->sample_rate;
|
||||
current->loop_end_sample = current->loop_end_second * vgmstream->sample_rate;
|
||||
|
@ -443,9 +460,27 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
|
|||
current->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
vgmstream_force_loop(vgmstream, current->loop_install, current->loop_start_sample, current->loop_end_sample);
|
||||
vgmstream_force_loop(vgmstream, current->loop_install_set, current->loop_start_sample, current->loop_end_sample);
|
||||
}
|
||||
|
||||
if (current->trim_set) {
|
||||
if (current->trim_second != 0.0) {
|
||||
current->trim_sample = current->trim_second * vgmstream->sample_rate;
|
||||
}
|
||||
|
||||
if (current->trim_sample < 0) {
|
||||
vgmstream->num_samples += current->trim_sample; /* trim from end (add negative) */
|
||||
}
|
||||
else if (vgmstream->num_samples > current->trim_sample) {
|
||||
vgmstream->num_samples = current->trim_sample; /* trim to value */
|
||||
}
|
||||
|
||||
/* readjust after triming if it went over (could check for more edge cases but eh) */
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples)
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
|
||||
/* add macro to mixing list */
|
||||
if (current->channel_mask) {
|
||||
int ch;
|
||||
|
@ -461,43 +496,57 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
|
|||
|
||||
/* copy mixing list (should be done last as some mixes depend on config) */
|
||||
if (current->mixing_count > 0) {
|
||||
int m;
|
||||
int m, position_samples;
|
||||
|
||||
for (m = 0; m < current->mixing_count; m++) {
|
||||
txtp_mix_data mix = current->mixing[m];
|
||||
txtp_mix_data *mix = ¤t->mixing[m];
|
||||
|
||||
switch(mix.command) {
|
||||
switch(mix->command) {
|
||||
/* base mixes */
|
||||
case MIX_SWAP: mixing_push_swap(vgmstream, mix.ch_dst, mix.ch_src); break;
|
||||
case MIX_ADD: mixing_push_add(vgmstream, mix.ch_dst, mix.ch_src, 1.0); break;
|
||||
case MIX_ADD_VOLUME: mixing_push_add(vgmstream, mix.ch_dst, mix.ch_src, mix.vol); break;
|
||||
case MIX_VOLUME: mixing_push_volume(vgmstream, mix.ch_dst, mix.vol); break;
|
||||
case MIX_LIMIT: mixing_push_limit(vgmstream, mix.ch_dst, mix.vol); break;
|
||||
case MIX_UPMIX: mixing_push_upmix(vgmstream, mix.ch_dst); break;
|
||||
case MIX_DOWNMIX: mixing_push_downmix(vgmstream, mix.ch_dst); break;
|
||||
case MIX_KILLMIX: mixing_push_killmix(vgmstream, mix.ch_dst); break;
|
||||
case MIX_SWAP: mixing_push_swap(vgmstream, mix->ch_dst, mix->ch_src); break;
|
||||
case MIX_ADD: mixing_push_add(vgmstream, mix->ch_dst, mix->ch_src, 1.0); break;
|
||||
case MIX_ADD_VOLUME: mixing_push_add(vgmstream, mix->ch_dst, mix->ch_src, mix->vol); break;
|
||||
case MIX_VOLUME: mixing_push_volume(vgmstream, mix->ch_dst, mix->vol); break;
|
||||
case MIX_LIMIT: mixing_push_limit(vgmstream, mix->ch_dst, mix->vol); break;
|
||||
case MIX_UPMIX: mixing_push_upmix(vgmstream, mix->ch_dst); break;
|
||||
case MIX_DOWNMIX: mixing_push_downmix(vgmstream, mix->ch_dst); break;
|
||||
case MIX_KILLMIX: mixing_push_killmix(vgmstream, mix->ch_dst); break;
|
||||
case MIX_FADE:
|
||||
/* Convert from time to samples now that sample rate is final.
|
||||
* Samples and time values may be mixed though, so it's done for every
|
||||
* value (if one is 0 the other will be too, though) */
|
||||
if (mix.time_pre > 0.0) mix.sample_pre = mix.time_pre * vgmstream->sample_rate;
|
||||
if (mix.time_start > 0.0) mix.sample_start = mix.time_start * vgmstream->sample_rate;
|
||||
if (mix.time_end > 0.0) mix.sample_end = mix.time_end * vgmstream->sample_rate;
|
||||
if (mix.time_post > 0.0) mix.sample_post = mix.time_post * vgmstream->sample_rate;
|
||||
if (mix->time_pre > 0.0) mix->sample_pre = mix->time_pre * vgmstream->sample_rate;
|
||||
if (mix->time_start > 0.0) mix->sample_start = mix->time_start * vgmstream->sample_rate;
|
||||
if (mix->time_end > 0.0) mix->sample_end = mix->time_end * vgmstream->sample_rate;
|
||||
if (mix->time_post > 0.0) mix->sample_post = mix->time_post * vgmstream->sample_rate;
|
||||
/* convert special meaning too */
|
||||
if (mix.time_pre < 0.0) mix.sample_pre = -1;
|
||||
if (mix.time_post < 0.0) mix.sample_post = -1;
|
||||
if (mix->time_pre < 0.0) mix->sample_pre = -1;
|
||||
if (mix->time_post < 0.0) mix->sample_post = -1;
|
||||
|
||||
mixing_push_fade(vgmstream, mix.ch_dst, mix.vol_start, mix.vol_end, mix.shape,
|
||||
mix.sample_pre, mix.sample_start, mix.sample_end, mix.sample_post);
|
||||
if (mix->position_type == TXTP_POSITION_LOOPS && vgmstream->loop_flag) {
|
||||
int loop_pre = vgmstream->loop_start_sample;
|
||||
int loop_samples = (vgmstream->loop_end_sample - vgmstream->loop_start_sample);
|
||||
|
||||
position_samples = loop_pre + loop_samples * mix->position;
|
||||
|
||||
if (mix->sample_pre >= 0) mix->sample_pre += position_samples;
|
||||
mix->sample_start += position_samples;
|
||||
mix->sample_end += position_samples;
|
||||
if (mix->sample_post >= 0) mix->sample_post += position_samples;
|
||||
}
|
||||
|
||||
|
||||
mixing_push_fade(vgmstream, mix->ch_dst, mix->vol_start, mix->vol_end, mix->shape,
|
||||
mix->sample_pre, mix->sample_start, mix->sample_end, mix->sample_post);
|
||||
break;
|
||||
|
||||
/* macro mixes */
|
||||
case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix.vol, mix.mask); break;
|
||||
case MACRO_TRACK: mixing_macro_track(vgmstream, mix.mask); break;
|
||||
case MACRO_LAYER: mixing_macro_layer(vgmstream, mix.max, mix.mask, mix.mode); break;
|
||||
case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix.max); break;
|
||||
case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix.max, mix.mode); break;
|
||||
case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix->vol, mix->mask); break;
|
||||
case MACRO_TRACK: mixing_macro_track(vgmstream, mix->mask); break;
|
||||
case MACRO_LAYER: mixing_macro_layer(vgmstream, mix->max, mix->mask, mix->mode); break;
|
||||
case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix->max); break;
|
||||
case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix->max, mix->mode); break;
|
||||
case MACRO_DOWNMIX: mixing_macro_downmix(vgmstream, mix->max); break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -543,14 +592,17 @@ static void clean_filename(char * filename) {
|
|||
* - %n: special match (not counted in return value), chars consumed until that point (can appear and be set multiple times)
|
||||
*/
|
||||
|
||||
static int get_double(const char * config, double *value) {
|
||||
static int get_double(const char * config, double *value, int *is_set) {
|
||||
int n, m;
|
||||
double temp;
|
||||
|
||||
if (is_set) *is_set = 0;
|
||||
|
||||
m = sscanf(config, " %lf%n", &temp,&n);
|
||||
if (m != 1 || temp < 0)
|
||||
return 0;
|
||||
|
||||
if (is_set) *is_set = 1;
|
||||
*value = temp;
|
||||
return n;
|
||||
}
|
||||
|
@ -567,6 +619,25 @@ static int get_int(const char * config, int *value) {
|
|||
return n;
|
||||
}
|
||||
|
||||
static int get_position(const char * config, double *value_f, char *value_type) {
|
||||
int n,m;
|
||||
double temp_f;
|
||||
char temp_c;
|
||||
|
||||
/* test if format is position: N.n(type) */
|
||||
m = sscanf(config, " %lf%c%n", &temp_f,&temp_c,&n);
|
||||
if (m != 2 || temp_f < 0.0)
|
||||
return 0;
|
||||
/* test accepted chars as it will capture anything */
|
||||
if (temp_c != TXTP_POSITION_LOOPS)
|
||||
return 0;
|
||||
|
||||
*value_f = temp_f;
|
||||
*value_type = temp_c;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static int get_time(const char * config, double *value_f, int32_t *value_i) {
|
||||
int n,m;
|
||||
int temp_i1, temp_i2;
|
||||
|
@ -577,7 +648,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
|
|||
m = sscanf(config, " %i%c%i%n", &temp_i1,&temp_c,&temp_i2,&n);
|
||||
if (m == 3 && (temp_c == ':' || temp_c == '_')) {
|
||||
m = sscanf(config, " %lf%c%lf%n", &temp_f1,&temp_c,&temp_f2,&n);
|
||||
if (m != 3 || temp_f1 < 0.0 || temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0)
|
||||
if (m != 3 || /*temp_f1 < 0.0 ||*/ temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0)
|
||||
return 0;
|
||||
|
||||
*value_f = temp_f1 * 60.0 + temp_f2;
|
||||
|
@ -588,7 +659,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
|
|||
m = sscanf(config, " %i.%i%n", &temp_i1,&temp_i2,&n);
|
||||
if (m == 2) {
|
||||
m = sscanf(config, " %lf%n", &temp_f1,&n);
|
||||
if (m != 1 || temp_f1 < 0.0)
|
||||
if (m != 1 /*|| temp_f1 < 0.0*/)
|
||||
return 0;
|
||||
*value_f = temp_f1;
|
||||
return n;
|
||||
|
@ -685,14 +756,14 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
|
|||
char type, separator;
|
||||
|
||||
m = sscanf(config, " %d %c%n", &mix->ch_dst, &type, &n);
|
||||
if (n == 0 || m != 2) goto fail;
|
||||
if (m != 2 || n == 0) goto fail;
|
||||
config += n;
|
||||
tn += n;
|
||||
|
||||
if (type == '^') {
|
||||
/* full definition */
|
||||
m = sscanf(config, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n);
|
||||
if (n == 0 || m != 3) goto fail;
|
||||
if (m != 3 || n == 0) goto fail;
|
||||
config += n;
|
||||
tn += n;
|
||||
|
||||
|
@ -702,7 +773,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
|
|||
tn += n;
|
||||
|
||||
m = sscanf(config, " %c%n", &separator, &n);
|
||||
if (n == 0 || m != 1 || separator != '~') goto fail;
|
||||
if ( m != 1 || n == 0 || separator != '~') goto fail;
|
||||
config += n;
|
||||
tn += n;
|
||||
|
||||
|
@ -712,7 +783,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
|
|||
tn += n;
|
||||
|
||||
m = sscanf(config, " %c%n", &separator, &n);
|
||||
if (n == 0 || m != 1 || separator != '+') goto fail;
|
||||
if (m != 1 || n == 0 || separator != '+') goto fail;
|
||||
config += n;
|
||||
tn += n;
|
||||
|
||||
|
@ -722,7 +793,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
|
|||
tn += n;
|
||||
|
||||
m = sscanf(config, " %c%n", &separator, &n);
|
||||
if (n == 0 || m != 1 || separator != '~') goto fail;
|
||||
if (m != 1 || n == 0 || separator != '~') goto fail;
|
||||
config += n;
|
||||
tn += n;
|
||||
|
||||
|
@ -750,13 +821,18 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
|
|||
mix->time_pre = -1.0;
|
||||
mix->sample_pre = -1;
|
||||
|
||||
n = get_position(config, &mix->position, &mix->position_type);
|
||||
//if (n == 0) goto fail; /* optional */
|
||||
config += n;
|
||||
tn += n;
|
||||
|
||||
n = get_time(config, &mix->time_start, &mix->sample_start);
|
||||
if (n == 0) goto fail;
|
||||
config += n;
|
||||
tn += n;
|
||||
|
||||
m = sscanf(config, " %c%n", &separator, &n);
|
||||
if (n == 0 || m != 1 || separator != '+') goto fail;
|
||||
if (m != 1 || n == 0 || separator != '+') goto fail;
|
||||
config += n;
|
||||
tn += n;
|
||||
|
||||
|
@ -783,8 +859,8 @@ void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) {
|
|||
return;
|
||||
}
|
||||
|
||||
/* parsers reads ch1 = first, but for mixing code ch0 = first
|
||||
* (if parser reads ch0 here it'll become -1 with special meaning in code) */
|
||||
/* parser reads ch1 = first, but for mixing code ch0 = first
|
||||
* (if parser reads ch0 here it'll become -1 with meaning of "all channels" in mixing code) */
|
||||
mix->ch_dst--;
|
||||
mix->ch_src--;
|
||||
mix->command = command;
|
||||
|
@ -795,14 +871,19 @@ void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) {
|
|||
|
||||
|
||||
static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filename) {
|
||||
|
||||
/* don't memcopy to allow list additions and ignore values not set,
|
||||
* as current can be "default" config */
|
||||
//*current = *cfg;
|
||||
|
||||
if (filename)
|
||||
strcpy(current->filename, filename);
|
||||
|
||||
current->subsong = cfg->subsong;
|
||||
if (cfg->subsong)
|
||||
current->subsong = cfg->subsong;
|
||||
|
||||
current->channel_mask = cfg->channel_mask;
|
||||
|
||||
//*current = *cfg; /* don't memcopy to allow list additions */ //todo save list first then memcpy
|
||||
if (cfg->channel_mask)
|
||||
current->channel_mask = cfg->channel_mask;
|
||||
|
||||
if (cfg->mixing_count > 0) {
|
||||
int i;
|
||||
|
@ -812,20 +893,46 @@ static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filenam
|
|||
}
|
||||
}
|
||||
|
||||
current->config_loop_count = cfg->config_loop_count;
|
||||
current->config_fade_time = cfg->config_fade_time;
|
||||
current->config_fade_delay = cfg->config_fade_delay;
|
||||
current->config_ignore_loop = cfg->config_ignore_loop;
|
||||
current->config_force_loop = cfg->config_force_loop;
|
||||
current->config_ignore_fade = cfg->config_ignore_fade;
|
||||
if (cfg->config_loop_count_set) {
|
||||
current->config_loop_count_set = cfg->config_loop_count_set;
|
||||
current->config_loop_count = cfg->config_loop_count;
|
||||
}
|
||||
if (cfg->config_fade_time_set) {
|
||||
current->config_fade_time_set = cfg->config_fade_time_set;
|
||||
current->config_fade_time = cfg->config_fade_time;
|
||||
}
|
||||
if (cfg->config_fade_delay_set) {
|
||||
current->config_fade_delay_set = cfg->config_fade_delay_set;
|
||||
current->config_fade_delay = cfg->config_fade_delay;
|
||||
}
|
||||
if (cfg->config_ignore_loop) {
|
||||
current->config_ignore_loop = cfg->config_ignore_loop;
|
||||
}
|
||||
if (cfg->config_force_loop) {
|
||||
current->config_force_loop = cfg->config_force_loop;
|
||||
}
|
||||
if (cfg->config_ignore_fade) {
|
||||
current->config_ignore_fade = cfg->config_ignore_fade;
|
||||
}
|
||||
|
||||
current->sample_rate = cfg->sample_rate;
|
||||
current->loop_install = cfg->loop_install;
|
||||
current->loop_end_max = cfg->loop_end_max;
|
||||
current->loop_start_sample = cfg->loop_start_sample;
|
||||
current->loop_start_second = cfg->loop_start_second;
|
||||
current->loop_end_sample = cfg->loop_end_sample;
|
||||
current->loop_end_second = cfg->loop_end_second;
|
||||
if (cfg->sample_rate > 0) {
|
||||
current->sample_rate = cfg->sample_rate;
|
||||
}
|
||||
|
||||
if (cfg->loop_install_set) {
|
||||
current->loop_install_set = cfg->loop_install_set;
|
||||
current->loop_end_max = cfg->loop_end_max;
|
||||
current->loop_start_sample = cfg->loop_start_sample;
|
||||
current->loop_start_second = cfg->loop_start_second;
|
||||
current->loop_end_sample = cfg->loop_end_sample;
|
||||
current->loop_end_second = cfg->loop_end_second;
|
||||
}
|
||||
|
||||
if (cfg->trim_set) {
|
||||
current->trim_set = cfg->trim_set;
|
||||
current->trim_second = cfg->trim_second;
|
||||
current->trim_sample = cfg->trim_sample;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_config(txtp_entry *cfg, char *config) {
|
||||
|
@ -980,15 +1087,15 @@ static void parse_config(txtp_entry *cfg, char *config) {
|
|||
//;VGM_LOG("TXTP: ignore_fade=%i\n", cfg->config_ignore_fade);
|
||||
}
|
||||
else if (strcmp(command,"l") == 0) {
|
||||
config += get_double(config, &cfg->config_loop_count);
|
||||
config += get_double(config, &cfg->config_loop_count, &cfg->config_loop_count_set);
|
||||
//;VGM_LOG("TXTP: loop_count=%f\n", cfg->config_loop_count);
|
||||
}
|
||||
else if (strcmp(command,"f") == 0) {
|
||||
config += get_double(config, &cfg->config_fade_time);
|
||||
config += get_double(config, &cfg->config_fade_time, &cfg->config_fade_time_set);
|
||||
//;VGM_LOG("TXTP: fade_time=%f\n", cfg->config_fade_time);
|
||||
}
|
||||
else if (strcmp(command,"d") == 0) {
|
||||
config += get_double(config, &cfg->config_fade_delay);
|
||||
config += get_double(config, &cfg->config_fade_delay, &cfg->config_fade_delay_set);
|
||||
//;VGM_LOG("TXTP: fade_delay %f\n", cfg->config_fade_delay);
|
||||
}
|
||||
else if (strcmp(command,"h") == 0) {
|
||||
|
@ -1006,17 +1113,22 @@ static void parse_config(txtp_entry *cfg, char *config) {
|
|||
}
|
||||
|
||||
config += n;
|
||||
cfg->loop_install = 1;
|
||||
cfg->loop_install_set = 1;
|
||||
}
|
||||
|
||||
//;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg->loop_install, cfg->loop_end_max,
|
||||
// cfg->loop_start_sample, cfg->loop_end_sample, cfg->loop_start_second, cfg->loop_end_second);
|
||||
}
|
||||
else if (strcmp(command,"t") == 0) {
|
||||
n = get_time(config, &cfg->trim_second, &cfg->trim_sample);
|
||||
cfg->trim_set = (n > 0);
|
||||
//;VGM_LOG("TXTP: trim %i - %f / %i\n", cfg->trim_set, cfg->trim_second, cfg->trim_sample);
|
||||
}
|
||||
//todo cleanup
|
||||
else if (strcmp(command,"@volume") == 0) {
|
||||
txtp_mix_data mix = {0};
|
||||
|
||||
nm = get_double(config, &mix.vol);
|
||||
nm = get_double(config, &mix.vol, NULL);
|
||||
config += nm;
|
||||
|
||||
if (nm == 0) continue;
|
||||
|
@ -1071,6 +1183,16 @@ static void parse_config(txtp_entry *cfg, char *config) {
|
|||
|
||||
add_mixing(cfg, &mix, type);
|
||||
}
|
||||
else if (strcmp(command,"@downmix") == 0) {
|
||||
txtp_mix_data mix = {0};
|
||||
|
||||
mix.max = 2; /* stereo only for now */
|
||||
//nm = get_int(config, &mix.max);
|
||||
//config += nm;
|
||||
//if (nm == 0) continue;
|
||||
|
||||
add_mixing(cfg, &mix, MACRO_DOWNMIX);
|
||||
}
|
||||
else if (config[nc] == ' ') {
|
||||
//;VGM_LOG("TXTP: comment\n");
|
||||
break; /* comment, ignore rest */
|
||||
|
@ -1305,29 +1427,12 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) {
|
|||
txtp->is_segmented = 1;
|
||||
|
||||
|
||||
/* empty file: use filename with config (ex. "song.ext#3.txtp") */
|
||||
if (get_streamfile_size(streamFile) == 0) {
|
||||
char filename[PATH_LIMIT] = {0};
|
||||
char* ext;
|
||||
get_streamfile_filename(streamFile, filename,PATH_LIMIT);
|
||||
|
||||
/* remove ".txtp" */
|
||||
ext = strrchr(filename,'.');
|
||||
if (!ext) goto fail; /* ??? */
|
||||
ext[0] = '\0';
|
||||
|
||||
if (!add_entry(txtp, filename, 0))
|
||||
goto fail;
|
||||
|
||||
return txtp;
|
||||
}
|
||||
|
||||
|
||||
/* skip BOM if needed */
|
||||
if ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF)
|
||||
if (file_size > 0 &&
|
||||
((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF))
|
||||
txt_offset = 0x02;
|
||||
|
||||
/* normal file: read and parse lines */
|
||||
/* read and parse lines */
|
||||
while (txt_offset < file_size) {
|
||||
char line[TXTP_LINE_MAX] = {0};
|
||||
char key[TXTP_LINE_MAX] = {0}, val[TXTP_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */
|
||||
|
@ -1359,6 +1464,16 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* mini-txth: if no entries are set try with filename, ex. from "song.ext#3.txtp" use "song.ext#3"
|
||||
* (it's possible to have default "commands" inside the .txtp plus filename+config) */
|
||||
if (txtp->entry_count == 0) {
|
||||
char filename[PATH_LIMIT] = {0};
|
||||
|
||||
get_streamfile_basename(streamFile, filename, sizeof(filename));
|
||||
|
||||
add_entry(txtp, filename, 0);
|
||||
}
|
||||
|
||||
|
||||
return txtp;
|
||||
fail:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
660
Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c
Normal file
660
Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue