Updated VGMStream to r1050-670-g165cda22.

CQTexperiment
Christopher Snowhill 2017-09-16 21:24:57 -07:00
parent 1a084b43c0
commit 9c80799aea
64 changed files with 3673 additions and 1277 deletions

View File

@ -25,7 +25,6 @@
831BA6201EAC61A500CF89B0 /* x360_nub.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6161EAC61A500CF89B0 /* x360_nub.c */; };
831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6171EAC61A500CF89B0 /* x360_pasx.c */; };
831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6221EAC61CB00CF89B0 /* coding_utils.c */; };
831BA62D1EAC61CB00CF89B0 /* wwise_vorbis_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 831BA6271EAC61CB00CF89B0 /* wwise_vorbis_utils.h */; };
8323894A1D22419B00482226 /* clHCA.c in Sources */ = {isa = PBXBuildFile; fileRef = 832389481D22419B00482226 /* clHCA.c */; };
8323894B1D22419B00482226 /* clHCA.h in Headers */ = {isa = PBXBuildFile; fileRef = 832389491D22419B00482226 /* clHCA.h */; settings = {ATTRIBUTES = (Public, ); }; };
832389501D2246C300482226 /* hca.c in Sources */ = {isa = PBXBuildFile; fileRef = 8323894F1D2246C300482226 /* hca.c */; };
@ -46,7 +45,6 @@
836F6F2118BDC2190095E648 /* aica_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE318BDC2180095E648 /* aica_decoder.c */; };
836F6F2218BDC2190095E648 /* at3_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE418BDC2180095E648 /* at3_decoder.c */; };
836F6F2318BDC2190095E648 /* coding.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6DE518BDC2180095E648 /* coding.h */; };
836F6F2418BDC2190095E648 /* ea_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE618BDC2180095E648 /* ea_decoder.c */; };
836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE718BDC2180095E648 /* g721_decoder.c */; };
836F6F2618BDC2190095E648 /* g7221_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE818BDC2180095E648 /* g7221_decoder.c */; };
836F6F2718BDC2190095E648 /* g72x_state.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6DE918BDC2180095E648 /* g72x_state.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -249,7 +247,6 @@
836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBE18BDC2190095E648 /* ps2_spm.c */; };
836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBF18BDC2190095E648 /* ps2_sps.c */; };
836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC018BDC2190095E648 /* ps2_ster.c */; };
836F6FFD18BDC2190095E648 /* ps2_stm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC118BDC2190095E648 /* ps2_stm.c */; };
836F6FFE18BDC2190095E648 /* ps2_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC218BDC2190095E648 /* ps2_str.c */; };
836F6FFF18BDC2190095E648 /* ps2_strlr.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC318BDC2190095E648 /* ps2_strlr.c */; };
836F700018BDC2190095E648 /* ps2_svag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC418BDC2190095E648 /* ps2_svag.c */; };
@ -322,7 +319,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 */; };
836F704918BDC2190095E648 /* xbox_stma.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0D18BDC2190095E648 /* xbox_stma.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 */; };
@ -379,6 +375,17 @@
83A3F0751E3AD8B900D6A794 /* formats.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A3F0721E3AD8B900D6A794 /* formats.h */; settings = {ATTRIBUTES = (Public, ); }; };
83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */; };
83A5F75F198DF021009AF94C /* bfwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A5F75E198DF021009AF94C /* bfwav.c */; };
83AA5D161F6E2F600020821C /* ea_xa_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */; };
83AA5D171F6E2F600020821C /* mpeg_custom_utils_ealayer3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */; };
83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */; };
83AA5D191F6E2F600020821C /* ea_xas_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */; };
83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */; };
83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */; };
83AA5D1F1F6E2F800020821C /* ea_sns_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D1C1F6E2F7F0020821C /* ea_sns_blocked.c */; };
83AA5D241F6E2F9C0020821C /* awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D201F6E2F9B0020821C /* awc.c */; };
83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA5D211F6E2F9C0020821C /* hca_keys.h */; };
83AA5D261F6E2F9C0020821C /* ea_snu.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D221F6E2F9C0020821C /* ea_snu.c */; };
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 */; };
@ -516,7 +523,6 @@
831BA6161EAC61A500CF89B0 /* x360_nub.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_nub.c; sourceTree = "<group>"; };
831BA6171EAC61A500CF89B0 /* x360_pasx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_pasx.c; sourceTree = "<group>"; };
831BA6221EAC61CB00CF89B0 /* coding_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coding_utils.c; sourceTree = "<group>"; };
831BA6271EAC61CB00CF89B0 /* wwise_vorbis_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wwise_vorbis_utils.h; sourceTree = "<group>"; };
831BD11F1EEE1CF200198540 /* ngc_ulw.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ngc_ulw.c; sourceTree = "<group>"; };
831BD1201EEE1D2A00198540 /* rws_blocked.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rws_blocked.c; sourceTree = "<group>"; };
832389481D22419B00482226 /* clHCA.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = clHCA.c; sourceTree = "<group>"; };
@ -540,7 +546,6 @@
836F6DE318BDC2180095E648 /* aica_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aica_decoder.c; sourceTree = "<group>"; };
836F6DE418BDC2180095E648 /* at3_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = at3_decoder.c; sourceTree = "<group>"; };
836F6DE518BDC2180095E648 /* coding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding.h; sourceTree = "<group>"; };
836F6DE618BDC2180095E648 /* ea_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_decoder.c; sourceTree = "<group>"; };
836F6DE718BDC2180095E648 /* g721_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g721_decoder.c; sourceTree = "<group>"; };
836F6DE818BDC2180095E648 /* g7221_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g7221_decoder.c; sourceTree = "<group>"; };
836F6DE918BDC2180095E648 /* g72x_state.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = g72x_state.h; sourceTree = "<group>"; };
@ -743,7 +748,6 @@
836F6EBE18BDC2190095E648 /* ps2_spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_spm.c; sourceTree = "<group>"; };
836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = "<group>"; };
836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = "<group>"; };
836F6EC118BDC2190095E648 /* ps2_stm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_stm.c; sourceTree = "<group>"; };
836F6EC218BDC2190095E648 /* ps2_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_str.c; sourceTree = "<group>"; };
836F6EC318BDC2190095E648 /* ps2_strlr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_strlr.c; sourceTree = "<group>"; };
836F6EC418BDC2190095E648 /* ps2_svag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag.c; sourceTree = "<group>"; };
@ -816,7 +820,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>"; };
836F6F0D18BDC2190095E648 /* xbox_stma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_stma.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>"; };
@ -871,6 +874,17 @@
83A3F0721E3AD8B900D6A794 /* formats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = formats.h; sourceTree = "<group>"; };
83A3F0731E3AD8B900D6A794 /* stack_alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_alloc.h; sourceTree = "<group>"; };
83A5F75E198DF021009AF94C /* bfwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfwav.c; sourceTree = "<group>"; };
83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_xa_decoder.c; sourceTree = "<group>"; };
83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_ealayer3.c; sourceTree = "<group>"; };
83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_awc.c; sourceTree = "<group>"; };
83AA5D151F6E2F600020821C /* ea_xas_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_xas_decoder.c; sourceTree = "<group>"; };
83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vgs.c; sourceTree = "<group>"; };
83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_awc.c; sourceTree = "<group>"; };
83AA5D1C1F6E2F7F0020821C /* ea_sns_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_sns_blocked.c; sourceTree = "<group>"; };
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>"; };
83AA5D221F6E2F9C0020821C /* ea_snu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_snu.c; 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>"; };
@ -1024,6 +1038,10 @@
836F6DDF18BDC2180095E648 /* coding */ = {
isa = PBXGroup;
children = (
83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */,
83AA5D151F6E2F600020821C /* ea_xas_decoder.c */,
83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */,
83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */,
83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */,
839E21D91F2EDAF000EE54D7 /* mpeg_custom_utils_ahx.c */,
839E21DD1F2EDAF000EE54D7 /* mpeg_custom_utils.c */,
@ -1039,7 +1057,6 @@
83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */,
83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */,
831BA6221EAC61CB00CF89B0 /* coding_utils.c */,
831BA6271EAC61CB00CF89B0 /* wwise_vorbis_utils.h */,
838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */,
832389511D224C0800482226 /* hca_decoder.c */,
83D7318B1A749EEE00CA1366 /* g719_decoder.c */,
@ -1049,7 +1066,6 @@
836F6DE318BDC2180095E648 /* aica_decoder.c */,
836F6DE418BDC2180095E648 /* at3_decoder.c */,
836F6DE518BDC2180095E648 /* coding.h */,
836F6DE618BDC2180095E648 /* ea_decoder.c */,
836F6DE718BDC2180095E648 /* g721_decoder.c */,
836F6DE818BDC2180095E648 /* g7221_decoder.c */,
836F6DE918BDC2180095E648 /* g72x_state.h */,
@ -1080,6 +1096,9 @@
836F6DFF18BDC2180095E648 /* layout */ = {
isa = PBXGroup;
children = (
83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */,
83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */,
83AA5D1C1F6E2F7F0020821C /* ea_sns_blocked.c */,
830165A11F256BF400CA0941 /* hwas_blocked.c */,
831BD1201EEE1D2A00198540 /* rws_blocked.c */,
836F6E0018BDC2180095E648 /* aax_layout.c */,
@ -1122,6 +1141,10 @@
836F6E2718BDC2180095E648 /* meta */ = {
isa = PBXGroup;
children = (
83AA5D201F6E2F9B0020821C /* awc.c */,
83AA5D221F6E2F9C0020821C /* ea_snu.c */,
83AA5D211F6E2F9C0020821C /* hca_keys.h */,
83AA5D231F6E2F9C0020821C /* stm.c */,
839E21EA1F2EDB0500EE54D7 /* sk_aud.c */,
830165971F256BD000CA0941 /* txth.c */,
830165981F256BD000CA0941 /* ea_schl_fixed.c */,
@ -1308,7 +1331,6 @@
836F6EBE18BDC2190095E648 /* ps2_spm.c */,
836F6EBF18BDC2190095E648 /* ps2_sps.c */,
836F6EC018BDC2190095E648 /* ps2_ster.c */,
836F6EC118BDC2190095E648 /* ps2_stm.c */,
836F6EC218BDC2190095E648 /* ps2_str.c */,
836F6EC318BDC2190095E648 /* ps2_strlr.c */,
836F6EC418BDC2190095E648 /* ps2_svag.c */,
@ -1381,7 +1403,6 @@
836F6F0A18BDC2190095E648 /* x360_tra.c */,
836F6F0B18BDC2190095E648 /* xbox_hlwav.c */,
836F6F0C18BDC2190095E648 /* xbox_ims.c */,
836F6F0D18BDC2190095E648 /* xbox_stma.c */,
836F6F0E18BDC2190095E648 /* xbox_wavm.c */,
836F6F0F18BDC2190095E648 /* xbox_xmu.c */,
836F6F1018BDC2190095E648 /* xbox_xvas.c */,
@ -1438,7 +1459,6 @@
83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */,
83A3F0751E3AD8B900D6A794 /* formats.h in Headers */,
8323894B1D22419B00482226 /* clHCA.h in Headers */,
831BA62D1EAC61CB00CF89B0 /* wwise_vorbis_utils.h in Headers */,
839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */,
839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */,
836F705918BDC2190095E648 /* vgmstream.h in Headers */,
@ -1447,6 +1467,7 @@
836F705718BDC2190095E648 /* util.h in Headers */,
836F6F9A18BDC2190095E648 /* meta.h in Headers */,
836F6F4D18BDC2190095E648 /* layout.h in Headers */,
83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */,
836F6F2318BDC2190095E648 /* coding.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1630,6 +1651,7 @@
836F6FB218BDC2190095E648 /* ngc_gcub.c in Sources */,
836F702818BDC2190095E648 /* sat_dvi.c in Sources */,
836F6F2F18BDC2190095E648 /* mtaf_decoder.c in Sources */,
83AA5D161F6E2F600020821C /* ea_xa_decoder.c in Sources */,
836F6F9B18BDC2190095E648 /* mn_str.c in Sources */,
836F6F5918BDC2190095E648 /* tra_blocked.c in Sources */,
836F6F9F18BDC2190095E648 /* musc.c in Sources */,
@ -1678,7 +1700,6 @@
836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */,
836F6FD818BDC2190095E648 /* ps2_gbts.c in Sources */,
83709E0A1ECBC1A4005C03D3 /* waa_wac_wad_wam.c in Sources */,
836F6F2418BDC2190095E648 /* ea_decoder.c in Sources */,
836F6F7A18BDC2190095E648 /* dc_dcsw_dcs.c in Sources */,
836F6F5318BDC2190095E648 /* ps2_iab_blocked.c in Sources */,
831BA61A1EAC61A500CF89B0 /* ps2_vds_vdm.c in Sources */,
@ -1709,7 +1730,6 @@
836F6F6718BDC2190095E648 /* acm.c in Sources */,
836F6F8A18BDC2190095E648 /* gcsw.c in Sources */,
836F6F9C18BDC2190095E648 /* mp4.c in Sources */,
836F704918BDC2190095E648 /* xbox_stma.c in Sources */,
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */,
836F6F6F18BDC2190095E648 /* akb.c in Sources */,
836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */,
@ -1719,6 +1739,8 @@
836F6FEF18BDC2190095E648 /* ps2_pnb.c in Sources */,
836F6FCB18BDC2190095E648 /* ps2_ads.c in Sources */,
836F6FD318BDC2190095E648 /* ps2_ccc.c in Sources */,
83AA5D261F6E2F9C0020821C /* ea_snu.c in Sources */,
83AA5D171F6E2F600020821C /* mpeg_custom_utils_ealayer3.c in Sources */,
836F704C18BDC2190095E648 /* xbox_xvas.c in Sources */,
836F6F3918BDC2190095E648 /* SASSC_decoder.c in Sources */,
836F703A18BDC2190095E648 /* vs.c in Sources */,
@ -1733,6 +1755,7 @@
836F701A18BDC2190095E648 /* psx_fag.c in Sources */,
836F703B18BDC2190095E648 /* vsf.c in Sources */,
836F6F3D18BDC2190095E648 /* aax_layout.c in Sources */,
83AA5D1F1F6E2F800020821C /* ea_sns_blocked.c in Sources */,
836F6F8218BDC2190095E648 /* ea_schl.c in Sources */,
836F700A18BDC2190095E648 /* ps2_vpk.c in Sources */,
836F6F7318BDC2190095E648 /* bcstm.c in Sources */,
@ -1745,6 +1768,7 @@
830165A21F256BF400CA0941 /* hwas_blocked.c in Sources */,
836F6F2C18BDC2190095E648 /* mp4_aac_decoder.c in Sources */,
836F701F18BDC2190095E648 /* riff.c in Sources */,
83AA5D191F6E2F600020821C /* ea_xas_decoder.c in Sources */,
836F6F9318BDC2190095E648 /* ivaud.c in Sources */,
836F6F8518BDC2190095E648 /* exakt_sc.c in Sources */,
836F6FA618BDC2190095E648 /* nds_sad.c in Sources */,
@ -1802,11 +1826,13 @@
836F6FC718BDC2190095E648 /* pona.c in Sources */,
836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */,
836F6F5118BDC2190095E648 /* nolayout.c in Sources */,
83AA5D241F6E2F9C0020821C /* awc.c in Sources */,
836F702918BDC2190095E648 /* sat_sap.c in Sources */,
836F6F3718BDC2190095E648 /* pcm_decoder.c in Sources */,
831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */,
836F6F5B18BDC2190095E648 /* ws_aud_blocked.c in Sources */,
836F700218BDC2190095E648 /* ps2_tk5.c in Sources */,
83AA5D271F6E2F9C0020821C /* stm.c in Sources */,
831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */,
836F703F18BDC2190095E648 /* wii_smp.c in Sources */,
836F6FB818BDC2190095E648 /* ngc_tydsp.c in Sources */,
@ -1821,7 +1847,6 @@
836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */,
836F6FFE18BDC2190095E648 /* ps2_str.c in Sources */,
836F6F4A18BDC2190095E648 /* interleave.c in Sources */,
836F6FFD18BDC2190095E648 /* ps2_stm.c in Sources */,
83EDE5D81A70951A005F5D84 /* mca.c in Sources */,
831BA61B1EAC61A500CF89B0 /* sgxd.c in Sources */,
838BDB6C1D3AFAB10022CA6F /* ffmpeg_decoder.c in Sources */,
@ -1846,6 +1871,7 @@
836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */,
836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */,
836F701218BDC2190095E648 /* ps3_ivag.c in Sources */,
83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */,
836F6F7718BDC2190095E648 /* capdsp.c in Sources */,
836F6FB018BDC2190095E648 /* ngc_dsp_ygo.c in Sources */,
836F703318BDC2190095E648 /* str_snds.c in Sources */,
@ -1860,6 +1886,7 @@
836F6F9518BDC2190095E648 /* kraw.c in Sources */,
836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */,
83709E051ECBC1A4005C03D3 /* gtd.c in Sources */,
83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */,
836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */,
836F6F8618BDC2190095E648 /* excitebots.c in Sources */,
836F6FF418BDC2190095E648 /* rws.c in Sources */,
@ -1891,6 +1918,7 @@
836F6FBE18BDC2190095E648 /* ogg_vorbis_file.c in Sources */,
836F702618BDC2190095E648 /* s14_sss.c in Sources */,
836F6F4818BDC2190095E648 /* halpst_blocked.c in Sources */,
83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */,
8323894A1D22419B00482226 /* clHCA.c in Sources */,
836F702E18BDC2190095E648 /* sli.c in Sources */,
836F701D18BDC2190095E648 /* raw.c in Sources */,

View File

@ -30,7 +30,8 @@ void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * out
void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ref_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
size_t ima_bytes_to_samples(size_t bytes, int channels);
@ -42,6 +43,9 @@ int32_t dsp_nibbles_to_samples(int32_t nibbles);
void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
void dsp_read_coefs_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be);
void dsp_read_hist_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be);
/* ngc_dtk_decoder */
void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
@ -51,15 +55,16 @@ void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
/* pcm_decoder */
void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm16LE_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm16BE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian);
void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian);
size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
/* psx_decoder */
@ -74,12 +79,15 @@ size_t ps_bytes_to_samples(size_t bytes, int channels);
void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void init_get_high_nibble(VGMSTREAM * vgmstream);
/* ea_decoder */
/* ea_xa_decoder */
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* ea_xas_decoder */
void decode_ea_xas(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);
@ -155,6 +163,7 @@ void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream, mpeg_codec_data * data, sam
void reset_mpeg(VGMSTREAM *vgmstream);
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample);
void free_mpeg(mpeg_codec_data *data);
void flush_mpeg(mpeg_codec_data * data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data);
void mpeg_set_error_logging(mpeg_codec_data * data, int enable);
@ -193,7 +202,9 @@ void free_at3plus(maiatrac3plus_codec_data *data);
#ifdef VGM_USE_FFMPEG
/* ffmpeg_decoder */
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
ffmpeg_codec_data * init_ffmpeg_offset_index(STREAMFILE *streamFile, uint64_t start, uint64_t size, int stream_index);
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size);
ffmpeg_codec_data * init_ffmpeg_header_offset_index(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int stream_index);
void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels);
void reset_ffmpeg(VGMSTREAM *vgmstream);

View File

@ -1,7 +1,8 @@
#include "coding.h"
#include "../util.h"
/* Various EA ADPCM codecs evolved from CDXA */
/* AKA "EA ADPCM", evolved from CDXA. Inconsistently called EA XA/EA-XA/EAXA.
* Some variations contain ADPCM hist header per block, but it's handled in ea_block.c */
/*
* Another way to get coefs in EAXA v2, with no diffs (no idea which table is actually used in games):
@ -28,7 +29,7 @@ static const int EA_XA_TABLE[20] = {
0, -1, -3, -4
};
/* EA XA v2 (inconsistently called EAXA or EA-XA too); like ea_xa_int but with "PCM samples" flag and doesn't add 128 on nibble expand */
/* EA XA v2; like ea_xa_int but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
uint8_t frame_info;
int32_t sample_count;
@ -84,7 +85,7 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
}
}
/* EA XA v1 stereo (aka "EA ADPCM") */
/* EA XA v1 stereo */
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
uint8_t frame_info;
int32_t coef1, coef2;
@ -211,23 +212,3 @@ void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
stream->offset=0;
}
}
/* EA MicroTalk 10:1 / 5:1 */
/**
* Rarely used but can be found in the wild: FIFA 2001 (PS2), FIFA Soccer 2002 (PS2)
*
* Decoding algorithm is unknown; some info found by analyzing sx.exe output:
* - Comes in 10:1 or 5:1 compression varieties (the later's byte layout looks similar but has roughly double frame size)
* - Also with "PCM samples" flag before each frame (later version) or without (first version)
* - When PCM flag is 0xEE it has 16b ? + 16b num_samples + PCM samples placed right after the frame, but they
* are written *before* (presumably so they have something while the frame is decoded), like EALayer3.
* - VBR ADPCM, apparently similar to Westwood VBR ADPCM: first byte seems a header with mode+count, but after it may
* be 8 bytes(?) of coefs/hist (unlike Westwood's), then data. Samples per frame changes with the mode used.
* ex. decoding pure silence (0000) takes 0x2E (10:1) or 0x48 (5:1) into 432 samples (RLE mode)
* - Variable frame size but seems to range from 0x20 to 0x80 (in 5:1 at least)
* - After a new SCDl block, first byte (in each channel) is a flag but various values have no effect in the output
* (01=first block, 00=normal block?) and should be skipped in the block parser.
*
*/
//void decode_ea_mt10(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {

View File

@ -0,0 +1,78 @@
#include "coding.h"
#include "../util.h"
static const int EA_XA_TABLE[20] = {
0, 240, 460, 392,
0, 0, -208, -220,
0, 1, 3, 4,
7, 8, 10, 11,
0, -1, -3, -4
};
/* EA-XAS, evolution of EA-XA and cousin of MTA2. Layout: blocks of 0x4c per channel (128 samples),
* divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?).
* To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */
void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int group, row, i;
int samples_done = 0, sample_count = 0;
/* internal interleave */
int block_samples = 128;
first_sample = first_sample % block_samples;
/* process groups */
for (group = 0; group < 4; group++) {
int coef1, coef2;
int16_t hist1, hist2;
uint8_t shift;
uint32_t group_header = (uint32_t)read_32bitLE(stream->offset + channel*0x4c + group*0x4, stream->streamfile); /* always LE */
coef1 = EA_XA_TABLE[(uint8_t)(group_header & 0x0F) + 0];
coef2 = EA_XA_TABLE[(uint8_t)(group_header & 0x0F) + 4];
hist2 = (int16_t)(group_header & 0xFFF0);
hist1 = (int16_t)((group_header >> 16) & 0xFFF0);
shift = 20 - ((group_header >> 16) & 0x0F);
/* 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++;
/* process nibbles per group */
for (row = 0; row < 15; row++) {
for (i = 0; i < 1*2; i++) {
uint8_t sample_byte = (uint8_t)read_8bit(stream->offset + channel*0x4c + 4*4 + row*0x04 + group + i/2, stream->streamfile);
int sample;
sample = get_nibble_signed(sample_byte, !(i&1)); /* upper first */
sample = sample << shift;
sample = (sample + hist1 * coef1 + hist2 * coef2 + 128) >> 8;
sample = clamp16(sample);
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = sample;
samples_done++;
}
sample_count++;
hist2 = hist1;
hist1 = sample;
}
}
}
/* internal interleave (interleaved channels, but manually advances to co-exist with ea blocks) */
if (first_sample + samples_done == block_samples) {
stream->offset += 0x4c * channelspacing;
}
}

View File

@ -272,13 +272,16 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
/* MAIN INIT/DECODER */
/* ******************************************** */
/**
* Manually init FFmpeg, from an offset.
* Used if the stream has internal data recognized by FFmpeg.
*/
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
return init_ffmpeg_header_offset(streamFile, NULL, 0, start, size);
return init_ffmpeg_header_offset_index(streamFile, NULL,0, start,size, 0);
}
ffmpeg_codec_data * init_ffmpeg_offset_index(STREAMFILE *streamFile, uint64_t start, uint64_t size, int stream_index) {
return init_ffmpeg_header_offset_index(streamFile, NULL,0, start,size, stream_index);
}
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
return init_ffmpeg_header_offset_index(streamFile, header,header_size, start,size, 0);
}
/**
* Manually init FFmpeg, from a fake header / offset.
@ -286,15 +289,17 @@ ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, u
* Takes a fake header, to trick FFmpeg into demuxing/decoding the stream.
* This header will be seamlessly inserted before 'start' offset, and total filesize will be 'header_size' + 'size'.
* The header buffer will be copied and memory-managed internally.
* NULL header can used given if the stream has internal data recognized by FFmpeg at offset.
* Stream index can be passed to FFmpeg, if the format has multiple streams (1=first).
*/
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
ffmpeg_codec_data * init_ffmpeg_header_offset_index(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int stream_index) {
char filename[PATH_LIMIT];
ffmpeg_codec_data * data;
int errcode, i;
int streamIndex, streamCount;
AVStream *stream;
AVCodecParameters *codecPar;
AVCodecParameters *codecPar = NULL;
AVRational tb;
@ -338,23 +343,28 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t *
if ((errcode = avformat_find_stream_info(data->formatCtx, NULL)) < 0) goto fail;
/* find valid audio stream inside */
/* find valid audio stream */
streamIndex = -1;
streamCount = 0; /* audio streams only */
streamCount = 0;
for (i = 0; i < data->formatCtx->nb_streams; ++i) {
stream = data->formatCtx->streams[i];
codecPar = stream->codecpar;
if (streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO) {
streamIndex = i; /* select first audio stream found */
} else {
stream->discard = AVDISCARD_ALL; /* disable demuxing unneded streams */
}
if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO)
streamCount++;
}
if (streamIndex < 0) goto fail;
if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
streamCount++;
/* select Nth audio stream if specified, or first one */
if (streamIndex < 0 || (stream_index > 0 && streamCount == stream_index)) {
codecPar = stream->codecpar;
streamIndex = i;
}
}
if (i != streamIndex)
stream->discard = AVDISCARD_ALL; /* disable demuxing for other streams */
}
if (streamCount < stream_index) goto fail;
if (streamIndex < 0 || !codecPar) goto fail;
data->streamIndex = streamIndex;
stream = data->formatCtx->streams[streamIndex];
@ -386,6 +396,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t *
data->bytesConsumedFromDecodedFrame = INT_MAX;
/* other setup */
data->sampleRate = data->codecCtx->sample_rate;
data->channels = data->codecCtx->channels;

View File

@ -678,6 +678,43 @@ void decode_ref_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou
stream->adpcm_step_index = step_index;
}
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//internal interleave, mono
int block_samples = (0x800 - 4) * 2;
first_sample = first_sample % block_samples;
//inverted header
if (first_sample == 0) {
off_t header_offset = stream->offset;
step_index = read_16bitLE(header_offset,stream->streamfile);
hist1 = read_16bitLE(header_offset+2,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
}
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
off_t byte_offset = stream->offset + 4 + i/2;
int nibble_shift = (i&1?4:0); //low nibble first
ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
}
//internal interleave: increment offset on complete frame
if (i == block_samples) stream->offset += 0x800;
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
/* MS 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;

View File

@ -54,7 +54,6 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
break;
case MPEG_LYN:
case MPEG_AWC:
goto fail; /* not fully implemented */
case MPEG_STANDARD:
@ -69,12 +68,9 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
}
/* unknown encryption */
if (data->type == MPEG_AHX && data->config.encryption) {
goto fail;
}
//todo: test more: this improves the output, but seems formats aren't usually prepared
// (and/or the num_samples includes all possible samples in file, so by discarding some it'll reach EOF)
#if 0
/* set encoder delay (samples to skip at the beginning of a stream) if needed, which varies with encoder used */
switch(data->type) {
case MPEG_AHX: data->skip_samples = 480; break; /* observed default */
@ -82,7 +78,7 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
default: break;
}
data->samples_to_discard = data->skip_samples;
#endif
return 1;
fail:
@ -121,10 +117,10 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
}
/* frame interleave (ie. read 1 data-frame, skip 1 data-frame per stream) */
current_interleave = data->config.interleave; /* constant, current_size+current_padding */
current_interleave = data->config.interleave; /* constant for multi-stream FSbs */
VGM_ASSERT(current_interleave != current_data_size+current_padding,
"MPEG FSB: non-constant interleave found @ 0x%08lx\n", stream->offset);
VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding,
"MPEG FSB: %i streams with non-constant interleave found @ 0x%08lx\n", data->streams_size, stream->offset);
break;
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
@ -154,7 +150,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
/* skip interleave once block is done, if defined */
if (current_interleave && ((stream->offset - stream->channel_start_offset) % current_interleave == 0)) {
stream->offset += current_interleave * (data->ms_size-1); /* skip a block each stream */
stream->offset += current_interleave * (data->streams_size-1); /* skip a block each stream */
}
return 1;

View File

@ -3,6 +3,7 @@
#ifdef VGM_USE_MPEG
#define MPEG_AHX_EXPECTED_FRAME_SIZE 0x414
static int ahx_decrypt_type08(mpeg_codec_data *data);
/* writes data to the buffer and moves offsets, transforming AHX frames as needed */
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data) {
@ -38,23 +39,25 @@ int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data)
}
/* read VBR frames with CBR header, 0-fill up to expected size to keep mpg123 happy */
/* 0-fill up to expected size to keep mpg123 happy */
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,current_data_size,stream->streamfile);
memset(data->buffer + data->bytes_in_buffer,0, MPEG_AHX_EXPECTED_FRAME_SIZE - data->bytes_in_buffer);
data->bytes_in_buffer = MPEG_AHX_EXPECTED_FRAME_SIZE;
/* encryption 0x08 modifies a few bits in the side_data every frame, here we decrypt the buffer */
if (data->config.encryption) {
VGM_LOG("MPEG AHX: unknown encryption\n");
goto fail;
/* decrypt if needed */
switch(data->config.encryption) {
case 0x00: break;
case 0x08: ahx_decrypt_type08(data); break;
default:
VGM_LOG("MPEG AHX: unknown encryption 0x%x\n", data->config.encryption);
break; /* garbled frame */
}
/* update offsets */
stream->offset += current_data_size;
if (stream->offset + 0x0c >= file_size)
stream->offset = file_size; /* move after 0x0c footer to reach EOF (shouldn't happen normally) */
stream->offset = file_size; /* skip 0x0c footer to reach EOF (shouldn't happen normally) */
return 1;
@ -62,5 +65,47 @@ fail:
return 0;
}
/* Decrypts an AHX type 0x08 (keystring) encrypted frame. Algorithm by Thealexbarney */
static int ahx_decrypt_type08(mpeg_codec_data *data) {
int i, index, encrypted_bits;
uint32_t value;
uint16_t current_key;
/* encryption 0x08 modifies a few bits every frame, here we decrypt and write to data buffer */
/* derive keystring to 3 primes, using the type 0x08 method, and assign each an index of 1/2/3 (0=no key) */
/* (externally done for now, see: https://github.com/Thealexbarney/VGAudio/blob/2.0/src/VGAudio/Codecs/CriAdx/CriAdxKey.cs) */
/* read 2b from a bitstream offset to decrypt, and use it as an index to get the key.
* AHX encrypted bitstream starts at 107b (0x0d*8+3), every frame, and seem to always use index 2 */
value = (uint32_t)get_32bitBE(data->buffer + 0x0d);
index = (value >> (32-3-2)) & 0x03;
switch(index) {
case 0: current_key = 0; break;
case 1: current_key = data->config.cri_key1; break;
case 2: current_key = data->config.cri_key2; break;
case 3: current_key = data->config.cri_key3; break;
default: goto fail;
}
/* AHX for DC: 16b, normal: 6b (no idea, probably some Layer II field) */
encrypted_bits = data->config.cri_type == 0x10 ? 16 : 6;
/* decrypt next bitstream 2b pairs, up to 16b (max key size):
* - read 2b from bitstream (from higher to lower)
* - read 2b from key (from lower to higher)
* - XOR them to decrypt */
for (i = 0; i < encrypted_bits; i+=2) {
uint32_t xor_2b = (current_key >> i) & 0x03;
value ^= ((xor_2b << (32-3-2-2)) >> i);
}
/* write output */
put_32bitBE(data->buffer + 0x0d, value);
return 1;
fail:
return 0;
}
#endif

View File

@ -0,0 +1,184 @@
#include "mpeg_decoder.h"
#ifdef VGM_USE_MPEG
/**
* AWC music uses blocks (sfx doesn't), the fun part being each channel has different num_samples/frames
* per block, so it's unsuitable for the normal "blocked" layout and parsed here instead.
* Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes
* the last few frames of a channel are repeated in the new block (marked with the "discard samples" field).
*/
/* block header size, algined/padded to 0x800 */
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, mpeg_codec_data *data) {
size_t header_size = 0;
int i;
int entries = data->config.channels;
int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE;
for (i = 0; i < entries; i++) {
header_size += 0x18;
header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */
}
if (header_size % 0x800) /* padded */
header_size += 0x800 - (header_size % 0x800);
return header_size;
}
/* find data that repeats in the beginning of a new block at the end of last block */
static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset) {
uint8_t new_frame[0x1000];/* buffer to avoid fseek back and forth */
mpeg_frame_info info;
off_t off;
int i;
/* read block first frame */
if ( !mpeg_get_frame_info(streamFile, new_offset, &info))
goto fail;
if (info.frame_size > 0x1000)
goto fail;
if (read_streamfile(new_frame,new_offset, info.frame_size,streamFile) != info.frame_size)
goto fail;
/* find the frame in last bytes of prev block */
off = last_offset - 0x4000; /* typical max is 5-10 frames of ~0x200, no way to know exact size */
while (off < last_offset) {
/* compare frame vs prev block data */
for (i = 0; i < info.frame_size; i++) {
if ((uint8_t)read_8bit(off+i,streamFile) != new_frame[i])
break;
}
/* frame fully compared? */
if (i == info.frame_size)
return last_offset - off;
else
off += i+1;
}
fail:
VGM_LOG("AWC: can't find repeat size, new=0x%08lx, last=0x%08lx\n", new_offset, last_offset);
return 0; /* keep on truckin' */
}
/* init config and validate */
int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) {
mpeg_frame_info info;
int is_music;
/* start_offset can point to a block header that always starts with 0 (music) or normal data (sfx) */
is_music = read_32bitBE(start_offset, streamFile) == 0x00000000;
if (is_music)
start_offset += get_block_header_size(streamFile, start_offset, data);
/* get frame info at offset */
if ( !mpeg_get_frame_info(streamFile, start_offset, &info))
goto fail;
switch(info.layer) {
case 1: *coding_type = coding_MPEG_layer1; break;
case 2: *coding_type = coding_MPEG_layer2; break;
case 3: *coding_type = coding_MPEG_layer3; break;
default: goto fail;
}
data->channels_per_frame = info.channels;
data->samples_per_frame = info.frame_samples;
/* extra checks */
if (is_music) {
if (data->config.chunk_size <= 0)
goto fail; /* needs block size */
}
/* no big encoder delay added (for sfx they can start in less than ~300 samples) */
return 1;
fail:
return 0;
}
/* writes data to the buffer and moves offsets, parsing AWC blocks */
int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
mpeg_custom_stream *ms = data->streams[num_stream];
mpeg_frame_info info;
size_t current_data_size = 0, data_offset;
size_t file_size = get_streamfile_size(stream->streamfile);
int i;
/* blocked layout used for music */
if (data->config.chunk_size) {
off_t last_offset = stream->offset; /* when block end needs to be known */
/* block ended for this channel, move to next block start */
if (ms->current_size_count > 0 && ms->current_size_count == ms->current_size_target) {
//mpg123_open_feed(ms->m); //todo reset maybe needed?
data_offset = stream->offset - stream->channel_start_offset; /* ignoring header */
data_offset -= data_offset % data->config.chunk_size; /* start of current block */
stream->offset = stream->channel_start_offset + data_offset + data->config.chunk_size;
ms->current_size_count = 0;
ms->current_size_target = 0;
}
/* just in case, shouldn't happen */
if (stream->offset >= file_size) {
goto fail;
}
/* block starts for this channel, point to mpeg data */
if (ms->current_size_count == 0) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE;
off_t channel_offset = 0;
/* block has a header with base info per channel and table per channel (see blocked_awc.c) */
ms->decode_to_discard = read_32bit(stream->offset + 0x18*num_stream + 0x08, stream->streamfile);
ms->current_size_target = read_32bit(stream->offset + 0x18*num_stream + 0x14, stream->streamfile);
for (i = 0; i < num_stream; i++) { /* num_stream serves as channel */
size_t channel_size = read_32bit(stream->offset + 0x18*i + 0x14, stream->streamfile);
if (channel_size % 0x10) /* 32b aligned */
channel_size += 0x10 - channel_size % 0x10;
channel_offset += channel_size;
}
//;VGM_ASSERT(ms->decode_to_discard > 0, "AWC: s%i discard of %x found at chunk %lx\n", num_stream, ms->decode_to_discard, stream->offset);
stream->offset += channel_offset + get_block_header_size(stream->streamfile, stream->offset, data);
/* A new block may repeat frame bytes from prev block, and decode_to_discard has the number of repeated samples.
* However in RDR PS3 (not GTA5?) the value can be off (ie. discards 1152 but the repeat decodes to ~1152*4).
* I can't figure out why, so just find and skip the repeat data manually (probably better for mpg123 too) */
if (ms->decode_to_discard) {
size_t repeat = get_repeated_data_size(stream->streamfile, stream->offset, last_offset);
if (repeat > 0)
ms->decode_to_discard = 0;
stream->offset += repeat;
ms->current_size_target -= repeat;
}
}
}
/* update frame */
if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) )
goto fail;
current_data_size = info.frame_size;
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset, current_data_size, stream->streamfile);
stream->offset += current_data_size;
ms->current_size_count += current_data_size;
return 1;
fail:
return 0;
}
#endif

View File

@ -0,0 +1,673 @@
#include "mpeg_decoder.h"
#ifdef VGM_USE_MPEG
/**
* Utils to parse EALayer3, an MP3 variant. EALayer3 frames have custom headers (removing unneded bits)
* with regular MPEG data and optional PCM blocks. We transform EA-frames to MPEG-frames on the fly
* and manually fill the sample PCM sample buffer.
*
* Layer III MPEG1 uses two granules (data chunks) per frame, while MPEG2/2.5 ("LSF mode") only one. EA-frames
* contain one granule, so to reconstruct one MPEG-frame we need two EA-frames (MPEG1) or one (MPEG2).
* EALayer v1 and v2 differ in part of the header, but are mostly the same.
*
* Reverse engineering: https://bitbucket.org/Zenchreal/ealayer3 (ealayer3.exe decoder)
* Reference: https://www.mp3-tech.org/programmer/docs/mp3_theory.pdf
* https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/mpegaudiodec_template.c#L1306
*/
/* **************************************************************************** */
/* DEFS */
/* **************************************************************************** */
#define EALAYER3_EA_FRAME_BUFFER_SIZE 0x1000*4 /* enough for one EA-frame */
#define EALAYER3_MAX_GRANULES 2
#define EALAYER3_MAX_CHANNELS 2
/* helper to simulate a bitstream */
typedef struct {
uint8_t * buf; /* buffer to read/write*/
size_t bufsize; /* max size of the buffer */
off_t b_off; /* current offset in bits inside the buffer */
off_t offset; /* info only */
} ealayer3_bitstream;
/* parsed info from a single EALayer3 frame */
typedef struct {
/* EALayer3 v1 header */
uint32_t v1_pcm_flag;
uint32_t v1_pcm_decode_discard;
uint32_t v1_pcm_number;
/* EALayer3 v2 header */
uint32_t v2_extended_flag;
uint32_t v2_stereo_flag;
uint32_t v2_unknown; /* unused? */
uint32_t v2_frame_size; /* full size including headers and pcm block */
uint32_t v2_mode; /* BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3 */
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 */
/* EALayer3 common header + side info */
uint32_t version_index;
uint32_t sample_rate_index;
uint32_t channel_mode;
uint32_t mode_extension;
uint32_t granule_index; /* 0 = first, 1 = second (for MPEG1, that needs pairs) */
uint32_t scfsi[EALAYER3_MAX_CHANNELS]; /* SCaleFactor Selection Info */
uint32_t main_data_size[EALAYER3_MAX_CHANNELS]; /* AKA part2_3_length */
uint32_t others_1[EALAYER3_MAX_CHANNELS]; /* rest of the side info as-is, divided in 2 */
uint32_t others_2[EALAYER3_MAX_CHANNELS];
/* derived from the above */
uint32_t data_offset_b; /* start of the MPEG data */
uint32_t pre_size; /* size of the V1/V2 part */
uint32_t base_size_b; /* size (bits) of the header+side info, up to data_size */
uint32_t data_size_b; /* size (bits) of the main MPEG data up to pcm block; can be zero */
uint32_t padding_size_b; /* size (bits) of the padding after base+data */
uint32_t common_size; /* size of the common part (base+data+padding) */
uint32_t pcm_size; /* size of the pcm block */
uint32_t eaframe_size; /* size of all of the above, for convenience */
int mpeg1; /* flag, as MPEG2/2.5 ("low sample frequency" mode) has some differences */
int version;
int channels;
int sample_rate;
} ealayer3_frame_info;
static int ealayer3_parse_frame(mpeg_codec_data *data, ealayer3_bitstream *is, ealayer3_frame_info * eaf);
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame);
static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info * eaf);
static int ealayer3_parse_frame_common(ealayer3_bitstream *is, ealayer3_frame_info * eaf);
static int ealayer3_rebuild_mpeg_frame(ealayer3_bitstream* is_0, ealayer3_frame_info* eaf_0, ealayer3_bitstream* is_1, ealayer3_frame_info* eaf_1, ealayer3_bitstream* os);
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_info * eaf);
static int r_bits(ealayer3_bitstream * iw, int num_bits, uint32_t * value);
static int w_bits(ealayer3_bitstream * ow, int num_bits, uint32_t value);
/* **************************************************************************** */
/* EXTERNAL API */
/* **************************************************************************** */
/* init codec from a EALayer3 frame */
int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) {
int ok;
ealayer3_frame_info eaf;
ealayer3_bitstream is;
uint8_t ibuf[EALAYER3_EA_FRAME_BUFFER_SIZE];
//;VGM_LOG("EAFRAME: EALayer3 init at %lx\n", start_offset);
if (data->type == MPEG_EAL32P || data->type == MPEG_EAL32S)
goto fail; /* untested */
/* get first frame for info */
{
is.buf = ibuf;
is.bufsize = read_streamfile(ibuf,start_offset,EALAYER3_EA_FRAME_BUFFER_SIZE, streamFile); /* reads less at EOF */;
is.b_off = 0;
ok = ealayer3_parse_frame(data, &is, &eaf);
if (!ok) goto fail;
}
;VGM_ASSERT(!eaf.mpeg1, "MPEG EAL3: mpeg2 found at 0x%lx\n", start_offset);
*coding_type = coding_MPEG_ealayer3;
data->channels_per_frame = eaf.channels;
data->samples_per_frame = eaf.mpeg1 ? 1152 : 576;
/* extra checks */
if (!data->channels_per_frame || data->config.channels != data->channels_per_frame){
VGM_LOG("MPEG EAL3: unknown %i multichannel layout\n", data->config.channels);
goto fail; /* unknown layout */
}
/* encoder delay: EALayer3 handles this while decoding (skips samples as writes PCM blocks) */
return 1;
fail:
return 0;
}
/* writes data to the buffer and moves offsets, transforming EALayer3 frames */
int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
int ok;
off_t current_offset = stream->offset;
ealayer3_frame_info eaf_0, eaf_1;
ealayer3_bitstream is_0, is_1, os;
uint8_t ibuf_0[EALAYER3_EA_FRAME_BUFFER_SIZE], ibuf_1[EALAYER3_EA_FRAME_BUFFER_SIZE];
/* read first frame/granule */
{
is_0.buf = ibuf_0;
is_0.bufsize = read_streamfile(ibuf_0,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
is_0.b_off = 0;
ok = ealayer3_parse_frame(data, &is_0, &eaf_0);
if (!ok) goto fail;
ok = ealayer3_write_pcm_block(stream, data, num_stream, &eaf_0);
if (!ok) goto fail;
stream->offset += eaf_0.eaframe_size;
}
/* get second frame/granule */
if (eaf_0.mpeg1) {
int granule1_found;
do {
is_1.buf = ibuf_1;
is_1.bufsize = read_streamfile(ibuf_1,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
is_1.b_off = 0;
ok = ealayer3_parse_frame(data, &is_1, &eaf_1);
if (!ok) goto fail;
ok = ealayer3_write_pcm_block(stream, data, num_stream, &eaf_1);
if (!ok) goto fail;
stream->offset += eaf_1.eaframe_size;
/* in V1 sometimes there is a PCM block between two granules, try next */
if (eaf_1.v1_pcm_flag == 0xEE)
granule1_found = 0;
else
granule1_found = 1; /* assume it does (bad infinite loops) */
}
while(!granule1_found);
}
else {
memset(&eaf_1, 0, sizeof(ealayer3_frame_info));
}
/* rebuild EALayer frame to MPEG frame */
{
os.buf = data->buffer;
os.bufsize = data->buffer_size;
os.b_off = 0;
os.offset = current_offset;
ok = ealayer3_rebuild_mpeg_frame(&is_0, &eaf_0, &is_1, &eaf_1, &os);
if (!ok) goto fail;
data->bytes_in_buffer = os.b_off / 8; /* wrote full MPEG frame, hopefully */
}
return 1;
fail:
return 0;
}
/* **************************************************************************** */
/* INTERNAL HELPERS */
/* **************************************************************************** */
static int ealayer3_parse_frame(mpeg_codec_data *data, ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
int ok;
memset(eaf, 0, sizeof(ealayer3_frame_info));
switch(data->type) {
case MPEG_EAL31: ok = ealayer3_parse_frame_v1(is, eaf, data->channels_per_frame); break;
case MPEG_EAL32P:
case MPEG_EAL32S: ok = ealayer3_parse_frame_v2(is, eaf); break;
default: goto fail;
}
if (!ok) goto fail;
//;VGM_LOG("EAFRAME: v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf->version, eaf->channels, eaf->sample_rate, eaf->granule_index, eaf->pre_size, eaf->common_size, eaf->pcm_size, eaf->eaframe_size);
//if (data->type==MPEG_EAL31) VGM_LOG("EAFRAME v1: pcm=%x, unk=%x, number=%x\n", eaf->v1_pcm_flag, eaf->v1_pcm_unknown, eaf->v1_pcm_number);
//else VGM_LOG("EAFRAME v2: stereo=%x, unk=%x, fs=%x, mode=%x, val=%x, number=%x, size=%x\n", eaf->v2_stereo_flag, eaf->v2_unknown, eaf->v2_frame_size, eaf->v2_mode, eaf->v2_mode_value, eaf->v2_pcm_number, eaf->v2_common_size);
return 1;
fail:
return 0;
}
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame) {
int ok;
/* read EA-frame V1 header */
r_bits(is, 8,&eaf->v1_pcm_flag);
eaf->pre_size = 1; /* 8b */
if (eaf->v1_pcm_flag != 0x00 && eaf->v1_pcm_flag != 0xEE) {
VGM_LOG("MPEG EAL3 v1: header not 0x00 or 0xEE\n");
goto fail; /* wrong offset? */
}
/* 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 */
if (!channels_per_frame) {
VGM_LOG("MPEG EAL3 v1: PCM block as first frame\n");
goto fail; /* must know from a prev frame */
}
eaf->pre_size += 2+2; /* 16b+16b */
eaf->pcm_size = (2*eaf->v1_pcm_number * channels_per_frame);
}
else {
/* read EA-frame common header */
ok = ealayer3_parse_frame_common(is, eaf);
if (!ok) goto fail;
}
eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size;
return 1;
fail:
return 0;
}
static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
int ok;
/* 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, 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, 10,&eaf->v2_common_size);
eaf->pre_size += 4; /* 32b */
}
/* read EA-frame common header */
ok = ealayer3_parse_frame_common(is, eaf);
if (!ok) goto fail;
//todo maybe v2 frames can be PCM-only like v1
if (!eaf->channels) {
VGM_LOG("MPEG EAL3: v2 frame with no channel number");
goto fail;
}
eaf->pcm_size = (2*eaf->v2_pcm_number * eaf->channels);
eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size;
if(eaf->v2_frame_size != eaf->eaframe_size) {
VGM_LOG("MPEG EAL3: different v2 frame size vs calculated (0x%x vs 0x%x)\n", eaf->v2_frame_size, eaf->eaframe_size);
goto fail;
}
return 1;
fail:
return 0;
}
/* Parses a EALayer3 frame (common part) */
static int ealayer3_parse_frame_common(ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
/* index tables */
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
static const int sample_rates[4][4] = { /* [version_index][sample rate index] */
{ 11025, 12000, 8000, -1}, /* MPEG2.5 */
{ -1, -1, -1, -1}, /* reserved */
{ 22050, 24000, 16000, -1}, /* MPEG2 */
{ 44100, 48000, 32000, -1}, /* MPEG1 */
};
static const int channels[4] = { 2,2,2, 1 }; /* [channel_mode] */
off_t start_b_off = is->b_off;
int i;
/* read main header */
r_bits(is, 2,&eaf->version_index);
r_bits(is, 2,&eaf->sample_rate_index);
r_bits(is, 2,&eaf->channel_mode);
r_bits(is, 2,&eaf->mode_extension);
/* check empty frame */
if (eaf->version_index == 0 &&
eaf->sample_rate_index == 0 &&
eaf->channel_mode == 0 &&
eaf->mode_extension == 0) {
VGM_LOG("MPEG EAL3: empty frame\n");
goto fail;
}
/* derived */
eaf->version = versions[eaf->version_index];
eaf->channels = channels[eaf->channel_mode];
eaf->sample_rate = sample_rates[eaf->version_index][eaf->sample_rate_index];
eaf->mpeg1 = (eaf->version == 1);
if (eaf->version == -1 || eaf->sample_rate == -1) {
VGM_LOG("MPEG EAL3: illegal header values\n");
goto fail;
}
/* read side info */
r_bits(is, 1,&eaf->granule_index);
if (eaf->mpeg1 && eaf->granule_index == 1) {
for (i = 0; i < eaf->channels; i++) {
r_bits(is, 4,&eaf->scfsi[i]);
}
}
for (i = 0; i < eaf->channels; i++) {
int others_2_bits = eaf->mpeg1 ? 47-32 : 51-32;
r_bits(is, 12,&eaf->main_data_size[i]);
/* divided in 47b=32+15 (MPEG1) or 51b=32+19 (MPEG2), arbitrarily */
r_bits(is, 32,&eaf->others_1[i]);
r_bits(is, others_2_bits,&eaf->others_2[i]);
}
/* derived */
eaf->data_offset_b = is->b_off;
eaf->base_size_b = (is->b_off - start_b_off); /* header + size info size */
for (i = 0; i < eaf->channels; i++) { /* data size (can be 0, meaning a micro EA-frame) */
eaf->data_size_b += eaf->main_data_size[i];
}
if ((eaf->base_size_b+eaf->data_size_b) % 8) /* aligned to closest 8b */
eaf->padding_size_b = 8 - ((eaf->base_size_b+eaf->data_size_b) % 8);
eaf->common_size = (eaf->base_size_b + eaf->data_size_b + eaf->padding_size_b)/8;
return 1;
fail:
return 0;
}
/* Converts a EALAYER3 frame to a standard MPEG frame from pre-parsed info */
static int ealayer3_rebuild_mpeg_frame(ealayer3_bitstream* is_0, ealayer3_frame_info* eaf_0, ealayer3_bitstream* is_1, ealayer3_frame_info* eaf_1, ealayer3_bitstream* os) {
uint32_t c = 0;
int i,j;
int expected_bitrate_index, expected_frame_size;
if (!eaf_0->common_size && !eaf_1->common_size)
return 1; /* empty frames, PCM block only */
/* get bitrate: use max bitrate (320/160) to simplify calcs for now (but some EA-frames use bit reservoir) */
expected_bitrate_index = 0x0E;
if (eaf_0->mpeg1) { /* 44100=0x414, 48000=0x3C0, 32000=0x5A0 */
expected_frame_size = 144l * 320 * 1000l / eaf_0->sample_rate;
} else { /* 22050=0x20A, 24000=0x1E0, 16000=0x2D0, 11025=0x414, 12000=0x3C0, 8000=0x5A0 */
expected_frame_size = 72l * 160 * 1000l / eaf_0->sample_rate;
}
/* extra checks */
if (eaf_0->mpeg1) {
if (!eaf_1
|| eaf_0->mpeg1 != eaf_1->mpeg1
|| eaf_0->version != eaf_1->version
|| eaf_0->granule_index == eaf_1->granule_index
|| !eaf_0->common_size || !eaf_1->common_size) {
VGM_LOG("MPEG EAL3: EA-frames for MPEG1 don't match at 0x%lx\n", os->offset);
goto fail;
}
}
/* write MPEG1/2 frame header */
w_bits(os, 11, 0x7FF); /* sync */
w_bits(os, 2, eaf_0->version_index);
w_bits(os, 2, 0x01); /* layer III index */
w_bits(os, 1, 1); /* "no CRC" flag */
w_bits(os, 4, expected_bitrate_index);
w_bits(os, 2, eaf_0->sample_rate_index);
w_bits(os, 1, 0); /* padding */
w_bits(os, 1, 0); /* private */
w_bits(os, 2, eaf_0->channel_mode);
w_bits(os, 2, eaf_0->mode_extension);
w_bits(os, 1, 1); /* copyrighted */
w_bits(os, 1, 1); /* original */
w_bits(os, 2, 0); /* emphasis */
if (eaf_0->mpeg1) {
int private_bits = (eaf_0->channels==1 ? 5 : 3);
/* write MPEG1 side info */
w_bits(os, 9, 0); /* main data start (no bit reservoir) */
w_bits(os, private_bits, 0);
for (i = 0; i < eaf_1->channels; i++) {
w_bits(os, 4, eaf_1->scfsi[i]); /* saved in granule1 only */
}
for (i = 0; i < eaf_0->channels; i++) { /* granule0 */
w_bits(os, 12, eaf_0->main_data_size[i]);
w_bits(os, 32, eaf_0->others_1[i]);
w_bits(os, 47-32, eaf_0->others_2[i]);
}
for (i = 0; i < eaf_1->channels; i++) { /* granule1 */
w_bits(os, 12, eaf_1->main_data_size[i]);
w_bits(os, 32, eaf_1->others_1[i]);
w_bits(os, 47-32, eaf_1->others_2[i]);
}
/* write MPEG1 main data */
is_0->b_off = eaf_0->data_offset_b;
for (i = 0; i < eaf_0->channels; i++) { /* granule0 */
for (j = 0; j < eaf_0->main_data_size[i]; j++) {
uint32_t c = 0;
r_bits(is_0, 1, &c);
w_bits(os, 1, c);
}
}
is_1->b_off = eaf_1->data_offset_b;
for (i = 0; i < eaf_1->channels; i++) { /* granule1 */
for (j = 0; j < eaf_1->main_data_size[i]; j++) {
r_bits(is_1, 1, &c);
w_bits(os, 1, c);
}
}
}
else {
int private_bits = (eaf_0->channels==1 ? 1 : 2);
/* write MPEG2 side info */
w_bits(os, 8, 0); /* main data start (no bit reservoir) */
w_bits(os, private_bits, 0);
for (i = 0; i < eaf_0->channels; i++) {
w_bits(os, 12, eaf_0->main_data_size[i]);
w_bits(os, 32, eaf_0->others_1[i]);
w_bits(os, 51-32, eaf_0->others_2[i]);
}
/* write MPEG2 main data */
is_0->b_off = eaf_0->data_offset_b;
for (i = 0; i < eaf_0->channels; i++) {
for (j = 0; j < eaf_0->main_data_size[i]; j++) {
r_bits(is_0, 1, &c);
w_bits(os, 1, c);
}
}
}
/* align to closest 8b */
if (os->b_off % 8) {
int align_bits = 8 - (os->b_off % 8);
w_bits(os, align_bits, 0);
}
if (os->b_off/8 > expected_frame_size) {
VGM_LOG("MPEG EAL3: written 0x%lx but expected less than 0x%x at 0x%lx\n", os->b_off/8, expected_frame_size, os->offset);
//todo bit reservoir! (doesn't seem to affect the output too much)
//;VGM_LOG("EAFRAME: F0 v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf_0->version, eaf_0->channels, eaf_0->sample_rate, eaf_0->granule_index, eaf_0->pre_size, eaf_0->common_size, eaf_0->pcm_size, eaf_0->eaframe_size);
//;VGM_LOG("EAFRAME: F1 v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf_1->version, eaf_1->channels, eaf_1->sample_rate, eaf_1->granule_index, eaf_1->pre_size, eaf_1->common_size, eaf_1->pcm_size, eaf_1->eaframe_size);
//;VGM_LOGB(os->buf, os->b_off/8, 0);
}
else {
/* fill ancillary data (ignored) */
memset(os->buf + os->b_off/8, 0x77, expected_frame_size - os->b_off/8);
}
os->b_off = expected_frame_size*8;
return 1;
fail:
return 0;
}
/* write PCM block directly to sample buffer (EALayer3 seems to use this as a prefectch of sorts) */
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_info * eaf) {
mpeg_custom_stream *ms = data->streams[num_stream];
size_t bytes_filled;
int i;
if (!eaf->pcm_size)
return 1;
bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame;
if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) {
VGM_LOG("MPEG EAL3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
goto fail;
}
if (eaf->v1_pcm_number) {
//;VGM_LOG("pcm discard = %i, number = %i at 0x%lx\n", eaf->v1_pcm_decode_discard, eaf->v1_pcm_number, stream->offset);
VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%lx\n", eaf->v1_pcm_decode_discard, stream->offset);
VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%lx\n", eaf->v1_pcm_number, stream->offset);
/* read + write PCM block samples (always BE) */
for (i = 0; i < eaf->v1_pcm_number * data->channels_per_frame; i++) {
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size + sizeof(sample)*i;
int16_t pcm_sample = read_16bitBE(pcm_offset,stream->streamfile);
put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample);
}
ms->samples_filled += eaf->v1_pcm_number;
/* skip decoded samples as PCM block 'overwrites' them */
{
size_t decode_to_discard = eaf->v1_pcm_decode_discard;
//todo should also discard v1_pcm_number, but block layout samples may be exhausted and won't move (maybe new block if offset = new offset detected)
/* special meanings */
if (decode_to_discard == 576)
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
ms->decode_to_discard += decode_to_discard;
}
}
/* todo V2 (id7 only?) supposed skip modes:
* BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3
*
* AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG
* D = mode:
* E = bytes 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
* 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
*/
return 1;
fail:
return 0;
}
/* ********************************************* */
/* Read bits (max 32) from buf and update the bit offset. Order is BE (MSF). */
static int r_bits(ealayer3_bitstream * is, int num_bits, uint32_t * value) {
off_t off, pos;
int i, bit_buf, bit_val;
if (num_bits == 0) return 1;
if (num_bits > 32 || num_bits < 0 || is->b_off + num_bits > is->bufsize*8) goto fail;
*value = 0; /* set all bits to 0 */
off = is->b_off / 8; /* byte offset */
pos = is->b_off % 8; /* bit sub-offset */
for (i = 0; i < num_bits; i++) {
bit_buf = (1U << (8-1-pos)) & 0xFF; /* bit check for buf */
bit_val = (1U << (num_bits-1-i)); /* bit to set in value */
if (is->buf[off] & bit_buf) /* is bit in buf set? */
*value |= bit_val; /* set bit */
pos++;
if (pos%8 == 0) { /* new byte starts */
pos = 0;
off++;
}
}
is->b_off += num_bits;
return 1;
fail:
return 0;
}
/* Write bits (max 32) to buf and update the bit offset. Order is BE (MSF). */
static int w_bits(ealayer3_bitstream * os, int num_bits, uint32_t value) {
off_t off, pos;
int i, bit_val, bit_buf;
if (num_bits == 0) return 1;
if (num_bits > 32 || num_bits < 0 || os->b_off + num_bits > os->bufsize*8) goto fail;
off = os->b_off / 8; /* byte offset */
pos = os->b_off % 8; /* bit sub-offset */
for (i = 0; i < num_bits; i++) {
bit_val = (1U << (num_bits-1-i)); /* bit check for value */
bit_buf = (1U << (8-1-pos)) & 0xFF; /* bit to set in buf */
if (value & bit_val) /* is bit in val set? */
os->buf[off] |= bit_buf; /* set bit */
else
os->buf[off] &= ~bit_buf; /* unset bit */
pos++;
if (pos%8 == 0) { /* new byte starts */
pos = 0;
off++;
}
}
os->b_off += num_bits;
return 1;
fail:
return 0;
}
#endif

View File

@ -8,26 +8,18 @@
/* TODO list for custom decoder
* - don't force channel param and get them from frame headers for some types (for MPEG_STANDARD)
* - use one stream per channel and advance streams as channels are done in case of streams like 2ch+1ch+1ch+2ch (not seen)
* (would also need to change sample_buffer copy)
* - improve validation of channels/samples_per_frame between streams
* - improve decoded samples to sample buffer copying (very picky with sizes)
* - use mpg123 stream_buffer, with flags per stream, and call copy to sample_buffer when all streams have some samples
* (so it could handle interleaved VBR frames).
* - AHX type 8 encryption
* - test encoder delays
* - improve error handling
* - in case of streams like 2ch+1ch+1ch+2ch (not seen) use one stream per channel and advance streams as channels are done
* - validate of channels between streams
* - fsb garbage in the first frames
*/
/* mostly arbitrary max values */
#define MPEG_DATA_BUFFER_SIZE 0x1000
#define MPEG_MAX_CHANNELS 16
#define MPEG_MAX_STREAM_FRAMES 10
#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1) */
static mpg123_handle * init_mpg123_handle();
static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream);
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, int num_stream);
/* Inits regular MPEG */
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) {
@ -110,7 +102,7 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset
goto fail;
/* reinit, to ignore the reading we've done so far */
/* reinit, to ignore the reading done */
mpg123_open_feed(main_m);
}
@ -122,10 +114,9 @@ fail:
}
/* Init custom MPEG, with given type and config. */
/* Init custom MPEG, with given type and config */
mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) {
mpeg_codec_data *data = NULL;
int stream_frames = 1;
int i, ok;
/* init codec */
@ -147,35 +138,31 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
switch(data->type) {
case MPEG_EAL31:
case MPEG_EAL32P:
case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
case MPEG_AWC: ok = mpeg_custom_setup_init_awc(streamFile, start_offset, data, coding_type); break;
default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break;
}
if (!ok)
goto fail;
if (channels <= 0 || channels > MPEG_MAX_CHANNELS) goto fail;
if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */
if (channels < data->channels_per_frame) goto fail;
/* init stream decoders (separate as MPEG frames may need N previous frames from their stream to decode) */
data->ms_size = channels / data->channels_per_frame;
data->ms = calloc(sizeof(mpg123_handle *), data->ms_size);
for (i=0; i < data->ms_size; i++) {
data->ms[i] = init_mpg123_handle();
if (!data->ms[i]) goto fail;
/* init streams */
data->streams_size = channels / data->channels_per_frame;
data->streams = calloc(data->streams_size, sizeof(mpeg_custom_stream*));
for (i=0; i < data->streams_size; i++) {
data->streams[i] = calloc(1, sizeof(mpeg_custom_stream));
data->streams[i]->m = init_mpg123_handle(); /* decoder not shared as may need several frames to decode)*/
if (!data->streams[i]->m) goto fail;
/* size could be any value */
data->streams[i]->output_buffer_size = sizeof(sample) * data->channels_per_frame * data->samples_per_frame;
data->streams[i]->output_buffer = calloc(data->streams[i]->output_buffer_size, sizeof(uint8_t));
if (!data->streams[i]->output_buffer) goto fail;
}
if (stream_frames > MPEG_MAX_STREAM_FRAMES) goto fail;
/* init stream buffer, big enough for one stream and N frames at a time (will be copied to sample buffer) */
data->stream_buffer_size = sizeof(sample) * data->channels_per_frame * stream_frames * data->samples_per_frame;
data->stream_buffer = calloc(sizeof(uint8_t), data->stream_buffer_size);
if (!data->stream_buffer) goto fail;
/* init sample buffer, big enough for all streams/channels and N frames at a time */
data->sample_buffer_size = sizeof(sample) * channels * stream_frames * data->samples_per_frame;
data->sample_buffer = calloc(sizeof(uint8_t), data->sample_buffer_size);
if (!data->sample_buffer) goto fail;
/* write output */
config->interleave = data->config.interleave; /* for FSB */
@ -290,188 +277,201 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * dat
/**
* Decode custom MPEG, allowing support for: single frames, interleave, mutant frames, multiple streams
* (1 frame = 1/2ch so Nch = 2ch*N/2 or 1ch*N or 2ch+1ch+2ch+...), etc.
* Decode custom MPEG, for: single frames, mutant frames, interleave/multiple streams (Nch = 2ch*N/2 or 1ch*N), etc.
*
* Decodes samples per each stream and muxes them into a single internal buffer before copying to outbuf
* (to make sure channel samples are orderly copied between decode_mpeg calls).
* decode_mpeg_custom_stream does the main decoding, while this handles layout and copying samples to output.
* Copies to outbuf when there are samples in all streams and calls decode_mpeg_custom_stream to decode.
. Depletes the stream's sample buffers before decoding more, so it doesn't run out of buffer space.
*/
static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
int samples_done = 0, bytes_max, bytes_to_copy;
int i, samples_done = 0;
while (samples_done < samples_to_do) {
int samples_to_copy = -1;
if (data->bytes_used_in_sample_buffer < data->bytes_in_sample_buffer) {
/* copy remaining samples */
bytes_to_copy = data->bytes_in_sample_buffer - data->bytes_used_in_sample_buffer;
bytes_max = (samples_to_do - samples_done) * sizeof(sample) * channels;
if (bytes_to_copy > bytes_max)
bytes_to_copy = bytes_max;
memcpy((uint8_t*)(outbuf+samples_done*channels), data->sample_buffer + data->bytes_used_in_sample_buffer, bytes_to_copy);
/* update samples copied */
data->bytes_used_in_sample_buffer += bytes_to_copy;
samples_done += bytes_to_copy / sizeof(sample) / channels;
/* find max to copy from all streams (equal for all channels) */
for (i = 0; i < data->streams_size; i++) {
size_t samples_in_stream = data->streams[i]->samples_filled - data->streams[i]->samples_used;
if (samples_to_copy < 0 || samples_in_stream < samples_to_copy)
samples_to_copy = samples_in_stream;
}
else {
/* fill the internal sample buffer */
int i;
data->bytes_in_sample_buffer = 0;
data->bytes_used_in_sample_buffer = 0;
/* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams)
* With multiple offsets it's expected offsets are set up pointing to the first frame of each stream. */
for (i=0; i < data->ms_size; i++) {
switch(data->type) {
//case MPEG_LYN:
case MPEG_FSB:
case MPEG_XVAG:
case MPEG_P3D:
/* multiple offsets, decodes 1 frame per stream until reaching interleave/block_size and skips it */
decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i);
break;
//case MPEG_EA: //?
case MPEG_AWC:
/* consecutive streams: multiple offsets, decodes 1 frame per stream */
decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i);
break;
/* discard if needed (for looping) */
if (data->samples_to_discard) {
int samples_to_discard = samples_to_copy;
if (samples_to_discard > data->samples_to_discard)
samples_to_discard = data->samples_to_discard;
default:
/* N frames: single offset, decodes all N frames per stream (sample buffer must be big enough for N) */
decode_mpeg_custom_stream(&vgmstream->ch[0], data, data->ms[i], channels, i);
break;
for (i = 0; i < data->streams_size; i++) {
data->streams[i]->samples_used += samples_to_discard;
}
data->samples_to_discard -= samples_to_discard;
samples_to_copy -= samples_to_discard;
}
if (samples_to_copy > 0) {
/* copy stream's samples to outbuf */
if (samples_to_copy > samples_to_do - samples_done)
samples_to_copy = samples_to_do - samples_done;
/* mux streams channels (1/2ch) to outbuf (Nch) (ex. 6ch: samples from 2ch+2ch+2ch) */
for (i = 0; i < data->streams_size; i++) {
mpeg_custom_stream *ms = data->streams[i];
int channels_frame = data->channels_per_frame;
int fch, s;
for (fch = 0; fch < channels_frame; fch++) {
for (s = 0; s < samples_to_copy; s++) {
size_t bytes_used = sizeof(sample)*ms->samples_used*channels_frame;
off_t in_offset = sizeof(sample)*s*channels_frame + sizeof(sample)*fch;
off_t out_offset = s*channels + i*channels_frame + fch;
memcpy((uint8_t*)(outbuf+samples_done*channels + out_offset),
ms->output_buffer+bytes_used + in_offset,
sizeof(sample));
}
}
ms->samples_used += samples_to_copy;
}
/* discard (for looping): 'remove' decoded samples from the buffer */
if (data->samples_to_discard) {
size_t bytes_to_discard = data->samples_to_discard * sizeof(sample) * channels;
samples_done += samples_to_copy;
}
else {
/* decode more into stream sample buffers */
/* 'remove' all buffer at most */
if (bytes_to_discard > data->bytes_in_sample_buffer)
bytes_to_discard = data->bytes_in_sample_buffer;
/* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams)
* With multiple offsets they should already start in the first frame of each stream. */
for (i=0; i < data->streams_size; i++) {
switch(data->type) {
//case MPEG_FSB:
/* same offset: alternate frames between streams (maybe needed for weird layouts?) */
//decode_mpeg_custom_stream(&vgmstream->ch[0], data, i);
/* pretend the samples were used up and readjust discard */
data->bytes_used_in_sample_buffer = bytes_to_discard;
data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;;
default:
/* offset per stream: absolute offsets, fixed interleave (skips other streams/interleave) */
decode_mpeg_custom_stream(&vgmstream->ch[i], data, i);
break;
}
}
}
}
}
/* Decodes frames from a stream and muxes samples into a intermediate buffer and moves the stream offsets. */
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream) {
size_t bytes_done = 0;
/* Decodes frames from a stream into the stream's sample buffer, feeding mpg123 buffer data.
* If not enough data to decode (as N data-frames = 1 full-frame) this will exit but be called again. */
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, int num_stream) {
size_t bytes_done = 0, bytes_filled, samples_filled;
size_t stream_size = get_streamfile_size(stream->streamfile);
int rc, ok;
mpeg_custom_stream *ms = data->streams[num_stream];
//;VGM_LOG("MPEG: decode stream%i @ 0x%08lx (filled=%i, used=%i)\n", num_stream, stream->offset, ms->samples_filled, ms->samples_used);
/* wait until samples are depleted, so buffers don't grow too big */
if (ms->samples_filled - ms->samples_used > 0) {
return; /* common with multi-streams, as they decode at different rates) */
}
/* no samples = reset the counters */
ms->samples_filled = 0;
ms->samples_used = 0;
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
if (!data->buffer_full && stream->offset >= stream_size) {
VGM_LOG("MPEG: EOF found but more data is requested\n");
goto decode_fail;
}
/* decode samples from one full-frame (as N data-frames = 1 full-frame) before exiting (to orderly copy to sample buffer) */
do {
//VGM_LOG("MPEG: new step of stream %i @ 0x%08lx\n", num_stream, stream->offset);
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
if (stream->offset >= stream_size) {
memset(data->stream_buffer, 0, data->stream_buffer_size);
bytes_done = data->stream_buffer_size;
break; /* continue with other streams */
/* read more raw data (could fill the sample buffer too in some cases, namely EALayer3) */
if (!data->buffer_full) {
//;VGM_LOG("MPEG: reading more raw data\n");
switch(data->type) {
case MPEG_EAL31:
case MPEG_EAL32P:
case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break;
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break;
case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break;
default: ok = mpeg_custom_parse_frame_default(stream, data); break;
}
if (!ok) {
VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset);
goto decode_fail; /* mpg123 could resync but custom MPEGs wouldn't need that */
}
//;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", data->bytes_in_buffer, stream->offset);
/* read more raw data */
if (!data->buffer_full) {
//VGM_LOG("MPEG: reading more raw data\n");
switch(data->type) {
case MPEG_EAL31:
case MPEG_EAL32P:
case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_parse_frame_ealayer3(stream, data); break;
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break;
default: ok = mpeg_custom_parse_frame_default(stream, data); break;
}
/* error/EOF, mpg123 can resync in some cases but custom MPEGs wouldn't need that */
if (!ok || !data->bytes_in_buffer) {
VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset);
memset(data->stream_buffer, 0, data->stream_buffer_size);
bytes_done = data->stream_buffer_size;
break; /* continue with other streams */
}
//VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset off=%lx\n", data->bytes_in_buffer, stream->offset);
/* parse frame may not touch the buffer (only move offset, or fill the sample buffer) */
if (data->bytes_in_buffer) {
data->buffer_full = 1;
data->buffer_used = 0;
}
#if 0
//TODO: in case of EALayer3 with "PCM flag" must put them in buffer
if (ea_pcm_flag) {
/* write some samples to data->stream_buffer *before* decoding this frame */
bytes_done += read_streamfile(data->stream_buffer,offset,num_pcm_samples,stream->streamfile);
}
#endif
#if 0
//TODO: FSB sometimes has garbage in the first frames, not sure why/when, no apparent patern
if (data->custom_type == MPEG_FSB && stream->offset == stream->channel_start_offset) { /* first frame */
VGM_LOG("MPEG: skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
data->buffer_full = 0;
memset(data->stream_buffer, 0, data->stream_buffer_size);
bytes_done = data->stream_buffer_size;
break;
}
#endif
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
if (!data->buffer_used) {
//VGM_LOG("MPEG: feed new data and get samples \n");
rc = mpg123_decode(m,
data->buffer, data->bytes_in_buffer,
(unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size,
&bytes_done);
data->buffer_used = 1;
}
else {
//VGM_LOG("MPEG: get samples from old data\n");
rc = mpg123_decode(m,
NULL,0,
(unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size,
&bytes_done);
}
/* not enough raw data, request more */
if (rc == MPG123_NEED_MORE) {
//VGM_LOG("MPEG: need more raw data to get samples\n");
/* (apparently mpg123 can give bytes and request more at the same time and may mess up some calcs, when/how? */
VGM_ASSERT(bytes_done > 0, "MPEG: bytes done but decoder requests more data\n");
data->buffer_full = 0;
continue;
}
//VGM_LOG("MPEG: got samples, bytes_done=0x%x (fsbs=0x%x)\n", bytes_done, data->stream_buffer_size);
break;
} while (1);
/* copy decoded full-frame to intermediate sample buffer, muxing channels
* (ex stream1: ch1s1 ch1s2, stream2: ch2s1 ch2s2 > ch1s1 ch2s1 ch1s2 ch2s2) */
{
size_t samples_done;
size_t sz = sizeof(sample);
int channels_f = data->channels_per_frame;
int fch, i;
samples_done = bytes_done / sz / channels_f;
for (fch = 0; fch < channels_f; fch++) { /* channels inside the frame */
for (i = 0; i < samples_done; i++) { /* decoded samples */
off_t in_offset = sz*i*channels_f + sz*fch;
off_t out_offset = sz*i*channels + sz*(num_stream*channels_f + fch);
memcpy(data->sample_buffer + out_offset, data->stream_buffer + in_offset, sz);
}
}
data->bytes_in_sample_buffer += bytes_done;
}
#if 0
//FSB sometimes has garbage in the first frames, not sure why/when, no apparent patern
if (data->custom_type == MPEG_FSB && stream->offset == stream->channel_start_offset) { /* first frame */
VGM_LOG("MPEG: skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
data->buffer_full = 0;
memset(data->stream_buffer, 0, data->stream_buffer_size);
bytes_done = data->stream_buffer_size;
break;
}
#endif
bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame;
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
if (!data->buffer_used) {
//;VGM_LOG("MPEG: feed new data and get samples \n");
rc = mpg123_decode(ms->m,
data->buffer, data->bytes_in_buffer,
(unsigned char*)ms->output_buffer + bytes_filled, ms->output_buffer_size - bytes_filled,
&bytes_done);
data->buffer_used = 1;
}
else {
//;VGM_LOG("MPEG: get samples from old data\n");
rc = mpg123_decode(ms->m,
NULL, 0,
(unsigned char*)ms->output_buffer + bytes_filled, ms->output_buffer_size - bytes_filled,
&bytes_done);
}
samples_filled = (bytes_done / sizeof(sample) / data->channels_per_frame);
/* discard for weird features (EALayer3 and PCM blocks, AWC and repeated frames) */
if (ms->decode_to_discard) {
size_t bytes_to_discard = 0;
size_t decode_to_discard = ms->decode_to_discard;
if (decode_to_discard > samples_filled)
decode_to_discard = samples_filled;
bytes_to_discard = sizeof(sample)*decode_to_discard*data->channels_per_frame;
bytes_done -= bytes_to_discard;
ms->decode_to_discard -= decode_to_discard;
ms->samples_used += decode_to_discard;
}
/* if no decoding was done bytes_done will be zero */
ms->samples_filled += samples_filled;
/* not enough raw data, set flag to request more next time
* (but only with empty mpg123 buffer, EA blocks wait for all samples decoded before advancing blocks) */
if (!bytes_done && rc == MPG123_NEED_MORE) {
//;VGM_LOG("MPEG: need more raw data to get samples (bytest_done=%x)\n", bytes_done);
data->buffer_full = 0;
}
return;
decode_fail:
/* 0-fill but continue with other streams */
bytes_filled = ms->samples_filled*data->channels_per_frame*sizeof(sample);
memset(ms->output_buffer + bytes_filled, 0, ms->output_buffer_size - bytes_filled);
ms->samples_filled = (ms->output_buffer_size / data->channels_per_frame / sizeof(sample));
}
@ -488,13 +488,12 @@ void free_mpeg(mpeg_codec_data *data) {
}
else {
int i;
for (i=0; i < data->ms_size; i++) {
mpg123_delete(data->ms[i]);
for (i=0; i < data->streams_size; i++) {
mpg123_delete(data->streams[i]->m);
free(data->streams[i]->output_buffer);
free(data->streams[i]);
}
free(data->ms);
free(data->stream_buffer);
free(data->sample_buffer);
free(data->streams);
}
free(data->buffer);
@ -519,15 +518,15 @@ void reset_mpeg(VGMSTREAM *vgmstream) {
}
else {
int i;
for (i=0; i < data->ms_size; i++) {
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
/* re-start from 0 */
for (i=0; i < data->streams_size; i++) {
mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset);
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->decode_to_discard = 0;
}
data->bytes_in_sample_buffer = 0;
data->bytes_used_in_sample_buffer = 0;
/* initial delay */
data->samples_to_discard = data->skip_samples;
data->samples_to_discard = data->skip_samples; /* initial delay */
}
}
@ -544,13 +543,15 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
else {
int i;
/* re-start from 0 */
for (i=0; i < data->ms_size; i++) {
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
for (i=0; i < data->streams_size; i++) {
mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset);
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->decode_to_discard = 0;
if (vgmstream->loop_ch)
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
}
data->bytes_in_sample_buffer = 0;
data->bytes_used_in_sample_buffer = 0;
/* manually discard samples, since we don't really know the correct offset */
data->samples_to_discard = num_sample;
@ -561,6 +562,32 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
data->buffer_used = 0;
}
/* resets mpg123 decoder and its internals (with mpg123_open_feed as mpg123_feedseek won't work) */
void flush_mpeg(mpeg_codec_data * data) {
if (!data)
return;
if (!data->custom) {
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
mpg123_open_feed(data->m);
}
else {
int i;
/* re-start from 0 */
for (i=0; i < data->streams_size; i++) {
mpg123_open_feed(data->streams[i]->m);
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->decode_to_discard = 0;
}
data->samples_to_discard = data->skip_samples; /* initial delay */
}
data->bytes_in_buffer = 0;
data->buffer_full = 0;
data->buffer_used = 0;
}
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) {
/* if not found just return 0 and expect to fail (if used for num_samples) */
@ -582,15 +609,15 @@ long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) {
}
}
/* disables/enables stderr output, useful for MPEG known to contain recoverable errors */
/* disables/enables stderr output, for MPEG known to contain recoverable errors */
void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
if (!data->custom) {
mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
}
else {
int i;
for (i=0; i < data->ms_size; i++) {
mpg123_param(data->ms[i], MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
for (i=0; i < data->streams_size; i++) {
mpg123_param(data->streams[i]->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
}
}
}

View File

@ -18,11 +18,13 @@ typedef struct {
int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info);
int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
//int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
//int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
#endif/* VGM_USE_MPEG */

View File

@ -100,9 +100,7 @@ size_t dsp_bytes_to_samples(size_t bytes, int channels) {
}
/**
* reads DSP coefs built in the streamfile
*/
/* reads DSP coefs built in the streamfile */
void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_coefs(vgmstream, streamFile, offset, spacing, 1);
}
@ -121,3 +119,23 @@ void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset,
}
}
/* reads DSP initial hist built in the streamfile */
void dsp_read_hist_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_hist(vgmstream, streamFile, offset, spacing, 1);
}
void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_hist(vgmstream, streamFile, offset, spacing, 0);
}
void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be) {
int ch;
/* get ADPCM hist */
for (ch=0; ch < vgmstream->channels; ch++) {
vgmstream->ch[ch].adpcm_history1_16 = be ?
read_16bitBE(offset + ch*spacing + 0*2, streamFile) :
read_16bitLE(offset + ch*spacing + 0*2, streamFile);;
vgmstream->ch[ch].adpcm_history2_16 = be ?
read_16bitBE(offset + ch*spacing + 1*2, streamFile) :
read_16bitLE(offset + ch*spacing + 1*2, streamFile);;
}
}

View File

@ -36,7 +36,7 @@ void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
}
void free_ogg_vorbis(ogg_vorbis_codec_data *data) {
if (!data) {
if (data) {
OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file);
ov_clear(ogg_vorbis_file);

View File

@ -68,12 +68,12 @@ void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channe
}
}
void decode_pcm16LE_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i;
int32_t sample_count;
void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) {
int i, sample_count;
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
outbuf[sample_count]=read_16bitLE(stream->offset+i*2*channelspacing,stream->streamfile);
outbuf[sample_count]=read_16bit(stream->offset+i*2*channelspacing,stream->streamfile);
}
}
@ -118,6 +118,22 @@ void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
}
}
void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) {
int i, sample_count;
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint32_t sample_int = read_32bit(stream->offset+i*4,stream->streamfile);
float* sample_float;
int sample_pcm;
sample_float = (float*)&sample_int;
sample_pcm = floor((*sample_float) * 32767.f + .5f);
outbuf[sample_count] = clamp16(sample_pcm);
}
}
size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) {
return bytes / channels / (bits_per_sample/8);
}

View File

@ -32,7 +32,7 @@ static const char* extension_list[] = {
//"aiff", //common
"aix",
"akb",
"amts",
"amts", //fake extension (to be removed)
"as4",
"asd",
"asf",
@ -42,6 +42,7 @@ static const char* extension_list[] = {
"at3",
"aud",
"aus",
"awc",
"b1s",
"baf",
@ -147,14 +148,15 @@ static const char* extension_list[] = {
"kovs",
"kraw",
"laac", //fake extension, for tri-Ace/FFmpeg
"laac", //fake extension, for AAC (tri-Ace/FFmpeg)
"leg",
"lmp4", //fake extension, for looping
"logg", //fake extension, for looping
"lmp4", //fake extension, for MP4s
"logg", //fake extension, for OGGs
"lpcm",
"lps",
"lsf",
"lwav", //fake extension, for looping
"lstm", //fake extension, for STMs
"lwav", //fake extension, for WAVs
"matx",
"mc3",
@ -187,7 +189,7 @@ static const char* extension_list[] = {
"ndp",
"ngca",
"nps",
"npsf",
"npsf", //fake extension (to be removed)
"nus3bank",
"nwa",
@ -206,7 +208,7 @@ static const char* extension_list[] = {
"pnb",
"pona",
"pos",
"ps2stm",
"ps2stm", //fake extension (to be removed)
"psh",
"psnd",
"psw",
@ -253,6 +255,7 @@ static const char* extension_list[] = {
"snds",
"sng",
"sns",
"snu",
"spd",
"spm",
"sps",
@ -266,7 +269,7 @@ static const char* extension_list[] = {
"ster",
"sth",
//"stm", //common
"stma",
"stma", //fake extension (to be removed)
"str",
"strm",
"sts",
@ -393,16 +396,17 @@ typedef struct {
static const coding_info coding_info_list[] = {
{coding_PCM16BE, "Big Endian 16-bit PCM"},
{coding_PCM16LE, "Little Endian 16-bit PCM"},
{coding_PCM16LE_int, "Little Endian 16-bit PCM with 2 byte interleave"},
{coding_PCM16LE_XOR_int, "Little Endian 16-bit PCM with 2 byte interleave and XOR obfuscation"},
{coding_PCM16BE, "Big Endian 16-bit PCM"},
{coding_PCM16_int, "16-bit PCM with 2 byte interleave"},
{coding_PCM8, "8-bit PCM"},
{coding_PCM8_U, "8-bit unsigned PCM"},
{coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave"},
{coding_PCM8_int, "8-bit PCM with 1 byte interleave"},
{coding_PCM8_SB_int, "8-bit PCM with sign bit, 1 byte interleave"},
{coding_ULAW, "8-bit u-Law"},
{coding_PCMFLOAT, "32-bit float PCM"},
{coding_CRI_ADX, "CRI ADX 4-bit ADPCM"},
{coding_CRI_ADX_exp, "CRI ADX 4-bit ADPCM with exponential scale"},
{coding_CRI_ADX_fixed, "CRI ADX 4-bit ADPCM with fixed coefficients"},
@ -445,6 +449,7 @@ static const coding_info coding_info_list[] = {
{coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"},
{coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"},
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
{coding_WS, "Westwood Studios VBR ADPCM"},
{coding_ACM, "InterPlay ACM"},
{coding_NWA0, "NWA DPCM Level 0"},
@ -462,6 +467,7 @@ static const coding_info coding_info_list[] = {
{coding_MTAF, "Konami MTAF 4-bit ADPCM"},
{coding_MTA2, "Konami MTA2 4-bit ADPCM"},
{coding_MC3, "Paradigm MC3 3-bit ADPCM"},
{coding_EA_XAS, "Electronic Arts EA-XAS 4-bit ADPCM"},
#ifdef VGM_USE_VORBIS
{coding_ogg_vorbis, "Ogg Vorbis"},
@ -469,6 +475,7 @@ static const coding_info coding_info_list[] = {
#endif
#ifdef VGM_USE_MPEG
{coding_MPEG_custom, "Custom MPEG Audio"},
{coding_MPEG_ealayer3, "EALayer3"},
{coding_MPEG_layer1, "MPEG Layer I Audio (MP1)"},
{coding_MPEG_layer2, "MPEG Layer II Audio (MP2)"},
{coding_MPEG_layer3, "MPEG Layer III Audio (MP3)"},
@ -497,8 +504,8 @@ static const layout_info layout_info_list[] = {
{layout_ast_blocked, "AST blocked"},
{layout_halpst_blocked, "HALPST blocked"},
{layout_xa_blocked, "CD-ROM XA"},
{layout_ea_blocked, "Electronic Arts Audio Blocks"},
{layout_eacs_blocked, "Electronic Arts (Old Version) Audio Blocks"},
{layout_ea_blocked, "Electronic Arts SCxx blocked"},
{layout_eacs_blocked, "Electronic Arts EACS blocked"},
{layout_caf_blocked, "CAF blocked"},
{layout_wsi_blocked, ".wsi blocked"},
{layout_xvas_blocked, ".xvas blocked"},
@ -526,6 +533,9 @@ static const layout_info layout_info_list[] = {
{layout_aix, "AIX interleave, internally 18-byte interleaved"},
{layout_aax, "AAX blocked, 18-byte interleaved"},
{layout_scd_int, "SCD multistream interleave"},
{layout_ea_sns_blocked, "Electronic Arts SNS blocked"},
{layout_blocked_awc, "blocked (AWC)"},
{layout_blocked_vgs, "blocked (VGS)"},
#ifdef VGM_USE_VORBIS
{layout_ogg_vorbis, "Ogg"},
#endif
@ -659,7 +669,6 @@ static const meta_info meta_info_list[] = {
{meta_PS2_TEC, "assumed TECMO badflagged stream by .tec extension"},
{meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"},
{meta_NGC_WVS, "Metal Arms WVS Header (GameCube)"},
{meta_XBOX_STMA, "Midnight Club 2 STMA Header"},
{meta_XBOX_MATX, "assumed Matrix file by .matx extension"},
{meta_DE2, "gurumin .de2 with embedded funky RIFF"},
{meta_VS, "Men in Black VS Header"},
@ -711,7 +720,7 @@ static const meta_info meta_info_list[] = {
{meta_MSVP, "MSVP Header"},
{meta_NGC_SSM, "SSM DSP Header"},
{meta_PS2_JOE, "Disney/Pixar JOE Header"},
{meta_VGS, "Guitar Hero Encore Rocks the 80's Header"},
{meta_VGS, "Guitar Hero VGS Header"},
{meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"},
{meta_WII_SMP, "SMP DSP Header"},
{meta_EMFF_PS2, "Eidos Music File Format Header"},
@ -761,7 +770,6 @@ static const meta_info meta_info_list[] = {
{meta_WII_WAS, "WAS (iSWS) DSP header"},
{meta_XBOX_HLWAV, "Half Life 2 bgm header"},
{meta_STX, "Nintendo .stx header"},
{meta_PS2_STM, "Red Dead Revolver .stm (.ps2stm)"},
{meta_MYSPD, "U-Sing .myspd header"},
{meta_HIS, "Her Interactive Sound header"},
{meta_PS2_AST, "KOEI AST header"},
@ -873,6 +881,10 @@ static const meta_info meta_info_list[] = {
{meta_EA_BNK, "Electronic Arts BNK header"},
{meta_SK_AUD, "Silicon Knights AUD header"},
{meta_AHX, "CRI AHX header"},
{meta_STM, "Angel Studios/Rockstar San Diego STMA header"},
{meta_BINK, "RAD Game Tools Bink header"},
{meta_EA_SNU, "Electronic Arts SNU header"},
{meta_AWC, "Rockstar AWC header"},
#ifdef VGM_USE_VORBIS
{meta_OGG_VORBIS, "Ogg Vorbis"},

View File

@ -51,7 +51,8 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block+=samples_to_do;
if (vgmstream->samples_into_block==samples_this_block) {
if (vgmstream->samples_into_block==samples_this_block
/*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */
switch (vgmstream->layout_type) {
case layout_ast_blocked:
ast_block_update(vgmstream->next_block_offset,vgmstream);
@ -139,6 +140,15 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
break;
case layout_hwas_blocked:
hwas_block_update(vgmstream->next_block_offset,vgmstream);
break;
case layout_ea_sns_blocked:
ea_sns_block_update(vgmstream->next_block_offset,vgmstream);
break;
case layout_blocked_awc:
block_update_awc(vgmstream->next_block_offset,vgmstream);
break;
case layout_blocked_vgs:
block_update_vgs(vgmstream->next_block_offset,vgmstream);
break;
default:
break;

View File

@ -0,0 +1,60 @@
#include "layout.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../vgmstream.h"
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, int channels, int big_endian);
/* AWC music chunks */
void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
size_t header_size, entries, block_size, block_samples;
int i;
/* assumed only AWC_IMA enters here, MPEG/XMA2 need special parsing as blocked layout is too limited */
entries = read_32bit(block_offset + 0x18*0 + 0x04, streamFile); /* assumed same for all channels */
block_samples = entries * (0x800-4)*2;
block_size = vgmstream->full_block_size;
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_samples = block_samples;
/* starts with a header block */
/* for each channel
* 0x00: start entry within channel (ie. entries * ch)
* 0x04: entries
* 0x08: samples to discard in the beginning of this block (MPEG only?)
* 0x0c: samples in channel (for MPEG/XMA2 can vary between channels)
* 0x10: MPEG only: close to number of frames but varies a bit?
* 0x14: MPEG only: channel usable data size (not counting padding)
* for each channel
* 32b * entries = global samples per frame in each block (for MPEG probably per full frame)
*/
header_size = get_block_header_size(streamFile, block_offset, vgmstream->channels, vgmstream->codec_endian);
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + header_size + 0x800*entries*i;
}
}
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, int channels, int big_endian) {
size_t header_size = 0;
int i;
int entries = channels;
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
for (i = 0; i < entries; i++) {
header_size += 0x18;
header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */
}
if (header_size % 0x800) /* padded */
header_size += 0x800 - (header_size % 0x800);
return header_size;
}

View File

@ -0,0 +1,32 @@
#include "layout.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../vgmstream.h"
/* VGS multistream frames */
void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
size_t file_size = get_streamfile_size(vgmstream->ch[0].streamfile);
int i;
size_t channel_size = 0x10;
/* set offsets */
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + channel_size*i;
}
vgmstream->current_block_size = channel_size;
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + channel_size*vgmstream->channels;
/* skip unhandled tracks: flag can be 0x0n per track, of 0x8x for last frame */
while (vgmstream->next_block_offset < file_size) {
if ((read_8bit(vgmstream->next_block_offset + 0x01, streamFile) & 0x0F) == 0x00)
break;
vgmstream->next_block_offset += channel_size;
}
}

View File

@ -5,6 +5,7 @@
/* set up for the block at the given offset */
void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
int new_schl = 0;
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
uint32_t id;
size_t file_size, block_size = 0, block_samples;
@ -58,6 +59,10 @@ void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
/* Usually there is padding between SCEl and SCHl too (aligned to 0x80) */
}
if (id == 0x5343486C) { /* "SCHl", new subfile */
new_schl = 1;
}
continue;
}
}
@ -114,6 +119,25 @@ void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
break;
#ifdef VGM_USE_MPEG
/* id, size, samples, offset?, unknown (null for MP2, some constant for all blocks for EALayer3) */
case coding_MPEG_custom:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
case coding_MPEG_ealayer3:
for (i = 0; i < vgmstream->channels; i++) {
off_t channel_start = read_32bit(block_offset + 0x0C,streamFile);
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
}
/* SCHl with multiple SCHl need to reset their MPEG decoder as there are trailing samples in the buffers */
if (new_schl) {
flush_mpeg(vgmstream->codec_data);
}
break;
#endif
/* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */
default:
for (i = 0; i < vgmstream->channels; i++) {
@ -187,7 +211,7 @@ void eacs_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
} else {
for (i=0;i<vgmstream->channels;i++) {
if(vgmstream->coding_type==coding_PCM16LE_int)
if(vgmstream->coding_type==coding_PCM16_int)
vgmstream->ch[i].offset = block_offset+(i*2);
else
vgmstream->ch[i].offset = block_offset+i;

View File

@ -0,0 +1,38 @@
#include "layout.h"
#include "../coding/coding.h"
#include "../vgmstream.h"
/* EA "SNS "blocks (most common in .SNS) */
void ea_sns_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
uint32_t block_size, block_samples;
size_t file_size = get_streamfile_size(streamFile);
int i;
/* always BE */
block_size = read_32bitBE(block_offset + 0x00,streamFile);
block_samples = read_32bitBE(block_offset + 0x04,streamFile);
/* EOF */
if (block_size == 0 || block_offset >= file_size) {
vgmstream->current_block_offset = file_size;
vgmstream->next_block_offset = file_size + 0x04;
vgmstream->current_block_samples = vgmstream->num_samples;
return;
}
/* known: 0x80 = last block, 0x40, 0x08, 0x04, 0x01 */
if (block_size & 0xFF000000) {
VGM_ASSERT(!(block_size & 0x80000000), "EA SNS: unknown flag found at %lx\n", block_offset);
block_size &= 0x00FFFFFF;
}
for (i = 0; i < vgmstream->channels; i++) {
off_t channel_start = 0x00;
vgmstream->ch[i].offset = block_offset + 0x08 + channel_start;
}
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_samples = block_samples;
}

View File

@ -63,6 +63,11 @@ void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void ea_sns_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream);
/* other layouts */
void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View File

@ -6,7 +6,7 @@
VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int channel_count = 1, loop_flag = 0;
int channel_count = 1, loop_flag = 0, type;
/* check extension, case insensitive */
if ( !check_extensions(streamFile, "ahx") ) goto fail;
@ -22,8 +22,8 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
goto fail;
/* check for encoding type (0x10 is AHX for DC with bigger frames, 0x11 is AHX, 0x0N are ADX) */
if (read_8bit(0x04,streamFile) != 0x10 &&
read_8bit(0x04,streamFile) != 0x11) goto fail;
type = read_8bit(0x04,streamFile);
if (type != 0x10 && type != 0x11) goto fail;
/* check for frame size (0 for AHX) */
if (read_8bit(0x05,streamFile) != 0) goto fail;
@ -52,6 +52,16 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
memset(&cfg, 0, sizeof(mpeg_custom_config));
cfg.encryption = read_8bit(0x13,streamFile); /* 0x08 = keyword encryption */
cfg.cri_type = type;
if (cfg.encryption) {
uint8_t keybuf[6];
if (read_key_file(keybuf, 6, streamFile)) {
cfg.cri_key1 = get_16bitBE(keybuf+0);
cfg.cri_key2 = get_16bitBE(keybuf+2);
cfg.cri_key3 = get_16bitBE(keybuf+4);
}
}
vgmstream->layout_type = layout_none;
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg);

View File

@ -0,0 +1,276 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
typedef struct {
int big_endian;
int is_encrypted;
int is_music;
int total_streams;
int channel_count;
int sample_rate;
int codec;
int num_samples;
int block_chunk;
off_t stream_offset;
off_t stream_size;
} awc_header;
static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc);
/* AWC - from RAGE (Rockstar Advanced Game Engine) audio (Red Dead Redemption, Max Payne 3, GTA5) */
VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
awc_header awc;
/* check extension */
if (!check_extensions(streamFile,"awc"))
goto fail;
/* check header */
if (!parse_awc_header(streamFile, &awc))
goto fail;
if (awc.is_encrypted)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(awc.channel_count, 0);
if (!vgmstream) goto fail;
vgmstream->sample_rate = awc.sample_rate;
vgmstream->num_samples = awc.num_samples;
vgmstream->num_streams = awc.total_streams;
vgmstream->meta_type = meta_AWC;
switch(awc.codec) {
case 0x01: /* PCM (PC/PS3) [sfx, rarely] */
if (awc.is_music) goto fail; /* blocked_awc needs to be prepared */
vgmstream->coding_type = awc.big_endian ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
case 0x04: /* IMA (PC) */
vgmstream->coding_type = coding_AWC_IMA;
vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none;
vgmstream->full_block_size = awc.block_chunk;
vgmstream->codec_endian = awc.big_endian;
break;
#ifdef VGM_USE_MPEG
case 0x07: { /* MPEG (PS3) */
mpeg_custom_config cfg;
memset(&cfg, 0, sizeof(mpeg_custom_config));
cfg.chunk_size = awc.block_chunk;
cfg.big_endian = awc.big_endian;
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
break;
}
#endif
case 0x05: /* XMA2 (X360) */
default:
VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec);
goto fail;
}
/* open files; channel offsets are updated below */
if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset))
goto fail;
if (vgmstream->layout_type == layout_blocked_awc)
block_update_awc(awc.stream_offset, vgmstream);
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* Parse Rockstar's AWC header (much info from LibertyV: https://github.com/koolkdev/libertyv).
* Made of entries for N streams, each with a number of tags pointing to chunks (header, data, events, etc). */
static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
int64_t (*read_64bit)(off_t,STREAMFILE*) = NULL;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
int i, ch, entries;
uint32_t flags, info_header, tag_count = 0, tags_skip = 0;
off_t off;
int target_stream = streamFile->stream_index;
memset(awc,0,sizeof(awc_header));
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x41444154 && /* "ADAT" (LE) */
read_32bitBE(0x00,streamFile) != 0x54414441) /* "TADA" (BE) */
goto fail;
awc->big_endian = read_32bitBE(0x00,streamFile) == 0x54414441;
if (awc->big_endian) {
read_64bit = read_64bitBE;
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
read_64bit = read_64bitLE;
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
flags = read_32bit(0x04,streamFile);
entries = read_32bit(0x08,streamFile);
//header_size = read_32bit(0x0c,streamFile); /* after to stream id/tags, not including chunks */
off = 0x10;
if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) {
VGM_LOG("AWC: unknown flags 0x%08x\n", flags);
goto fail;
}
if (flags & 0x00010000) /* some kind of mini offset table */
off += 0x2 * entries;
//if (flags % 0x00020000) /* seems to indicate chunks are not ordered (ie. header may go after data) */
// ...
//if (flags % 0x00040000) /* music/multichannel flag? (GTA5, not seen in RDR) */
// awc->is_music = 1;
if (flags & 0x00080000) /* encrypted data chunk (most of GTA5 PC) */
awc->is_encrypted = 1;
/* Music when the first id is 0 (base/fake entry with info for all channels), sfx pack otherwise.
* sfx = N single streams, music = N-1 interleaved mono channels (even for MP3/XMA).
* Music seems layered (N-1/2 stereo pairs), maybe set with events? */
awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000;
if (awc->is_music) { /* all streams except id 0 is a channel */
awc->total_streams = 1;
target_stream = 1; /* we only need id 0, though channels may have its own tags/chunks */
}
else { /* each stream is a single sound */
awc->total_streams = entries;
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || target_stream > awc->total_streams || awc->total_streams < 1) goto fail;
}
/* get stream base info */
for (i = 0; i < entries; i++) {
info_header = read_32bit(off + 0x04*i, streamFile);
tag_count = (info_header >> 29) & 0x7; /* 3b */
//id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */
if (target_stream-1 == i)
break;
tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */
}
off += 0x04*entries;
off += 0x08*tags_skip;
/* get stream tags */
for (i = 0; i < tag_count; i++) {
uint64_t tag_header, tag, size, offset;
tag_header = (uint64_t)read_64bit(off + 0x08*i,streamFile);
tag = (tag_header >> 56) & 0xFF; /* 8b */
size = (tag_header >> 28) & 0x0FFFFFFF; /* 28b */
offset = (tag_header >> 0) & 0x0FFFFFFF; /* 28b */
/* Tags are apparently part of a hash derived from a word ("data", "format", etc).
* If music + 1ch, the header and data chunks can repeat for no reason (sometimes not even pointed). */
switch(tag) {
case 0x55: /* data */
awc->stream_offset = offset;
awc->stream_size = size;
break;
case 0x48: /* music header */
if (!awc->is_music) {
VGM_LOG("AWC: music header found in sfx\n");
goto fail;
}
/* 0x00(32): unknown (some count?) */
awc->block_chunk = read_32bit(offset + 0x04,streamFile);
awc->channel_count = read_32bit(offset + 0x08,streamFile);
if (awc->channel_count != entries - 1) { /* not counting id-0 */
VGM_LOG("AWC: number of music channels doesn't match entries\n");
goto fail;
}
for (ch = 0; ch < awc->channel_count; ch++) {
int num_samples, sample_rate, codec;
/* 0x00(32): stream id (not always in the header entries order) */
/* 0x08(16): headroom?, 0x0d(8): round size?, 0x0e(16): unknown (zero?) */
num_samples = read_32bit(offset + 0x0c + 0x10*ch + 0x04,streamFile);
sample_rate = (uint16_t)read_16bit(offset + 0x0c + 0x10*ch + 0x0a,streamFile);
codec = read_8bit(offset + 0x0c + 0x10*ch + 0x0c, streamFile);
/* validate as all channels should repeat this */
if ((awc->num_samples && awc->num_samples != num_samples) ||
(awc->sample_rate && awc->sample_rate != sample_rate) ||
(awc->codec && awc->codec != codec)) {
VGM_LOG("AWC: found header diffs between channels\n"); /* can rarely happen in stereo pairs */
goto fail;
}
awc->num_samples = num_samples;
awc->sample_rate = sample_rate;
awc->codec = codec;
}
break;
case 0xFA: /* sfx header */
if (awc->is_music) {
VGM_LOG("AWC: sfx header found in music\n");
goto fail;
}
/* 0x04(32): -1?, 0x0a(16x4): unknown x4, 0x12: null? */
awc->num_samples = read_32bit(offset + 0x00,streamFile);
awc->sample_rate = (uint16_t)read_16bit(offset + 0x08,streamFile);
awc->codec = read_8bit(offset + 0x13, streamFile);
awc->channel_count = 1;
break;
case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block) */
case 0xBD: /* events (32bx4): type_hash, params_hash, timestamp_ms, flags */
default: /* 0x5C=animation/RSC?, 0x68=midi?, 0x36/0x2B/0x5A/0xD9=? */
//VGM_LOG("AWC: ignoring unknown tag 0x%02x\n", tag);
break;
}
}
if (!awc->stream_offset) {
VGM_LOG("AWC: stream offset not found\n");
goto fail;
}
/* If music, data is divided into blocks of block_chunk size with padding.
* Each block has a header/seek table and interleaved data for all channels */
if (awc->is_music && read_32bit(awc->stream_offset, streamFile) != 0) {
VGM_LOG("AWC: music found, but block doesn't start with seek table\n");
goto fail;
}
return 1;
fail:
return 0;
}

View File

@ -2,81 +2,94 @@
#include "../coding/coding.h"
#include "../util.h"
#ifdef VGM_USE_FFMPEG
static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples);
static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample);
/* BIK 1/2 - RAD Game Tools movies (audio/video format) */
/* BINK 1/2 - RAD Game Tools movies (audio/video format) */
VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
ffmpeg_codec_data *data = NULL;
int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0, total_streams = 0;
int stream_index = streamFile->stream_index;
/* check extension, case insensitive (bika = manually demuxed audio) */
if (!check_extensions(streamFile,"bik,bika,bik2,bik2a,bk2,bk2a")) goto fail;
/* check header "BIK" (bik1) or "KB2" (bik2) (followed by version-char) */
/* check header "BIK" (bink 1) or "KB2" (bink 2), followed by version-char (audio is the same for both) */
if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 &&
(read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail;
/* FFmpeg can parse BIK audio, but can't get the number of samples, which vgmstream needs.
* The only way to get them is to read all frame headers */
data = init_ffmpeg_offset(streamFile, 0x0, get_streamfile_size(streamFile));
if (!data) goto fail;
vgmstream = allocate_vgmstream(data->channels, 0); /* alloc FFmpeg first to get channel count */
if (!vgmstream) goto fail;
vgmstream->codec_data = data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_FFmpeg;
vgmstream->sample_rate = data->sampleRate;
/* manually get num_samples since data->totalSamples is always 0 */
vgmstream->num_samples = bik_get_num_samples(streamFile, data->bitsPerSample);
if (vgmstream->num_samples == 0)
/* find target stream info and samples */
if (!bink_get_info(streamFile, &total_streams, &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_streams;
vgmstream->meta_type = meta_BINK;
#ifdef VGM_USE_FFMPEG
{
/* init_FFmpeg uses streamFile->stream_index internally, if specified */
vgmstream->codec_data = init_ffmpeg_offset_index(streamFile, 0x0, get_streamfile_size(streamFile), stream_index);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
}
#else
goto fail;
#endif
return vgmstream;
fail:
free_ffmpeg(data);
if (vgmstream) {
vgmstream->codec_data = NULL;
close_vgmstream(vgmstream);
}
close_vgmstream(vgmstream);
return NULL;
}
/**
* Gets the number of samples in a BIK file by reading all frames' headers,
* as they are not in the main header. The header for BIK1 and 2 is the same.
* Gets stream info, and number of samples in a BINK file by reading all frames' headers (as it's VBR),
* as they are not in the main header. The header for BINK1 and 2 is the same.
* (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough)
*
* Needs bits per sample to calculate PCM samples, since most bink audio seems to use 32, actually.
*/
static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample) {
static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples) {
uint32_t *offsets = NULL;
uint32_t num_samples_b = 0;
uint32_t num_frames, num_samples_b = 0;
off_t cur_offset;
size_t filesize;
int i, j, num_frames, num_tracks;
int target_stream = 0;
int i, j, sample_rate, channel_count;
int total_streams, target_stream = streamFile->stream_index;
filesize = get_streamfile_size(streamFile);
size_t filesize = get_streamfile_size(streamFile);
uint32_t signature = (read_32bitBE(0x00,streamFile) & 0xffffff00);
uint8_t revision = (read_32bitBE(0x00,streamFile) & 0xFF);
num_frames = read_32bitLE(0x08,streamFile);
if (num_frames <= 0) goto fail;
if (num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */
if (read_32bitLE(0x04,streamFile)+ 0x08 != filesize)
goto fail;
/* multichannel audio is usually N tracks of stereo/mono, no way to know channel layout */
num_tracks = read_32bitLE(0x28,streamFile);
if (num_tracks<=0 || num_tracks > 255) goto fail;
num_frames = (uint32_t)read_32bitLE(0x08,streamFile);
if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */
/* find the frame index table, which is after 3 audio headers of size 4 for each track */
cur_offset = 0x2c + num_tracks*4 * 3;
/* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */
total_streams = read_32bitLE(0x28,streamFile);
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || target_stream > total_streams || total_streams < 1 || total_streams > 255) goto fail;
/* read offsets in a buffer, to avoid fseeking to the table back and forth
* the number of frames can be highly variable so we'll alloc */
/* find stream info and position in offset table */
cur_offset = 0x2c;
if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */
(signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */
cur_offset += 0x04; /* unknown v2 header field */
cur_offset += 0x04*total_streams; /* skip streams max packet bytes */
sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x00,streamFile);
channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */
cur_offset += 0x04*total_streams; /* skip streams info */
cur_offset += 0x04*total_streams; /* skip streams ids */
/* read frame offsets in a buffer, to avoid fseeking to the table back and forth */
offsets = malloc(sizeof(uint32_t) * num_frames);
if (!offsets) goto fail;
@ -87,41 +100,40 @@ static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample)
if (offsets[i] > filesize) goto fail;
}
/* after the last index is the file size, validate just in case */
if (read_32bitLE(cur_offset,streamFile)!=filesize) goto fail;
/* multistream support just for fun (FFmpeg should select the same target stream)
* (num_samples for other streams seem erratic though) */
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || target_stream > num_tracks || num_tracks < 1) goto fail;
if (read_32bitLE(cur_offset,streamFile) != filesize) goto fail;
/* read each frame header and sum all samples
* a frame has N audio packets with header (one per track) + video packet */
* a frame has N audio packets with a header (one per stream) + video packet */
for (i=0; i < num_frames; i++) {
cur_offset = offsets[i];
/* read audio packet headers */
for (j=0; j < num_tracks; j++) {
uint32_t ap_size, samples_b;
ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */
samples_b = read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */
if (ap_size==0) break; /* no audio in this frame */
/* read audio packet headers per stream */
for (j=0; j < total_streams; j++) {
uint32_t ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */
if (j == target_stream-1) { /* target samples found, read next frame */
num_samples_b += samples_b;
break;
} else { /* check next audio packet */
cur_offset += 4 + ap_size; /* todo sometimes ap_size doesn't include itself (+4), others it does? */
if (j == target_stream-1) {
if (ap_size > 0)
num_samples_b += read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */
break; /* next frame */
}
else { /* next stream packet or frame */
cur_offset += 4 + ap_size; //todo sometimes ap_size doesn't include itself (+4), others it does?
}
}
}
free(offsets);
return num_samples_b / (bits_per_sample / 8);
if (out_total_streams) *out_total_streams = total_streams;
if (out_sample_rate) *out_sample_rate = sample_rate;
if (out_channel_count) *out_channel_count = channel_count;
//todo returns a few more samples (~48) than binkconv.exe?
if (out_num_samples) *out_num_samples = num_samples_b / (2 * channel_count);
return 1;
fail:
free(offsets);
return 0;
}
#endif

View File

@ -61,7 +61,7 @@ VGMSTREAM * init_vgmstream_eacs(STREAMFILE *streamFile) {
vgmstream->sample_rate = ea_header->dwSampleRate;
if(ea_header->bCompression==0) {
vgmstream->coding_type = coding_PCM16LE_int;
vgmstream->coding_type = coding_PCM16_int;
if(ea_header->bBits==1)
vgmstream->coding_type = coding_PCM8_int;
}

View File

@ -46,6 +46,7 @@
#define EA_CODEC2_MT5 0x16
#define EA_CODEC2_EALAYER3 0x17
#define EA_CODEC2_ATRAC3PLUS 0x1B /* Medal of Honor Heroes 2 (PSP) */
//todo #define EA_CODEC2_ATRAC9 0x-- /* supposedly exists */
#define EA_MAX_CHANNELS 6
@ -86,7 +87,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
/* check extension; exts don't seem enforced by EA's tools, but usually:
* STR/ASF/MUS ~early, EAM ~mid, SNG/AUD ~late, rest uncommon/one game (ex. STRM: MySims Kingdom Wii) */
if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm"))
if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm,ast"))
goto fail;
/* check header */
@ -115,20 +116,20 @@ fail:
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
off_t start_offset, header_offset, offset;
off_t start_offset, header_offset, offset, table_offset;
size_t header_size;
ea_header ea;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
int i, bnk_version;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extension */
/* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */
if (!check_extensions(streamFile,"bnk,sdt,mus"))
goto fail;
VGM_LOG("1\n");
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
if (read_32bitBE(0x00,streamFile) == 0x424E4B6C || /* "BNKl" (common) */
read_32bitBE(0x00,streamFile) == 0x424E4B62) /* "BNKb" (FIFA 98 SS) */
@ -137,7 +138,7 @@ VGM_LOG("1\n");
offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */
else
goto fail;
VGM_LOG("2\n");
/* use header size as endianness flag */
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) {
read_32bit = read_32bitBE;
@ -152,28 +153,34 @@ VGM_LOG("2\n");
/* check multi-streams */
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
VGM_LOG("3\n");
switch(bnk_version) {
case 0x02: /* early (Need For Speed PC, Fifa 98 SS) */
table_offset = 0x0c;
header_size = read_32bit(offset + 0x08,streamFile); /* full size */
header_offset = offset + 0x0c + 0x04*(target_stream-1) + read_32bit(offset + 0x0c + 0x04*(target_stream-1),streamFile);
header_offset = offset + table_offset + 0x04*(target_stream-1) + read_32bit(offset + table_offset + 0x04*(target_stream-1),streamFile);
break;
case 0x04: /* mid (last used in PSX banks) */
case 0x05: /* late (generated by sx.exe ~v2+) */
/* 0x08: header/file size, 0x0C: file size/null, 0x10: always null */
table_offset = 0x14;
if (read_32bit(offset + table_offset,streamFile) == 0x00)
table_offset += 0x4; /* MOH Heroes 2 PSP has an extra empty field, not sure why */
header_size = get_streamfile_size(streamFile); /* unknown (header is variable and may have be garbage until data) */
header_offset = offset + 0x14 + 0x04*(target_stream-1) + read_32bit(offset + 0x14 + 0x04*(target_stream-1),streamFile);
header_offset = offset + table_offset + 0x04*(target_stream-1) + read_32bit(offset + table_offset + 0x04*(target_stream-1),streamFile);
break;
default:
VGM_LOG("EA BNK: unknown version %x\n", bnk_version);
goto fail;
}
VGM_LOG("4\n");
if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset))
goto fail;
VGM_LOG("5\n");
/* fix absolute offsets so it works in next funcs */
if (offset) {
for (i = 0; i < ea.channels; i++) {
@ -338,7 +345,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */
case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */
default:
VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
VGM_LOG("EA SCHl: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
goto fail;
}
@ -609,7 +616,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
break;
default:
VGM_LOG("EA: unknown patch 0x%02x at 0x%04lx\n", patch_type, (offset-1));
VGM_LOG("EA SCHl: unknown patch 0x%02x at 0x%04lx\n", patch_type, (offset-1));
break;
}
}
@ -653,7 +660,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case EA_PLATFORM_3DS: ea->version = EA_VERSION_V3; break;
case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break;
default:
VGM_LOG("EA: unknown default version for platform 0x%02x\n", ea->platform);
VGM_LOG("EA SCHl: unknown default version for platform 0x%02x\n", ea->platform);
goto fail;
}
}
@ -667,7 +674,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; // assumed
case EA_PLATFORM_SAT: ea->codec1 = EA_CODEC1_PCM; break;
default:
VGM_LOG("EA: unknown default codec1 for platform 0x%02x\n", ea->platform);
VGM_LOG("EA SCHl: unknown default codec1 for platform 0x%02x\n", ea->platform);
goto fail;
}
}
@ -682,7 +689,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case EA_CODEC1_EAXA: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_CODEC1_MT10: ea->codec2 = EA_CODEC2_MT10; break;
default:
VGM_LOG("EA: unknown codec1 0x%02x\n", ea->codec1);
VGM_LOG("EA SCHl: unknown codec1 0x%02x\n", ea->codec1);
goto fail;
}
}
@ -701,7 +708,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case EA_PLATFORM_PSP: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_3DS: ea->codec2 = EA_CODEC2_GCADPCM; break;
default:
VGM_LOG("EA: unknown default codec2 for platform 0x%02x\n", ea->platform);
VGM_LOG("EA SCHl: unknown default codec2 for platform 0x%02x\n", ea->platform);
goto fail;
}
}
@ -722,7 +729,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case EA_PLATFORM_PSP: ea->sample_rate = 22050; break;
//case EA_PLATFORM_3DS: ea->sample_rate = 44100; break;//todo (not 22050/16000)
default:
VGM_LOG("EA: unknown default sample rate for platform 0x%02x\n", ea->platform);
VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform);
goto fail;
}
}

View File

@ -0,0 +1,107 @@
#include "meta.h"
#include "../layout/layout.h"
/* .SNU - EA new-ish header (Dead Space, The Godfather 2) */
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int channel_count, loop_flag = 0, channel_config, codec, sample_rate, flags;
uint32_t num_samples, loop_start = 0, loop_end = 0;
off_t start_offset;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"snu"))
goto fail;
/* check header */
//if ((read_32bitBE(0x00,streamFile) & 0x00FFFF00 != 0x00000000) && (read_32bitBE(0x0c,streamFile) != 0x00000000))
// goto fail;
/* 0x00: related to sample rate?, 0x02: always 0?, 0x03: related to channels? (usually match but may be 0) */
/* 0x04: some size, maybe >>2 ~= number of 0x4c frames (BE/LE depending on platform) */
/* 0x08: always 0x20? (also BE/LE), 0x0c: always 0? */
start_offset = 0x20; /* first block */
codec = read_8bit(0x10,streamFile);
channel_config = read_8bit(0x11,streamFile);
sample_rate = (uint16_t)read_16bitBE(0x12,streamFile);
flags = (uint8_t)read_8bit(0x14,streamFile); /* upper nibble only? */
num_samples = (uint32_t)read_32bitBE(0x14,streamFile) & 0x00FFFFFF;
/* 0x18: null?, 0x1c: null? */
if (flags != 0x60 && flags != 0x40) {
VGM_LOG("EA SNS: unknown flag\n");
goto fail;
}
#if 0
//todo not working ok with blocks
if (flags & 0x60) { /* full loop, seen in ambient tracks */
loop_flag = 1;
loop_start = 0;
loop_end = num_samples;
}
#endif
//channel_count = (channel_config >> 2) + 1; //todo test
/* 01/02/03 = 1 ch?, 05/06/07 = 2/3 ch?, 0d/0e/0f = 4/5 ch?, 14/15/16/17 = 6/7 ch?, 1d/1e/1f = 8 ch? */
switch(channel_config) {
case 0x00: channel_count = 1; break;
case 0x04: channel_count = 2; break;
case 0x0c: channel_count = 4; break;
case 0x14: channel_count = 6; break;
case 0x1c: channel_count = 8; break;
default:
VGM_LOG("EA SNU: unknown channel config 0x%02x\n", channel_config);
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;
vgmstream->loop_end_sample = loop_end;
vgmstream->meta_type = meta_EA_SNU;
vgmstream->layout_type = layout_ea_sns_blocked;
switch(codec) {
case 0x04: /* "Xas1": EA-XAS (Dead Space) */
vgmstream->coding_type = coding_EA_XAS;
break;
case 0x00: /* "NONE" */
case 0x01: /* not used? */
case 0x02: /* "P6B0": PCM16BE */
case 0x03: /* "EXm0": EA-XMA */
case 0x05: /* "EL31": EALayer3 v1 b (with PCM blocks in normal EA-frames?) */
case 0x06: /* "EL32P": EALayer3 v2 "P" */
case 0x07: /* "EL32S": EALayer3 v2 "S" */
case 0x09: /* EASpeex? */
case 0x0c: /* EAOpus? */
case 0x0e: /* XAS variant? */
case 0x0f: /* EALayer3 variant? */
/* also 0x1n variations, used in other headers */
default:
VGM_LOG("EA SNU: unknown codec 0x%02x\n", codec);
goto fail;
}
/* open the file for reading by each channel */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
ea_sns_block_update(start_offset, vgmstream);
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -92,8 +92,11 @@ typedef struct {
/* extra */
uint32_t hdrsize;
uint32_t shdrsize_min;
meta_t meta_type;
} FSB_HEADER;
off_t name_offset;
size_t name_size;
} fsb_header;
/* ********************************************************************************** */
@ -116,12 +119,12 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
off_t start_offset;
size_t custom_data_offset;
int loop_flag = 0;
int target_stream = 0;
int target_stream = streamFile->stream_index;
FSB_HEADER fsbh;
fsb_header fsbh;
/* check extensions */
if ( !check_extensions(streamFile, "fsb,wii") )
/* check extensions (.bnk = Hard Corps Uprising PS3) */
if ( !check_extensions(streamFile, "fsb,wii,bnk") )
goto fail;
/* check header */
@ -144,7 +147,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
{
off_t s_off = offset+fsbh.hdrsize;
/* 0x00:name(len=0x20) */
fsbh.name_offset = s_off;
fsbh.name_size = 0x20;
fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile);
fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile);
fsbh.deffreq = read_32bitLE(s_off+0x28,streamFile);
@ -209,9 +213,11 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
off_t s_off = offset + fsbh.hdrsize;
off_t d_off = offset + fsbh.hdrsize + fsbh.shdrsize;
/* find target_stream data offset, reading each header */
for(i=1; i <= fsbh.numsamples; i++) {
/* 0x00:size 0x02:name(len=size) */
/* find target_stream header (variable sized) */
for(i = 0; i < fsbh.numsamples; i++) {
size_t stream_header_size = (uint16_t)read_16bitLE(s_off+0x00,streamFile);
fsbh.name_offset = s_off+0x02;
fsbh.name_size = 0x20-0x02;
fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile);
fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile);
fsbh.loopstart = read_32bitLE(s_off+0x28,streamFile);
@ -221,20 +227,19 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
/* 0x38:defvol 0x3a:defpan 0x3c:defpri */
fsbh.numchannels = read_16bitLE(s_off+0x3e,streamFile);
/* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsbh.varpan */
/* FSB3/4: 0x50:extended_data size_32bits (not always given) */
if (target_stream == i) /* d_off found */
if (i+1 == target_stream) /* d_off found */
break;
s_off += fsbh.shdrsize_min; /* default size */
if (fsbh.version == FMOD_FSB_VERSION_4_0) {
uint32_t extended_data = read_32bitLE(s_off+0x48,streamFile); /* +0x50:extended_data of size_32bits */
if (extended_data > fsbh.shdrsize_min)
s_off += extended_data;
}
s_off += stream_header_size;
d_off += fsbh.lengthcompressedbytes; /* there is no offset so manually count */
//d_off += d_off % 0x30; /*todo some formats need padding, not sure when/how */
/* IMAs streams have weird end padding (maybe: FSB3=no padding, FSB4=always padding) */
if ((fsbh.mode & FSOUND_IMAADPCM) && (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) {
if (d_off % 0x20)
d_off += 0x20 - (d_off % 0x20);
}
}
if (i > fsbh.numsamples) goto fail; /* not found */
@ -243,26 +248,26 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
}
}
#if 0
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
if (fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED) {
VGM_LOG("FSB ENCRYPTED found\n");
goto fail;
}
//VGM_ASSERT(fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
/* sometimes there is garbage at the end or missing bytes due to improper demuxing */
if (fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset) {
VGM_LOG("FSB wrong head/datasize found\n");
goto fail;
}
#endif
VGM_ASSERT(fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset,
"FSB wrong head/datasize found\n");
/* Loops unless disabled. FMOD default seems 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 is it looks jingly enough. */
loop_flag = !(fsbh.mode & FSOUND_LOOP_OFF);
if(!(fsbh.mode & FSOUND_LOOP_NORMAL) /* rarely set */
&& fsbh.loopstart+fsbh.loopend+1 == fsbh.lengthsamples /* full loop */
&& fsbh.lengthsamples < 20*fsbh.deffreq) /* seconds, lame but no other way to know */
loop_flag = 0;
/* Loops by default unless disabled (sometimes may add FSOUND_LOOP_NORMAL). Often streams
* repeat over and over (some tracks that shouldn't do this based on the flags, no real way to identify them). */
loop_flag = !(fsbh.mode & FSOUND_LOOP_OFF); /* (fsbh.mode & FSOUND_LOOP_NORMAL) */
/* ping-pong looping = no looping? (forward > reverse > forward) */
VGM_ASSERT(fsbh.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n");
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(fsbh.numchannels,loop_flag);
if (!vgmstream) goto fail;
@ -273,6 +278,9 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
vgmstream->loop_end_sample = fsbh.loopend;
vgmstream->num_streams = fsbh.numsamples;
vgmstream->meta_type = fsbh.meta_type;
if (fsbh.name_offset)
read_string(vgmstream->stream_name,fsbh.name_size+1, fsbh.name_offset,streamFile);
/* parse format */
if (fsbh.mode & FSOUND_MPEG) {
@ -300,7 +308,6 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
#endif
}
else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */
//VGM_ASSERT(fsbh.mode & FSOUND_IMAADPCMSTEREO, "FSB FSOUND_IMAADPCMSTEREO found\n");
/* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different
* (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */
@ -314,14 +321,13 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
else if (fsbh.mode & FSOUND_VAG) {
/* FSB1: Jurassic Park Operation Genesis
* FSB3: ?; FSB4: Spider Man Web of Shadows, Speed Racer, Silent Hill: Shattered Memories (PS2) */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
}
else if (fsbh.mode & FSOUND_XMA) {
/* FSB4: Xbox360 Armored Core V, Hard Corps, Halo Anniversary */
#if defined(VGM_USE_FFMPEG)
/* FSB4: Xbox360 Armored Core V, Hard Corps, Halo Anniversary */
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[FAKE_RIFF_BUFFER_SIZE];
size_t bytes, block_size, block_count;
@ -346,36 +352,26 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
}
else if (fsbh.mode & FSOUND_GCADPCM) {
/* FSB3: ?; FSB4: de Blob (Wii), Night at the Museum, M. Night Shyamalan Avatar: The Last Airbender */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave_byte;
vgmstream->interleave_block_size = 0x2;
dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e);
}
else if (fsbh.mode & FSOUND_CELT) { /* || fsbh.mode & FSOUND_OGG (same flag) */
else if (fsbh.mode & FSOUND_CELT) { /* || fsbh.mode & FSOUND_OGG (same flag, unused) */
/* FSB4: War Thunder (PC), The Witcher 2 (PC) */
VGM_LOG("FSB4 FSOUND_CELT found\n");
goto fail;
}
else { /* PCM */
if (fsbh.mode & FSOUND_8BITS) {
VGM_LOG("FSB FSOUND_8BITS found\n");
if (fsbh.mode & FSOUND_UNSIGNED) {
vgmstream->coding_type = coding_PCM8_U; /* ? coding_PCM8_U_int */
} else { /* FSOUND_SIGNED */
vgmstream->coding_type = coding_PCM8; /* ? coding_PCM8_int / coding_PCM8_SB_int */
}
vgmstream->coding_type = (fsbh.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x1;
} else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
}
else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
/* sometimes FSOUND_STEREO/FSOUND_MONO is not set (ex. Dead Space iOS),
* or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */
if (fsbh.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) {
vgmstream->coding_type = coding_PCM16BE;
} else {
vgmstream->coding_type = coding_PCM16LE; /* ? coding_PCM16LE_int ? */
}
vgmstream->coding_type = (fsbh.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
}

View File

@ -5,17 +5,16 @@
/* FSB5 - FMOD Studio multiplatform format */
VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t StartOffset = 0;
off_t StartOffset = 0, NameOffset = 0;
off_t SampleHeaderStart = 0, DSPInfoStart = 0;
size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0;
uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0;
int LoopFlag = 0, ChannelCount = 0, SampleRate = 0, CodingID;
int TotalStreams, TargetStream = 0;
int TotalStreams, TargetStream = streamFile->stream_index;
uint32_t VorbisSetupId = 0;
int i;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"fsb")) goto fail;
@ -43,23 +42,23 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
for (i = 1; i <= TotalStreams; i++) {
off_t DataStart = 0;
size_t StreamHeaderLength = 0;
uint32_t SampleMode, SampleMode2;
uint32_t SampleMode1, SampleMode2;
/* seems ok but could use some testing against FMOD's SDK */
SampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+0x00,streamFile);
SampleMode1 = (uint32_t)read_32bitLE(SampleHeaderStart+0x00,streamFile);
SampleMode2 = (uint32_t)read_32bitLE(SampleHeaderStart+0x04,streamFile);
StreamHeaderLength += 0x08;
/* get samples */
NumSamples = ((SampleMode2 >> 2) & 0x3FFFFFFF); /* bits 31..2 (30) */
// bits 1..0 part of DataStart?
/* get offset inside data section */
DataStart = ((SampleMode >> 7) & 0x0FFFFFF) << 5; /* bits 31..8 (24) * 0x20 */
DataStart = ((SampleMode1 >> 7) & 0x1FFFFFF) << 5; /* bits 31..8 (25) * 0x20 */
//SampleMode2 bits 1..0 part of DataStart for files larger than 0x3FFFFFE0?
/* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */
switch ((SampleMode >> 5) & 0x03) { /* bits 7..6 (2) */
switch ((SampleMode1 >> 5) & 0x03) { /* bits 7..6 (2) */
case 0: ChannelCount = 1; break;
case 1: ChannelCount = 2; break;
case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */
@ -69,7 +68,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
}
/* get sample rate */
switch ((SampleMode >> 1) & 0x0f) { /* bits 5..1 (4) */
switch ((SampleMode1 >> 1) & 0x0f) { /* bits 5..1 (4) */
case 0: SampleRate = 4000; break; //???
case 1: SampleRate = 8000; break;
case 2: SampleRate = 11000; break;
@ -87,7 +86,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
}
/* get extra flags */
if (SampleMode & 0x01) { /* bit 0 (1) */
if (SampleMode1 & 0x01) { /* bit 0 (1) */
uint32_t ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd;
ExtraFlagStart = SampleHeaderStart+0x08;
@ -163,16 +162,19 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
/* continue searching */
SampleHeaderStart += StreamHeaderLength;
}
/* target stream not found*/
if (!StartOffset || !StreamSize) goto fail;
/* get stream name */
if (NameTableLength) {
NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetStream-1),streamFile);
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ChannelCount,LoopFlag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->sample_rate = SampleRate;
vgmstream->num_streams = TotalStreams;
vgmstream->num_samples = NumSamples;
@ -181,13 +183,20 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = LoopEnd;
}
vgmstream->meta_type = meta_FSB5;
if (NameOffset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile);
/* parse codec */
switch (CodingID) {
case 0x00: /* FMOD_SOUND_FORMAT_NONE */
goto fail;
case 0x01: /* FMOD_SOUND_FORMAT_PCM8 */
goto fail;
case 0x01: /* FMOD_SOUND_FORMAT_PCM8 [Anima - Gate of Memories (PC)] */
vgmstream->layout_type = ChannelCount == 1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x01;
vgmstream->coding_type = coding_PCM8_U;
break;
case 0x02: /* FMOD_SOUND_FORMAT_PCM16 */
if (ChannelCount == 1) {
@ -206,10 +215,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
case 0x04: /* FMOD_SOUND_FORMAT_PCM32 */
goto fail;
case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT */
goto fail;
case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT [Anima - Gate of Memories (PC)] */
vgmstream->coding_type = coding_PCMFLOAT;
vgmstream->layout_type = (ChannelCount == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x04;
break;
case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM */
case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM [Sonic Boom - Fire and Ice (3DS)] */
if (ChannelCount == 1) {
vgmstream->layout_type = layout_none;
} else {
@ -306,7 +318,6 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset))
goto fail;

View File

@ -9,7 +9,7 @@ static VGMSTREAM * init_vgmstream_kt_wiibgm_offset(STREAMFILE *streamFile, off_t
* It probably makes more sense to extract it externally, it's here mainly for Hyrule Warriors */
VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int type, num_streams, target_stream = 1;
int type, num_streams, target_stream = streamFile->stream_index;
off_t stream_offset;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;

View File

@ -6,7 +6,7 @@ typedef enum { XMA2 } gtd_codec;
/* GTD - found in Knights Contract (X360, PS3), Valhalla Knights 3 (PSV) */
VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, chunk_offset;
off_t start_offset, chunk_offset, stpr_offset, name_offset = 0;
size_t data_size, chunk_size;
int loop_flag, channel_count, sample_rate;
int num_samples, loop_start_sample, loop_end_sample;
@ -32,14 +32,22 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
start_offset = read_32bitBE(0x58,streamFile); /* always 0x800 */
data_size = read_32bitBE(0x5c,streamFile);
/* 0x40(18): null, 0x60(4): header size (0x70), 0x64(4): seek table size again, 0x68(8): null */
/* 0x70: seek table; then a "STPR" chunk with the file ID and filename */
/* 0x34(18): null, 0x54(4): seek table offset, 0x58(4): seek table size, 0x5c(8): null, 0x64: seek table */
stpr_offset = read_32bitBE(chunk_offset+0x54,streamFile) + read_32bitBE(chunk_offset+0x58,streamFile);;
if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */
name_offset = stpr_offset + 0xB8; /* there are offsets fields but seems to work */
}
codec = XMA2;
}
else {
/* there are PSV (LE, ATRAC9) and PS3 (MSF inside?) variations, somewhat-but-not-quite similar
* (contain the "STPR" chunk but the rest is mostly different) */
/* there are PSV (LE, ATRAC9 data) and PS3 (MSF inside?) variations, somewhat-but-not-quite similar */
/* for PSV: */
/* 0x0c: data_size, 0x10: channles, 0x14: sample rate, 0x18-0x2c: fixed and unknown values */
/* 0x2c: STPR chunk, with name_offset at + 0xE8 */
goto fail;
}
@ -53,6 +61,8 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->meta_type = meta_GTD;
if (name_offset) //encoding is Shift-Jis in some PSV files
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
switch(codec) {
#ifdef VGM_USE_FFMPEG

View File

@ -1,116 +1,171 @@
#include "../vgmstream.h"
#include "meta.h"
#include "../util.h"
#include "hca_keys.h"
#include "../coding/coding.h"
static VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
#define HCA_KEY_MAX_TEST_CLIPS 400 /* hopefully nobody masters files with more that a handful... */
#define HCA_KEY_MAX_TEST_FRAMES 100 /* ~102400 samples */
#define HCA_KEY_MAX_TEST_SAMPLES 10240 /* ~10 frames of non-blank samples */
static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2);
VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
uint8_t buffer[0x8000]; /* hca header buffer data (probably max ~0x400) */
char filename[PATH_LIMIT];
off_t start = 0;
size_t file_size = streamFile->get_size(streamFile);
int header_size;
hca_codec_data * hca_data = NULL; /* vgmstream HCA context */
clHCA * hca; /* HCA_Decoder context */
unsigned int ciphKey1, ciphKey2;
/* check extension, case insensitive */
if ( !check_extensions(streamFile, "hca")) return NULL;
return init_vgmstream_hca_offset( streamFile, 0, streamFile->get_size(streamFile) );
}
static VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
unsigned int ciphKey1;
unsigned int ciphKey2;
/* test/init header (find real header size first) */
if ( file_size < 8 ) goto fail;
if ( read_streamfile(buffer, start, 8, streamFile) != 8 ) goto fail;
char filename[PATH_LIMIT];
VGMSTREAM * vgmstream = NULL;
header_size = clHCA_isOurFile0(buffer);
if ( header_size < 0 || header_size > 0x8000 ) goto fail;
hca_codec_data * hca_file = ( hca_codec_data * ) calloc(1, sizeof(hca_codec_data) + clHCA_sizeof());
void * hca_data = NULL;
clHCA * hca;
if ( read_streamfile(buffer, start, header_size, streamFile) != header_size ) goto fail;
if ( clHCA_isOurFile1(buffer, header_size) < 0 ) goto fail;
uint8_t header[8];
int header_size;
/* init vgmstream context */
hca_data = (hca_codec_data *) calloc(1, sizeof(hca_codec_data) + clHCA_sizeof());
if (!hca_data) goto fail;
//hca_data->size = file_size;
hca_data->start = 0;
hca_data->sample_ptr = clHCA_samplesPerBlock;
if ( !hca_file ) goto fail;
/* HCA_Decoder context memory goes right after our codec data (reserved in alloc'ed) */
hca = (clHCA *)(hca_data + 1);
if ( size < 8 ) goto fail;
/* pre-load streamfile so the hca_data is ready before key detection */
streamFile->get_name( streamFile, filename, sizeof(filename) );
hca_data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!hca_data->streamfile) goto fail;
hca_file->streamfile = streamFile;
hca_file->start = start;
hca_file->size = size;
if ( read_streamfile( header, start, 8, streamFile) != 8 ) goto fail;
header_size = clHCA_isOurFile0( header );
if ( header_size < 0 ) goto fail;
hca_data = malloc( header_size );
if ( !hca_data ) goto fail;
memcpy( hca_data, header, 8 );
if ( read_streamfile( ((uint8_t*)hca_data) + 8, start + 8, header_size - 8, streamFile ) != header_size - 8 ) goto fail;
if ( clHCA_isOurFile1( hca_data, header_size ) < 0 ) goto fail;
hca = (clHCA *)(hca_file + 1);
/* try to find key in external file */
/* find decryption key in external file or preloaded list */
{
uint8_t keybuf[8];
if ( read_key_file(keybuf, 8, streamFile) ) {
ciphKey2 = get_32bitBE(keybuf+0);
ciphKey1 = get_32bitBE(keybuf+4);
} else {
/* PSO2 */
ciphKey2=0xCC554639;
ciphKey1=0x30DBE1AB;
find_hca_key(hca_data, hca, buffer, header_size, &ciphKey1, &ciphKey2);
}
}
clHCA_clear(hca, ciphKey1, ciphKey2);
if (clHCA_Decode(hca, hca_data, header_size, 0) < 0) goto fail;
/* init decoder with key */
clHCA_clear(hca, ciphKey1, ciphKey2);
if ( clHCA_Decode(hca, buffer, header_size, 0) < 0 ) goto fail;
if ( clHCA_getInfo(hca, &hca_data->info) < 0 ) goto fail;
free( hca_data );
hca_data = NULL;
if (clHCA_getInfo(hca, &hca_file->info) < 0) goto fail;
hca_file->sample_ptr = clHCA_samplesPerBlock;
hca_file->samples_discard = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(hca_data->info.channelCount, hca_data->info.loopEnabled);
if (!vgmstream) goto fail;
streamFile->get_name( streamFile, filename, sizeof(filename) );
vgmstream->sample_rate = hca_data->info.samplingRate;
vgmstream->num_samples = hca_data->info.blockCount * clHCA_samplesPerBlock;
vgmstream->loop_start_sample = hca_data->info.loopStart * clHCA_samplesPerBlock;
vgmstream->loop_end_sample = hca_data->info.loopEnd * clHCA_samplesPerBlock;
hca_file->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!hca_file->streamfile) goto fail;
vgmstream = allocate_vgmstream( hca_file->info.channelCount, 1 );
if (!vgmstream) goto fail;
vgmstream->coding_type = coding_CRI_HCA;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_HCA;
vgmstream->loop_flag = hca_file->info.loopEnabled;
vgmstream->loop_start_sample = hca_file->info.loopStart * clHCA_samplesPerBlock;
vgmstream->loop_end_sample = hca_file->info.loopEnd * clHCA_samplesPerBlock;
vgmstream->codec_data = hca_data;
vgmstream->codec_data = hca_file;
vgmstream->channels = hca_file->info.channelCount;
vgmstream->sample_rate = hca_file->info.samplingRate;
vgmstream->num_samples = hca_file->info.blockCount * clHCA_samplesPerBlock;
vgmstream->coding_type = coding_CRI_HCA;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_HCA;
return vgmstream;
return vgmstream;
fail:
if ( hca_data ) {
free( hca_data );
}
if ( hca_file ) {
free( hca_file );
}
return NULL;
free(hca_data);
return NULL;
}
/* Tries to find the decryption key from a list. Simply decodes a few frames and checks if there aren't too many
* clipped samples, as it's common for invalid keys (though possible with valid keys in poorly mastered files). */
static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2) {
sample testbuf[clHCA_samplesPerBlock];
int i;
size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
int min_clip_count = -1;
/* defaults to PSO2 key, most common */
unsigned int best_key2 = 0xCC554639;
unsigned int best_key1 = 0x30DBE1AB;
/* find a candidate key */
for (i = 0; i < keys_length; i++) {
int clip_count = 0, sample_count = 0;
int f = 0, s;
unsigned int key1, key2;
uint64_t key = hcakey_list[i].key;
key2 = (key >> 32) & 0xFFFFFFFF;
key1 = (key >> 0) & 0xFFFFFFFF;
/* re-init HCA with the current key as buffer becomes invalid (probably can be simplified) */
hca_data->curblock = 0;
hca_data->sample_ptr = clHCA_samplesPerBlock;
if ( read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile) != header_size ) continue;
clHCA_clear(hca, key1, key2);
if (clHCA_Decode(hca, buffer, header_size, 0) < 0) continue;
if (clHCA_getInfo(hca, &hca_data->info) < 0) continue;
/* test enough frames, but not too many */
while (f < HCA_KEY_MAX_TEST_FRAMES && f < hca_data->info.blockCount) {
decode_hca(hca_data, testbuf, clHCA_samplesPerBlock, hca_data->info.channelCount);
for (s = 0; s < clHCA_samplesPerBlock; s++) {
if (testbuf[s] != 0x0000 && testbuf[s] != 0xFFFF)
sample_count++; /* ignore upper/lower blank samples */
if (testbuf[s] == 0x7FFF || testbuf[s] == 0x8000)
clip_count++; /* upper/lower clip */
}
if (clip_count > HCA_KEY_MAX_TEST_CLIPS)
break; /* too many, don't bother */
if (sample_count >= HCA_KEY_MAX_TEST_SAMPLES)
break; /* enough non-blank samples tested */
f++;
}
//;VGM_LOG("HCA: key %08x%08x clip_count=%i\n", ciphKey2,ciphKey1, clip_count);
if (min_clip_count < 0 || clip_count < min_clip_count) {
min_clip_count = clip_count;
best_key2 = key2;
best_key1 = key1;
}
if (min_clip_count == 0)
break; /* can't get better than this */
/* a few clips is normal, but some invalid keys may give low numbers too */
//if (clip_count < 10)
// break;
}
/* reset HCA */
hca_data->curblock = 0;
hca_data->sample_ptr = clHCA_samplesPerBlock;
read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile);
VGM_LOG("HCA: best key=%08x%08x (clips=%i)\n", best_key2,best_key1, min_clip_count);
*out_key2 = best_key2;
*out_key1 = best_key1;
}

View File

@ -0,0 +1,106 @@
#ifndef _HCA_KEYS_H_
#define _HCA_KEYS_H_
typedef struct {
uint64_t key;
} hcakey_info;
/* CRI's tools expect an unsigned 64 bit number, but keys are commonly found online in hex form */
static const hcakey_info hcakey_list[] = {
// HCA Decoder default
{9621963164387704}, // CF222F1FE0748978
// Phantasy Star Online 2 (multi?)
// used by most console games
{0xCC55463930DBE1AB}, // CC55463930DBE1AB / 14723751768204501419
// variation from VGAudio, but some 2ch poster says the above works with CRI's tools; seems to decode the same
{24002584467202475}, // 0055463930DBE1AB
// Old Phantasy Star Online 2 (multi?)
{61891147883431481}, // 30DBE1ABCC554639
// Jojo All Star Battle (PS3)
{19700307}, // 00000000012C9A53
// Ro-Kyu-Bu! Himitsu no Otoshimono (PSP)
{2012082716}, // 0000000077EDF21C
// Ro-Kyu-Bu! Naisho no Shutter Chance (PSV)
{1234253142}, // 0000000049913556
// Idolm@ster Cinderella Stage (iOS/Android)
// Shadowverse (iOS/Android)
{59751358413602}, // 00003657F27E3B22
// Grimoire ~Shiritsu Grimoire Mahou Gakuen~ (iOS/Android)
{5027916581011272}, // 0011DCDD0DC57F48
// Idol Connect (iOS/Android)
{2424}, // 0000000000000978
// Kamen Rider Battle Rush (iOS/Android)
{29423500797988784}, // 00688884A11CCFB0
// SD Gundam Strikers (iOS/Android)
{30260840980773}, // 00001B85A6AD6125
// Sonic Runners (iOS/Android)
{19910623}, // 00000000012FCFDF
// Fate/Grand Order (iOS/Android) base assets
{12345}, // 0000000000003039
// Fate/Grand Order (iOS/Android) download assets *unconfirmed
{9117927877783581796}, // 7E89631892EBF464
// Raramagi (iOS/Android)
{45719322}, // 0000000002B99F1A
// Idolm@ster Million Live (iOS/Android)
{765765765765765}, // 0002B875BC731A85
// Kurokishi to Shiro no Maou (iOS/Android)
{3003875739822025258}, // 29AFE911F5816A2A
// Puella Magi Madoka Magica Side Story: Magia Record (iOS/Android)
// Hortensia Saga (iOS/Android)
{20536401}, // 0000000001395C51
// The Tower of Princess (iOS/Android)
{9101518402445063}, // 002055C8634B5F07
// Fallen Princess (iOS/Android)
{145552191146490718}, // 02051AF25990FB5E
// Diss World (iOS/Android)
{9001712656335836006}, // 7CEC81F7C3091366
// Ikemen Vampire - Ijin-tachi to Koi no Yuuwaku (iOS/Android)
{45152594117267709}, // 00A06A0B8D0C10FD
// Super Robot Wars X-Omega (iOS/Android)
{165521992944278}, // 0000968A97978A96
// BanG Dream! Girls Band Party! (iOS/Android)
{8910}, // 00000000000022CE
// Tokyo 7th Sisters (iOS/Android) *unconfirmed
{0xFDAE531AAB414BA1}, // FDAE531AAB414BA1
// One Piece Dance Battle (iOS/Android)
{1905818}, // 00000000001D149A
// Derby Stallion Masters (iOS/Android)
{19840202}, // 00000000012EBCCA
// World Chain (iOS/Android)
{4892292804961027794}, // 43E4EA62B8E6C6D2
// Yuyuyui (iOS/Android) *unconfirmed
{4867249871962584729}, // 438BF1F883653699
};
#endif/*_HCA_KEYS_H_*/

View File

@ -68,7 +68,7 @@ VGMSTREAM * init_vgmstream_his(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_PCM16LE;
if (channel_count == 2)
{
vgmstream->coding_type = coding_PCM16LE_int;
vgmstream->coding_type = coding_PCM16_int;
vgmstream->interleave_block_size = 2;
}
}

View File

@ -88,10 +88,6 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_amts(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xbox_stma(STREAMFILE *streamFile);
#ifdef VGM_USE_VORBIS
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile);
@ -462,8 +458,6 @@ VGMSTREAM * init_vgmstream_xbox_hlwav(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_stx(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_stm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_his(STREAMFILE* streamFile);
@ -686,4 +680,10 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_awc(STREAMFILE * streamFile);
#endif /*_META_H*/

View File

@ -920,122 +920,6 @@ fail:
return NULL;
}
/* AMTS - .amts files */
VGMSTREAM * init_vgmstream_amts(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
off_t interleave;
int channel_count;
struct dsp_header ch0_header,ch1_header;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("amts",filename_extension(filename))) goto fail;
/* check header magic */
if (read_32bitBE(0x0,streamFile) != 0x414D5453) goto fail; /* "sadb" */
channel_count=read_32bitBE(0x14,streamFile);
start_offset = 0x800;
interleave = read_32bitBE(0x08,streamFile);
if (read_dsp_header(&ch0_header, 0x20, streamFile)) goto fail;
/* check initial predictor/scale */
if (ch0_header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile))
goto fail;
if(channel_count==2) {
if (read_dsp_header(&ch1_header, 0x80, streamFile)) goto fail;
if (ch1_header.initial_ps != (uint8_t)read_8bit(start_offset+interleave,streamFile))
goto fail;
/* check type==0 and gain==0 */
if (ch0_header.format || ch0_header.gain ||
ch1_header.format || ch1_header.gain)
goto fail;
/* check for agreement */
if (
ch0_header.sample_count != ch1_header.sample_count ||
ch0_header.nibble_count != ch1_header.nibble_count ||
ch0_header.sample_rate != ch1_header.sample_rate ||
ch0_header.loop_flag != ch1_header.loop_flag ||
ch0_header.loop_start_offset != ch1_header.loop_start_offset ||
ch0_header.loop_end_offset != ch1_header.loop_end_offset
) goto fail;
if (ch0_header.loop_flag) {
off_t loop_off;
/* check loop predictor/scale */
loop_off = ch0_header.loop_start_offset/16*8;
loop_off = (loop_off/interleave*interleave*2) + (loop_off%interleave);
if (ch0_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile))
goto fail;
if (ch1_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off+interleave,streamFile))
goto fail;
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,ch0_header.loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = ch0_header.sample_count;
vgmstream->sample_rate = ch0_header.sample_rate;
/* TODO: adjust for interleave? */
vgmstream->loop_start_sample = dsp_nibbles_to_samples(
ch0_header.loop_start_offset);
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
ch0_header.loop_end_offset)+1;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_DSP_AMTS;
/* coeffs */
for (i=0;i<16;i++) {
vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i];
vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i];
}
/* initial history */
/* always 0 that I've ever seen, but for completeness... */
vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1;
vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2;
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if(channel_count==2) {
vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1;
vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2;
vgmstream->ch[1].streamfile = vgmstream->ch[0].streamfile;
}
if (!vgmstream->ch[0].streamfile) goto fail;
/* open the file for reading */
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+i*interleave;
}
return vgmstream;
fail:
/* clean up anything we may have opened */
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
/* .wsi as found in Alone in the Dark for Wii */
/* These appear to be standard .dsp, but interleaved in a blocked format */

View File

@ -4,7 +4,7 @@
/* P3D - from Radical's Prototype 1/2 (PC/PS3/X360) */
VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, parse_offset;
off_t start_offset, parse_offset, name_offset = 0;
size_t header_size, file_size, data_size;
int loop_flag = 0, channel_count, sample_rate, codec;
int i, name_count, text_len, block_size = 0, block_count = 0, num_samples;
@ -49,9 +49,12 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
if (name_count != 2 && name_count != 3) goto fail; /* 2: Prototype1, 3: Prototype2 */
parse_offset += 4;
for (i = 0; i < 2; i++) { /* skip names */
text_len = read_32bit(parse_offset,streamFile);
parse_offset += 4 + text_len + 1;
/* skip names */
for (i = 0; i < 2; i++) {
if (!name_offset)
name_offset = parse_offset + 4;
text_len = read_32bit(parse_offset,streamFile) + 1; /* null-terminated */
parse_offset += 4 + text_len;
}
/* info count? */
@ -65,8 +68,8 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
/* extra "Music" string in Prototype 2 */
if (name_count == 3) {
text_len = read_32bit(parse_offset,streamFile);
parse_offset += 4 + text_len + 1;
text_len = read_32bit(parse_offset,streamFile) + 1; /* null-terminated */
parse_offset += 4 + text_len;
}
@ -129,6 +132,8 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->meta_type = meta_P3D;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
/* codec init */
switch(codec) {

View File

@ -53,7 +53,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
vgmstream->current_block_offset=8+32*numSounds;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x20,streamFile);
vgmstream->coding_type = coding_PCM16LE_int;
vgmstream->coding_type = coding_PCM16_int;
vgmstream->num_samples = (int32_t)((get_streamfile_size(streamFile)-vgmstream->current_block_offset)/2/channel_count);
if(loop_flag)
{

View File

@ -1,77 +1,49 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* NPFS - found in Namco PS2/PSP games:
* Tekken 5, Ace Combat 5, Yumeria, Venus & Braves (.nps), Ridge Racer PSP, etc
*/
/* NPFS - found in Namco PS2/PSP games (Tekken 5, Ace Combat 5, Yumeria, Venus & Braves, Ridge Racer PSP) */
VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag=0;
int channel_count;
off_t start_offset;
int i;
int loop_flag, channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("npsf",filename_extension(filename)) &&
strcasecmp("nps",filename_extension(filename)))
/* check extension, case insensitive (should be .nps as per Venus & Braves data files) */
if ( !check_extensions(streamFile,"nps,npsf"))
goto fail;
/* check NPSF Header */
if (read_32bitBE(0x00,streamFile) != 0x4E505346)
if (read_32bitBE(0x00,streamFile) != 0x4E505346) /* "NPSF" */
goto fail;
/* check loop */
loop_flag = (read_32bitLE(0x14,streamFile)!=0xFFFFFFFF);
channel_count=read_32bitLE(0x0C,streamFile);
loop_flag = (read_32bitLE(0x14,streamFile) != 0xFFFFFFFF);
channel_count = read_32bitLE(0x0C,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = read_32bitLE(0x0C,streamFile);
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
/* Check for Compression Scheme */
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x08,streamFile)*28/16;
/* Get loop point values */
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1); /* single channel data */
if(vgmstream->loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile);
vgmstream->loop_end_sample = read_32bitLE(0x08,streamFile)*28/16;
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1);
}
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile)/2;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile) / 2;
vgmstream->meta_type = meta_PS2_NPSF;
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, 0x34,streamFile);
start_offset = (off_t)read_32bitLE(0x10,streamFile);
if (vgmstream->channels == 1) {
vgmstream->layout_type = layout_none;
} else {
vgmstream->layout_type = layout_interleave;
}
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x8000);
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);
}
}
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -6,14 +6,14 @@
VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, chunk_offset;
size_t data_size;
off_t start_offset, chunk_offset, name_offset = 0;
size_t data_size, chunk_size;
int loop_flag = 0, channel_count, is_separate, type, sample_rate;
int32_t loop_start, loop_end;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extensions */
/* .xws: header and data, .xwh+xwb: header + data */
/* .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");
@ -35,14 +35,16 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
/* file size (just the .xwh/xws) */
if (read_32bitLE(0x04,streamHeader)+0x10 != get_streamfile_size(streamHeader))
goto fail;
/* 0x08(4): version/type? (0x200), 0x0C: null */
/* 0x08(4): version (0x100/0x200), 0x0C: null */
/* typical chunks: FORM, FTXT, MARK, BODY (for .xws) */
if (read_32bitBE(0x10,streamHeader) != 0x464F524D) /* "FORM", main header (always first) */
goto fail;
/* 0x04: chunk size (-0x10), 0x08 version/type? (0x100), 0x0c: null */
chunk_size = read_32bitLE(0x10+0x04,streamHeader); /* size - 0x10 */
/* 0x08 version (0x100), 0x0c: null */
chunk_offset = 0x20;
/* check multi-streams */
total_streams = read_32bitLE(chunk_offset+0x00,streamHeader);
if (target_stream == 0) target_stream = 1;
@ -92,6 +94,14 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* get stream name (always follows FORM) */
if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */
chunk_offset = 0x10+0x10 + chunk_size + 0x10;
if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_streams) {
name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x04,streamHeader);
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
@ -100,6 +110,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_PS2_RXWS;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (type) {
case 0x00: /* PS-ADPCM */
@ -107,9 +119,9 @@ 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); //todo (read_32bitLE(0x38,streamFile)*28/16)/2;
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count); //todo read_32bitLE(0x3C,streamFile)/16*14;
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count); //todo read_32bitLE(0x38,streamFile)/16*14;
vgmstream->num_samples = ps_bytes_to_samples(loop_end, 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);
break;
case 0x01: /* PCM */

View File

@ -1,78 +0,0 @@
#include "meta.h"
#include "../util.h"
/* STM: Red Dead Revolver */
VGMSTREAM * init_vgmstream_ps2_stm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("ps2stm",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x0,streamFile) != 0x53544d41) goto fail;
if (read_32bitBE(0x4,streamFile) != 0x6b690000) goto fail;
/* check bps */
if (read_32bitLE(0x10,streamFile) != 4) goto fail;
loop_flag = read_32bitLE(0x20,streamFile);
channel_count = read_32bitLE(0x14,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x800;
vgmstream->sample_rate = (uint16_t)read_32bitLE(0xc,streamFile);
vgmstream->coding_type = coding_DVI_IMA_int;
vgmstream->num_samples = read_32bitLE(0x18,streamFile);
//vgmstream->interleave_block_size = read_32bitLE(0x8,streamFile) / channel_count;
vgmstream->interleave_block_size = 0x40;
if(1 < channel_count)
{
/* not sure if this is right ... */
vgmstream->layout_type = layout_interleave;
} else {
vgmstream->layout_type = layout_none;
}
vgmstream->meta_type = meta_PS2_STM;
if(loop_flag) {
vgmstream->loop_start_sample=read_32bitLE(0x24,streamFile);
vgmstream->loop_end_sample=vgmstream->num_samples;
}
/* 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;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -63,7 +63,11 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) {
break;
case 'p': /* "VAGp" (extended) [most common, ex Ratchet & Clank] */
if ((version <= 0x00000004) && (datasize < filesize / 2)) { /* two VAGp in the same file */
if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */
channel_count = 2; /* The Simpsons Wrestling PSX interleave */
loop_flag = 0;
}
else if ((version <= 0x00000004) && (datasize < filesize / 2)) { /* two VAGp in the same file */
if (is_swag)
loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd);
else
@ -129,7 +133,15 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) {
case 'p': // VAGp
interleave=0x10;
if ((version == 0x00000004) && (datasize < filesize / 2)) {
if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */
interleave = 0x6000; /* The Simpsons Wrestling PSX interleave, includes header */
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_PS2_VAGs;
vgmstream->num_samples = datasize / 16 * 28;
start_offset = 0x30;
}
else if ((version == 0x00000004) && (datasize < filesize / 2)) {
vgmstream->channels=2;
vgmstream->layout_type=layout_interleave;
vgmstream->meta_type=meta_PS2_VAGs;
@ -212,6 +224,9 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) {
vgmstream->loop_end_sample += (int32_t)(loopEnd%(interleave*channel_count))/16*28;
}
/* always, but can be null or used as special string */
read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile);
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )

View File

@ -9,11 +9,13 @@ static off_t get_rws_string_size(off_t off, STREAMFILE *streamFile);
/* RWS - RenderWare Stream (from games using RenderWare Audio middleware) */
VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, off, coefs_offset = 0, stream_offset = 0;
off_t start_offset, off, coefs_offset = 0, stream_offset = 0, name_offset = 0;
int loop_flag = 0, channel_count = 0, codec = 0, sample_rate = 0;
size_t file_size, header_size, data_size, stream_size = 0, block_size_max = 0, block_size = 0;
size_t file_size, header_size, data_size, stream_size_full = 0, stream_size = 0, stream_size_expected = 0;
size_t block_size = 0, block_size_total = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int i, total_segments, total_streams, target_stream = 0;
int i, total_segments;
int total_streams, target_stream = streamFile->stream_index;
if (!check_extensions(streamFile,"rws"))
@ -42,87 +44,126 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
/* inside header chunk (many unknown fields are probably IDs/config, as two same-sized files vary a lot) */
off = 0x0c + 0x0c;
/* 0x00: actual header size (less than chunk size), useful to check endianness (GC/Wii/X360 = BE) */
read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE;
/* 0x04-14: sizes of various sections?, others: ? */
/* get 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(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */
total_segments = read_32bit(off+0x20,streamFile);
total_streams = read_32bit(off+0x28,streamFile);
/* 0x2c: unk, 0x30: 0x800?, 0x34: max block size?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid? */
off += 0x50 + get_rws_string_size(off+0x50, streamFile); /* skip audio file name */
/* check streams/segments */
/* Data can be divided into segments (cues/divisions within data, ex. intro+main, voice1+2+..N) or
* tracks/streams in interleaved blocks; last track (only?) has some padding. Tracks seems to be used for multichannel.
* ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN) */
/* skip audio file name */
off += 0x50 + get_rws_string_size(off+0x50, streamFile);
/* Data is divided into "segments" (cues/divisions within data, ex. intro+main, voice1+2+..N) and "streams"
* of interleaved blocks (for multichannel?). last stream (only?) has padding. Segments divide all streams.
* ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN). */
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
/* skip segment stuff and get stream size (from sizes for all segments, repeated per track) */
off += 0x20 * total_segments; /* segment data (mostly unknown except @ 0x18: full data size, 0x1c: offset) */
/* get segment info, for all streams */
/* 0x00/04/0c: command?, 0x18: full segment size (including all streams), 0x1c: offset, others: ?) */
for (i = 0; i < total_segments; i++) {
stream_size_full += read_32bit(off + 0x20*i + 0x18,streamFile);
}
off += 0x20 * total_segments;
/* get usable segment sizes (usually ok but sometimes > stream_size), per stream */
for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */
stream_size += read_32bit(off + 0x04 * i + 0x04 * total_segments*(target_stream-1),streamFile);
stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_stream-1),streamFile);
}
off += 0x04 * (total_segments * total_streams);
off += 0x10 * total_segments; /* segment uuids? */
for (i = 0; i < total_segments; i++) { /* skip segments names */
/* skip segment uuids */
off += 0x10 * total_segments;
/* skip segment names */
for (i = 0; i < total_segments; i++) {
off += get_rws_string_size(off, streamFile);
}
/* get stream layout: 0xc: samples per frame (ex. 28 in VAG), 0x24: offset within data chunk, others: ? */
/* get block_size for our target stream and from all streams, to skip their blocks during decode */
/* get stream layout */
/* 0x00/04/14: command?, 0x08: null? 0x0c: spf related? (XADPCM=07, VAG=1C, DSP=0E, PCM=01)
* 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */
for (i = 0; i < total_streams; i++) { /* get block_sizes */
block_size_max += read_32bit(off+0x10 + 0x28*i,streamFile); /* includes padding and can be different per stream */
if (target_stream-1 == i) {
block_size = read_32bit(off+0x20 + 0x28*i,streamFile); /* actual size */
stream_offset = read_32bit(off+0x24 + 0x28*i,streamFile); /* within data */
block_size_total += read_32bit(off + 0x10 + 0x28*i, streamFile); /* for all streeams, to skip during decode */
if (i+1 == target_stream) {
//block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */
block_size = read_32bit(off + 0x20 + 0x28*i, streamFile); /* without padding */
stream_offset = read_32bit(off + 0x24 + 0x28*i, streamFile); /* within data */
}
}
off += 0x28 * total_streams;
/* get stream config: 0x0c(1): bits per sample, others: ? */
for (i = 0; i < total_streams; i++) {/* size depends on codec so we must parse it */
sample_rate = read_32bit(off+0x00, streamFile);
//unk_size = read_32bit(off+0x08, streamFile); /* segment size? loop-related? */
channel_count = read_8bit(off+0x0d, streamFile);
codec = read_32bitBE(off+0x1c, streamFile); /* uuid of 128b but first 32b is enough */
/* get stream config */
/* 0x04: command?, 0x0c(1): bits per sample, others: null? */
for (i = 0; i < total_streams; i++) { /* size depends on codec so we must parse it */
int prev_codec = 0;
if (i+1 == target_stream) {
sample_rate = read_32bit(off+0x00, streamFile);
//unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */
channel_count = read_8bit(off+0x0d, streamFile);
codec = read_32bitBE(off+0x1c, streamFile); /* uuid of 128b but first 32b is enough */
}
prev_codec = read_32bitBE(off+0x1c, streamFile);
off += 0x2c;
if (codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */
if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */
/* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */
coefs_offset = off + 0x1c;
if (i+1 == target_stream) {
coefs_offset = off + 0x1c;
}
off += 0x60;
}
if (total_streams > 1) /* multitracks have an unknown field */
off += 0x04;
if (i == target_stream-1)
break;
off += 0x04; /* padding/garbage */
}
/* next is 0x14 * streams = ?(4) + uuid? (header ends), rest is garbage/padding until chunk end (may contain strings and weird stuff) */
/* skip stream uuids */
off += 0x10 * total_streams;
start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset; /* usually 0x800 but not always */
/* get stream name */
for (i = 0; i < total_streams; i++) {
if (i+1 == target_stream) {
name_offset = off;
}
off += get_rws_string_size(off, streamFile);
}
/* rest is padding/garbage until chunk end (may contain strings and weird stuff) */
// ...
/* usually 0x800 but not always */
start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset;
/* sometimes it's wrong for no apparent reason (probably a bug in RWS) */
stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_streams) / total_streams;
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;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RWS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_RWS;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
vgmstream->layout_type = layout_rws_blocked;
vgmstream->current_block_size = block_size / vgmstream->channels;
vgmstream->full_block_size = block_size_max;
vgmstream->full_block_size = block_size_total;
switch(codec) {
case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */
/* The Legend of Spyro (X360) */
vgmstream->coding_type = coding_PCM16BE;
//vgmstream->interleave_block_size = block_size / 2; //0x2; //todo 2ch PCM not working correctly (interleaved PCM not ok?)
/* ex. The Legend of Spyro (X360) */
vgmstream->coding_type = coding_PCM16_int;
vgmstream->codec_endian = 1; /* big */
vgmstream->interleave_block_size = 0x02; /* only used to setup channels */
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
break;
@ -152,7 +193,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_XBOX;
vgmstream->interleave_block_size = 0; /* uses regular XBOX/MS-IMA interleave */
vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x48, channel_count);
vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x24 * channel_count, channel_count);
break;
default:

View File

@ -8,13 +8,13 @@
VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, data_offset, chunk_offset;
off_t start_offset, data_offset, chunk_offset, name_offset = 0;
size_t data_size;
int is_sgx, is_sgb;
int loop_flag, channels, type;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extension, case insensitive */
@ -48,7 +48,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
}
/* typical chunks: WAVE, NAME (strings), RGND, SEQD (related to SFX), WSUR, WMKR, BUSS */
/* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */
/* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */
if (is_sgx) { /* position after chunk+size */
if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */
@ -69,7 +69,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/
/* 0x00 ? (00/01/02) */
/* 0x04 sometimes global offset to wave_name */
if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */
name_offset = read_32bitLE(chunk_offset+0x04,streamHeader);
type = read_8bit(chunk_offset+0x08,streamHeader);
channels = read_8bit(chunk_offset+0x09,streamHeader);
/* 0x0a null */
@ -97,6 +98,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
@ -107,6 +109,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_SGXD;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
/* needs -1 to match RIFF AT3's loop chunk
* (maybe SGXD = "loop before this sample" rather than "loop after this sample" as expected by vgmstream) */

View File

@ -20,26 +20,6 @@ typedef struct _SCDINTSTREAMFILE
static STREAMFILE *open_scdint_with_STREAMFILE(STREAMFILE *file, const char * filename, off_t start_offset, off_t interleave_block_size, off_t stride_size, size_t total_size);
#ifdef VGM_USE_VORBIS
/* V3 decryption table found in the .exe */
static const uint8_t scd_ogg_v3_lookuptable[256] = { /* FF XIV Heavensward */
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
};
static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read);
static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read);
#endif
@ -53,7 +33,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
int headers_entries;
int32_t loop_start, loop_end;
int target_stream = 1; /* N=Nth stream, 0=auto (first) */
int target_stream = streamFile->stream_index;
int loop_flag = 0, channel_count, codec_id;
int aux_chunk_count;
@ -129,30 +109,30 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
post_meta_offset = meta_offset + 0x20;
start_offset = post_meta_offset + read_32bit(meta_offset+0x18,streamFile);
aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile);
/* 0x01e(e): unknown, seen in some FF XIV sfx (IMA) */
/* only "MARK" chunk is known (some FF XIV PS3 have "STBL" but it's not counted) */
if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx has 0x01000000 */
VGM_LOG("SCD: unknown aux chunk count %i\n", aux_chunk_count);
goto fail;
}
/* skips aux chunks, sometimes needed (Lightning Returns X360, FF XIV PC) */
if (aux_chunk_count && read_32bitBE(post_meta_offset, streamFile) == 0x4D41524B) { /* "MARK" */
post_meta_offset += read_32bit(post_meta_offset+0x04, streamFile);
}
#ifdef VGM_USE_VORBIS
/* special case using init_vgmstream_ogg_vorbis with callbacks */
if (codec_id == 0x6)
{
vgm_vorbis_info_t inf;
uint32_t seek_table_size;
uint32_t vorb_header_size;
VGMSTREAM * result = NULL;
/* todo this skips the "MARK" chunk for FF XIV "03.scd", but without it actually the start_offset
* lands in a "OggS" though will fail in the "try skipping seek table"
* maybe this isn't needed and there is another bug, since other games with "MARK" don't need this */
if (aux_chunk_count) {
post_meta_offset = meta_offset + 0x20;
/* data at meta_offset is only 0x20 bytes, but there may be auxiliary chunks before anything else */
for (; aux_chunk_count > 0; aux_chunk_count--) {
post_meta_offset += read_32bit(post_meta_offset+4,streamFile); /* skip aux chunks */
}
start_offset = post_meta_offset + read_32bit(meta_offset+0x18,streamFile);
}
uint32_t seek_table_size, vorb_header_size;
uint8_t xor_version, xor_byte;
vgm_vorbis_info_t inf;
seek_table_size = read_32bit(post_meta_offset+0x10, streamFile);
vorb_header_size = read_32bit(post_meta_offset+0x14, streamFile);
memset(&inf, 0, sizeof(inf));
inf.loop_start = loop_start;
@ -163,61 +143,68 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
inf.layout_type = layout_ogg_vorbis;
inf.meta_type = meta_SQEX_SCD;
result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf);
/* the following could be simplified but it's not clear what field signals that seek table exists
* (seems that encrypted = always seek table, but maybe post_meta_offset+0x01 = 0x20) */
if (result != NULL) {
return result;
/* try regular Ogg with default values */
{
result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf);
if (result != NULL)
return result;
}
// try skipping seek table
/* skip seek table and try regular Ogg again */
{
seek_table_size = read_32bit(post_meta_offset+0x10, streamFile);
vorb_header_size = read_32bit(post_meta_offset+0x14, streamFile);
if ((post_meta_offset-meta_offset) + seek_table_size + vorb_header_size != read_32bit(meta_offset+0x18, streamFile)) {
return NULL;
}
start_offset = post_meta_offset + 0x20 + seek_table_size;
result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf);
if (result != NULL) {
if (result != NULL)
return result;
}
}
// failed with Ogg, try deobfuscating header
/* try encrypted Ogg (with seek table already skipped) */
{
// skip chunks before xor_byte
uint8_t xor_version, xor_byte;
xor_version = read_8bit(post_meta_offset + 0x00, streamFile);
xor_byte = read_8bit(post_meta_offset + 0x02, streamFile);
if (xor_byte == 0)
return NULL; /* not actually encrypted, happens but should be handled above */
xor_version = read_8bit(post_meta_offset + 0, streamFile);
xor_byte = read_8bit(post_meta_offset + 2, streamFile);
if (xor_byte == 0) {
return NULL;
}
if (xor_version <= 2) {
if (xor_version == 2) { /* header is XOR'ed using byte */
inf.decryption_enabled = 1;
inf.decryption_callback = scd_ogg_decrypt_v2_callback;
inf.scd_xor = xor_byte;
inf.scd_xor_length = vorb_header_size; /* header is XOR'ed */
} else if (xor_version == 3) {
inf.scd_xor_length = vorb_header_size;
}
else if (xor_version == 3) { /* full file is XOR'ed using table */
inf.decryption_enabled = 1;
inf.decryption_callback = scd_ogg_decrypt_v3_callback;
inf.scd_xor = stream_size & 0xFF; /* xor_byte is not used? */
inf.scd_xor_length = stream_size; /* full file is XOR'ed */
inf.scd_xor = stream_size & 0xFF; /* xor_byte is not used? (also there is data at +0x03) */
inf.scd_xor_length = stream_size;
}
result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf);
/* always? */
return result;
else {
VGM_LOG("SCD: unknown encryption 0x%x\n", xor_version);
return NULL;
}
/* hope this works */
return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf);
}
}
#endif
/* build the VGMSTREAM */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bit(meta_offset+8,streamFile);
vgmstream->num_streams = headers_entries;
vgmstream->meta_type = meta_SQEX_SCD;
@ -225,7 +212,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
switch (codec_id) {
case 0x1:
/* PCM */
vgmstream->coding_type = coding_PCM16LE_int;
vgmstream->coding_type = coding_PCM16_int;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = stream_size / 2 / channel_count;
@ -568,6 +555,25 @@ static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, vo
}
static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read) {
/* V3 decryption table found in the .exe */
static const uint8_t scd_ogg_v3_lookuptable[256] = { /* FF XIV Heavensward */
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
};
ogg_vorbis_streamfile *ov_streamfile = (ogg_vorbis_streamfile*)datasource;
/* file is XOR'd with a table (algorithm and table by Ioncannon) */

View File

@ -0,0 +1,118 @@
#include "meta.h"
#include "../coding/coding.h"
/* STM - from Angel Studios/Rockstar San Diego games (Red Dead Revolver, Midnight Club 2, TransWorld Surf) */
VGMSTREAM * init_vgmstream_stm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count;
int big_endian, bps, interleave, data_size, loop_start = 0, loop_end = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
/* check extension, case insensitive
* .stm is the real ext but common, rename to .lstm or .stma/amts/ps2stm (older) */
if ( !check_extensions(streamFile,"stm,lstm,stma,amts,ps2stm"))
goto fail;
/* check header */
if ((read_32bitBE(0x00,streamFile) != 0x53544d41) && /* "SMTA" (LE) */
(read_32bitBE(0x00,streamFile) != 0x414D5453)) /* "AMTS" (BE) */
goto fail;
/* 0x04: revision (696F/696B/696A/6969) */
big_endian = (read_32bitBE(0x00,streamFile) == 0x414D5453);
if (big_endian) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
start_offset = 0x800;
interleave = read_32bit(0x08,streamFile);
bps = read_32bit(0x10,streamFile);
channel_count = read_32bit(0x14,streamFile);
data_size = read_32bit(0x18,streamFile);
loop_end = read_32bit(0x1c,streamFile); /* absolute offset */
if (data_size + start_offset != get_streamfile_size(streamFile)) goto fail;
if (big_endian) {
/* GC AMTS have a regular DSP header beyond 0x20, just use what we need, no point on validating all fields */
loop_flag = read_16bit(0x2c,streamFile);
}
else {
/* PS2/Xbox STMA have either loop info or padding beyond 0x20 */
if (read_32bit(0x20,streamFile) == 1) { /* may contain 0xCCCCCCCC garbage */
loop_flag = 1;
loop_start = read_32bit(0x24,streamFile);
/* 0x28 (32b * ch): loop start hist+step per channel */ //todo setup
}
#if 0
/* this works for some files that do full repeats, but also repeats many SFX */
else if (data_size != loop_end && data_size != loop_end - 0x100) { /* data_size vs adjusted loop_end */
loop_flag = 1;
}
#endif
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bit(0xc,streamFile);
vgmstream->meta_type = meta_STM;
vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none;
switch(bps) {
case 4:
if (big_endian) { /* GC DSP ADPCM (TransWorld Surf GC) */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = read_32bit(0x20,streamFile);
vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bit(0x30,streamFile));
vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bit(0x34,streamFile))+1;
dsp_read_coefs_be(vgmstream, streamFile, 0x3c, 0x60);
dsp_read_hist_be(vgmstream, streamFile, 0x60, 0x60);
}
else { /* DVI IMA ADPCM (Red Dead Revolver, Midnight Club 2) */
vgmstream->coding_type = coding_DVI_IMA_int;
/* 'interleave not' reliable, strange values and rarely needs 0x80 */
vgmstream->interleave_block_size = (interleave == 0xc000) ? 0x80 : 0x40;
vgmstream->num_samples = ima_bytes_to_samples(data_size, vgmstream->channels);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = ima_bytes_to_samples(loop_end - start_offset, vgmstream->channels);
}
break;
case 16: /* PCM (Spy Hunter 2 PS2, rare) */
vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE;
vgmstream->interleave_block_size = 0x02; /* interleave not always reliable */
vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, bps);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end - start_offset, vgmstream->channels, bps);
break;
default:
VGM_LOG("STM: unknown bps %i\n", bps);
goto fail;
}
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -6,12 +6,12 @@
VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, chunk_offset, first_offset = 0x60;
off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
int is_separate;
int loop_flag, channels, type;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extension, case insensitive */
@ -85,6 +85,20 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */
if (is_separate && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */
/* table: relative offset (32b) + hash? (32b) + cue index (32b) */
int i;
int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /*can be more than streams */
for (i = 0; i < num_entries; i++) {
uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader);
if (index+1 == target_stream) {
name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader);
break;
}
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
@ -96,7 +110,8 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_SXD;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (type) {
case 0x01: /* HEVAG */

View File

@ -3,6 +3,8 @@
#include "../layout/layout.h"
#include "../util.h"
#define TXTH_LINE_MAX 0x2000
/* known TXTH types */
typedef enum {
PSX = 0, /* PSX ADPCM */
@ -390,13 +392,6 @@ fail:
static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth) {
off_t off = 0;
off_t file_size = get_streamfile_size(streamText);
#ifndef LINE_MAX /* some platforms define this via limits.h */
#if defined(_MSC_VER) && (_MSC_VER < 1900)
enum { LINE_MAX = 0x2000 }; /* arbitrary max */
#else
const size_t LINE_MAX = 0x2000;
#endif
#endif
txth->data_size = get_streamfile_size(streamFile); /* for later use */
@ -406,15 +401,15 @@ static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_hea
/* read lines */
while (off < file_size) {
char line[LINE_MAX];
char key[LINE_MAX];
char val[LINE_MAX]; /* at least as big as a line to avoid overflows (I hope) */
char line[TXTH_LINE_MAX];
char key[TXTH_LINE_MAX];
char val[TXTH_LINE_MAX]; /* at least as big as a line to avoid overflows (I hope) */
int ok;
off_t line_start = off, line_end = 0;
line[0] = key[0] = val[0] = 0;
/* find line end */
while (line_end == 0 && off - line_start < LINE_MAX) {
while (line_end == 0 && off - line_start < TXTH_LINE_MAX) {
char c = (char)read_8bit(off, streamText);
if (c == '\n')
line_end = off;
@ -629,8 +624,12 @@ fail:
}
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
if (!txth->channels)
return 0; /* div-by-zero is no fun */
switch(txth->codec) {
case MS_IMA:
if (!txth->interleave) return 0;
return ms_ima_bytes_to_samples(bytes, txth->interleave, txth->channels);
case XBOX:
return ms_ima_bytes_to_samples(bytes, 0x24 * txth->channels, txth->channels);
@ -647,10 +646,13 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case PCM8_U:
return pcm_bytes_to_samples(bytes, txth->channels, 8);
case MSADPCM:
if (!txth->interleave) return 0;
return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels);
case ATRAC3:
if (!txth->interleave) return 0;
return atrac3_bytes_to_samples(bytes, txth->interleave);
case ATRAC3PLUS:
if (!txth->interleave) return 0;
return atrac3plus_bytes_to_samples(bytes, txth->interleave);
/* XMA bytes-to-samples is done at the end as the value meanings are a bit different */
@ -668,6 +670,7 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case NGC_DTK:
return bytes / 32 * 28; /* always stereo? */
case APPLE_IMA4:
if (!txth->interleave) return 0;
return (bytes / txth->interleave) * (txth->interleave - 2) * 2;
case MPEG: /* a bit complex */

View File

@ -1,100 +1,76 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
/* VGS (from Guitar Hero Encore - Rocks the 80s) */
/* VGS - from Guitar Hero Encore - Rocks the 80s, Guitar Hero II PS2 */
VGMSTREAM * init_vgmstream_vgs(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
size_t channel_size = 0, stream_data_size, stream_frame_count;
int channel_count = 0, loop_flag = 0, sample_rate = 0, stream_sample_rate;
int i;
int loop_flag;
int channel_flag;
int channel_flag_offset;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("vgs",filename_extension(filename))) goto fail;
if (!check_extensions(streamFile,"vgs"))
goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x56675321) /* "VgS!" */
goto fail;
/* 0x04: version? */
loop_flag = 0;
channel_flag_offset = get_streamfile_size(streamFile)-0x10;
channel_flag = read_32bitBE(channel_flag_offset,streamFile);
/* Only seen files up to 5 channels, but just
to be sure we will look up to 8 chanels */
switch (channel_flag) {
case 0x00800000:
channel_count = 1;
break;
case 0x00810000:
channel_count = 2;
break;
case 0x00820000:
channel_count = 3;
break;
case 0x00830000:
channel_count = 4;
break;
case 0x00840000:
channel_count = 5;
break;
case 0x00850000:
channel_count = 6;
break;
case 0x00860000:
channel_count = 7;
break;
case 0x00870000:
channel_count = 8;
break;
default:
goto fail;
}
/* contains N streams, which can have one less frame, or half frame and sample rate */
for (i = 0; i < 8; i++) {
stream_sample_rate = read_32bitLE(0x08 + 0x08*i + 0x00,streamFile);
stream_frame_count = read_32bitLE(0x08 + 0x08*i + 0x04,streamFile);
stream_data_size = stream_frame_count*0x10;
if (stream_sample_rate == 0)
break;
if (!sample_rate || !channel_size) {
sample_rate = stream_sample_rate;
channel_size = stream_data_size;
}
/* some streams end 1 frame early */
if (channel_size - 0x10 == stream_data_size) {
channel_size -= 0x10;
}
/* Guitar Hero II sometimes uses half sample rate for last stream */
if (sample_rate != stream_sample_rate) {
VGM_LOG("VGS: ignoring stream %i\n", i);
//total_streams++; // todo handle substreams
break;
}
channel_count++;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x80;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
vgmstream->coding_type = coding_PSX_badflags;
vgmstream->num_samples = (read_32bitLE(0x0C,streamFile)*channel_count*0x10)*28/16/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = (read_32bitLE(0x0C,streamFile)*channel_count*0x10)*28/16/channel_count;
}
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(channel_size*channel_count, channel_count);
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->coding_type = coding_PSX_badflags; /* flag = stream/channel number */
vgmstream->layout_type = layout_blocked_vgs;
vgmstream->meta_type = meta_VGS;
/* 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;
}
}
/* open files; channel offsets are updated below */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
block_update_vgs(start_offset, vgmstream);
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,64 +0,0 @@
#include "meta.h"
#include "../util.h"
/* STMA
STMA (found in Midnight Club 2)
*/
VGMSTREAM * init_vgmstream_xbox_stma(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag=0;
int channel_count;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("stma",filename_extension(filename))) goto fail;
if(read_32bitBE(0x0,streamFile)!=0x53544D41)
goto fail;
loop_flag = ((read_32bitLE(0x20,streamFile)==1) || (read_32bitLE(0x18,streamFile)>read_32bitLE(0x1C,streamFile)));
/* Seems that the loop flag is not allways well defined */
/* Some of the tracks should loop, but without flag set */
channel_count=read_32bitLE(0x14,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(0x0C,streamFile);
vgmstream->coding_type = coding_DVI_IMA_int;
vgmstream->num_samples = read_32bitLE(0x18,streamFile)*2/vgmstream->channels;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size=0x40;
vgmstream->meta_type = meta_XBOX_STMA;
if(loop_flag) {
vgmstream->loop_start_sample=read_32bitLE(0x24,streamFile);
vgmstream->loop_end_sample=vgmstream->num_samples;
}
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,36);
vgmstream->ch[i].offset = 0x800+(i*vgmstream->interleave_block_size);
if (!vgmstream->ch[i].streamfile) goto fail;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -63,13 +63,15 @@ typedef struct {
uint32_t loop_end_sample;
} xwb_header;
static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_header * xwb, STREAMFILE *streamFile);
/* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */
VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, off, suboff;
xwb_header xwb;
int target_stream = 0;
int target_stream = streamFile->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
@ -336,6 +338,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = xwb.loop_end_sample;
vgmstream->num_streams = xwb.streams;
vgmstream->meta_type = meta_XWB;
get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile);
switch(xwb.codec) {
case PCM:
@ -462,3 +465,329 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* ****************************************************************************** */
/* XSB parsing from xwb_split (mostly untouched), could be improved */
#define XSB_XACT1_MAX 11
#define XSB_XACT2_MAX 41
/**
* XWB contain stream info (channels, loop, data etc), often from multiple streams.
* XSBs contain info about how to play sounds (volume, pitch, name, etc) from XWBs (music or SFX).
* We only need to parse the XSB for the stream names.
*/
typedef struct {
int sound_count;
} xsb_wavebank;
typedef struct {
int stream_index; /* stream id in the xwb (doesn't need to match xsb sound order) */
int wavebank; /* xwb id, if the xsb has multiple wavebanks */
off_t name_index; /* name order */
off_t name_offset; /* global offset to the name string */
off_t sound_offset; /* global offset to the xsb sound */
off_t unk_index; /* some kind of number up to sound_count or 0xffff */
} xsb_sound;
typedef struct {
/* XSB header info */
xsb_sound * xsb_sounds; /* array of sounds info from the xsb, simplified */
xsb_wavebank * xsb_wavebanks; /* array of wavebank info from the xsb, simplified */
off_t xsb_sounds_offset;
size_t xsb_sounds_count;
size_t xsb_simple_sounds_offset; /* sound cues */
size_t xsb_simple_sounds_count;
size_t xsb_complex_sounds_offset;
size_t xsb_complex_sounds_count;
size_t xsb_wavebanks_count;
off_t xsb_nameoffsets_offset;
} xsb_header;
/* try to find the stream name in a companion XSB file, a comically complex cue format. */
static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_header * xwb, STREAMFILE *streamXwb) {
STREAMFILE *streamFile;
int i,j, start_sound, cfg__start_sound = 0, cfg__selected_wavebank = 0;
int xsb_version;
off_t off, suboff, name_offset = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
xsb_header xsb;
memset(&xsb,0,sizeof(xsb_header)); /* before any "fail"! */
streamFile = open_stream_ext(streamXwb, "xsb");
if (!streamFile) goto fail;
//todo try common names (xwb and xsb often are named slightly differently using a common convention)
/* check header */
if ((read_32bitBE(0x00,streamFile) != 0x5344424B) && /* "SDBK" (LE) */
(read_32bitBE(0x00,streamFile) != 0x4B424453)) /* "KBDS" (BE) */
goto fail;
if (read_32bitBE(0x00,streamFile) == 0x5344424B) { /* SDBK */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
} else {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
}
/* read main header (SoundBankHeader) */
xsb_version = read_16bit(0x04, streamFile);
if ((xwb->version <= XACT1_1_MAX && xsb_version > XSB_XACT1_MAX) || (xwb->version <= XACT2_2_MAX && xsb_version > XSB_XACT2_MAX)) {
VGM_LOG("XSB: xsb and xwb are from different XACT versions (xsb v%i vs xwb v%i)", xsb_version, xwb->version);
goto fail;
}
off = 0;
if (xsb_version <= XSB_XACT1_MAX) {
xsb.xsb_wavebanks_count = 1; //read_8bit(0x22, streamFile);
xsb.xsb_sounds_count = read_16bit(0x1e, streamFile);//@ 0x1a? 0x1c?
//xsb.xsb_names_size = 0;
//xsb.xsb_names_offset = 0;
xsb.xsb_nameoffsets_offset = 0;
xsb.xsb_sounds_offset = 0x38;
} else if (xsb_version <= XSB_XACT2_MAX) {
xsb.xsb_simple_sounds_count = read_16bit(0x09, streamFile);
xsb.xsb_complex_sounds_count = read_16bit(0x0B, streamFile);
xsb.xsb_wavebanks_count = read_8bit(0x11, streamFile);
xsb.xsb_sounds_count = read_16bit(0x12, streamFile);
//0x14: 16b unk
//xsb.xsb_names_size = read_32bit(0x16, streamFile);
xsb.xsb_simple_sounds_offset = read_32bit(0x1a, streamFile);
xsb.xsb_complex_sounds_offset = read_32bit(0x1e, streamFile); //todo 0x1e?
//xsb.xsb_names_offset = read_32bit(0x22, streamFile);
xsb.xsb_nameoffsets_offset = read_32bit(0x3a, streamFile);
xsb.xsb_sounds_offset = read_32bit(0x3e, streamFile);
} else {
xsb.xsb_simple_sounds_count = read_16bit(0x13, streamFile);
xsb.xsb_complex_sounds_count = read_16bit(0x15, streamFile);
xsb.xsb_wavebanks_count = read_8bit(0x1b, streamFile);
xsb.xsb_sounds_count = read_16bit(0x1c, streamFile);
//xsb.xsb_names_size = read_32bit(0x1e, streamFile);
xsb.xsb_simple_sounds_offset = read_32bit(0x22, streamFile);
xsb.xsb_complex_sounds_offset = read_32bit(0x26, streamFile);
//xsb.xsb_names_offset = read_32bit(0x2a, streamFile);
xsb.xsb_nameoffsets_offset = read_32bit(0x42, streamFile);
xsb.xsb_sounds_offset = read_32bit(0x46, streamFile);
}
VGM_ASSERT(xsb.xsb_sounds_count < xwb->streams,
"XSB: number of streams in xsb lower than xwb (xsb %i vs xwb %i)", xsb.xsb_sounds_count, xwb->streams);
VGM_ASSERT(xsb.xsb_simple_sounds_count + xsb.xsb_complex_sounds_count != xsb.xsb_sounds_count,
"XSB: number of xsb sounds doesn't match simple + complex sounds (simple %i, complex %i, total %i)", xsb.xsb_simple_sounds_count, xsb.xsb_complex_sounds_count, xsb.xsb_sounds_count);
/* init stuff */
xsb.xsb_sounds = calloc(xsb.xsb_sounds_count, sizeof(xsb_sound));
if (!xsb.xsb_sounds) goto fail;
xsb.xsb_wavebanks = calloc(xsb.xsb_wavebanks_count, sizeof(xsb_wavebank));
if (!xsb.xsb_wavebanks) goto fail;
/* The following is a bizarre soup of flags, tables, offsets to offsets and stuff, just to get the actual name.
* info: https://wiki.multimedia.cx/index.php/XACT */
/* parse xsb sounds */
off = xsb.xsb_sounds_offset;
for (i = 0; i < xsb.xsb_sounds_count; i++) {
xsb_sound *s = &(xsb.xsb_sounds[i]);
uint32_t flag;
size_t size;
if (xsb_version <= XSB_XACT1_MAX) {
/* The format seems constant */
flag = read_8bit(off+0x00, streamFile);
size = 0x14;
if (flag != 0x01) {
VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented", flag, off);
goto fail;
}
s->wavebank = 0; //read_8bit(off+suboff + 0x02, streamFile);
s->stream_index = read_16bit(off+0x02, streamFile);
s->sound_offset = off;
s->name_offset = read_16bit(off+0x04, streamFile);
}
else {
/* Each XSB sound has a variable size and somewhere inside is the stream/wavebank index.
* Various flags control the sound layout, but I can't make sense of them so quick hack instead */
flag = read_8bit(off+0x00, streamFile);
//0x01 16b unk, 0x03: 8b unk 04: 16b unk, 06: 8b unk
size = read_16bit(off+0x07, streamFile);
if (!(flag & 0x01)) { /* simple sound */
suboff = 0x09;
} else { /* complex sound */
/* not very exact but seems to work */
if (flag==0x01 || flag==0x03 || flag==0x05 || flag==0x07) {
if (size == 0x49) { //grotesque hack for Eschatos (these flags are way too complex)
suboff = 0x23;
} else if (size % 2 == 1 && read_16bit(off+size-0x2, streamFile)!=0) {
suboff = size - 0x08 - 0x07; //7 unk bytes at the end
} else {
suboff = size - 0x08;
}
} else {
VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented", flag, off);
goto fail;
}
}
s->stream_index = read_16bit(off+suboff + 0x00, streamFile);
s->wavebank = read_8bit(off+suboff + 0x02, streamFile);
s->sound_offset = off;
}
if (s->wavebank+1 > xsb.xsb_wavebanks_count) {
VGM_LOG("XSB: unknown xsb wavebank id %i at offset 0x%lx", s->wavebank, off);
goto fail;
}
xsb.xsb_wavebanks[s->wavebank].sound_count += 1;
off += size;
}
/* parse name offsets */
if (xsb_version > XSB_XACT1_MAX) {
/* "cue" name order: first simple sounds, then complex sounds
* Both aren't ordered like the sound entries, instead use a global offset to the entry
*
* ex. of a possible XSB:
* name 1 = simple sound 1 > sound entry 2 (points to xwb stream 4): stream 4 uses name 1
* name 2 = simple sound 2 > sound entry 1 (points to xwb stream 1): stream 1 uses name 2
* name 3 = complex sound 1 > sound entry 3 (points to xwb stream 3): stream 3 uses name 3
* name 4 = complex sound 2 > sound entry 4 (points to xwb stream 2): stream 2 uses name 4
*
* Multiple cues can point to the same sound entry but we only use the first name (meaning some won't be used) */
off_t n_off = xsb.xsb_nameoffsets_offset;
off = xsb.xsb_simple_sounds_offset;
for (i = 0; i < xsb.xsb_simple_sounds_count; i++) {
off_t sound_offset = read_32bit(off + 0x01, streamFile);
off += 0x05;
/* find sound by offset */
for (j = 0; j < xsb.xsb_sounds_count; j++) {
xsb_sound *s = &(xsb.xsb_sounds[j]);;
/* update with the current name offset */
if (!s->name_offset && sound_offset == s->sound_offset) {
s->name_offset = read_32bit(n_off + 0x00, streamFile);
s->unk_index = read_16bit(n_off + 0x04, streamFile);
n_off += 0x06;
break;
}
}
}
off = xsb.xsb_complex_sounds_offset;
for (i = 0; i < xsb.xsb_complex_sounds_count; i++) {
off_t sound_offset = read_32bit(off + 0x01, streamFile);
off += 0x0f;
/* find sound by offset */
for (j = 0; j < xsb.xsb_sounds_count; j++) {
xsb_sound *s = &(xsb.xsb_sounds[j]);;
/* update with the current name offset */
if (!s->name_offset && sound_offset == s->sound_offset) {
s->name_offset = read_32bit(n_off + 0x00, streamFile);
s->unk_index = read_16bit(n_off + 0x04, streamFile);
n_off += 0x06;
break;
}
}
}
}
// todo: it's possible to find the wavebank using the name
/* try to find correct wavebank, in cases of multiple */
if (!cfg__selected_wavebank) {
for (i = 0; i < xsb.xsb_wavebanks_count; i++) {
xsb_wavebank *w = &(xsb.xsb_wavebanks[i]);
//CHECK_EXIT(w->sound_count == 0, "ERROR: xsb wavebank %i has no sounds", i); //Ikaruga PC
if (w->sound_count == xwb->streams) {
if (!cfg__selected_wavebank) {
VGM_LOG("XSB: multiple xsb wavebanks with the same number of sounds, use -w to specify one of the wavebanks");
goto fail;
}
cfg__selected_wavebank = i+1;
}
}
}
/* banks with different number of sounds but only one wavebank, just select the first */
if (!cfg__selected_wavebank && xsb.xsb_wavebanks_count==1) {
cfg__selected_wavebank = 1;
}
if (!cfg__selected_wavebank) {
VGM_LOG("XSB: multiple xsb wavebanks but autodetect didn't work");
goto fail;
}
if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count == 0) {
VGM_LOG("XSB: xsb selected wavebank %i has no sounds", cfg__selected_wavebank);
goto fail;
}
if (cfg__start_sound) {
if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - (cfg__start_sound-1) < xwb->streams) {
VGM_LOG("XSB: starting sound too high (max in selected wavebank is %i)", xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - xwb->streams + 1);
goto fail;
}
} else {
/*
if (!cfg->ignore_names_not_found)
CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count > xwb->streams_count, "ERROR: number of streams in xsb wavebank bigger than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
if (!cfg->ignore_names_not_found)
CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count < xwb->streams_count, "ERROR: number of streams in xsb wavebank lower than xwb (xsb %i vs xwb %i), use -n to ignore (some names won't be extracted)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
*/
//if (!cfg->ignore_names_not_found)
// CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count != xwb->streams_count, "ERROR: number of streams in xsb wavebank different than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
}
/* *************************** */
start_sound = cfg__start_sound ? cfg__start_sound-1 : 0;
/* get name offset */
for (i = start_sound; i < xsb.xsb_sounds_count; i++) {
xsb_sound *s = &(xsb.xsb_sounds[i]);
VGM_LOG("wa=%i, sel=%i, si=%i vs ti=%i\n", s->wavebank, cfg__selected_wavebank, s->stream_index, target_stream);
if (s->wavebank == cfg__selected_wavebank-1
&& s->stream_index == target_stream-1){
name_offset = s->name_offset;
break;
}
}
if (name_offset)
read_string(buf,maxsize, name_offset,streamFile);
//return; /* no return, let free */
fail:
free(xsb.xsb_sounds);
free(xsb.xsb_wavebanks);
return;
}

View File

@ -12,7 +12,7 @@
#define STREAMFILE_IGNORE_EOF 0
/* buffered file reader */
/* a STREAMFILE that operates via standard IO using a buffer */
typedef struct {
STREAMFILE sf; /* callbacks */
FILE * infile; /* actual FILE */
@ -31,7 +31,8 @@ typedef struct {
#endif
} STDIOSTREAMFILE;
static STREAMFILE * open_stdio_streamfile_buffer_by_FILE(FILE *infile,const char * const filename, size_t buffersize);
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize);
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize);
static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOSTREAMFILE * streamfile) {
size_t length_read_total=0;
@ -211,12 +212,13 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil
if (!filename)
return NULL;
// if same name, duplicate the file pointer we already have open
if (!strcmp(streamFile->name,filename)) {
if (((newfd = dup(fileno(streamFile->infile))) >= 0) &&
(newfile = fdopen( newfd, "rb" )))
{
newstreamFile = open_stdio_streamfile_buffer_by_FILE(newfile,filename,buffersize);
newstreamFile = open_stdio_streamfile_buffer_by_file(newfile,filename,buffersize);
if (newstreamFile) {
return newstreamFile;
}
@ -228,7 +230,7 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil
return open_stdio_streamfile_buffer(filename,buffersize);
}
static STREAMFILE * open_stdio_streamfile_buffer_by_FILE(FILE *infile,const char * const filename, size_t buffersize) {
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize) {
uint8_t * buffer;
STDIOSTREAMFILE * streamfile;
@ -269,14 +271,14 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_FILE(FILE *infile,const char
return &streamfile->sf;
}
STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize) {
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize) {
FILE * infile;
STREAMFILE *streamFile;
infile = fopen(filename,"rb");
if (!infile) return NULL;
streamFile = open_stdio_streamfile_buffer_by_FILE(infile,filename,buffersize);
streamFile = open_stdio_streamfile_buffer_by_file(infile,filename,buffersize);
if (!streamFile) {
fclose(infile);
}
@ -284,6 +286,18 @@ STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t bu
return streamFile;
}
STREAMFILE * open_stdio_streamfile(const char * filename) {
return open_stdio_streamfile_buffer(filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
}
STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) {
return open_stdio_streamfile_buffer_by_file(file,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
}
/* **************************************************** */
/* Read a line into dst. The source files are MS-DOS style,
* separated (not terminated) by CRLF. Return 1 if the full line was
* retrieved (if it could fit in dst), 0 otherwise. In any case the result
@ -344,6 +358,28 @@ size_t get_streamfile_dos_line(int dst_length, char * dst, off_t offset,
}
/* reads a c-string, up to maxsize or NULL, returning size. buf is optional. */
int read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamFile) {
int i;
for (i=0; i < maxsize; i++) {
char c = read_8bit(offset + i, streamFile);
if (buf) buf[i] = c;
if (c == '\0')
return i;
if (i+1 == maxsize) { /* null at maxsize and don't validate (expected to be garbage) */
if (buf) buf[i] = '\0';
return maxsize;
}
if (c < 0x20 || c > 0xA5)
goto fail;
}
fail:
if (buf) buf[0] = '\0';
return 0;
}
/**
* Opens an stream using the base streamFile name plus a new extension (ex. for headers in a separate file)
*/

View File

@ -42,34 +42,44 @@
#endif
#endif
/* struct representing a file with callbacks. Code should use STREAMFILEs and not std C functions
* to do file operations, as plugins may need to provide their own callbacks. */
typedef struct _STREAMFILE {
size_t (*read)(struct _STREAMFILE *,uint8_t * dest, off_t offset, size_t length);
size_t (*get_size)(struct _STREAMFILE *);
off_t (*get_offset)(struct _STREAMFILE *);
// for dual-file support
/* for dual-file support */
void (*get_name)(struct _STREAMFILE *,char *name,size_t length);
// for when the "name" is encoded specially, this is the actual user
// visible name
/* for when the "name" is encoded specially, this is the actual user visible name */
void (*get_realname)(struct _STREAMFILE *,char *name,size_t length);
struct _STREAMFILE * (*open)(struct _STREAMFILE *,const char * const filename,size_t buffersize);
void (*close)(struct _STREAMFILE *);
#ifdef PROFILE_STREAMFILE
size_t (*get_bytes_read)(struct _STREAMFILE *);
int (*get_error_count)(struct _STREAMFILE *);
#endif
/* Substream selection for files with multiple streams. Manually used in metas if supported.
* Not ideal here, but it's the simplest way to pass to all init_vgmstream_x functions. */
int stream_index; /* 0=default/auto (first), 1=first, N=Nth */
} STREAMFILE;
/* create a STREAMFILE from path */
STREAMFILE * open_stdio_streamfile(const char * filename);
/* create a STREAMFILE from pre-opened file path */
STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename);
/* close a file, destroy the STREAMFILE object */
static inline void close_streamfile(STREAMFILE * streamfile) {
streamfile->close(streamfile);
}
/* read from a file
*
* returns number of bytes read
*/
/* read from a file, returns number of bytes read */
static inline size_t read_streamfile(uint8_t * dest, off_t offset, size_t length, STREAMFILE * streamfile) {
return streamfile->read(streamfile,dest,offset,length);
}
@ -124,6 +134,18 @@ static inline int32_t read_32bitBE(off_t offset, STREAMFILE * streamfile) {
if (read_streamfile(buf,offset,4,streamfile)!=4) return -1;
return get_32bitBE(buf);
}
static inline int64_t read_64bitLE(off_t offset, STREAMFILE * streamfile) {
uint8_t buf[8];
if (read_streamfile(buf,offset,8,streamfile)!=8) return -1;
return get_64bitLE(buf);
}
static inline int64_t read_64bitBE(off_t offset, STREAMFILE * streamfile) {
uint8_t buf[8];
if (read_streamfile(buf,offset,8,streamfile)!=8) return -1;
return get_64bitBE(buf);
}
static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) {
uint8_t buf[1];
@ -132,26 +154,15 @@ static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) {
return buf[0];
}
/* open file with a set buffer size, create a STREAMFILE object
*
* Returns pointer to new STREAMFILE or NULL if open failed
*/
STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize);
/* open file with a default buffer size, create a STREAMFILE object
*
* Returns pointer to new STREAMFILE or NULL if open failed
*/
static inline STREAMFILE * open_stdio_streamfile(const char * const filename) {
return open_stdio_streamfile_buffer(filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
}
/* various STREAMFILE helpers functions */
size_t get_streamfile_dos_line(int dst_length, char * dst, off_t offset, STREAMFILE * infile, int *line_done_ptr);
STREAMFILE * open_stream_ext(STREAMFILE *streamFile, const char * ext);
int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
int read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile);
int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
int check_extensions(STREAMFILE *streamFile, const char * cmp_exts);

View File

@ -25,6 +25,14 @@ static inline int32_t get_32bitLE(uint8_t * p) {
return (p[0]) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
}
static inline int64_t get_64bitBE(uint8_t * p) {
return (uint64_t)(((uint64_t)p[0]<<56) | ((uint64_t)p[1]<<48) | ((uint64_t)p[2]<<40) | ((uint64_t)p[3]<<32) | ((uint64_t)p[4]<<24) | ((uint64_t)p[5]<<16) | ((uint64_t)p[6]<<8) | ((uint64_t)p[7]));
}
static inline int64_t get_64bitLE(uint8_t * p) {
return (uint64_t)(((uint64_t)p[0]) | ((uint64_t)p[1]<<8) | ((uint64_t)p[2]<<16) | ((uint64_t)p[3]<<24) | ((uint64_t)p[4]<<32) | ((uint64_t)p[5]<<40) | ((uint64_t)p[6]<<48) | ((uint64_t)p[7]<<56));
}
void put_8bit(uint8_t * buf, int8_t i);
void put_16bitLE(uint8_t * buf, int16_t i);
@ -38,6 +46,11 @@ void put_32bitBE(uint8_t * buf, int32_t i);
/* signed nibbles come up a lot */
static int nibble_to_int[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1};
static inline int get_nibble_signed(uint8_t n, int upper) {
/*return ((n&0x70)-(n&0x80))>>4;*/
return nibble_to_int[(n >> (upper?4:0)) & 0x0f];
}
static inline int get_high_nibble_signed(uint8_t n) {
/*return ((n&0x70)-(n&0x80))>>4;*/
return nibble_to_int[n>>4];
@ -70,6 +83,7 @@ void concatn(int length, char * dst, const char * src);
/* Simple stdout logging for debugging and regression testing purposes.
* Needs C99 variadic macros. */
#ifdef VGM_DEBUG_OUTPUT
/* equivalent to printf when condition is true */
#define VGM_ASSERT(condition, ...) \
do { if (condition) printf(__VA_ARGS__); } while (0)
@ -79,6 +93,9 @@ void concatn(int length, char * dst, const char * src);
/* prints file/line/func */
#define VGM_LOGF() \
do { printf("%s:%i '%s'\n", __FILE__, __LINE__, __func__); } while (0)
/* prints to a file */
#define VGM_LOGT(txt, ...) \
do { FILE *fl = fopen(txt,"a+"); if(fl){fprintf(fl,__VA_ARGS__); fflush(fl);} fclose(fl); } while(0)
/* prints a buffer/array */
#define VGM_LOGB(buf, buf_size, bytes_per_line) \
do { \
@ -89,13 +106,15 @@ void concatn(int length, char * dst, const char * src);
} \
printf("\n"); \
} while (0)
#else
#else/*VGM_DEBUG_OUTPUT*/
#define VGM_ASSERT(condition, ...) /* nothing */
#define VGM_LOG(...) /* nothing */
#define VGM_LOGF() /* nothing */
#define VGM_LOGT() /* nothing */
#define VGM_LOGB(buf, buf_size, bytes_per_line) /* nothing */
#endif
#endif/*VGM_DEBUG_OUTPUT*/
#endif

View File

@ -86,7 +86,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ws_aud,
init_vgmstream_ahx,
init_vgmstream_ivb,
init_vgmstream_amts,
init_vgmstream_svs,
init_vgmstream_riff,
init_vgmstream_rifx,
@ -139,7 +138,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ngc_wvs,
init_vgmstream_dc_str,
init_vgmstream_dc_str_v2,
init_vgmstream_xbox_stma,
init_vgmstream_xbox_matx,
init_vgmstream_de2,
init_vgmstream_vs,
@ -252,7 +250,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_pona_psx,
init_vgmstream_xbox_hlwav,
init_vgmstream_stx,
init_vgmstream_ps2_stm,
init_vgmstream_myspd,
init_vgmstream_his,
init_vgmstream_ps2_ast,
@ -347,8 +344,8 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_akb2_multi,
#ifdef VGM_USE_FFMPEG
init_vgmstream_mp4_aac_ffmpeg,
init_vgmstream_bik,
#endif
init_vgmstream_bik,
init_vgmstream_x360_ast,
init_vgmstream_wwise,
init_vgmstream_ubi_raki,
@ -369,6 +366,9 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ea_bnk,
init_vgmstream_ea_schl_fixed,
init_vgmstream_sk_aud,
init_vgmstream_stm,
init_vgmstream_ea_snu,
init_vgmstream_awc,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
@ -378,7 +378,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
/* internal version with all parameters */
VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) {
static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) {
int i, fcns_size;
if (!streamFile)
@ -446,6 +446,9 @@ VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) {
}
#endif
/* save info */
vgmstream->stream_index = streamFile->stream_index;
/* save start things so we can restart for seeking */
/* copy the channels */
memcpy(vgmstream->start_ch,vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
@ -510,6 +513,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type==coding_MPEG_custom ||
vgmstream->coding_type==coding_MPEG_ealayer3 ||
vgmstream->coding_type==coding_MPEG_layer1 ||
vgmstream->coding_type==coding_MPEG_layer2 ||
vgmstream->coding_type==coding_MPEG_layer3) {
@ -698,6 +702,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type==coding_MPEG_custom ||
vgmstream->coding_type==coding_MPEG_ealayer3 ||
vgmstream->coding_type==coding_MPEG_layer1 ||
vgmstream->coding_type==coding_MPEG_layer2 ||
vgmstream->coding_type==coding_MPEG_layer3) {
@ -869,7 +874,7 @@ int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double
* Most files cut abruply after the loop, but some do have proper endings.
* With looptimes = 1 this option should give the same output vs loop disabled */
int loop_count = (int)looptimes; /* no half loops allowed */
vgmstream->loop_target = loop_count;
//vgmstream->loop_target = loop_count; /* handled externally, as this is into-only */
return vgmstream->loop_start_sample
+ (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count
+ (vgmstream->num_samples - vgmstream->loop_end_sample);
@ -929,6 +934,9 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_ps2_strlr_blocked:
case layout_rws_blocked:
case layout_hwas_blocked:
case layout_ea_sns_blocked:
case layout_blocked_awc:
case layout_blocked_vgs:
render_vgmstream_blocked(buffer,sample_count,vgmstream);
break;
case layout_interleave_byte:
@ -964,21 +972,24 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_NGC_DSP:
return 14;
case coding_PCM16LE:
case coding_PCM16LE_int:
case coding_PCM16LE_XOR_int:
case coding_PCM16BE:
case coding_PCM16_int:
case coding_PCM8:
case coding_PCM8_U:
case coding_PCM8_int:
case coding_PCM8_SB_int:
case coding_PCM8_U_int:
case coding_ULAW:
case coding_PCMFLOAT:
return 1;
#ifdef VGM_USE_VORBIS
case coding_ogg_vorbis:
case coding_VORBIS_custom:
#endif
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
case coding_MPEG_ealayer3:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
@ -1031,6 +1042,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 28;
case coding_MAXIS_XA:
return 14*vgmstream->channels;
case coding_EA_XAS:
return 128;
case coding_WS:
/* only works if output sample size is 8 bit, which always is for WS ADPCM */
return vgmstream->ws_output_size;
@ -1043,6 +1056,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_WWISE_IMA:
case coding_REF_IMA:
return (vgmstream->interleave_block_size-4*vgmstream->channels)*2/vgmstream->channels;
case coding_AWC_IMA:
return (0x800-4)*2;
case coding_RAD_IMA_mono:
return 32;
case coding_NDS_PROCYON:
@ -1115,9 +1130,9 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_NGC_DSP:
return 8;
case coding_PCM16LE:
case coding_PCM16LE_int:
case coding_PCM16LE_XOR_int:
case coding_PCM16BE:
case coding_PCM16_int:
return 2;
case coding_PCM8:
case coding_PCM8_U:
@ -1125,6 +1140,9 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_PCM8_SB_int:
case coding_PCM8_U_int:
case coding_ULAW:
return 1;
case coding_PCMFLOAT:
return 4;
case coding_SDX2:
case coding_SDX2_int:
case coding_CBD2:
@ -1143,6 +1161,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_WWISE_IMA:
case coding_REF_IMA:
return vgmstream->interleave_block_size;
case coding_AWC_IMA:
return 0x800;
case coding_RAD_IMA_mono:
return 0x14;
case coding_NGC_DTK:
@ -1179,6 +1199,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x0F*vgmstream->channels;
case coding_EA_XA_V2:
return 1; /* the frame is variant in size (ADPCM frames of 0x0F or PCM frames) */
case coding_EA_XAS:
return 0x4c*vgmstream->channels;
case coding_WS:
return vgmstream->current_block_size;
case coding_IMA_int:
@ -1292,13 +1314,6 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do);
}
break;
case coding_PCM16LE_int:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_pcm16LE_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do);
}
break;
case coding_PCM16LE_XOR_int:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_pcm16LE_XOR_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
@ -1313,6 +1328,14 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do);
}
break;
case coding_PCM16_int:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_pcm16_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,
vgmstream->codec_endian);
}
break;
case coding_PCM8:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_pcm8(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
@ -1355,6 +1378,15 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do);
}
break;
case coding_PCMFLOAT:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_pcmfloat(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,
vgmstream->codec_endian);
}
break;
case coding_NDS_IMA:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_nds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
@ -1495,6 +1527,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do,chan);
}
break;
case coding_EA_XAS:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_ea_xas(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,chan);
}
break;
#ifdef VGM_USE_VORBIS
case coding_ogg_vorbis:
decode_ogg_vorbis(vgmstream->codec_data,
@ -1621,6 +1660,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do,chan);
}
break;
case coding_AWC_IMA:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_awc_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do);
}
break;
case coding_WS:
for (chan=0;chan<vgmstream->channels;chan++) {
@ -1632,6 +1678,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
case coding_MPEG_ealayer3:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
@ -1867,6 +1914,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type==coding_MPEG_custom ||
vgmstream->coding_type==coding_MPEG_ealayer3 ||
vgmstream->coding_type==coding_MPEG_layer1 ||
vgmstream->coding_type==coding_MPEG_layer2 ||
vgmstream->coding_type==coding_MPEG_layer3) {
@ -2047,10 +2095,23 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
/* only interesting if more than one */
if (vgmstream->num_streams > 1) {
snprintf(temp,TEMPSIZE,
"\nnumber of streams: %d",
"\nstream count: %d",
vgmstream->num_streams);
concatn(length,desc,temp);
}
if (vgmstream->num_streams > 1 && vgmstream->stream_index > 0) {
snprintf(temp,TEMPSIZE,
"\nstream index: %d",
vgmstream->stream_index);
concatn(length,desc,temp);
}
if (vgmstream->stream_name[0] != '\0') {
snprintf(temp,TEMPSIZE,
"\nstream name: %s",
vgmstream->stream_name);
concatn(length,desc,temp);
}
}
/* filename search pairs for dual file stereo */

View File

@ -6,6 +6,7 @@
#define _VGMSTREAM_H
enum { PATH_LIMIT = 32768 };
enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */
#include "streamfile.h"
@ -77,23 +78,26 @@ enum { PATH_LIMIT = 32768 };
#include "nwa_decoder.h"
#endif
/* The encoding type specifies the format the sound data itself takes */
typedef enum {
/* 16-bit PCM */
/* PCM */
coding_PCM16LE, /* little endian 16-bit PCM */
coding_PCM16LE_int, /* little endian 16-bit PCM with sample-level interleave */
coding_PCM16LE_XOR_int, /* little endian 16-bit PCM with sample-level xor */
coding_PCM16BE, /* big endian 16-bit PCM */
coding_PCM16_int, /* 16-bit PCM with sample-level interleave */
/* 8-bit PCM */
coding_PCM8, /* 8-bit PCM */
coding_PCM8_int, /* 8-Bit PCM with sample-level interleave */
coding_PCM8_U, /* 8-bit PCM, unsigned (0x80 = 0) */
coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave */
coding_PCM8_SB_int, /* 8-bit PCM, sign bit (others are 2's complement) with sample-level interleave */
coding_ULAW, /* 8-bit u-Law (non-linear PCM) */
/* 4-bit ADPCM */
coding_PCMFLOAT, /* 32 bit float PCM */
/* ADPCM */
coding_CRI_ADX, /* CRI ADX */
coding_CRI_ADX_fixed, /* CRI ADX, encoding type 2 with fixed coefficients */
coding_CRI_ADX_exp, /* CRI ADX, encoding type 4 with exponential scale */
@ -117,6 +121,7 @@ typedef enum {
coding_EA_XA_int, /* Electronic Arts EA-XA ADPCM v1 (mono/interleave) */
coding_EA_XA_V2, /* Electronic Arts EA-XA ADPCM v2 */
coding_MAXIS_XA, /* Maxis EA-XA ADPCM */
coding_EA_XAS, /* Electronic Arts EA-XAS ADPCM */
coding_XBOX, /* XBOX IMA ADPCM */
coding_XBOX_int, /* XBOX IMA ADPCM (interleaved) */
@ -136,6 +141,7 @@ typedef enum {
coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */
coding_WWISE_IMA, /* Audiokinetic Wwise IMA ADPCM */
coding_REF_IMA, /* Reflections IMA ADPCM */
coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */
coding_MSADPCM, /* Microsoft ADPCM */
coding_WS, /* Westwood Studios VBR ADPCM */
@ -172,6 +178,7 @@ typedef enum {
#ifdef VGM_USE_MPEG
coding_MPEG_custom, /* MPEG audio with custom features (MDCT-based) */
coding_MPEG_ealayer3, /* EALayer3, custom MPEG frames */
coding_MPEG_layer1, /* MP1 MPEG audio (MDCT-based) */
coding_MPEG_layer2, /* MP2 MPEG audio (MDCT-based) */
coding_MPEG_layer3, /* MP3 MPEG audio (MDCT-based) */
@ -238,6 +245,9 @@ typedef enum {
layout_ps2_strlr_blocked,
layout_rws_blocked,
layout_hwas_blocked,
layout_ea_sns_blocked, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */
layout_blocked_awc, /* Rockstar AWC */
layout_blocked_vgs, /* Guitar Hero II */
/* otherwise odd */
layout_acm, /* libacm layout */
@ -271,7 +281,6 @@ typedef enum {
meta_DSP_STR, /* Conan .str files */
meta_DSP_SADB, /* .sad */
meta_DSP_WSI, /* .wsi */
meta_DSP_AMTS, /* .amts */
meta_DSP_WII_IDSP, /* .gcm with IDSP header */
meta_DSP_WII_MUS, /* .mus */
meta_DSP_WII_WSD, /* Phantom Brave (WII) */
@ -443,7 +452,6 @@ typedef enum {
meta_ADS, /* Gauntlet Dark Legends (GC) */
meta_PS2_SPS, /* Ape Escape 2 */
meta_PS2_XA2_RRP, /* RC Revenge Pro */
meta_PS2_STM, /* Red Dead Revolver .stm, renamed .ps2stm */
meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */
meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */
@ -451,7 +459,6 @@ typedef enum {
meta_XBOX_RIFF, /* XBOX RIFF/WAVE File */
meta_XBOX_WVS, /* XBOX WVS */
meta_NGC_WVS, /* Metal Arms - Glitch in the System */
meta_XBOX_STMA, /* XBOX STMA */
meta_XBOX_MATX, /* XBOX MATX */
meta_XBOX_XMU, /* XBOX XMU */
meta_XBOX_XVAS, /* XBOX VAS */
@ -623,6 +630,10 @@ typedef enum {
meta_TXTH, /* generic text header */
meta_SK_AUD, /* Silicon Knights .AUD (Eternal Darkness GC) */
meta_AHX, /* CRI AHX header */
meta_STM, /* Angel Studios/Rockstar San Diego Games */
meta_BINK, /* RAD Game Tools BINK audio/video */
meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */
meta_AWC, /* Rockstar AWC (GTA5, RDR) */
#ifdef VGM_USE_VORBIS
meta_OGG_VORBIS, /* Ogg Vorbis */
@ -705,7 +716,11 @@ typedef struct {
coding_t coding_type; /* type of encoding */
layout_t layout_type; /* type of layout for data */
meta_t meta_type; /* how we know the metadata */
int num_streams; /* info only, for a few multi-stream formats (0=not set/one, 1=one stream) */
/* streams (info only) */
int num_streams; /* for multi-stream formats (0=not set/one, 1=one stream) */
int stream_index; /* current stream */
char stream_name[STREAM_NAME_SIZE]; /* name of the current stream, if the file stores it and it's filled */
/* looping */
int loop_flag; /* is this stream looped? */
@ -803,6 +818,7 @@ typedef enum {
VORBIS_WWISE, /* many variations (custom setup, headers and data) */
VORBIS_OGL, /* custom packet headers */
VORBIS_SK /* "OggS" replaced by "SK" */
//VORBIS_LYN /* two interleaved Ogg (including setup, duplicated) */
} vorbis_custom_t;
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
@ -840,6 +856,7 @@ typedef struct {
uint8_t * buffer; /* internal raw data buffer */
size_t buffer_size;
size_t samples_to_discard; /* for looping purposes */
int samples_full; /* flag, samples available in vorbis buffers */
@ -856,8 +873,8 @@ typedef struct {
} vorbis_custom_codec_data;
#endif
#ifdef VGM_USE_MPEG
#ifdef VGM_USE_MPEG
/* Custom MPEG modes, mostly differing in the data layout */
typedef enum {
MPEG_STANDARD, /* 1 stream */
@ -866,21 +883,43 @@ typedef enum {
MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */
MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */
MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */
MPEG_EAL31, /* EALayer3 v1, custom frames */
MPEG_EAL32P, /* EALayer3 v2 "P" (PCM?), altered custom frames */
MPEG_EAL32S, /* EALayer3 v2 "S" (Spike?), altered custom frames */
MPEG_EAL31, /* EALayer3 v1, custom frames with v1 header */
MPEG_EAL32P, /* EALayer3 v2 "P" (PCM?), custom frames with v2 header */
MPEG_EAL32S, /* EALayer3 v2 "S" (Spike?), custom frames with v2 header */
MPEG_LYN, /* N streams of fixed interleave */
MPEG_AWC /* N streams in absolute offsets (consecutive) */
MPEG_AWC /* N streams in block layout (music) or absolute offsets (sfx) */
} mpeg_custom_t;
/* config for the above modes */
typedef struct {
int channels; /* max channels */
int fsb_padding; /* fsb padding mode */
int chunk_size; /* size of a data portion */
int interleave; /* size of stream interleave */
int encryption; /* encryption mode */
int big_endian;
/* for AHX */
int cri_type;
uint16_t cri_key1;
uint16_t cri_key2;
uint16_t cri_key3;
} mpeg_custom_config;
/* represents a single MPEG stream */
typedef struct {
mpg123_handle *m; /* MPEG decoder */
uint8_t *output_buffer; /* decoded samples from this stream (in bytes for mpg123) */
size_t output_buffer_size;
size_t samples_filled; /* data in the buffer (in samples) */
size_t samples_used; /* data extracted from the buffer */
size_t current_size_count; /* data read (if the parser needs to know) */
size_t current_size_target; /* max data, until something happens */
size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */
} mpeg_custom_stream;
typedef struct {
uint8_t *buffer; /* internal raw data buffer */
size_t buffer_size;
@ -899,19 +938,11 @@ typedef struct {
mpeg_custom_t type; /* mpeg subtype */
mpeg_custom_config config; /* config depending on the mode */
mpg123_handle **ms; /* custom MPEG decoder array */
size_t ms_size;
mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */
size_t streams_size;
uint8_t *stream_buffer; /* contains samples from N frames of one stream */
size_t stream_buffer_size;
uint8_t *sample_buffer; /* contains samples from N frames of all streams/channels */
size_t sample_buffer_size;
size_t bytes_in_sample_buffer;
size_t bytes_used_in_sample_buffer;
size_t samples_to_discard; /* for custom mpeg looping */
size_t skip_samples; /* base encoder delay */
size_t samples_to_discard; /* for custom mpeg looping */
} mpeg_codec_data;
#endif
@ -992,12 +1023,13 @@ typedef struct {
typedef struct {
STREAMFILE *streamfile;
uint64_t start;
uint64_t size;
//uint64_t size;
clHCA_stInfo info;
unsigned int curblock;
unsigned int sample_ptr, samples_discard;
unsigned int sample_ptr;
unsigned int samples_discard;
signed short sample_buffer[clHCA_samplesPerBlock * 16];
// clHCA exists here
//clHCA * hca exists here (pre-alloc'ed)
} hca_codec_data;
#ifdef VGM_USE_FFMPEG

View File

@ -12,6 +12,8 @@
836F705C18BDC40E0095E648 /* VGMDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 836F705B18BDC40E0095E648 /* VGMDecoder.m */; };
836F705D18BDC4580095E648 /* vgmstream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 836F6B6D18BDB8890095E648 /* vgmstream.framework */; };
836F705F18BDC47F0095E648 /* vgmstream.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836F6B6D18BDB8890095E648 /* vgmstream.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83AA5D2D1F6E30080020821C /* VGMInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D281F6E30080020821C /* VGMInterface.m */; };
83AA5D2E1F6E30080020821C /* VGMContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D2A1F6E30080020821C /* VGMContainer.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -52,6 +54,11 @@
836F705B18BDC40E0095E648 /* VGMDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VGMDecoder.m; sourceTree = "<group>"; };
836F706018BDC84D0095E648 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../../Audio/Plugin.h; sourceTree = "<group>"; };
836F706118BDC8650095E648 /* PlaylistController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistController.h; path = ../../../Playlist/PlaylistController.h; sourceTree = "<group>"; };
83AA5D281F6E30080020821C /* VGMInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VGMInterface.m; sourceTree = "<group>"; };
83AA5D2A1F6E30080020821C /* VGMContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VGMContainer.m; sourceTree = "<group>"; };
83AA5D2B1F6E30080020821C /* VGMInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VGMInterface.h; sourceTree = "<group>"; };
83AA5D2C1F6E30080020821C /* VGMContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VGMContainer.h; sourceTree = "<group>"; };
83AA5D2F1F6E301B0020821C /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../../Utils/Logging.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -107,6 +114,11 @@
836F6B1918BDB80D0095E648 /* vgmstream */ = {
isa = PBXGroup;
children = (
83AA5D2F1F6E301B0020821C /* Logging.h */,
83AA5D2C1F6E30080020821C /* VGMContainer.h */,
83AA5D2A1F6E30080020821C /* VGMContainer.m */,
83AA5D2B1F6E30080020821C /* VGMInterface.h */,
83AA5D281F6E30080020821C /* VGMInterface.m */,
836F706118BDC8650095E648 /* PlaylistController.h */,
836F706018BDC84D0095E648 /* Plugin.h */,
836F705A18BDC40E0095E648 /* VGMDecoder.h */,
@ -219,6 +231,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
83AA5D2D1F6E30080020821C /* VGMInterface.m in Sources */,
83AA5D2E1F6E30080020821C /* VGMContainer.m in Sources */,
836F705C18BDC40E0095E648 /* VGMDecoder.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -0,0 +1,17 @@
//
// VGMContainer.h
// VGMStream
//
// Created by Christopher Snowhill on 9/1/17.
// Copyright 2017 __LoSnoCo__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "Plugin.h"
@interface VGMContainer : NSObject <CogContainer> {
}
@end

View File

@ -0,0 +1,60 @@
//
// VGMContainer.m
// VGMStream
//
// Created by Christopher Snowhill on 9/1/17.
// Copyright 2017 __LoSnoCo__. All rights reserved.
//
#import "VGMContainer.h"
#import "VGMDecoder.h"
#import "VGMInterface.h"
#import "Logging.h"
@implementation VGMContainer
+ (NSArray *)fileTypes
{
return [VGMDecoder fileTypes];
}
+ (NSArray *)mimeTypes
{
return nil;
}
+ (float)priority
{
return [VGMDecoder priority];
}
+ (NSArray *)urlsForContainerURL:(NSURL *)url
{
if ([url fragment]) {
// input url already has fragment defined - no need to expand further
return [NSMutableArray arrayWithObject:url];
}
VGMSTREAM * stream = init_vgmstream_from_cogfile([[[url absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String], 0);
if (!stream)
{
ALog(@"Open failed for file: %@", [url absoluteString]);
return NO;
}
NSMutableArray *tracks = [NSMutableArray array];
int i;
int subsongs = stream->num_streams;
if (subsongs == 0)
subsongs = 1;
for (i = 1; i <= subsongs; ++i) {
[tracks addObject:[NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"#%i", i]]];
}
return tracks;
}
@end

View File

@ -7,123 +7,17 @@
//
#import "VGMDecoder.h"
#import "VGMInterface.h"
#import "PlaylistController.h"
typedef struct _COGSTREAMFILE {
STREAMFILE sf;
void *file;
off_t offset;
char name[PATH_LIMIT];
} COGSTREAMFILE;
static void cogsf_seek(COGSTREAMFILE *this, off_t offset) {
NSObject* _file = (__bridge NSObject *)(this->file);
id<CogSource> __unsafe_unretained file = (id) _file;
if ([file seek:offset whence:SEEK_SET] != 0)
this->offset = offset;
else
this->offset = [file tell];
}
static off_t cogsf_get_size(COGSTREAMFILE *this) {
NSObject* _file = (__bridge NSObject *)(this->file);
id<CogSource> __unsafe_unretained file = (id) _file;
off_t offset = [file tell];
[file seek:0 whence:SEEK_END];
off_t size = [file tell];
[file seek:offset whence:SEEK_SET];
return size;
}
static off_t cogsf_get_offset(COGSTREAMFILE *this) {
return this->offset;
}
static void cogsf_get_name(COGSTREAMFILE *this, char *buffer, size_t length) {
strncpy(buffer, this->name, length);
buffer[length-1]='\0';
}
static size_t cogsf_read(COGSTREAMFILE *this, uint8_t *dest, off_t offset, size_t length) {
NSObject* _file = (__bridge NSObject *)(this->file);
id<CogSource> __unsafe_unretained file = (id) _file;
size_t read;
if (this->offset != offset)
cogsf_seek(this, offset);
read = [file read:dest amount:length];
if (read > 0)
this->offset += read;
return read;
}
static void cogsf_close(COGSTREAMFILE *this) {
CFBridgingRelease(this->file);
free(this);
}
static STREAMFILE *cogsf_create_from_path(const char *path);
static STREAMFILE *cogsf_open(COGSTREAMFILE *this, const char *const filename,size_t buffersize) {
if (!filename) return NULL;
return cogsf_create_from_path(filename);
}
static STREAMFILE *cogsf_create(id file, const char *path) {
COGSTREAMFILE *streamfile = malloc(sizeof(COGSTREAMFILE));
if (!streamfile) return NULL;
memset(streamfile,0,sizeof(COGSTREAMFILE));
streamfile->sf.read = (void*)cogsf_read;
streamfile->sf.get_size = (void*)cogsf_get_size;
streamfile->sf.get_offset = (void*)cogsf_get_offset;
streamfile->sf.get_name = (void*)cogsf_get_name;
streamfile->sf.get_realname = (void*)cogsf_get_name;
streamfile->sf.open = (void*)cogsf_open;
streamfile->sf.close = (void*)cogsf_close;
streamfile->file = (void*)CFBridgingRetain(file);
streamfile->offset = 0;
strncpy(streamfile->name, path, sizeof(streamfile->name));
return &streamfile->sf;
}
STREAMFILE *cogsf_create_from_path(const char *path) {
id<CogSource> source;
NSString * urlString = [NSString stringWithUTF8String:path];
NSURL * url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
id audioSourceClass = NSClassFromString(@"AudioSource");
source = [audioSourceClass audioSourceForURL:url];
if (![source open:url])
return 0;
if (![source seekable])
return 0;
return cogsf_create(source, path);
}
VGMSTREAM *init_vgmstream_from_cogfile(const char *path) {
STREAMFILE *sf;
VGMSTREAM *vgm = NULL;
sf = cogsf_create_from_path(path);
if (sf) {
vgm = init_vgmstream_from_STREAMFILE(sf);
cogsf_close((COGSTREAMFILE *)sf);
}
return vgm;
}
@implementation VGMDecoder
- (BOOL)open:(id<CogSource>)s
{
stream = init_vgmstream_from_cogfile([[[[s url] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String]);
int track_num = [[[s url] fragment] intValue];
stream = init_vgmstream_from_cogfile([[[[s url] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String], track_num);
if ( !stream )
return NO;

View File

@ -0,0 +1,18 @@
//
// VGMInterface.h
// VGMStream
//
// Created by Christopher Snowhill on 9/1/17.
// Copyright 2017 __LoSnoCo__. All rights reserved.
//
#import <vgmstream/vgmstream.h>
typedef struct _COGSTREAMFILE {
STREAMFILE sf;
void *file;
off_t offset;
char name[PATH_LIMIT];
} COGSTREAMFILE;
VGMSTREAM *init_vgmstream_from_cogfile(const char *path, int subsong);

View File

@ -0,0 +1,118 @@
//
// VGMInterface.m
// VGMStream
//
// Created by Christopher Snowhill on 9/1/17.
// Copyright 2017 __LoSnoCo__. All rights reserved.
//
#import "VGMInterface.h"
#import "Plugin.h"
static void cogsf_seek(COGSTREAMFILE *this, off_t offset) {
NSObject* _file = (__bridge NSObject *)(this->file);
id<CogSource> __unsafe_unretained file = (id) _file;
if ([file seek:offset whence:SEEK_SET] != 0)
this->offset = offset;
else
this->offset = [file tell];
}
static off_t cogsf_get_size(COGSTREAMFILE *this) {
NSObject* _file = (__bridge NSObject *)(this->file);
id<CogSource> __unsafe_unretained file = (id) _file;
off_t offset = [file tell];
[file seek:0 whence:SEEK_END];
off_t size = [file tell];
[file seek:offset whence:SEEK_SET];
return size;
}
static off_t cogsf_get_offset(COGSTREAMFILE *this) {
return this->offset;
}
static void cogsf_get_name(COGSTREAMFILE *this, char *buffer, size_t length) {
strncpy(buffer, this->name, length);
buffer[length-1]='\0';
}
static size_t cogsf_read(COGSTREAMFILE *this, uint8_t *dest, off_t offset, size_t length) {
NSObject* _file = (__bridge NSObject *)(this->file);
id<CogSource> __unsafe_unretained file = (id) _file;
size_t read;
if (this->offset != offset)
cogsf_seek(this, offset);
read = [file read:dest amount:length];
if (read > 0)
this->offset += read;
return read;
}
static void cogsf_close(COGSTREAMFILE *this) {
CFBridgingRelease(this->file);
free(this);
}
static STREAMFILE *cogsf_create_from_path(const char *path);
static STREAMFILE *cogsf_open(COGSTREAMFILE *this, const char *const filename,size_t buffersize) {
if (!filename) return NULL;
return cogsf_create_from_path(filename);
}
static STREAMFILE *cogsf_create(id file, const char *path) {
COGSTREAMFILE *streamfile = malloc(sizeof(COGSTREAMFILE));
if (!streamfile) return NULL;
memset(streamfile,0,sizeof(COGSTREAMFILE));
streamfile->sf.read = (void*)cogsf_read;
streamfile->sf.get_size = (void*)cogsf_get_size;
streamfile->sf.get_offset = (void*)cogsf_get_offset;
streamfile->sf.get_name = (void*)cogsf_get_name;
streamfile->sf.get_realname = (void*)cogsf_get_name;
streamfile->sf.open = (void*)cogsf_open;
streamfile->sf.close = (void*)cogsf_close;
streamfile->file = (void*)CFBridgingRetain(file);
streamfile->offset = 0;
strncpy(streamfile->name, path, sizeof(streamfile->name));
return &streamfile->sf;
}
STREAMFILE *cogsf_create_from_path(const char *path) {
id<CogSource> source;
NSString * urlString = [NSString stringWithUTF8String:path];
NSURL * url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
id audioSourceClass = NSClassFromString(@"AudioSource");
source = [audioSourceClass audioSourceForURL:url];
if (![source open:url])
return 0;
if (![source seekable])
return 0;
return cogsf_create(source, path);
}
VGMSTREAM *init_vgmstream_from_cogfile(const char *path, int subsong) {
STREAMFILE *sf;
VGMSTREAM *vgm = NULL;
if (!subsong)
subsong = 1;
sf = cogsf_create_from_path(path);
if (sf) {
sf->stream_index = subsong;
vgm = init_vgmstream_from_STREAMFILE(sf);
cogsf_close((COGSTREAMFILE *)sf);
}
return vgm;
}