diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index 21672b803..eb27adbe8 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -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 = ""; }; 831BA6171EAC61A500CF89B0 /* x360_pasx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_pasx.c; sourceTree = ""; }; 831BA6221EAC61CB00CF89B0 /* coding_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coding_utils.c; sourceTree = ""; }; - 831BA6271EAC61CB00CF89B0 /* wwise_vorbis_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wwise_vorbis_utils.h; sourceTree = ""; }; 831BD11F1EEE1CF200198540 /* ngc_ulw.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ngc_ulw.c; sourceTree = ""; }; 831BD1201EEE1D2A00198540 /* rws_blocked.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rws_blocked.c; sourceTree = ""; }; 832389481D22419B00482226 /* clHCA.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = clHCA.c; sourceTree = ""; }; @@ -540,7 +546,6 @@ 836F6DE318BDC2180095E648 /* aica_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aica_decoder.c; sourceTree = ""; }; 836F6DE418BDC2180095E648 /* at3_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = at3_decoder.c; sourceTree = ""; }; 836F6DE518BDC2180095E648 /* coding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding.h; sourceTree = ""; }; - 836F6DE618BDC2180095E648 /* ea_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_decoder.c; sourceTree = ""; }; 836F6DE718BDC2180095E648 /* g721_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g721_decoder.c; sourceTree = ""; }; 836F6DE818BDC2180095E648 /* g7221_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g7221_decoder.c; sourceTree = ""; }; 836F6DE918BDC2180095E648 /* g72x_state.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = g72x_state.h; sourceTree = ""; }; @@ -743,7 +748,6 @@ 836F6EBE18BDC2190095E648 /* ps2_spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_spm.c; sourceTree = ""; }; 836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = ""; }; 836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = ""; }; - 836F6EC118BDC2190095E648 /* ps2_stm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_stm.c; sourceTree = ""; }; 836F6EC218BDC2190095E648 /* ps2_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_str.c; sourceTree = ""; }; 836F6EC318BDC2190095E648 /* ps2_strlr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_strlr.c; sourceTree = ""; }; 836F6EC418BDC2190095E648 /* ps2_svag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag.c; sourceTree = ""; }; @@ -816,7 +820,6 @@ 836F6F0A18BDC2190095E648 /* x360_tra.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_tra.c; sourceTree = ""; }; 836F6F0B18BDC2190095E648 /* xbox_hlwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_hlwav.c; sourceTree = ""; }; 836F6F0C18BDC2190095E648 /* xbox_ims.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_ims.c; sourceTree = ""; }; - 836F6F0D18BDC2190095E648 /* xbox_stma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_stma.c; sourceTree = ""; }; 836F6F0E18BDC2190095E648 /* xbox_wavm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_wavm.c; sourceTree = ""; }; 836F6F0F18BDC2190095E648 /* xbox_xmu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xmu.c; sourceTree = ""; }; 836F6F1018BDC2190095E648 /* xbox_xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xvas.c; sourceTree = ""; }; @@ -871,6 +874,17 @@ 83A3F0721E3AD8B900D6A794 /* formats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = formats.h; sourceTree = ""; }; 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_alloc.h; sourceTree = ""; }; 83A5F75E198DF021009AF94C /* bfwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfwav.c; sourceTree = ""; }; + 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_xa_decoder.c; sourceTree = ""; }; + 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_ealayer3.c; sourceTree = ""; }; + 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_awc.c; sourceTree = ""; }; + 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_xas_decoder.c; sourceTree = ""; }; + 83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vgs.c; sourceTree = ""; }; + 83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_awc.c; sourceTree = ""; }; + 83AA5D1C1F6E2F7F0020821C /* ea_sns_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_sns_blocked.c; sourceTree = ""; }; + 83AA5D201F6E2F9B0020821C /* awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awc.c; sourceTree = ""; }; + 83AA5D211F6E2F9C0020821C /* hca_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys.h; sourceTree = ""; }; + 83AA5D221F6E2F9C0020821C /* ea_snu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_snu.c; sourceTree = ""; }; + 83AA5D231F6E2F9C0020821C /* stm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stm.c; sourceTree = ""; }; 83AB8C731E8072A100086084 /* nub_vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_vag.c; sourceTree = ""; }; 83AB8C741E8072A100086084 /* x360_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_ast.c; sourceTree = ""; }; 83BAFB6B19F45EB3005DAB60 /* bfstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfstm.c; sourceTree = ""; }; @@ -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 */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 2c85347f8..3ec4efcaf 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -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); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c similarity index 82% rename from Frameworks/vgmstream/vgmstream/src/coding/ea_decoder.c rename to Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c index 6466b1e9f..0a9e03ba8 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c @@ -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) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c new file mode 100644 index 000000000..aa9822494 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c @@ -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; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index 0947c5db3..ae31a4bef 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index 1f73a3aaa..8901653b3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -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; ioffset + 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; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index f02d7a211..ae1f244c9 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c index 01a5eb5c0..b29979922 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c @@ -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 diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c new file mode 100644 index 000000000..9d229dce7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c @@ -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 diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c new file mode 100644 index 000000000..b233b3bfb --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -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 diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index ebbd42a07..8a0cab0b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -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); } } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h index 544581a24..d6a0a6787 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h @@ -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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ngc_dsp_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ngc_dsp_decoder.c index 8bc5463c7..8e84d57e9 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ngc_dsp_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ngc_dsp_decoder.c @@ -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);; + } +} + diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c index ceb12675f..17fba4b8f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c @@ -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); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c index 2342582f2..ea350ae3c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c @@ -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; ioffset+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; ioffset+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); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/wwise_vorbis_utils.h b/Frameworks/vgmstream/vgmstream/src/coding/wwise_vorbis_utils.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 7e34ff74f..7764ab401 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -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"}, diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index 12c25ac61..c9d372e74 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_awc.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_awc.c new file mode 100644 index 000000000..763f046d3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_awc.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_vgs.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_vgs.c new file mode 100644 index 000000000..de5c0e5a5 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_vgs.c @@ -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; + } + +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ea_block.c b/Frameworks/vgmstream/vgmstream/src/layout/ea_block.c index 7efa1585e..244aad6dc 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/ea_block.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/ea_block.c @@ -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;ichannels;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; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ea_sns_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/ea_sns_blocked.c new file mode 100644 index 000000000..6e8c41e29 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/ea_sns_blocked.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 2cf51f534..b3af1efc0 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -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); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c index 21f259a03..a1e2ac250 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c @@ -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); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c new file mode 100644 index 000000000..b91ccb3a8 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bik.c b/Frameworks/vgmstream/vgmstream/src/meta/bik.c index 61db43229..20ca20ff9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bik.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bik.c @@ -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 diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_old.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_old.c index 8e40a5969..d32878be3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_old.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_old.c @@ -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; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index e493813c6..2d41606bf 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -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; } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_snu.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_snu.c new file mode 100644 index 000000000..722533333 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_snu.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 86f3429c7..733ae2932 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -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; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 649d2306d..f358cf31d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/g1l.c b/Frameworks/vgmstream/vgmstream/src/meta/g1l.c index 43e7e6eb8..ca447d119 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/g1l.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/g1l.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c index 8550c79eb..de7c5e13d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c @@ -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 diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 8a16e7b41..7417c225d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -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; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h new file mode 100644 index 000000000..e3012873e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -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_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/his.c b/Frameworks/vgmstream/vgmstream/src/meta/his.c index 63e2b2c2b..9ed5d0787 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/his.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/his.c @@ -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; } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 3b64b6737..f920bb111 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -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*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index 19a48b4b1..06eeabcde 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -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;ich[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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/p3d.c b/Frameworks/vgmstream/vgmstream/src/meta/p3d.c index cac85b641..5556fc18c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/p3d.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/p3d.c @@ -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) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_sob.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_sob.c index d6a5d7296..3754a2c22 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_sob.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/pc_sob.c @@ -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) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_npsf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_npsf.c index d37ee6c12..5289bdaae 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_npsf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_npsf.c @@ -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;ich[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; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c index 522f64706..93701d595 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c @@ -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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_stm.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_stm.c deleted file mode 100644 index eb03761fc..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_stm.c +++ /dev/null @@ -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;ich[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; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c index 6b6d6da2c..96f9552d7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c @@ -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) ) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rws.c b/Frameworks/vgmstream/vgmstream/src/meta/rws.c index 3e63690ae..fee8a1709 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rws.c @@ -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: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index 2ed824cae..e31ac99da 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -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) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index 7dc008d07..f55532618 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -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) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/stm.c b/Frameworks/vgmstream/vgmstream/src/meta/stm.c new file mode 100644 index 000000000..54146b5aa --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/stm.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c index 190b38616..71243ec78 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c @@ -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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 91314b2b5..d75af34fd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vgs.c b/Frameworks/vgmstream/vgmstream/src/meta/vgs.c index a2d8616ad..ba2e66de4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vgs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vgs.c @@ -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;ich[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; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xbox_stma.c b/Frameworks/vgmstream/vgmstream/src/meta/xbox_stma.c deleted file mode 100644 index a96750950..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/xbox_stma.c +++ /dev/null @@ -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;ich[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; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 1fbe9b1d3..99f251df1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index a2bb3b2da..32ef75fc3 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -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) */ diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 2aa417cdb..22b94742b 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -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); diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index 6d12377c7..0062e6ac3 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -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 diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index a585b050f..2ef7dbe0d 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -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;chanchannels;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;chanchannels;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;chanchannels;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;chanchannels;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;chanchannels;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;chanchannels;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;chanchannels;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;chanchannels;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;chanchannels;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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index a31f65cff..a7b2cda8f 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -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 diff --git a/Plugins/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Plugins/vgmstream/vgmstream.xcodeproj/project.pbxproj index 22703f60b..6a9c2683a 100644 --- a/Plugins/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Plugins/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -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 = ""; }; 836F706018BDC84D0095E648 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../../Audio/Plugin.h; sourceTree = ""; }; 836F706118BDC8650095E648 /* PlaylistController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistController.h; path = ../../../Playlist/PlaylistController.h; sourceTree = ""; }; + 83AA5D281F6E30080020821C /* VGMInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VGMInterface.m; sourceTree = ""; }; + 83AA5D2A1F6E30080020821C /* VGMContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VGMContainer.m; sourceTree = ""; }; + 83AA5D2B1F6E30080020821C /* VGMInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VGMInterface.h; sourceTree = ""; }; + 83AA5D2C1F6E30080020821C /* VGMContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VGMContainer.h; sourceTree = ""; }; + 83AA5D2F1F6E301B0020821C /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../../Utils/Logging.h; sourceTree = ""; }; /* 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; diff --git a/Plugins/vgmstream/vgmstream/VGMContainer.h b/Plugins/vgmstream/vgmstream/VGMContainer.h new file mode 100755 index 000000000..fca21a537 --- /dev/null +++ b/Plugins/vgmstream/vgmstream/VGMContainer.h @@ -0,0 +1,17 @@ +// +// VGMContainer.h +// VGMStream +// +// Created by Christopher Snowhill on 9/1/17. +// Copyright 2017 __LoSnoCo__. All rights reserved. +// + +#import + +#import "Plugin.h" + +@interface VGMContainer : NSObject { + +} + +@end diff --git a/Plugins/vgmstream/vgmstream/VGMContainer.m b/Plugins/vgmstream/vgmstream/VGMContainer.m new file mode 100755 index 000000000..5d43036c1 --- /dev/null +++ b/Plugins/vgmstream/vgmstream/VGMContainer.m @@ -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 diff --git a/Plugins/vgmstream/vgmstream/VGMDecoder.m b/Plugins/vgmstream/vgmstream/VGMDecoder.m index 22b18d0e8..decff34a9 100644 --- a/Plugins/vgmstream/vgmstream/VGMDecoder.m +++ b/Plugins/vgmstream/vgmstream/VGMDecoder.m @@ -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 __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 __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 __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 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)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; diff --git a/Plugins/vgmstream/vgmstream/VGMInterface.h b/Plugins/vgmstream/vgmstream/VGMInterface.h new file mode 100644 index 000000000..909dd1448 --- /dev/null +++ b/Plugins/vgmstream/vgmstream/VGMInterface.h @@ -0,0 +1,18 @@ +// +// VGMInterface.h +// VGMStream +// +// Created by Christopher Snowhill on 9/1/17. +// Copyright 2017 __LoSnoCo__. All rights reserved. +// + +#import + +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); diff --git a/Plugins/vgmstream/vgmstream/VGMInterface.m b/Plugins/vgmstream/vgmstream/VGMInterface.m new file mode 100644 index 000000000..093f2267a --- /dev/null +++ b/Plugins/vgmstream/vgmstream/VGMInterface.m @@ -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 __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 __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 __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 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; +} +