diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 2dd6dd7b1..3386223e0 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -68,7 +68,6 @@ 8306B0E420984590000302D4 /* wave.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C92098458E000302D4 /* wave.c */; }; 8306B0E520984590000302D4 /* ubi_lyn.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CA2098458E000302D4 /* ubi_lyn.c */; }; 8306B0E620984590000302D4 /* msb_msh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CB2098458E000302D4 /* msb_msh.c */; }; - 8306B0E720984590000302D4 /* opus_ppp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CC2098458E000302D4 /* opus_ppp.c */; }; 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */; }; 8306B0E920984590000302D4 /* opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CE2098458E000302D4 /* opus.c */; }; 8306B0EA20984590000302D4 /* caf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CF2098458F000302D4 /* caf.c */; }; @@ -93,7 +92,6 @@ 831BA61B1EAC61A500CF89B0 /* sgxd.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6111EAC61A500CF89B0 /* sgxd.c */; }; 831BA61C1EAC61A500CF89B0 /* sxd.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6121EAC61A500CF89B0 /* sxd.c */; }; 831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6131EAC61A500CF89B0 /* ubi_raki.c */; }; - 831BA61E1EAC61A500CF89B0 /* vawx.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6141EAC61A500CF89B0 /* vawx.c */; }; 831BA61F1EAC61A500CF89B0 /* x360_cxs.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6151EAC61A500CF89B0 /* x360_cxs.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 */; }; @@ -141,7 +139,6 @@ 8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */; }; 8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E41FE6253800E26435 /* blocked_ea_schl.c */; }; 8349A8EB1FE6253900E26435 /* blocked_ivaud.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E51FE6253800E26435 /* blocked_ivaud.c */; }; - 8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E61FE6253900E26435 /* blocked_vawx.c */; }; 8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E71FE6253900E26435 /* blocked_ea_sns.c */; }; 8349A9071FE6258200E26435 /* dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8EE1FE6257C00E26435 /* dec.c */; }; 8349A9081FE6258200E26435 /* ezw.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8EF1FE6257C00E26435 /* ezw.c */; }; @@ -533,6 +530,13 @@ 83A3F0741E3AD8B900D6A794 /* formats.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A3F0711E3AD8B900D6A794 /* formats.c */; }; 83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */; }; 83A5F75F198DF021009AF94C /* bfwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A5F75E198DF021009AF94C /* bfwav.c */; }; + 83A8BADD256679C5000F5F3F /* wady_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BADC256679C5000F5F3F /* wady_decoder.c */; }; + 83A8BADF256679E3000F5F3F /* blocked_xwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BADE256679E3000F5F3F /* blocked_xwav.c */; }; + 83A8BAE525667AA8000F5F3F /* wady.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BAE025667AA7000F5F3F /* wady.c */; }; + 83A8BAE625667AA8000F5F3F /* ps2_enth_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A8BAE125667AA7000F5F3F /* ps2_enth_streamfile.h */; }; + 83A8BAE725667AA8000F5F3F /* xse.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BAE225667AA7000F5F3F /* xse.c */; }; + 83A8BAE825667AA8000F5F3F /* xwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BAE325667AA7000F5F3F /* xwav.c */; }; + 83A8BAE925667AA8000F5F3F /* cpk.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BAE425667AA7000F5F3F /* cpk.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 */; }; @@ -812,7 +816,6 @@ 8306B0C92098458E000302D4 /* wave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wave.c; sourceTree = ""; }; 8306B0CA2098458E000302D4 /* ubi_lyn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_lyn.c; sourceTree = ""; }; 8306B0CB2098458E000302D4 /* msb_msh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msb_msh.c; sourceTree = ""; }; - 8306B0CC2098458E000302D4 /* opus_ppp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opus_ppp.c; sourceTree = ""; }; 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opus_interleave_streamfile.h; sourceTree = ""; }; 8306B0CE2098458E000302D4 /* opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opus.c; sourceTree = ""; }; 8306B0CF2098458F000302D4 /* caf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = caf.c; sourceTree = ""; }; @@ -836,7 +839,6 @@ 831BA6111EAC61A500CF89B0 /* sgxd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sgxd.c; sourceTree = ""; }; 831BA6121EAC61A500CF89B0 /* sxd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sxd.c; sourceTree = ""; }; 831BA6131EAC61A500CF89B0 /* ubi_raki.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_raki.c; sourceTree = ""; }; - 831BA6141EAC61A500CF89B0 /* vawx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vawx.c; sourceTree = ""; }; 831BA6151EAC61A500CF89B0 /* x360_cxs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_cxs.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 = ""; }; @@ -884,7 +886,6 @@ 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_1snh.c; sourceTree = ""; }; 8349A8E41FE6253800E26435 /* blocked_ea_schl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_schl.c; sourceTree = ""; }; 8349A8E51FE6253800E26435 /* blocked_ivaud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ivaud.c; sourceTree = ""; }; - 8349A8E61FE6253900E26435 /* blocked_vawx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vawx.c; sourceTree = ""; }; 8349A8E71FE6253900E26435 /* blocked_ea_sns.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_sns.c; sourceTree = ""; }; 8349A8EE1FE6257C00E26435 /* dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dec.c; sourceTree = ""; }; 8349A8EF1FE6257C00E26435 /* ezw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ezw.c; sourceTree = ""; }; @@ -1275,6 +1276,13 @@ 83A3F0711E3AD8B900D6A794 /* formats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = formats.c; 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 = ""; }; + 83A8BADC256679C5000F5F3F /* wady_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wady_decoder.c; sourceTree = ""; }; + 83A8BADE256679E3000F5F3F /* blocked_xwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xwav.c; sourceTree = ""; }; + 83A8BAE025667AA7000F5F3F /* wady.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wady.c; sourceTree = ""; }; + 83A8BAE125667AA7000F5F3F /* ps2_enth_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ps2_enth_streamfile.h; sourceTree = ""; }; + 83A8BAE225667AA7000F5F3F /* xse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xse.c; sourceTree = ""; }; + 83A8BAE325667AA7000F5F3F /* xwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwav.c; sourceTree = ""; }; + 83A8BAE425667AA7000F5F3F /* cpk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cpk.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 = ""; }; @@ -1603,6 +1611,7 @@ 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */, 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */, 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, + 83A8BADC256679C5000F5F3F /* wady_decoder.c */, 836F6DFD18BDC2180095E648 /* ws_decoder.c */, 836F6DFE18BDC2180095E648 /* xa_decoder.c */, 834FE0B0215C798C000A5D3D /* xmd_decoder.c */, @@ -1642,7 +1651,6 @@ 8306B09820984550000302D4 /* blocked_thp.c */, 8306B09920984550000302D4 /* blocked_tra.c */, 83031ECA243C50CB00C3F3E0 /* blocked_ubi_sce.c */, - 8349A8E61FE6253900E26435 /* blocked_vawx.c */, 83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */, 83031ECB243C50CB00C3F3E0 /* blocked_vid1.c */, 832BF80321E050DC006F50F1 /* blocked_vs_square.c */, @@ -1654,6 +1662,7 @@ 8306B0912098454E000302D4 /* blocked_xa.c */, 83A21F7A201D895B000F04B9 /* blocked_xvag.c */, 8306B08C2098454D000302D4 /* blocked_xvas.c */, + 83A8BADE256679E3000F5F3F /* blocked_xwav.c */, 836F6E0418BDC2180095E648 /* blocked.c */, 834FE0BD215C79A9000A5D3D /* flat.c */, 836F6E0D18BDC2180095E648 /* interleave.c */, @@ -1721,6 +1730,7 @@ 8306B0CF2098458F000302D4 /* caf.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */, 834FE0E8215C79EC000A5D3D /* ck.c */, + 83A8BAE425667AA7000F5F3F /* cpk.c */, 83FC177023AC59A800E1025F /* cri_utf.c */, 83FC176C23AC58D100E1025F /* cri_utf.h */, 83FC176B23AC58D100E1025F /* csb.c */, @@ -1879,7 +1889,6 @@ 831BA60F1EAC61A500CF89B0 /* ogl.c */, 8349A8FB1FE6257F00E26435 /* omu.c */, 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */, - 8306B0CC2098458E000302D4 /* opus_ppp.c */, 8306B0CE2098458E000302D4 /* opus.c */, 836F6E8318BDC2180095E648 /* otm.c */, 836F6E8418BDC2180095E648 /* p3d.c */, @@ -1904,6 +1913,7 @@ 836F6E9618BDC2180095E648 /* ps2_bmdx.c */, 836F6E9718BDC2180095E648 /* ps2_ccc.c */, 836F6E9818BDC2180095E648 /* ps2_dxh.c */, + 83A8BAE125667AA7000F5F3F /* ps2_enth_streamfile.h */, 836F6E9918BDC2180095E648 /* ps2_enth.c */, 836F6E9A18BDC2180095E648 /* ps2_exst.c */, 836F6E9B18BDC2180095E648 /* ps2_filp.c */, @@ -2035,7 +2045,6 @@ 834FE0DD215C79EB000A5D3D /* utk.c */, 834FE0E4215C79EC000A5D3D /* vag.c */, 834FE0D3215C79E9000A5D3D /* vai.c */, - 831BA6141EAC61A500CF89B0 /* vawx.c */, 836F6EFD18BDC2190095E648 /* vgs.c */, 83031ED7243C510400C3F3E0 /* vid1.c */, 834FE0CE215C79E8000A5D3D /* vis.c */, @@ -2048,6 +2057,7 @@ 83F0AA5D21E2028B004BBC04 /* vsv_streamfile.h */, 83F0AA5E21E2028C004BBC04 /* vsv.c */, 8349A9011FE6258000E26435 /* vxn.c */, + 83A8BAE025667AA7000F5F3F /* wady.c */, 8306B0C22098458C000302D4 /* waf.c */, 8306B0D02098458F000302D4 /* wave_segmented.c */, 8306B0C92098458E000302D4 /* wave.c */, @@ -2087,11 +2097,13 @@ 832BF81921E0514A006F50F1 /* xopus.c */, 832BF80A21E05148006F50F1 /* xpcm.c */, 832BF80C21E05148006F50F1 /* xps.c */, + 83A8BAE225667AA7000F5F3F /* xse.c */, 836F6F1218BDC2190095E648 /* xss.c */, 83AFABB923795201002F3947 /* xssb.c */, 834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */, 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, 837CEADC23487F2900E62A4A /* xvas.c */, + 83A8BAE325667AA7000F5F3F /* xwav.c */, 83C727FB22BC893800678B4A /* xwb_xsb.h */, 836F6F1318BDC2190095E648 /* xwb.c */, 83A21F7D201D8980000F04B9 /* xwc.c */, @@ -2187,6 +2199,7 @@ 83FBD506235D31F800D35BCD /* riff_ogg_streamfile.h in Headers */, 48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */, 836F6F9A18BDC2190095E648 /* meta.h in Headers */, + 83A8BAE625667AA8000F5F3F /* ps2_enth_streamfile.h in Headers */, 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */, 83031ED9243C510500C3F3E0 /* ubi_lyn_streamfile.h in Headers */, @@ -2372,6 +2385,7 @@ 839E21E41F2EDAF100EE54D7 /* vorbis_custom_utils_fsb.c in Sources */, 839E21E11F2EDAF100EE54D7 /* vorbis_custom_decoder.c in Sources */, 839E21E71F2EDAF100EE54D7 /* mpeg_custom_utils.c in Sources */, + 83A8BAE525667AA8000F5F3F /* wady.c in Sources */, 839E21E31F2EDAF100EE54D7 /* mpeg_custom_utils_ahx.c in Sources */, 8306B0AF20984552000302D4 /* blocked_rws.c in Sources */, 83C7281A22BC893D00678B4A /* mus_vc.c in Sources */, @@ -2476,11 +2490,13 @@ 836F703C18BDC2190095E648 /* wii_bns.c in Sources */, 830EBE132004656E0023AA10 /* xnb.c in Sources */, 835027131ED119E000C25929 /* mta2_decoder.c in Sources */, + 83A8BAE725667AA8000F5F3F /* xse.c in Sources */, 8306B0DB20984590000302D4 /* nxap.c in Sources */, 836F6FA718BDC2190095E648 /* nds_strm.c in Sources */, 8349A91A1FE6258200E26435 /* vxn.c in Sources */, 8349A8EB1FE6253900E26435 /* blocked_ivaud.c in Sources */, 83A3F0741E3AD8B900D6A794 /* formats.c in Sources */, + 83A8BAE925667AA8000F5F3F /* cpk.c in Sources */, 836F6F6E18BDC2190095E648 /* aix.c in Sources */, 836F6F8718BDC2190095E648 /* ffw.c in Sources */, 8349A9141FE6258200E26435 /* omu.c in Sources */, @@ -2499,6 +2515,7 @@ 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */, 83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */, 832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */, + 83A8BADD256679C5000F5F3F /* wady_decoder.c in Sources */, 8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */, 834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */, 83F0AA6121E2028C004BBC04 /* vsv.c in Sources */, @@ -2516,7 +2533,7 @@ 836F701118BDC2190095E648 /* ps3_cps.c in Sources */, 8306B0DA20984590000302D4 /* ea_wve_au00.c in Sources */, 83A21F85201D8981000F04B9 /* atx.c in Sources */, - 8306B0E720984590000302D4 /* opus_ppp.c in Sources */, + 83A8BADF256679E3000F5F3F /* blocked_xwav.c in Sources */, 836F6F6518BDC2190095E648 /* 2dx9.c in Sources */, 830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */, 836F700818BDC2190095E648 /* ps2_vms.c in Sources */, @@ -2544,6 +2561,7 @@ 8349A9181FE6258200E26435 /* ea_1snh.c in Sources */, 83AA7F7E2519C042004C5298 /* svag_kcet.c in Sources */, 8373342C23F60CDC00DE14DC /* kwb.c in Sources */, + 83A8BAE825667AA8000F5F3F /* xwav.c in Sources */, 83AA7F852519C042004C5298 /* zwv.c in Sources */, 83EED5D4203A8BC7008BEB45 /* aus.c in Sources */, 836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */, @@ -2618,7 +2636,6 @@ 836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */, 836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */, 83AA7F8D2519C076004C5298 /* decode.c in Sources */, - 8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */, 834FE0EA215C79ED000A5D3D /* aif_asobo.c in Sources */, 836F700418BDC2190095E648 /* ps2_vas.c in Sources */, 836F6F9818BDC2190095E648 /* mattel_hyperscan.c in Sources */, @@ -2743,7 +2760,6 @@ 8349A9161FE6258200E26435 /* flx.c in Sources */, 832BF82921E0514B006F50F1 /* msf_banpresto.c in Sources */, 834FE0BE215C79A9000A5D3D /* blocked_xa_aiff.c in Sources */, - 831BA61E1EAC61A500CF89B0 /* vawx.c in Sources */, 836F702A18BDC2190095E648 /* sd9.c in Sources */, 836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */, 836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 7a2410304..82619ec6d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -114,8 +114,7 @@ size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_fo /* 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(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); 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); int32_t ea_xa_bytes_to_samples(size_t bytes, int channels); @@ -165,10 +164,10 @@ int msadpcm_check_coefs(STREAMFILE* sf, off_t offset); /* yamaha_decoder */ void decode_aica(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); -void decode_aska(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_aska(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size); void decode_nxap(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t yamaha_bytes_to_samples(size_t bytes, int channels); -size_t aska_bytes_to_samples(size_t bytes, int channels); +size_t aska_bytes_to_samples(size_t bytes, size_t frame_size, int channels); /* tgcadpcm_decoder */ @@ -223,6 +222,8 @@ void decode_xmd(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, /* derf_decoder */ void decode_derf(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +/* wady_decoder */ +void decode_wady(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* circus_decoder */ typedef struct circus_codec_data circus_codec_data; @@ -539,19 +540,25 @@ typedef struct { int coupled_count; int stream_count; int channel_mapping[8]; + /* frame table */ + off_t table_offset; + int table_count; } opus_config; ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg); ffmpeg_codec_data* init_ffmpeg_switch_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); -ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip); +ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip); size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf); size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); +size_t fsb_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); #endif @@ -603,6 +610,7 @@ size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align); size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels); size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); +int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, size_t* p_loop_start, size_t* p_loop_end, int is_vbr); /* helper to pass a wrapped, clamped, fake extension-ed, SF to another meta */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c index 7e6080bc3..857dd6df7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c @@ -29,7 +29,7 @@ static const int EA_XA_TABLE[20] = { 0, -1, -3, -4 }; -/* EA XA v2 (always mono); like ea_xa_int but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */ +/* EA XA v2 (always mono); like v1 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 coef1, coef2; @@ -183,71 +183,39 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac } #endif -/* 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) { +/* EA XA v1 (mono/stereo) */ +void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel, int is_stereo) { uint8_t frame_info; int32_t coef1, coef2; int i, sample_count, shift; int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */ - int frame_size = 0x1e; + int frame_size = is_stereo ? 0x0f*2 : 0x0f; int frame_samples = 28; first_sample = first_sample % frame_samples; - /* header (coefs ch0+ch1 + shift ch0+ch1) */ - frame_info = read_8bit(stream->offset+0x00,stream->streamfile); - coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0]; - coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4]; - shift = (frame_info & 0x0F) + 8; + if (is_stereo) { + /* header (coefs ch0+ch1 + shift ch0+ch1) */ + frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); + coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0]; + coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4]; - frame_info = read_8bit(stream->offset+0x01,stream->streamfile); - shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8; - - /* samples */ - for (i=first_sample,sample_count=0; ioffset + 0x02 + i); - int nibble_shift = (hn ? 4 : 0); /* high nibble first */ - - sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); - sample_nibble = (sample_byte >> nibble_shift) & 0x0F; - new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ - new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; - new_sample = clamp16(new_sample); - - outbuf[sample_count] = new_sample; - stream->adpcm_history2_32 = stream->adpcm_history1_32; - stream->adpcm_history1_32 = new_sample; + frame_info = read_8bit(stream->offset + 0x01, stream->streamfile); + shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8; + } else { + /* header (coefs + shift ch0) */ + frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); + coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; + coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; + shift = (frame_info & 0x0F) + 8; } - /* only increment offset on complete frame */ - if (i == frame_samples) - stream->offset += frame_size; -} - -/* EA-XA v1 mono/interleave */ -void decode_ea_xa_int(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; - int i, sample_count, shift; - - int frame_size = 0x0f; - int frame_samples = 28; - first_sample = first_sample % frame_samples; - - /* header (coefs+shift ch0) */ - frame_info = read_8bit(stream->offset,stream->streamfile); - coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; - coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; - shift = (frame_info & 0x0F) + 8; - /* samples */ for (i=first_sample,sample_count=0; ioffset + 0x01 + i/2); - int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */ + off_t byte_offset = is_stereo ? (stream->offset + 0x02 + i) : (stream->offset + 0x01 + i/2); + int nibble_shift = is_stereo ? (hn ? 4 : 0) : ((!(i & 1)) ? 4 : 0); /* high nibble first */ sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); sample_nibble = (sample_byte >> nibble_shift) & 0x0F; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c index 5fde33b54..37979dd1c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c @@ -1,181 +1,181 @@ -#include "coding.h" -#include "../util.h" - -#if 0 -/* known game code/platforms use float buffer and coefs, but some approximations around use this int math: - * ... - * coef1 = table[index + 0] - * coef2 = table[index + 4] - * sample = clamp16(((signed_nibble << (20 - shift)) + hist1 * coef1 + hist2 * coef2 + 128) >> 8); */ -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 -}; -#endif - -/* standard CD-XA's K0/K1 filter pairs */ -static const float xa_coefs[16][2] = { - { 0.0, 0.0 }, - { 0.9375, 0.0 }, - { 1.796875, -0.8125 }, - { 1.53125, -0.859375 }, - /* only 4 pairs exist, assume 0s for bad indexes */ -}; - -/* EA-XAS v1, evolution of EA-XA/XAS and cousin of MTA2. Reverse engineered from various .exes/.so - * - * Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes. - * Original code reads all headers first then processes all nibbles (for CPU cache/parallelism/SIMD optimizations). - * To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */ -void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - uint8_t frame[0x4c] = {0}; - off_t frame_offset; - int group, row, i, samples_done = 0, sample_count = 0; - size_t bytes_per_frame, samples_per_frame; - - - /* internal interleave */ - bytes_per_frame = 0x4c; - samples_per_frame = 128; - first_sample = first_sample % samples_per_frame; - - frame_offset = stream->offset + bytes_per_frame * channel; - read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - - //todo: original code uses float sample buffer: - //- header pcm-hist to float-hist: hist * (1/32768) - //- nibble to signed to float: (int32_t)(pnibble << 28) * SHIFT_MUL_LUT[shift_index] - // look-up table just simplifies ((nibble << 12 << 12) >> 12 + shift) * (1/32768) - // though maybe introduces rounding errors? - //- coefs apply normally, though hists are already floats - //- final float sample isn't clamped - - - /* parse group headers */ - for (group = 0; group < 4; group++) { - float coef1, coef2; - int16_t hist1, hist2; - uint8_t shift; - uint32_t group_header = (uint32_t)get_32bitLE(frame + group*0x4); /* always LE */ - - coef1 = xa_coefs[group_header & 0x0F][0]; - coef2 = xa_coefs[group_header & 0x0F][1]; - hist2 = (int16_t)((group_header >> 0) & 0xFFF0); - hist1 = (int16_t)((group_header >> 16) & 0xFFF0); - shift = (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 nibbles = frame[4*4 + row*0x04 + group + i/2]; - int sample; - - sample = i&1 ? /* high nibble first */ - (nibbles >> 0) & 0x0f : - (nibbles >> 4) & 0x0f; - sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */ - sample = sample + hist1 * coef1 + hist2 * coef2; - 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 == samples_per_frame) { - stream->offset += bytes_per_frame * channelspacing; - } -} - - -/* EA-XAS v0, without complex layouts and closer to EA-XA. Somewhat based on daemon1's decoder */ -void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - uint8_t frame[0x13] = {0}; - off_t frame_offset; - int i, frames_in, samples_done = 0, sample_count = 0; - size_t bytes_per_frame, samples_per_frame; - - - /* external interleave (fixed size), mono */ - bytes_per_frame = 0x02 + 0x02 + 0x0f; - samples_per_frame = 1 + 1 + 0x0f*2; - frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; - - frame_offset = stream->offset + bytes_per_frame * frames_in; - read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - - //todo see above - - /* process frame */ - { - float coef1, coef2; - int16_t hist1, hist2; - uint8_t shift; - uint32_t frame_header = (uint32_t)get_32bitLE(frame); /* always LE */ - - coef1 = xa_coefs[frame_header & 0x0F][0]; - coef2 = xa_coefs[frame_header & 0x0F][1]; - hist2 = (int16_t)((frame_header >> 0) & 0xFFF0); - hist1 = (int16_t)((frame_header >> 16) & 0xFFF0); - shift = (frame_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 */ - for (i = 0; i < 0x0f*2; i++) { - uint8_t nibbles = frame[0x02 + 0x02 + i/2]; - int sample; - - sample = i&1 ? /* high nibble first */ - (nibbles >> 0) & 0x0f : - (nibbles >> 4) & 0x0f; - sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */ - sample = sample + hist1 * coef1 + hist2 * coef2; - 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; - } - } -} +#include "coding.h" +#include "../util.h" + +#if 0 +/* known game code/platforms use float buffer and coefs, but some approximations around use this int math: + * ... + * coef1 = table[index + 0] + * coef2 = table[index + 4] + * sample = clamp16(((signed_nibble << (20 - shift)) + hist1 * coef1 + hist2 * coef2 + 128) >> 8); */ +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 +}; +#endif + +/* standard CD-XA's K0/K1 filter pairs */ +static const float xa_coefs[16][2] = { + { 0.0, 0.0 }, + { 0.9375, 0.0 }, + { 1.796875, -0.8125 }, + { 1.53125, -0.859375 }, + /* only 4 pairs exist, assume 0s for bad indexes */ +}; + +/* EA-XAS (XA Seekable) Version 1, evolution of EA-XA/XAS and cousin of MTA2. Reverse engineered from various .exes/.so + * + * Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes. + * Original code reads all headers first then processes all nibbles (for CPU cache/parallelism/SIMD optimizations). + * To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */ +void decode_ea_xas_v1(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x4c] = {0}; + off_t frame_offset; + int group, row, i, samples_done = 0, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + + + /* internal interleave */ + bytes_per_frame = 0x4c; + samples_per_frame = 128; + first_sample = first_sample % samples_per_frame; + + frame_offset = stream->offset + bytes_per_frame * channel; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + + //todo: original code uses float sample buffer: + //- header pcm-hist to float-hist: hist * (1/32768) + //- nibble to signed to float: (int32_t)(pnibble << 28) * SHIFT_MUL_LUT[shift_index] + // look-up table just simplifies ((nibble << 12 << 12) >> 12 + shift) * (1/32768) + // though maybe introduces rounding errors? + //- coefs apply normally, though hists are already floats + //- final float sample isn't clamped + + + /* parse group headers */ + for (group = 0; group < 4; group++) { + float coef1, coef2; + int16_t hist1, hist2; + uint8_t shift; + uint32_t group_header = (uint32_t)get_32bitLE(frame + group*0x4); /* always LE */ + + coef1 = xa_coefs[group_header & 0x0F][0]; + coef2 = xa_coefs[group_header & 0x0F][1]; + hist2 = (int16_t)((group_header >> 0) & 0xFFF0); + hist1 = (int16_t)((group_header >> 16) & 0xFFF0); + shift = (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 nibbles = frame[4*4 + row*0x04 + group + i/2]; + int sample; + + sample = i&1 ? /* high nibble first */ + (nibbles >> 0) & 0x0f : + (nibbles >> 4) & 0x0f; + sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */ + sample = sample + hist1 * coef1 + hist2 * coef2; + 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 == samples_per_frame) { + stream->offset += bytes_per_frame * channelspacing; + } +} + + +/* EA-XAS v0 (xas0), without complex layouts and closer to EA-XA. Somewhat based on daemon1's decoder. */ +void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x13] = {0}; + off_t frame_offset; + int i, frames_in, samples_done = 0, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + + + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x02 + 0x02 + 0x0f; + samples_per_frame = 1 + 1 + 0x0f*2; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + + //todo see above + + /* process frame */ + { + float coef1, coef2; + int16_t hist1, hist2; + uint8_t shift; + uint32_t frame_header = (uint32_t)get_32bitLE(frame); /* always LE */ + + coef1 = xa_coefs[frame_header & 0x0F][0]; + coef2 = xa_coefs[frame_header & 0x0F][1]; + hist2 = (int16_t)((frame_header >> 0) & 0xFFF0); + hist1 = (int16_t)((frame_header >> 16) & 0xFFF0); + shift = (frame_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 */ + for (i = 0; i < 0x0f*2; i++) { + uint8_t nibbles = frame[0x02 + 0x02 + i/2]; + int sample; + + sample = i&1 ? /* high nibble first */ + (nibbles >> 0) & 0x0f : + (nibbles >> 4) & 0x0f; + sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */ + sample = sample + hist1 * coef1 + hist2 * coef2; + 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; + } + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c index 93eb023db..2d47a60b3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c @@ -17,14 +17,13 @@ * https://github.com/hcs64/ww2ogg */ -typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X } opus_type_t; +typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB, OPUS_WWISE } opus_type_t; static size_t make_oggs_first(uint8_t *buf, int buf_size, opus_config *cfg); static size_t make_oggs_page(uint8_t *buf, int buf_size, size_t data_size, int page_sequence, int granule); static size_t opus_get_packet_samples(const uint8_t *buf, int len); -static size_t opus_get_packet_samples_sf(STREAMFILE *sf, off_t offset); -static size_t get_xopus_packet_size(int packet, STREAMFILE *streamfile); -static opus_type_t get_ue4opus_version(STREAMFILE *sf, off_t offset); +static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset); +static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset); typedef struct { /* config */ @@ -32,6 +31,11 @@ typedef struct { off_t stream_offset; size_t stream_size; + /* list of OPUS frame sizes, for variations that preload this (must alloc/dealloc on init/close) */ + off_t table_offset; + int table_count; + uint16_t* frame_table; + /* state */ off_t logical_offset; /* offset that corresponds to physical_offset */ off_t physical_offset; /* actual file offset */ @@ -46,11 +50,14 @@ typedef struct { size_t head_size; /* OggS head page size */ size_t logical_size; + } opus_io_data; +static size_t get_table_frame_size(opus_io_data* data, int packet); + /* Convers custom Opus packets to Ogg Opus, so the resulting data is larger than physical data. */ -static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, opus_io_data* data) { +static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, opus_io_data* data) { size_t total_read = 0; /* ignore bad reads */ @@ -59,12 +66,12 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, } /* previous offset: re-start as we can't map logical<>physical offsets */ - if (offset < data->logical_offset) { + if (offset < data->logical_offset || data->logical_offset < 0) { data->physical_offset = data->stream_offset; data->logical_offset = 0x00; data->page_size = 0; data->samples_done = 0; - data->sequence = 2; /* appended header is 0/1 */ + data->sequence = 2; /* appended header+comment is 0/1 */ if (offset >= data->head_size) data->logical_offset = data->head_size; @@ -101,24 +108,26 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, switch(data->type) { case OPUS_SWITCH: /* format seem to come from opus_test and not Nintendo-specific */ - data_size = read_u32be(data->physical_offset, streamfile); + data_size = read_u32be(data->physical_offset, sf); skip_size = 0x08; /* size + Opus state(?) */ break; case OPUS_UE4_v1: - data_size = read_u16le(data->physical_offset, streamfile); + case OPUS_FSB: + data_size = read_u16le(data->physical_offset, sf); skip_size = 0x02; break; case OPUS_UE4_v2: - data_size = read_u16le(data->physical_offset + 0x00, streamfile); - packet_samples = read_u16le(data->physical_offset + 0x02, streamfile); + data_size = read_u16le(data->physical_offset + 0x00, sf); + packet_samples = read_u16le(data->physical_offset + 0x02, sf); skip_size = 0x02 + 0x02; break; case OPUS_EA: - data_size = read_u16be(data->physical_offset, streamfile); + data_size = read_u16be(data->physical_offset, sf); skip_size = 0x02; break; case OPUS_X: - data_size = get_xopus_packet_size(data->sequence - 2, streamfile); + case OPUS_WWISE: + data_size = get_table_frame_size(data, data->sequence - 2); skip_size = 0; break; default: @@ -131,13 +140,13 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, data->page_size = oggs_size + data_size; if (data->page_size > sizeof(data->page_buffer)) { /* happens on bad reads/EOF too */ - VGM_LOG("OPUS: buffer can't hold OggS at %x\n", (uint32_t)data->physical_offset); + VGM_LOG("OPUS: buffer can't hold OggS at %x, size=%x\n", (uint32_t)data->physical_offset, data->page_size); data->page_size = 0; break; } /* create fake OggS page (full page for checksums) */ - read_streamfile(data->page_buffer+oggs_size, data->physical_offset + skip_size, data_size, streamfile); /* store page data */ + read_streamfile(data->page_buffer+oggs_size, data->physical_offset + skip_size, data_size, sf); /* store page data */ if (packet_samples == 0) packet_samples = opus_get_packet_samples(data->page_buffer + oggs_size, data_size); data->samples_done += packet_samples; @@ -178,7 +187,7 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, } -static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { +static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) { off_t physical_offset, max_physical_offset; size_t logical_size = 0; int packet = 0; @@ -186,8 +195,8 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { if (data->logical_size) return data->logical_size; - if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) { - VGM_LOG("OPUS: wrong streamsize %x + %x vs %x\n", (uint32_t)data->stream_offset, data->stream_size, get_streamfile_size(streamfile)); + if (data->stream_offset + data->stream_size > get_streamfile_size(sf)) { + VGM_LOG("OPUS: wrong streamsize %x + %x vs %x\n", (uint32_t)data->stream_offset, data->stream_size, get_streamfile_size(sf)); return 0; } @@ -201,30 +210,37 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { switch(data->type) { case OPUS_SWITCH: - data_size = read_u32be(physical_offset, streamfile); + data_size = read_u32be(physical_offset, sf); skip_size = 0x08; break; case OPUS_UE4_v1: - data_size = read_u16le(physical_offset, streamfile); + case OPUS_FSB: + data_size = read_u16le(physical_offset, sf); skip_size = 0x02; break; case OPUS_UE4_v2: - data_size = read_u16le(physical_offset, streamfile); + data_size = read_u16le(physical_offset, sf); skip_size = 0x02 + 0x02; break; case OPUS_EA: - data_size = read_u16be(physical_offset, streamfile); + data_size = read_u16be(physical_offset, sf); skip_size = 0x02; break; case OPUS_X: - data_size = get_xopus_packet_size(packet, streamfile); + case OPUS_WWISE: + data_size = get_table_frame_size(data, packet); skip_size = 0x00; break; default: return 0; } - if (data_size == 0 ) { + /* FSB pads data after end (total size without frame headers is given but not too useful here) */ + if (data->type == OPUS_FSB && data_size == 0) { + break; + } + + if (data_size == 0) { VGM_LOG("OPUS: data_size is 0 at %x\n", (uint32_t)physical_offset); return 0; /* bad rip? or could 'break' and truck along */ } @@ -237,7 +253,7 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { } /* logical size can be bigger though */ - if (physical_offset > get_streamfile_size(streamfile)) { + if (physical_offset > get_streamfile_size(sf)) { VGM_LOG("OPUS: wrong size\n"); return 0; } @@ -246,39 +262,62 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { return data->logical_size; } +static int opus_io_init(STREAMFILE* sf, opus_io_data* data) { + //;VGM_LOG("OPUS: init\n"); + + /* read table containing frame sizes */ + if (data->table_count) { + int i; + //;VGM_LOG("OPUS: reading table, offset=%lx, entries=%i\n", data->table_offset, data->table_count); + + data->frame_table = malloc(data->table_count * sizeof(uint16_t)); + if (!data->frame_table) goto fail; + + for (i = 0; i < data->table_count; i++) { + data->frame_table[i] = read_u16le(data->table_offset + i * 0x02, sf); + } + } + + data->logical_offset = -1; /* force reset in case old data was cloned when re-opening SFs */ + data->logical_size = opus_io_size(sf, data); /* force size */ + return 1; +fail: + free(data->frame_table); + return 0; +} + +static void opus_io_close(STREAMFILE* sf, opus_io_data* data) { + //;VGM_LOG("OPUS: closing\n"); + + free(data->frame_table); +} + + /* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */ -static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; +static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) { + STREAMFILE* new_sf = NULL; opus_io_data io_data = {0}; - size_t io_data_size = sizeof(opus_io_data); + + if (!cfg->sample_rate) + cfg->sample_rate = 48000; /* default / only value for opus */ io_data.type = type; io_data.stream_offset = stream_offset; io_data.stream_size = stream_size; io_data.physical_offset = stream_offset; + io_data.table_offset = cfg->table_offset; + io_data.table_count = cfg->table_count; + io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), cfg); if (!io_data.head_size) goto fail; - io_data.sequence = 2; - io_data.logical_size = opus_io_size(streamFile, &io_data); /* force init */ /* setup subfile */ - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, opus_io_read,opus_io_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_buffer_streamfile(new_streamFile,0); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - return temp_streamFile; - + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_streamfile_ex_f(new_sf, &io_data, sizeof(opus_io_data), opus_io_read, opus_io_size, opus_io_init, opus_io_close); + new_sf = open_buffer_streamfile_f(new_sf, 0); + return new_sf; fail: - close_streamfile(temp_streamFile); return NULL; } @@ -321,7 +360,7 @@ static uint32_t crc_lookup[256]={ }; /* from ww2ogg */ -static uint32_t get_oggs_checksum(uint8_t * data, int bytes) { +static uint32_t get_oggs_checksum(uint8_t* data, int bytes) { uint32_t crc_reg=0; int i; @@ -332,7 +371,7 @@ static uint32_t get_oggs_checksum(uint8_t * data, int bytes) { } /* from opus_decoder.c's opus_packet_get_samples_per_frame */ -static uint32_t opus_packet_get_samples_per_frame(const uint8_t * data, int Fs) { +static uint32_t opus_packet_get_samples_per_frame(const uint8_t* data, int Fs) { int audiosize; if (data[0]&0x80) { @@ -352,7 +391,7 @@ static uint32_t opus_packet_get_samples_per_frame(const uint8_t * data, int Fs) } /* from opus_decoder.c's opus_packet_get_nb_frames */ -static int opus_packet_get_nb_frames(const uint8_t * packet, int len) { +static int opus_packet_get_nb_frames(const uint8_t* packet, int len) { int count; if (len<1) return 0; @@ -369,7 +408,7 @@ static int opus_packet_get_nb_frames(const uint8_t * packet, int len) { /* ******************************** */ -static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule) { +static size_t make_oggs_page(uint8_t* buf, int buf_size, size_t data_size, int page_sequence, int granule) { size_t page_done, lacing_done = 0; uint64_t absolute_granule = granule; /* wrong values seem validated (0, less than real samples, etc) */ int header_type_flag = (page_sequence==0 ? 2 : 0); @@ -383,15 +422,15 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int } segment_count = (int)(data_size / 0xFF + 1); - put_32bitBE(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */ - put_8bit (buf+0x04, 0); /* stream structure version, fixed */ - put_8bit (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */ - put_32bitLE(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */ - put_32bitLE(buf+0x0A, (uint32_t)(absolute_granule >> 32 & 0xFFFFFFFF)); /* upper */ - put_32bitLE(buf+0x0E, stream_serial_number); /* for interleaved multi-streams */ - put_32bitLE(buf+0x12, page_sequence); - put_32bitLE(buf+0x16, checksum); /* 0 for now, until all data is written */ - put_8bit (buf+0x1A, segment_count); /* count of all lacing values */ + put_u32be(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */ + put_u8 (buf+0x04, 0); /* stream structure version, fixed */ + put_u8 (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */ + put_u32le(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */ + put_u32le(buf+0x0A, (uint32_t)(absolute_granule >> 32 & 0xFFFFFFFF)); /* upper */ + put_u32le(buf+0x0E, stream_serial_number); /* for interleaved multi-streams */ + put_u32le(buf+0x12, page_sequence); + put_u32le(buf+0x16, checksum); /* 0 for now, until all data is written */ + put_u8 (buf+0x1A, segment_count); /* count of all lacing values */ /* segment table: size N in "lacing values" (ex. 0x20E=0xFF+FF+10; 0xFF=0xFF+00) */ page_done = 0x1B; @@ -400,12 +439,12 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int if (bytes > 0xFF) bytes = 0xFF; - put_8bit(buf+page_done, bytes); + put_u8(buf+page_done, bytes); page_done++; lacing_done += bytes; if (lacing_done == data_size && bytes == 0xFF) { - put_8bit(buf+page_done, 0x00); + put_u8(buf+page_done, 0x00); page_done++; } } @@ -416,14 +455,14 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int /* final checksum */ checksum = get_oggs_checksum(buf, page_done); - put_32bitLE(buf+0x16, checksum); + put_u32le(buf+0x16, checksum); return page_done; fail: return 0; } -static size_t make_opus_header(uint8_t * buf, int buf_size, opus_config *cfg) { +static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) { size_t header_size = 0x13; int mapping_family = 0; @@ -439,25 +478,25 @@ static size_t make_opus_header(uint8_t * buf, int buf_size, opus_config *cfg) { goto fail; } - put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ - put_32bitBE(buf+0x04, 0x48656164); /* "Head" header magic */ - put_8bit (buf+0x08, 1); /* version */ - put_8bit (buf+0x09, cfg->channels); - put_16bitLE(buf+0x0A, cfg->skip); - put_32bitLE(buf+0x0c, cfg->sample_rate); - put_16bitLE(buf+0x10, 0); /* output gain */ - put_8bit (buf+0x12, mapping_family); + put_u32be(buf+0x00, 0x4F707573); /* "Opus" header magic */ + put_u32be(buf+0x04, 0x48656164); /* "Head" header magic */ + put_u8 (buf+0x08, 1); /* version */ + put_u8 (buf+0x09, cfg->channels); + put_s16le(buf+0x0A, cfg->skip); + put_u32le(buf+0x0c, cfg->sample_rate); + put_u16le(buf+0x10, 0); /* output gain */ + put_u8 (buf+0x12, mapping_family); if (mapping_family > 0) { int i; /* internal mono/stereo streams (N mono/stereo streams form M channels) */ - put_8bit(buf+0x13, cfg->stream_count); + put_u8(buf+0x13, cfg->stream_count); /* joint stereo streams (rest would be mono, so 6ch can be 2ch+2ch+1ch+1ch = 2 coupled */ - put_8bit(buf+0x14, cfg->coupled_count); + put_u8(buf+0x14, cfg->coupled_count); /* mapping bits per channel? */ for (i = 0; i < cfg->channels; i++) { - put_8bit(buf+0x15+i, cfg->channel_mapping[i]); + put_u8(buf+0x15+i, cfg->channel_mapping[i]); } } @@ -466,9 +505,9 @@ fail: return 0; } -static size_t make_opus_comment(uint8_t * buf, int buf_size) { - const char * vendor_string = "vgmstream"; - const char * user_comment_0_string = "vgmstream Opus converter"; +static size_t make_opus_comment(uint8_t* buf, int buf_size) { + const char* vendor_string = "vgmstream"; + const char* user_comment_0_string = "vgmstream Opus converter"; size_t comment_size; int vendor_string_length, user_comment_0_length; @@ -481,20 +520,20 @@ static size_t make_opus_comment(uint8_t * buf, int buf_size) { goto fail; } - put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ - put_32bitBE(buf+0x04, 0x54616773); /* "Tags" header magic */ - put_32bitLE(buf+0x08, vendor_string_length); - memcpy (buf+0x0c, vendor_string, vendor_string_length); - put_32bitLE(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */ - put_32bitLE(buf+0x0c + vendor_string_length+0x04, user_comment_0_length); - memcpy (buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length); + put_u32be(buf+0x00, 0x4F707573); /* "Opus" header magic */ + put_u32be(buf+0x04, 0x54616773); /* "Tags" header magic */ + put_u32le(buf+0x08, vendor_string_length); + memcpy (buf+0x0c, vendor_string, vendor_string_length); + put_u32le(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */ + put_u32le(buf+0x0c + vendor_string_length+0x04, user_comment_0_length); + memcpy (buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length); return comment_size; fail: return 0; } -static size_t make_oggs_first(uint8_t * buf, int buf_size, opus_config *cfg) { +static size_t make_oggs_first(uint8_t* buf, int buf_size, opus_config* cfg) { int buf_done = 0; size_t bytes; @@ -516,10 +555,10 @@ fail: return 0; } -static size_t opus_get_packet_samples(const uint8_t * buf, int len) { +static size_t opus_get_packet_samples(const uint8_t* buf, int len) { return opus_packet_get_nb_frames(buf, len) * opus_packet_get_samples_per_frame(buf, 48000); } -static size_t opus_get_packet_samples_sf(STREAMFILE *sf, off_t offset) { +static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset) { uint8_t buf[0x04]; /* at least 0x02 */ read_streamfile(buf, offset, sizeof(buf), sf); return opus_get_packet_samples(buf, sizeof(buf)); @@ -527,15 +566,19 @@ static size_t opus_get_packet_samples_sf(STREAMFILE *sf, off_t offset) { /************************** */ -static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile) { - /* XOPUS has a packet size table at the beginning, get size from there. - * Maybe should copy the table during setup to avoid IO, but all XOPUS are - * quite small so it isn't very noticeable. */ - return read_u16le(0x20 + packet*0x02, streamfile); +/* some formats store all frames in a table, rather than right before the frame */ +static size_t get_table_frame_size(opus_io_data* data, int frame) { + if (frame < 0 || frame >= data->table_count) { + VGM_LOG("OPUS: wrong requested frame %i, count=%i\n", frame, data->table_count); + return 0; + } + + //;VGM_LOG("OPUS: frame %i size=%x\n", frame, data->frame_table[frame]); + return data->frame_table[frame]; } -static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *sf, opus_type_t type) { +static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf, opus_type_t type) { size_t num_samples = 0; off_t end_offset = offset + stream_size; int packet = 0; @@ -555,6 +598,7 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI skip_size = 0x08; break; case OPUS_UE4_v1: + case OPUS_FSB: data_size = read_u16le(offset, sf); skip_size = 0x02; break; @@ -567,10 +611,14 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI data_size = read_u16be(offset, sf); skip_size = 0x02; break; + +#if 0 // needs data* for frame table, but num_samples should exist on header case OPUS_X: - data_size = get_xopus_packet_size(packet, sf); + case OPUS_WWISE: + data_size = get_table_frame_size(data, packet); skip_size = 0x00; break; +#endif default: return 0; } @@ -586,12 +634,12 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI return num_samples; } -size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *streamFile) { - return custom_opus_get_samples(offset, stream_size, streamFile, OPUS_SWITCH); +size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf) { + return custom_opus_get_samples(offset, stream_size, sf, OPUS_SWITCH); } -static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_type_t type) { +static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE* sf, opus_type_t type) { size_t skip_size, packet_samples = 0; switch(type) { @@ -599,6 +647,7 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_t skip_size = 0x08; break; case OPUS_UE4_v1: + case OPUS_FSB: skip_size = 0x02; break; case OPUS_UE4_v2: @@ -609,6 +658,7 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_t skip_size = 0x02; break; case OPUS_X: + case OPUS_WWISE: skip_size = 0x00; break; default: @@ -620,30 +670,33 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_t /* encoder delay seems fixed to 1/8 of samples per frame, but may need more testing */ return packet_samples / 8; } -size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { - return custom_opus_get_encoder_delay(offset, streamFile, OPUS_SWITCH); +size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) { + return custom_opus_get_encoder_delay(offset, sf, OPUS_SWITCH); } -size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { - return custom_opus_get_encoder_delay(offset, streamFile, get_ue4opus_version(streamFile, offset)); +size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) { + return custom_opus_get_encoder_delay(offset, sf, get_ue4opus_version(sf, offset)); } -size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { - return custom_opus_get_encoder_delay(offset, streamFile, OPUS_EA); +size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) { + return custom_opus_get_encoder_delay(offset, sf, OPUS_EA); +} +size_t fsb_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) { + return custom_opus_get_encoder_delay(offset, sf, OPUS_FSB); } /* ******************************************************* */ -/* actual FFmpeg only-code starts here (the above is universal enough but no point to compile) */ +/* actual FFmpeg only-code starts here (the above is universal enough but no point to compile separatedly) */ //#ifdef VGM_USE_FFMPEG -static ffmpeg_codec_data * init_ffmpeg_custom_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config *cfg, opus_type_t type) { - ffmpeg_codec_data * ffmpeg_data = NULL; - STREAMFILE *temp_streamFile = NULL; +static ffmpeg_codec_data* init_ffmpeg_custom_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config *cfg, opus_type_t type) { + ffmpeg_codec_data* ffmpeg_data = NULL; + STREAMFILE* temp_sf = NULL; - temp_streamFile = setup_opus_streamfile(streamFile, cfg, start_offset, data_size, type); - if (!temp_streamFile) goto fail; + temp_sf = setup_opus_streamfile(sf, cfg, start_offset, data_size, type); + if (!temp_sf) goto fail; - ffmpeg_data = init_ffmpeg_offset(temp_streamFile, 0x00,get_streamfile_size(temp_streamFile)); + ffmpeg_data = init_ffmpeg_offset(temp_sf, 0x00, get_streamfile_size(temp_sf)); if (!ffmpeg_data) goto fail; /* FFmpeg + libopus: skips samples, notifies skip in codecCtx->delay (not in stream->skip_samples) @@ -653,39 +706,58 @@ static ffmpeg_codec_data * init_ffmpeg_custom_opus_config(STREAMFILE *streamFile // ffmpeg_set_skip_samples(ffmpeg_data, skip); //} - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return ffmpeg_data; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return NULL; } -static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { + +static ffmpeg_codec_data* init_ffmpeg_custom_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { opus_config cfg = {0}; cfg.channels = channels; cfg.skip = skip; cfg.sample_rate = sample_rate; - return init_ffmpeg_custom_opus_config(streamFile, start_offset, data_size, &cfg, type); + return init_ffmpeg_custom_opus_config(sf, start_offset, data_size, &cfg, type); } -ffmpeg_codec_data * init_ffmpeg_switch_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config* cfg) { - return init_ffmpeg_custom_opus_config(streamFile, start_offset, data_size, cfg, OPUS_SWITCH); -} -ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { - return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH); -} -ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { - return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, get_ue4opus_version(streamFile, start_offset)); -} -ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { - return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_EA); -} -ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { - return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_X); +ffmpeg_codec_data* init_ffmpeg_custom_table_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { + opus_config cfg = {0}; + cfg.channels = channels; + cfg.skip = skip; + cfg.sample_rate = sample_rate; + cfg.table_offset = table_offset; + cfg.table_count = table_count; + + return init_ffmpeg_custom_opus_config(sf, data_offset, data_size, &cfg, type); } -static opus_type_t get_ue4opus_version(STREAMFILE *sf, off_t offset) { + +ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg) { + return init_ffmpeg_custom_opus_config(sf, start_offset, data_size, cfg, OPUS_SWITCH); +} +ffmpeg_codec_data* init_ffmpeg_switch_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH); +} +ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, get_ue4opus_version(sf, start_offset)); +} +ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_EA); +} +ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip) { + return init_ffmpeg_custom_table_opus(sf, table_offset, table_count, data_offset, data_size, channels, skip, 0, OPUS_X); +} +ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_FSB); +} +ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip) { + return init_ffmpeg_custom_table_opus(sf, table_offset, table_count, data_offset, data_size, channels, skip, 0, OPUS_WWISE); +} + +static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset) { int read_samples, calc_samples; /* UE4OPUS v2 has packet samples right after packet size, check if data follows this */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index 88eb711ff..f066ab4fd 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -3,12 +3,12 @@ #ifdef VGM_USE_MPEG /* init config and validate per type */ -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_default(STREAMFILE* sf, off_t start_offset, mpeg_codec_data* data, coding_t* coding_type) { mpeg_frame_info info; /* get frame info at offset */ - if ( !mpeg_get_frame_info(streamFile, start_offset, &info)) + if ( !mpeg_get_frame_info(sf, start_offset, &info)) goto fail; switch(info.layer) { case 1: *coding_type = coding_MPEG_layer1; break; @@ -113,7 +113,7 @@ fail: /* writes data to the buffer and moves offsets */ -int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { +int mpeg_custom_parse_frame_default(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; @@ -242,7 +242,7 @@ fail: * Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow * it's wrong at times (maybe because we use an ancient version) so here we do our thing. */ -static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info *info) { +static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) { /* index tables */ static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; static const int layers[4] = { -1,3,2,1 }; @@ -313,12 +313,12 @@ static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info *info) { fail: return 0; } -int mpeg_get_frame_info(STREAMFILE *sf, off_t offset, mpeg_frame_info *info) { +int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info) { uint32_t header = read_u32be(offset, sf); return mpeg_get_frame_info_h(header, info); } -size_t mpeg_get_samples(STREAMFILE *sf, off_t start_offset, size_t bytes) { +size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) { off_t offset = start_offset; off_t max_offset = start_offset + bytes; int frames = 0, samples = 0, encoder_delay = 0, encoder_padding = 0; @@ -428,4 +428,48 @@ size_t mpeg_get_samples(STREAMFILE *sf, off_t start_offset, size_t bytes) { return samples; } + +/* variation of the above, for clean streams = no ID3/VBR headers + * (maybe should be fused in a single thing with config, API is kinda messy too) */ +int32_t mpeg_get_samples_clean(STREAMFILE *sf, off_t start, size_t size, size_t* p_loop_start, size_t* p_loop_end, int is_vbr) { + mpeg_frame_info info; + off_t offset = start; + int32_t num_samples = 0, loop_start = 0, loop_end = 0; + + if (!is_vbr) { + /* CBR = quick calcs */ + if (!mpeg_get_frame_info(sf, offset, &info)) + goto fail; + + num_samples = size / info.frame_size * info.frame_samples; + loop_start = *p_loop_start / info.frame_size * info.frame_samples; + loop_end = *p_loop_end / info.frame_size * info.frame_samples; + } + else { + /* VBR (or unknown) = count frames */ + while (offset < start + size) { + if (!mpeg_get_frame_info(sf, offset, &info)) + goto fail; + + if (*p_loop_start + start == offset) + loop_start = num_samples; + + num_samples += info.frame_samples; + offset += info.frame_size; + + if (*p_loop_end + start == offset) + loop_end = num_samples; + } + } + + + *p_loop_start = loop_start; + *p_loop_end = loop_end; + + return num_samples; +fail: + VGM_LOG("MPEG: sample reader failed at %lx\n", offset); + return 0; +} + #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c index d2f59ad87..746c6be03 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c @@ -119,7 +119,7 @@ void decode_vadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacin } /* update hist once all frame is actually copied */ - if (first_sample + sample_count == samples_per_frame) { + if (first_sample + sample_count / channelspacing == samples_per_frame) { stream->adpcm_history2_16 = hist[6]; stream->adpcm_history1_16 = hist[7]; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/wady_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/wady_decoder.c new file mode 100644 index 000000000..6f474f8c4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/wady_decoder.c @@ -0,0 +1,48 @@ +#include "coding.h" + + +/* originally only positives are stored (pre-init by copying negatives) */ +static const int wady_table[64+64] = { + 0, 2, 4, 6, 8, 10, 12, 15, + 18, 21, 24, 28, 32, 36, 40, 44, + 49, 54, 59, 64, 70, 76, 82, 88, + 95, 102, 109, 116, 124, 132, 140, 148, + 160, 170, 180, 190, 200, 210, 220, 230, + 240, 255, 270, 285, 300, 320, 340, 360, + 380, 400, 425, 450, 475, 500, 525, 550, + 580, 610, 650, 700, 750, 800, 900, 1000, + -0, -2, -4, -6, -8, -10, -12, -15, + -18, -21, -24, -28, -32, -36, -40, -44, + -49, -54, -59, -64, -70, -76, -82, -88, + -95, -102,-109,-116,-124,-132,-140,-148, + -160,-170,-180,-190,-200,-210,-220,-230, + -240,-255,-270,-285,-300,-320,-340,-360, + -380,-400,-425,-450,-475,-500,-525,-550, + -580,-610,-650,-700,-750,-800,-900,-1000, +}; + +/* There is another decoding mode mainly for SFX. Uses headered frames/blocks (big), + * L-frame then R-frame, DPCM uses another table plus a RLE/LZ-like mode */ + +/* Marble engine WADY decoder, decompiled from the exe + * (some info from: https://github.com/morkt/GARbro/blob/master/ArcFormats/Marble/AudioWADY.cs) */ +void decode_wady(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i, sample_pos = 0; + off_t frame_offset = stream->offset; /* frame size is 1 */ + int32_t hist = stream->adpcm_history1_32; + int scale = stream->adpcm_scale; + + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int8_t code = read_s8(frame_offset + i, stream->streamfile); + + if (code & 0x80) + hist = (code << 9); /* PCM */ + else + hist += scale * wady_table[code]; /* DPCM */ + + outbuf[sample_pos] = hist; /* no clamp */ + sample_pos += channelspacing; + } + + stream->adpcm_history1_32 = hist; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c index d0345c2e6..4ba8e91c1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c @@ -19,10 +19,10 @@ static const int scale_delta[16] = { }; /* Yamaha ADPCM-B (aka DELTA-T) expand used in YM2608/YM2610/etc (cross referenced with various sources and .so) */ -static void yamaha_adpcmb_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) { +static void yamaha_adpcmb_expand_nibble(uint8_t byte, int shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) { int code, delta, sample; - code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; + code = (byte >> shift) & 0xf; delta = ((((code & 0x7) * 2) + 1) * (*step_size)) >> 3; /* like 'mul' IMA */ if (code & 8) delta = -delta; @@ -109,38 +109,47 @@ void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin /* tri-Ace Aska ADPCM, Yamaha ADPCM-B with headered frames (reversed from Android SO's .so) * implements table with if-else/switchs too but that's too goofy */ -void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0, num_frame; +void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size) { + uint8_t frame[0x100] = {0}; /* known max is 0xC0 */ + off_t frame_offset; + int i, sample_count = 0, frames_in; int16_t out_sample; int32_t hist1 = stream->adpcm_history1_32; int step_size = stream->adpcm_step_index; /* external interleave */ - int block_samples = (0x40 - 0x04*channelspacing) * 2 / channelspacing; - num_frame = first_sample / block_samples; + int block_samples = (frame_size - 0x04*channelspacing) * 2 / channelspacing; + frames_in = first_sample / block_samples; first_sample = first_sample % block_samples; + if (frame_size > sizeof(frame)) { + VGM_LOG_ONCE("ASKA: unknown frame size %x\n", frame_size); + return; + } + + /* parse frame */ + frame_offset = stream->offset + frame_size * frames_in; + read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */ + /* header (hist+step) */ if (first_sample == 0) { - off_t header_offset = stream->offset + 0x40*num_frame + 0x04*channel; - - hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); - step_size = read_16bitLE(header_offset+0x02,stream->streamfile); - /* in most files 1st frame has step 0 but it seems ok and accounted for */ + hist1 = get_s16le(frame + 0x04*channel + 0x00); + step_size = get_s16le(frame + 0x04*channel + 0x02); + /* in most files 1st frame has step 0 but it seems ok and needed for correct waveform */ //if (step_size < 0x7f) step_size = 0x7f; //else if (step_size > 0x6000) step_size = 0x6000; } - /* decode nibbles (layout: varies) */ + /* decode nibbles (layout: one nibble per channel, low-high order, ex 6ch=10325410 32541032 ...) */ for (i = first_sample; i < first_sample + samples_to_do; i++) { - off_t byte_offset = (channelspacing == 2) ? - (stream->offset + 0x40*num_frame + 0x04*channelspacing) + i : /* stereo: one nibble per channel */ - (stream->offset + 0x40*num_frame + 0x04*channelspacing) + i/2; /* mono: consecutive nibbles */ - int nibble_shift = (channelspacing == 2) ? - (!(channel&1) ? 0:4) : - (!(i&1) ? 0:4); /* even = low, odd = high */ + int pos = (channelspacing == 1) ? + (0x04*channelspacing) + i/2 : + (0x04*channelspacing) + (i * 4 * channelspacing + 4*channel) / 8; /* nibble position to closest byte */ + int shift = (channelspacing == 1) ? /* low first */ + (!(i&1) ? 0:4) : + (!(channel&1) ? 0:4); - yamaha_adpcmb_expand_nibble(stream, byte_offset, nibble_shift, &hist1, &step_size, &out_sample); + yamaha_adpcmb_expand_nibble(frame[pos], shift, &hist1, &step_size, &out_sample); outbuf[sample_count] = out_sample; sample_count += channelspacing; } @@ -150,24 +159,29 @@ void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin } -/* NXAP ADPCM, Yamaha ADPCM-B with weird headered frames */ +/* NXAP ADPCM, Yamaha ADPCM-B with weird headered frames, partially rev'd from the ELF */ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i, sample_count = 0, num_frame; + uint8_t frame[0x40] = {0}; /* known max is 0xC0 */ + off_t frame_offset; + int i, sample_count = 0, frames_in; int32_t hist1 = stream->adpcm_history1_32; int step_size = stream->adpcm_step_index; int16_t out_sample; /* external interleave, mono */ - int block_samples = (0x40 - 0x4) * 2; - num_frame = first_sample / block_samples; + size_t frame_size = 0x40; + int block_samples = (frame_size - 0x4) * 2; + frames_in = first_sample / block_samples; first_sample = first_sample % block_samples; + /* parse frame */ + frame_offset = stream->offset + frame_size * frames_in; + read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */ + /* header (hist+step) */ if (first_sample == 0) { - off_t header_offset = stream->offset + 0x40*num_frame; - - hist1 = read_s16le(header_offset+0x00,stream->streamfile); - step_size = read_u16le(header_offset+0x02,stream->streamfile) >> 1; /* remove lower bit, also note unsignedness */ + hist1 = get_s16le(frame + 0x00); + step_size = get_u16le(frame + 0x02) >> 1; /* remove lower bit, also note unsignedness */ if (step_size < 0x7f) step_size = 0x7f; else if (step_size > 0x6000) step_size = 0x6000; /* step's lower bit is hist1 sign (useless), and code doesn't seem to do anything useful with it? */ @@ -175,10 +189,10 @@ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin /* decode nibbles (layout: all nibbles from one channel) */ for (i = first_sample; i < first_sample + samples_to_do; i++) { - off_t byte_offset = (stream->offset + 0x40*num_frame + 0x04) + i/2; - int nibble_shift = (i&1?0:4); + int pos = 0x04 + i/2; + int shift = (i&1?0:4); - yamaha_adpcmb_expand_nibble(stream, byte_offset, nibble_shift, &hist1, &step_size, &out_sample); + yamaha_adpcmb_expand_nibble(frame[pos], shift, &hist1, &step_size, &out_sample); outbuf[sample_count] = out_sample; sample_count += channelspacing; } @@ -193,8 +207,8 @@ size_t yamaha_bytes_to_samples(size_t bytes, int channels) { return bytes * 2 / channels; } -size_t aska_bytes_to_samples(size_t bytes, int channels) { - int block_align = 0x40; +size_t aska_bytes_to_samples(size_t bytes, size_t frame_size, int channels) { + int block_align = frame_size; if (channels <= 0) return 0; return (bytes / block_align) * (block_align - 0x04*channels) * 2 / channels + ((bytes % block_align) ? ((bytes % block_align) - 0x04*channels) * 2 / channels : 0); diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c index e74ec0f10..427dd3828 100644 --- a/Frameworks/vgmstream/vgmstream/src/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -343,6 +343,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { case coding_CBD2: case coding_ACM: case coding_DERF: + case coding_WADY: case coding_NWA: case coding_SASSC: case coding_CIRCUS_ADPCM: @@ -424,7 +425,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { case coding_AICA_int: return 2; case coding_ASKA: - return (0x40-0x04*vgmstream->channels) * 2 / vgmstream->channels; + return (vgmstream->frame_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; case coding_NXAP: return (0x40-0x04) * 2; case coding_NDS_PROCYON: @@ -540,6 +541,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { case coding_SDX2_int: case coding_CBD2: case coding_DERF: + case coding_WADY: case coding_NWA: case coding_SASSC: case coding_CIRCUS_ADPCM: @@ -624,6 +626,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { case coding_AICA_int: return 0x01; case coding_ASKA: + return vgmstream->frame_size; case coding_NXAP: return 0x40; case coding_NDS_PROCYON: @@ -938,17 +941,14 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ } break; case coding_EA_XA: + case coding_EA_XA_int: { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_EA_XA); for (ch = 0; ch < vgmstream->channels; ch++) { decode_ea_xa(&vgmstream->ch[ch], buffer+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_EA_XA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xa_int(&vgmstream->ch[ch], buffer+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_stereo); } break; + } case coding_EA_XA_V2: for (ch = 0; ch < vgmstream->channels; ch++) { decode_ea_xa_v2(&vgmstream->ch[ch], buffer+ch, @@ -1031,6 +1031,12 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ vgmstream->channels, vgmstream->samples_into_block, samples_to_do); } break; + case coding_WADY: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wady(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; case coding_CIRCUS_ADPCM: for (ch = 0; ch < vgmstream->channels; ch++) { decode_circus_adpcm(&vgmstream->ch[ch], buffer+ch, @@ -1241,7 +1247,7 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ case coding_ASKA: for (ch = 0; ch < vgmstream->channels; ch++) { decode_aska(&vgmstream->ch[ch], buffer+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, vgmstream->frame_size); } break; case coding_NXAP: diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 7eca85a3c..66e6c4089 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -37,7 +37,6 @@ static const char* extension_list[] = { "abk", //"ac3", //common, FFmpeg/not parsed (AC3) "acb", - "ace", //fake extension for tri-Ace's .aac (renamed, to be removed) "acm", "ad", //txth/reserved [Xenosaga Freaks (PS2)] "adc", //txth/reserved [Tomb Raider The Last Revelation (DC), Tomb Raider Chronicles (DC)] @@ -67,6 +66,7 @@ static const char* extension_list[] = { "ams", //txth/reserved [Super Dragon Ball Z (PS2) ELF names] "amts", //fake extension/header id for .stm (renamed? to be removed?) "ao", + "ap", "apc", "as4", "asd", @@ -126,6 +126,7 @@ static const char* extension_list[] = { "ccc", "cd", "cfn", //fake extension for CAF (renamed, to be removed?) + "chk", "ckb", "ckd", "cks", @@ -166,7 +167,6 @@ static const char* extension_list[] = { "enm", "eno", "ens", - "enth", "exa", "ezw", @@ -261,6 +261,7 @@ static const char* extension_list[] = { "lasf", //fake extension for .asf (various) "lbin", //fake extension for .bin (various) "leg", + "lep", "lflac", //fake extension for .flac, FFmpeg/not parsed "lin", "lm0", @@ -277,6 +278,7 @@ static const char* extension_list[] = { "lmpc", //fake extension for .mpc, FFmpeg/not parsed "logg", //fake extension for .ogg "lopus", //fake extension for .opus + "lp", "lpcm", "lpk", "lps", @@ -343,6 +345,7 @@ static const char* extension_list[] = { "nop", "nps", "npsf", //fake extension/header id for .nps (in bigfiles) + "nsopus", "nub", "nub2", "nus3audio", @@ -553,6 +556,7 @@ static const char* extension_list[] = { "wavebatch", "wavm", "wavx", //txth/reserved [LEGO Star Wars (Xbox)] + "way", "wb", "wb2", "wbd", @@ -588,6 +592,7 @@ static const char* extension_list[] = { "xmu", "xnb", "xsf", + "xse", "xsew", "xss", "xvag", @@ -768,6 +773,7 @@ static const coding_info coding_info_list[] = { {coding_OKI16, "OKI 4-bit ADPCM (16-bit output)"}, {coding_OKI4S, "OKI 4-bit ADPCM (4-shift)"}, {coding_PTADPCM, "Platinum 4-bit ADPCM"}, + {coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"}, {coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"}, {coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, @@ -775,11 +781,11 @@ static const coding_info coding_info_list[] = { {coding_CBD2_int, "Cuberoot-delta-exact (CBD2) 8-bit DPCM with 1 byte interleave"}, {coding_SASSC, "Activision / EXAKT SASSC 8-bit DPCM"}, {coding_DERF, "Xilam DERF 8-bit DPCM"}, - {coding_ACM, "InterPlay ACM"}, + {coding_WADY, "Marble WADY 8-bit DPCM"}, {coding_NWA, "VisualArt's NWA DPCM"}, + {coding_ACM, "InterPlay ACM"}, {coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"}, {coding_UBI_ADPCM, "Ubisoft 4/6-bit ADPCM"}, - {coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"}, {coding_EA_MT, "Electronic Arts MicroTalk"}, {coding_CIRCUS_VQ, "Circus VQ"}, @@ -857,7 +863,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_ea_sns, "blocked (EA SNS)"}, {layout_blocked_awc, "blocked (AWC)"}, {layout_blocked_vgs, "blocked (VGS)"}, - {layout_blocked_vawx, "blocked (VAWX)"}, + {layout_blocked_xwav, "blocked (XWAV)"}, {layout_blocked_xvag_subsong, "blocked (XVAG subsong)"}, {layout_blocked_ea_wve_au00, "blocked (EA WVE au00)"}, {layout_blocked_ea_wve_ad10, "blocked (EA WVE Ad10)"}, @@ -1044,7 +1050,7 @@ static const meta_info meta_info_list[] = { {meta_RIFF_WAVE_MWV, "RIFF WAVE header with .mwv flavoring"}, {meta_FFCC_STR, "Final Fantasy: Crystal Chronicles STR header"}, {meta_SAT_BAKA, "BAKA header from Crypt Killer"}, - {meta_SWAV, "Gameloft SWAV header"}, + {meta_SWAV, "Nintendo SWAV header"}, {meta_VSF, "Square-Enix VSF header"}, {meta_NDS_RRDS, "Ridger Racer DS Header"}, {meta_PS2_TK5, "Tekken 5 Stream Header"}, @@ -1129,7 +1135,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_IAB, "Runtime .IAB header"}, {meta_VS_STR, "Square .VS STR* header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, - {meta_VAWX, "feelplus VAWX header"}, + {meta_XWAV, "feelplus XWAV header"}, {meta_RAW_SNDS, "PC .snds raw header"}, {meta_PS2_WMUS, "assumed The Warriors Sony ADPCM by .wmus extension"}, {meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"}, @@ -1174,9 +1180,7 @@ static const meta_info meta_info_list[] = { {meta_OGL, "Shin'en OGL header"}, {meta_MC3, "Paradigm MC3 header"}, {meta_GTD, "GTD/GHS header"}, - {meta_TA_AAC_X360, "tri-Ace AAC (X360) header"}, - {meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"}, - {meta_TA_AAC_MOBILE, "tri-Ace AAC (Mobile) header"}, + {meta_TA_AAC, "tri-Ace AAC header"}, {meta_MTA2, "Konami MTA2 header"}, {meta_NGC_ULW, "Criterion ULW raw header"}, {meta_XA_XA30, "Reflections XA30 header"}, @@ -1227,10 +1231,9 @@ static const meta_info meta_info_list[] = { {meta_TXTP, "TXTP generic header"}, {meta_SMC_SMH, "Genki SMC+SMH header"}, {meta_PPST, "Parappa PPST header"}, - {meta_OPUS_PPP, "AT9 OPUS header"}, + {meta_SPS_N1, "Nippon Ichi .SPS header"}, {meta_UBI_BAO, "Ubisoft BAO header"}, {meta_DSP_SWITCH_AUDIO, "UE4 Switch Audio header"}, - {meta_TA_AAC_VITA, "tri-Ace AAC (Vita) header"}, {meta_SADF, "Procyon Studio SADF header"}, {meta_H4M, "Hudson HVQM4 header"}, {meta_ASF, "Argonaut ASF header"}, @@ -1309,6 +1312,10 @@ static const meta_info meta_info_list[] = { {meta_KAT, "Sega KAT header"}, {meta_PCM_SUCCESS, "Success PCM header"}, {meta_ADP_KONAMI, "Konami ADP header"}, + {meta_SDRH, "feelplus SDRH header"}, + {meta_WADY, "Marble WADY header"}, + {meta_DSP_SQEX, "Square Enix DSP header"}, + {meta_DSP_WIIVOICE, "Koei Tecmo WiiVoice header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index fe7696c63..c28c4fe06 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -182,8 +182,8 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) { case layout_blocked_vgs: block_update_vgs(block_offset,vgmstream); break; - case layout_blocked_vawx: - block_update_vawx(block_offset,vgmstream); + case layout_blocked_xwav: + block_update_xwav(block_offset,vgmstream); break; case layout_blocked_xvag_subsong: block_update_xvag_subsong(block_offset,vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index fe3aa80db..8cded1499 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -12,6 +12,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { uint32_t flag_lang = (vgmstream->codec_config >> 16) & 0xFFFF; int flag_be = (vgmstream->codec_config & 0x02); int flag_adpcm = (vgmstream->codec_config & 0x01); + int flag_offsets = (vgmstream->codec_config & 0x04); /* EOF reads: signal we have nothing and let the layout fail */ @@ -90,105 +91,139 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { /* set new channel offsets and ADPCM history */ /* ADPCM hist could be considered part of the stream/decoder (some EAXA decoders call it "EAXA R1" when it has hist), and BNKs * (with no blocks) may also have them in the first offset, but also may not. To simplify we just read them here. */ - switch(vgmstream->coding_type) { - /* id, size, unk1, unk2, interleaved data */ - case coding_PSX: - for (i = 0; i < vgmstream->channels; i++) { - size_t interleave = (block_size-0x10) / vgmstream->channels; - vgmstream->ch[i].offset = block_offset + 0x10 + i*interleave; - } - /* 0x08/0x0c: unknown (doesn't look like hist or offsets, as 1ch files has them too) */ - - break; - - /* id, size, IMA hist, stereo/mono data */ - case coding_DVI_IMA: - for(i = 0; i < vgmstream->channels; i++) { - off_t header_offset = block_offset + 0xc + i*4; - vgmstream->ch[i].adpcm_history1_32 = read_16bitLE(header_offset+0x00, vgmstream->ch[i].streamfile); - vgmstream->ch[i].adpcm_step_index = read_16bitLE(header_offset+0x02, vgmstream->ch[i].streamfile); - vgmstream->ch[i].offset = block_offset + 0xc + (4*vgmstream->channels); - } - - break; - - /* id, size, samples */ - case coding_PCM16_int: - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x02); - } - - break; - - /* id, size, samples, hists-per-channel, stereo/interleaved data */ - case coding_EA_XA: - //case coding_EA_XA_V2: /* handled in default */ - case coding_EA_XA_int: - for (i = 0; i < vgmstream->channels; i++) { - int is_interleaved = vgmstream->coding_type == coding_EA_XA_int; - size_t interleave; - - /* read ADPCM history from all channels before data (not actually read in sx.exe) */ - //vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile); - //vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile); - - /* the block can have padding so find the channel size from num_samples */ - interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; - - /* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */ - vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave; - } - - break; - - /* id, size, samples, offsets-per-channel, flag (0x01 = data start), data */ - case coding_EA_MT: - for (i = 0; i < vgmstream->channels; i++) { - off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); - vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start + 0x01; - } - - /* flush decoder in every block change */ - flush_ea_mt(vgmstream); - break; - -#ifdef VGM_USE_MPEG - /* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */ - 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; - - /* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */ - if (vgmstream->channels > 2) { - channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile); - } else { - channel_start = read_32bit(block_offset + 0x0C,streamFile); - } - - vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; - } - break; -#endif - /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ - default: - for (i = 0; i < vgmstream->channels; i++) { - off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); - vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; - } - - /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ - if (flag_adpcm) { + if (!flag_offsets) { /* v0 doesn't provide channel offsets, they need to be calculated */ + switch (vgmstream->coding_type) { + /* id, size, samples, data */ + case coding_PCM8_int: for (i = 0; i < vgmstream->channels; i++) { - //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); - //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); - vgmstream->ch[i].offset += 4; + vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x01); } - } - break; + break; + + /* id, size, samples, data */ + case coding_PCM16_int: + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x02); + } + + break; + + /* id, size, samples, data */ + case coding_PCM8: + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x0c + (block_samples*i*0x01); + } + + break; + + /* id, size, samples, data */ + case coding_PCM16LE: + case coding_PCM16BE: + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x0c + (block_samples*i*0x02); + } + + break; + + /* id, size, unk1, unk2, interleaved data */ + case coding_PSX: + for (i = 0; i < vgmstream->channels; i++) { + size_t interleave = (block_size-0x10) / vgmstream->channels; + vgmstream->ch[i].offset = block_offset + 0x10 + i*interleave; + } + /* 0x08/0x0c: unknown (doesn't look like hist or offsets, as 1ch files has them too) */ + + break; + + /* id, size, samples, IMA hist, stereo/mono data */ + case coding_DVI_IMA: + for(i = 0; i < vgmstream->channels; i++) { + off_t header_offset = block_offset + 0xc + i*4; + vgmstream->ch[i].adpcm_history1_32 = read_16bitLE(header_offset+0x00, vgmstream->ch[i].streamfile); + vgmstream->ch[i].adpcm_step_index = read_16bitLE(header_offset+0x02, vgmstream->ch[i].streamfile); + vgmstream->ch[i].offset = block_offset + 0xc + (4*vgmstream->channels); + } + + break; + + /* id, size, samples, hists-per-channel, stereo/interleaved data */ + case coding_EA_XA: + case coding_EA_XA_int: + for (i = 0; i < vgmstream->channels; i++) { + int is_interleaved = vgmstream->coding_type == coding_EA_XA_int; + size_t interleave; + + /* read ADPCM history from all channels before data (not actually read in sx.exe) */ + //vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile); + //vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile); + + /* the block can have padding so find the channel size from num_samples */ + interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; + + /* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */ + vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave; + } + + break; + + case coding_EA_MT: /* not seen in v0 streams so far, may not exist */ + default: + VGM_LOG("EA SCHl: Unkonwn channel offsets in blocked layout\n"); + vgmstream->current_block_samples = -1; + break; + } + } else { + switch(vgmstream->coding_type) { + /* id, size, samples, offsets-per-channel, flag (0x01 = data start), data */ + case coding_EA_MT: + for (i = 0; i < vgmstream->channels; i++) { + off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); + vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start + 0x01; + } + + /* flush decoder in every block change */ + flush_ea_mt(vgmstream); + break; + + #ifdef VGM_USE_MPEG + /* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */ + 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; + + /* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */ + if (vgmstream->channels > 2) { + channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile); + } else { + channel_start = read_32bit(block_offset + 0x0C,streamFile); + } + + vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; + } + break; + #endif + /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ + default: + for (i = 0; i < vgmstream->channels; i++) { + off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); + vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; + } + + /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ + if (flag_adpcm) { + for (i = 0; i < vgmstream->channels; i++) { + //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); + //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); + vgmstream->ch[i].offset += 4; + } + } + + break; + } } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_vawx.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xwav.c similarity index 88% rename from Frameworks/vgmstream/vgmstream/src/layout/blocked_vawx.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_xwav.c index dd28dc7ef..ddd5dafb1 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_vawx.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xwav.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* pseudo-blocks that must skip last 0x20 every 0x8000 */ -void block_update_vawx(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_xwav(off_t block_offset, VGMSTREAM * vgmstream) { int i; size_t block_size; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/flat.c b/Frameworks/vgmstream/vgmstream/src/layout/flat.c index 547cd55bb..ff3001b77 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/flat.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/flat.c @@ -26,7 +26,7 @@ void render_vgmstream_flat(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vg samples_to_do = sample_count - samples_written; if (samples_to_do == 0) { /* when decoding more than num_samples */ - VGM_LOG("FLAT: samples_to_do 0\n"); + VGM_LOG_ONCE("FLAT: samples_to_do 0\n"); goto decode_fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c index fb707aa12..0714d6d93 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c @@ -145,6 +145,6 @@ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTR } return; fail: - VGM_LOG("layout_interleave: wrong values found\n"); + VGM_LOG_ONCE("layout_interleave: wrong values found\n"); memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t)); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layered.c b/Frameworks/vgmstream/vgmstream/src/layout/layered.c index de392ef8d..25f6e2122 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layered.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/layered.c @@ -35,7 +35,7 @@ void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM* samples_to_do = sample_count - samples_written; if (samples_to_do <= 0) { /* when decoding more than num_samples */ - VGM_LOG("LAYERED: samples_to_do 0\n"); + VGM_LOG_ONCE("LAYERED: samples_to_do 0\n"); goto decode_fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 2b6405dc2..db4beef07 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -38,7 +38,7 @@ void block_update_hwas(off_t block_offset, VGMSTREAM* vgmstream); void block_update_ea_sns(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); -void block_update_vawx(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_xwav(off_t block_offset, VGMSTREAM* vgmstream); void block_update_xvag_subsong(off_t block_offset, VGMSTREAM* vgmstream); void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM* vgmstream); void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM* vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 035b0a223..fe0422a8c 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -22,7 +22,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA } if (data->current_segment >= data->segment_count) { - VGM_LOG("SEGMENT: wrong current segment\n"); + VGM_LOG_ONCE("SEGMENT: wrong current segment\n"); goto decode_fail; } @@ -42,7 +42,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA data->current_segment++; if (data->current_segment >= data->segment_count) { /* when decoding more than num_samples */ - VGM_LOG("SEGMENTED: reached last segment\n"); + VGM_LOG_ONCE("SEGMENTED: reached last segment\n"); goto decode_fail; } @@ -62,7 +62,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA samples_to_do = VGMSTREAM_SEGMENT_SAMPLE_BUFFER; if (samples_to_do < 0) { /* 0 is ok? */ - VGM_LOG("SEGMENTED: wrong samples_to_do %i found\n", samples_to_do); + VGM_LOG_ONCE("SEGMENTED: wrong samples_to_do %i found\n", samples_to_do); goto decode_fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acb.c b/Frameworks/vgmstream/vgmstream/src/meta/acb.c index de32d6925..e5fbf35b0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/acb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/acb.c @@ -15,11 +15,11 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) { /* checks */ if (!check_extensions(sf, "acb")) goto fail; - if (read_32bitBE(0x00,sf) != 0x40555446) /* "@UTF" */ + if (read_u32be(0x00,sf) != 0x40555446) /* "@UTF" */ goto fail; /* .acb is a cue sheet that uses @UTF (CRI's generic table format) to store row/columns - * with complex info (cues, sequences, spatial info, etc). it can store a memory .awb + * with complex info (cues, sequences, spatial info, etc). It can store a memory .awb * (our target here), or reference external/streamed .awb (loaded elsewhere) * we only want .awb with actual waves but may use .acb to get names */ { @@ -34,8 +34,6 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) { if (rows != 1 || strcmp(name, "Header") != 0) goto fail; - //todo acb+cpk is also possible - if (!utf_query_data(utf, 0, "AwbFile", &offset, &size)) goto fail; @@ -52,10 +50,16 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) { temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "awb"); if (!temp_sf) goto fail; - vgmstream = init_vgmstream_awb_memory(temp_sf, sf); - if (!vgmstream) goto fail; + if (read_u32be(0x00, temp_sf) == 0x43504B20) { /* "CPK " */ + vgmstream = init_vgmstream_cpk_memory(temp_sf, sf); /* older */ + if (!vgmstream) goto fail; + } + else { + vgmstream = init_vgmstream_awb_memory(temp_sf, sf); /* newer */ + if (!vgmstream) goto fail; + } - /* name-loading for this for memory .awb will be called from init_vgmstream_awb_memory */ + /* name-loading for this for memory .awb will be called from init_vgmstream_awb/cpk_memory */ utf_close(utf); close_streamfile(temp_sf); @@ -271,11 +275,12 @@ static int load_acb_synth(acb_header* acb, int16_t Index) { acb->synth_depth++; - if (acb->synth_depth > 2) { + /* sometimes 2 (ex. Yakuza 6) or even 3 (Street Fighter vs Tekken) */ + if (acb->synth_depth > 3) { VGM_LOG("ACB: Synth depth too high\n"); - goto fail; /* max Synth > Synth > Waveform (ex. Yakuza 6) */ + goto fail; } - + //todo .CommandIndex > CommandTable //todo .TrackValues > TrackTable? @@ -351,7 +356,7 @@ static int load_acb_command_tlvs(acb_header* acb, STREAMFILE* sf, uint32_t Comma tlv_code = read_u16be(offset + 0x00, sf); tlv_size = read_u8 (offset + 0x02, sf); offset += 0x03; - + /* There are around 160 codes (some unused), with things like set volume, pan, stop, mute, and so on. * Multiple commands are linked and only "note on" seems to point so other objects, so maybe others * apply to current object (since there is "note off" without reference. */ @@ -533,7 +538,7 @@ static int load_acb_block(acb_header* acb, int16_t Index) { VGM_LOG("ACB: wrong Block.TrackIndex size\n"); goto fail; } - + //todo .ActionTrackStartIndex/NumActionTracks > ? /* read Tracks inside Block */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awb.c b/Frameworks/vgmstream/vgmstream/src/meta/awb.c index b0be134b0..929d6f014 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awb.c @@ -178,9 +178,13 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre char filename[PATH_LIMIT]; int len_name, len_cmp; + /* try parsing TXTM if present */ + sf_acb = read_filemap_file(sf, 0); /* try (name).awb + (name).awb */ - sf_acb = open_streamfile_by_ext(sf, "acb"); + if (!sf_acb) { + sf_acb = open_streamfile_by_ext(sf, "acb"); + } /* try (name)_streamfiles.awb + (name).acb */ if (!sf_acb) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c index 2eceef972..a31044b2d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c @@ -32,13 +32,14 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { * as other chunks, and may have a DATA/DIDX index to memory .wem in DATA. * We support the internal .wem mainly for quick tests, as the HIRC is * complex and better handled with TXTP (some info from Nicknine's script). - * unlike RIFF, first chunk follows chunk rules */ + * Use this to explore HIRC and covert to .txtp: https://github.com/bnnm/wwiser */ version = read_u32(base_offset + 0x08, sf); if (version == 0 || version == 1) { /* early games */ version = read_u32(base_offset + 0x10, sf); } + /* first chunk also follows standard chunk sizes unlike RIFF */ if (version <= 26) { off_t data_offset, data_start, offset; if (!find_chunk(sf, 0x44415441, base_offset, 0, &data_offset, NULL, big_endian, 0)) /* "DATA" */ @@ -50,7 +51,7 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { * 08: entries size * 0c: padding size after entries * 10: data size - * 14: size? + * 14: size? or null * 18: data start * 1c: data size * per entry: @@ -135,13 +136,23 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { vgmstream->num_streams = total_subsongs; - - if (is_dummy) - snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/dummy", subfile_id); - else if (is_wmid) - snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/wmid", subfile_id); - else if (subfile_id != 0xFFFFFFFF) - snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id); + + { + const char* info = NULL; + if (is_dummy) + info = "dummy"; + else if (is_wmid) + info = "wmid"; + + /* old Wwise shows index or (more often) -1, unify to index*/ + if (subfile_id == 0xFFFFFFFF) + subfile_id = target_subsong - 1; + + if (info) + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/%s", subfile_id, info); + else + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id); + } close_streamfile(temp_sf); return vgmstream; @@ -153,7 +164,7 @@ fail: } -/* BKHD mini format, probably from a FX generator plugin [Borderlands 2 (X360)] */ +/* BKHD mini format, for FX plugins [Borderlands 2 (X360), Warhammer 40000 (PC)] */ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset, data_size; @@ -162,29 +173,48 @@ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) { /* checks */ - if (!check_extensions(sf,"wem,bnk")) /* assumed */ + /* .wem: used when (rarely) external */ + if (!check_extensions(sf,"wem,bnk")) goto fail; big_endian = guess_endianness32bit(0x00, sf); read_u32 = big_endian ? read_u32be : read_u32le; - if (read_u32(0x00, sf) != 0x0400) /* codec? */ - goto fail; - if (read_u32(0x04, sf) != 0x0800) /* codec? */ - goto fail; - sample_rate = read_u32(0x08, sf); - channels = read_u32(0x0c, sf); - /* 0x10: some id or small size? */ - /* 0x14/18: some float? */ - entries = read_u32(0x1c, sf); - /* 0x20 data size / 0x10 */ - /* 0x24 usually 4, sometimes higher values? */ - /* 0x30: unknown table of 16b that goes up and down */ + /* Not an actual stream but typically convolution reverb models and other FX plugin helpers. + * Useless but to avoid "subsong not playing" complaints. */ + + if (read_u32(0x00, sf) == 0x0400 && + read_u32(0x04, sf) == 0x0800) { + sample_rate = read_u32(0x08, sf); + channels = read_u32(0x0c, sf) & 0xFF; /* 0x31 at 0x0d in PC, field is 32b vs X360 */ + /* 0x10: some id or small size? (related to entries?) */ + /* 0x14/18: some float? */ + entries = read_u32(0x1c, sf); + /* 0x20 data size / 0x10 */ + /* 0x24 usually 4, sometimes higher values? */ + /* 0x30: unknown table of 16b that goes up and down, or is fixed */ + + start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10); + data_size = get_streamfile_size(sf) - start_offset; + } + else if (read_u32be(0x04, sf) == 0x00004844 && /* floats actually? */ + read_u32be(0x08, sf) == 0x0000FA45 && + read_u32be(0x1c, sf) == 0x80000000) { + /* seen in Crucible banks */ + sample_rate = 48000; /* meh */ + channels = 1; + + start_offset = 0; + data_size = get_streamfile_size(sf); + big_endian = 0; + } + else { + goto fail; + } - start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10); - data_size = get_streamfile_size(sf) - start_offset; loop_flag = 0; - /* output sounds a bit funny, maybe not an actual stream but parts using the table */ + + /* data seems divided in chunks of 0x2000 */ /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channels, loop_flag); @@ -200,7 +230,7 @@ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) { vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 32); - if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cpk.c b/Frameworks/vgmstream/vgmstream/src/meta/cpk.c new file mode 100644 index 000000000..22dcccd6b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/cpk.c @@ -0,0 +1,248 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "cri_utf.h" + + +typedef enum { HCA, CWAV, } cpk_type_t; + +static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid); + +/* CPK - CRI container as audio bank [Metal Gear Solid: Snake Eater 3D (3DS), Street Fighter X Tekken (X360), Ace Combat Infinity (PS3)] */ +VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf) { + return init_vgmstream_cpk_memory(sf, NULL); +} + +VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset = 0; + size_t subfile_size = 0; + utf_context* utf = NULL; + int total_subsongs, target_subsong = sf->stream_index; + int subfile_id = 0; + cpk_type_t type; + const char* extension = NULL; + uint32_t* sizes = NULL; + + + /* checks */ + if (!check_extensions(sf, "awb")) + goto fail; + if (read_u32be(0x00,sf) != 0x43504B20) /* "CPK " */ + goto fail; + if (read_u32be(0x10,sf) != 0x40555446) /* "@UTF" */ + goto fail; + /* 04: 0xFF? */ + /* 08: 0x02A0? */ + /* 0c: null? */ + + /* CPK .cpk is CRI's generic file container, but here we only support CPK .awb used as + * early audio bank, that like standard AFS2 .awb comes with .acb */ + { + int rows, i; + const char* name; + const char* Tvers; + uint32_t table_offset = 0, offset; + uint32_t Files = 0, FilesL = 0, FilesH = 0; + uint64_t ContentOffset = 0, ItocOffset = 0; + uint16_t Align = 0; + uint32_t DataL_offset = 0, DataL_size = 0, DataH_offset = 0, DataH_size = 0; + + /* base header */ + table_offset = 0x10; + utf = utf_open(sf, table_offset, &rows, &name); + if (!utf || strcmp(name, "CpkHeader") != 0 || rows != 1) + goto fail; + + if (!utf_query_string(utf, 0, "Tvers", &Tvers) || + !utf_query_u32(utf, 0, "Files", &Files) || + !utf_query_u64(utf, 0, "ContentOffset", &ContentOffset) || /* absolute */ + !utf_query_u64(utf, 0, "ItocOffset", &ItocOffset) || /* Toc seems used for regular files */ + !utf_query_u16(utf, 0, "Align", &Align)) + goto fail; + + utf_close(utf); + utf = NULL; + + if (strncmp(Tvers, "awb", 3) != 0) /* starts with "awb" + ".(version)" (SFvTK, MGS3D) or " for (version)" (ACI) */ + goto fail; + if (Files <= 0) + goto fail; + + + /* Itoc header (regular .CPK tend to use Toc or Etoc header) */ + table_offset = 0x10 + ItocOffset; + utf = utf_open(sf, table_offset, &rows, &name); + if (!utf) goto fail; + + if (rows != 1 || strcmp(name, "CpkItocInfo") != 0) + goto fail; + + if (!utf_query_u32(utf, 0, "FilesL", &FilesL) || + !utf_query_u32(utf, 0, "FilesH", &FilesH) || + !utf_query_data(utf, 0, "DataL", &DataL_offset, &DataL_size) || /* absolute */ + !utf_query_data(utf, 0, "DataH", &DataH_offset, &DataH_size)) /* absolute */ + goto fail; + + utf_close(utf); + utf = NULL; + + + /* For maximum annoyance there are 2 tables (small+big files) that only list sizes, + * and files can be mixed (small small big small big). + * Must pre-read all entries to find actual offset plus subsongs number. */ + if (FilesL + FilesH != Files) + goto fail; + + total_subsongs = Files; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + + sizes = calloc(Files, sizeof(uint32_t)); + if (!sizes) goto fail; + + /* DataL header */ + table_offset = DataL_offset; + utf = utf_open(sf, table_offset, &rows, &name); + if (!utf || strcmp(name, "CpkItocL") != 0 || rows != FilesL) + goto fail; + + for (i = 0; i < rows; i++) { + uint16_t ID = 0; + uint16_t FileSize, ExtractSize; + + if (!utf_query_u16(utf, i, "ID", &ID) || + !utf_query_u16(utf, i, "FileSize", &FileSize) || + !utf_query_u16(utf, i, "ExtractSize", &ExtractSize)) + goto fail; + + if (ID >= Files || FileSize != ExtractSize || sizes[ID]) + goto fail; + + sizes[ID] = FileSize; + } + + utf_close(utf); + utf = NULL; + + /* DataR header */ + table_offset = DataH_offset; + utf = utf_open(sf, table_offset, &rows, &name); + if (!utf || strcmp(name, "CpkItocH") != 0 || rows != FilesH) + goto fail; + + for (i = 0; i < rows; i++) { + uint16_t ID = 0; + uint32_t FileSize, ExtractSize; + + if (!utf_query_u16(utf, i, "ID", &ID) || + !utf_query_u32(utf, i, "FileSize", &FileSize) || + !utf_query_u32(utf, i, "ExtractSize", &ExtractSize)) + goto fail; + + if (ID >= Files || FileSize != ExtractSize || sizes[ID]) + goto fail; + + sizes[ID] = FileSize; + } + + utf_close(utf); + utf = NULL; + + + /* find actual offset */ + offset = ContentOffset; + for (i = 0; i < Files; i++) { + uint32_t size = sizes[i]; + if (i + 1 == target_subsong) { + subfile_id = i; + subfile_offset = offset; + subfile_size = size; + break; + } + + offset += size; + if (Align && (offset % Align)) + offset += Align - (offset % Align); + } + + free(sizes); + sizes = NULL; + } + + if (!subfile_offset) + goto fail; + + //;VGM_LOG("CPK: subfile offset=%lx + %x, id=%i\n", subfile_offset, subfile_size, subfile_id); + + + if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" */ + type = HCA; + extension = "hca"; + } + else if (read_u32be(subfile_offset,sf) == 0x43574156) { /* "CWAV" */ + type = CWAV; + extension = "bcwav"; + } + else { + goto fail; + } + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension); + if (!temp_sf) goto fail; + + switch(type) { + case HCA: + vgmstream = init_vgmstream_hca(temp_sf); + if (!vgmstream) goto fail; + break; + case CWAV: /* Metal Gear Solid: Snake Eater 3D (3DS) */ + vgmstream = init_vgmstream_rwsd(temp_sf); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + + /* try to load cue names */ + load_cpk_name(sf, sf_acb, vgmstream, subfile_id); + + close_streamfile(temp_sf); + return vgmstream; + +fail: + free(sizes); + utf_close(utf); + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + +static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) { + int is_memory = (sf_acb != NULL); + + /* .acb is passed when loading memory .awb inside .acb */ + if (!is_memory) { + /* try parsing TXTM if present */ + sf_acb = read_filemap_file(sf, 0); + + /* try (name).awb + (name).awb */ + if (!sf_acb) + sf_acb = open_streamfile_by_ext(sf, "acb"); + + /* (name)_streamfiles.awb + (name).acb also exist */ + + if (!sf_acb) + return; + + /* companion .acb probably loaded */ + load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory); + + close_streamfile(sf_acb); + } + else { + load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory); + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c index f1dddb2d1..94851fed8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c @@ -369,15 +369,30 @@ static int utf_query_value(utf_context* utf, int row, const char* column, void* return 1; } +int utf_query_s8(utf_context* utf, int row, const char* column, int8_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT8); +} int utf_query_u8(utf_context* utf, int row, const char* column, uint8_t* value) { return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT8); } +int utf_query_s16(utf_context* utf, int row, const char* column, int16_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT16); +} int utf_query_u16(utf_context* utf, int row, const char* column, uint16_t* value) { return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT16); } +int utf_query_s32(utf_context* utf, int row, const char* column, int32_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT32); +} int utf_query_u32(utf_context* utf, int row, const char* column, uint32_t* value) { return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT32); } +int utf_query_s64(utf_context* utf, int row, const char* column, int64_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT64); +} +int utf_query_u64(utf_context* utf, int row, const char* column, uint64_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT64); +} int utf_query_string(utf_context* utf, int row, const char* column, const char** value) { return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_STRING); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h index e8d2a6d20..6e7ec59db 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h @@ -24,9 +24,14 @@ typedef struct utf_context utf_context; utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const char** p_row_name); void utf_close(utf_context* utf); /* query calls */ +int utf_query_s8(utf_context* utf, int row, const char* column, int8_t* value); int utf_query_u8(utf_context* utf, int row, const char* column, uint8_t* value); +int utf_query_s16(utf_context* utf, int row, const char* column, int16_t* value); int utf_query_u16(utf_context* utf, int row, const char* column, uint16_t* value); +int utf_query_s32(utf_context* utf, int row, const char* column, int32_t* value); int utf_query_u32(utf_context* utf, int row, const char* column, uint32_t* value); +int utf_query_s64(utf_context* utf, int row, const char* column, int64_t* value); +int utf_query_u64(utf_context* utf, int row, const char* column, uint64_t* value); int utf_query_string(utf_context* utf, int row, const char* column, const char** value); int utf_query_data(utf_context* utf, int row, const char* column, uint32_t* offset, uint32_t* size); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index 647496925..1abeca016 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -25,17 +25,17 @@ typedef struct { int codec_config; int is_bank; int total_subsongs; -} ea_header; +} eacs_header; -static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); -static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea); +static int parse_header(STREAMFILE* streamFile, eacs_header* ea, off_t begin_offset); +static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, eacs_header* ea); -static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, ea_header *ea, int find_loop); -static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); +static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eacs_header *ea, int find_loop); +static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const eacs_header* ea); /* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { - ea_header ea = { 0 }; + eacs_header ea = { 0 }; off_t offset, eacs_offset; VGMSTREAM *vgmstream = NULL; @@ -93,7 +93,7 @@ fail: /* EA EACS - from early EA games, bank (~1996, ex. Need for Speed) */ VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE *streamFile) { - ea_header ea = {0}; + eacs_header ea = {0}; off_t eacs_offset; @@ -157,7 +157,7 @@ fail: } -static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea) { +static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, eacs_header* ea) { VGMSTREAM * vgmstream = NULL; @@ -210,7 +210,7 @@ fail: return NULL; } -static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { +static int parse_header(STREAMFILE* streamFile, eacs_header* ea, off_t offset) { /* audio header endianness doesn't always match block headers, use sample rate to detect */ int32_t (*read_32bit)(off_t,STREAMFILE*); @@ -278,7 +278,7 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { } /* get total samples by parsing block headers, needed when EACS isn't present */ -static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, ea_header *ea, int find_loop) { +static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eacs_header *ea, int find_loop) { int32_t num_samples = 0, block_id; size_t file_size; int32_t(*read_32bit)(off_t, STREAMFILE *) = ea->big_endian ? read_32bitBE : read_32bitLE; @@ -323,7 +323,7 @@ static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile } /* find codec version used, with or without ADPCM hist per block */ -static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { +static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const eacs_header* ea) { off_t block_offset = start_offset; size_t file_size = get_streamfile_size(streamFile); int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 348b9d4c6..cd7ac3907 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -4,7 +4,7 @@ #include "../coding/coding.h" #include "ea_eaac_streamfile.h" -/* EAAudioCore formats, EA's current audio middleware */ +/* EAAudioCore (aka SND10) formats, EA's current audio middleware */ #define EAAC_VERSION_V0 0x00 /* SNR/SNS */ #define EAAC_VERSION_V1 0x01 /* SPS */ @@ -105,12 +105,12 @@ fail: /* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { int is_dupe, total_sounds = 0, target_stream = sf->stream_index; - off_t bnk_offset, header_table_offset, base_offset, unk_struct_offset, table_offset, snd_entry_offset, ast_offset; - off_t num_entries_off, base_offset_off, entries_off, sound_table_offset_off; - uint32_t i, j, k, num_sounds, total_sound_tables; - uint16_t num_tables, bnk_index, bnk_target_index; - uint8_t num_entries, extra_entries; - off_t sound_table_offsets[0x2000]; + off_t bnk_offset, modules_table, module_data, player_offset, samples_table, entry_offset, ast_offset; + off_t cfg_num_players_off, cfg_module_data_off, cfg_module_entry_size, cfg_samples_table_off; + uint32_t i, j, k, num_sounds, num_sample_tables; + uint16_t num_modules, bnk_index, bnk_target_index; + uint8_t num_players; + off_t sample_tables[0x400]; VGMSTREAM *vgmstream; int32_t(*read_32bit)(off_t, STREAMFILE*); int16_t(*read_16bit)(off_t, STREAMFILE*); @@ -135,10 +135,10 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { if (target_stream < 0) goto fail; - num_tables = read_16bit(0x0A, sf); - header_table_offset = read_32bit(0x1C, sf); + num_modules = read_16bit(0x0A, sf); + modules_table = read_32bit(0x1C, sf); bnk_offset = read_32bit(0x20, sf); - total_sound_tables = 0; + num_sample_tables = 0; bnk_target_index = 0xFFFF; ast_offset = 0; @@ -146,36 +146,35 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { goto fail; /* set up some common values */ - if (header_table_offset == 0x5C) { + if (modules_table == 0x5C) { /* the usual variant */ - num_entries_off = 0x24; - base_offset_off = 0x2C; - entries_off = 0x3C; - sound_table_offset_off = 0x04; - } else if (header_table_offset == 0x78) { + cfg_num_players_off = 0x24; + cfg_module_data_off = 0x2C; + cfg_module_entry_size = 0x3C; + cfg_samples_table_off = 0x04; + } else if (modules_table == 0x78) { /* FIFA 08 has a bunch of extra zeroes all over the place, don't know what's up with that */ - num_entries_off = 0x40; - base_offset_off = 0x54; - entries_off = 0x68; - sound_table_offset_off = 0x0C; + cfg_num_players_off = 0x40; + cfg_module_data_off = 0x54; + cfg_module_entry_size = 0x68; + cfg_samples_table_off = 0x0C; } else { goto fail; } - for (i = 0; i < num_tables; i++) { - num_entries = read_8bit(header_table_offset + num_entries_off, sf); - extra_entries = read_8bit(header_table_offset + num_entries_off + 0x03, sf); - base_offset = read_32bit(header_table_offset + base_offset_off, sf); - if (num_entries == 0xff) goto fail; /* EOF read */ + for (i = 0; i < num_modules; i++) { + num_players = read_8bit(modules_table + cfg_num_players_off, sf); + module_data = read_32bit(modules_table + cfg_module_data_off, sf); + if (num_players == 0xff) goto fail; /* EOF read */ - for (j = 0; j < num_entries; j++) { - unk_struct_offset = read_32bit(header_table_offset + entries_off + 0x04 * j, sf); - table_offset = read_32bit(base_offset + unk_struct_offset + sound_table_offset_off, sf); + for (j = 0; j < num_players; j++) { + player_offset = read_32bit(modules_table + cfg_module_entry_size + 0x04 * j, sf); + samples_table = read_32bit(module_data + player_offset + cfg_samples_table_off, sf); - /* For some reason, there are duplicate entries pointing at the same sound tables */ + /* multiple players may point at the same sound table */ is_dupe = 0; - for (k = 0; k < total_sound_tables; k++) { - if (table_offset == sound_table_offsets[k]) { + for (k = 0; k < num_sample_tables; k++) { + if (samples_table == sample_tables[k]) { is_dupe = 1; break; } @@ -184,17 +183,17 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { if (is_dupe) continue; - sound_table_offsets[total_sound_tables++] = table_offset; - num_sounds = read_32bit(table_offset, sf); + sample_tables[num_sample_tables++] = samples_table; + num_sounds = read_32bit(samples_table, sf); if (num_sounds == 0xffffffff) goto fail; /* EOF read */ for (k = 0; k < num_sounds; k++) { /* 0x00: sound index */ - /* 0x02: ??? */ - /* 0x04: ??? */ + /* 0x02: priority */ + /* 0x03: azimuth */ /* 0x08: streamed data offset */ - snd_entry_offset = table_offset + 0x04 + 0x0C * k; - bnk_index = read_16bit(snd_entry_offset + 0x00, sf); + entry_offset = samples_table + 0x04 + 0x0C * k; + bnk_index = read_16bit(entry_offset + 0x00, sf); /* some of these are dummies */ if (bnk_index == 0xFFFF) @@ -203,12 +202,14 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { total_sounds++; if (target_stream == total_sounds) { bnk_target_index = bnk_index; - ast_offset = read_32bit(snd_entry_offset + 0x08, sf); + ast_offset = read_32bit(entry_offset + 0x08, sf); } } } - header_table_offset += entries_off + num_entries * 0x04 + extra_entries * 0x04; + /* skip class controllers */ + num_players += read_8bit(modules_table + cfg_num_players_off + 0x03, sf); + modules_table += cfg_module_entry_size + num_players * 0x04; } if (bnk_target_index == 0xFFFF || ast_offset == 0) @@ -234,7 +235,9 @@ static VGMSTREAM * parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t targ /* header is always big endian */ /* 0x00: header magic */ - /* 0x04: zero */ + /* 0x04: version */ + /* 0x05: padding */ + /* 0x06: serial number */ /* 0x08: number of files */ /* 0x0C: offsets table */ if (read_32bitBE(offset + 0x00, sf) != 0x53313041) /* "S10A" */ @@ -380,13 +383,14 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) { int32_t(*read_32bit)(off_t, STREAMFILE*); /* 0x00: ID */ - /* 0x02: userdata size */ + /* 0x02: parameters (userdata size, ...) */ /* 0x03: number of files */ /* 0x04: sub-ID (used for different police voices in NFS games) */ - /* 0x08: alt number of files? */ - /* 0x09: zero */ - /* 0x0A: related to size? */ - /* 0x0C: zero */ + /* 0x08: sample repeat (alt number of files?) */ + /* 0x09: block size (always zero?) */ + /* 0x0A: number of blocks (related to size?) */ + /* 0x0C: number of sub-banks (always zero?) */ + /* 0x0E: padding */ /* 0x10: table start */ if (!check_extensions(sf, "hdr")) @@ -508,9 +512,9 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) {"world.mpf", "World_Stream.mus"}, {"FreSkate.mpf", "track.mus,ram.mus"}, /* Skate It */ {"nsf_sing.mpf", "track_main.mus"}, /* Need for Speed: Nitro */ - {"nsf_wii.mpf", "Track.mus"}, /* Need for Speed: Nitro */ + {"nsf_wii.mpf", "Track.mus"}, {"ssx_fe.mpf", "stream_1.mus,stream_2.mus"}, /* SSX 2012 */ - {"ssxdd.mpf", "main_trk.mus," /* SSX 2012 */ + {"ssxdd.mpf", "main_trk.mus," "trick_alaska0.mus," "trick_rockies0.mus," "trick_pata0.mus," @@ -522,16 +526,20 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) "trick_alps0.mus," "trick_lhotse0.mus"} }; - STREAMFILE *musFile = NULL; + STREAMFILE *sf_mus = NULL; char file_name[PATH_LIMIT]; int pair_count = (sizeof(mapfile_pairs) / sizeof(mapfile_pairs[0])); int i, j; size_t file_len, map_len; + /* try parsing TXTM if present */ + sf_mus = read_filemap_file(sf, track); + if (sf_mus) return sf_mus; + /* if loading the first track, try opening MUS with the same name first (most common scenario) */ if (track == 0) { - musFile = open_streamfile_by_ext(sf, "mus"); - if (musFile) return musFile; + sf_mus = open_streamfile_by_ext(sf, "mus"); + if (sf_mus) return sf_mus; } get_streamfile_filename(sf, file_name, PATH_LIMIT); @@ -575,8 +583,8 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) strncpy(file_name, pch, PATH_LIMIT - 1); } - musFile = open_streamfile_by_filename(sf, file_name); - if (musFile) return musFile; + sf_mus = open_streamfile_by_filename(sf, file_name); + if (sf_mus) return sf_mus; get_streamfile_filename(sf, file_name, PATH_LIMIT); /* reset for next loop */ } @@ -587,8 +595,8 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) char *mod_name = strchr(file_name, '+'); if (mod_name) { mod_name[0] = '\0'; - musFile = open_streamfile_by_filename(sf, file_name); - if (musFile) return musFile; + sf_mus = open_streamfile_by_filename(sf, file_name); + if (sf_mus) return sf_mus; } } @@ -729,31 +737,35 @@ fail: return NULL; } -/* EA TMX - used for engine sounds in NFS games (2007-present) */ +/* EA TMX - used for engine sounds in NFS games (2007-2011) */ VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE* sf) { - uint32_t num_sounds, sound_type; - off_t table_offset, data_offset, entry_offset, sound_offset; + uint32_t num_sounds, sound_type, table_offset, data_offset, entry_offset, sound_offset; VGMSTREAM *vgmstream = NULL; int target_stream = sf->stream_index; + uint32_t(*read_u32)(off_t, STREAMFILE *); if (!check_extensions(sf, "tmx")) goto fail; - /* always little endian */ - if (read_32bitLE(0x0c, sf) != 0x30303031) /* "0001" */ + if (read_u32be(0x0c, sf) == 0x30303031) { /* "0001" */ + read_u32 = read_u32be; + } else if (read_u32le(0x0c, sf) == 0x30303031) { /* "1000" */ + read_u32 = read_u32le; + } else { goto fail; + } - num_sounds = read_32bitLE(0x20, sf); - table_offset = read_32bitLE(0x58, sf); - data_offset = read_32bitLE(0x5c, sf); + num_sounds = read_u32(0x20, sf); + table_offset = read_u32(0x58, sf); + data_offset = read_u32(0x5c, sf); if (target_stream == 0) target_stream = 1; if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds) goto fail; entry_offset = table_offset + (target_stream - 1) * 0x24; - sound_type = read_32bitLE(entry_offset + 0x00, sf); - sound_offset = read_32bitLE(entry_offset + 0x08, sf) + data_offset; + sound_type = read_u32(entry_offset + 0x00, sf); + sound_offset = read_u32(entry_offset + 0x08, sf) + data_offset; switch (sound_type) { case 0x47494E20: /* "GIN " */ @@ -1018,7 +1030,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM eaac.channel_config = (header1 >> 18) & 0x3F; /* 6 bits */ eaac.sample_rate = (header1 >> 0) & 0x03FFFF; /* 18 bits */ eaac.type = (header2 >> 30) & 0x03; /* 2 bits */ - eaac.loop_flag = (header2 >> 29) & 0x01; /* 1 bits */ + eaac.loop_flag = (header2 >> 29) & 0x01; /* 1 bit */ eaac.num_samples = (header2 >> 0) & 0x1FFFFFFF; /* 29 bits */ /* rest is optional, depends on used flags and codec (handled below) */ @@ -1028,7 +1040,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM /* EA 6ch channel mapping is L C R BL BR LFE, but may use stereo layers for dynamic music * instead, so we can't re-map automatically (use TXTP) */ - /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than block flags) */ + /* V0: SNR+SNS, V1: SPH+SPS (no apparent differences, other than block flags) */ if (eaac.version != EAAC_VERSION_V0 && eaac.version != EAAC_VERSION_V1) { VGM_LOG("EA EAAC: unknown version\n"); goto fail; @@ -1046,6 +1058,12 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM goto fail; } + if (eaac.version == EAAC_VERSION_V1 && eaac.type != EAAC_TYPE_STREAM) { + /* should never happen */ + VGM_LOG("EA EAAC: bad stream type for version %x\n", eaac.version); + goto fail; + } + /* Non-streamed sounds are stored as a single block (may not set block end flags) */ eaac.streamed = (eaac.type != EAAC_TYPE_RAM); @@ -1082,12 +1100,13 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM } break; case EAAC_TYPE_GIGASAMPLE: /* rarely seen [Def Jam Icon (X360)] */ - if (eaac.loop_flag) { - VGM_LOG("EAAC: Looped gigasample found.\n"); - goto fail; - } header_size += 0x04; - eaac.prefetch_samples = read_32bitBE(header_offset + 0x08, sf_head); + eaac.prefetch_samples = read_32bitBE(header_offset + eaac.loop_flag ? 0x0c : 0x08, sf_head); + + if (eaac.loop_flag && eaac.loop_start >= eaac.prefetch_samples) { + header_size += 0x04; + eaac.loop_offset = read_32bitBE(header_offset + 0x10, sf_head); + } break; } @@ -1115,7 +1134,20 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM /* SNR+SNS are separate so offsets are relative to the data start * (first .SNS block, or extra data before the .SNS block in case of .SNU) * SPS have headers+data together so offsets are relative to the file start [ex. FIFA 18 (PC)] */ - if (eaac.version == EAAC_VERSION_V1) { + if (eaac.version == EAAC_VERSION_V0) { + if (eaac.prefetch_samples != 0) { + if (eaac.loop_start == 0) { + /* loop from the beginning */ + eaac.loop_offset = 0x00; + } else if (eaac.loop_start < eaac.prefetch_samples) { + /* loop from the second RAM block */ + eaac.loop_offset = read_32bitBE(eaac.prefetch_offset, sf_head) & 0x00FFFFFF; + } else { + /* loop from offset within SNS */ + eaac.loop_offset += read_32bitBE(eaac.prefetch_offset, sf_head) & 0x00FFFFFF; + } + } + } else { eaac.loop_offset -= header_block_size; } } else if (eaac.loop_start > 0) { @@ -1341,13 +1373,15 @@ static size_t calculate_eaac_size(STREAMFILE *sf, eaac_header *ea, uint32_t num_ stream_size += block_size; block_offset += block_size; - if (is_ram) { - /* RAM data only consists of one block (two for looped sounds) */ - if (ea->loop_start > 0 && !looped) looped = 1; - else break; - } else if (ea->version == EAAC_VERSION_V0 && block_id == EAAC_BLOCKID0_END) { - if (ea->loop_offset > 0 && !looped) looped = 1; - else break; + if (ea->version == EAAC_VERSION_V0) { + if (is_ram) { + /* RAM data only consists of one block (two for looped sounds) */ + if (ea->loop_start > 0 && ea->loop_start < num_samples && !looped) looped = 1; + else break; + } else if (block_id == EAAC_BLOCKID0_END) { + if (ea->loop_offset > 0 && ea->loop_start >= ea->prefetch_samples && !looped) looped = 1; + else break; + } } } @@ -1426,13 +1460,7 @@ static STREAMFILE *setup_eaac_streamfile(eaac_header *ea, STREAMFILE* sf_head, S break; } } else { - if (ea->type == EAAC_TYPE_GIGASAMPLE) { - /* not seen so far, need samples */ - VGM_LOG("EAAC: Found SPS gigasample\n"); - goto fail; - } - - data_size = calculate_eaac_size(sf_head, ea, ea->num_samples, ea->stream_offset, ea->type == EAAC_TYPE_RAM); + data_size = calculate_eaac_size(sf_head, ea, ea->num_samples, ea->stream_offset, 0); if (data_size == 0) goto fail; new_sf = open_wrap_streamfile(sf_head); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 96a0081ee..75fb332b8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -5,51 +5,63 @@ /* header version */ #define EA_VERSION_NONE -1 -#define EA_VERSION_V0 0x00 /* ~early PC (when codec1 was used) */ -#define EA_VERSION_V1 0x01 /* ~PC */ -#define EA_VERSION_V2 0x02 /* ~PS1 */ -#define EA_VERSION_V3 0x03 /* ~PS2 */ +#define EA_VERSION_V0 0x00 /* ~early PC (when codec1 was used) */ +#define EA_VERSION_V1 0x01 /* ~PC */ +#define EA_VERSION_V2 0x02 /* ~PS1 */ +#define EA_VERSION_V3 0x03 /* ~PS2 */ /* platform constants (unassigned values seem internal only) */ -#define EA_PLATFORM_GENERIC -1 /* typically Wii/X360/PS3/videos */ #define EA_PLATFORM_PC 0x00 #define EA_PLATFORM_PSX 0x01 #define EA_PLATFORM_N64 0x02 #define EA_PLATFORM_MAC 0x03 #define EA_PLATFORM_SAT 0x04 #define EA_PLATFORM_PS2 0x05 -#define EA_PLATFORM_GC_WII 0x06 +#define EA_PLATFORM_GC 0x06 /* also used on Wii */ #define EA_PLATFORM_XBOX 0x07 +#define EA_PLATFORM_GENERIC 0x08 /* typically Wii/X360/PS3/videos */ #define EA_PLATFORM_X360 0x09 #define EA_PLATFORM_PSP 0x0A -#define EA_PLATFORM_PS3 0x0E /* very rare [Need for Speed: Carbon (PS3)] */ +#define EA_PLATFORM_PS3 0x0E /* very rare [Need for Speed: Carbon (PS3)] */ +#define EA_PLATFORM_WII 0x10 #define EA_PLATFORM_3DS 0x14 /* codec constants (undefined are probably reserved, ie.- sx.exe encodes PCM24/DVI but no platform decodes them) */ /* CODEC1 values were used early, then they migrated to CODEC2 values */ #define EA_CODEC1_NONE -1 #define EA_CODEC1_PCM 0x00 -#define EA_CODEC1_VAG 0x01 // unsure +#define EA_CODEC1_VAG 0x01 /* unsure */ #define EA_CODEC1_EAXA 0x07 #define EA_CODEC1_MT10 0x09 #define EA_CODEC1_N64 0x64 /* unknown but probably before MT10 */ #define EA_CODEC2_NONE -1 +#define EA_CODEC2_S16LE_INT 0x00 +#define EA_CODEC2_S16BE_INT 0x01 +#define EA_CODEC2_S8_INT 0x02 +#define EA_CODEC2_EAXA_INT 0x03 #define EA_CODEC2_MT10 0x04 #define EA_CODEC2_VAG 0x05 +#define EA_CODEC2_N64 0x06 #define EA_CODEC2_S16BE 0x07 #define EA_CODEC2_S16LE 0x08 #define EA_CODEC2_S8 0x09 #define EA_CODEC2_EAXA 0x0A +//#define EA_CODEC2_U8_INT 0x0B /* not used */ +//#define EA_CODEC2_CDXA 0x0C /* not used */ +//#define EA_CODEC2_IMA 0x0D /* not used */ +//#define EA_CODEC2_LAYER1 0x0E /* not used */ #define EA_CODEC2_LAYER2 0x0F -#define EA_CODEC2_LAYER3 0x10 +#define EA_CODEC2_LAYER3 0x10 /* not seen so far but may be used somewhere */ #define EA_CODEC2_GCADPCM 0x12 +//#define EA_CODEC2_S24LE_INT 0x13 /* not used */ #define EA_CODEC2_XBOXADPCM 0x14 +//#define EA_CODEC2_S24BE_INT 0x15 /* not used */ #define EA_CODEC2_MT5 0x16 #define EA_CODEC2_EALAYER3 0x17 +//#define EA_CODEC2_ATRAC3 0x1A /* not seen so far */ #define EA_CODEC2_ATRAC3PLUS 0x1B -#define EA_CODEC2_N64 0x64 /* unknown but probably before MT10 */ /* Block headers, SCxy - where x is block ID and y is endianness flag (always 'l'?) */ #define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */ @@ -105,6 +117,7 @@ typedef struct { int big_endian; int loop_flag; int codec_config; + int use_pcm_blocks; size_t stream_size; } ea_header; @@ -125,6 +138,7 @@ VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) { * .asf: ~early (audio stream file?) [ex. Need for Speed II (PC)] * .lasf: fake for plugins * .str: ~early [ex. FIFA 98 (PS1), FIFA 2002 (PS1)] + * .chk: ~early [ex. NBA Live 98 (PS1)] * .eam: ~mid? * .exa: ~mid [ex. 007 - From Russia with Love] * .sng: ~late (FIFA games) @@ -138,7 +152,7 @@ VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) { * .gsf: 007 - Everything or Nothing (GC) * .mus: map/mpf+mus only? * (extensionless): SSX (PS2) (inside .big) */ - if (!check_extensions(sf,"asf,lasf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) + if (!check_extensions(sf,"asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) goto fail; /* check header */ @@ -287,11 +301,11 @@ fail: /* streamed assets are stored externally in AST file (mostly seen in earlier 6th-gen games) */ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = sf->stream_index; - off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset, schl_loop_offset; - uint32_t i, j, k, num_sounds, total_sound_tables; - uint16_t num_tables; - uint8_t sound_type, num_entries; - off_t sound_table_offsets[0x2000]; + off_t bnk_offset, modules_table, module_data, player_offset, samples_table, entry_offset, target_entry_offset, schl_offset, schl_loop_offset; + uint32_t i, j, k, num_sounds, num_sample_tables; + uint16_t num_modules; + uint8_t sound_type, num_players; + off_t sample_tables[0x400]; STREAMFILE * astData = NULL; VGMSTREAM * vgmstream = NULL; segmented_layout_data *data_s = NULL; @@ -318,11 +332,11 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { if (target_stream < 0) goto fail; - num_tables = read_16bit(0x0A, sf); - header_table_offset = read_32bit(0x1C, sf); + num_modules = read_16bit(0x0A, sf); + modules_table = read_32bit(0x1C, sf); bnk_offset = read_32bit(0x20, sf); target_entry_offset = 0; - total_sound_tables = 0; + num_sample_tables = 0; /* check to avoid clashing with the newer ABK format */ if (bnk_offset && @@ -330,19 +344,19 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { read_32bitBE(bnk_offset, sf) != EA_BNK_HEADER_BE) goto fail; - for (i = 0; i < num_tables; i++) { - num_entries = read_8bit(header_table_offset + 0x24, sf); - base_offset = read_32bit(header_table_offset + 0x2C, sf); - if (num_entries == 0xff) goto fail; /* EOF read */ + for (i = 0; i < num_modules; i++) { + num_players = read_8bit(modules_table + 0x24, sf); + module_data = read_32bit(modules_table + 0x2C, sf); + if (num_players == 0xff) goto fail; /* EOF read */ - for (j = 0; j < num_entries; j++) { - value_offset = read_32bit(header_table_offset + 0x3C + 0x04 * j, sf); - table_offset = read_32bit(base_offset + value_offset + 0x04, sf); + for (j = 0; j < num_players; j++) { + player_offset = read_32bit(modules_table + 0x3C + 0x04 * j, sf); + samples_table = read_32bit(module_data + player_offset + 0x04, sf); - /* For some reason, there are duplicate entries pointing at the same sound tables */ + /* multiple players may point at the same sound table */ is_dupe = 0; - for (k = 0; k < total_sound_tables; k++) { - if (table_offset == sound_table_offsets[k]) { + for (k = 0; k < num_sample_tables; k++) { + if (samples_table == sample_tables[k]) { is_dupe = 1; break; } @@ -351,12 +365,12 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { if (is_dupe) continue; - sound_table_offsets[total_sound_tables++] = table_offset; - num_sounds = read_32bit(table_offset, sf); + sample_tables[num_sample_tables++] = samples_table; + num_sounds = read_32bit(samples_table, sf); if (num_sounds == 0xffffffff) goto fail; /* EOF read */ for (k = 0; k < num_sounds; k++) { - entry_offset = table_offset + 0x04 + 0x0C * k; + entry_offset = samples_table + 0x04 + 0x0C * k; sound_type = read_8bit(entry_offset + 0x00, sf); /* some of these are dummies pointing at sound 0 in BNK */ @@ -369,16 +383,17 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { } } - /* there can be another set of values, don't know what they mean */ - num_entries += read_8bit(header_table_offset + 0x27, sf); - header_table_offset += 0x3C + num_entries * 0x04; + /* skip class controllers */ + num_players += read_8bit(modules_table + 0x27, sf); + modules_table += 0x3C + num_players * 0x04; } if (target_entry_offset == 0) goto fail; /* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed looped) */ - /* 0x01: ??? */ + /* 0x01: priority */ + /* 0x02: padding */ /* 0x04: index for normal sounds, offset for streamed sounds */ /* 0x08: loop offset for streamed sounds */ sound_type = read_8bit(target_entry_offset + 0x00, sf); @@ -439,18 +454,9 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(data_s->segments[0]->channels, 1); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = data_s->segments[0]->sample_rate; - vgmstream->num_samples = data_s->segments[0]->num_samples + data_s->segments[1]->num_samples; - vgmstream->loop_start_sample = data_s->segments[0]->num_samples; - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->meta_type = meta_EA_SCHL; - vgmstream->coding_type = data_s->segments[0]->coding_type; - vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data_s; + vgmstream = allocate_segmented_vgmstream(data_s, 1, 1, 1); + if (!vgmstream) + goto fail; break; default: @@ -562,10 +568,11 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE* sf) { /* 0x02: parameters (userdata size, ...) */ /* 0x03: number of files */ /* 0x04: sub-ID (used for different police voices in NFS games) */ - /* 0x08: alt number of files? */ - /* 0x09: offset mult */ - /* 0x0a: DAT size divided by offset mult */ - /* 0x0c: zero */ + /* 0x08: sample repeat (alt number of files?) */ + /* 0x09: block size (offset multiplier) */ + /* 0x0A: number of blocks (DAT size divided by block size) */ + /* 0x0C: number of sub-banks (always zero?) */ + /* 0x0E: padding */ /* 0x10: table start */ /* no nice way to validate these so we do what we can */ @@ -627,13 +634,8 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) {"mus_ctrl.mpf", "mus_str.mus"}, /* GoldenEye - Rogue Agent (others) */ {"AKA_Mus.mpf", "Track.mus"}, /* Boogie */ {"SSX4FE.mpf", "TrackFE.mus"}, /* SSX On Tour */ - {"SSX4Path.mpf", "Track.mus"}, /* SSX On Tour */ + {"SSX4Path.mpf", "Track.mus"}, {"SSX4.mpf", "moments0.mus,main.mus,load_loop0.mus"}, /* SSX Blur */ - {"willow.mpf", "willow.mus,willow_o.mus"}, /* Harry Potter and the Chamber of Secrets */ - {"exterior.mpf", "exterior.mus,ext_o.mus"}, /* Harry Potter and the Chamber of Secrets */ - {"Peak1Amb.mpf", "Peak1_Strm.mus,Peak1_Ovr0.mus"}, /* SSX 3 */ - {"Peak2Amb.mpf", "Peak2_Strm.mus,Peak2_Ovr0.mus"}, - {"Peak3Amb.mpf", "Peak3_Strm.mus,Peak3_Ovr0.mus"}, {"*.mpf", "*_main.mus"}, /* 007 - Everything or Nothing */ /* TODO: need better wildcard support * NSF2: @@ -656,6 +658,10 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) int i, j; size_t file_len, map_len; + /* try parsing TXTM if present */ + sf_mus = read_filemap_file(sf, track); + if (sf_mus) return sf_mus; + /* if loading the first track, try opening MUS with the same name first (most common scenario) */ if (track == 0) { sf_mus = open_streamfile_by_ext(sf, "mus"); @@ -782,6 +788,7 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE* sf) { goto fail; vgmstream->num_streams = num_sounds; + get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE); close_streamfile(sf_mus); return vgmstream; @@ -794,12 +801,12 @@ fail: VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE* sf_mus = NULL; - off_t tracks_table, samples_table, section_offset, entry_offset, eof_offset, off_mult, sound_offset; - uint32_t track_start, track_hash = 0; + segmented_layout_data *data_s = NULL; + uint32_t track_start, track_end = 0, track_hash = 0, tracks_table, samples_table = 0, section_offset, entry_offset = 0, eof_offset = 0, off_mult, sound_offset; uint16_t num_nodes; - uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num; - int32_t(*read_32bit)(off_t, STREAMFILE*); - int16_t(*read_16bit)(off_t, STREAMFILE*); + uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num = 0; + uint32_t(*read_u32)(off_t, STREAMFILE*); + uint16_t(*read_u16)(off_t, STREAMFILE*); int i; int target_stream = sf->stream_index, total_streams, big_endian, is_bnk = 0; @@ -809,29 +816,29 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { /* detect endianness */ if (read_32bitBE(0x00, sf) == 0x50464478) { /* "PFDx" */ - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; + read_u32 = read_u32be; + read_u16 = read_u16be; big_endian = 1; } else if (read_32bitLE(0x00, sf) == 0x50464478) { /* "xDFP" */ - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; + read_u32 = read_u32le; + read_u16 = read_u16le; big_endian = 0; } else { goto fail; } - version = read_8bit(0x04, sf); - sub_version = read_8bit(0x05, sf); + version = read_u8(0x04, sf); + sub_version = read_u8(0x05, sf); if (version < 3 || version > 5) goto fail; if (version == 5 && sub_version > 2) goto fail; /* newer version using SNR/SNS */ - num_tracks = read_8bit(0x0d, sf); - num_sections = read_8bit(0x0e, sf); - num_events = read_8bit(0x0f, sf); - num_routers = read_8bit(0x10, sf); - num_vars = read_8bit(0x11, sf); - num_nodes = read_16bit(0x12, sf); + num_tracks = read_u8(0x0d, sf); + num_sections = read_u8(0x0e, sf); + num_events = read_u8(0x0f, sf); + num_routers = read_u8(0x10, sf); + num_vars = read_u8(0x11, sf); + num_nodes = read_u16(0x12, sf); /* HACK: number of sub-entries for nodes and events is stored in bitstreams that are different in LE and BE */ /* I can't figure it out, so let's just use a workaround for now */ @@ -841,109 +848,123 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { if (version == 3) /* SSX Tricky (v3.1), Harry Potter and the Chamber of Secrets (v3.4) */ { /* we need to go through all the sections to get to the samples table */ + if (sub_version != 1 && sub_version != 2 && sub_version != 4) + goto fail; + /* get the last entry offset */ section_offset = 0x24; - entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; - if (sub_version == 1) { - subentry_num = read_8bit(entry_offset + 0x0b, sf); + entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; + if (sub_version == 1 || sub_version == 2) { + subentry_num = read_u8(entry_offset + 0x0b, sf); } else if (sub_version == 4) { if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 19) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 19) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 16) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 16) & 0xFF; } - } else { - goto fail; } section_offset = entry_offset + 0x0c + subentry_num * 0x04; section_offset += align_size_to_block(num_events * num_tracks * num_sections, 0x04); section_offset += num_routers * 0x04; section_offset += num_vars * 0x04; - tracks_table = read_32bit(section_offset, sf) * 0x04; - samples_table = tracks_table + (num_tracks + 1) * 0x04; + + tracks_table = read_u32(section_offset, sf) * 0x04; + if (sub_version == 1 || sub_version == 2) + samples_table = tracks_table + num_tracks * 0x04; + else if (sub_version == 4) + samples_table = tracks_table + (num_tracks + 1) * 0x04; + if (sub_version == 1 || sub_version == 2) + eof_offset = get_streamfile_size(sf); + else if (sub_version == 4) + eof_offset = read_u32(tracks_table + num_tracks * 0x04, sf) * 0x04; + total_streams = (eof_offset - samples_table) / 0x08; + off_mult = 0x04; + + track_start = total_streams; for (i = num_tracks - 1; i >= 0; i--) { - track_start = read_32bit(tracks_table + i * 0x04, sf) * 0x04; + track_end = track_start; + track_start = read_u32(tracks_table + i * 0x04, sf) * 0x04; track_start = (track_start - samples_table) / 0x08; if (track_start <= target_stream - 1) break; } - - eof_offset = read_32bit(tracks_table + num_tracks * 0x04, sf) * 0x04; - total_streams = (eof_offset - samples_table) / 0x08; - off_mult = 0x04; } else if (version == 4) { /* Need for Speed: Underground 2, SSX 3, Harry Potter and the Prisoner of Azkaban */ /* we need to go through all the sections to get to the samples table */ /* get the last entry offset */ section_offset = 0x20; - entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; + entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 15) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 15) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 20) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 20) & 0xFF; } section_offset = entry_offset + 0x10 + subentry_num * 0x04; /* get the last entry offset */ - entry_offset = (uint16_t)read_16bit(section_offset + (num_events - 1) * 0x02, sf) * 0x04; + entry_offset = read_u16(section_offset + (num_events - 1) * 0x02, sf) * 0x04; if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x0c, sf) >> 10) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 10) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x0c, sf) >> 8) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 8) & 0xFF; } section_offset = entry_offset + 0x10 + subentry_num * 0x10; /* TODO: verify this */ - section_offset = read_32bit(section_offset, sf) * 0x04; + section_offset = read_u32(section_offset, sf) * 0x04; section_offset += num_routers * 0x04; section_offset += num_vars * 0x04; + tracks_table = section_offset; samples_table = tracks_table + (num_tracks + 1) * 0x04; + eof_offset = read_u32(tracks_table + num_tracks * 0x04, sf) * 0x04; + total_streams = (eof_offset - samples_table) / 0x08; + off_mult = 0x80; + + track_start = total_streams; for (i = num_tracks - 1; i >= 0; i--) { - track_start = read_32bit(tracks_table + i * 0x04, sf) * 0x04; + track_end = track_start; + track_start = read_u32(tracks_table + i * 0x04, sf) * 0x04; track_start = (track_start - samples_table) / 0x08; if (track_start <= target_stream - 1) break; } - - eof_offset = read_32bit(tracks_table + num_tracks * 0x04, sf) * 0x04; - total_streams = (eof_offset - samples_table) / 0x08; - off_mult = 0x80; } else if (version == 5) { /* Need for Speed: Most Wanted, Need for Speed: Carbon */ - tracks_table = read_32bit(0x2c, sf); - samples_table = read_32bit(0x34, sf); + tracks_table = read_u32(0x2c, sf); + samples_table = read_u32(0x34, sf); + eof_offset = read_u32(0x38, sf); + total_streams = (eof_offset - samples_table) / 0x08; + off_mult = 0x80; + + track_start = total_streams; for (i = num_tracks - 1; i >= 0; i--) { - entry_offset = read_32bit(tracks_table + i * 0x04, sf) * 0x04; - track_start = read_32bit(entry_offset + 0x00, sf); + track_end = track_start; + entry_offset = read_u32(tracks_table + i * 0x04, sf) * 0x04; + track_start = read_u32(entry_offset + 0x00, sf); + + if (track_start == 0 && i != 0) + continue; /* empty track */ if (track_start <= target_stream - 1) { - track_hash = read_32bitBE(entry_offset + 0x08, sf); + track_hash = read_u32be(entry_offset + 0x08, sf); is_bnk = (track_hash == 0xF1F1F1F1); /* checks to distinguish it from SNR/SNS version */ if (is_bnk) { - if (read_32bitBE(entry_offset + 0x0c, sf) == 0x00) + if (read_u32(entry_offset + 0x0c, sf) == 0x00) goto fail; - - track_hash = read_32bitBE(entry_offset + 0x14, sf); - if (track_hash == 0xF1F1F1F1) - continue; /* empty track */ } else { - if (read_32bitBE(entry_offset + 0x0c, sf) != 0x00) + if (read_u32(entry_offset + 0x0c, sf) != 0x00) goto fail; } break; } } - - eof_offset = read_32bit(0x38, sf); - total_streams = (eof_offset - samples_table) / 0x08; - off_mult = 0x80; } else { goto fail; } @@ -956,22 +977,83 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { if (!sf_mus) goto fail; - if (version == 5) { - if (read_32bitBE(0x00, sf_mus) != track_hash) - goto fail; - } else { - is_bnk = (read_32bitBE(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE)); + if (version < 5) { + is_bnk = (read_u32be(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE)); } /* 0x00 - offset/BNK index, 0x04 - duration (in milliseconds) */ + sound_offset = read_u32(samples_table + (target_stream - 1) * 0x08 + 0x00, sf); + if (is_bnk) { - /* TODO: Harry Potter COS appears to reference only the first segments of multi-segment BNK sounds? */ - sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, sf); - vgmstream = parse_bnk_header(sf_mus, version < 5 ? 0x00 : 0x100, sound_offset, 1); + /* for some reason, RAM segments are almost always split into multiple sounds (usually 4) */ + off_t bnk_offset = version < 5 ? 0x00 : 0x100; + uint32_t bnk_sound_index = (sound_offset & 0x0000FFFF); + uint32_t bnk_index = (sound_offset & 0xFFFF0000) >> 16; + uint32_t next_entry; + uint32_t bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_mus); + int bnk_segments; + STREAMFILE *sf_bnk = sf_mus; + + if (version == 5 && bnk_index != 0) { + /* HACK: open proper .mus now since open_mapfile_pair doesn't let us adjust the name */ + char filename[PATH_LIMIT], basename[PATH_LIMIT], ext[32]; + int basename_len; + + get_streamfile_basename(sf_mus, basename, PATH_LIMIT); + basename_len = strlen(basename); + get_streamfile_ext(sf_mus, ext, sizeof(ext)); + + /* strip off 0 at the end */ + basename[basename_len - 1] = '\0'; + + /* append bank index to the name */ + snprintf(filename, PATH_LIMIT, "%s%d.%s", basename, bnk_index, ext); + + sf_bnk = open_streamfile_by_filename(sf_mus, filename); + if (!sf_bnk) goto fail; + bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_bnk); + close_streamfile(sf_mus); + sf_mus = sf_bnk; + } + + if (version == 5) { + track_hash = read_u32be(entry_offset + 0x14 + 0x10 * bnk_index, sf); + if (read_u32be(0x00, sf_mus) != track_hash) + goto fail; + } + + /* play until the next entry in MPF track or the end of BNK */ + if (target_stream < track_end) { + next_entry = read_u32(samples_table + (target_stream - 0) * 0x08 + 0x00, sf); + if (((next_entry & 0xFFFF0000) >> 16) == bnk_index) { + bnk_segments = (next_entry & 0x0000FFFF) - bnk_sound_index; + } else { + bnk_segments = bnk_total_sounds - bnk_sound_index; + } + } else { + bnk_segments = bnk_total_sounds - bnk_sound_index; + } + + /* init layout */ + data_s = init_layout_segmented(bnk_segments); + if (!data_s) goto fail; + + for (i = 0; i < bnk_segments; i++) { + data_s->segments[i] = parse_bnk_header(sf_mus, bnk_offset, bnk_sound_index + i, 1); + if (!data_s->segments[i]) goto fail; + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data_s)) goto fail; + + vgmstream = allocate_segmented_vgmstream(data_s, 0, 0, 0); if (!vgmstream) goto fail; } else { - sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, sf) * off_mult; + if (version == 5 && read_u32be(0x00, sf_mus) != track_hash) + goto fail; + + sound_offset *= off_mult;; if (read_32bitBE(sound_offset, sf_mus) != EA_BLOCKID_HEADER) goto fail; @@ -987,6 +1069,8 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { fail: close_streamfile(sf_mus); + free_layout_segmented(data_s); + return NULL; } @@ -1142,79 +1226,53 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* vgmstream->codec_config = ea->codec_config; vgmstream->meta_type = is_bnk ? meta_EA_BNK : meta_EA_SCHL; - - if (is_bnk) { - vgmstream->layout_type = layout_none; - - /* BNKs usually have absolute offsets for all channels ("full" interleave) except in some versions */ - if (ea->channels > 1 && ea->codec1 == EA_CODEC1_PCM) { - int interleave = (ea->num_samples * (ea->bps == 8 ? 0x01 : 0x02)); /* full interleave */ - for (i = 0; i < ea->channels; i++) { - ea->offsets[i] = ea->offsets[0] + interleave*i; - } - } - else if (ea->channels > 1 && ea->codec1 == EA_CODEC1_VAG) { - int interleave = (ea->num_samples / 28 * 16); /* full interleave */ - for (i = 0; i < ea->channels; i++) { - ea->offsets[i] = ea->offsets[0] + interleave*i; - } - } - else if (ea->channels > 1 && ea->codec2 == EA_CODEC2_GCADPCM && ea->offsets[0] == ea->offsets[1]) { - /* pcstream+gcadpcm with sx.exe v2, not in flag_value, probably a bug (even with this parts of the wave are off) */ - int interleave = (ea->num_samples / 14 * 8); /* full interleave */ - for (i = 0; i < ea->channels; i++) { - ea->offsets[i] = ea->offsets[0] + interleave*i; - } - } - else if (ea->channels > 1 && ea->codec2 == EA_CODEC2_N64 && ea->offsets[1] == 0) { - uint32_t interleave = ea->flag_value; - for (i = 0; i < ea->channels; i++) { - ea->offsets[i] = ea->offsets[0] + interleave * i; - } - } - } - else { - vgmstream->layout_type = layout_blocked_ea_schl; - } + vgmstream->layout_type = is_bnk ? layout_none : layout_blocked_ea_schl; /* EA usually implements their codecs in all platforms (PS2/WII do EAXA/MT/EALAYER3) and * favors them over platform's natives (ex. EAXA vs VAG/DSP). * Unneeded codecs are removed over time (ex. LAYER3 when EALAYER3 was introduced). */ switch (ea->codec2) { - case EA_CODEC2_EAXA: /* EA-XA, CDXA ADPCM variant */ - if (ea->version == EA_VERSION_V0) { - if (ea->platform != EA_PLATFORM_SAT && ea->channels > 1) - vgmstream->coding_type = coding_EA_XA; /* original version, stereo stream */ - else - vgmstream->coding_type = coding_EA_XA_int; /* interleaved mono streams */ - } - else { /* later revision with PCM blocks and slighty modified decoding */ + case EA_CODEC2_EAXA_INT: /* EA-XA (stereo) */ + vgmstream->coding_type = coding_EA_XA; + break; + + case EA_CODEC2_EAXA: /* EA-XA (split mono) */ + if (!ea->use_pcm_blocks) { + /* original version */ + vgmstream->coding_type = coding_EA_XA_int; + } else { + /* later revision with PCM blocks and slighty modified decoding */ vgmstream->coding_type = coding_EA_XA_V2; } break; - case EA_CODEC2_S8: /* PCM8 */ + case EA_CODEC2_S8_INT: /* PCM8 (interleaved) */ + vgmstream->coding_type = coding_PCM8_int; + break; + + case EA_CODEC2_S16LE_INT: /* PCM16LE (interleaved) */ + case EA_CODEC2_S16BE_INT: /* PCM16BE (interleaved) */ + vgmstream->coding_type = coding_PCM16_int; + break; + + case EA_CODEC2_S8: /* PCM8 (split) */ vgmstream->coding_type = coding_PCM8; break; - case EA_CODEC2_S16BE: /* PCM16BE */ - vgmstream->coding_type = coding_PCM16BE; + case EA_CODEC2_S16LE: /* PCM16LE (split) */ + vgmstream->coding_type = coding_PCM16LE; break; - case EA_CODEC2_S16LE: /* PCM16LE */ - if (ea->version > EA_VERSION_V0) { - vgmstream->coding_type = coding_PCM16LE; - } else { /* Need for Speed III: Hot Pursuit (PC) */ - vgmstream->coding_type = coding_PCM16_int; - } + case EA_CODEC2_S16BE: /* PCM16BE (split) */ + vgmstream->coding_type = coding_PCM16BE; break; case EA_CODEC2_VAG: /* PS-ADPCM */ vgmstream->coding_type = coding_PSX; break; - case EA_CODEC2_XBOXADPCM: /* XBOX IMA (interleaved mono) */ + case EA_CODEC2_XBOXADPCM: /* XBOX IMA (split mono) */ vgmstream->coding_type = coding_XBOX_IMA_int; break; @@ -1235,7 +1293,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* case EA_CODEC2_N64: /* VADPCM */ vgmstream->coding_type = coding_VADPCM; - vgmstream->layout_type = layout_none; for (ch = 0; ch < ea->channels; ch++) { int order = read_u32be(ea->coefs[ch] + 0x00, sf); @@ -1254,7 +1311,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* if (!mpeg_start_offset) goto fail; /* layout is still blocks, but should work fine with the custom mpeg decoder */ - vgmstream->codec_data = init_mpeg_custom(sf, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, mpeg_start_offset, &vgmstream->coding_type, ea->channels, MPEG_EA, &cfg); if (!vgmstream->codec_data) goto fail; break; } @@ -1267,23 +1324,14 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* if (!mpeg_start_offset) goto fail; /* layout is still blocks, but should work fine with the custom mpeg decoder */ - vgmstream->codec_data = init_mpeg_custom(sf, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, mpeg_start_offset, &vgmstream->coding_type, ea->channels, MPEG_EAL31, &cfg); if (!vgmstream->codec_data) goto fail; break; } #endif case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */ - case EA_CODEC2_MT5: { /* MicroTalk (5:1 compression) */ - int use_pcm_blocks = 0; - - if (ea->version == EA_VERSION_V3 || (ea->version == EA_VERSION_V2 && - (ea->platform == EA_PLATFORM_PC || - ea->platform == EA_PLATFORM_MAC || - ea->platform == EA_PLATFORM_GENERIC))) { - use_pcm_blocks = 1; - } - + case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */ /* make relative loops absolute for the decoder */ if (ea->loop_flag) { for (i = 0; i < ea->channels; i++) { @@ -1292,13 +1340,12 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* } vgmstream->coding_type = coding_EA_MT; - vgmstream->codec_data = init_ea_mt_loops(vgmstream->channels, use_pcm_blocks, ea->loop_start, ea->loops); + vgmstream->codec_data = init_ea_mt_loops(ea->channels, ea->use_pcm_blocks, ea->loop_start, ea->loops); if (!vgmstream->codec_data) goto fail; break; - } #ifdef VGM_USE_FFMPEG - case EA_CODEC2_ATRAC3PLUS: { + case EA_CODEC2_ATRAC3PLUS: { /* ATRAC3+ */ /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header [Medal of Honor Heroes 2 (PSP)] */ if (!is_bnk) { STREAMFILE* temp_sf = NULL; @@ -1337,31 +1384,76 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* if (is_bnk) { - /* setup channel offsets */ - if (vgmstream->coding_type == coding_EA_XA) { - /* shared (stereo/mono codec) */ - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = ea->offsets[0]; + /* BNKs usually have absolute offsets for all channels ("full" interleave) except in some versions */ + if (!(ea->codec_config & 0x04)) { + switch (vgmstream->coding_type) { + case coding_EA_XA: + /* shared (stereo version) */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0]; + } + break; + case coding_EA_XA_int: { + int interleave = ea->num_samples / 28 * 0x0f; /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] * interleave*i; + } + break; + } + case coding_PCM8_int: + case coding_PCM16_int: { + int interleave = ea->bps==8 ? 0x01 : 0x02; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + case coding_PCM8: + case coding_PCM16LE: + case coding_PCM16BE: { + int interleave = ea->num_samples * (ea->bps==8 ? 0x01 : 0x02); /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + case coding_PSX: { + int interleave = ea->num_samples / 28 * 0x10; /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + case coding_VADPCM: { + uint32_t interleave = ea->flag_value; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + case coding_EA_MT: { + uint32_t interleave = ea->flag_value; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + default: + VGM_LOG("EA SCHl: Unknown channel offsets for codec 0x%02x in version %d\n", ea->codec1, ea->version); + goto fail; } - } - //else if (vgmstream->layout_type == layout_interleave) { /* interleaved */ - // for (i = 0; i < vgmstream->channels; i++) { - // vgmstream->ch[i].offset = ea->offsets[0] + vgmstream->interleave_block_size*i; - // } - //} - else if (vgmstream->coding_type == coding_PCM16_int && ea->version == EA_VERSION_V0) { - /* Need for Speed II (PC) bad offsets */ - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = ea->offsets[0] + 0x02*i; + } else if (vgmstream->coding_type == coding_NGC_DSP && vgmstream->channels > 1 && ea->offsets[0] == ea->offsets[1]) { + /* pcstream+gcadpcm with sx.exe v2, not in flag_value, probably a bug (even with this parts of the wave are off) */ + int interleave = (ea->num_samples / 14 * 8); /* full interleave */ + for (i = 0; i < ea->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; } - } - else if (vgmstream->coding_type == coding_PCM8 && ea->platform == EA_PLATFORM_PS2 && ea->version == EA_VERSION_V3) { - /* SSX3 (PS2) weird 0x10 mini header (codec/loop start/loop end/samples) */ + } else if (ea->platform == EA_PLATFORM_PS2 && (ea->flag_value & 0x100)) { + /* weird 0x10 mini header when played on IOP (codec/loop start/loop end/samples) [SSX 3 (PS2)] */ for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = ea->offsets[0] + 0x10; + vgmstream->ch[i].offset = ea->offsets[i] + 0x10; } - } - else { + } else { /* absolute */ for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = ea->offsets[i]; @@ -1369,8 +1461,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* } /* TODO: Figure out how to get stream size for BNK sounds */ - } - else { + } else { update_ea_stream_size_and_samples(sf, start_offset, vgmstream, standalone); } @@ -1632,9 +1723,10 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs if (ea->platform == EA_PLATFORM_N64 || ea->platform == EA_PLATFORM_MAC || ea->platform == EA_PLATFORM_SAT - || ea->platform == EA_PLATFORM_GC_WII + || ea->platform == EA_PLATFORM_GC || ea->platform == EA_PLATFORM_X360 || ea->platform == EA_PLATFORM_PS3 + || ea->platform == EA_PLATFORM_WII || ea->platform == EA_PLATFORM_GENERIC) { ea->big_endian = 1; } @@ -1648,16 +1740,17 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs if (ea->version == EA_VERSION_NONE) { switch(ea->platform) { case EA_PLATFORM_PC: ea->version = EA_VERSION_V0; break; - case EA_PLATFORM_PSX: ea->version = EA_VERSION_V0; break; // assumed + case EA_PLATFORM_PSX: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_N64: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_MAC: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_SAT: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_PS2: ea->version = EA_VERSION_V1; break; - case EA_PLATFORM_GC_WII: ea->version = EA_VERSION_V2; break; + case EA_PLATFORM_GC: ea->version = EA_VERSION_V2; break; case EA_PLATFORM_XBOX: ea->version = EA_VERSION_V2; break; case EA_PLATFORM_X360: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_PSP: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_PS3: ea->version = EA_VERSION_V3; break; + case EA_PLATFORM_WII: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_3DS: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break; default: @@ -1670,9 +1763,9 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs if (ea->codec1 == EA_CODEC1_NONE && ea->version == EA_VERSION_V0) { switch(ea->platform) { case EA_PLATFORM_PC: ea->codec1 = EA_CODEC1_PCM; break; - case EA_PLATFORM_PSX: ea->codec1 = EA_CODEC1_VAG; break; // assumed + case EA_PLATFORM_PSX: ea->codec1 = EA_CODEC1_VAG; break; case EA_PLATFORM_N64: ea->codec1 = EA_CODEC1_N64; break; - case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; // assumed + case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; case EA_PLATFORM_SAT: ea->codec1 = EA_CODEC1_PCM; break; default: VGM_LOG("EA SCHl: unknown default codec1 for platform 0x%02x\n", ea->platform); @@ -1684,10 +1777,18 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs if (ea->codec1 != EA_CODEC1_NONE && ea->codec2 == EA_CODEC2_NONE) { switch (ea->codec1) { case EA_CODEC1_PCM: - ea->codec2 = ea->bps==8 ? EA_CODEC2_S8 : (ea->big_endian ? EA_CODEC2_S16BE : EA_CODEC2_S16LE); + if (ea->platform == EA_PLATFORM_PC) + ea->codec2 = ea->bps==8 ? EA_CODEC2_S8_INT : (ea->big_endian ? EA_CODEC2_S16BE_INT : EA_CODEC2_S16LE_INT); + else + ea->codec2 = ea->bps==8 ? EA_CODEC2_S8 : (ea->big_endian ? EA_CODEC2_S16BE : EA_CODEC2_S16LE); break; case EA_CODEC1_VAG: ea->codec2 = EA_CODEC2_VAG; break; - case EA_CODEC1_EAXA: ea->codec2 = EA_CODEC2_EAXA; break; + case EA_CODEC1_EAXA: + if (ea->platform == EA_PLATFORM_PC || ea->platform == EA_PLATFORM_MAC) + ea->codec2 = EA_CODEC2_EAXA_INT; + else + ea->codec2 = EA_CODEC2_EAXA; + break; case EA_CODEC1_MT10: ea->codec2 = EA_CODEC2_MT10; break; case EA_CODEC1_N64: ea->codec2 = EA_CODEC2_N64; break; default: @@ -1704,11 +1805,12 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs case EA_PLATFORM_PSX: ea->codec2 = EA_CODEC2_VAG; break; case EA_PLATFORM_MAC: ea->codec2 = EA_CODEC2_EAXA; break; case EA_PLATFORM_PS2: ea->codec2 = EA_CODEC2_VAG; break; - case EA_PLATFORM_GC_WII: ea->codec2 = EA_CODEC2_S16BE; break; + case EA_PLATFORM_GC: ea->codec2 = EA_CODEC2_S16BE; break; case EA_PLATFORM_XBOX: ea->codec2 = EA_CODEC2_S16LE; break; case EA_PLATFORM_X360: ea->codec2 = EA_CODEC2_EAXA; break; case EA_PLATFORM_PSP: ea->codec2 = EA_CODEC2_EAXA; break; case EA_PLATFORM_PS3: ea->codec2 = EA_CODEC2_EAXA; break; + //case EA_PLATFORM_WII: ea->codec2 = EA_CODEC2_EAXA; break; /* not set? */ case EA_PLATFORM_3DS: ea->codec2 = EA_CODEC2_GCADPCM; break; default: VGM_LOG("EA SCHl: unknown default codec2 for platform 0x%02x\n", ea->platform); @@ -1726,11 +1828,12 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs case EA_PLATFORM_MAC: ea->sample_rate = 22050; break; case EA_PLATFORM_SAT: ea->sample_rate = 22050; break; case EA_PLATFORM_PS2: ea->sample_rate = 22050; break; - case EA_PLATFORM_GC_WII: ea->sample_rate = 24000; break; + case EA_PLATFORM_GC: ea->sample_rate = 24000; break; case EA_PLATFORM_XBOX: ea->sample_rate = 24000; break; case EA_PLATFORM_X360: ea->sample_rate = 44100; break; case EA_PLATFORM_PSP: ea->sample_rate = 22050; break; case EA_PLATFORM_PS3: ea->sample_rate = 44100; break; + case EA_PLATFORM_WII: ea->sample_rate = 32000; break; case EA_PLATFORM_3DS: ea->sample_rate = 32000; break; default: VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform); @@ -1738,6 +1841,12 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs } } + /* EA-XA and MicroTalk got updated revisions with PCM blocks in sx v2.30 */ + ea->use_pcm_blocks = (ea->version == EA_VERSION_V3 || (ea->version == EA_VERSION_V2 && + (ea->platform == EA_PLATFORM_PC || + ea->platform == EA_PLATFORM_MAC || + ea->platform == EA_PLATFORM_GENERIC))); + /* some codecs have ADPCM hist at the start of every block in streams (but not BNKs) */ if (!is_bnk) { if (ea->codec2 == EA_CODEC2_GCADPCM) { @@ -1745,20 +1854,18 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs ea->codec_config |= 0x01; } else if (ea->codec2 == EA_CODEC2_EAXA) { - /* EA-XA has ADPCM hist in earlier versions */ - /* V0, V1: always */ - /* V2: consoles only */ - /* V3: never */ - if (ea->version <= EA_VERSION_V1) { + /* EA-XA has ADPCM hist in the original version */ + if (!ea->use_pcm_blocks) ea->codec_config |= 0x01; - } - else if (ea->version == EA_VERSION_V2) { - if (ea->platform == EA_PLATFORM_PS2 || ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX) - ea->codec_config |= 0x01; - } } } + if (ea->version > EA_VERSION_V0) { + /* v0 needs channel offsets to be manually calculated + * v1+ always has split channels and provides channel offsets */ + ea->codec_config |= 0x04; + } + return offset; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c index eb5f48737..33f7b18a4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c @@ -16,9 +16,9 @@ typedef struct { int big_endian; int loop_flag; -} ea_header; +} ea_fixed_header; -static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); +static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset); /* EA SCHl with fixed header - from EA games (~1997? ex. NHL 97 PC) */ @@ -26,7 +26,7 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; size_t header_size; - ea_header ea = {0}; + ea_fixed_header ea = {0}; /* checks */ @@ -87,7 +87,7 @@ fail: } -static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset) { +static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset) { off_t offset = begin_offset; if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index a0b60e7fb..3d2d48a04 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -22,13 +22,13 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) { if (get_streamfile_size(sf) <= 0x1000) goto fail; + if (target_subsong == 0) target_subsong = 1; /* init ffmpeg */ - data = init_ffmpeg_offset(sf, 0, get_streamfile_size(sf)); + data = init_ffmpeg_header_offset_subsong(sf, NULL, 0, 0, get_streamfile_size(sf), target_subsong); if (!data) return NULL; - total_subsongs = data->streamCount; - if (target_subsong == 0) target_subsong = 1; + total_subsongs = data->streamCount; /* uncommon, ex. wmv [Lost Odyssey (X360)] */ if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* try to get .pos data */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 046b1a4b1..1ae3d71a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -207,9 +207,9 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { * (xN entries) */ break; - case 0x0d: /* unknown 32b (config? usually 0x3fnnnn00 BE and sometimes 0x3dnnnn00 BE) */ - /* found in some XMA2/Vorbis/FADPCM */ - VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_32bitLE(extraflag_offset+0x04,sf)); + case 0x0d: /* peak volume float (optional setting when making fsb) */ + break; + case 0x0f: /* OPUS data size not counting frames headers */ break; case 0x0e: /* number of layered Vorbis channels [Invisible, Inc. (Switch)] */ default: @@ -479,6 +479,20 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { vgmstream->interleave_block_size = 0x8c; break; +#if 0 //disabled until some game is found, can be created in the GUI tool +#ifdef VGM_USE_FFMPEG + case 0x11: { /* FMOD_SOUND_FORMAT_OPUS */ + int skip = 312; //fsb_opus_get_encoder_delay(fsb5.stream_offset, sf); /* returns 120 but this seems correct */ + //vgmstream->num_samples -= skip; + + vgmstream->codec_data = init_ffmpeg_fsb_opus(sf, fsb5.stream_offset, fsb5.stream_size, vgmstream->channels, skip, vgmstream->sample_rate); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif +#endif default: VGM_LOG("FSB5: unknown codec %x found\n", fsb5.codec); goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c index 2ea1b2176..4014716c9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c @@ -1,6 +1,9 @@ #include "meta.h" #include "../coding/coding.h" + +static int get_subsongs(STREAMFILE* sf, off_t fsb5_offset, size_t fsb5_size); + /* FEV+FSB5 container [Just Cause 3 (PC), Shantae: Half-Genie Hero (Switch)] */ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; @@ -8,6 +11,8 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { off_t subfile_offset, chunk_offset, bank_offset, offset; size_t subfile_size, bank_size; uint32_t version = 0; + int fsb5_pos, fsb5_subsong; + int total_subsongs, target_subsong = sf->stream_index; /* checks */ @@ -31,7 +36,7 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { goto fail; /* event .fev has "OBCT" instead of "BNKI" (which can also be empty) */ - /* inside BNKI is a bunch of LIST each with event subchunks and somewhere the FSB5 offset */ + /* inside BNKI is a bunch of LISTs each with event subchunks and somewhere the FSB5 offsets */ bank_offset = 0; offset = chunk_offset + 0x04; while (bank_offset == 0 && offset < get_streamfile_size(sf)) { @@ -65,39 +70,52 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { if (bank_offset == 0) goto fail; - /* 0x00: unknown (chunk version? ex LE: 0x00080003, 0x00080005) */ { - /* versions: - * 0x28: Transistor (iOS) [+2015] - * 0x50: Runic Rampage (PC), Forza 7 (PC), Shantae: Half Genie Hero (Switch) [+2017] - * 0x58: Mana Spark (PC), Shantae and the Seven Sirens (PC) [+2018] - * 0x63: Banner Saga 3 (PC) [+2018] - * 0x64: Guacamelee! Super Turbo Championship Edition (Switch) [+2018] - * 0x65: Carrion (Switch) [+2020] - * 0x7D: Fall Guys (PC) [+2020] */ + /* known bank versions: + * 0x28: Transistor (iOS) [~2015] + * 0x50: Runic Rampage (PC), Forza 7 (PC), Shantae: Half Genie Hero (Switch) [~2017] + * 0x58: Mana Spark (PC), Shantae and the Seven Sirens (PC) [~2018] + * 0x63: Banner Saga 3 (PC) [~2018] + * 0x64: Guacamelee! Super Turbo Championship Edition (Switch) [~2018] + * 0x65: Carrion (Switch) [~2020] + * 0x7D: Fall Guys (PC) [~2020] + * 0x84: SCP Unity (PC) [~2020] + * 0x86: Hades (Switch) [~2020] */ size_t entry_size = version <= 0x28 ? 0x04 : 0x08; - int banks; + int i, banks; - /* multiple banks is possible but rare (only seen an extra "Silence" FSB5 in Guacamelee 2 (Switch), - * which on PC is a regular subsong in the only FSB5) */ + /* 0x00: unknown (chunk version? ex LE: 0x00080003, 0x00080005) */ banks = (bank_size - 0x04) / entry_size; - VGM_ASSERT(banks > 1, "FSB5FEV: multiple banks found\n"); + + /* multiple banks is possible but rare [Hades (Switch), Guacamelee 2 (Switch)], + * must map bank (global) subsong to FSB (internal) subsong */ - /* Could try to set stream index based on FSB subsong ranges, also fixing num_streams and stream_index - * kinda involved and hard to test so for now just ignore it and use first offset */ + if (target_subsong == 0) target_subsong = 1; - if (banks > 2) - goto fail; - if (banks == 2) { - off_t temp_offset = read_u32le(bank_offset + 0x04 + entry_size*1 + 0x00,sf); - //size_t temp_size = read_u32le(bank_offset + 0x04 + entry_size*1 + 0x04,sf); + fsb5_pos = 0; + fsb5_subsong = -1; + total_subsongs = 0; + for (i = 0; i < banks; i++) { + //TODO: fsb5_size fails for v0x28< + encrypted, but only used with multibanks = unlikely + off_t fsb5_offset = read_u32le(bank_offset + 0x04 + entry_size*i + 0x00,sf); + size_t fsb5_size = read_u32le(bank_offset+0x08 + entry_size*i,sf); + int fsb5_subsongs = get_subsongs(sf, fsb5_offset, fsb5_size); + if (!fsb5_subsongs) + goto fail; - int bank_subsongs = read_s32le(temp_offset + 0x08,sf); - if (bank_subsongs != 1) goto fail; + /* target in range */ + if (target_subsong >= total_subsongs + 1 && target_subsong < total_subsongs + 1 + fsb5_subsongs) { + fsb5_pos = i; + fsb5_subsong = target_subsong - total_subsongs; + } + + total_subsongs += fsb5_subsongs; } + if (fsb5_subsong < 0) + goto fail; if (version <= 0x28) { - subfile_offset = read_u32le(bank_offset+0x04,sf); + subfile_offset = read_u32le(bank_offset+0x04 + entry_size*fsb5_pos,sf); subfile_size = /* meh */ read_u32le(subfile_offset + 0x0C,sf) + read_u32le(subfile_offset + 0x10,sf) + @@ -105,23 +123,27 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { 0x3C; } else { - subfile_offset = read_u32le(bank_offset+0x04,sf); - subfile_size = read_u32le(bank_offset+0x08,sf); + subfile_offset = read_u32le(bank_offset+0x04 + entry_size*fsb5_pos,sf); + subfile_size = read_u32le(bank_offset+0x08 + entry_size*fsb5_pos,sf); } } + //;VGM_LOG("FSB5 FEV: offset=%lx, size=%x\n", subfile_offset,subfile_size); + temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "fsb"); if (!temp_sf) goto fail; - if (read_u32be(0x00, temp_sf) == 0x46534235) { - vgmstream = init_vgmstream_fsb5(temp_sf); - close_streamfile(temp_sf); - } - else { //other flag? - vgmstream = init_vgmstream_fsb_encrypted(temp_sf); - close_streamfile(temp_sf); - } + temp_sf->stream_index = fsb5_subsong; /* relative subsong, in case of multiple FSBs */ + vgmstream = (read_u32be(0x00, temp_sf) == 0x46534235) ? /* "FSB5" (better flag?)*/ + init_vgmstream_fsb5(temp_sf) : + init_vgmstream_fsb_encrypted(temp_sf); + if (!vgmstream) goto fail; + + vgmstream->stream_index = sf->stream_index; //target_subsong; /* 0-index matters */ + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_sf); return vgmstream; fail: @@ -129,3 +151,29 @@ fail: close_vgmstream(vgmstream); return NULL; } + +static int get_subsongs(STREAMFILE* sf, off_t fsb5_offset, size_t fsb5_size) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + int subsongs = 0; + + + /* standard */ + if (read_u32be(fsb5_offset, sf) == 0x46534235) { /* FSB5 */ + return read_s32le(fsb5_offset + 0x08,sf); + } + + /* encrypted, meh */ + temp_sf = setup_subfile_streamfile(sf, fsb5_offset, fsb5_size, "fsb"); + if (!temp_sf) goto end; + + vgmstream = init_vgmstream_fsb_encrypted(temp_sf); + if (!vgmstream) goto end; + + subsongs = vgmstream->num_streams; + +end: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return subsongs; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index b56530d13..16640d55f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -1,157 +1,160 @@ -#ifndef _FSB_KEYS_H_ -#define _FSB_KEYS_H_ - -typedef struct { - int is_fsb5; /* FSB5 or FSB4/3*/ - int is_alt; /* alt XOR mode (seemingly not tied to FSB version or anything) */ - size_t fsbkey_size; - const uint8_t *fsbkey; -} fsbkey_info; - -/** - * List of known keys, found in aluigi's site (http://aluigi.altervista.org), forums, guessfsb.exe or manually. - */ - -/* DJ Hero 2 (X360) */ //"nos71RiT" -static const uint8_t key_dj2[] = { 0x6E,0x6F,0x73,0x37,0x31,0x52,0x69,0x54 }; - -/* Double Fine Productions: Brutal Legend, Massive Chalice, etc (multi) */ //"DFm3t4lFTW" -static const uint8_t key_dfp[] = { 0x44,0x46,0x6D,0x33,0x74,0x34,0x6C,0x46,0x54,0x57 }; - -/* N++ (PC?) */ //"H$#FJa%7gRZZOlxLiN50&g5Q" -static const uint8_t key_npp[] = { 0x48,0x24,0x23,0x46,0x4A,0x61,0x25,0x37,0x67,0x52,0x5A,0x5A,0x4F,0x6C,0x78,0x4C,0x69,0x4E,0x35,0x30,0x26,0x67,0x35,0x51 }; - -/* Slightly Mad Studios: Project CARS (PC?), World of Speed (PC) */ //"sTOoeJXI2LjK8jBMOk8h5IDRNZl3jq3I" -static const uint8_t key_sms[] = { 0x73,0x54,0x4F,0x6F,0x65,0x4A,0x58,0x49,0x32,0x4C,0x6A,0x4B,0x38,0x6A,0x42,0x4D,0x4F,0x6B,0x38,0x68,0x35,0x49,0x44,0x52,0x4E,0x5A,0x6C,0x33,0x6A,0x71,0x33,0x49 }; - -/* Ghost in the Shell: First Assault (PC) */ //"%lAn2{Pi*Lhw3T}@7*!kV=?qS$@iNlJ" -static const uint8_t key_gfs[] = { 0x25,0x6C,0x41,0x6E,0x32,0x7B,0x50,0x69,0x2A,0x4C,0x68,0x77,0x33,0x54,0x7D,0x40,0x37,0x2A,0x21,0x6B,0x56,0x3D,0x3F,0x71,0x53,0x24,0x40,0x69,0x4E,0x6C,0x4A }; - -/* RevHeadz Engine Sounds (Mobile) */ //"1^7%82#&5$~/8sz" -static const uint8_t key_rev[] = { 0x31,0x5E,0x37,0x25,0x38,0x32,0x23,0x26,0x35,0x24,0x7E,0x2F,0x38,0x73,0x7A }; - -/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" -static const uint8_t key_ds3[] = { 0x46,0x44,0x50,0x72,0x56,0x75,0x54,0x34,0x66,0x41,0x46,0x76,0x64,0x48,0x4A,0x59,0x41,0x67,0x79,0x4D,0x7A,0x52,0x46,0x34,0x45,0x63,0x42,0x41,0x6E,0x4B,0x67 }; - -/* Mortal Kombat X */ -static const uint8_t key_mkx[] = { 0x99,0x61,0x64,0xB5,0xFC,0x0F,0x40,0x29,0x83,0xF6,0x1F,0x22,0x0B,0xB5,0x1D,0xC6 }; - -/* Xian Xia Chuan (PC) */ //"gat@tcqs2010" -static const uint8_t key_xxc[] = { 0x67,0x61,0x74,0x40,0x74,0x63,0x71,0x73,0x32,0x30,0x31,0x30 }; - -/* Mirror War Reincarnation of Holiness (PC) */ //"logicsounddesignmwsdev" -static const uint8_t key_mwr[] = { 0x6C,0x6F,0x67,0x69,0x63,0x73,0x6F,0x75,0x6E,0x64,0x64,0x65,0x73,0x69,0x67,0x6E,0x6D,0x77,0x73,0x64,0x65,0x76 }; - -/* Need for Speed Shift 2 Unleashed (PC demo?) */ //"p&oACY^c4LK5C2v^x5nIO6kg5vNH$tlj" -static const uint8_t key_n2u[] = { 0x70,0x26,0x6F,0x41,0x43,0x59,0x5E,0x63,0x34,0x4C,0x4B,0x35,0x43,0x32,0x76,0x5E,0x78,0x35,0x6E,0x49,0x4F,0x36,0x6B,0x67,0x35,0x76,0x4E,0x48,0x24,0x74,0x6C,0x6A }; - -/* Critter Crunch, Superbrothers: Sword & Sworcery */ //"j1$Mk0Libg3#apEr42mo" -static const uint8_t key_ccr[] = { 0x6A,0x31,0x24,0x4D,0x6B,0x30,0x4C,0x69,0x62,0x67,0x33,0x23,0x61,0x70,0x45,0x72,0x34,0x32,0x6D,0x6F }; - -/* Cyphers */ //"@kdj43nKDN^k*kj3ndf02hd95nsl(NJG" -static const uint8_t key_cyp[] = { 0x40,0x6B,0x64,0x6A,0x34,0x33,0x6E,0x4B,0x44,0x4E,0x5E,0x6B,0x2A,0x6B,0x6A,0x33,0x6E,0x64,0x66,0x30,0x32,0x68,0x64,0x39,0x35,0x6E,0x73,0x6C,0x28,0x4E,0x4A,0x47 }; - -/* Xuan Dou Zhi Wang / King of Combat */ //"Xiayuwu69252.Sonicli81223#$*@*0" -static const uint8_t key_xdz[] = { 0x58,0x69,0x61,0x79,0x75,0x77,0x75,0x36,0x39,0x32,0x35,0x32,0x2E,0x53,0x6F,0x6E,0x69,0x63,0x6C,0x69,0x38,0x31,0x32,0x32,0x33,0x23,0x24,0x2A,0x40,0x2A,0x30 }; - -/* Ji Feng Zhi Ren / Kritika Online */ //"kri_tika_5050_" -static const uint8_t key_jzz[] = { 0x6B,0x72,0x69,0x5F,0x74,0x69,0x6B,0x61,0x5F,0x35,0x30,0x35,0x30,0x5F }; - -/* Invisible Inc. */ //"mint78run52" -static const uint8_t key_inv[] = { 0x6D,0x69,0x6E,0x74,0x37,0x38,0x72,0x75,0x6E,0x35,0x32 }; - -/* Guitar Hero 3 */ //"5atu6w4zaw" -static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,0x77 }; - -/* Supreme Commander 2 */ //"B2A7BB00" -static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 }; - -/* Cookie Run: Ovenbreak */ //"ghfxhslrghfxhslr" -static const uint8_t key_cro[] = { 0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72,0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72 }; - -/* Monster Jam (PS2) */ //"truck/impact/carbody" -static const uint8_t key_mtj[] = { 0x74,0x72,0x75,0x63,0x6B,0x2F,0x69,0x6D,0x70,0x61,0x63,0x74,0x2F,0x63,0x61,0x72,0x62,0x6F,0x64,0x79 }; - -/* Guitar Hero 5 (X360) */ -static const uint8_t key_gh5[] = { 0xFC,0xF9,0xE4,0xB3,0xF5,0x57,0x5C,0xA5,0xAC,0x13,0xEC,0x4A,0x43,0x19,0x58,0xEB,0x4E,0xF3,0x84,0x0B,0x8B,0x78,0xFA,0xFD,0xBB,0x18,0x46,0x7E,0x31,0xFB,0xD0 }; - -/* Sekiro: Shadows Die Twice (PC) */ //"G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC" -static const uint8_t key_sek[] = { 0x47,0x30,0x4B,0x54,0x72,0x57,0x6A,0x53,0x39,0x73,0x79,0x71,0x46,0x37,0x76,0x56,0x44,0x36,0x52,0x61,0x56,0x58,0x6C,0x46,0x44,0x39,0x31,0x67,0x4D,0x67,0x6B,0x43 }; - -/* SCP: Unity (PC) */ //"BasicEncryptionKey" -static const uint8_t key_scp[] = { 0x42,0x61,0x73,0x69,0x63,0x45,0x6E,0x63,0x72,0x79,0x70,0x74,0x69,0x6F,0x6E,0x4B,0x65,0x79 }; - -/* Guitar Hero: Metallica (X360) */ -static const uint8_t key_ghm[] = { 0x8C,0xFA,0xF3,0x14,0xB1,0x53,0xDA,0xAB,0x2B,0x82,0x6B,0xD5,0x55,0x16,0xCF,0x01,0x90,0x20,0x28,0x14,0xB1,0x53,0xD8 }; - -// Unknown: -// - Battle: Los Angeles -// - Guitar Hero: Warriors of Rock, DJ hero FSB -// - Longmenkezhan -// - Gas Guzzlers: Combat Carnage (PC?) "C5FA83EA64B34EC2BFE" hex or text? [FSB5] - -static const fsbkey_info fsbkey_list[] = { - { 0,0, sizeof(key_dj2),key_dj2 }, - { 0,0, sizeof(key_dfp),key_dfp },//FSB4 - { 1,0, sizeof(key_dfp),key_dfp },//untested - { 1,1, sizeof(key_dfp),key_dfp },//untested - { 1,0, sizeof(key_npp),key_npp },//FSB5 - { 1,0, sizeof(key_sms),key_sms },//FSB5 - { 1,0, sizeof(key_gfs),key_gfs },//FSB5 - { 1,0, sizeof(key_rev),key_rev },//FSB5 - { 1,0, sizeof(key_ds3),key_ds3 },//untested - { 1,1, sizeof(key_ds3),key_ds3 }, - { 1,0, sizeof(key_mkx),key_mkx },//untested - { 1,1, sizeof(key_mkx),key_mkx },//untested - { 0,0, sizeof(key_xxc),key_xxc },//untested - { 0,1, sizeof(key_xxc),key_xxc },//untested - { 1,0, sizeof(key_xxc),key_xxc },//untested - { 1,1, sizeof(key_xxc),key_xxc },//untested - { 0,0, sizeof(key_mwr),key_mwr },//untested - { 0,1, sizeof(key_mwr),key_mwr },//untested - { 1,0, sizeof(key_mwr),key_mwr },//untested - { 1,1, sizeof(key_mwr),key_mwr },//untested - { 0,0, sizeof(key_n2u),key_n2u },//untested - { 0,1, sizeof(key_n2u),key_n2u },//untested - { 1,0, sizeof(key_n2u),key_n2u },//untested - { 1,1, sizeof(key_n2u),key_n2u },//untested - { 0,0, sizeof(key_ccr),key_ccr },//untested - { 0,1, sizeof(key_ccr),key_ccr },//untested - { 1,0, sizeof(key_ccr),key_ccr },//untested - { 1,1, sizeof(key_ccr),key_ccr },//untested - { 0,0, sizeof(key_cyp),key_cyp },//untested - { 0,1, sizeof(key_cyp),key_cyp },//untested - { 1,0, sizeof(key_cyp),key_cyp },//untested - { 1,1, sizeof(key_cyp),key_cyp },//untested - { 0,0, sizeof(key_xdz),key_xdz },//untested - { 0,1, sizeof(key_xdz),key_xdz },//untested - { 1,0, sizeof(key_xdz),key_xdz },//untested - { 1,1, sizeof(key_xdz),key_xdz },//untested - { 0,0, sizeof(key_jzz),key_jzz },//untested - { 0,1, sizeof(key_jzz),key_jzz },//untested - { 1,0, sizeof(key_jzz),key_jzz },//untested - { 1,1, sizeof(key_jzz),key_jzz },//untested - { 0,0, sizeof(key_inv),key_inv },//untested - { 0,1, sizeof(key_inv),key_inv },//untested - { 1,0, sizeof(key_inv),key_inv },//untested - { 1,1, sizeof(key_inv),key_inv },//untested - { 0,0, sizeof(key_gh3),key_gh3 },//untested - { 0,1, sizeof(key_gh3),key_gh3 },//untested - { 1,0, sizeof(key_gh3),key_gh3 },//untested - { 1,1, sizeof(key_gh3),key_gh3 },//untested - { 0,0, sizeof(key_sc2),key_sc2 },//untested - { 0,1, sizeof(key_sc2),key_sc2 },//untested - { 1,0, sizeof(key_sc2),key_sc2 },//untested - { 1,1, sizeof(key_sc2),key_sc2 },//untested - { 1,0, sizeof(key_cro),key_cro }, - { 0,1, sizeof(key_mtj),key_mtj },// FSB3 - { 0,1, sizeof(key_gh5),key_gh5 },// FSB4 - { 1,0, sizeof(key_sek),key_sek },// FSB5 - { 1,0, sizeof(key_scp),key_scp },// FSB5 - { 0,1, sizeof(key_ghm),key_ghm },// FSB4 - -}; -static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); - - -#endif /* _FSB_KEYS_H_ */ +#ifndef _FSB_KEYS_H_ +#define _FSB_KEYS_H_ + +/** + * List of known keys, found in aluigi's site (http://aluigi.altervista.org), forums, guessfsb.exe or manually. + */ + +/* DJ Hero 2 (X360) */ //"nos71RiT" +static const uint8_t key_dj2[] = { 0x6E,0x6F,0x73,0x37,0x31,0x52,0x69,0x54 }; + +/* Double Fine Productions: Brutal Legend, Massive Chalice, etc (multi) */ //"DFm3t4lFTW" +static const uint8_t key_dfp[] = { 0x44,0x46,0x6D,0x33,0x74,0x34,0x6C,0x46,0x54,0x57 }; + +/* N++ (PC?) */ //"H$#FJa%7gRZZOlxLiN50&g5Q" +static const uint8_t key_npp[] = { 0x48,0x24,0x23,0x46,0x4A,0x61,0x25,0x37,0x67,0x52,0x5A,0x5A,0x4F,0x6C,0x78,0x4C,0x69,0x4E,0x35,0x30,0x26,0x67,0x35,0x51 }; + +/* Slightly Mad Studios: Project CARS (PC?), World of Speed (PC) */ //"sTOoeJXI2LjK8jBMOk8h5IDRNZl3jq3I" +static const uint8_t key_sms[] = { 0x73,0x54,0x4F,0x6F,0x65,0x4A,0x58,0x49,0x32,0x4C,0x6A,0x4B,0x38,0x6A,0x42,0x4D,0x4F,0x6B,0x38,0x68,0x35,0x49,0x44,0x52,0x4E,0x5A,0x6C,0x33,0x6A,0x71,0x33,0x49 }; + +/* Ghost in the Shell: First Assault (PC) */ //"%lAn2{Pi*Lhw3T}@7*!kV=?qS$@iNlJ" +static const uint8_t key_gfs[] = { 0x25,0x6C,0x41,0x6E,0x32,0x7B,0x50,0x69,0x2A,0x4C,0x68,0x77,0x33,0x54,0x7D,0x40,0x37,0x2A,0x21,0x6B,0x56,0x3D,0x3F,0x71,0x53,0x24,0x40,0x69,0x4E,0x6C,0x4A }; + +/* RevHeadz Engine Sounds (Mobile) */ //"1^7%82#&5$~/8sz" +static const uint8_t key_rev[] = { 0x31,0x5E,0x37,0x25,0x38,0x32,0x23,0x26,0x35,0x24,0x7E,0x2F,0x38,0x73,0x7A }; + +/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" +static const uint8_t key_ds3[] = { 0x46,0x44,0x50,0x72,0x56,0x75,0x54,0x34,0x66,0x41,0x46,0x76,0x64,0x48,0x4A,0x59,0x41,0x67,0x79,0x4D,0x7A,0x52,0x46,0x34,0x45,0x63,0x42,0x41,0x6E,0x4B,0x67 }; + +/* Mortal Kombat X */ +static const uint8_t key_mkx[] = { 0x99,0x61,0x64,0xB5,0xFC,0x0F,0x40,0x29,0x83,0xF6,0x1F,0x22,0x0B,0xB5,0x1D,0xC6 }; + +/* Xian Xia Chuan (PC) */ //"gat@tcqs2010" +static const uint8_t key_xxc[] = { 0x67,0x61,0x74,0x40,0x74,0x63,0x71,0x73,0x32,0x30,0x31,0x30 }; + +/* Mirror War Reincarnation of Holiness (PC) */ //"logicsounddesignmwsdev" +static const uint8_t key_mwr[] = { 0x6C,0x6F,0x67,0x69,0x63,0x73,0x6F,0x75,0x6E,0x64,0x64,0x65,0x73,0x69,0x67,0x6E,0x6D,0x77,0x73,0x64,0x65,0x76 }; + +/* Need for Speed Shift 2 Unleashed (PC demo?) */ //"p&oACY^c4LK5C2v^x5nIO6kg5vNH$tlj" +static const uint8_t key_n2u[] = { 0x70,0x26,0x6F,0x41,0x43,0x59,0x5E,0x63,0x34,0x4C,0x4B,0x35,0x43,0x32,0x76,0x5E,0x78,0x35,0x6E,0x49,0x4F,0x36,0x6B,0x67,0x35,0x76,0x4E,0x48,0x24,0x74,0x6C,0x6A }; + +/* Critter Crunch, Superbrothers: Sword & Sworcery */ //"j1$Mk0Libg3#apEr42mo" +static const uint8_t key_ccr[] = { 0x6A,0x31,0x24,0x4D,0x6B,0x30,0x4C,0x69,0x62,0x67,0x33,0x23,0x61,0x70,0x45,0x72,0x34,0x32,0x6D,0x6F }; + +/* Cyphers */ //"@kdj43nKDN^k*kj3ndf02hd95nsl(NJG" +static const uint8_t key_cyp[] = { 0x40,0x6B,0x64,0x6A,0x34,0x33,0x6E,0x4B,0x44,0x4E,0x5E,0x6B,0x2A,0x6B,0x6A,0x33,0x6E,0x64,0x66,0x30,0x32,0x68,0x64,0x39,0x35,0x6E,0x73,0x6C,0x28,0x4E,0x4A,0x47 }; + +/* Xuan Dou Zhi Wang / King of Combat */ //"Xiayuwu69252.Sonicli81223#$*@*0" +static const uint8_t key_xdz[] = { 0x58,0x69,0x61,0x79,0x75,0x77,0x75,0x36,0x39,0x32,0x35,0x32,0x2E,0x53,0x6F,0x6E,0x69,0x63,0x6C,0x69,0x38,0x31,0x32,0x32,0x33,0x23,0x24,0x2A,0x40,0x2A,0x30 }; + +/* Ji Feng Zhi Ren / Kritika Online */ //"kri_tika_5050_" +static const uint8_t key_jzz[] = { 0x6B,0x72,0x69,0x5F,0x74,0x69,0x6B,0x61,0x5F,0x35,0x30,0x35,0x30,0x5F }; + +/* Invisible Inc. */ //"mint78run52" +static const uint8_t key_inv[] = { 0x6D,0x69,0x6E,0x74,0x37,0x38,0x72,0x75,0x6E,0x35,0x32 }; + +/* Guitar Hero 3 */ //"5atu6w4zaw" +static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,0x77 }; + +/* Supreme Commander 2 */ //"B2A7BB00" +static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 }; + +/* Cookie Run: Ovenbreak */ //"ghfxhslrghfxhslr" +static const uint8_t key_cro[] = { 0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72,0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72 }; + +/* Monster Jam (PS2) */ //"truck/impact/carbody" +static const uint8_t key_mtj[] = { 0x74,0x72,0x75,0x63,0x6B,0x2F,0x69,0x6D,0x70,0x61,0x63,0x74,0x2F,0x63,0x61,0x72,0x62,0x6F,0x64,0x79 }; + +/* Guitar Hero 5 (X360) */ +static const uint8_t key_gh5[] = { 0xFC,0xF9,0xE4,0xB3,0xF5,0x57,0x5C,0xA5,0xAC,0x13,0xEC,0x4A,0x43,0x19,0x58,0xEB,0x4E,0xF3,0x84,0x0B,0x8B,0x78,0xFA,0xFD,0xBB,0x18,0x46,0x7E,0x31,0xFB,0xD0 }; + +/* Sekiro: Shadows Die Twice (PC) */ //"G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC" +static const uint8_t key_sek[] = { 0x47,0x30,0x4B,0x54,0x72,0x57,0x6A,0x53,0x39,0x73,0x79,0x71,0x46,0x37,0x76,0x56,0x44,0x36,0x52,0x61,0x56,0x58,0x6C,0x46,0x44,0x39,0x31,0x67,0x4D,0x67,0x6B,0x43 }; + +/* SCP: Unity (PC) */ //"BasicEncryptionKey" +static const uint8_t key_scp[] = { 0x42,0x61,0x73,0x69,0x63,0x45,0x6E,0x63,0x72,0x79,0x70,0x74,0x69,0x6F,0x6E,0x4B,0x65,0x79 }; + +/* Guitar Hero: Metallica (X360) */ +static const uint8_t key_ghm[] = { 0x8C,0xFA,0xF3,0x14,0xB1,0x53,0xDA,0xAB,0x2B,0x82,0x6B,0xD5,0x55,0x16,0xCF,0x01,0x90,0x20,0x28,0x14,0xB1,0x53,0xD8 }; + +/* Worms Rumble Beta (PC) */ //"FXnTffGJ9LS855Gc" +static const uint8_t key_wrb[] = { 0x46,0x58,0x6E,0x54,0x66,0x66,0x47,0x4A,0x39,0x4C,0x53,0x38,0x35,0x35,0x47,0x63 }; + +// Unknown: +// - Battle: Los Angeles +// - Guitar Hero: Warriors of Rock, DJ hero FSB +// - Longmenkezhan +// - Gas Guzzlers: Combat Carnage (PC?) "C5FA83EA64B34EC2BFE" hex or text? [FSB5] + +typedef struct { + int is_fsb5; /* FSB5 or FSB4/3*/ + int is_alt; /* alt XOR mode (seemingly not tied to FSB version or anything) */ + size_t fsbkey_size; + const uint8_t *fsbkey; +} fsbkey_info; + +static const fsbkey_info fsbkey_list[] = { + { 0,0, sizeof(key_dj2),key_dj2 }, + { 0,0, sizeof(key_dfp),key_dfp },//FSB4 + { 1,0, sizeof(key_dfp),key_dfp },//untested + { 1,1, sizeof(key_dfp),key_dfp },//untested + { 1,0, sizeof(key_npp),key_npp },//FSB5 + { 1,0, sizeof(key_sms),key_sms },//FSB5 + { 1,0, sizeof(key_gfs),key_gfs },//FSB5 + { 1,0, sizeof(key_rev),key_rev },//FSB5 + { 1,0, sizeof(key_ds3),key_ds3 },//untested + { 1,1, sizeof(key_ds3),key_ds3 }, + { 1,0, sizeof(key_mkx),key_mkx },//untested + { 1,1, sizeof(key_mkx),key_mkx },//untested + { 0,0, sizeof(key_xxc),key_xxc },//untested + { 0,1, sizeof(key_xxc),key_xxc },//untested + { 1,0, sizeof(key_xxc),key_xxc },//untested + { 1,1, sizeof(key_xxc),key_xxc },//untested + { 0,0, sizeof(key_mwr),key_mwr },//untested + { 0,1, sizeof(key_mwr),key_mwr },//untested + { 1,0, sizeof(key_mwr),key_mwr },//untested + { 1,1, sizeof(key_mwr),key_mwr },//untested + { 0,0, sizeof(key_n2u),key_n2u },//untested + { 0,1, sizeof(key_n2u),key_n2u },//untested + { 1,0, sizeof(key_n2u),key_n2u },//untested + { 1,1, sizeof(key_n2u),key_n2u },//untested + { 0,0, sizeof(key_ccr),key_ccr },//untested + { 0,1, sizeof(key_ccr),key_ccr },//untested + { 1,0, sizeof(key_ccr),key_ccr },//untested + { 1,1, sizeof(key_ccr),key_ccr },//untested + { 0,0, sizeof(key_cyp),key_cyp },//untested + { 0,1, sizeof(key_cyp),key_cyp },//untested + { 1,0, sizeof(key_cyp),key_cyp },//untested + { 1,1, sizeof(key_cyp),key_cyp },//untested + { 0,0, sizeof(key_xdz),key_xdz },//untested + { 0,1, sizeof(key_xdz),key_xdz },//untested + { 1,0, sizeof(key_xdz),key_xdz },//untested + { 1,1, sizeof(key_xdz),key_xdz },//untested + { 0,0, sizeof(key_jzz),key_jzz },//untested + { 0,1, sizeof(key_jzz),key_jzz },//untested + { 1,0, sizeof(key_jzz),key_jzz },//untested + { 1,1, sizeof(key_jzz),key_jzz },//untested + { 0,0, sizeof(key_inv),key_inv },//untested + { 0,1, sizeof(key_inv),key_inv },//untested + { 1,0, sizeof(key_inv),key_inv },//untested + { 1,1, sizeof(key_inv),key_inv },//untested + { 0,0, sizeof(key_gh3),key_gh3 },//untested + { 0,1, sizeof(key_gh3),key_gh3 },//untested + { 1,0, sizeof(key_gh3),key_gh3 },//untested + { 1,1, sizeof(key_gh3),key_gh3 },//untested + { 0,0, sizeof(key_sc2),key_sc2 },//untested + { 0,1, sizeof(key_sc2),key_sc2 },//untested + { 1,0, sizeof(key_sc2),key_sc2 },//untested + { 1,1, sizeof(key_sc2),key_sc2 },//untested + { 1,0, sizeof(key_cro),key_cro }, + { 0,1, sizeof(key_mtj),key_mtj },// FSB3 + { 0,1, sizeof(key_gh5),key_gh5 },// FSB4 + { 1,0, sizeof(key_sek),key_sek },// FSB5 + { 1,0, sizeof(key_scp),key_scp },// FSB5 + { 0,1, sizeof(key_ghm),key_ghm },// FSB4 + { 1,0, sizeof(key_wrb),key_wrb },// FSB5 +}; +static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); + + +#endif /* _FSB_KEYS_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 3bfbd9d82..b68724328 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -181,9 +181,10 @@ done: static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey) { STREAMFILE* sf_keys = NULL; uint8_t* buf = NULL; - int best_score = -1; + int best_score = 0xFFFFFF, cur_score; off_t keys_size, bytes; int pos; + uint64_t old_key = 0; VGM_LOG("HCA: test keys\n"); @@ -202,9 +203,12 @@ static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigne bytes = read_streamfile(buf, 0, keys_size, sf_keys); if (bytes != keys_size) goto done; + VGM_LOG("HCA: start\n"); + pos = 0; while (pos < keys_size - 4) { uint64_t key; + VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos); /* keys are usually u32le lower, u32le upper (u64le) but other orders may exist */ key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32le(buf + pos + 0x04) << 32); @@ -213,25 +217,32 @@ static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigne //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 32) | ((uint64_t)get_u32be(buf + pos + 0x04) << 0); //key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */ //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */ - if (key == 0) - continue; - test_key(hca_data, key, subkey, &best_score, out_keycode); - if (best_score == 1) + /* observed files have aligned keys, change if needed */ + pos += 0x04; //pos++; + + if (key == 0 || key == old_key) + continue; + old_key = key; + + cur_score = 0; + test_key(hca_data, key, subkey, &cur_score, out_keycode); + if (cur_score == 1) goto done; - VGM_ASSERT(pos % 0x100000 == 0, "HCA: pos %x...\n", pos); - - /* observed files have aligned keys in the .text section, change if needed */ - pos += 0x04; - //pos++; + if (cur_score > 0 && cur_score <= 500) { + VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n", + (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04); + if (best_score > cur_score) + best_score = cur_score; + } } done: VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n", (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score); VGM_ASSERT(best_score < 0, "HCA: key not found\n"); - + close_streamfile(sf_keys); free(buf); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index a6bb11c38..a8e7899ac 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -373,6 +373,9 @@ static const hcakey_info hcakey_list[] = { /* Re:Zero - Lost in Memories (Android) */ {1611432018519751642}, // 165CF4E2138F7BDA + /* D4DJ Groovy Mix (Android) [base files] */ + {393410674916959300}, // 0575ACECA945A444 + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/imuse.c b/Frameworks/vgmstream/vgmstream/src/meta/imuse.c index 5b6222cc0..a8706ebec 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/imuse.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/imuse.c @@ -108,23 +108,19 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) { if (!is_id4("DATA", head_offset + 0x10 + map_size + 0x00, sf)) goto fail; data_bytes = read_u32be(head_offset + 0x10 + map_size + 0x04, sf); - num_samples = data_bytes / channels / sizeof(int16_t); - //num_samples = (read_u32be(head_offset + 0x04,sf) - head_size) / channels / sizeof(int16_t); /* equivalent */ } else if (is_id4("RIFF", head_offset, sf)) { /* MCMP voices */ - /* standard (LE), with fake codec 1 and sizes also in decoded bytes (see above) */ + /* standard (LE), with fake codec 1 and sizes also in decoded bytes (see above), + * has standard RIFF chunks (may include extra), start offset in MCSC */ - if (!is_id4("fmt ", head_offset + 0x0c, sf)) + if (!find_chunk_le(sf, 0x666D7420, head_offset + 0x0c, 0, &offset, NULL)) /* "fmt " */ goto fail; - offset = head_offset + 0x14; channels = read_u16le(offset + 0x02,sf); sample_rate = read_u32le(offset + 0x04,sf); - if (!is_id4("data", head_offset + 0x24, sf)) + if (!find_chunk_le(sf, 0x64617461, head_offset + 0x0c, 0, NULL, &data_bytes)) /*"data"*/ goto fail; - data_bytes = read_u32le(head_offset + 0x28, sf); - num_samples = data_bytes / channels / sizeof(int16_t); } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kwb.c b/Frameworks/vgmstream/vgmstream/src/meta/kwb.c index 6a9c14387..d40aac1d7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/kwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/kwb.c @@ -1,9 +1,10 @@ #include "meta.h" #include "../coding/coding.h" -typedef enum { PCM16, MSADPCM, DSP, AT9 } kwb_codec; +typedef enum { PCM16, MSADPCM, DSP_HEAD, DSP_BODY, AT9, MSF } kwb_codec; typedef struct { + int big_endian; int total_subsongs; int target_subsong; kwb_codec codec; @@ -24,13 +25,15 @@ typedef struct { } kwb_header; static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b); +static int parse_xws(kwb_header* kwb, STREAMFILE* sf); /* KWB - WaveBank from Koei games */ -VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; STREAMFILE *sf_h = NULL, *sf_b = NULL; kwb_header kwb = {0}; + int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; int target_subsong = sf->stream_index; @@ -67,6 +70,7 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf) { if (!parse_kwb(&kwb, sf_h, sf_b)) goto fail; + read_s32 = kwb.big_endian ? read_s32be : read_s32le; /* build the VGMSTREAM */ @@ -92,13 +96,24 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf) { vgmstream->frame_size = kwb.block_size; break; - case DSP: + case DSP_HEAD: + case DSP_BODY: if (kwb.channels > 1) goto fail; vgmstream->coding_type = coding_NGC_DSP; /* subinterleave? */ vgmstream->layout_type = layout_interleave; - vgmstream->layout_type = 0x08; - dsp_read_coefs_le(vgmstream, sf_h, kwb.dsp_offset + 0x1c, 0x60); - dsp_read_hist_le (vgmstream, sf_h, kwb.dsp_offset + 0x40, 0x60); + vgmstream->interleave_block_size = 0x08; + if (kwb.codec == DSP_HEAD) { + dsp_read_coefs(vgmstream, sf_h, kwb.dsp_offset + 0x1c, 0x60, kwb.big_endian); + dsp_read_hist (vgmstream, sf_h, kwb.dsp_offset + 0x40, 0x60, kwb.big_endian); + } + else { + /* typical DSP header + data */ + vgmstream->num_samples = read_s32(kwb.stream_offset + 0x00, sf_b); + dsp_read_coefs(vgmstream, sf_b, kwb.stream_offset + 0x1c, 0x60, kwb.big_endian); + dsp_read_hist (vgmstream, sf_b, kwb.stream_offset + 0x40, 0x60, kwb.big_endian); + kwb.stream_offset += 0x60; + } + break; #ifdef VGM_USE_ATRAC9 @@ -130,8 +145,6 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf) { break; } #endif - - default: goto fail; } @@ -148,6 +161,54 @@ fail: return NULL; } +/* XWS - WaveStream? from Koei games */ +VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + kwb_header kwb = {0}; + int target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf, "xws")) + goto fail; + + if (target_subsong == 0) target_subsong = 1; + kwb.target_subsong = target_subsong; + + if (!parse_xws(&kwb, sf)) + goto fail; + + if (kwb.codec == MSF) { + if (kwb.stream_offset == 0) { + vgmstream = init_vgmstream_silence(0,0,0); /* dummy, whatevs */ + if (!vgmstream) goto fail; + } + else { + kwb.stream_size = read_u32be(kwb.stream_offset + 0x0c, sf) + 0x40; + + temp_sf = setup_subfile_streamfile(sf, kwb.stream_offset, kwb.stream_size, "msf"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_msf(temp_sf); + if (!vgmstream) goto fail; + } + + vgmstream->num_streams = kwb.total_subsongs; + } + else { + goto fail; + } + + close_streamfile(temp_sf); + return vgmstream; +fail: + close_streamfile(temp_sf); + return NULL; +} + + + static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { int i, j, sounds; @@ -220,7 +281,7 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { kwb->codec = MSADPCM; break; case 0x90: - kwb->codec = DSP; + kwb->codec = DSP_HEAD; kwb->dsp_offset = subsound_offset + 0x4c; break; default: @@ -257,8 +318,8 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { /* a format mimicking PSVita's hd4+bd4 format */ - /* 00 K4HD id */ - /* 04 chunk size */ + /* 00: K4HD id */ + /* 04: chunk size */ /* 08: ? */ /* 0c: ? */ /* 10: PPPG offset ('program'? cues?) */ @@ -271,7 +332,7 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { if (read_u32be(ppva_offset + 0x00, sf_h) != 0x50505641) /* "PPVA" */ goto fail; - entry_size = read_u32le(ppva_offset + 0x08, sf_h); /* */ + entry_size = read_u32le(ppva_offset + 0x08, sf_h); /* 0x0c: -1? */ /* 0x10: 0? */ entries = read_u32le(ppva_offset + 0x14, sf_h) + 1; @@ -314,16 +375,78 @@ static int parse_type_sdsd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { return 0; } +static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { + off_t smpl_offset, header_offset; + int entries; + size_t entry_size; + + + /* variation of SDsd */ + /* 00: SDWiVers */ + /* 08: chunk size */ + /* 0c: null */ + /* 10: SDsdHead */ + /* 18: chunk size */ + /* 1c: WBH_ size */ + /* 20: WBD_ size */ + /* 24: SDsdProg offset ('program'? cues?) */ + /* 28: SDsdSmpl offset ('samples'? waves?) */ + /* rest: ? */ + smpl_offset = read_u32be(offset + 0x28, sf_h); + smpl_offset += offset; + + /* Smpl table: */ + if (read_u32be(smpl_offset + 0x00, sf_h) != 0x53447364 && /* "SDsd" */ + read_u32be(smpl_offset + 0x04, sf_h) != 0x536D706C) /* "Smpl" */ + goto fail; + + /* 0x08: ? */ + entries = read_u32le(smpl_offset + 0x0c, sf_h); /* LE! */ + entry_size = 0x40; + + kwb->total_subsongs = entries; + if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; + + header_offset = smpl_offset + 0x10 + (kwb->target_subsong-1) * entry_size; + + /* 00: "SS" + ID (0..N) */ + kwb->stream_offset = read_u32be(header_offset + 0x04, sf_h); + /* 08: flag? */ + /* 0c: ? + channels? */ + kwb->sample_rate = read_u32be(header_offset + 0x10, sf_h); + /* 14: bitrate */ + /* 18: codec? + bps */ + /* 1c: null? */ + /* 20: null? */ + kwb->stream_size = read_u32be(header_offset + 0x24, sf_h); + /* 28: full stream size (with padding) */ + /* 2c: related to samples? */ + /* 30: ID */ + /* 34-38: null */ + + kwb->codec = DSP_BODY; + kwb->channels = 1; + + return 1; +fail: + return 0; +} + static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { off_t head_offset, body_offset, start; uint32_t type; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; if (read_u32be(0x00, sf_h) == 0x57484431) { /* "WHD1" */ - /* container of wbh+wbd */ + /* container of fused .wbh+wbd */ /* 0x04: fixed value? */ - /* 0x08: version? */ - start = read_u32le(0x0c, sf_h); + kwb->big_endian = read_u8(0x08, sf_h) == 0xFF; + /* 0x0a: version? */ + + read_u32 = kwb->big_endian ? read_u32be : read_u32le; + + start = read_u32(0x0c, sf_h); /* 0x10: file size */ /* 0x14: subfiles? */ /* 0x18: subfiles? */ @@ -331,8 +454,8 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { /* 0x20: some size? */ /* 0x24: some size? */ - head_offset = read_u32le(start + 0x00, sf_h); - body_offset = read_u32le(start + 0x04, sf_h); + head_offset = read_u32(start + 0x00, sf_h); + body_offset = read_u32(start + 0x04, sf_h); /* 0x10: head size */ /* 0x14: body size */ } @@ -340,13 +463,17 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { /* dual file */ head_offset = 0x00; body_offset = 0x00; + + kwb->big_endian = guess_endianness32bit(head_offset + 0x08, sf_h); + + read_u32 = kwb->big_endian ? read_u32be : read_u32le; } - if (read_u32be(head_offset + 0x00, sf_h) != 0x5F484257 || /* "_HBW" */ - read_u32be(head_offset + 0x04, sf_h) != 0x30303030) /* "0000" */ + if (read_u32(head_offset + 0x00, sf_h) != 0x5742485F || /* "WBH_" */ + read_u32(head_offset + 0x04, sf_h) != 0x30303030) /* "0000" */ goto fail; - if (read_u32be(body_offset + 0x00, sf_b) != 0x5F444257 || /* "_DBW" */ - read_u32be(body_offset + 0x04, sf_b) != 0x30303030) /* "0000" */ + if (read_u32(body_offset + 0x00, sf_b) != 0x5742445F || /* "WBD_" */ + read_u32(body_offset + 0x04, sf_b) != 0x30303030) /* "0000" */ goto fail; /* 0x08: head/body size */ @@ -356,26 +483,109 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { /* format has multiple bank subtypes that are quite different from each other */ type = read_u32be(head_offset + 0x00, sf_h); switch(type) { - case 0x4B574232: /* "KWB2" (PC) */ - case 0x4B57424E: /* "KWBN" (Switch) */ + case 0x4B574232: /* "KWB2" [Bladestorm Nightmare (PC), Dissidia NT (PC)] */ + case 0x4B57424E: /* "KWBN" [Fire Emblem Warriors (Switch)] */ if (!parse_type_kwb2(kwb, head_offset, sf_h)) goto fail; break; - case 0x4B344844: /* "K4HD" (PS4/Vita) */ + case 0x4B344844: /* "K4HD" [Dissidia NT (PS4), (Vita) */ if (!parse_type_k4hd(kwb, head_offset, sf_h)) goto fail; break; - case 0x53447364: /* "SDsd" (PS3?) */ + case 0x53447364: /* "SDsd" (PS3? leftover files) */ if (!parse_type_sdsd(kwb, head_offset, sf_h)) goto fail; break; + case 0x53445769: /* "SDWi" [Fatal Frame 5 (WiiU)] */ + if (!parse_type_sdwi(kwb, head_offset, sf_h)) + goto fail; + break; + default: goto fail; } + kwb->stream_offset += body_offset; + + return 1; +fail: + return 0; +} + +static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) { + /* this is just like XWSF, abridged: */ + off_t header_offset; + + kwb->total_subsongs = read_u32be(offset + 0x14, sf); + if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; + + header_offset = offset + 0x30 + (kwb->target_subsong-1) * 0x04; + + /* just a dumb table pointing to MSF, entries can be dummy */ + kwb->stream_offset = read_u32be(header_offset, sf); + kwb->codec = MSF; + + return 1; +fail: + return 0; +} + + +static int parse_xws(kwb_header* kwb, STREAMFILE* sf) { + off_t head_offset, body_offset, start; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + int chunks, chunks2; + off_t msfb_offset; + + /* format is similar to WHD1 with some annoyances of its own + * variations: + * - tdpack: points to N XWSFILE + * - XWSFILE w/ 4 chunks: CUEBANK offset, ? offset, MSFBANK offset, end offset (PS3) + * [Ninja Gaiden Sigma 2 (PS3), Ninja Gaiden 3 Razor's Edge (PS3)] + * - XWSFILE w/ 2*N chunks: KWB2 offset + data offset * N (ex. 3 pairs = 6 chunks) + * [Dead or Alive 5 Last Round (PC)] + * + * for now basic support for the second case, others we'd have to map subsong N to internal bank M + */ + + if (read_u32be(0x00, sf) != 0x58575346 || /* "XWSF" */ + read_u32be(0x04, sf) != 0x494C4500) /* "ILE\0" */ + goto fail; + + kwb->big_endian = read_u8(0x08, sf) == 0xFF; + /* 0x0a: version? */ + + read_u32 = kwb->big_endian ? read_u32be : read_u32le; + + start = read_u32(0x0c, sf); + /* 0x10: file size */ + chunks = read_u32(0x14, sf); + chunks2 = read_u32(0x18, sf); + /* 0x1c: null */ + /* 0x20: some size? */ + /* 0x24: some size? */ + if (chunks != chunks2) + goto fail; + + if (chunks != 4) + goto fail; + + msfb_offset = read_u32(start + 0x08, sf); + if (read_u32be(msfb_offset, sf) == 0x4D534642) { /* "MSFB" + "ANK\0" */ + head_offset = msfb_offset; + body_offset = msfb_offset; /* relative to start */ + + if (!parse_type_msfbank(kwb, head_offset, sf)) + goto fail; + } + else { + goto fail; + } + + kwb->stream_offset += body_offset; return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 42f158762..c19b9c136 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -26,34 +26,36 @@ VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_idsp_namco(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_wii_was(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_adpx(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_itl(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_dsp_stm(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_mpdsp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_dsp_std_int(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_idsp_namco(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_sadf(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_idsp_tt(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_idsp_nl(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_wii_wsd(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_ddsp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_wii_was(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_str_ig(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_xiii(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_cabelas(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_wii_ndp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_dsp_aaap(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_dspw(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_dsp_iadp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_mcadpcm(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_switch_audio(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_itl_ch(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_adpy(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_adpx(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf); VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile); @@ -515,7 +517,8 @@ VGMSTREAM * init_vgmstream_vs_str(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_vawx(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_xwav_new(STREAMFILE* sf); +VGMSTREAM * init_vgmstream_xwav_old(STREAMFILE* sf); VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE* streamFile); @@ -599,11 +602,7 @@ VGMSTREAM * init_vgmstream_mc3(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ta_aac_mobile_vorbis(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf); VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile); @@ -635,18 +634,19 @@ VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_awc(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_std(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_sqex(STREAMFILE* streamFile); +VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_n1(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf); VGMSTREAM * init_vgmstream_raw_al(STREAMFILE * streamFile); @@ -690,6 +690,7 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_sps_n1_segmented(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile); @@ -726,8 +727,6 @@ VGMSTREAM * init_vgmstream_smc_smh(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_opus_sps_n1_segmented(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ubi_bao_atomic(STREAMFILE *streamFile); @@ -889,7 +888,8 @@ VGMSTREAM* init_vgmstream_fda(STREAMFILE *sf); VGMSTREAM * init_vgmstream_tgc(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf); VGMSTREAM * init_vgmstream_lrmd(STREAMFILE* sf); @@ -920,4 +920,12 @@ VGMSTREAM* init_vgmstream_dsb(STREAMFILE* sf); VGMSTREAM* init_vgmstream_bsf(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_xse_new(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_xse_old(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_wady(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msf.c b/Frameworks/vgmstream/vgmstream/src/meta/msf.c index 332dc643e..a8d2ffb4f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/msf.c @@ -2,59 +2,59 @@ #include "../coding/coding.h" /* MSF - Sony's PS3 SDK format (MultiStream File) */ -VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; uint32_t data_size, loop_start = 0, loop_end = 0; uint32_t codec, flags; - int loop_flag, channel_count, sample_rate; + int loop_flag, channels, sample_rate; /* checks */ /* .msf: standard * .msa: Sonic & Sega All-Stars Racing (PS3) - * .at3: Silent Hill HD Collection (PS3) + * .at3: Silent Hill HD Collection (PS3), Z/X Zekkai no Crusade (PS3) * .mp3: Darkstalkers Resurrection (PS3) */ - if (!check_extensions(streamFile,"msf,msa,at3,mp3")) + if (!check_extensions(sf,"msf,msa,at3,mp3")) goto fail; /* check header "MSF" + version-char, usually: - * 0x01, 0x02, 0x30 ("0"), 0x35 ("5"), 0x43 ("C") (last/most common version) */ - if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4D534600) /* "MSF\0" */ + * 0x01, 0x02, 0x30="0", 0x35="5", 0x43="C" (last/most common version) */ + if ((read_u32be(0x00,sf) & 0xffffff00) != 0x4D534600) /* "MSF\0" */ goto fail; start_offset = 0x40; - codec = read_32bitBE(0x04,streamFile); - channel_count = read_32bitBE(0x08,streamFile); - data_size = read_32bitBE(0x0C,streamFile); /* without header */ + codec = read_u32be(0x04,sf); + channels = read_s32be(0x08,sf); + data_size = read_u32be(0x0C,sf); /* without header */ if (data_size == 0xFFFFFFFF) /* unneeded? */ - data_size = get_streamfile_size(streamFile) - start_offset; - sample_rate = read_32bitBE(0x10,streamFile); + data_size = get_streamfile_size(sf) - start_offset; + sample_rate = read_s32be(0x10,sf); /* byte flags, not in MSFv1 or v2 * 0x01/02/04/08: loop marker 0/1/2/3 * 0x10: resample options (force 44/48khz) - * 0x20: VBR MP3 source (changed into simplified 0x1a1 CBR) + * 0x20: VBR MP3 source (encoded with min/max quality options, may end up being CBR) * 0x40: joint stereo MP3 (apparently interleaved stereo for other formats) * 0x80+: (none/reserved) */ - flags = read_32bitBE(0x14,streamFile); + flags = read_u32be(0x14,sf); /* sometimes loop_start/end is set with flag 0x10, but from tests it only loops if 0x01/02 is set * 0x10 often goes with 0x01 but not always (Castlevania HoD); Malicious PS3 uses flag 0x2 instead */ - loop_flag = flags != 0xffffffff && ((flags & 0x01) || (flags & 0x02)); + loop_flag = (flags != 0xffffffff) && ((flags & 0x01) || (flags & 0x02)); /* loop markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */ if (loop_flag) { - loop_start = read_32bitBE(0x18,streamFile); - loop_end = read_32bitBE(0x1C,streamFile); /* loop duration */ + loop_start = read_u32be(0x18,sf); + loop_end = read_u32be(0x1C,sf); /* loop duration */ loop_end = loop_start + loop_end; /* usually equals data_size but not always */ - if (loop_end > data_size)/* not seen */ + if (loop_end > data_size) /* not seen */ loop_end = data_size; } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_MSF; @@ -63,16 +63,16 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { vgmstream->sample_rate = 48000; switch (codec) { - case 0x00: /* PCM (Big Endian) */ + case 0x00: /* PCM (Big Endian) [MSEnc tests] */ case 0x01: { /* PCM (Little Endian) [Smash Cars (PS3)] */ vgmstream->coding_type = codec==0 ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count,16); + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 16); if (loop_flag){ - vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count,16); - vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channel_count,16); + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channels, 16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channels, 16); } break; @@ -87,10 +87,10 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; - vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + vgmstream->num_samples = ps_bytes_to_samples(data_size, channels); if (loop_flag) { - vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start,channel_count); - vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end,channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start,channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end,channels); } break; @@ -111,7 +111,7 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { if (vgmstream->sample_rate == 0xFFFFFFFF) /* some MSFv1 (Digi World SP) */ vgmstream->sample_rate = 44100; /* voice tracks seems to use 44khz, not sure about other tracks */ - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -127,41 +127,37 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { } #endif #if defined(VGM_USE_MPEG) - case 0x07: { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */ + case 0x07: { /* MPEG (LAME MP3) [Dengeki Bunko Fighting Climax (PS3), Asura's Wrath (PS3)-vbr] */ + int is_vbr = (flags & 0x20); /* must calc samples/loop offsets manually */ - vgmstream->codec_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); + vgmstream->codec_data = init_mpeg(sf, start_offset, &vgmstream->coding_type, vgmstream->channels); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data); - if (loop_flag) { - vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, vgmstream->codec_data); - vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, vgmstream->codec_data); - /* loops are always aligned to CBR frame beginnings */ - } + vgmstream->num_samples = mpeg_get_samples_clean(sf, start_offset, data_size, &loop_start, &loop_end, is_vbr); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + /* MPEG here seems stripped from ID3/Xing headers, loops are frame offsets */ /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; } #elif defined(VGM_USE_FFMPEG) - case 0x07: - { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */ + case 0x07: { /* MPEG (LAME MP3) [Dengeki Bunko Fighting Climax (PS3), Asura's Wrath (PS3)-vbr] */ ffmpeg_codec_data *ffmpeg_data = NULL; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, streamFile->get_size(streamFile)); + ffmpeg_data = init_ffmpeg_offset(sf, start_offset, sf->get_size(sf)); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + //todo use same calcs as above vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; if (loop_flag) { vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; vgmstream->loop_end_sample = (int64_t)loop_end * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; - /* loops are always aligned to CBR frame beginnings */ } - - /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; } #endif @@ -171,7 +167,7 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c b/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c index 88b02a1a1..963c67234 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c @@ -194,7 +194,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo char dir_name[NAME_LENGTH]; char subdir_name[NAME_LENGTH]; - int file_count; + int file_count = 0; size_t bytes_read; int line_ok = 0; off_t mus_offset = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index bb785523d..50a8cb081 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -28,15 +28,15 @@ struct dsp_header { }; /* read the above struct; returns nonzero on failure */ -static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE *streamFile, int big_endian) { +static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) { int32_t (*get_32bit)(const uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; int16_t (*get_16bit)(const uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; int i; uint8_t buf[0x4e]; - if (offset > get_streamfile_size(streamFile)) + if (offset > get_streamfile_size(sf)) return 1; - if (read_streamfile(buf, offset, 0x4e, streamFile) != 0x4e) + if (read_streamfile(buf, offset, 0x4e, sf) != 0x4e) return 1; header->sample_count = get_32bit(buf+0x00); header->nibble_count = get_32bit(buf+0x04); @@ -59,10 +59,10 @@ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREA header->block_size = get_16bit(buf+0x4c); return 0; } -static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE *file) { +static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE* file) { return read_dsp_header_endian(header, offset, file, 1); } -static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFILE *file) { +static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFILE* file) { return read_dsp_header_endian(header, offset, file, 0); } @@ -98,8 +98,8 @@ typedef struct { /* Common parser for most DSPs that are basically the same with minor changes. * Custom variants will just concatenate or interleave standard DSP headers and data, * so we make sure to validate read vs expected values, based on dsp_meta config. */ -static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *dspm) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { + VGMSTREAM* vgmstream = NULL; int i, j; int loop_flag; struct dsp_header ch_header[COMMON_DSP_MAX_CHANNELS]; @@ -113,7 +113,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d /* load standard DSP header per channel */ { for (i = 0; i < dspm->channel_count; i++) { - if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, streamFile, !dspm->little_endian)) + if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) goto fail; } } @@ -156,7 +156,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d for (i = 0; i < channels; i++) { off_t channel_offset = dspm->start_offset + i*dspm->interleave; - if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, streamFile)) + if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, sf)) goto fail; } } @@ -174,7 +174,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave); } - if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,streamFile)) + if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) goto fail; } } @@ -235,7 +235,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d } - if (!vgmstream_open_stream(vgmstream,streamFile,dspm->start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,dspm->start_offset)) goto fail; return vgmstream; @@ -247,8 +247,8 @@ fail: /* ********************************* */ /* .dsp - standard dsp as generated by DSPADPCM.exe */ -VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; @@ -258,16 +258,16 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { /* .dsp: standard * .adp: Dr. Muto/Battalion Wars (GC) mono files * (extensionless): Tony Hawk's Downhill Jam (Wii) */ - if (!check_extensions(streamFile, "dsp,adp,")) + if (!check_extensions(sf, "dsp,adp,")) goto fail; - if (read_dsp_header(&header, 0x00, streamFile)) + if (read_dsp_header(&header, 0x00, sf)) goto fail; channel_count = 1; start_offset = header_size; - if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) + if (header.initial_ps != (uint8_t)read_8bit(start_offset,sf)) goto fail; /* check initial predictor/scale */ if (header.format || header.gain) goto fail; /* check type==0 and gain==0 */ @@ -282,7 +282,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { struct dsp_header header2; /* ignore headers one after another */ - ko = read_dsp_header(&header2, header_size, streamFile); + ko = read_dsp_header(&header2, header_size, sf); if (!ko && header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && @@ -293,7 +293,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { /* ignore headers after interleave [Ultimate Board Collection (Wii)] */ - ko = read_dsp_header(&header2, 0x10000, streamFile); + ko = read_dsp_header(&header2, 0x10000, sf); if (!ko && header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && @@ -307,7 +307,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { off_t loop_off; /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; - if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { + if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,sf)) { /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ VGM_LOG("DSP (std): bad loop_predictor\n"); @@ -341,7 +341,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -351,8 +351,8 @@ fail: } /* .dsp - little endian dsp, possibly main Switch .dsp [LEGO Worlds (Switch)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ngc_dsp_std_le(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; @@ -360,16 +360,16 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { /* checks */ /* .adpcm: LEGO Worlds */ - if (!check_extensions(streamFile, "adpcm")) + if (!check_extensions(sf, "adpcm")) goto fail; - if (read_dsp_header_le(&header, 0x00, streamFile)) + if (read_dsp_header_le(&header, 0x00, sf)) goto fail; channel_count = 1; start_offset = header_size; - if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) + if (header.initial_ps != (uint8_t)read_8bit(start_offset,sf)) goto fail; /* check initial predictor/scale */ if (header.format || header.gain) goto fail; /* check type==0 and gain==0 */ @@ -380,7 +380,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { * predictor/scale check if the first byte is 0 */ { struct dsp_header header2; - read_dsp_header_le(&header2, header_size, streamFile); + read_dsp_header_le(&header2, header_size, sf); if (header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && @@ -394,7 +394,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { off_t loop_off; /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; - if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { + if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,sf)) { /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ VGM_LOG("DSP (std): bad loop_predictor\n"); @@ -428,7 +428,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -438,28 +438,28 @@ fail: } /* .dsp - standard multi-channel dsp as generated by DSPADPCM.exe (later revisions) */ -VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; int i, c, channel_count; /* checks */ - if (!check_extensions(streamFile, "dsp,mdsp")) + if (!check_extensions(sf, "dsp,mdsp")) goto fail; - if (read_dsp_header(&header, 0x00, streamFile)) + if (read_dsp_header(&header, 0x00, sf)) goto fail; channel_count = header.channel_count==0 ? 1 : header.channel_count; start_offset = header_size * channel_count; /* named .dsp and no channels? likely another interleaved dsp */ - if (check_extensions(streamFile,"dsp") && header.channel_count == 0) + if (check_extensions(sf,"dsp") && header.channel_count == 0) goto fail; - if (header.initial_ps != (uint8_t)read_8bit(start_offset, streamFile)) + if (header.initial_ps != (uint8_t)read_8bit(start_offset, sf)) goto fail; /* check initial predictor/scale */ if (header.format || header.gain) goto fail; /* check type==0 and gain==0 */ @@ -483,7 +483,7 @@ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { vgmstream->interleave_last_block_size = (header.nibble_count / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; for (i = 0; i < channel_count; i++) { - if (read_dsp_header(&header, header_size * i, streamFile)) goto fail; + if (read_dsp_header(&header, header_size * i, sf)) goto fail; /* adpcm coeffs/history */ for (c = 0; c < 16; c++) @@ -492,7 +492,7 @@ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { vgmstream->ch[i].adpcm_history2_16 = header.initial_hist2; } - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -504,39 +504,39 @@ fail: /* ********************************* */ /* .stm - Intelligent Systems + others (same programmers) full interleaved dsp [Paper Mario TTYD (GC), Fire Emblem: POR (GC), Cubivore (GC)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_dsp_stm(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .lstm/dsp: renamed to avoid hijacking Scream Tracker 2 Modules */ - if (!check_extensions(streamFile, "stm,lstm,dsp")) + if (!check_extensions(sf, "stm,lstm,dsp")) goto fail; - if (read_16bitBE(0x00, streamFile) != 0x0200) + if (read_16bitBE(0x00, sf) != 0x0200) goto fail; /* 0x02(2): sample rate, 0x08+: channel sizes/loop offsets? */ - dspm.channel_count = read_32bitBE(0x04, streamFile); + dspm.channel_count = read_32bitBE(0x04, sf); dspm.max_channels = 2; dspm.fix_looping = 1; dspm.header_offset = 0x40; dspm.header_spacing = 0x60; dspm.start_offset = 0x100; - dspm.interleave = (read_32bitBE(0x08, streamFile) + 0x20) / 0x20 * 0x20; /* strange rounding, but works */ + dspm.interleave = (read_32bitBE(0x08, sf) + 0x20) / 0x20 * 0x20; /* strange rounding, but works */ dspm.meta_type = meta_DSP_STM; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .(mp)dsp - single header + interleaved dsp [Monopoly Party! (GC)] */ -VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_mpdsp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .mpdsp: renamed since standard .dsp would catch it otherwise */ - if (!check_extensions(streamFile, "mpdsp")) + if (!check_extensions(sf, "mpdsp")) goto fail; /* at 0x48 is extra data that could help differenciating these DSPs, but seems like @@ -553,18 +553,18 @@ VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile) { dspm.interleave = 0xf000; dspm.meta_type = meta_DSP_MPDSP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* various dsp with differing extensions and interleave values */ -VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_dsp_std_int(STREAMFILE* sf) { dsp_meta dspm = {0}; char filename[PATH_LIMIT]; /* checks */ - if (!check_extensions(streamFile, "dsp,mss,gcm")) + if (!check_extensions(sf, "dsp,mss,gcm")) goto fail; dspm.channel_count = 2; @@ -575,16 +575,16 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { dspm.header_spacing = 0x60; dspm.start_offset = 0xc0; - streamFile->get_name(streamFile,filename,sizeof(filename)); + sf->get_name(sf,filename,sizeof(filename)); if (strlen(filename) > 7 && !strcasecmp("_lr.dsp",filename+strlen(filename)-7)) { //todo improve dspm.interleave = 0x14180; dspm.meta_type = meta_DSP_JETTERS; /* Bomberman Jetters (GC) */ - } else if (check_extensions(streamFile, "mss")) { + } else if (check_extensions(sf, "mss")) { dspm.interleave = 0x1000; dspm.meta_type = meta_DSP_MSS; /* Free Radical GC games */ /* Timesplitters 2 GC's ts2_atom_smasher_44_fx.mss differs slightly in samples but plays ok */ dspm.ignore_header_agreement = 1; - } else if (check_extensions(streamFile, "gcm")) { + } else if (check_extensions(sf, "gcm")) { /* older Traveller's Tales games [Lego Star Wars (GC), The Chronicles of Narnia (GC), Sonic R (GC)] */ dspm.interleave = 0x8000; dspm.meta_type = meta_DSP_GCM; @@ -592,19 +592,19 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { goto fail; } - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* IDSP - Namco header (from NUB/NUS3) + interleaved dsp [SSB4 (3DS), Tekken Tag Tournament 2 (WiiU)] */ -VGMSTREAM * init_vgmstream_idsp_namco(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_idsp_namco(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "idsp")) + if (!check_extensions(sf, "idsp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ goto fail; dspm.max_channels = 8; @@ -612,52 +612,52 @@ VGMSTREAM * init_vgmstream_idsp_namco(STREAMFILE *streamFile) { dspm.fix_looping = 1; /* 0x04: null */ - dspm.channel_count = read_32bitBE(0x08, streamFile); + dspm.channel_count = read_32bitBE(0x08, sf); /* 0x0c: sample rate */ /* 0x10: num_samples */ /* 0x14: loop start */ /* 0x18: loop end */ - dspm.interleave = read_32bitBE(0x1c,streamFile); /* usually 0x10 */ - dspm.header_offset = read_32bitBE(0x20,streamFile); - dspm.header_spacing = read_32bitBE(0x24,streamFile); - dspm.start_offset = read_32bitBE(0x28,streamFile); + dspm.interleave = read_32bitBE(0x1c,sf); /* usually 0x10 */ + dspm.header_offset = read_32bitBE(0x20,sf); + dspm.header_spacing = read_32bitBE(0x24,sf); + dspm.start_offset = read_32bitBE(0x28,sf); /* Soul Calibur: Broken destiny (PSP), Taiko no Tatsujin: Atsumete Tomodachi Daisakusen (WiiU) */ if (dspm.interleave == 0) /* half interleave (happens sometimes), use channel size */ - dspm.interleave = read_32bitBE(0x2c,streamFile); + dspm.interleave = read_32bitBE(0x2c,sf); dspm.meta_type = meta_IDSP_NAMCO; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* sadb - Procyon Studio header + interleaved dsp [Shiren the Wanderer 3 (Wii), Disaster: Day of Crisis (Wii)] */ -VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "sad")) + if (!check_extensions(sf, "sad")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x73616462) /* "sadb" */ + if (read_32bitBE(0x00,sf) != 0x73616462) /* "sadb" */ goto fail; - dspm.channel_count = read_8bit(0x32, streamFile); + dspm.channel_count = read_8bit(0x32, sf); dspm.max_channels = 2; dspm.header_offset = 0x80; dspm.header_spacing = 0x60; - dspm.start_offset = read_32bitBE(0x48,streamFile); + dspm.start_offset = read_32bitBE(0x48,sf); dspm.interleave = 0x10; dspm.meta_type = meta_DSP_SADB; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* IDSP - Traveller's Tales header + interleaved dsps [Lego Batman (Wii), Lego Dimensions (Wii U)] */ -VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_idsp_tt(STREAMFILE* sf) { dsp_meta dspm = {0}; int version_main, version_sub; @@ -665,14 +665,14 @@ VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { /* .gcm: standard * .idsp: header id? * .wua: Lego Dimensions (Wii U) */ - if (!check_extensions(streamFile, "gcm,idsp,wua")) + if (!check_extensions(sf, "gcm,idsp,wua")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ goto fail; - version_main = read_32bitBE(0x04, streamFile); - version_sub = read_32bitBE(0x08, streamFile); /* extra check since there are other IDSPs */ + version_main = read_32bitBE(0x04, sf); + version_sub = read_32bitBE(0x08, sf); /* extra check since there are other IDSPs */ if (version_main == 0x01 && version_sub == 0xc8) { /* Transformers: The Game (Wii) */ dspm.channel_count = 2; @@ -694,7 +694,7 @@ VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { else if (version_main == 0x03 && version_sub == 0x12c) { /* Lego The Lord of the Rings (Wii) */ /* Lego Dimensions (Wii U) */ - dspm.channel_count = read_32bitBE(0x10, streamFile); + dspm.channel_count = read_32bitBE(0x10, sf); dspm.max_channels = 2; dspm.header_offset = 0x20; /* 0x14+: "I_AM_PADDING" */ @@ -705,22 +705,22 @@ VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + 0x60 * dspm.channel_count; - dspm.interleave = read_32bitBE(0x0c, streamFile); + dspm.interleave = read_32bitBE(0x0c, sf); dspm.meta_type = meta_IDSP_TT; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* IDSP - from Next Level games [Super Mario Strikers (GC), Mario Strikers: Charged (Wii)] */ -VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_idsp_nl(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "idsp")) + if (!check_extensions(sf, "idsp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ goto fail; dspm.channel_count = 2; @@ -729,11 +729,11 @@ VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { dspm.header_offset = 0x0c; dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; - dspm.interleave = read_32bitBE(0x04,streamFile); + dspm.interleave = read_32bitBE(0x04,sf); /* 0x08: usable channel size */ { - size_t stream_size = get_streamfile_size(streamFile); - if (read_32bitBE(stream_size - 0x04,streamFile) == 0x30303030) + size_t stream_size = get_streamfile_size(sf); + if (read_32bitBE(stream_size - 0x04,sf) == 0x30303030) stream_size -= 0x14; /* remove padding */ stream_size -= dspm.start_offset; @@ -746,87 +746,87 @@ VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { dspm.force_loop_seconds = 15; dspm.meta_type = meta_IDSP_NL; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .wsd - Custom header + full interleaved dsp [Phantom Brave (Wii)] */ -VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_wii_wsd(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "wsd")) + if (!check_extensions(sf, "wsd")) goto fail; - if (read_32bitBE(0x08,streamFile) != read_32bitBE(0x0c,streamFile)) /* channel sizes */ + if (read_32bitBE(0x08,sf) != read_32bitBE(0x0c,sf)) /* channel sizes */ goto fail; dspm.channel_count = 2; dspm.max_channels = 2; - dspm.header_offset = read_32bitBE(0x00,streamFile); - dspm.header_spacing = read_32bitBE(0x04,streamFile) - dspm.header_offset; + dspm.header_offset = read_32bitBE(0x00,sf); + dspm.header_spacing = read_32bitBE(0x04,sf) - dspm.header_offset; dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_WII_WSD; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .ddsp - full interleaved dsp [The Sims 2 - Pets (Wii)] */ -VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_ddsp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "ddsp")) + if (!check_extensions(sf, "ddsp")) goto fail; dspm.channel_count = 2; dspm.max_channels = 2; dspm.header_offset = 0x00; - dspm.header_spacing = (get_streamfile_size(streamFile) / dspm.channel_count); + dspm.header_spacing = (get_streamfile_size(sf) / dspm.channel_count); dspm.start_offset = 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_DDSP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* iSWS - Sumo Digital header + interleaved dsp [DiRT 2 (Wii), F1 2009 (Wii)] */ -VGMSTREAM * init_vgmstream_wii_was(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_wii_was(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "was,dsp,isws")) + if (!check_extensions(sf, "was,dsp,isws")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x69535753) /* "iSWS" */ + if (read_32bitBE(0x00,sf) != 0x69535753) /* "iSWS" */ goto fail; - dspm.channel_count = read_32bitBE(0x08,streamFile); + dspm.channel_count = read_32bitBE(0x08,sf); dspm.max_channels = 2; - dspm.header_offset = 0x08 + read_32bitBE(0x04,streamFile); + dspm.header_offset = 0x08 + read_32bitBE(0x04,sf); dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; - dspm.interleave = read_32bitBE(0x10,streamFile); + dspm.interleave = read_32bitBE(0x10,sf); dspm.meta_type = meta_WII_WAS; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .str - Infogrames raw interleaved dsp [Micro Machines (GC), Superman: Shadow of Apokolips (GC)] */ -VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_str_ig(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "str")) + if (!check_extensions(sf, "str")) goto fail; dspm.channel_count = 2; @@ -838,17 +838,17 @@ VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE *streamFile) { dspm.interleave = 0x4000; dspm.meta_type = meta_DSP_STR_IG; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .dsp - Ubisoft interleaved dsp with bad loop start [Speed Challenge: Jacques Villeneuve's Racing Vision (GC), XIII (GC)] */ -VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_xiii(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "dsp")) + if (!check_extensions(sf, "dsp")) goto fail; dspm.channel_count = 2; @@ -861,25 +861,25 @@ VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE *streamFile) { dspm.interleave = 0x08; dspm.meta_type = meta_DSP_XIII; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* NPD - Icon Games header + subinterleaved DSPs [Vertigo (Wii), Build n' Race (Wii)] */ -VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_wii_ndp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "ndp")) + if (!check_extensions(sf, "ndp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4E445000) /* "NDP\0" */ + if (read_32bitBE(0x00,sf) != 0x4E445000) /* "NDP\0" */ goto fail; - if (read_32bitLE(0x08,streamFile) + 0x18 != get_streamfile_size(streamFile)) + if (read_32bitLE(0x08,sf) + 0x18 != get_streamfile_size(sf)) goto fail; /* 0x0c: sample rate */ - dspm.channel_count = read_32bitLE(0x10,streamFile); + dspm.channel_count = read_32bitLE(0x10,sf); dspm.max_channels = 2; dspm.header_offset = 0x18; @@ -888,25 +888,25 @@ VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile) { dspm.interleave = 0x04; dspm.meta_type = meta_WII_NDP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* Cabela's series (Magic Wand dev?) - header + interleaved dsp * [Cabela's Big Game Hunt 2005 Adventures (GC), Cabela's Outdoor Adventures (GC)] */ -VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_cabelas(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "dsp")) + if (!check_extensions(sf, "dsp")) goto fail; /* has extra stuff in the reserved data, without it this meta may catch other DSPs it shouldn't */ - if (read_32bitBE(0x50,streamFile) == 0 || read_32bitBE(0x54,streamFile) == 0) + if (read_32bitBE(0x50,sf) == 0 || read_32bitBE(0x54,sf) == 0) goto fail; /* sfx are mono, but standard dsp will catch them tho */ - dspm.channel_count = read_32bitBE(0x00,streamFile) == read_32bitBE(0x60,streamFile) ? 2 : 1; + dspm.channel_count = read_32bitBE(0x00,sf) == read_32bitBE(0x60,sf) ? 2 : 1; dspm.max_channels = 2; dspm.force_loop = (dspm.channel_count > 1); @@ -916,49 +916,49 @@ VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE *streamFile) { dspm.interleave = 0x10; dspm.meta_type = meta_DSP_CABELAS; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* AAAp - Acclaim Austin Audio header + interleaved dsp [Vexx (GC), Turok: Evolution (GC)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_dsp_aaap(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "dsp")) + if (!check_extensions(sf, "dsp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41414170) /* "AAAp" */ + if (read_32bitBE(0x00,sf) != 0x41414170) /* "AAAp" */ goto fail; - dspm.channel_count = read_16bitBE(0x06,streamFile); + dspm.channel_count = read_16bitBE(0x06,sf); dspm.max_channels = 2; dspm.header_offset = 0x08; dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; - dspm.interleave = (uint16_t)read_16bitBE(0x04,streamFile); + dspm.interleave = (uint16_t)read_16bitBE(0x04,sf); dspm.meta_type = meta_NGC_DSP_AAAP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* DSPW - Capcom header + full interleaved DSP [Sengoku Basara 3 (Wii), Monster Hunter 3 Ultimate (WiiU)] */ -VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_dspw(STREAMFILE* sf) { dsp_meta dspm = {0}; size_t data_size; /* check extension */ - if (!check_extensions(streamFile, "dspw")) + if (!check_extensions(sf, "dspw")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x44535057) /* "DSPW" */ + if (read_32bitBE(0x00,sf) != 0x44535057) /* "DSPW" */ goto fail; /* ignore time marker */ - data_size = read_32bitBE(0x08, streamFile); - if (read_32bitBE(data_size - 0x10, streamFile) == 0x74494D45) /* "tIME" */ + data_size = read_32bitBE(0x08, sf); + if (read_32bitBE(data_size - 0x10, sf) == 0x74494D45) /* "tIME" */ data_size -= 0x10; /* (ignore, 2 ints in YYYYMMDD hhmmss00) */ /* some files have a mrkr section with multiple loop regions added at the end (variable size) */ @@ -966,7 +966,7 @@ VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { off_t mrkr_offset = data_size - 0x04; off_t max_offset = data_size - 0x1000; while (mrkr_offset > max_offset) { - if (read_32bitBE(mrkr_offset, streamFile) != 0x6D726B72) { /* "mrkr" */ + if (read_32bitBE(mrkr_offset, sf) != 0x6D726B72) { /* "mrkr" */ mrkr_offset -= 0x04; } else { data_size = mrkr_offset; @@ -977,7 +977,7 @@ VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { data_size -= 0x20; /* header size */ /* 0x10: loop start, 0x14: loop end, 0x1c: num_samples */ - dspm.channel_count = read_32bitBE(0x18, streamFile); + dspm.channel_count = read_32bitBE(0x18, sf); dspm.max_channels = 6; /* 6ch in Monster Hunter 3 Ultimate */ dspm.header_offset = 0x20; @@ -986,74 +986,74 @@ VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { dspm.interleave = data_size / dspm.channel_count; dspm.meta_type = meta_DSP_DSPW; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* iadp - custom header + interleaved dsp [Dr. Muto (GC)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_dsp_iadp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .adp: actual extension, .iadp: header id */ - if (!check_extensions(streamFile, "adp,iadp")) + if (!check_extensions(sf, "adp,iadp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x69616470) /* "iadp" */ + if (read_32bitBE(0x00,sf) != 0x69616470) /* "iadp" */ goto fail; - dspm.channel_count = read_32bitBE(0x04,streamFile); + dspm.channel_count = read_32bitBE(0x04,sf); dspm.max_channels = 2; dspm.header_offset = 0x20; dspm.header_spacing = 0x60; - dspm.start_offset = read_32bitBE(0x1C,streamFile); - dspm.interleave = read_32bitBE(0x08,streamFile); + dspm.start_offset = read_32bitBE(0x1C,sf); + dspm.interleave = read_32bitBE(0x08,sf); dspm.meta_type = meta_NGC_DSP_IADP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .mcadpcm - Custom header + full interleaved dsp [Skyrim (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_mcadpcm(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "mcadpcm")) + if (!check_extensions(sf, "mcadpcm")) goto fail; /* could validate dsp sizes but only for +1ch, check_dsp_samples will do it anyway */ - //if (read_32bitLE(0x08,streamFile) != read_32bitLE(0x10,streamFile)) + //if (read_32bitLE(0x08,sf) != read_32bitLE(0x10,sf)) // goto fail; - dspm.channel_count = read_32bitLE(0x00,streamFile); + dspm.channel_count = read_32bitLE(0x00,sf); dspm.max_channels = 2; dspm.little_endian = 1; - dspm.header_offset = read_32bitLE(0x04,streamFile); + dspm.header_offset = read_32bitLE(0x04,sf); dspm.header_spacing = dspm.channel_count == 1 ? 0 : - read_32bitLE(0x0c,streamFile) - dspm.header_offset; /* channel 2 start, only with Nch */ + read_32bitLE(0x0c,sf) - dspm.header_offset; /* channel 2 start, only with Nch */ dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_MCADPCM; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .switch_audio - UE4 standard LE header + full interleaved dsp [Gal Gun 2 (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_switch_audio(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .switch_audio: possibly UE4 class name rather than extension, .dsp: assumed */ - if (!check_extensions(streamFile, "switch_audio,dsp")) + if (!check_extensions(sf, "switch_audio,dsp")) goto fail; /* manual double header test */ - if (read_32bitLE(0x00, streamFile) == read_32bitLE(get_streamfile_size(streamFile) / 2, streamFile)) + if (read_32bitLE(0x00, sf) == read_32bitLE(get_streamfile_size(sf) / 2, sf)) dspm.channel_count = 2; else dspm.channel_count = 1; @@ -1061,28 +1061,28 @@ VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile) { dspm.little_endian = 1; dspm.header_offset = 0x00; - dspm.header_spacing = get_streamfile_size(streamFile) / dspm.channel_count; + dspm.header_spacing = get_streamfile_size(sf) / dspm.channel_count; dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_SWITCH_AUDIO; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .vag - Nippon Ichi SPS wrapper [Penny-Punching Princess (Switch), Ys VIII (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .vag: Penny-Punching Princess (Switch) * .nlsd: Ys VIII (Switch) */ - if (!check_extensions(streamFile, "vag,nlsd")) + if (!check_extensions(sf, "vag,nlsd")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x08000000) /* file type (see other N1 SPS) */ + if (read_32bitBE(0x00,sf) != 0x08000000) /* file type (see other N1 SPS) */ goto fail; - if ((uint16_t)read_16bitLE(0x08,streamFile) != read_32bitLE(0x24,streamFile)) /* header has various repeated values */ + if ((uint16_t)read_16bitLE(0x08,sf) != read_32bitLE(0x24,sf)) /* header has various repeated values */ goto fail; dspm.channel_count = 1; @@ -1097,17 +1097,17 @@ VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile) { dspm.fix_loop_start = 1; dspm.meta_type = meta_DSP_VAG; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .itl - from Chanrinko Hero (GC) */ -VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_itl_ch(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "itl")) + if (!check_extensions(sf, "itl")) goto fail; dspm.channel_count = 2; @@ -1121,26 +1121,26 @@ VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile) { dspm.fix_looping = 1; dspm.meta_type = meta_DSP_ITL; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* ADPY - AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_adpy(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "adpcmx")) + if (!check_extensions(sf, "adpcmx")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41445059) /* "ADPY" */ + if (read_32bitBE(0x00,sf) != 0x41445059) /* "ADPY" */ goto fail; /* 0x04(2): 1? */ /* 0x08: some size? */ /* 0x0c: null */ - dspm.channel_count = read_16bitLE(0x06,streamFile); + dspm.channel_count = read_16bitLE(0x06,sf); dspm.max_channels = 2; dspm.little_endian = 1; @@ -1150,56 +1150,56 @@ VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile) { dspm.interleave = 0x08; dspm.meta_type = meta_DSP_ADPY; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* ADPX - AQUASTYLE wrapper [Fushigi no Gensokyo: Lotus Labyrinth (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_adpx(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_adpx(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "adpcmx")) + if (!check_extensions(sf, "adpcmx")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41445058) /* "ADPX" */ + if (read_32bitBE(0x00,sf) != 0x41445058) /* "ADPX" */ goto fail; /* from 0x04 *6 are probably channel sizes, so max would be 6ch; this assumes 2ch */ - if (read_32bitLE(0x04,streamFile) != read_32bitLE(0x08,streamFile) && - read_32bitLE(0x0c,streamFile) != 0) + if (read_32bitLE(0x04,sf) != read_32bitLE(0x08,sf) && + read_32bitLE(0x0c,sf) != 0) goto fail; dspm.channel_count = 2; dspm.max_channels = 2; dspm.little_endian = 1; dspm.header_offset = 0x1c; - dspm.header_spacing = read_32bitLE(0x04,streamFile); + dspm.header_spacing = read_32bitLE(0x04,sf); dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_ADPX; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .ds2 - LucasArts wrapper [Star Wars: Bounty Hunter (GC)] */ -VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf) { dsp_meta dspm = {0}; size_t file_size, channel_offset; /* checks */ /* .ds2: real extension, dsp: fake/renamed */ - if (!check_extensions(streamFile, "ds2,dsp")) + if (!check_extensions(sf, "ds2,dsp")) goto fail; - if (!(read_32bitBE(0x50,streamFile) == 0 && - read_32bitBE(0x54,streamFile) == 0 && - read_32bitBE(0x58,streamFile) == 0 && - read_32bitBE(0x5c,streamFile) != 0)) + if (!(read_32bitBE(0x50,sf) == 0 && + read_32bitBE(0x54,sf) == 0 && + read_32bitBE(0x58,sf) == 0 && + read_32bitBE(0x5c,sf) != 0)) goto fail; - file_size = get_streamfile_size(streamFile); - channel_offset = read_32bitBE(0x5c,streamFile); /* absolute offset to 2nd channel */ + file_size = get_streamfile_size(sf); + channel_offset = read_32bitBE(0x5c,sf); /* absolute offset to 2nd channel */ if (channel_offset < file_size / 2 || channel_offset > file_size) /* just to make sure */ goto fail; @@ -1213,23 +1213,23 @@ VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile) { dspm.interleave = channel_offset - dspm.start_offset; dspm.meta_type = meta_DSP_DS2; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .itl - Incinerator Studios interleaved dsp [Cars Race-o-rama (Wii), MX vs ATV Untamed (Wii)] */ -VGMSTREAM * init_vgmstream_dsp_itl(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf) { dsp_meta dspm = {0}; size_t stream_size; /* checks */ /* .itl: standard * .dsp: default to catch a similar file, not sure which devs */ - if (!check_extensions(streamFile, "itl,dsp")) + if (!check_extensions(sf, "itl,dsp")) goto fail; - stream_size = get_streamfile_size(streamFile); + stream_size = get_streamfile_size(sf); dspm.channel_count = 2; dspm.max_channels = 2; @@ -1244,7 +1244,65 @@ VGMSTREAM * init_vgmstream_dsp_itl(STREAMFILE *streamFile) { //todo some files end in half a frame and may click at the very end //todo when .dsp should refer to Ultimate Board Collection (Wii), not sure about dev dspm.meta_type = meta_DSP_ITL_i; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); +fail: + return NULL; +} + +/* .wav - Square Enix wrapper [Dragon Quest I-III (Switch)] */ +VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(sf, "wav,lwav")) + goto fail; + if (read_u32be(0x00,sf) != 0x00000000) + goto fail; + + dspm.channel_count = read_u32le(0x04,sf); + dspm.header_offset = read_u32le(0x08,sf); + /* 0x0c: channel size */ + dspm.start_offset = dspm.header_offset + 0x60; + + if (dspm.channel_count > 1) { + dspm.interleave = read_u32le(0x10,sf) - dspm.header_offset; + dspm.header_spacing = dspm.interleave; + } + + + dspm.max_channels = 2; + dspm.little_endian = 1; + + dspm.meta_type = meta_DSP_SQEX; + return init_vgmstream_dsp_common(sf, &dspm); +fail: + return NULL; +} + +/* WiiVoice - Koei Tecmo wrapper [Fatal Frame 5 (WiiU)] */ +VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf) { + dsp_meta dspm = {0}; + /* also see g1l.c for WiiBGM weirder variation */ + + /* checks */ + /* .dsp: assumed */ + if (!check_extensions(sf, "dsp")) + goto fail; + if (read_u32be(0x00,sf) != 0x57696956 && /* "WiiV" */ + read_u32be(0x04,sf) != 0x6F696365) /* "oice" */ + goto fail; + + dspm.channel_count = 1; + dspm.max_channels = 1; + + dspm.header_offset = read_u32be(0x08,sf); + /* 0x10: file size */ + /* 0x14: data size */ + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; + + dspm.meta_type = meta_DSP_WIIVOICE; + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c b/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c index 81baf9fb2..277cab413 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c @@ -3,28 +3,29 @@ #include "nus3bank_streamfile.h" -typedef enum { IDSP, IVAG, BNSF, RIFF, OPUS, RIFF_ENC, } nus3bank_codec; +typedef enum { IDSP, IVAG, BNSF, RIFF, RIFF_XMA2, OPUS, RIFF_ENC, } nus3bank_codec; -/* .nus3bank - Namco's newest audio container [Super Smash Bros (Wii U), idolmaster (PS4))] */ -VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +/* .nus3bank - Namco's newest audio container [Super Smash Bros (Wii U), THE iDOLM@STER 2 (PS3/X360)] */ +VGMSTREAM* init_vgmstream_nus3bank(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t tone_offset = 0, pack_offset = 0, name_offset = 0, subfile_offset = 0; size_t name_size = 0, subfile_size = 0; nus3bank_codec codec; const char* fake_ext; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs, target_subsong = sf->stream_index; + /* checks */ /* .nub2: early [THE iDOLM@STER 2 (PS3/X360)] * .nus3bank: standard */ - if (!check_extensions(streamFile, "nub2,nus3bank")) + if (!check_extensions(sf, "nub2,nus3bank")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4E555333) /* "NUS3" */ + if (read_u32be(0x00,sf) != 0x4E555333) /* "NUS3" */ goto fail; - if (read_32bitBE(0x08,streamFile) != 0x42414E4B) /* "BANK" */ + if (read_u32be(0x08,sf) != 0x42414E4B) /* "BANK" */ goto fail; - if (read_32bitBE(0x0c,streamFile) != 0x544F4320) /* "TOC\0" */ + if (read_u32be(0x0c,sf) != 0x544F4320) /* "TOC\0" */ goto fail; /* header is always LE, while contained files may use another endianness */ @@ -32,12 +33,12 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { /* parse TOC with all existing chunks and sizes (offsets must be derived) */ { int i; - off_t offset = 0x14 + read_32bitLE(0x10, streamFile); /* TOC size */ - size_t chunk_count = read_32bitLE(0x14, streamFile); /* rarely not 7 (ex. SMB U's snd_bgm_CRS12_Simple_Result_Final) */ + off_t offset = 0x14 + read_u32le(0x10, sf); /* TOC size */ + size_t chunk_count = read_u32le(0x14, sf); /* rarely not 7 (ex. SMB U's snd_bgm_CRS12_Simple_Result_Final) */ for (i = 0; i < chunk_count; i++) { - uint32_t chunk_id = (uint32_t)read_32bitBE(0x18+(i*0x08)+0x00, streamFile); - size_t chunk_size = (size_t)read_32bitLE(0x18+(i*0x08)+0x04, streamFile); + uint32_t chunk_id = read_u32be(0x18+(i*0x08)+0x00, sf); + size_t chunk_size = read_u32le(0x18+(i*0x08)+0x04, sf); switch(chunk_id) { case 0x544F4E45: /* "TONE": stream info */ @@ -71,7 +72,7 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { { int i; uint32_t codec_id = 0; - size_t entries = read_32bitLE(tone_offset+0x00, streamFile); + size_t entries = read_u32le(tone_offset+0x00, sf); /* get actual number of subsongs */ total_subsongs = 0; @@ -82,8 +83,8 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { size_t tone_header_size, stream_name_size, stream_size; uint8_t flags2; - tone_header_offset = read_32bitLE(tone_offset+0x04+(i*0x08)+0x00, streamFile); - tone_header_size = read_32bitLE(tone_offset+0x04+(i*0x08)+0x04, streamFile); + tone_header_offset = read_u32le(tone_offset+0x04+(i*0x08)+0x00, sf); + tone_header_size = read_u32le(tone_offset+0x04+(i*0x08)+0x04, sf); offset = tone_offset + tone_header_offset; //;VGM_LOG("NUS3BANK: tone at %lx, size %x\n", tone_offset + tone_header_offset, tone_header_size); @@ -96,7 +97,7 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { /* 0x00: type? normally 0x00 and rarely 0x09 */ /* 0x04: usually -1, found when tone is not a stream (most flags are off too) */ /* 0x06: flags1 */ - flags2 = read_8bit(offset + 0x07, streamFile); + flags2 = read_8bit(offset + 0x07, sf); offset += 0x08; /* flags3-6 (early .nub2 and some odd non-stream don't have them) */ @@ -104,18 +105,18 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { offset += 0x04; } - stream_name_size = read_8bit(offset + 0x00, streamFile); /* includes null */ + stream_name_size = read_8bit(offset + 0x00, sf); /* includes null */ stream_name_offset = offset + 0x01; offset += align_size_to_block(0x01 + stream_name_size, 0x04); /* padded if needed */ /* 0x00: subtype? should be 0 */ - if (read_32bitLE(offset + 0x04, streamFile) != 0x08) { /* flag? */ + if (read_u32le(offset + 0x04, sf) != 0x08) { /* flag? */ //;VGM_LOG("NUS3BANK: bad tone type at %lx, size %x\n", tone_offset + tone_header_offset, tone_header_size); continue; } - stream_offset = read_32bitLE(offset + 0x08, streamFile) + pack_offset; - stream_size = read_32bitLE(offset + 0x0c, streamFile); + stream_offset = read_u32le(offset + 0x08, sf) + pack_offset; + stream_size = read_u32le(offset + 0x0c, sf); //;VGM_LOG("NUS3BANK: so=%lx, ss=%x\n", stream_offset, stream_size); /* Beyond are a bunch of sub-chunks of unknown size with floats and stuff, that seemingly @@ -148,17 +149,25 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { } //todo improve, codec may be in one of the tone sub-chunks (or other chunk? one bank seems to use one codec) - codec_id = read_32bitBE(subfile_offset, streamFile); + codec_id = read_u32be(subfile_offset + 0x00, sf); switch(codec_id) { case 0x49445350: /* "IDSP" [Super Smash Bros. for 3DS (3DS)] */ codec = IDSP; fake_ext = "idsp"; break; - case 0x52494646: /* "RIFF" [THE iDOLM@STER 2 (PS3), Mario Kart Arcade GP DX (PC), idolm@ster: Platinum Stars (PS4)] */ - codec = RIFF; - fake_ext = "wav"; //TODO: works but should have better detection + case 0x52494646: { /* "RIFF" [THE iDOLM@STER 2 (PS3), Mario Kart Arcade GP DX (PC), idolm@ster: Platinum Stars (PS4)] */ + uint16_t format = read_u16le(subfile_offset + 0x14, sf); + if (format == 0x0166) { /* Tekken Tag Tournament 2 (X360) */ + codec = RIFF_XMA2; + fake_ext = "xma"; + } + else { + codec = RIFF; + fake_ext = "wav"; //TODO: works but should have better detection + } break; + } case 0x4F505553: /* "OPUS" [Taiko no Tatsujin (Switch)] */ codec = OPUS; @@ -186,9 +195,9 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { } } - //;VGM_LOG("NUS3BANK: subfile=%lx, size=%x\n", subfile_offset, subfile_size); + //;VGM_LOG("NUS3BANK: subfile=%lx, size=%x, %s\n", subfile_offset, subfile_size, fake_ext); - temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, fake_ext); + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, fake_ext); if (!temp_sf) goto fail; /* init the VGMSTREAM */ @@ -218,6 +227,11 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { if (!vgmstream) goto fail; break; + case RIFF_XMA2: + vgmstream = init_vgmstream_xma(temp_sf); + if (!vgmstream) goto fail; + break; + case RIFF_ENC: vgmstream = init_vgmstream_nus3bank_encrypted(temp_sf); if (!vgmstream) goto fail; @@ -229,7 +243,7 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { vgmstream->num_streams = total_subsongs; if (name_offset) - read_string(vgmstream->stream_name,name_size, name_offset,streamFile); + read_string(vgmstream->stream_name, name_size, name_offset, sf); close_streamfile(temp_sf); @@ -242,9 +256,9 @@ fail: } /* encrypted RIFF from the above, in case kids try to extract and play single files */ -VGMSTREAM* init_vgmstream_nus3bank_encrypted(STREAMFILE *sf) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +VGMSTREAM* init_vgmstream_nus3bank_encrypted(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; /* checks */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index 07c46558c..5cfebf283 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -6,7 +6,7 @@ #include "ogg_vorbis_streamfile.h" -static void um3_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { +static void um3_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { uint8_t *ptr8 = ptr; size_t bytes_read = size * nmemb; ogg_vorbis_io *io = datasource; @@ -23,7 +23,7 @@ static void um3_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo } } -static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { +static void kovs_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { uint8_t *ptr8 = ptr; size_t bytes_read = size * nmemb; ogg_vorbis_io *io = datasource; @@ -41,7 +41,7 @@ static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v } } -static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { +static void psychic_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { static const uint8_t key[6] = { 0x23,0x31,0x20,0x2e,0x2e,0x28 }; @@ -58,7 +58,7 @@ static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb } } -static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { +static void rpgmvo_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { static const uint8_t header[16] = { /* OggS, packet type, granule, stream id(empty) */ 0x4F,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; @@ -97,9 +97,9 @@ static const uint32_t xiph_mappings[] = { /* Ogg Vorbis, may contain loop comments */ -VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; +VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; ogg_vorbis_io_config_data cfg = {0}; ogg_vorbis_meta_info_t ovmi = {0}; off_t start_offset = 0; @@ -124,54 +124,59 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { * .acm: Planescape Torment Enhanced Edition (PC) * .sod: Zone 4 (PC) * .aif/laif/aif-Loop: Psychonauts (PC) raw extractions (named) */ - if (check_extensions(streamFile,"ogg,logg,adx,rof,acm,sod,aif,laif,aif-Loop")) { + if (check_extensions(sf,"ogg,logg,adx,rof,acm,sod,aif,laif,aif-Loop")) { is_ogg = 1; - } else if (check_extensions(streamFile,"um3")) { + } else if (check_extensions(sf,"um3")) { is_um3 = 1; - } else if (check_extensions(streamFile,"kvs,kovs")) { + } else if (check_extensions(sf,"kvs,kovs")) { /* .kvs: Atelier Sophie (PC), kovs: header id only? */ is_kovs = 1; - } else if (check_extensions(streamFile,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */ + } else if (check_extensions(sf,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */ is_sngw = 1; - } else if (check_extensions(streamFile,"isd")) { /* .isd: Inti Creates PC games */ + } else if (check_extensions(sf,"isd")) { /* .isd: Inti Creates PC games */ is_isd = 1; - } else if (check_extensions(streamFile,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */ + } else if (check_extensions(sf,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */ is_rpgmvo = 1; - } else if (check_extensions(streamFile,"eno")) { /* .eno: Metronomicon (PC) */ + } else if (check_extensions(sf,"eno")) { /* .eno: Metronomicon (PC) */ is_eno = 1; - } else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */ + } else if (check_extensions(sf,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */ is_gwm = 1; - } else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */ + } else if (check_extensions(sf,"mus")) { /* .mus: Redux - Dark Matters (PC) */ is_mus = 1; - } else if (check_extensions(streamFile,"lse")) { /* .lse: Labyrinth of Refrain: Coven of Dusk (PC) */ + } else if (check_extensions(sf,"lse")) { /* .lse: Labyrinth of Refrain: Coven of Dusk (PC) */ is_lse = 1; - } else if (check_extensions(streamFile,"bgm")) { /* .bgm: Fortissimo (PC) */ + } else if (check_extensions(sf,"bgm")) { /* .bgm: Fortissimo (PC) */ is_bgm = 1; } else { goto fail; } if (is_ogg) { - if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */ + if (read_32bitBE(0x00,sf) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */ ovmi.decryption_callback = psychic_ogg_decryption_callback; } - else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */ + else if (read_32bitBE(0x00,sf) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */ cfg.is_header_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */ + else if (read_32bitBE(0x00,sf) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */ cfg.key[0] = 0xF0; cfg.key_len = 1; cfg.is_nibble_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,streamFile) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */ - read_32bitBE(0x3a,streamFile) == 0x4F676753) { /* "OggS" in next page */ + else if (read_32bitBE(0x00,sf) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */ + read_32bitBE(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */ cfg.is_header_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,streamFile) == 0x4F676753) { /* "OggS" (standard) */ + else if (read_32bitBE(0x00,sf) != 0x4F676753 && /* random(?) swap instead of "OggS" [Tobi Tsukihime (PC)] */ + read_32bitBE(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */ + cfg.is_header_swap = 1; + cfg.is_encrypted = 1; + } + else if (read_32bitBE(0x00,sf) == 0x4F676753) { /* "OggS" (standard) */ ; } else { @@ -180,16 +185,16 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_um3) { /* ["Ultramarine3" (???)] */ - if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */ + if (read_32bitBE(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */ ovmi.decryption_callback = um3_ogg_decryption_callback; } } if (is_kovs) { /* Koei Tecmo PC games */ - if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */ + if (read_32bitBE(0x00,sf) != 0x4b4f5653) { /* "KOVS" */ goto fail; } - ovmi.loop_start = read_32bitLE(0x08,streamFile); + ovmi.loop_start = read_32bitLE(0x08,sf); ovmi.loop_flag = (ovmi.loop_start != 0); ovmi.decryption_callback = kovs_ogg_decryption_callback; ovmi.meta_type = meta_OGG_KOVS; @@ -198,8 +203,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_sngw) { /* [Capcom's MT Framework PC games] */ - if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */ - cfg.key_len = read_streamfile(cfg.key, 0x00, 0x04, streamFile); + if (read_32bitBE(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */ + cfg.key_len = read_streamfile(cfg.key, 0x00, 0x04, sf); cfg.is_header_swap = 1; cfg.is_nibble_swap = 1; cfg.is_encrypted = 1; @@ -212,7 +217,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { const char *isl_name = NULL; /* check various encrypted "OggS" values */ - if (read_32bitBE(0x00,streamFile) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */ + if (read_32bitBE(0x00,sf) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */ static const uint8_t isd_gv_key[16] = { 0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00 }; @@ -220,7 +225,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { memcpy(cfg.key, isd_gv_key, cfg.key_len); isl_name = "GV_steam.isl"; } - else if (read_32bitBE(0x00,streamFile) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */ + else if (read_32bitBE(0x00,sf) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */ static const uint8_t isd_mgv_key[120] = { 0x40,0x80,0xE0,0x80,0x40,0x40,0xA0,0x00,0xA0,0x40,0x00,0x80,0x00,0x40,0xA0,0x00, 0xC0,0x40,0xE0,0x00,0x60,0x40,0x80,0x00,0xA0,0x00,0xE0,0x00,0x60,0x40,0xC0,0x00, @@ -235,7 +240,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { memcpy(cfg.key, isd_mgv_key, cfg.key_len); isl_name = "MGV_steam.isl"; } - else if (read_32bitBE(0x00,streamFile) == 0x0FA74753) { /* Blaster Master Zero (PC) */ + else if (read_32bitBE(0x00,sf) == 0x0FA74753) { /* Blaster Master Zero (PC) */ static const uint8_t isd_bmz_key[120] = { 0x40,0xC0,0x20,0x00,0x40,0xC0,0xC0,0x00,0x00,0x80,0xE0,0x80,0x80,0x40,0x20,0x00, 0x60,0xC0,0xC0,0x00,0xA0,0x80,0x60,0x00,0x40,0x40,0x20,0x00,0x60,0x40,0xC0,0x00, @@ -263,19 +268,19 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { * 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes) * - .isl: looping table (encrypted like the files) */ if (isl_name) { - STREAMFILE *islFile = NULL; + STREAMFILE* islFile = NULL; - islFile = open_streamfile_by_filename(streamFile, isl_name); + islFile = open_streamfile_by_filename(sf, isl_name); if (!islFile) { /* try in ../(file) too since that's how the .isl is stored on disc */ char isl_path[PATH_LIMIT]; snprintf(isl_path, sizeof(isl_path), "../%s", isl_name); - islFile = open_streamfile_by_filename(streamFile, isl_path); + islFile = open_streamfile_by_filename(sf, isl_path); } if (islFile) { - STREAMFILE *dec_sf = NULL; + STREAMFILE* dec_sf = NULL; dec_sf = setup_ogg_vorbis_streamfile(islFile, cfg); if (dec_sf) { @@ -284,7 +289,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { /* has a bunch of tables then a list with file names without extension and loops */ loop_offset = read_32bitLE(0x18, dec_sf); - get_streamfile_basename(streamFile, basename, sizeof(basename)); + get_streamfile_basename(sf, basename, sizeof(basename)); while (loop_offset < get_streamfile_size(dec_sf)) { char testname[0x20]; @@ -310,8 +315,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_rpgmvo) { /* [RPG Maker MV (PC)] */ - if (read_32bitBE(0x00,streamFile) != 0x5250474D && /* "RPGM" */ - read_32bitBE(0x00,streamFile) != 0x56000000) { /* "V\0\0\0" */ + if (read_32bitBE(0x00,sf) != 0x5250474D && /* "RPGM" */ + read_32bitBE(0x00,sf) != 0x56000000) { /* "V\0\0\0" */ goto fail; } ovmi.decryption_callback = rpgmvo_ogg_decryption_callback; @@ -321,7 +326,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (is_eno) { /* [Metronomicon (PC)] */ /* first byte probably derives into key, but this works too */ - cfg.key[0] = (uint8_t)read_8bit(0x05,streamFile); /* regular ogg have a zero at this offset = easy key */ + cfg.key[0] = (uint8_t)read_8bit(0x05,sf); /* regular ogg have a zero at this offset = easy key */ cfg.key_len = 1; cfg.is_encrypted = 1; start_offset = 0x01; /* "OggS" starts after key-thing */ @@ -344,7 +349,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_lse) { /* [Nippon Ichi PC games] */ - if (read_32bitBE(0x00,streamFile) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */ + if (read_32bitBE(0x00,sf) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */ cfg.key[0] = 0xFF; cfg.key_len = 1; cfg.is_header_swap = 1; @@ -353,7 +358,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */ int i; /* found at file_size-1 but this works too (same key for most files but can vary) */ - uint8_t base_key = (uint8_t)read_8bit(0x04,streamFile) - 0x04; + uint8_t base_key = (uint8_t)read_8bit(0x04,sf) - 0x04; cfg.key_len = 256; for (i = 0; i < cfg.key_len; i++) { @@ -364,13 +369,13 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_bgm) { /* [Fortissimo (PC)] */ - size_t file_size = get_streamfile_size(streamFile); + size_t file_size = get_streamfile_size(sf); uint8_t key[0x04]; uint32_t xor_be; put_32bitLE(key, (uint32_t)file_size); xor_be = (uint32_t)get_32bitBE(key); - if ((read_32bitBE(0x00,streamFile) ^ xor_be) == 0x4F676753) { /* "OggS" */ + if ((read_32bitBE(0x00,sf) ^ xor_be) == 0x4F676753) { /* "OggS" */ int i; cfg.key_len = 4; for (i = 0; i < cfg.key_len; i++) { @@ -382,8 +387,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (cfg.is_encrypted) { - temp_streamFile = setup_ogg_vorbis_streamfile(streamFile, cfg); - if (!temp_streamFile) goto fail; + temp_sf = setup_ogg_vorbis_streamfile(sf, cfg); + if (!temp_sf) goto fail; } if (ovmi.meta_type == 0) { @@ -393,18 +398,18 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { ovmi.meta_type = meta_OGG_VORBIS; } - vgmstream = init_vgmstream_ogg_vorbis_callbacks(temp_streamFile != NULL ? temp_streamFile : streamFile, NULL, start_offset, &ovmi); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(temp_sf != NULL ? temp_sf : sf, NULL, start_offset, &ovmi); - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return vgmstream; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return NULL; } -VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks, off_t start, const ogg_vorbis_meta_info_t *ovmi) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ogg_vorbis_callbacks(STREAMFILE* sf, ov_callbacks* callbacks, off_t start, const ogg_vorbis_meta_info_t *ovmi) { + VGMSTREAM* vgmstream = NULL; ogg_vorbis_codec_data* data = NULL; ogg_vorbis_io io = {0}; char name[STREAM_NAME_SIZE] = {0}; @@ -418,7 +423,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb int32_t loop_end = ovmi->loop_end; size_t stream_size = ovmi->stream_size ? ovmi->stream_size : - get_streamfile_size(streamFile) - start; + get_streamfile_size(sf) - start; int disable_reordering = ovmi->disable_reordering; @@ -428,7 +433,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb io.scd_xor_length = ovmi->scd_xor_length; io.xor_value = ovmi->xor_value; - data = init_ogg_vorbis(streamFile, start, stream_size, &io); + data = init_ogg_vorbis(sf, start, stream_size, &io); if (!data) goto fail; ogg_vorbis_get_info(data, &channels, &sample_rate); @@ -436,7 +441,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb /* search for loop comments */ {//todo ignore if loop flag already set? - const char * comment = NULL; + const char* comment = NULL; while (ogg_vorbis_get_comment(data, &comment)) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c index 932ecef2e..b2fff433f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -5,32 +5,32 @@ /* Nintendo OPUS - from Switch games, including header variations (not the same as Ogg Opus) */ -static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, off_t offset, int32_t num_samples, int32_t loop_start, int32_t loop_end) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t offset, int32_t num_samples, int32_t loop_start, int32_t loop_end) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int loop_flag = 0, channel_count; off_t data_offset, multichannel_offset = 0; size_t data_size, skip = 0; - if ((uint32_t)read_32bitLE(offset + 0x00,streamFile) != 0x80000001) + if ((uint32_t)read_32bitLE(offset + 0x00,sf) != 0x80000001) goto fail; - channel_count = read_8bit(offset + 0x09, streamFile); + channel_count = read_8bit(offset + 0x09, sf); /* 0x0a: packet size if CBR, 0 if VBR */ - data_offset = offset + read_32bitLE(offset + 0x10, streamFile); - skip = read_16bitLE(offset + 0x1c, streamFile); + data_offset = offset + read_32bitLE(offset + 0x10, sf); + skip = read_16bitLE(offset + 0x1c, sf); /* 0x1e: ? (seen in Lego Movie 2 (Switch)) */ /* recent >2ch info [Clannad (Switch)] */ - if ((uint32_t)read_32bitLE(offset + 0x20, streamFile) == 0x80000005) { + if ((uint32_t)read_32bitLE(offset + 0x20, sf) == 0x80000005) { multichannel_offset = offset + 0x20; } - if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) + if ((uint32_t)read_32bitLE(data_offset, sf) != 0x80000004) goto fail; - data_size = read_32bitLE(data_offset + 0x04, streamFile); + data_size = read_32bitLE(data_offset + 0x04, sf); start_offset = data_offset + 0x08; loop_flag = (loop_end > 0); /* -1 when not set */ @@ -41,7 +41,7 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, if (!vgmstream) goto fail; vgmstream->meta_type = meta_type; - vgmstream->sample_rate = read_32bitLE(offset + 0x0c,streamFile); + vgmstream->sample_rate = read_32bitLE(offset + 0x0c,sf); if (vgmstream->sample_rate == 16000) vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header vgmstream->num_samples = num_samples; @@ -59,28 +59,28 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, if (multichannel_offset && vgmstream->channels <= 8) { int i; - cfg.stream_count = read_8bit(multichannel_offset + 0x08,streamFile); - cfg.coupled_count = read_8bit(multichannel_offset + 0x09,streamFile); + cfg.stream_count = read_8bit(multichannel_offset + 0x08,sf); + cfg.coupled_count = read_8bit(multichannel_offset + 0x09,sf); for (i = 0; i < vgmstream->channels; i++) { - cfg.channel_mapping[i] = read_8bit(multichannel_offset + 0x0a + i,streamFile); + cfg.channel_mapping[i] = read_8bit(multichannel_offset + 0x0a + i,sf); } } - vgmstream->codec_data = init_ffmpeg_switch_opus_config(streamFile, start_offset,data_size, &cfg); + vgmstream->codec_data = init_ffmpeg_switch_opus_config(sf, start_offset,data_size, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data); if (vgmstream->num_samples == 0) { - vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip; + vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, sf) - skip; } } #else goto fail; #endif - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) goto fail; return vgmstream; @@ -91,20 +91,20 @@ fail: /* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */ -VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf) { STREAMFILE * PSIFile = NULL; off_t offset; int num_samples, loop_start, loop_end; /* checks */ - if (!check_extensions(streamFile,"opus,lopus")) + if (!check_extensions(sf,"opus,lopus")) goto fail; offset = 0x00; /* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */ /* Maybe future Arc System Works games will use this too? */ - PSIFile = open_streamfile_by_ext(streamFile, "psi"); + PSIFile = open_streamfile_by_ext(sf, "psi"); if (PSIFile) { num_samples = read_32bitLE(0x8C, PSIFile); loop_start = read_32bitLE(0x84, PSIFile); @@ -117,56 +117,56 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { loop_end = 0; } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: return NULL; } /* Nippon1 variation [Disgaea 5 (Switch)] */ -VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_n1(STREAMFILE* sf) { off_t offset; int num_samples, loop_start, loop_end; /* checks */ - if ( !check_extensions(streamFile,"opus,lopus")) + if ( !check_extensions(sf,"opus,lopus")) goto fail; - if (!((read_32bitBE(0x04,streamFile) == 0x00000000 && read_32bitBE(0x0c,streamFile) == 0x00000000) || - (read_32bitBE(0x04,streamFile) == 0xFFFFFFFF && read_32bitBE(0x0c,streamFile) == 0xFFFFFFFF))) + if (!((read_32bitBE(0x04,sf) == 0x00000000 && read_32bitBE(0x0c,sf) == 0x00000000) || + (read_32bitBE(0x04,sf) == 0xFFFFFFFF && read_32bitBE(0x0c,sf) == 0xFFFFFFFF))) goto fail; offset = 0x10; num_samples = 0; - loop_start = read_32bitLE(0x00,streamFile); - loop_end = read_32bitLE(0x08,streamFile); + loop_start = read_32bitLE(0x00,sf); + loop_end = read_32bitLE(0x08,sf); - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: return NULL; } /* Capcom variation [Ultra Street Fighter II (Switch), Resident Evil: Revelations (Switch)] */ -VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) { VGMSTREAM *vgmstream = NULL; off_t offset; int num_samples, loop_start, loop_end; int channel_count; /* checks */ - if ( !check_extensions(streamFile,"opus,lopus")) + if ( !check_extensions(sf,"opus,lopus")) goto fail; - channel_count = read_32bitLE(0x04,streamFile); + channel_count = read_32bitLE(0x04,sf); if (channel_count != 1 && channel_count != 2 && channel_count != 6) goto fail; /* unknown stream layout */ - num_samples = read_32bitLE(0x00,streamFile); + num_samples = read_32bitLE(0x00,sf); /* 0x04: channels, >2 uses interleaved streams (2ch+2ch+2ch) */ - loop_start = read_32bitLE(0x08,streamFile); - loop_end = read_32bitLE(0x0c,streamFile); + loop_start = read_32bitLE(0x08,sf); + loop_end = read_32bitLE(0x0c,sf); /* 0x10: frame size (with extra data) */ /* 0x14: extra chunk count */ /* 0x18: null */ - offset = read_32bitLE(0x1c,streamFile); + offset = read_32bitLE(0x1c,sf); /* 0x20-8: config? (0x0077C102 04000000 E107070C) */ /* 0x2c: some size? */ /* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */ @@ -193,7 +193,7 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) { /* open each layer subfile */ for (i = 0; i < layers; i++) { - STREAMFILE* temp_sf = setup_opus_interleave_streamfile(streamFile, offset, i, layers); + STREAMFILE* temp_sf = setup_opus_interleave_streamfile(sf, offset, i, layers); if (!temp_sf) goto fail; data->layers[i] = init_vgmstream_opus(temp_sf, meta_OPUS, 0x00, num_samples,loop_start,loop_end); @@ -215,7 +215,7 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) { return vgmstream; } else { - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); } @@ -225,85 +225,85 @@ fail: } /* Procyon Studio variation [Xenoblade Chronicles 2 (Switch)] */ -VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf) { off_t offset; int num_samples, loop_start = 0, loop_end = 0, loop_flag; /* checks */ - if (!check_extensions(streamFile,"nop")) + if (!check_extensions(sf,"nop")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x73616466 || /* "sadf" */ - read_32bitBE(0x08, streamFile) != 0x6f707573) /* "opus" */ + if (read_32bitBE(0x00, sf) != 0x73616466 || /* "sadf" */ + read_32bitBE(0x08, sf) != 0x6f707573) /* "opus" */ goto fail; - offset = read_32bitLE(0x1c, streamFile); - num_samples = read_32bitLE(0x28, streamFile); - loop_flag = read_8bit(0x19, streamFile); + offset = read_32bitLE(0x1c, sf); + num_samples = read_32bitLE(0x28, sf); + loop_flag = read_8bit(0x19, sf); if (loop_flag) { - loop_start = read_32bitLE(0x2c, streamFile); - loop_end = read_32bitLE(0x30, streamFile); + loop_start = read_32bitLE(0x2c, sf); + loop_end = read_32bitLE(0x30, sf); } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: return NULL; } /* Shin'en variation [Fast RMX (Switch)] */ -VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf) { off_t offset = 0; int num_samples, loop_start, loop_end; /* checks */ - if ( !check_extensions(streamFile,"opus,lopus")) + if ( !check_extensions(sf,"opus,lopus")) goto fail; - if (read_32bitBE(0x08,streamFile) != 0x01000080) + if (read_32bitBE(0x08,sf) != 0x01000080) goto fail; offset = 0x08; num_samples = 0; - loop_start = read_32bitLE(0x00,streamFile); - loop_end = read_32bitLE(0x04,streamFile); /* 0 if no loop */ + loop_start = read_32bitLE(0x00,sf); + loop_end = read_32bitLE(0x04,sf); /* 0 if no loop */ if (loop_start > loop_end) goto fail; /* just in case */ - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: return NULL; } /* Bandai Namco Opus (found in NUS3Banks) [Taiko no Tatsujin: Nintendo Switch Version!] */ -VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf) { off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ /* .opus: header ID (they only exist inside .nus3bank) */ - if (!check_extensions(streamFile, "opus,lopus")) + if (!check_extensions(sf, "opus,lopus")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x4F505553) /* "OPUS" */ + if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */ goto fail; /* Here's an interesting quirk, OPUS header contains big endian values while the Nintendo Opus header and data that follows remain little endian as usual */ - offset = read_32bitBE(0x20, streamFile); - num_samples = read_32bitBE(0x08, streamFile); + offset = read_32bitBE(0x20, sf); + num_samples = read_32bitBE(0x08, sf); /* Check if there's a loop end value to determine loop_flag*/ - loop_flag = read_32bitBE(0x18, streamFile); + loop_flag = read_32bitBE(0x18, sf); if (loop_flag) { - loop_start = read_32bitBE(0x14, streamFile); - loop_end = read_32bitBE(0x18, streamFile); + loop_start = read_32bitBE(0x14, sf); + loop_end = read_32bitBE(0x18, sf); } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* Nippon Ichi SPS wrapper (non-segmented) [Ys VIII: Lacrimosa of Dana (Switch)] */ -VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf) { off_t offset; int num_samples, loop_start = 0, loop_end = 0, loop_flag; @@ -311,28 +311,28 @@ VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE *streamFile) { /* .sps: Labyrinth of Refrain: Coven of Dusk (Switch) * .nlsd: Disgaea Refine (Switch), Ys VIII (Switch) * .at9: void tRrLM(); //Void Terrarium (Switch) */ - if (!check_extensions(streamFile, "sps,nlsd,at9")) + if (!check_extensions(sf, "sps,nlsd,at9")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x09000000) /* file type (see other N1 SPS) */ + if (read_32bitBE(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */ goto fail; - num_samples = read_32bitLE(0x0C, streamFile); + num_samples = read_32bitLE(0x0C, sf); - if (read_32bitBE(0x1c, streamFile) == 0x01000080) { + if (read_32bitBE(0x1c, sf) == 0x01000080) { offset = 0x1C; /* older games loop section (remnant of segmented opus_sps_n1): */ - loop_start = read_32bitLE(0x10, streamFile); /* intro samples */ - loop_end = loop_start + read_32bitLE(0x14, streamFile); /* loop samples */ + loop_start = read_32bitLE(0x10, sf); /* intro samples */ + loop_end = loop_start + read_32bitLE(0x14, sf); /* loop samples */ /* 0x18: end samples (all must add up to num_samples) */ - loop_flag = read_32bitLE(0x18, streamFile); /* with loop disabled only loop_end has a value */ + loop_flag = read_32bitLE(0x18, sf); /* with loop disabled only loop_end has a value */ } else { offset = 0x18; /* newer games loop section: */ - loop_start = read_32bitLE(0x10, streamFile); - loop_end = read_32bitLE(0x14, streamFile); + loop_start = read_32bitLE(0x10, sf); + loop_end = read_32bitLE(0x14, sf); loop_flag = loop_start != loop_end; /* with loop disabled start and end are the same as num samples */ } @@ -341,29 +341,29 @@ VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE *streamFile) { loop_end = 0; } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */ -VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf) { off_t offset; int num_samples, loop_start = 0, loop_end = 0; float modifier; /* checks */ - if (!check_extensions(streamFile, "opusx")) + if (!check_extensions(sf, "opusx")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x4F505553) /* "OPUS" */ + if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */ goto fail; offset = 0x10; /* values are for the original 44100 files, but Opus resamples to 48000 */ modifier = 48000.0f / 44100.0f; - num_samples = 0;//read_32bitLE(0x04, streamFile) * modifier; /* better use calc'd num_samples */ - loop_start = read_32bitLE(0x08, streamFile) * modifier; - loop_end = read_32bitLE(0x0c, streamFile) * modifier; + num_samples = 0;//read_32bitLE(0x04, sf) * modifier; /* better use calc'd num_samples */ + loop_start = read_32bitLE(0x08, sf) * modifier; + loop_end = read_32bitLE(0x0c, sf) * modifier; /* resampling calcs are slighly off and may to over num_samples, but by removing delay seems ok */ if (loop_start >= 120) { @@ -374,81 +374,102 @@ VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE *streamFile) { loop_end = 0; } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* Prototype variation [Clannad (Switch)] */ -VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf) { off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ - if (!check_extensions(streamFile, "opus,lopus")) + if (!check_extensions(sf, "opus,lopus")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x4F505553 || /* "OPUS" */ - read_32bitBE(0x18, streamFile) != 0x01000080) + if (read_32bitBE(0x00, sf) != 0x4F505553 || /* "OPUS" */ + read_32bitBE(0x18, sf) != 0x01000080) goto fail; offset = 0x18; - num_samples = read_32bitLE(0x08, streamFile); + num_samples = read_32bitLE(0x08, sf); /* Check if there's a loop end value to determine loop_flag*/ - loop_flag = read_32bitLE(0x10, streamFile); + loop_flag = read_32bitLE(0x10, sf); if (loop_flag) { - loop_start = read_32bitLE(0x0C, streamFile); - loop_end = read_32bitLE(0x10, streamFile); + loop_start = read_32bitLE(0x0C, sf); + loop_end = read_32bitLE(0x10, sf); } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* Edelweiss variation [Astebreed (Switch)] */ -VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf) { off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0; /* checks */ - if (!check_extensions(streamFile, "opus,lopus")) + if (!check_extensions(sf, "opus,lopus")) goto fail; - if (read_64bitBE(0x00, streamFile) != 0x4F5055534E580000) /* "OPUSNX\0\0" */ + if (read_64bitBE(0x00, sf) != 0x4F5055534E580000) /* "OPUSNX\0\0" */ goto fail; offset = 0x10; - num_samples = 0; //read_32bitLE(0x08, streamFile); /* samples with encoder delay */ - if (read_32bitLE(0x0c, streamFile) != 0) + num_samples = 0; //read_32bitLE(0x08, sf); /* samples with encoder delay */ + if (read_32bitLE(0x0c, sf) != 0) goto fail; - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); +fail: + return NULL; +} + +/* Edelweiss variation [Sakuna: Of Rice and Ruin (Switch)] */ +VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf) { + off_t offset = 0; + int num_samples = 0, loop_start = 0, loop_end = 0; + + /* checks */ + if (!check_extensions(sf, "nsopus")) + goto fail; + if (read_u32be(0x00, sf) != 0x45574E4F) /* "EWNO" */ + goto fail; + + offset = 0x08; + num_samples = 0; //read_32bitLE(0x08, sf); /* samples without encoder delay? (lower than count) */ + + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* Square Enix variation [Dragon Quest I-III (Switch)] */ -VGMSTREAM * init_vgmstream_opus_sqex(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf) { off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; - + /* checks */ - if (!check_extensions(streamFile, "opus,lopus")) + /* .wav: default + * .opus: fake? */ + if (!check_extensions(sf, "wav,lwav,opus,lopus")) goto fail; - if (read_64bitBE(0x00, streamFile) != 0x0100000002000000) + if (read_u32be(0x00, sf) != 0x01000000) goto fail; - - offset = read_32bitLE(0x0C, streamFile); - num_samples = read_32bitLE(0x1C, streamFile); - - /* Check if there's a loop end value to determine loop_flag*/ - loop_flag = read_32bitLE(0x18, streamFile); + /* 0x04: channels */ + /* 0x08: data_size */ + offset = read_32bitLE(0x0C, sf); + num_samples = read_32bitLE(0x1C, sf); + + loop_flag = read_32bitLE(0x18, sf); if (loop_flag) { - loop_start = read_32bitLE(0x14, streamFile); - loop_end = read_32bitLE(0x18, streamFile); + loop_start = read_32bitLE(0x14, sf); + loop_end = read_32bitLE(0x18, sf); } - - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c b/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c deleted file mode 100644 index 0ff9a26ae..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" -#include "../layout/layout.h" - -/* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch)] */ -VGMSTREAM * init_vgmstream_opus_sps_n1_segmented(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t segment_offset; - int loop_flag, channel_count; - int i; - - segmented_layout_data *data = NULL; - int segment_count, loop_start_segment = 0, loop_end_segment = 0; - int num_samples = 0, loop_start_sample = 0, loop_end_sample = 0; - - - /* checks */ - if (!check_extensions(streamFile, "at9")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x09000000) /* file type (see other N1 SPS) */ - goto fail; - if (read_32bitLE(0x04,streamFile) + 0x1c != get_streamfile_size(streamFile)) - goto fail; - /* 0x08(2): sample rate, 0x0a(2): flag?, 0x0c: num_samples (slightly smaller than added samples) */ - - segment_count = 3; /* intro/loop/end */ - loop_start_segment = 1; - loop_end_segment = 1; - loop_flag = (segment_count > 0); - - /* init layout */ - data = init_layout_segmented(segment_count); - if (!data) goto fail; - - /* open each segment subfile */ - segment_offset = 0x1c; - for (i = 0; i < segment_count; i++) { - STREAMFILE* temp_streamFile; - size_t segment_size = read_32bitLE(0x10+0x04*i,streamFile); - - if (!segment_size) - goto fail; - - temp_streamFile = setup_subfile_streamfile(streamFile, segment_offset,segment_size, "opus"); - if (!temp_streamFile) goto fail; - - data->segments[i] = init_vgmstream_opus_std(temp_streamFile); - close_streamfile(temp_streamFile); - if (!data->segments[i]) goto fail; - - segment_offset += segment_size; - - //todo there are some trailing samples that must be removed for smooth loops, start skip seems ok - data->segments[i]->num_samples -= 374; //not correct for all files, no idea how to calculate - - /* get looping and samples */ - if (loop_flag && loop_start_segment == i) - loop_start_sample = num_samples; - - num_samples += data->segments[i]->num_samples; - - if (loop_flag && loop_end_segment == i) - loop_end_sample = num_samples; - } - - /* setup segmented VGMSTREAMs */ - if (!setup_layout_segmented(data)) - goto fail; - - - channel_count = data->segments[0]->channels; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); - vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start_sample; - vgmstream->loop_end_sample = loop_end_sample; - - vgmstream->meta_type = meta_OPUS_PPP; - vgmstream->coding_type = data->segments[0]->coding_type; - vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data; - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - free_layout_segmented(data); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth.c index c3e6eb19c..4501a1f54 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth.c @@ -1,94 +1,92 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +#include "ps2_enth_streamfile.h" -/* ENTH (from Enthusia - Professional Racing) */ -VGMSTREAM * init_vgmstream_ps2_enth(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; +/* LP/AP/LEP - from Konami (KCES)'s Enthusia: Professional Racing (PS2) */ +VGMSTREAM* init_vgmstream_ps2_enth(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t start_offset; - int header_check; - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("enth",filename_extension(filename))) goto fail; - - /* check header and loop_flag */ - header_check = read_32bitBE(0x00,streamFile); - switch (header_check) { - case 0x41502020: /* AP */ - loop_flag = (read_32bitLE(0x14,streamFile)!=0); - break; - case 0x4C455020: /* LEP */ - loop_flag = (read_32bitLE(0x58,streamFile)!=0); - break; - default: - goto fail; - } - - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - header_check = read_32bitBE(0x00,streamFile); - - switch (header_check) { - case 0x41502020: /* AP */ - start_offset = read_32bitLE(0x1C,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x18,streamFile))*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x14,streamFile))*28/16/channel_count; - vgmstream->loop_end_sample = (read_32bitLE(0x18,streamFile))*28/16/channel_count; - } - vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile); - break; - case 0x4C455020: /* LEP */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x12,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x08,streamFile))*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x58,streamFile))*28/16/channel_count; - vgmstream->loop_end_sample = (read_32bitLE(0x08,streamFile))*28/16/channel_count; - } - vgmstream->interleave_block_size = 0x10; - break; - default: - goto fail; -} + int loop_flag, channels, sample_rate, interleave; + int32_t data_size, loop_start; + uint32_t id; - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_PS2_ENTH; + /* checks */ + /* .bin/lbin: internal (no names in bigfiles but exes mention "bgm%05d.bin" and "LEP data") + * .lp/lep/ap: header ID */ + if (!check_extensions(sf, "bin,lbin,lp,lep,ap")) + goto fail; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; + id = read_u32be(0x00,sf); + switch (id) { + case 0x41502020: /* "AP " */ + case 0x4C502020: /* "LP " */ + sample_rate = read_u32le(0x08,sf); + interleave = read_u32le(0x0c,sf); + loop_start = read_u32le(0x14,sf); + data_size = read_u32le(0x18,sf); + start_offset = read_u32le(0x1C,sf); + break; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; + case 0x4C455020: /* "LEP " */ + data_size = read_u32le(0x08,sf); + sample_rate = read_u16le(0x12,sf); + loop_start = read_u32le(0x58,sf); + interleave = 0x10; + start_offset = 0x800; + break; - } + default: + goto fail; } - return vgmstream; + loop_flag = loop_start != 0; + channels = 2; - /* clean up anything we may have opened */ + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PS2_ENTH; + vgmstream->sample_rate = sample_rate; + + switch (id) { + case 0x4C502020: /* "LP " */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 16); + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channels, 16); + vgmstream->loop_end_sample = vgmstream->num_samples; + + temp_sf = setup_lp_streamfile(sf, start_offset); /* encrypted/obfuscated PCM */ + if (!temp_sf) goto fail; + break; + + case 0x41502020: /* "AP " */ + case 0x4C455020: /* "LEP " */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels); + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, temp_sf ? temp_sf : sf, start_offset)) + goto fail; + close_streamfile(temp_sf); + return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_sf); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth_streamfile.h new file mode 100644 index 000000000..5cac41312 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth_streamfile.h @@ -0,0 +1,39 @@ +#ifndef _LP_STREAMFILE_H_ +#define _LP_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + off_t start; +} lp_io_data; + +static size_t lp_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, lp_io_data* data) { + int i; + size_t bytes = read_streamfile(dest, offset, length, sf); + + /* PCM16LE frames are ROL'ed (or lower bit is ignored?) so this only works if reads are aligned + * to sizeof(uint16_t); maybe could be a new decoder but seems like a waste */ + for (i = 0; i < bytes / 2 * 2; i += 2) { + if (offset + i >= data->start) { + uint16_t v = get_u16le(dest + i); + v = (v << 1) | ((v >> 15) & 0x0001); + put_u16le(dest + i, v); + } + } + + return bytes; +} + +/* decrypts Enthusia "LP" PCM streams */ +static STREAMFILE* setup_lp_streamfile(STREAMFILE* sf, off_t start) { + STREAMFILE* new_sf = NULL; + lp_io_data io_data = {0}; + + io_data.start = start; + + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(lp_io_data), lp_io_read, NULL); + return new_sf; +} + +#endif /* _LP_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index e8dea9b41..9b488c2d9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -412,6 +412,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { else if (codec == 0x0011 && (riff_size / 2 / 2 == read_32bitLE(0x30,sf))) /* riff_size = pcm_size (always stereo, has fact at 0x30) */ riff_size = file_size - 0x08; /* [Asphalt 6 (iOS)] (sfx/memory wavs have ok sizes?) */ + + else if (codec == 0xFFFE && riff_size + 0x08 + 0x30 == file_size) + riff_size += 0x30; /* [E.X. Troopers (PS3)] (adds "ver /eBIT/tIME/mrkr" empty chunks but RIFF size wasn't updated) */ } /* check for truncated RIFF */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c index 99bd6f95f..50f7a4c37 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c @@ -3,7 +3,7 @@ /* RSD - from Radical Entertainment games */ -VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_rsd(STREAMFILE *sf) { VGMSTREAM * vgmstream = NULL; off_t start_offset, name_offset; size_t data_size; @@ -13,24 +13,24 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile,"rsd,rsp")) + if (!check_extensions(sf,"rsd,rsp")) goto fail; - if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */ + if ((read_32bitBE(0x00,sf) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */ goto fail; loop_flag = 0; - codec = (uint32_t)read_32bitBE(0x04,streamFile); - channel_count = read_32bitLE(0x08, streamFile); + codec = (uint32_t)read_32bitBE(0x04,sf); + channel_count = read_32bitLE(0x08, sf); /* 0x0c: always 16? */ - sample_rate = read_32bitLE(0x10, streamFile); + sample_rate = read_32bitLE(0x10, sf); - version = read_8bit(0x03, streamFile); + version = read_8bit(0x03, sf); switch(version) { case '2': /* known codecs: VAG/XADP/PCMB [The Simpsons: Road Rage] */ case '3': /* known codecs: VAG/PCM/PCMB/GADP? [Dark Summit] */ - interleave = read_32bitLE(0x14,streamFile); /* VAG only, 0x04 otherwise */ - start_offset = read_32bitLE(0x18,streamFile); + interleave = read_32bitLE(0x14,sf); /* VAG only, 0x04 otherwise */ + start_offset = read_32bitLE(0x18,sf); name_offset = 0; break; @@ -43,7 +43,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { /* PCMB/PCM/GADP normally start early but sometimes have padding [The Simpsons: Hit & Run (GC/Xbox)] */ if ((codec == 0x50434D20 || codec == 0x550434D42 || codec == 0x47414450) - && read_32bitLE(0x80,streamFile) != 0x2D2D2D2D) + && read_32bitLE(0x80,sf) != 0x2D2D2D2D) start_offset = 0x80; break; @@ -58,7 +58,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { goto fail; } - data_size = get_streamfile_size(streamFile) - start_offset; + data_size = get_streamfile_size(sf) - start_offset; /* build the VGMSTREAM */ @@ -104,8 +104,8 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x08; /* assumed, known files are mono */ - dsp_read_coefs_le(vgmstream,streamFile,0x14,0x2e); /* LE! */ - dsp_read_hist_le (vgmstream,streamFile,0x38,0x2e); + dsp_read_coefs_le(vgmstream,sf,0x14,0x2e); /* LE! */ + dsp_read_hist_le (vgmstream,sf,0x38,0x2e); vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); break; @@ -114,8 +114,8 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP_subint; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = 0x02; - dsp_read_coefs_be(vgmstream,streamFile,0x1a4,0x28); - dsp_read_hist_be (vgmstream,streamFile,0x1c8,0x28); + dsp_read_coefs_be(vgmstream,sf,0x1a4,0x28); + dsp_read_hist_be (vgmstream,sf,0x1c8,0x28); vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); break; @@ -134,7 +134,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { ovmi.meta_type = meta_RSD; close_vgmstream(vgmstream); - vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi); if (!vgmstream) goto fail; break; } @@ -145,22 +145,22 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { ffmpeg_codec_data *ffmpeg_data = NULL; /* mini header + WMA header at start_offset */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset+0x08,data_size); + ffmpeg_data = init_ffmpeg_offset(sf, start_offset+0x08,data_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */ - //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile) / channel_count / 2; /* may be PCM data size, but not exact */ - vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, streamFile); + //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf) / channel_count / 2; /* may be PCM data size, but not exact */ + vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, sf); break; } case 0x4154332B: { /* "AT3+" [Crash of the Titans (PSP)] */ int fact_samples = 0; - vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, &fact_samples); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, &fact_samples); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -177,21 +177,21 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { /* skip mini header */ - start_offset = read_32bitBE(0x800, streamFile) + read_32bitBE(0x804, streamFile) + 0xc; /* assumed, seek table always at 0x800 */ - xma_size = read_32bitBE(0x808, streamFile); - xma_version = read_32bitBE(0x80C, streamFile); + start_offset = 0x800 + read_32bitBE(0x800, sf) + read_32bitBE(0x804, sf) + 0xc; /* assumed, seek table always at 0x800 */ + xma_size = read_32bitBE(0x808, sf); + xma_version = read_32bitBE(0x80C, sf); switch (xma_version) { case 0x03010000: - vgmstream->sample_rate = read_32bitBE(0x818, streamFile); - vgmstream->num_samples = read_32bitBE(0x824, streamFile); - block_count = read_32bitBE(0x828, streamFile); + vgmstream->sample_rate = read_32bitBE(0x818, sf); + vgmstream->num_samples = read_32bitBE(0x824, sf); + block_count = read_32bitBE(0x828, sf); block_size = 0x10000; break; case 0x04010000: - vgmstream->num_samples = read_32bitBE(0x814, streamFile); - vgmstream->sample_rate = read_32bitBE(0x818, streamFile); - block_count = read_32bitBE(0x830, streamFile); + vgmstream->num_samples = read_32bitBE(0x814, sf); + vgmstream->sample_rate = read_32bitBE(0x818, sf); + block_count = read_32bitBE(0x830, sf); block_size = 0x10000; break; default: @@ -199,14 +199,14 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { } bytes = ffmpeg_make_riff_xma2(buf,sizeof(buf), vgmstream->num_samples, xma_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, xma_size); + ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, xma_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; /* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */ - //xma_fix_raw_samples(vgmstream, streamFile, start_offset,xma_size, 0, 0,0); + //xma_fix_raw_samples(vgmstream, sf, start_offset,xma_size, 0, 0,0); ffmpeg_set_skip_samples(ffmpeg_data, 512+64); break; } @@ -217,9 +217,9 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { } if (name_offset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf); - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c index d75101bac..8e26967cb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c @@ -1,54 +1,179 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* .SPS - Nippon Ichi wrapper [ClaDun (PSP)] */ -VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; - int type, sample_rate; - off_t subfile_offset; - size_t subfile_size; - - /* check extensions */ - if ( !check_extensions(streamFile,"sps")) - goto fail; - - /* mini header */ - type = read_32bitLE(0x00,streamFile); - subfile_size = read_32bitLE(0x04,streamFile); - sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); - /* 0x0a: flag? */ - //num_samples = read_32bitLE(0x0c,streamFile); - subfile_offset = 0x10; - - /* init the VGMSTREAM */ - switch(type) { - case 1: /* .vag */ - temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "vag"); - if (!temp_streamFile) goto fail; - - vgmstream = init_vgmstream_vag(temp_streamFile); - if (!vgmstream) goto fail; - break; - - case 2: /* .at3 */ - temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "at3"); - if (!temp_streamFile) goto fail; - - vgmstream = init_vgmstream_riff(temp_streamFile); - if (!vgmstream) goto fail; - break; - default: - goto fail; - } - - vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */ - - close_streamfile(temp_streamFile); - return vgmstream; - -fail: - close_streamfile(temp_streamFile); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +/* also see init_vgmstream_dsp_sps_n1 and init_vgmstream_opus_sps_n1 */ + +/* Nippon Ichi SPS wrapper [ClaDun (PSP)] */ +VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + int type, sample_rate; + off_t subfile_offset; + size_t subfile_size; + + VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL; + const char* extension; + + + /* checks */ + if (!check_extensions(sf,"sps")) + goto fail; + + type = read_u32le(0x00,sf); + subfile_size = read_u32le(0x04,sf); + sample_rate = read_u16le(0x08,sf); + /* 0x0a: flag? (stereo?) */ + /* 0x0b: flag? */ + /* 0x0c: num_samples */ + + switch(type) { + case 1: + init_vgmstream_subfile = init_vgmstream_vag; + extension = "vag"; + break; + + case 2: + init_vgmstream_subfile = init_vgmstream_riff; + extension = "at3"; + break; + + default: + goto fail; + } + + subfile_offset = 0x10; + if (subfile_size + subfile_offset != get_streamfile_size(sf)) + goto fail; + + /* init the VGMSTREAM */ + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_subfile(temp_sf); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */ + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + +/* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch), Disgaea 4 Complete (PC)] */ +VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t segment_offset; + size_t data_size, max_size; + int loop_flag, type, sample_rate; + int i, segment; + + VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL; + const char* extension; + segmented_layout_data* data = NULL; + int segment_count, loop_start_segment, loop_end_segment; + + + /* checks */ + /* .at9: Penny-Punching Princess (Switch) + * .nlsd: Disgaea 4 Complete (PC) */ + if (!check_extensions(sf, "at9,nlsd")) + goto fail; + + type = read_u32le(0x00,sf); + data_size = read_u32le(0x04,sf); + sample_rate = read_u16le(0x08,sf); + /* 0x0a: flag? (stereo?) */ + /* 0x0b: flag? */ + /* 0x0c: num_samples (slightly smaller than added samples?) */ + + switch(type) { + #ifdef VGM_USE_VORBIS + case 7: + init_vgmstream_subfile = init_vgmstream_ogg_vorbis; + extension = "ogg"; + break; + #endif + + case 9: + init_vgmstream_subfile = init_vgmstream_opus_std; + extension = "opus"; + break; + + default: + goto fail; + } + + segment_offset = 0x1c; + if (data_size + segment_offset != get_streamfile_size(sf)) + goto fail; + + /* segmented using 3 files (intro/loop/outro). non-segmented wrapper is the same + * but with loop samples instead of sub-sizes */ + max_size = 0; + segment_count = 0; + for (i = 0; i < 3; i++) { + size_t segment_size = read_u32le(0x10 + 0x04*i,sf); + max_size += segment_size; + /* may only set 1 segment (Disgaea4's bgm_185) */ + if (segment_size) + segment_count++; + } + if (data_size != max_size) + goto fail; + + loop_flag = segment_count > 1; /* intro+loop section must exit */ + loop_start_segment = 1; + loop_end_segment = 1; + + /* init layout */ + data = init_layout_segmented(segment_count); + if (!data) goto fail; + + /* open each segment subfile */ + segment = 0; + for (i = 0; i < 3; i++) { + STREAMFILE* temp_sf; + size_t segment_size = read_u32le(0x10 + 0x04*i,sf); + + if (!segment_size) + continue; + + temp_sf = setup_subfile_streamfile(sf, segment_offset,segment_size, extension); + if (!temp_sf) goto fail; + + data->segments[segment] = init_vgmstream_subfile(temp_sf); + close_streamfile(temp_sf); + if (!data->segments[segment]) goto fail; + + segment_offset += segment_size; + segment++; + + if (type == 9) { + //todo there are some trailing samples that must be removed for smooth loops, start skip seems ok + //not correct for all files, no idea how to calculate + data->segments[segment]->num_samples -= 374; + } + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + vgmstream = allocate_segmented_vgmstream(data, loop_flag, loop_start_segment, loop_end_segment); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_SPS_N1; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + free_layout_segmented(data); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/swav.c b/Frameworks/vgmstream/vgmstream/src/meta/swav.c index 0e8b33532..ba1c5eaf0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/swav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/swav.c @@ -2,7 +2,7 @@ #include "../util.h" -/* SWAV - from Gameloft(?) games [Asphalt Urban GT (DS), Asphalt Urban GT 2 (DS)] */ +/* SWAV - wave files generated by the DS SDK */ VGMSTREAM* init_vgmstream_swav(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; int channel_count, loop_flag; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c index 05dd29a4f..d1c707d37 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c @@ -1,306 +1,170 @@ #include "meta.h" #include "../coding/coding.h" -/* AAC - tri-Ace (Aska engine) Audio Container */ +typedef struct { + int total_subsongs; + int codec; + int channels; + int sample_rate; -/* Xbox 360 Variants (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ -VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - size_t sampleRate, numSamples, startSample, dataSize, blockSize, blockCount; // A mess + int block_count; + int block_size; - /* check extension, case insensitive */ - /* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */ - if ( !check_extensions(streamFile,"aac,laac,ace")) + int32_t num_samples; + int32_t loop_start; + int32_t loop_end; + int loop_flag; + + off_t stream_offset; + off_t stream_size; + off_t extra_offset; + + off_t name_offset; +} aac_header; + +static int parse_aac(STREAMFILE* sf, aac_header* aac); + + +/* AAC - tri-Ace (ASKA engine) Audio Container */ +VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + aac_header aac = {0}; + + + /* checks */ + /* .aac: actual extension, .laac: for players to avoid hijacking MP4/AAC */ + if (!check_extensions(sf, "aac,laac")) + goto fail; + if (read_u32be(0x00, sf) != 0x41414320 && + read_u32le(0x00, sf) != 0x41414320) /* "AAC " */ goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41414320) /* "AAC " */ + if (!parse_aac(sf, &aac)) goto fail; - /* Ok, let's check what's behind door number 1 */ - if (read_32bitBE(0x1000, streamFile) == 0x41534320) /* "ASC " */ - { - loop_flag = read_32bitBE(0x1118, streamFile); - - /*Funky Channel Count Checking */ - if (read_32bitBE(0x1184, streamFile) == 0x7374726D) - channel_count = 6; - else if (read_32bitBE(0x1154, streamFile) == 0x7374726D) - channel_count = 4; - else - channel_count = read_8bit(0x1134, streamFile); - - sampleRate = read_32bitBE(0x10F4, streamFile); - numSamples = read_32bitBE(0x10FC, streamFile); - startSample = read_32bitBE(0x10F8, streamFile); - dataSize = read_32bitBE(0x10F0, streamFile); - blockSize = read_32bitBE(0x1100, streamFile); - blockCount = read_32bitBE(0x110C, streamFile); - } - else if (read_32bitBE(0x1000, streamFile) == 0x57415645) /* "WAVE" */ - { - loop_flag = read_32bitBE(0x1048, streamFile); - - /*Funky Channel Count Checking */ - if (read_32bitBE(0x10B0, streamFile) == 0x7374726D) - channel_count = 6; - else if (read_32bitBE(0x1080, streamFile) == 0x7374726D) - channel_count = 4; - else - channel_count = read_8bit(0x1060, streamFile); - - sampleRate = read_32bitBE(0x1024, streamFile); - numSamples = read_32bitBE(0x102C, streamFile); - startSample = read_32bitBE(0x1028, streamFile); - dataSize = read_32bitBE(0x1020, streamFile); - blockSize = read_32bitBE(0x1030, streamFile); - blockCount = read_32bitBE(0x103C, streamFile); - } - else if (read_32bitBE(0x1000, streamFile) == 0x00000000) /* some like to be special */ - { - loop_flag = read_32bitBE(0x6048, streamFile); - - /*Funky Channel Count Checking */ - if (read_32bitBE(0x60B0, streamFile) == 0x7374726D) - channel_count = 6; - else if (read_32bitBE(0x6080, streamFile) == 0x7374726D) - channel_count = 4; - else - channel_count = read_8bit(0x6060, streamFile); - - sampleRate = read_32bitBE(0x6024, streamFile); - numSamples = read_32bitBE(0x602C, streamFile); - startSample = read_32bitBE(0x6028, streamFile); - dataSize = read_32bitBE(0x6020, streamFile); - blockSize = read_32bitBE(0x6030, streamFile); - blockCount = read_32bitBE(0x603C, streamFile); - } - else - goto fail; //cuz I don't know if there are other variants - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - if (read_32bitBE(0x1000, streamFile) == 0x00000000) - start_offset = 0x7000; - else - start_offset = 0x2000; - - vgmstream->sample_rate = sampleRate; - vgmstream->channels = channel_count; - vgmstream->num_samples = numSamples; - if (loop_flag) { - vgmstream->loop_start_sample = startSample; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - vgmstream->meta_type = meta_TA_AAC_X360; - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - size_t bytes, datasize, block_size, block_count; - - block_count = blockCount; - block_size = blockSize; - datasize = dataSize; - - bytes = ffmpeg_make_riff_xma2(buf,100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples(vgmstream, streamFile, start_offset, datasize, 0, 1,1); - if (loop_flag) { /* reapply adjusted samples */ - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - } -#else - goto fail; -#endif - - /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - -/* PlayStation 3 Variants (Star Ocean International, Resonance of Fate) */ -VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - uint32_t data_size, loop_start, loop_end, codec_id, asc_chunk; - - /* check extension, case insensitive */ - /* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */ - if (!check_extensions(streamFile, "aac,laac,ace")) - goto fail; - - if (read_32bitBE(0x00, streamFile) != 0x41414320) /* "AAC " */ - goto fail; - - /* Find the ASC chunk, That's where the goodies are */ - asc_chunk = read_32bitBE(0x40, streamFile); - if (read_32bitBE(asc_chunk, streamFile) != 0x41534320) /* "ASC " */ - goto fail; - - if (read_32bitBE(asc_chunk+0x104, streamFile) != 0xFFFFFFFF) - loop_flag = 1; - else - loop_flag = 0; - - channel_count = read_32bitBE(asc_chunk + 0xF4, streamFile); - codec_id = read_32bitBE(asc_chunk + 0xF0, streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - /* ASC header */ - start_offset = asc_chunk + 0x110; - vgmstream->sample_rate = read_32bitBE(asc_chunk + 0xFC, streamFile); - vgmstream->channels = channel_count; - vgmstream->meta_type = meta_TA_AAC_PS3; - data_size = read_32bitBE(asc_chunk + 0xF8, streamFile); - loop_start = read_32bitBE(asc_chunk + 0x104, streamFile); - loop_end = read_32bitBE(asc_chunk + 0x108, streamFile); - -#ifdef VGM_USE_FFMPEG - { - int block_align, encoder_delay; - - block_align = (codec_id == 4 ? 0x60 : (codec_id == 5 ? 0x98 : 0xC0)) * vgmstream->channels; - encoder_delay = 1024 + 69; /* approximate, gets good loops */ - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; - - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ - vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); // - encoder_delay - vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; - } -#endif - - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - -/* Android/iOS Variants (Star Ocean Anamnesis (APK v1.9.2), Heaven x Inferno (iOS)) */ -VGMSTREAM * init_vgmstream_ta_aac_mobile_vorbis(STREAMFILE *streamFile) { -#ifdef VGM_USE_VORBIS - off_t start_offset; - int8_t codec_id; - - /* check extension, case insensitive */ - /* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */ - if (!check_extensions(streamFile, "aac,laac,ace")) - goto fail; - - if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ - goto fail; - - if (read_32bitLE(0xf0, streamFile) != 0x57415645) /* "WAVE" */ - goto fail; - - codec_id = read_8bit(0x104, streamFile); - if (codec_id == 0xe) /* Vorbis */ - { - ogg_vorbis_meta_info_t ovmi = {0}; - VGMSTREAM * result = NULL; - - ovmi.meta_type = meta_TA_AAC_MOBILE; - ovmi.loop_start = read_32bitLE(0x140, streamFile); - ovmi.loop_end = read_32bitLE(0x144, streamFile); - ovmi.loop_flag = ovmi.loop_end > ovmi.loop_start; - ovmi.loop_end_found = ovmi.loop_flag; - - start_offset = read_32bitLE(0x120, streamFile); - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); - - if (result != NULL) { - return result; - } - } - -fail: - /* clean up anything we may have opened */ -#endif - return NULL; -} - -/* Android/iOS Variants, before they switched to Vorbis (Star Ocean Anamnesis (Android), Heaven x Inferno (iOS)) */ -VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int channel_count, loop_flag, codec; - size_t data_size; - - - /* check extension, case insensitive */ - /* .aac: expected, .laac: for players to avoid hijacking MP4/AAC */ - if (!check_extensions(streamFile, "aac,laac")) - goto fail; - - if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ - goto fail; - - if (read_32bitLE(0xf0, streamFile) != 0x57415645) /* "WAVE" */ - goto fail; - - codec = read_8bit(0x104, streamFile); - channel_count = read_8bit(0x105, streamFile); - /* 0x106: 0x01?, 0x107: 0x10? */ - data_size = read_32bitLE(0x10c, streamFile); /* usable data only, cuts last frame */ - start_offset = read_32bitLE(0x120, streamFile); - /* 0x124: full data size */ - loop_flag = (read_32bitLE(0x134, streamFile) > 0); - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); + vgmstream = allocate_vgmstream(aac.channels, aac.loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x108, streamFile); - vgmstream->meta_type = meta_TA_AAC_MOBILE; + vgmstream->meta_type = meta_TA_AAC; + vgmstream->sample_rate = aac.sample_rate; + vgmstream->num_streams = aac.total_subsongs; + vgmstream->stream_size = aac.stream_size; - switch(codec) { - case 0x0d: - if (read_32bitLE(0x144, streamFile) != 0x40) goto fail; /* frame size */ - /* 0x148 or 0x150 (later games): frame samples */ - if (channel_count > 2) goto fail; /* unknown data layout */ + switch(aac.codec) { + +#ifdef VGM_USE_FFMPEG + case 0x0165: { /* Infinite Undiscovery (X360), Star Ocean 4 (X360), Resonance of Fate (X360) */ + uint8_t buf[0x100]; + size_t bytes; + + bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), aac.num_samples, aac.stream_size, aac.channels, aac.sample_rate, aac.block_count, aac.block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, aac.stream_offset, aac.stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = aac.num_samples; + vgmstream->loop_start_sample = aac.loop_start; + vgmstream->loop_end_sample = aac.loop_end; + + xma_fix_raw_samples(vgmstream, sf, aac.stream_offset, aac.stream_size, 0, 0,1); + break; + } + + case 0x04: + case 0x05: + case 0x06: { /* Resonance of Fate (PS3), Star Ocean 4 (PS3) */ + int block_align = (aac.codec == 0x04 ? 0x60 : (aac.codec == 0x05 ? 0x98 : 0xC0)) * aac.channels; + int encoder_delay = 1024 + 69; /* AT3 default, gets good loops */ + + vgmstream->num_samples = atrac3_bytes_to_samples(aac.stream_size, block_align) - encoder_delay; + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(aac.loop_start, block_align); // - encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(aac.loop_end, block_align) - encoder_delay; + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, aac.stream_offset, aac.stream_size, vgmstream->num_samples, vgmstream->channels, vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif +#ifdef VGM_USE_ATRAC9 + case 0x08: { /* Judas Code (Vita) */ + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.encoder_delay = read_s32le(aac.extra_offset + 0x04,sf); + cfg.config_data = read_u32be(aac.extra_offset + 0x08,sf); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = atrac9_bytes_to_samples(aac.stream_size, vgmstream->codec_data); + vgmstream->num_samples -= cfg.encoder_delay; + vgmstream->loop_start_sample = atrac9_bytes_to_samples(aac.loop_start, vgmstream->codec_data); + vgmstream->loop_end_sample = atrac9_bytes_to_samples(aac.loop_end, vgmstream->codec_data); + break; + } +#endif + case 0x0a: /* Star Ocean 4 (PC) */ + if (aac.channels > 2) goto fail; /* unknown data layout */ + /* 0x00: some value * channels? */ + /* 0x04: frame size */ + /* 0x08: frame samples */ + + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->frame_size = read_u32le(aac.extra_offset + 0x04,sf); + + vgmstream->num_samples = msadpcm_bytes_to_samples(aac.stream_size, vgmstream->frame_size, aac.channels); + vgmstream->loop_start_sample = msadpcm_bytes_to_samples(aac.loop_start, vgmstream->frame_size, aac.channels); + vgmstream->loop_end_sample = msadpcm_bytes_to_samples(aac.loop_end, vgmstream->frame_size, aac.channels); + break; + + case 0x0d: /* Star Ocean Anamnesis (Android), Heaven x Inferno (iOS), Star Ocean 4 (PC), Resonance of Fate (PC) */ + /* 0x00: 0x17700 * channels? */ + /* 0x04: frame size */ + /* 0x08: frame samples (not always?) */ vgmstream->coding_type = coding_ASKA; vgmstream->layout_type = layout_none; + vgmstream->frame_size = read_u32le(aac.extra_offset + 0x04,sf); /* usually 0x40, rarely 0x20/C0 (ex. some ROF PC) */ + /* N-channel frames are allowed (ex. 4/6ch in SO4/ROF PC) */ + if (vgmstream->frame_size > 0xc0) /* known max */ + goto fail; - vgmstream->num_samples = aska_bytes_to_samples(data_size, channel_count); - vgmstream->loop_start_sample = aska_bytes_to_samples(read_32bitLE(0x130, streamFile), channel_count); - vgmstream->loop_end_sample = aska_bytes_to_samples(read_32bitLE(0x134, streamFile), channel_count); + vgmstream->num_samples = aska_bytes_to_samples(aac.stream_size, vgmstream->frame_size, aac.channels); + vgmstream->loop_start_sample = aska_bytes_to_samples(aac.loop_start, vgmstream->frame_size, aac.channels); + vgmstream->loop_end_sample = aska_bytes_to_samples(aac.loop_end, vgmstream->frame_size, aac.channels); break; +#ifdef VGM_USE_VORBIS + case 0x0e: { /* Star Ocean Anamnesis (Android-v1.9.2), Heaven x Inferno (iOS) */ + vgmstream->codec_data = init_ogg_vorbis(sf, aac.stream_offset, aac.stream_size, NULL); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = aac.num_samples; + vgmstream->loop_start_sample = read_s32le(aac.extra_offset + 0x00,sf); + vgmstream->loop_end_sample = read_s32le(aac.extra_offset + 0x04,sf); + /* seek table after loops */ + break; + } +#endif default: goto fail; } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (aac.name_offset) + read_string(vgmstream->stream_name, STREAM_NAME_SIZE, aac.name_offset, sf); + + if (!vgmstream_open_stream(vgmstream, sf, aac.stream_offset)) goto fail; return vgmstream; @@ -309,65 +173,334 @@ fail: return NULL; } -/* Vita variants [Judas Code (Vita)] */ -VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int channel_count, loop_flag; +/* DIR/dirn + WAVE chunk [Infinite Undiscovery (X360), Star Ocean 4 (X360)] */ +static int parse_aac_v1(STREAMFILE* sf, aac_header* aac) { + off_t offset, test_offset, wave_offset; + int target_subsong = sf->stream_index; - /* check extension, case insensitive */ - /* .aac: expected, .laac: for players to avoid hijacking MP4/AAC */ - if (!check_extensions(streamFile, "aac,laac")) + /* base header */ + /* 0x00: id */ + /* 0x04: size */ + /* 0x10: subsongs */ + /* 0x14: base size */ + /* 0x14: head size */ + /* 0x18: data size */ + /* 0x20: config? (0x00010003) */ + /* 0x30+ DIR + dirn subsongs */ + + if (read_u32be(0x30, sf) != 0x44495220) /* "DIR " */ goto fail; + aac->total_subsongs = read_u32be(0x40, sf); - if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ - goto fail; - if (read_32bitLE(0x14, streamFile) != 0x56495441) /* "VITA" */ - goto fail; - if (read_32bitLE(0x10d0, streamFile) != 0x57415645) /* "WAVE" */ - goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail; - /* there is a bunch of chunks but we simplify */ - - /* 0x10E4: codec 0x08? */ - channel_count = read_8bit(0x10E5, streamFile); - start_offset = read_32bitLE(0x1100, streamFile); - loop_flag = (read_32bitLE(0x1114, streamFile) > 0); - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitLE(0x10e8, streamFile); - vgmstream->meta_type = meta_TA_AAC_VITA; - -#ifdef VGM_USE_ATRAC9 { - atrac9_config cfg = {0}; + int i; + offset = 0; + test_offset = 0x50; + for (i = 0; i < aac->total_subsongs; i++) { + uint32_t entry_type = read_u32be(test_offset + 0x00, sf); + uint32_t entry_size = read_u32be(test_offset + 0x04, sf); - cfg.channels = vgmstream->channels; - cfg.encoder_delay = read_32bitLE(0x1124,streamFile); - cfg.config_data = read_32bitBE(0x1128,streamFile); + switch(entry_type) { + case 0x6469726E: /* "dirn" */ + if (i + 1 == target_subsong) { + aac->name_offset = test_offset + 0x10; + offset = read_u32be(test_offset + 0x90, sf); /* absolute */ + } + break; - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; + default: + goto fail; + } - vgmstream->num_samples = atrac9_bytes_to_samples(read_32bitLE(0x10EC, streamFile), vgmstream->codec_data); - vgmstream->num_samples -= cfg.encoder_delay; - vgmstream->loop_start_sample = atrac9_bytes_to_samples(read_32bitLE(0x1110, streamFile), vgmstream->codec_data); - vgmstream->loop_end_sample = atrac9_bytes_to_samples(read_32bitLE(0x1114, streamFile), vgmstream->codec_data); + test_offset += entry_size; + } } -#endif - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ goto fail; - return vgmstream; + wave_offset = offset; + offset += 0x10; + { + /* X360 */ + int i, streams; + off_t strm_offset; + + /* 0x00: 0x0400 + song ID */ + streams = read_u16be(offset + 0x04, sf); + aac->codec = read_u16be(offset + 0x06, sf); + /* 0x08: null */ + /* 0x0c: null */ + aac->stream_size = read_u32be(offset + 0x10, sf); + aac->sample_rate = read_s32be(offset + 0x14, sf); + aac->loop_start = read_u32be(offset + 0x18, sf); + aac->loop_end = read_u32be(offset + 0x1C, sf); /* max samples if not set */ + aac->block_size = read_u32be(offset + 0x20, sf); + /* 0x24: max samples */ + aac->num_samples = read_u32be(offset + 0x28, sf); + aac->block_count = read_u32be(offset + 0x2c, sf); + + /* one UI file has a smaller header, early version? */ + if (read_u32be(offset + 0x30, sf) == 0x7374726D) { + aac->loop_flag = 0; /* ? */ + strm_offset = 0x30; + } + else { + /* 0x30: null */ + /* 0x34: encoder delay? */ + aac->loop_flag = read_u32be(offset + 0x38, sf) != 0; /* loop end block */ + /* 0x3c: size? (loop-related) */ + strm_offset = 0x40; + } + + aac->stream_offset = wave_offset + 0x1000; + + /* channels depends on streams definitions, "strm" chunk (max 2ch per strm) */ + aac->channels = 0; + for (i = 0; i < streams; i++) { + /* format: "strm", size, null, null, channels, ?, sample rate, encoder delay, samples, nulls */ + aac->channels += read_s8(offset + strm_offset + i*0x30 + 0x10, sf); + } + } + + return 1; fail: - close_vgmstream(vgmstream); - return NULL; + return 0; +} + +/* ASC + WAVE chunks [Resonance of Fate (X360/PS3), Star Ocean 4 (PS3)] */ +static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) { + off_t offset, start, size, test_offset, asc_offset; + int target_subsong = sf->stream_index; + + /* base header */ + /* 0x00: id */ + /* 0x04: size */ + /* 0x10: config? (0x00020100/0x00020002/0x00020301/etc) */ + /* 0x14: flag (0x80/01) */ + /* 0x18: align? (PS3=0x30, X360=0xFD0) */ + /* 0x28: platform (PS3=3, X360=2) */ + /* 0x30+ offsets+sizes to ASC or GUIDs */ + + start = read_u32be(0x2c, sf); + + if (target_subsong == 0) target_subsong = 1; + aac->total_subsongs = 0; + + if (read_u32be(start + 0x00, sf) == 0x414D4620) { /* "AMF " */ + /* GUID subsongs */ + if (read_u32be(start + 0x10, sf) != 0x68656164) /* "head" */ + goto fail; + size = read_u32be(start + 0x10 + 0x10, sf); + + offset = 0; + test_offset = start + 0x10; + while (test_offset < start + size) { + uint32_t entry_type = read_u32be(test_offset + 0x00, sf); + uint32_t entry_size = read_u32be(test_offset + 0x04, sf); + + if (entry_type == 0) + break; + + switch(entry_type) { + case 0x61646472: /* "addr" (GUID + config) */ + aac->total_subsongs++; + if (aac->total_subsongs == target_subsong) { + offset = read_u32be(test_offset + 0x2c, sf) + start + size; + } + break; + + default: /* "head", "buff" */ + break; + } + + test_offset += entry_size; + } + } + else if (read_u32be(start + 0x00, sf) == 0x41534320) { /* "ASC " */ + /* regular subsongs */ + offset = 0; + for (test_offset = 0x30; test_offset < start; test_offset += 0x10) { + uint32_t entry_offset = read_u32be(test_offset + 0x00, sf); + /* 0x04: entry size */ + + if (entry_offset) { /* often 0 */ + aac->total_subsongs++; + if (aac->total_subsongs == target_subsong) { + offset = entry_offset; + } + } + } + } + else { + goto fail; + } + + if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail; + + if (read_u32be(offset + 0x00, sf) != 0x41534320) /* "ASC " */ + goto fail; + asc_offset = offset; + + /* ASC section has offsets to "PLBK" chunk (?) and "WAVE" (header), may be followed by "VRC " (?) */ + /* 0x50: PLBK offset */ + offset += read_u32be(offset + 0x54, sf); /* WAVE offset */ + if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ + goto fail; + offset += 0x10; + + if (read_u16be(offset + 0x00, sf) == 0x0400) { + /* X360 */ + int i, streams; + + /* 0x00: 0x0400 + song ID? (0) */ + streams = read_u16be(offset + 0x04, sf); + aac->codec = read_u16be(offset + 0x06, sf); + /* 0x08: null */ + /* 0x0c: null */ + aac->stream_size = read_u32be(offset + 0x10, sf); + aac->sample_rate = read_s32be(offset + 0x14, sf); + aac->loop_start = read_u32be(offset + 0x18, sf); + aac->loop_end = read_u32be(offset + 0x1C, sf); /* max samples if not set */ + aac->block_size = read_u32be(offset + 0x20, sf); + /* 0x24: max samples */ + aac->num_samples = read_u32be(offset + 0x28, sf); + aac->block_count = read_u32be(offset + 0x2c, sf); + /* 0x30: null */ + /* 0x34: encoder delay? */ + aac->loop_flag = read_u32be(offset + 0x38, sf) != 0; /* loop end block */ + /* 0x3c: size? (loop-related) */ + aac->stream_offset = read_u32be(offset + 0x40, sf) + asc_offset; + + /* channels depends on streams definitions, "strm" chunk (max 2ch per strm) */ + aac->channels = 0; + for (i = 0; i < streams; i++) { + /* format: "strm", size, null, null, channels, ?, sample rate, encoder delay, samples, nulls */ + aac->channels += read_s8(offset + 0x44 + i*0x30 + 0x10, sf); + } + + /* after streams and aligned to 0x10 is "Seek" table */ + } + else { + /* PS3 */ + aac->codec = read_u32be(offset + 0x00, sf); + aac->channels = read_u32be(offset + 0x04, sf); + aac->stream_size = read_u32be(offset + 0x08, sf); /* usable size (without padding) */ + aac->sample_rate = read_s32be(offset + 0x0c, sf); + /* 0x10: 0x51? */ + aac->loop_start = read_u32be(offset + 0x14, sf); + aac->loop_end = read_u32be(offset + 0x18, sf); + /* 0x1c: null */ + + aac->stream_offset = offset + 0x20; + } + + aac->loop_flag = (aac->loop_start != -1); + + return 1; +fail: + return 0; +} + +/* AAOB + WAVE + WAVB chunks [Judas Code (Vita), Star Ocean Anamnesis (Android), Star Ocean 4 (PC)] */ +static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) { + off_t offset, size, test_offset; + int target_subsong = sf->stream_index; + + /* base header */ + /* 0x00: id */ + /* 0x04: size */ + /* 0x10: config? (0x00020100/0x00020002/0x00020301/etc) */ + /* 0x14: platform ("VITA"=Vita, "DRD "=Android, "MSPC"=PC) */ + + /* offsets table: offset + flag? + size + align? */ + offset = read_u32le(0x20, sf); /* "AAOB" table (audio object?) */ + /* 0x30: "VRCB" table (some cue/config? related to subsongs? may be empty) */ + /* 0x40: "WAVB" table (wave body, has offset + size per stream then data, not needed since offsets are elsewhere too) */ + + if (read_u32le(offset + 0x00, sf) != 0x41414F42) /* "AAOB" */ + goto fail; + size = read_u32le(offset + 0x04, sf); + + if (target_subsong == 0) target_subsong = 1; + aac->total_subsongs = 0; + + /* AAOB may point to N AAO (headers) in SFX/voice packs, seems signaled with flag 0x80 at AAOB+0x10 + * but there is no subsong count or even max size (always 0x1000?) */ + { + for (test_offset = offset + 0x20; offset + size; test_offset += 0x10) { + uint32_t entry_offset = read_u32le(test_offset + 0x00, sf); + /* 0x04: entry size */ + + if (entry_offset == 0x41414F20) /* reached first "AAO " */ + break; + + if (entry_offset) { /* often 0 */ + aac->total_subsongs++; + if (aac->total_subsongs == target_subsong) { + offset += entry_offset; + } + } + } + } + + if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail; + + if (read_u32le(offset + 0x00, sf) != 0x41414F20) /* "AAO " */ + goto fail; + + + /* AAO section has offsets to "PLBK" chunk (?) and "WAVE" (header) */ + /* 0x14: PLBK offset */ + offset += read_u32le(offset + 0x18, sf); /* WAVE offset */ + if (read_u32le(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ + goto fail; + offset += 0x10; + + /* 0x00: 0x00/01/01CC0000? */ + aac->codec = read_u8(offset + 0x04, sf); + aac->channels = read_u8(offset + 0x05, sf); + /* 0x06: 0x01? */ + /* 0x07: 0x10? (rarely 0x00) */ + aac->sample_rate = read_s32le(offset + 0x08, sf); + aac->stream_size = read_u32le(offset + 0x0C, sf); /* usable size (without padding) */ + /* 0x10-1c: null */ + aac->stream_offset = read_u32le(offset + 0x20, sf); /* absolute */ + /* 0x24: data size (with padding) */ + /* 0x28: null */ + /* 0x2c: null */ + aac->loop_start = read_u32le(offset + 0x30, sf); /* table positions(?) in OGG */ + aac->loop_end = read_u32le(offset + 0x34, sf); + /* 0x38: ? in OGG */ + aac->num_samples = read_s32le(offset + 0x3c, sf); /* OGG only */ + aac->extra_offset = offset + 0x40; /* codec specific */ + /* may have seek tables or other stuff per codec */ + + aac->loop_flag = (aac->loop_end > 0); + + return 1; +fail: + return 0; +} + +static int parse_aac(STREAMFILE* sf, aac_header* aac) { + int ok = 0; + + /* try variations as format evolved over time + * chunk headers are always: id + size + null + null (ids in machine endianness) */ + + ok = parse_aac_v1(sf, aac); + if (ok) return 1; + + ok = parse_aac_v2(sf, aac); + if (ok) return 1; + + ok = parse_aac_v3(sf, aac); + if (ok) return 1; + + return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 1319d7941..958db9e4d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -95,7 +95,7 @@ typedef struct { int target_subsong; uint32_t subsong_count; - uint32_t subsong_offset; + uint32_t subsong_spacing; uint32_t name_offset_set; uint32_t name_offset; @@ -346,14 +346,14 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { break; case coding_EA_XA: /* from 'raw' modes in sx.exe [Harry Potter and the Chamber of Secrets (PC)] */ - if (vgmstream->channels == 1 || txth.codec_mode == 1) { /* mono/interleave */ + if (txth.codec_mode == 1) { /* mono interleave */ coding = coding_EA_XA_int; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = txth.interleave; vgmstream->interleave_last_block_size = txth.interleave_last; - } else { /* stereo */ + } else { /* mono/stereo */ if (vgmstream->channels > 2) - goto fail; /* only 2ch is known */ + goto fail; /* only 1ch and 2ch are known */ vgmstream->layout_type = layout_none; } @@ -1017,7 +1017,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch txth->num_samples = get_bytes_to_samples(txth, txth->num_samples * (txth->interleave*txth->channels)); } } - else if (is_string(key,"loop_start_sample")) { + else if (is_string(key,"loop_start_sample") || is_string(key,"loop_start")) { if (!parse_num(txth->sf_head,txth,val, &txth->loop_start_sample)) goto fail; if (txth->sample_type==1) txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample); @@ -1026,7 +1026,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch if (txth->loop_adjust) txth->loop_start_sample += txth->loop_adjust; } - else if (is_string(key,"loop_end_sample")) { + else if (is_string(key,"loop_end_sample") || is_string(key,"loop_end")) { if (is_string(val,"data_size")) { txth->loop_end_sample = get_bytes_to_samples(txth, txth->data_size); } @@ -1098,8 +1098,8 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch if (!parse_num(txth->sf_head,txth,val, &txth->coef_offset)) goto fail; /* special adjustments */ txth->coef_offset += txth->base_offset; - if (txth->subsong_offset) - txth->coef_offset += txth->subsong_offset * (txth->target_subsong - 1); + if (txth->subsong_spacing) + txth->coef_offset += txth->subsong_spacing * (txth->target_subsong - 1); } else if (is_string(key,"coef_spacing")) { if (!parse_num(txth->sf_head,txth,val, &txth->coef_spacing)) goto fail; @@ -1125,8 +1125,8 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch txth->hist_set = 1; /* special adjustment */ txth->hist_offset += txth->hist_offset; - if (txth->subsong_offset) - txth->hist_offset += txth->subsong_offset * (txth->target_subsong - 1); + if (txth->subsong_spacing) + txth->hist_offset += txth->subsong_spacing * (txth->target_subsong - 1); } else if (is_string(key,"hist_spacing")) { if (!parse_num(txth->sf_head,txth,val, &txth->hist_spacing)) goto fail; @@ -1143,16 +1143,23 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch else if (is_string(key,"subsong_count")) { if (!parse_num(txth->sf_head,txth,val, &txth->subsong_count)) goto fail; } - else if (is_string(key,"subsong_offset")) { - if (!parse_num(txth->sf_head,txth,val, &txth->subsong_offset)) goto fail; + else if (is_string(key,"subsong_spacing") || is_string(key,"subsong_offset")) { + if (!parse_num(txth->sf_head,txth,val, &txth->subsong_spacing)) goto fail; } else if (is_string(key,"name_offset")) { if (!parse_num(txth->sf_head,txth,val, &txth->name_offset)) goto fail; txth->name_offset_set = 1; /* special adjustment */ txth->name_offset += txth->base_offset; - if (txth->subsong_offset) - txth->name_offset += txth->subsong_offset * (txth->target_subsong - 1); + if (txth->subsong_spacing) + txth->name_offset += txth->subsong_spacing * (txth->target_subsong - 1); + } + else if (is_string(key,"name_offset_absolute")) { + if (!parse_num(txth->sf_head,txth,val, &txth->name_offset)) goto fail; + txth->name_offset_set = 1; + /* special adjustment */ + txth->name_offset += txth->base_offset; + /* unlike the above this is meant for reads that point to somewhere in the file, regardless subsong number */ } else if (is_string(key,"name_size")) { if (!parse_num(txth->sf_head,txth,val, &txth->name_size)) goto fail; @@ -1561,7 +1568,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32 uint32_t value_div = txth->value_div; uint32_t value_add = txth->value_add; uint32_t value_sub = txth->value_sub; - uint32_t subsong_offset = txth->subsong_offset; + uint32_t subsong_spacing = txth->subsong_spacing; char op = ' '; int brackets = 0; @@ -1626,8 +1633,8 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32 else if (!(ed1 == 'L' && ed2 == 'E')) goto fail; - if (subsong_offset) - offset = offset + subsong_offset * (txth->target_subsong - 1); + if (subsong_spacing) + offset = offset + subsong_spacing * (txth->target_subsong - 1); switch(size) { case 1: value = (uint8_t)read_8bit(offset,sf); break; @@ -1654,9 +1661,12 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32 else if ((n = is_string_field(val,"data_size"))) value = txth->data_size; else if ((n = is_string_field(val,"num_samples"))) value = txth->num_samples; else if ((n = is_string_field(val,"loop_start_sample"))) value = txth->loop_start_sample; + else if ((n = is_string_field(val,"loop_start"))) value = txth->loop_start_sample; else if ((n = is_string_field(val,"loop_end_sample"))) value = txth->loop_end_sample; + else if ((n = is_string_field(val,"loop_end"))) value = txth->loop_end_sample; else if ((n = is_string_field(val,"subsong_count"))) value = txth->subsong_count; - else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_offset; + else if ((n = is_string_field(val,"subsong_spacing"))) value = txth->subsong_spacing; + else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_spacing; else if ((n = is_string_field(val,"subfile_offset"))) value = txth->subfile_offset; else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size; else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index d02e6b952..49a4860d4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -10,6 +10,7 @@ #define TXTP_GROUP_MODE_SEGMENTED 'S' #define TXTP_GROUP_MODE_LAYERED 'L' #define TXTP_GROUP_MODE_RANDOM 'R' +#define TXTP_GROUP_RANDOM_ALL '-' #define TXTP_GROUP_REPEAT 'R' #define TXTP_POSITION_LOOPS 'L' @@ -88,6 +89,9 @@ typedef struct { int32_t loop_start_sample; double loop_end_second; int32_t loop_end_sample; + /* flags */ + int loop_anchor_start; + int loop_anchor_end; int trim_set; double trim_second; @@ -174,6 +178,10 @@ VGMSTREAM* init_vgmstream_txtp(STREAMFILE* sf) { /* should result in a final, single vgmstream possibly containing multiple vgmstreams */ vgmstream = txtp->vgmstream[0]; + /* flags for title config */ + vgmstream->config.is_txtp = 1; + vgmstream->config.is_mini_txtp = (get_streamfile_size(sf) == 0); + clean_txtp(txtp, 0); return vgmstream; @@ -311,6 +319,7 @@ static void update_vgmstream_list(VGMSTREAM* vgmstream, txtp_header* txtp, int p for (i = position + count; i < txtp->vgmstream_count; i++) { //;VGM_LOG("TXTP: copy %i to %i\n", i, i + 1 - count); txtp->vgmstream[i + 1 - count] = txtp->vgmstream[i]; + txtp->entry[i + 1 - count] = txtp->entry[i]; /* memcpy old settings for other groups */ } /* list can only become smaller, no need to alloc/free/etc */ @@ -318,10 +327,38 @@ static void update_vgmstream_list(VGMSTREAM* vgmstream, txtp_header* txtp, int p //;VGM_LOG("TXTP: compact vgmstreams=%i\n", txtp->vgmstream_count); } +static int find_loop_anchors(txtp_header* txtp, int position, int count, int* p_loop_start, int* p_loop_end) { + int loop_start = 0, loop_end = 0; + int i, j; + + //;VGM_LOG("TXTP: find loop anchors from %i to %i\n", position, count); + + for (i = position, j = 0; i < position + count; i++, j++) { + if (txtp->entry[i].loop_anchor_start) { + loop_start = j + 1; /* logic elsewhere also uses +1 */ + } + if (txtp->entry[i].loop_anchor_end) { + loop_end = j + 1; + } + } + + if (loop_start) { + if (!loop_end) + loop_end = count; + *p_loop_start = loop_start; + *p_loop_end = loop_end; + //;VGM_LOG("TXTP: loop anchors %i, %i\n", loop_start, loop_end); + return 1; + } + + return 0; +} + static int make_group_segment(txtp_header* txtp, int is_group, int position, int count) { VGMSTREAM* vgmstream = NULL; segmented_layout_data *data_s = NULL; int i, loop_flag = 0; + int loop_start = 0, loop_end = 0; /* allowed for actual groups (not final "mode"), otherwise skip to optimize */ @@ -335,16 +372,25 @@ static int make_group_segment(txtp_header* txtp, int is_group, int position, int return 1; } - /* loop settings only make sense if this group becomes final vgmstream */ - if (position == 0 && txtp->vgmstream_count == count) { - if (txtp->loop_start_segment && !txtp->loop_end_segment) { - txtp->loop_end_segment = count; + + /* set loops with "anchors" (this allows loop config inside groups, not just in the final group, + * which is sometimes useful when paired with random/selectable groups or loop times) */ + if (find_loop_anchors(txtp, position, count, &loop_start, &loop_end)) { + loop_flag = (loop_start > 0 && loop_start <= count); + } + /* loop segment settings only make sense if this group becomes final vgmstream */ + else if (position == 0 && txtp->vgmstream_count == count) { + loop_start = txtp->loop_start_segment; + loop_end = txtp->loop_end_segment; + + if (loop_start && !loop_end) { + loop_end = count; } else if (txtp->is_loop_auto) { /* auto set to last segment */ - txtp->loop_start_segment = count; - txtp->loop_end_segment = count; + loop_start = count; + loop_end = count; } - loop_flag = (txtp->loop_start_segment > 0 && txtp->loop_start_segment <= count); + loop_flag = (loop_start > 0 && loop_start <= count); } @@ -363,7 +409,7 @@ static int make_group_segment(txtp_header* txtp, int is_group, int position, int goto fail; /* build the layout VGMSTREAM */ - vgmstream = allocate_segmented_vgmstream(data_s,loop_flag, txtp->loop_start_segment - 1, txtp->loop_end_segment - 1); + vgmstream = allocate_segmented_vgmstream(data_s, loop_flag, loop_start - 1, loop_end - 1); if (!vgmstream) goto fail; /* custom meta name if all parts don't match */ @@ -378,13 +424,13 @@ static int make_group_segment(txtp_header* txtp, int is_group, int position, int if (loop_flag && txtp->is_loop_keep) { int32_t current_samples = 0; for (i = 0; i < count; i++) { - if (txtp->loop_start_segment == i+1 /*&& data_s->segments[i]->loop_start_sample*/) { + if (loop_start == i+1 /*&& data_s->segments[i]->loop_start_sample*/) { vgmstream->loop_start_sample = current_samples + data_s->segments[i]->loop_start_sample; } current_samples += data_s->segments[i]->num_samples; - if (txtp->loop_end_segment == i+1 && data_s->segments[i]->loop_end_sample) { + if (loop_end == i+1 && data_s->segments[i]->loop_end_sample) { vgmstream->loop_end_sample = current_samples - data_s->segments[i]->num_samples + data_s->segments[i]->loop_end_sample; } } @@ -479,9 +525,13 @@ static int make_group_random(txtp_header* txtp, int is_group, int position, int return 1; } + /* special case meaning "play all", basically for quick testing */ + if (selected == count) { + return make_group_segment(txtp, is_group, position, count); + } /* 0=actually random for fun and testing, but undocumented since random music is kinda weird, may change anytime - * (plus foobar caches song duration so it can get strange if randoms are too different) */ + * (plus foobar caches song duration unless .txtp is modifies, so it can get strange if randoms are too different) */ if (selected < 0) { static int random_seed = 0; srand((unsigned)txtp + random_seed++); /* whatevs */ @@ -563,6 +613,7 @@ static int parse_groups(txtp_header* txtp) { /* group may also have settings (like downmixing) */ apply_settings(txtp->vgmstream[grp->position], &grp->group_settings); + txtp->entry[grp->position] = grp->group_settings; /* memcpy old settings for subgroups */ } /* final tweaks (should be integrated with the above?) */ @@ -840,6 +891,44 @@ static int get_position(const char* params, double* value_f, char* value_type) { return n; } +static int get_volume(const char* params, double *value, int *is_set) { + int n, m; + double temp_f; + char temp_c1, temp_c2; + + if (is_set) *is_set = 0; + + /* test if format is NdB (decibels) */ + m = sscanf(params, " %lf%c%c%n", &temp_f, &temp_c1, &temp_c2, &n); + if (m == 3 && temp_c1 == 'd' && (temp_c2 == 'B' || temp_c2 == 'b')) { + /* dB 101: + * - logaritmic scale + * - dB = 20 * log(percent / 100) + * - percent = pow(10, dB / 20)) * 100 + * - for audio: 100% = 0dB (base max volume of current file = reference dB) + * - negative dB decreases volume, positive dB increases + * ex. + * 200% = 20 * log(200 / 100) = +6.02059991328 dB + * 50% = 20 * log( 50 / 100) = -6.02059991328 dB + * 6dB = pow(10, 6 / 20) * 100 = +195.26231497 % + * -6dB = pow(10, -6 / 20) * 100 = +50.50118723362 % + */ + + if (is_set) *is_set = 1; + *value = pow(10, temp_f / 20.0); /* dB to % where 1.0 = max */ + return n; + } + + /* test if format is N.N (percent) */ + m = sscanf(params, " %lf%n", &temp_f, &n); + if (m == 1) { + if (is_set) *is_set = 1; + *value = temp_f; + return n; + } + + return 0; +} static int get_time(const char* params, double* value_f, int32_t* value_i) { int n,m; @@ -1127,6 +1216,14 @@ static void add_settings(txtp_entry* current, txtp_entry* entry, const char* fil current->mixing_count++; } } + + current->loop_anchor_start = entry->loop_anchor_start; + current->loop_anchor_end = entry->loop_anchor_end; +} + +//TODO use +static inline int is_match(const char* str1, const char* str2) { + return strcmp(str1, str2) == 0; } static void parse_params(txtp_entry* entry, char* params) { @@ -1357,12 +1454,21 @@ static void parse_params(txtp_entry* entry, char* params) { //;VGM_LOG("TXTP: trim %i - %f / %i\n", entry->trim_set, entry->trim_second, entry->trim_sample); } + else if (is_match(command,"a") || is_match(command,"@loop")) { + entry->loop_anchor_start = 1; + //;VGM_LOG("TXTP: anchor start set\n"); + } + else if (is_match(command,"A") || is_match(command,"@LOOP")) { + entry->loop_anchor_end = 1; + //;VGM_LOG("TXTP: anchor end set\n"); + } + //todo cleanup /* macros */ - else if (strcmp(command,"@volume") == 0) { + else if (is_match(command,"v") || is_match(command,"@volume")) { txtp_mix_data mix = {0}; - nm = get_double(params, &mix.vol, NULL); + nm = get_volume(params, &mix.vol, NULL); params += nm; if (nm == 0) continue; @@ -1479,11 +1585,18 @@ static int add_group(txtp_header* txtp, char* line) { line += n; } - m = sscanf(line, " >%d%n", &cfg.selected, &n); - if (m == 1) { - cfg.selected--; /* externally 1=first but internally 0=first */ + m = sscanf(line, " >%c%n", &c, &n); + if (m == 1 && c == TXTP_GROUP_RANDOM_ALL) { + cfg.selected = cfg.count; /* special meaning */ line += n; } + else { + m = sscanf(line, " >%d%n", &cfg.selected, &n); + if (m == 1) { + cfg.selected--; /* externally 1=first but internally 0=first */ + line += n; + } + } parse_params(&cfg.group_settings, line); @@ -1555,6 +1668,7 @@ static void clean_filename(char* filename) { } +//TODO see if entry can be set to &default/&entry[entry_count] to avoid add_settings static int add_entry(txtp_header* txtp, char* filename, int is_default) { int i; txtp_entry entry = {0}; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index 37ddc39de..92e96985e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -120,6 +120,7 @@ typedef struct { int is_dat; int is_ps2_bnm; int is_blk; + int has_numbered_banks; STREAMFILE* sf_header; uint32_t version; /* 16b+16b major+minor version */ uint32_t version_empty; /* map sbX versions are empty */ @@ -631,24 +632,20 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_ STREAMFILE *sf_data = NULL; if (sb->is_external) { - if (strcmp(sb->resource_name, "silence.wav") == 0) { - /* some Rayman 2 banks reference non-existent silence.wav, looks like some kind of hack? */ - sb->duration = (float)(sb->stream_size / sb->channels / 2) / (float)sb->sample_rate; - return init_vgmstream_ubi_sb_silence(sb, sf_index, sf); - } - sf_data = open_streamfile_by_filename(sf, sb->resource_name); if (!sf_data) { - VGM_LOG("UBI DAT: no matching KAT found\n"); - goto fail; + /* play silence if external file is not found since Rayman 2 seems to rely on this behavior */ + VGM_LOG("UBI DAT: external stream '%s' not found\n", sb->resource_name); + strncat(sb->readable_name, " (missing)", sizeof(sb->readable_name)); + sb->duration = (float)pcm_bytes_to_samples(sb->stream_size, sb->channels, 16) / (float)sb->sample_rate; + return init_vgmstream_ubi_sb_silence(sb, sf_index, sf); } } /* DAT banks don't work with raw audio data, they open full external files and rely almost entirely * on their metadata, that's why we're handling this here, separately from other types */ switch (sb->stream_type) { - case 0x01: - { + case 0x01: { if (!sb->is_external) { /* Dreamcast bank */ if (sb->version == 0x00000000) { uint32_t entry_offset, start_offset, num_samples, codec; @@ -725,7 +722,7 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_ } break; } - case 0x04:{ /* standard WAV */ + case 0x04: { /* standard WAV */ if (!sb->is_external) { VGM_LOG("Ubi DAT: Found RAM stream_type 0x04\n"); goto fail; @@ -1410,7 +1407,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_sequence(ubi_sb_header* sb, STREAMFILE* /* bnm sequences may use to entries from other banks, do some voodoo */ - if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { + if (sb->has_numbered_banks) { /* see if *current* bank has changed (may use a different bank N times) */ if (is_other_bank(sb, sf_bank, sb->sequence_banks[i])) { char bank_name[255]; @@ -1947,7 +1944,7 @@ static int parse_type_sequence(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) uint32_t entry_number = (uint32_t)read_32bit(table_offset+sb->cfg.sequence_entry_number, sf); /* bnm sequences may refer to entries from different banks, whee */ - if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { + if (sb->has_numbered_banks) { int16_t bank_number = (entry_number >> 16) & 0xFFFF; entry_number = (entry_number >> 00) & 0xFFFF; @@ -2113,7 +2110,7 @@ static int parse_type_random(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { uint32_t entry_number = (uint32_t)read_32bit(table_offset+0x00, sf); //uint32_t entry_chance = (uint32_t)read_32bit(table_offset+0x04, sf); - if (sb->is_bnm) { + if (sb->has_numbered_banks) { int16_t bank_number = (entry_number >> 16) & 0xFFFF; entry_number = (entry_number >> 00) & 0xFFFF; @@ -2866,6 +2863,10 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* games <= 0x00100000 seem to use old types, rest new types */ + if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { + /* these all have names in BNK_%num% format and can reference each other by index */ + sb->has_numbered_banks = 1; + } /* maybe 0x20/0x24 for some but ok enough (null terminated) */ sb->cfg.resource_name_size = 0x28; /* min for Brother in Arms 2 (PS2) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c b/Frameworks/vgmstream/vgmstream/src/meta/vawx.c deleted file mode 100644 index ef40a9802..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - - -/* VAWX - found in feelplus games [No More Heroes: Heroes Paradise (PS3/X360), Moon Diver (PS3/X360)] */ -VGMSTREAM* init_vgmstream_vawx(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; - off_t start_offset, data_size; - int loop_flag = 0, channel_count, codec; - - - /* checks */ - /* .xwv: actual extension [Moon Diver (PS3/X360)] - * .vawx: header id */ - if ( !check_extensions(sf, "xwv,vawx") ) - goto fail; - if (read_32bitBE(0x00,sf) != 0x56415758) /* "VAWX" */ - goto fail; - - loop_flag = read_8bit(0x37,sf); - channel_count = read_8bit(0x39,sf); - start_offset = 0x800; /* ? read_32bitLE(0x0c,sf); */ - codec = read_8bit(0x36,sf); /* could be at 0x38 too */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* 0x04: filesize */ - /* 0x16: file id */ - vgmstream->num_samples = read_32bitBE(0x3c,sf); - vgmstream->sample_rate = read_32bitBE(0x40,sf); - - vgmstream->meta_type = meta_VAWX; - - switch(codec) { - case 2: /* PS-ADPCM */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = channel_count == 6 ? layout_blocked_vawx : layout_interleave; - vgmstream->interleave_block_size = 0x10; - - vgmstream->loop_start_sample = read_32bitBE(0x44,sf); - vgmstream->loop_end_sample = read_32bitBE(0x48,sf); - - break; - -#ifdef VGM_USE_FFMPEG - case 1: { /* XMA2 */ - uint8_t buf[0x100]; - int32_t bytes, block_size, block_count; - - data_size = get_streamfile_size(sf)-start_offset; - block_size = 0x10000; /* VAWX default */ - block_count = (uint16_t)read_16bitBE(0x3A, sf); /* also at 0x56 */ - - bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - vgmstream->loop_start_sample = read_32bitBE(0x44,sf); - vgmstream->loop_end_sample = read_32bitBE(0x48,sf); - - //todo fix loops/samples vs ATRAC3 - /* may be only applying end_skip to num_samples? */ - xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); - break; - } - - case 7: { /* ATRAC3 */ - int block_align, encoder_delay; - - data_size = read_32bitBE(0x54,sf); - block_align = 0x98 * vgmstream->channels; - encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; /* original samples break looping in some files otherwise */ - - vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ - vgmstream->loop_start_sample = atrac3_bytes_to_samples(read_32bitBE(0x44,sf), block_align); //- encoder_delay - vgmstream->loop_end_sample = atrac3_bytes_to_samples(read_32bitBE(0x48,sf), block_align) - encoder_delay; - break; - } -#endif - default: - goto fail; - - } - - - if (!vgmstream_open_stream(vgmstream, sf, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wady.c b/Frameworks/vgmstream/vgmstream/src/meta/wady.c new file mode 100644 index 000000000..575d2faae --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wady.c @@ -0,0 +1,62 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* WADY - from Marble engine games [Eien no Owari ni (PC), Elf no Futagohime (PC)] */ +VGMSTREAM* init_vgmstream_wady(STREAMFILE *sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, sample_rate, scale, num_samples; + size_t codec_size; + + + /* checks */ + /* .way/extensionless: found in some bigfiles */ + if (!check_extensions(sf, "way,")) + goto fail; + if (read_u32be(0x00,sf) != 0x57414459) /* "WADY" */ + goto fail; + + /* 0x04: none */ + scale = read_u8(0x05,sf); + channels = read_u16le(0x06,sf); + sample_rate = read_s32le(0x08,sf); + codec_size = read_u32le(0x0c,sf); + num_samples = read_s32le(0x10,sf); + /* 0x14: PCM size? */ + /* 0x18/1c: null */ + /* 0x20: fmt-like subheader (codec/channels/srate/bitrate/bps/spf) */ + + start_offset = 0x30; + loop_flag = 0; + + //todo implement + /* codec variation used in SFX */ + if (codec_size + start_offset != get_streamfile_size(sf)) + goto fail; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_WADY; + vgmstream->sample_rate = sample_rate; + vgmstream->coding_type = coding_WADY; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + vgmstream->num_samples = num_samples; + + { + int i; + for (i = 0; i < channels; i++) { + vgmstream->ch[i].adpcm_scale = scale; + } + } + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index c545054af..15a9307d8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -7,19 +7,30 @@ * There is some repetition from other metas, but not enough to bother me. * * Some info: https://www.audiokinetic.com/en/library/edge/ + * .bnk (dynamic music/loop) info: https://github.com/bnnm/wwiser */ -typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, PTADPCM } wwise_codec; +typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, OPUSWW, PTADPCM } wwise_codec; typedef struct { int big_endian; size_t file_size; int truncated; /* chunks references */ - off_t fmt_offset; + off_t fmt_offset; size_t fmt_size; - off_t data_offset; + off_t data_offset; size_t data_size; - off_t chunk_offset; + off_t xma2_offset; + size_t xma2_size; + off_t vorb_offset; + size_t vorb_size; + off_t wiih_offset; + size_t wiih_size; + off_t smpl_offset; + size_t smpl_size; + off_t seek_offset; + size_t seek_size; + /* standard fmt stuff */ wwise_codec codec; @@ -38,16 +49,19 @@ typedef struct { int32_t loop_end_sample; } wwise_header; +static int parse_wwise(STREAMFILE* sf, wwise_header* ww); static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset); /* Wwise - Audiokinetic Wwise (Wave Works Interactive Sound Engine) middleware */ -VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; wwise_header ww = {0}; - off_t start_offset, first_offset = 0xc; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + off_t start_offset; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; + uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; + /* checks */ /* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011) @@ -58,194 +72,24 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (!check_extensions(sf,"wem,wav,lwav,ogg,logg,xma,bnk")) goto fail; - if (read_32bitBE(0x00,sf) != 0x52494646 && /* "RIFF" (LE) */ - read_32bitBE(0x00,sf) != 0x52494658) /* "RIFX" (BE) */ - goto fail; - if (read_32bitBE(0x08,sf) != 0x57415645 && /* "WAVE" */ - read_32bitBE(0x08,sf) != 0x58574D41) /* "XWMA" */ + if (!parse_wwise(sf, &ww)) goto fail; - ww.big_endian = read_32bitBE(0x00,sf) == 0x52494658; /* RIFX */ - if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */ - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - } else { - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - } - - ww.file_size = sf->get_size(sf); - -#if 0 - /* Wwise's RIFF size is often wonky, seemingly depending on codec: - * - PCM, IMA/PTADPCM, VORBIS, AAC, OPUSNX/OPUS: correct - * - DSP, XWMA, ATRAC9: almost always slightly smaller (around 0x50) - * - HEVAG: very off - * - XMA2: exact file size - * - some RIFX have LE size - * (later we'll validate "data" which fortunately is correct) - */ - if (read_32bit(0x04,sf)+0x04+0x04 != ww.file_size) { - VGM_LOG("WWISE: bad riff size (real=0x%x vs riff=0x%x)\n", read_32bit(0x04,sf)+0x04+0x04, ww.file_size); - goto fail; - } -#endif - - /* ignore LyN RIFF */ - { - off_t fact_offset; - size_t fact_size; - - if (find_chunk(sf, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */ - if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, sf) == 0x4C794E20) /* "LyN " */ - goto fail; /* parsed elsewhere */ - } - /* Wwise doesn't use "fact", though */ - } - - /* parse format (roughly spec-compliant but some massaging is needed) */ - { - off_t loop_offset; - size_t loop_size; - - /* find basic chunks */ - if (read_32bitBE(0x0c, sf) == 0x584D4132) { /* "XMA2" with no "fmt" [Too Human (X360)] */ - ww.format = 0x0165; /* signal for below */ - } - else { - if (!find_chunk(sf, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) /* "fmt " */ - goto fail; - if (ww.fmt_size < 0x12) - goto fail; - ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,sf); - } - - - if (ww.format == 0x0165) { - /* pseudo-XMA2WAVEFORMAT ("fmt"+"XMA2" or just "XMA2) */ - if (!find_chunk(sf, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0)) /* "XMA2" */ - goto fail; - xma2_parse_xma2_chunk(sf, ww.chunk_offset,&ww.channels,&ww.sample_rate, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample); - } - else { - /* pseudo-WAVEFORMATEX */ - ww.channels = read_16bit(ww.fmt_offset+0x02,sf); - ww.sample_rate = read_32bit(ww.fmt_offset+0x04,sf); - ww.average_bps = read_32bit(ww.fmt_offset+0x08,sf);/* bytes per sec */ - ww.block_align = (uint16_t)read_16bit(ww.fmt_offset+0x0c,sf); - ww.bits_per_sample = (uint16_t)read_16bit(ww.fmt_offset+0x0e,sf); - if (ww.fmt_size > 0x10 && ww.format != 0x0165 && ww.format != 0x0166) /* ignore XMAWAVEFORMAT */ - ww.extra_size = (uint16_t)read_16bit(ww.fmt_offset+0x10,sf); - if (ww.extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */ - /* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */ - ww.channel_layout = read_32bit(ww.fmt_offset+0x14,sf); - /* later games (+2018?) have a pseudo-format instead to handle more cases: - * - 8b: uNumChannels - * - 4b: eConfigType (0=none, 1=standard, 2=ambisonic) - * - 19b: uChannelMask */ - if ((ww.channel_layout & 0xFF) == ww.channels) { - ww.channel_layout = (ww.channel_layout >> 12); - } - } - } - - /* find loop info ("XMA2" chunks already read them) */ - if (ww.format == 0x0166) { /* XMA2WAVEFORMATEX in fmt */ - ww.chunk_offset = ww.fmt_offset; - xma2_parse_fmt_chunk_extra(sf, ww.chunk_offset, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample, ww.big_endian); - } - else if (find_chunk(sf, 0x736D706C,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /* "smpl", common */ - if (loop_size >= 0x34 - && read_32bit(loop_offset+0x1c, sf)==1 /* loop count */ - && read_32bit(loop_offset+0x24+4, sf)==0) { - ww.loop_flag = 1; - ww.loop_start_sample = read_32bit(loop_offset+0x24+0x8, sf); - ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc, sf) + 1; /* like standard RIFF */ - } - } - //else if (find_chunk(sf, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */ - // /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */ - //} - - /* other Wwise specific chunks: - * "JUNK": optional padding for aligment (0-size JUNK exists too) - * "akd ": seem to store extra info for Wwise editor (wave peaks/loudness/HDR envelope?) - */ - - if (!find_chunk(sf, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) /* "data" */ - goto fail; - } - - /* format to codec */ - switch(ww.format) { - case 0x0001: ww.codec = PCM; break; /* older Wwise */ - case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */ - case 0x0069: ww.codec = IMA; break; /* older Wwise [Spiderman Web of Shadows (X360), LotR Conquest (PC)] */ - case 0x0161: ww.codec = XWMA; break; /* WMAv2 */ - case 0x0162: ww.codec = XWMA; break; /* WMAPro */ - case 0x0165: ww.codec = XMA2; break; /* XMA2-chunk XMA (Wwise doesn't use XMA1) */ - case 0x0166: ww.codec = XMA2; break; /* fmt-chunk XMA */ - case 0xAAC0: ww.codec = AAC; break; - case 0xFFF0: ww.codec = DSP; break; - case 0xFFFB: ww.codec = HEVAG; break; - case 0xFFFC: ww.codec = ATRAC9; break; - case 0xFFFE: ww.codec = PCM; break; /* "PCM for Wwise Authoring" */ - case 0xFFFF: ww.codec = VORBIS; break; - case 0x3039: ww.codec = OPUSNX; break; /* later renamed from "OPUS" */ - case 0x3040: ww.codec = OPUS; break; - case 0x8311: ww.codec = PTADPCM; break; /* newer, rare [Genshin Impact (PC)] */ - default: - goto fail; - } - - /* identify system's ADPCM */ - if (ww.format == 0x0002) { - if (ww.extra_size == 0x0c + ww.channels * 0x2e) { - /* newer Wwise DSP with coefs [Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS)] */ - ww.codec = DSP; - } else if (ww.extra_size == 0x0a && find_chunk(sf, 0x57696948, first_offset,0, NULL,NULL, ww.big_endian, 0)) { /* WiiH */ - /* few older Wwise DSP with num_samples in extra_size [Tony Hawk: Shred (Wii)] */ - ww.codec = DSP; - } else if (ww.block_align == 0x104 * ww.channels) { - ww.codec = PTADPCM; /* Bayonetta 2 (Switch) */ - } - } - - - /* Some Wwise .bnk (RAM) files have truncated, prefetch mirrors of another file, that - * play while the rest of the real stream loads. We'll add basic support to avoid - * complaints of this or that .wem not playing */ - if (ww.data_offset + ww.data_size > ww.file_size) { - //VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size); - - /* catch wrong rips as truncated tracks' file_size should be much smaller than data_size, - * but it's possible to pre-fetch small files too [Punch Out!! (Wii)] */ - if (ww.data_offset + ww.data_size - ww.file_size < 0x5000 && ww.file_size > 0x10000) { - VGM_LOG("WWISE: wrong expected data_size\n"); - goto fail; - } - - if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == DSP || ww.codec == XMA2 || - ww.codec == OPUSNX || ww.codec == OPUS || ww.codec == PTADPCM) { - ww.truncated = 1; /* only seen those, probably all exist (XWMA, AAC, HEVAG, ATRAC9?) */ - } else { - VGM_LOG("WWISE: wrong size, maybe truncated\n"); - goto fail; - } - } - + read_u32 = ww.big_endian ? read_u32be : read_u32le; + read_s32 = ww.big_endian ? read_s32be : read_s32le; + read_u16 = ww.big_endian ? read_u16be : read_u16le; start_offset = ww.data_offset; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(ww.channels,ww.loop_flag); + vgmstream = allocate_vgmstream(ww.channels, ww.loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_WWISE_RIFF; vgmstream->sample_rate = ww.sample_rate; vgmstream->loop_start_sample = ww.loop_start_sample; vgmstream->loop_end_sample = ww.loop_end_sample; vgmstream->channel_layout = ww.channel_layout; - vgmstream->meta_type = meta_WWISE_RIFF; switch(ww.codec) { case PCM: /* common */ @@ -265,10 +109,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { break; case IMA: /* common */ - /* slightly modified XBOX-IMA */ + /* slightly modified and mono-interleaved XBOX-IMA */ /* Wwise reuses common codec ids (ex. 0x0002 MSADPCM) for IMA so this parser should go AFTER riff.c avoid misdetection */ - if (ww.fmt_size != 0x28 && ww.fmt_size != 0x18) goto fail; /* old, new */ + if (ww.fmt_size != 0x14 && ww.fmt_size != 0x28 && ww.fmt_size != 0x18) goto fail; /* oldest, old, new */ if (ww.bits_per_sample != 4) goto fail; if (ww.block_align != 0x24 * ww.channels) goto fail; @@ -277,6 +121,15 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->interleave_block_size = ww.block_align / ww.channels; vgmstream->codec_endian = ww.big_endian; + /* oldest version uses regular XBOX IMA with stereo mode [Shadowrun (PC)] */ + if (ww.fmt_size == 0x14 && ww.format == 0x0069) { + if (ww.channels > 2) goto fail; /* unlikely but just in case */ + if (ww.big_endian) goto fail; /* unsure */ + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0; + } + if (ww.truncated) { ww.data_size = ww.file_size - ww.data_offset; } @@ -285,10 +138,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { break; #ifdef VGM_USE_VORBIS - case VORBIS: { /* common */ + case VORBIS: { /* common */ /* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder). */ - off_t vorb_offset, data_offsets, block_offsets; - size_t vorb_size, setup_offset, audio_offset; + off_t data_offsets, block_offsets; + size_t setup_offset, audio_offset; vorbis_custom_config cfg = {0}; cfg.channels = ww.channels; @@ -298,10 +151,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */ /* autodetect format (fields are mostly common, see the end of the file) */ - if (find_chunk(sf, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /* "vorb" */ + if (ww.vorb_offset) { /* older Wwise (~<2012) */ - switch(vorb_size) { + switch(ww.vorb_size) { case 0x2C: /* earliest (~2009) [The Lord of the Rings: Conquest (PC)] */ case 0x28: /* early (~2009) [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)] */ data_offsets = 0x18; @@ -329,22 +182,22 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { break; default: - VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size); + VGM_LOG("WWISE: unknown vorb size 0x%x\n", ww.vorb_size); goto fail; } - vgmstream->num_samples = read_32bit(vorb_offset + 0x00, sf); - setup_offset = read_32bit(vorb_offset + data_offsets + 0x00, sf); /* within data (0 = no seek table) */ - audio_offset = read_32bit(vorb_offset + data_offsets + 0x04, sf); /* within data */ + vgmstream->num_samples = read_s32(ww.vorb_offset + 0x00, sf); + setup_offset = read_u32(ww.vorb_offset + data_offsets + 0x00, sf); /* within data (0 = no seek table) */ + audio_offset = read_u32(ww.vorb_offset + data_offsets + 0x04, sf); /* within data */ if (block_offsets) { - cfg.blocksize_1_exp = read_8bit(vorb_offset + block_offsets + 0x00, sf); /* small */ - cfg.blocksize_0_exp = read_8bit(vorb_offset + block_offsets + 0x01, sf); /* big */ + cfg.blocksize_1_exp = read_u8(ww.vorb_offset + block_offsets + 0x00, sf); /* small */ + cfg.blocksize_0_exp = read_u8(ww.vorb_offset + block_offsets + 0x01, sf); /* big */ } ww.data_size -= audio_offset; /* detect normal packets */ - if (vorb_size == 0x2a) { + if (ww.vorb_size == 0x2a) { /* almost all blocksizes are 0x08+0x0B except a few with 0x0a+0x0a [Captain America: Super Soldier (X360) voices/sfx] */ if (cfg.blocksize_0_exp == cfg.blocksize_1_exp) cfg.packet_type = WWV_STANDARD; @@ -354,12 +207,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { * - full inline: ~2009, ex. The King of Fighters XII (X360), The Saboteur (PC) * - trimmed inline: ~2010, ex. Army of Two: 40 days (X360) some multiplayer files * - external: ~2010, ex. Assassin's Creed Brotherhood (X360), Dead Nation (X360) */ - if (vorb_size == 0x34) { - size_t setup_size = (uint16_t)read_16bit(start_offset + setup_offset, sf); - uint32_t id = (uint32_t)read_32bitBE(start_offset + setup_offset + 0x06, sf); + if (ww.vorb_size == 0x34) { + size_t setup_size = read_u16 (start_offset + setup_offset + 0x00, sf); + uint32_t setup_id = read_u32be(start_offset + setup_offset + 0x06, sf); /* if the setup after header starts with "(data)BCV" it's an inline codebook) */ - if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ + if ((setup_id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ cfg.setup_type = WWV_FULL_SETUP; } /* if the setup is suspiciously big it's probably trimmed inline codebooks */ @@ -390,15 +243,15 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { break; default: - VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size); + VGM_LOG("WWISE: unknown extra size 0x%x\n", ww.vorb_size); goto fail; } - vgmstream->num_samples = read_32bit(extra_offset + 0x00, sf); - setup_offset = read_32bit(extra_offset + data_offsets + 0x00, sf); /* within data */ - audio_offset = read_32bit(extra_offset + data_offsets + 0x04, sf); /* within data */ - cfg.blocksize_1_exp = read_8bit(extra_offset + block_offsets + 0x00, sf); /* small */ - cfg.blocksize_0_exp = read_8bit(extra_offset + block_offsets + 0x01, sf); /* big */ + vgmstream->num_samples = read_s32(extra_offset + 0x00, sf); + setup_offset = read_u32(extra_offset + data_offsets + 0x00, sf); /* within data */ + audio_offset = read_u32(extra_offset + data_offsets + 0x04, sf); /* within data */ + cfg.blocksize_1_exp = read_u8(extra_offset + block_offsets + 0x00, sf); /* small */ + cfg.blocksize_0_exp = read_u8(extra_offset + block_offsets + 0x01, sf); /* big */ ww.data_size -= audio_offset; /* detect normal packets */ @@ -433,10 +286,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { } #endif - case DSP: { /* Wii/3DS/WiiU */ - off_t wiih_offset; - size_t wiih_size; - + case DSP: { /* Wii/3DS/WiiU */ //if (ww.fmt_size != 0x28 && ww.fmt_size != ?) goto fail; /* old, new */ if (ww.bits_per_sample != 4) goto fail; @@ -445,17 +295,17 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->interleave_block_size = 0x08; /* ww.block_align = 0x8 in older Wwise, samples per block in newer Wwise */ /* find coef position */ - if (find_chunk(sf, 0x57696948,first_offset,0, &wiih_offset,&wiih_size, ww.big_endian, 0)) { /*"WiiH", older Wwise */ + if (ww.wiih_offset) { /* older */ vgmstream->num_samples = dsp_bytes_to_samples(ww.data_size, ww.channels); - if (wiih_size != 0x2e * ww.channels) goto fail; + if (ww.wiih_size != 0x2e * ww.channels) goto fail; - if (is_dsp_full_interleave(sf, &ww, wiih_offset)) + if (is_dsp_full_interleave(sf, &ww, ww.wiih_offset)) vgmstream->interleave_block_size = ww.data_size / 2; } - else if (ww.extra_size == 0x0c + ww.channels * 0x2e) { /* newer Wwise */ - vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, sf); - wiih_offset = ww.fmt_offset + 0x1c; - wiih_size = 0x2e * ww.channels; + else if (ww.extra_size == 0x0c + ww.channels * 0x2e) { /* newer */ + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf); + ww.wiih_offset = ww.fmt_offset + 0x1c; + ww.wiih_size = 0x2e * ww.channels; } else { goto fail; @@ -473,27 +323,29 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->loop_flag = 0; } - dsp_read_coefs(vgmstream,sf,wiih_offset + 0x00, 0x2e, ww.big_endian); - dsp_read_hist (vgmstream,sf,wiih_offset + 0x24, 0x2e, ww.big_endian); + dsp_read_coefs(vgmstream, sf, ww.wiih_offset + 0x00, 0x2e, ww.big_endian); + dsp_read_hist (vgmstream, sf, ww.wiih_offset + 0x24, 0x2e, ww.big_endian); break; } #ifdef VGM_USE_FFMPEG - case XMA2: { /* X360/XBone */ + case XMA2: { /* X360/XBone */ uint8_t buf[0x100]; int bytes; - off_t xma2_offset; - size_t xma2_size; /* endian check should be enough */ //if (ww.fmt_size != ...) goto fail; /* XMA1 0x20, XMA2old: 0x34, XMA2new: 0x40, XMA2 Guitar Hero Live/padded: 0x64, etc */ - if (!ww.big_endian) goto fail; /* must be Wwise (real XMA are LE and parsed elsewhere) */ - if (find_chunk(sf, 0x584D4132,first_offset,0, &xma2_offset,&xma2_size, ww.big_endian, 0)) { /*"XMA2"*/ /* older Wwise */ - bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, xma2_offset, xma2_size, ww.data_size, sf); - } else { /* newer Wwise */ - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, ww.fmt_offset, ww.fmt_size, ww.data_size, sf, ww.big_endian); + /* only Wwise XMA: X360=BE, or XBone=LE+wem (real X360 XMA are LE and parsed elsewhere) */ + if (!(ww.big_endian || (!ww.big_endian && check_extensions(sf,"wem,bnk")))) + goto fail; + + if (ww.xma2_offset) { /* older */ + bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf, sizeof(buf), ww.xma2_offset, ww.xma2_size, ww.data_size, sf); + } + else { /* newer */ + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), ww.fmt_offset, ww.fmt_size, ww.data_size, sf, ww.big_endian); } vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, ww.data_offset,ww.data_size); @@ -504,23 +356,19 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->num_samples = ww.num_samples; /* set while parsing XMAWAVEFORMATs */ /* Wwise loops are always pre-adjusted (old or new) and only num_samples is off */ - xma_fix_raw_samples(vgmstream, sf, ww.data_offset,ww.data_size, ww.chunk_offset, 1,0); - - /* "XMAc": rare Wwise extension, XMA2 physical loop regions (loop_start_b, loop_end_b, loop_subframe_data) - * Can appear even in the file doesn't loop, maybe it's meant to be the playable physical region */ - //VGM_ASSERT(find_chunk(sf, 0x584D4163,first_offset,0, NULL,NULL, ww.big_endian, 0), "WWISE: XMAc chunk found\n"); - /* other chunks: "seek", regular XMA2 seek table */ + xma_fix_raw_samples(vgmstream, sf, ww.data_offset, ww.data_size, ww.xma2_offset ? ww.xma2_offset : ww.fmt_offset, 1,0); /* XMA is VBR so this is very approximate percent, meh */ if (ww.truncated) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * (double)(ww.file_size - start_offset) / (double)ww.data_size); + //todo data size, call function } break; } - case XWMA: { /* X360 */ + case XWMA: { /* X360 */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[0x100]; int bytes; @@ -528,8 +376,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (ww.fmt_size != 0x18) goto fail; if (!ww.big_endian) goto fail; /* must be from Wwise X360 (PC LE XWMA is parsed elsewhere) */ - bytes = ffmpeg_make_riff_xwma(buf,0x100, ww.format, ww.data_size, vgmstream->channels, vgmstream->sample_rate, ww.average_bps, ww.block_align); - ffmpeg_data = init_ffmpeg_header_offset(sf, buf,bytes, ww.data_offset,ww.data_size); + bytes = ffmpeg_make_riff_xwma(buf, sizeof(buf), ww.format, ww.data_size, ww.channels, ww.sample_rate, ww.average_bps, ww.block_align); + ffmpeg_data = init_ffmpeg_header_offset(sf, buf,bytes, ww.data_offset, ww.data_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; @@ -545,9 +393,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { msd.data_size = ww.data_size; if (ww.format == 0x0162) - wmapro_get_samples(&msd, sf, ww.block_align, ww.sample_rate,0x00E0); + wmapro_get_samples(&msd, sf, ww.block_align, ww.sample_rate, 0x00E0); else - wma_get_samples(&msd, sf, ww.block_align, ww.sample_rate,0x001F); + wma_get_samples(&msd, sf, ww.block_align, ww.sample_rate, 0x001F); vgmstream->num_samples = msd.num_samples; if (!vgmstream->num_samples) @@ -578,24 +426,28 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { case OPUSNX: { /* Switch */ size_t skip; + size_t seek_size; + if (ww.fmt_size != 0x28) goto fail; /* values up to 0x14 seem fixed and similar to HEVAG's (block_align 0x02/04, bits_per_sample 0x10) */ - if (ww.fmt_size == 0x28) { - size_t seek_size; - vgmstream->num_samples += read_32bit(ww.fmt_offset + 0x18, sf); - /* 0x1c: null? 0x20: data_size without seek_size */ - seek_size = read_32bit(ww.fmt_offset + 0x24, sf); + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf); + /* 0x1c: null? + * 0x20: data_size without seek_size */ + seek_size = read_u32(ww.fmt_offset + 0x24, sf); - start_offset += seek_size; - ww.data_size -= seek_size; - } - else { - goto fail; - } + start_offset += seek_size; + ww.data_size -= seek_size; skip = switch_opus_get_encoder_delay(start_offset, sf); /* should be 120 */ + /* some voices have original sample rate but OPUS can only do 48000 (ex. Mario Kart Home Circuit 24khz) */ + if (vgmstream->sample_rate != 48000) { + vgmstream->sample_rate = 48000; + vgmstream->num_samples = switch_opus_get_samples(start_offset,ww.data_size, sf); /* also original's */ + vgmstream->num_samples -= skip; + } + /* OPUS is VBR so this is very approximate percent, meh */ if (ww.truncated) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * @@ -603,18 +455,18 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { ww.data_size = ww.file_size - start_offset; } - vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset,ww.data_size, vgmstream->channels, skip, vgmstream->sample_rate); + vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset,ww.data_size, ww.channels, skip, vgmstream->sample_rate); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case OPUS: { /* PC/mobile/etc, rare (most games still use Vorbis) [Girl Cafe Gun (Mobile)] */ + case OPUS: { /* alt to Vorbis [Girl Cafe Gun (Mobile)] */ if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* extra: size 0x12 */ - vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, sf); + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf); /* 0x1c: stream size without OggS? */ /* 0x20: full samples (without encoder delay) */ @@ -625,15 +477,45 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { ww.data_size = ww.file_size - start_offset; } - vgmstream->codec_data = init_ffmpeg_offset(sf, ww.data_offset,ww.data_size); + vgmstream->codec_data = init_ffmpeg_offset(sf, ww.data_offset, ww.data_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } +#if 0 // disabled until more files/tests + case OPUSWW: { /* updated Opus [Assassin's Creed Valhalla (PC)] */ + int skip, table_count; + + if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; + if (!ww.seek_offset)) goto fail; + + /* extra: size 0x10 */ + /* 0x12: samples per frame */ + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf); + table_count = read_u32(ww.fmt_offset + 0x1c, sf); /* same as seek size / 2 */ + skip = read_u16(ww.fmt_offset + 0x20, sf); + /* 0x22: 1? (though extra size is declared as 0x10 so this is outsize, AK plz */ + + /* OPUS is VBR so this is very approximate percent, meh */ + if (ww.truncated) { + vgmstream->num_samples = (int32_t)(vgmstream->num_samples * + (double)(ww.file_size - start_offset) / (double)ww.data_size); + ww.data_size = ww.file_size - start_offset; + } + + /* Wwise Opus saves all frame sizes in the seek table */ + vgmstream->codec_data = init_ffmpeg_wwise_opus(sf, ww.seek_offset, table_count, ww.data_offset, ww.data_size, ww.channels, skip); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } #endif - case HEVAG: /* PSV */ + +#endif + case HEVAG: /* PSV */ /* changed values, another bizarre Wwise quirk */ //ww.block_align /* unknown (1ch=2, 2ch=4) */ //ww.bits_per_sample; /* unknown (0x10) */ @@ -642,7 +524,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (ww.fmt_size != 0x18) goto fail; if (ww.big_endian) goto fail; - /* extra_data: size 0x06, @0x00: samples per block (0x1c), @0x04: channel config */ + /* extra_data (size 0x06) + * 0x00: samples per block (0x1c) + * 0x04: channel config (again?) */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; @@ -658,20 +542,20 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (ww.fmt_size != 0x24) goto fail; if (ww.extra_size != 0x12) goto fail; - cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(ww.fmt_offset+0x18,sf); - cfg.encoder_delay = read_32bit(ww.fmt_offset+0x20,sf); + cfg.channels = ww.channels; + cfg.config_data = read_u32be(ww.fmt_offset + 0x18,sf); + cfg.encoder_delay = read_u32(ww.fmt_offset + 0x20,sf); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; - vgmstream->num_samples = read_32bit(ww.fmt_offset+0x1c,sf); + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x1c, sf); break; } #endif - case PTADPCM: /* substitutes IMA as default ADPCM codec */ + case PTADPCM: /* newer ADPCM [Bayonetta 2 (Switch), Genshin Impact (PC)] */ if (ww.bits_per_sample != 4) goto fail; if (ww.block_align != 0x24 * ww.channels && ww.block_align != 0x104 * ww.channels) goto fail; @@ -692,8 +576,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { } - - if ( !vgmstream_open_stream(vgmstream,sf,start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset) ) goto fail; return vgmstream; @@ -738,6 +621,219 @@ static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_o } +static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; + + if (read_u32be(0x00,sf) != 0x52494646 && /* "RIFF" (LE) */ + read_u32be(0x00,sf) != 0x52494658) /* "RIFX" (BE) */ + goto fail; + if (read_u32be(0x08,sf) != 0x57415645 && /* "WAVE" */ + read_u32be(0x08,sf) != 0x58574D41) /* "XWMA" */ + goto fail; + + ww->big_endian = read_u32be(0x00,sf) == 0x52494658; /* RIFX */ + if (ww->big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */ + read_u32 = read_u32be; + read_u16 = read_u16be; + } else { + read_u32 = read_u32le; + read_u16 = read_u16le; + } + + ww->file_size = get_streamfile_size(sf); + +#if 0 + /* Wwise's RIFF size is often wonky, seemingly depending on codec: + * - PCM, IMA/PTADPCM, VORBIS, AAC, OPUSNX/OPUS: correct + * - DSP, XWMA, ATRAC9: almost always slightly smaller (around 0x50) + * - HEVAG: very off + * - XMA2: exact file size + * - some RIFX have LE size + * (later we'll validate "data" which fortunately is correct) + */ + if (read_u32(0x04,sf) + 0x04 + 0x04 != ww->file_size) { + VGM_LOG("WWISE: bad riff size\n"); + goto fail; + } +#endif + + /* parse chunks (reads once linearly) */ + { + off_t offset = 0x0c; + while (offset < ww->file_size) { + uint32_t type = read_u32be(offset + 0x00,sf); + uint32_t size = read_u32 (offset + 0x04,sf); + offset += 0x08; + + switch(type) { + case 0x666d7420: /* "fmt " */ + ww->fmt_offset = offset; + ww->fmt_size = size; + break; + case 0x584D4132: /* "XMA2" */ + ww->xma2_offset = offset; + ww->xma2_size = size; + break; + case 0x64617461: /* "data" */ + ww->data_offset = offset; + ww->data_size = size; + break; + case 0x766F7262: /* "vorb" */ + ww->vorb_offset = offset; + ww->vorb_size = size; + break; + case 0x57696948: /* "WiiH" */ + ww->wiih_offset = offset; + ww->wiih_size = size; + break; + case 0x7365656B: /* "seek" */ + ww->seek_offset = offset; + ww->seek_size = size; + break; + case 0x736D706C: /* "smpl" */ + ww->smpl_offset = offset; + ww->smpl_size = size; + break; + + case 0x66616374: /* "fact" */ + /* Wwise shouldn't use fact, but if somehow some file does uncomment the following: */ + //if (size == 0x10 && read_u32be(offset + 0x04, sf) == 0x4C794E20) /* "LyN " */ + // goto fail; /* ignore LyN RIFF */ + goto fail; + + /* "XMAc": rare XMA2 physical loop regions (loop_start_b, loop_end_b, loop_subframe_data) + * Can appear even in the file doesn't loop, maybe it's meant to be the playable physical region */ + /* "LIST": leftover 'cue' info from OG .wavs (ex. loop starts in Platinum Games) */ + /* "JUNK": optional padding for aligment (0-size JUNK exists too) */ + /* "akd ": extra info for Wwise? (wave peaks/loudness/HDR envelope?) */ + default: + break; + } + + /* chunks are even-aligned and don't need to add padding byte, unlike real RIFFs */ + offset += size; + } + } + + + /* parse format (roughly spec-compliant but some massaging is needed) */ + if (ww->xma2_offset) { + /* pseudo-XMA2WAVEFORMAT, "fmt"+"XMA2" (common) or only "XMA2" [Too Human (X360)] */ + ww->format = 0x0165; /* signal for below */ + xma2_parse_xma2_chunk(sf, + ww->xma2_offset, &ww->channels, &ww->sample_rate, &ww->loop_flag, + &ww->num_samples, &ww->loop_start_sample, &ww->loop_end_sample); + } + else { + /* pseudo-WAVEFORMATEX */ + if (ww->fmt_size < 0x10) + goto fail; + ww->format = read_u16(ww->fmt_offset + 0x00,sf); + ww->channels = read_u16(ww->fmt_offset + 0x02,sf); + ww->sample_rate = read_u32(ww->fmt_offset + 0x04,sf); + ww->average_bps = read_u32(ww->fmt_offset + 0x08,sf); + ww->block_align = read_u16(ww->fmt_offset + 0x0c,sf); + ww->bits_per_sample = read_u16(ww->fmt_offset + 0x0e,sf); + if (ww->fmt_size > 0x10 && ww->format != 0x0165 && ww->format != 0x0166) /* ignore XMAWAVEFORMAT */ + ww->extra_size = read_u16(ww->fmt_offset + 0x10,sf); + if (ww->extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */ + /* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */ + ww->channel_layout = read_u32(ww->fmt_offset + 0x14,sf); + /* later games (+2018?) have a pseudo-format instead to handle more cases: + * - 8b: uNumChannels + * - 4b: eConfigType (0=none, 1=standard, 2=ambisonic) + * - 19b: uChannelMask */ + if ((ww->channel_layout & 0xFF) == ww->channels) { + ww->channel_layout = (ww->channel_layout >> 12); + } + } + + if (ww->format == 0x0166) { /* XMA2WAVEFORMATEX in fmt */ + xma2_parse_fmt_chunk_extra(sf, ww->fmt_offset, &ww->loop_flag, + &ww->num_samples, &ww->loop_start_sample, &ww->loop_end_sample, ww->big_endian); + } + } + + /* common loops ("XMA2" chunks already read them) */ + if (ww->smpl_offset) { + if (ww->smpl_size >= 0x34 + && read_u32(ww->smpl_offset + 0x1c, sf) == 1 /* loop count */ + && read_u32(ww->smpl_offset + 0x24 + 0x04, sf) == 0) { /* loop type */ + ww->loop_flag = 1; + ww->loop_start_sample = read_u32(ww->smpl_offset + 0x24 + 0x8, sf); + ww->loop_end_sample = read_u32(ww->smpl_offset + 0x24 + 0xc, sf) + 1; /* +1 like standard RIFF */ + } + } + + if (!ww->data_offset) + goto fail; + + + /* format to codec */ + switch(ww->format) { + case 0x0001: ww->codec = PCM; break; /* older Wwise */ + case 0x0002: ww->codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */ + case 0x0069: ww->codec = IMA; break; /* older Wwise [Spiderman Web of Shadows (X360), LotR Conquest (PC)] */ + case 0x0161: ww->codec = XWMA; break; /* WMAv2 */ + case 0x0162: ww->codec = XWMA; break; /* WMAPro */ + case 0x0165: ww->codec = XMA2; break; /* XMA2-chunk XMA (Wwise doesn't use XMA1) */ + case 0x0166: ww->codec = XMA2; break; /* fmt-chunk XMA */ + case 0xAAC0: ww->codec = AAC; break; + case 0xFFF0: ww->codec = DSP; break; + case 0xFFFB: ww->codec = HEVAG; break; + case 0xFFFC: ww->codec = ATRAC9; break; + case 0xFFFE: ww->codec = PCM; break; /* "PCM for Wwise Authoring" */ + case 0xFFFF: ww->codec = VORBIS; break; + case 0x3039: ww->codec = OPUSNX; break; /* renamed from "OPUS" on Wwise 2018.1 */ + case 0x3040: ww->codec = OPUS; break; + case 0x3041: ww->codec = OPUSWW; break; /* added on Wwise 2019.2.3, presumably replaces OPUS */ + case 0x8311: ww->codec = PTADPCM; break; /* added on Wwise 2019.1, replaces IMA */ + default: + goto fail; + } + + /* identify system's ADPCM */ + if (ww->format == 0x0002) { + if (ww->extra_size == 0x0c + ww->channels * 0x2e) { + /* newer Wwise DSP with coefs [Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS)] */ + ww->codec = DSP; + } else if (ww->extra_size == 0x0a && ww->wiih_offset) { /* WiiH */ + /* few older Wwise DSP with num_samples in extra_size [Tony Hawk: Shred (Wii)] */ + ww->codec = DSP; + } else if (ww->block_align == 0x104 * ww->channels) { + ww->codec = PTADPCM; /* Bayonetta 2 (Switch) */ + } + } + + + /* Some Wwise .bnk (RAM) files have truncated, prefetch mirrors of another file, that + * play while the rest of the real stream loads. We'll add basic support to avoid + * complaints of this or that .wem not playing */ + if (ww->data_offset + ww->data_size > ww->file_size) { + //;VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww->data_size, ww->file_size); + + /* catch wrong rips as truncated tracks' file_size should be much smaller than data_size, + * but it's possible to pre-fetch small files too [Punch Out!! (Wii)] */ + if (ww->data_offset + ww->data_size - ww->file_size < 0x5000 && ww->file_size > 0x10000) { + VGM_LOG("WWISE: wrong expected data_size\n"); + goto fail; + } + + if (ww->codec == PCM || ww->codec == IMA || ww->codec == VORBIS || ww->codec == DSP || ww->codec == XMA2 || + ww->codec == OPUSNX || ww->codec == OPUS || ww->codec == OPUSWW || ww->codec == PTADPCM) { + ww->truncated = 1; /* only seen those, probably all exist (XWMA, AAC, HEVAG, ATRAC9?) */ + } else { + VGM_LOG("WWISE: wrong size, maybe truncated\n"); + goto fail; + } + } + + return 1; +fail: + return 0; +} + /* VORBIS FORMAT RESEARCH */ /* diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xopus.c b/Frameworks/vgmstream/vgmstream/src/meta/xopus.c index 8ef98813a..9782b92d8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xopus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xopus.c @@ -1,63 +1,64 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* XOPUS - from Exient games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */ -VGMSTREAM * init_vgmstream_xopus(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag = 0, channel_count, sample_rate, num_samples, skip; - size_t data_size; - int entries; - - - /* checks*/ - if (!check_extensions(streamFile, "xopus")) - goto fail; - if (read_32bitBE(0x00, streamFile) != 0x584F7075) /* "XOpu" */ - goto fail; - - /* 0x04: always 0x01? */ - channel_count = read_8bit(0x05, streamFile); - /* 0x06: always 0x30? */ - /* 0x08: always 0xc8? max allowed packet size? */ - num_samples = read_32bitLE(0x0c, streamFile); - skip = read_32bitLE(0x10, streamFile); - entries = read_32bitLE(0x14, streamFile); - data_size = read_32bitLE(0x18, streamFile); - /* 0x1c: unused */ - /* 0x20+: packet sizes table */ - - sample_rate = 48000; - loop_flag = 0; - - start_offset = 0x20 + 0x02*entries; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_XOPUS; - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - -#ifdef VGM_USE_FFMPEG - { - vgmstream->codec_data = init_ffmpeg_x_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - } -#else - goto fail; -#endif - - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* XOPUS - from Exient games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */ +VGMSTREAM* init_vgmstream_xopus(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, table_offset; + int loop_flag = 0, channels, sample_rate, num_samples, skip; + size_t data_size; + int entries; + + + /* checks*/ + if (!check_extensions(sf, "xopus")) + goto fail; + if (read_u32be(0x00, sf) != 0x584F7075) /* "XOpu" */ + goto fail; + + /* 0x04: always 0x01? */ + channels = read_u8(0x05, sf); + /* 0x06: always 0x30? */ + /* 0x08: always 0xc8? max allowed packet size? */ + num_samples = read_s32le(0x0c, sf); + skip = read_s32le(0x10, sf); + entries = read_u32le(0x14, sf); + data_size = read_u32le(0x18, sf); + /* 0x1c: unused */ + /* 0x20+: packet sizes table */ + + sample_rate = 48000; + loop_flag = 0; + + table_offset = 0x20; + start_offset = table_offset + 0x02*entries; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XOPUS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + +#ifdef VGM_USE_FFMPEG + { + vgmstream->codec_data = init_ffmpeg_x_opus(sf, table_offset, entries, start_offset, data_size, vgmstream->channels, skip); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + } +#else + goto fail; +#endif + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xse.c b/Frameworks/vgmstream/vgmstream/src/meta/xse.c new file mode 100644 index 000000000..1b5a46968 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xse.c @@ -0,0 +1,301 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* SDRH - banks for newer feelplus-related games [Mindjack (PS3/X360)] */ +VGMSTREAM* init_vgmstream_xse_new(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size, stream_size; + int loop_flag = 0, channels, codec, sample_rate, seek_count; + int32_t num_samples, loop_start, loop_end; + off_t offset; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf, "xse")) + goto fail; + if (read_u32be(0x00,sf) != 0x48524453) /* "HRDS" */ + goto fail; + + /* similar to older version but BE and a bit less complex */ + /* 0x04: version/config? + * 0x08: data size + * 0x30: file name in some strange encoding/compression? + * others: ? (change in old/new) + */ + + /* parse section */ + { + int i; + int tables = read_u16be(0x1C,sf); + off_t base_size, stream_offset; + int entries; + + offset = 0; + /* read sections (FE=cues?, WV=mini-headers?, XW=waves) */ + for (i = 0; i < tables; i++) { + uint16_t id = read_u16be(0x40 + 0x10 * i + 0x00,sf); + /* 0x02: offset in 0x40s */ + /* 0x04: section size */ + /* 0x08: always 1 */ + /* 0x0c: null */ + if (id == 0x5857) { /* "XW" */ + offset += read_u16be(0x40 + 0x10 * i + 0x02,sf) * 0x40; + break; + } + } + + /* section header (other sections have a similar header) */ + /* 0x00: section size */ + base_size = read_u16be(offset + 0x04,sf); + entries = read_u16be(offset + 0x06,sf); + /* 0x08: null */ + start_offset = read_u32be(offset + 0x0c,sf) + offset; /* size including padding up to start */ + + offset += base_size; + + total_subsongs = entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* find stream header (entries can be variable-sized) */ + for (i = 0; i < entries; i++) { + size_t seek_size = read_u16be(offset + 0x0a,sf) * 0x04; + size_t entry_size = align_size_to_block(0x30 + seek_size, 0x10); + + if (i + 1 == target_subsong) + break; + offset += entry_size; + } + + /* parse target header (similar to xwav) */ + stream_size = read_u32be(offset + 0x00,sf); + /* 0x04: codec? (16=PS3, 03=X360) */ + codec = read_u8(offset + 0x06,sf); /* assumed */ + loop_flag = read_u8(offset + 0x07,sf); /* assumed */ + /* 0x08: bps? */ + channels = read_u8(offset + 0x09,sf); + seek_count = read_u16be(offset + 0x0a,sf); + num_samples = read_u32be(offset + 0x0c,sf); + sample_rate = read_u32be(offset + 0x10,sf); + loop_start = read_u32be(offset + 0x14,sf); + loop_end = read_u32be(offset + 0x18,sf); + /* 0x1c: ? */ + stream_offset = read_u32be(offset + 0x20,sf); /* within data */ + /* 0x24: ? */ + /* 0x26 seek entries */ + /* 0x28: ? */ + /* 0x2c: null? */ + + start_offset += stream_offset; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SDRH; + vgmstream->num_samples = num_samples; + vgmstream->sample_rate = sample_rate; + + vgmstream->stream_size = stream_size; + vgmstream->num_streams = total_subsongs; + + switch(codec) { + case 2: /* Mindjack (PS3) */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + break; + +#ifdef VGM_USE_FFMPEG + case 1: { /* Mindjack (X360) */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf) - start_offset; + block_size = 0x10000; /* XWAV new default */ + block_count = seek_count; + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + //todo fix loops/samples vs ATRAC3 + /* may be only applying end_skip to num_samples? */ + xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* SDRH - banks for older feelplus-related games [Lost Odyssey (X360)] */ +VGMSTREAM* init_vgmstream_xse_old(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size, stream_size; + int loop_flag = 0, channels, codec, sample_rate, seek_count; + int32_t num_samples, loop_start, loop_end; + off_t offset; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + /* .xse: assumed */ + if (!check_extensions(sf, "xse")) + goto fail; + if (read_u32be(0x00,sf) != 0x53445248) /* "SDRH" */ + goto fail; + + /* similar to older version but LE and a bit more complex */ + /* 0x04: version/config? + * 0x08: data size + * 0x30: file name in some strange encoding/compression? + * others: ? (change in old/new) + */ + + /* parse section */ + { + int i; + int tables = read_u8(0x15,sf); + off_t base_size, stream_offset; + int entries; + + offset = 0x40; + /* read sections (FE=cues?, WV=mini-headers?, FT=?, FQ=?, XW=waves) */ + for (i = 0; i < tables; i++) { + uint16_t id = read_u16be(0x40 + 0x08 * i + 0x00,sf); + /* 0x02: null? */ + /* 0x04: offset from table start */ + if (id == 0x5857) { /* "XW" */ + offset += read_u32le(0x40 + 0x08 * i + 0x04,sf); + break; + } + } + + /* section header (other sections have a similar header) */ + /* 0x00: section size */ + base_size = read_u16le(offset + 0x04,sf); + /* 0x06: ? */ + entries = read_u16le(offset + 0x08,sf); + start_offset = read_u32le(offset + 0x0c,sf) + offset; /* size including padding up to start */ + + offset += base_size; + + total_subsongs = entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* find stream header */ + stream_offset = 0; + for (i = 0; i < entries; i++) { + size_t data_size = read_u32le(offset + 0x00,sf) - 0x30; + size_t seek_size = 0; //read_u16be(offset + 0x0a,sf) * 0x04; /* not seen */ + size_t entry_size = align_size_to_block(0x30 + seek_size, 0x10); + + if (i + 1 == target_subsong) + break; + offset += entry_size; + stream_offset += data_size; /* no offset? */ + } + + /* parse target header (similar to xwav) */ + stream_size = read_u32le(offset + 0x00,sf) - 0x30; /* adds entry size */ + /* 0x04: codec? (16=PS3, 03=X360) */ + codec = read_u8(offset + 0x06,sf); /* assumed */ + /* 0x07: flag? */ + /* 0x08: bps? */ + /* 0x09: codec? */ + /* 0x0a: null */ + num_samples = read_u32le(offset + 0x0c,sf); + seek_count = read_u16le(offset + 0x10,sf); + sample_rate = read_u32le(offset + 0x14,sf); + loop_start = 0; //read_u32le(offset + 0x18,sf); /* ? */ + loop_end = 0; //read_u32le(offset + 0x1c,sf); /* ? */ + /* 0x20: null */ + /* 0x24: ? */ + /* 0x26: channel layout */ + channels = read_u8(offset + 0x27,sf); + /* 0x28: ? */ + /* 0x2c: null? */ + + loop_flag = loop_end > 0; + + start_offset += stream_offset; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SDRH; + vgmstream->num_samples = num_samples; + vgmstream->sample_rate = sample_rate; + + vgmstream->stream_size = stream_size; + vgmstream->num_streams = total_subsongs; + + switch(codec) { + +#ifdef VGM_USE_FFMPEG + case 4: { /* Lost Odyssey (X360) */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf) - start_offset; + block_size = 0x8000; /* XWAV old default */ + block_count = seek_count; + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, 0, 0, 1); + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwav.c b/Frameworks/vgmstream/vgmstream/src/meta/xwav.c new file mode 100644 index 000000000..d3a0c8e4f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwav.c @@ -0,0 +1,260 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* XWAV - streams for newer feelplus-related games [No More Heroes: Heroes Paradise (PS3/X360), Moon Diver (PS3/X360)] */ +VGMSTREAM* init_vgmstream_xwav_new(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size; + int loop_flag = 0, channels, codec, sample_rate; + int32_t num_samples, loop_start, loop_end; + + + /* checks */ + /* .xwv: actual extension [Moon Diver (PS3/X360)] + * .vawx: header id */ + if (!check_extensions(sf, "xwv,vawx")) + goto fail; + if (read_u32be(0x00,sf) != 0x56415758) /* "VAWX" */ + goto fail; + + /* similar to older version but BE and a bit less complex */ + /* 0x04: data size + * 0x08: version (always 3) + * 0x0a: sub-version (0 in NMH/NNN2, 5 in MD) + * 0x0c: ? (0080 + some value) + * 0x10: ? (00402000) + * 0x14: ? (3380) + * 0x16: file number + * 0x18: null + * 0x1c: null + * 0x20: file name in some strange encoding/compression? + */ + start_offset = 0x800; + + /* parse header */ + { + /* 0x00: stream size */ + /* 0x04: ? */ + codec = read_u8(0x30 + 0x06,sf); + loop_flag = read_u8(0x30 + 0x07,sf); + /* 0x08: ? */ + channels = read_u8(0x30 + 0x09,sf); + /* 0x0a: seek entries */ + num_samples = read_u32be(0x30 + 0x0c,sf); + sample_rate = read_u32be(0x30 + 0x10,sf); + loop_start = read_u32be(0x30 + 0x14,sf); + loop_end = read_u32be(0x30 + 0x18,sf); + /* rest: ? (also see xse) */ + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XWAV; + vgmstream->num_samples = num_samples; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case 2: /* No Nore Heroes (PS3) */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = channels == 6 ? layout_blocked_xwav : layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + break; + +#ifdef VGM_USE_FFMPEG + case 1: { /* No Nore Heroes (X360), Moon Diver (X360), Ninety-Nine Nights 2 (X360) */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf) - start_offset; + block_size = 0x10000; /* XWAV new default */ + block_count = read_u16be(0x30 + 0x0A, sf); /* also at 0x56 */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + //todo fix loops/samples vs ATRAC3 + /* may be only applying end_skip to num_samples? */ + xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); + break; + } + + case 7: { /* Moon Diver (PS3) */ + int block_align, encoder_delay; + + data_size = read_u32be(0x54,sf); + block_align = 0x98 * vgmstream->channels; + encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; /* original samples break looping in some files otherwise */ + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* XWAV - streams for older feelplus-related games [Bullet Witch (X360), Lost Odyssey (X360)] */ +VGMSTREAM* init_vgmstream_xwav_old(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size; + int loop_flag = 0, channels, codec, tracks, sample_rate; + int32_t num_samples, loop_start, loop_end; + + + /* checks */ + /* .xwv: actual extension [Bullet Witch (X360)] */ + if (!check_extensions(sf, "xwv")) + goto fail; + if (read_u32be(0x00,sf) != 0x58574156) /* "XWAV" */ + goto fail; + + /* similar to newer version but LE and a bit more complex */ + /* 0x04: data size + * 0x08: version (always 2) + * 0x0a: sub-version? (0x100/200/300 in LO, 0x200 in BW) + * 0x0c: ? + * 0x10: start offset (in 0x10s) + * 0x12: ? (low number) + * 0x20: stream size + * 0x24: ? + * 0x26: codec? + * 0x27: tracks + * rest varies depending on codec + */ + start_offset = read_u16le(0x10,sf) * 0x10; + + codec = read_u8(0x26,sf); + tracks = read_u8(0x27,sf); + + switch(codec) { + case 2: /* PSX */ + /* 0x2c: null? */ + num_samples = read_u32le(0x30,sf); + sample_rate = read_u16le(0x34,sf); + channels = read_u8(0x37,sf); + loop_start = read_u32le(0x38,sf); + loop_end = read_u32le(0x3c,sf); + if (tracks > 1) + goto fail; + break; + + case 4: /* XMA */ + num_samples = read_u32le(0x2c,sf); + /* 0x30: xma blocks of 0x8000 */ + sample_rate = read_u16le(0x34,sf); + /* 0x38: ? (0x10/20) */ + /* 0x3c: null */ + loop_start = read_u32le(0x48,sf); /* per stream, but all should match */ + loop_end = read_u32le(0x4C,sf); + + /* listed as XMA streams like XMA1, but XMA2 shouldn't need this (uses proper Nch XMA2) */ + { + channels = 0; + for (int i = 0; i < tracks; i++) { + /* 0x00: null */ + /* 0x04: null */ + /* 0x06: channel layout null */ + channels += read_u8(0x40 + 0x10 * i + 0x07,sf); + /* 0x08: loop start */ + /* 0x0c: loop end */ + } + } + + /* next is a seek table, padded to 0x10 */ + break; + + default: + goto fail; + } + + loop_flag = loop_end > 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XWAV; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + switch(codec) { + case 2: /* Bullet Witch (X360) (seems unused as there are .xwb) */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels); + break; + +#ifdef VGM_USE_FFMPEG + case 4: { /* Lost Odyssey (X360) */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf) - start_offset; + block_size = 0x8000; /* XWAV old default */ + block_count = read_u16be(0x30, sf); + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, 0, 0, 1); + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 36a62bcfd..7d951aafb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -342,7 +342,8 @@ VGMSTREAM* init_vgmstream_xwb(STREAMFILE* sf) { xwb.loop_end = 0; } else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 - && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 + && (xwb.bits_per_sample == 0x00 || xwb.bits_per_sample == 0x01) /* bps=0+ba=2 in mono? (Blossom Tales) */ + && (xwb.block_align == 0x02 || xwb.block_align == 0x04) && read_u32le(xwb.stream_offset + 0x08, sf) == xwb.sample_rate /* DSP header */ && read_u16le(xwb.stream_offset + 0x0e, sf) == 0 && read_u32le(xwb.stream_offset + 0x18, sf) == 2 diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h index c4f56cd1d..b68e30597 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h @@ -905,6 +905,10 @@ static STREAMFILE * open_xsb_filename_pair(STREAMFILE *streamXwb) { char temp_filename[PATH_LIMIT]; int target_len; + /* try parsing TXTM if present */ + streamXsb = read_filemap_file(streamXwb, 0); + if (streamXsb) return streamXsb; + /* try names in external .xsb, using a bunch of possible name pairs */ get_streamfile_filename(streamXwb,target_filename,PATH_LIMIT); target_len = strlen(target_filename); diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.c b/Frameworks/vgmstream/vgmstream/src/plugins.c index 085c3dad2..f9199a3ce 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.c +++ b/Frameworks/vgmstream/vgmstream/src/plugins.c @@ -68,6 +68,7 @@ void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM char* pos2; char temp[1024]; + buf[0] = '\0'; /* name without path */ pos = strrchr(filename, '\\'); @@ -80,23 +81,31 @@ void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM strncpy(buf, pos, buf_len); /* name without extension */ - pos2 = strrchr(buf, '.'); - if (pos2) - pos2[0] = '\0'; + if (cfg->remove_extension) { + pos2 = strrchr(buf, '.'); + if (pos2 && strlen(pos2) < 15) /* too big extension = file name probably has a dot in the middle */ + pos2[0] = '\0'; + } { const char* stream_name = vgmstream->stream_name; int total_subsongs = vgmstream->num_streams; int target_subsong = vgmstream->stream_index; //int is_first = vgmstream->stream_index == 0; - //int is_txtp = ; //todo don't show number/name for txtp but show for mini-txtp int show_name; + /* special considerations for TXTP: + * - full txtp: don't show subsong number, nor name (assumes one names .txtp as wanted) + * - mini txtp: don't show subsong number, but show name (assumes one choses song #n in filename, but wants title) + */ + int full_txtp = vgmstream->config.is_txtp && !vgmstream->config.is_mini_txtp; + int mini_txtp = vgmstream->config.is_mini_txtp; + if (target_subsong == 0) target_subsong = 1; /* show number if file has more than 1 subsong */ - if (total_subsongs > 1) { + if (total_subsongs > 1 && !(full_txtp || mini_txtp)) { if (cfg && cfg->subsong_range) snprintf(temp, sizeof(temp), "%s#1~%i", buf, total_subsongs); else @@ -105,13 +114,19 @@ void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM } /* show name for some cases */ - show_name = (total_subsongs > 0 && (!cfg || !cfg->subsong_range)) || - (cfg && cfg->force_title); + show_name = (total_subsongs > 0) && (!cfg || !cfg->subsong_range); + if (full_txtp) + show_name = 0; + if (cfg && cfg->force_title) + show_name = 1; + if (stream_name[0] != '\0' && show_name) { snprintf(temp, sizeof(temp), "%s (%s)", buf, stream_name); strncpy(buf, temp, buf_len); } } + + buf[buf_len - 1] = '\0'; } @@ -174,6 +189,9 @@ static void load_default_config(play_config_t* def, play_config_t* tcfg) { copy_time(&def->trim_begin_set, &def->trim_begin, &def->trim_begin_s, &tcfg->trim_begin_set, &tcfg->trim_begin, &tcfg->trim_begin_s); copy_time(&def->trim_end_set, &def->trim_end, &def->trim_end_s, &tcfg->trim_end_set, &tcfg->trim_end, &tcfg->trim_end_s); copy_time(&def->body_time_set, &def->body_time, &def->body_time_s, &tcfg->body_time_set, &tcfg->body_time, &tcfg->body_time_s); + + def->is_mini_txtp = tcfg->is_mini_txtp; + def->is_txtp = tcfg->is_txtp; } static void load_player_config(play_config_t* def, vgmstream_cfg_t* vcfg) { diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.h b/Frameworks/vgmstream/vgmstream/src/plugins.h index fff652e26..b31b4862d 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.h +++ b/Frameworks/vgmstream/vgmstream/src/plugins.h @@ -71,6 +71,7 @@ void vgmstream_set_play_forever(VGMSTREAM* vgmstream, int enabled); typedef struct { int force_title; int subsong_range; + int remove_extension; } vgmstream_title_t; /* get a simple title for plugins */ diff --git a/Frameworks/vgmstream/vgmstream/src/render.c b/Frameworks/vgmstream/vgmstream/src/render.c index 1bcd8ffea..308f32f4e 100644 --- a/Frameworks/vgmstream/vgmstream/src/render.c +++ b/Frameworks/vgmstream/vgmstream/src/render.c @@ -294,7 +294,7 @@ static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstre case layout_blocked_ea_sns: case layout_blocked_awc: case layout_blocked_vgs: - case layout_blocked_vawx: + case layout_blocked_xwav: case layout_blocked_xvag_subsong: case layout_blocked_ea_wve_au00: case layout_blocked_ea_wve_ad10: @@ -404,13 +404,21 @@ static int render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { } } -static int render_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do) { +static int render_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { + play_state_t* ps = &vgmstream->pstate; int channels = vgmstream->pstate.output_channels; + int start = 0; - /* since anything beyond pad end is silence no need to check ranges */ + /* since anything beyond pad end is silence no need to check end */ + if (ps->play_position < ps->pad_end_start) { + start = samples_done - (ps->play_position + samples_done - ps->pad_end_start); + } + else { + start = 0; + } - memset(buf, 0, samples_to_do * sizeof(sample_t) * channels); - return samples_to_do; + memset(buf + (start * channels), 0, (samples_done - start) * channels * sizeof(sample_t)); + return samples_done; } @@ -445,9 +453,10 @@ int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */ } - /* end padding (done before to avoid decoding if possible) */ + /* end padding (done before to avoid decoding if possible, samples_to_do becomes 0) */ if (!vgmstream->config.play_forever /* && ps->pad_end_left */ - && ps->play_position + samples_done >= ps->pad_end_start) { + && ps->play_position + samples_done >= ps->pad_end_start + && samples_to_do) { done = render_pad_end(vgmstream, tmpbuf, samples_to_do); samples_done += done; samples_to_do -= done; @@ -462,10 +471,16 @@ int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) samples_done += done; - /* simple fadeout */ - if (!vgmstream->config.play_forever && ps->fade_left - && ps->play_position + done >= ps->fade_start) { - render_fade(vgmstream, tmpbuf, done); + if (!vgmstream->config.play_forever) { + /* simple fadeout */ + if (ps->fade_left && ps->play_position + done >= ps->fade_start) { + render_fade(vgmstream, tmpbuf, done); + } + + /* silence leftover buf samples (rarely used when no fade is set) */ + if (ps->play_position + done >= ps->pad_end_start) { + render_pad_end(vgmstream, tmpbuf, done); + } } tmpbuf += done * vgmstream->pstate.output_channels; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 1f9b36e85..9e7cf0042 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -890,7 +890,7 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * fil char partname[PATH_LIMIT]; char *path, *name; - if (!streamfile || !filename) return NULL; + if (!streamfile || !filename || !filename[0]) return NULL; streamfile->get_name(streamfile, fullname, sizeof(fullname)); @@ -1122,6 +1122,76 @@ fail: return 0; } +STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num) { + char filename[PATH_LIMIT]; + off_t txt_offset, file_size; + STREAMFILE *sf_map = NULL; + + sf_map = open_streamfile_by_filename(sf, ".txtm"); + if (!sf_map) goto fail; + + get_streamfile_filename(sf, filename, sizeof(filename)); + + txt_offset = 0x00; + file_size = get_streamfile_size(sf_map); + + /* skip BOM if needed */ + if ((uint16_t)read_16bitLE(0x00, sf_map) == 0xFFFE || + (uint16_t)read_16bitLE(0x00, sf_map) == 0xFEFF) { + txt_offset = 0x02; + } else if (((uint32_t)read_32bitBE(0x00, sf_map) & 0xFFFFFF00) == 0xEFBBBF00) { + txt_offset = 0x03; + } + + /* read lines and find target filename, format is (filename): value1, ... valueN */ + while (txt_offset < file_size) { + char line[0x2000]; + char key[PATH_LIMIT] = { 0 }, val[0x2000] = { 0 }; + int ok, bytes_read, line_ok; + + bytes_read = read_line(line, sizeof(line), txt_offset, sf_map, &line_ok); + if (!line_ok) goto fail; + + txt_offset += bytes_read; + + /* get key/val (ignores lead spaces, stops at space/comment/separator) */ + ok = sscanf(line, " %[^ \t#:] : %[^\t#\r\n] ", key, val); + if (ok != 2) { /* ignore line if no key=val (comment or garbage) */ + continue; + } + + if (strcmp(key, filename) == 0) { + int n; + char subval[PATH_LIMIT]; + const char *current = val; + int i; + + for (i = 0; i <= file_num; i++) { + if (current[0] == '\0') + goto fail; + + ok = sscanf(current, " %[^\t#\r\n,]%n ", subval, &n); + if (ok != 1) + goto fail; + + if (i == file_num) + { + close_streamfile(sf_map); + return open_streamfile_by_filename(sf, subval); + } + + current += n; + if (current[0] == ',') + current++; + } + } + } + +fail: + close_streamfile(sf_map); + return NULL; +} + void fix_dir_separators(char * filename) { char c; int i = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 72b7d343b..83d684171 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -345,6 +345,10 @@ size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE* * returns size of key if found and copied */ size_t read_key_file(uint8_t* buf, size_t buf_size, STREAMFILE* sf); +/* Opens .txtm file containing file:companion file(-s) mappings and tries to see if there's a match + * then loads the associated companion file if one is found */ +STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num); + /* hack to allow relative paths in various OSs */ void fix_dir_separators(char* filename); diff --git a/Frameworks/vgmstream/vgmstream/src/streamtypes.h b/Frameworks/vgmstream/vgmstream/src/streamtypes.h index 9907b2eeb..1de682f00 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamtypes.h +++ b/Frameworks/vgmstream/vgmstream/src/streamtypes.h @@ -22,8 +22,8 @@ #include #endif -#ifndef inline /* (_MSC_VER < 1900)? */ -#define inline _inline +#if (_MSC_VER < 1800) && !defined(__cplusplus) +#define inline __inline #endif #define strcasecmp _stricmp diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index fb46a8684..f82f4ba5d 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -167,7 +167,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_seg, init_vgmstream_nds_strm_ffta2, init_vgmstream_str_asr, - init_vgmstream_zwdsp, init_vgmstream_gca, init_vgmstream_spt_spd, init_vgmstream_ish_isd, @@ -273,7 +272,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_ps2_iab, init_vgmstream_vs_str, init_vgmstream_lsf_n1nj4n, - init_vgmstream_vawx, + init_vgmstream_xwav_new, + init_vgmstream_xwav_old, init_vgmstream_ps2_wmus, init_vgmstream_hyperscan_kvag, init_vgmstream_ios_psnd, @@ -318,11 +318,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_ogl, init_vgmstream_mc3, init_vgmstream_gtd, - init_vgmstream_ta_aac_x360, - init_vgmstream_ta_aac_ps3, - init_vgmstream_ta_aac_mobile, - init_vgmstream_ta_aac_mobile_vorbis, - init_vgmstream_ta_aac_vita, + init_vgmstream_ta_aac, init_vgmstream_va3, init_vgmstream_mta2, init_vgmstream_mta2_container, @@ -392,7 +388,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_txtp, init_vgmstream_smc_smh, init_vgmstream_ppst, - init_vgmstream_opus_sps_n1_segmented, + init_vgmstream_sps_n1_segmented, init_vgmstream_ubi_bao_pk, init_vgmstream_ubi_bao_atomic, init_vgmstream_dsp_switch_audio, @@ -508,6 +504,14 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_zwv, init_vgmstream_dsb, init_vgmstream_bsf, + init_vgmstream_xse_new, + init_vgmstream_xse_old, + init_vgmstream_wady, + init_vgmstream_dsp_sqex, + init_vgmstream_dsp_wiivoice, + init_vgmstream_xws, + init_vgmstream_cpk, + init_vgmstream_opus_nsopus, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ @@ -519,6 +523,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_raw_pcm, /* .raw raw PCM */ init_vgmstream_s14_sss, /* .s14/sss raw siren14 */ init_vgmstream_raw_al, /* .al/al2 raw A-LAW */ + init_vgmstream_zwdsp, /* fake format */ #ifdef VGM_USE_FFMPEG init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ #endif @@ -1039,6 +1044,95 @@ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) { } } +void describe_vgmstream_info(VGMSTREAM* vgmstream, vgmstream_info* info) { + if (!info) { + return; + } + + memset(info, 0, sizeof(*info)); + + if (!vgmstream) { + return; + } + + info->sample_rate = vgmstream->sample_rate; + + info->channels = vgmstream->channels; + + { + int output_channels = 0; + mixing_info(vgmstream, NULL, &output_channels); + + if (output_channels != vgmstream->channels) { + info->mixing_info.input_channels = vgmstream->channels; + info->mixing_info.output_channels = output_channels; + } + } + + info->channel_layout = vgmstream->channel_layout; + + if (vgmstream->loop_start_sample >= 0 && vgmstream->loop_end_sample > vgmstream->loop_start_sample) { + info->loop_info.start = vgmstream->loop_start_sample; + info->loop_info.end = vgmstream->loop_end_sample; + } + + info->num_samples = vgmstream->num_samples; + + get_vgmstream_coding_description(vgmstream, info->encoding, sizeof(info->encoding)); + + get_vgmstream_layout_description(vgmstream, info->layout, sizeof(info->layout)); + + if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) { + info->interleave_info.value = vgmstream->interleave_block_size; + + if (vgmstream->interleave_first_block_size && vgmstream->interleave_first_block_size != vgmstream->interleave_block_size) { + info->interleave_info.first_block = vgmstream->interleave_first_block_size; + } + + if (vgmstream->interleave_last_block_size && vgmstream->interleave_last_block_size != vgmstream->interleave_block_size) { + info->interleave_info.last_block = vgmstream->interleave_last_block_size; + } + } + + /* codecs with configurable frame size */ + if (vgmstream->frame_size > 0 || vgmstream->interleave_block_size > 0) { + int32_t frame_size = vgmstream->frame_size > 0 ? vgmstream->frame_size : vgmstream->interleave_block_size; + switch (vgmstream->coding_type) { + case coding_MSADPCM: + case coding_MSADPCM_int: + case coding_MSADPCM_ck: + case coding_MS_IMA: + case coding_MC3: + case coding_WWISE_IMA: + case coding_REF_IMA: + case coding_PSX_cfg: + info->frame_size = frame_size; + break; + default: + break; + } + } + + get_vgmstream_meta_description(vgmstream, info->metadata, sizeof(info->metadata)); + + info->bitrate = get_vgmstream_average_bitrate(vgmstream); + + /* only interesting if more than one */ + if (vgmstream->num_streams > 1) { + info->stream_info.total = vgmstream->num_streams; + } + else { + info->stream_info.total = 1; + } + + if (vgmstream->num_streams > 1) { + info->stream_info.current = vgmstream->stream_index == 0 ? 1 : vgmstream->stream_index; + } + + if (vgmstream->stream_name[0] != '\0') { + snprintf(info->stream_info.name, sizeof(info->stream_info.name), "%s", vgmstream->stream_name); + } +} /* See if there is a second file which may be the second channel, given an already opened mono vgmstream. * If a suitable file is found, open it and change opened_vgmstream to a stereo vgmstream. */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index c44c4643e..8c1eb292d 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -182,11 +182,12 @@ typedef enum { coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ coding_SDX2_int, /* SDX2 2:1 Squareroot-Delta-Exact compression with sample-level interleave */ coding_CBD2, /* CBD2 2:1 Cuberoot-Delta-Exact compression DPCM */ - coding_CBD2_int, /* CBD2 2:1 Cuberoot-Delta-Exact compression, with sample-level interleave */ + coding_CBD2_int, /* CBD2 2:1 Cuberoot-Delta-Exact compression, with sample-level interleave */ coding_SASSC, /* Activision EXAKT SASSC 8-bit DPCM */ coding_DERF, /* DERF 8-bit DPCM */ + coding_WADY, /* WADY 8-bit DPCM */ + coding_NWA, /* VisualArt's NWA DPCM */ coding_ACM, /* InterPlay ACM */ - coding_NWA, /* VisualArt's NWA */ coding_CIRCUS_ADPCM, /* Circus 8-bit ADPCM */ coding_UBI_ADPCM, /* Ubisoft 4/6-bit ADPCM */ @@ -276,7 +277,7 @@ typedef enum { layout_blocked_ea_sns, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ layout_blocked_awc, /* Rockstar AWC */ layout_blocked_vgs, /* Guitar Hero II (PS2) */ - layout_blocked_vawx, /* No More Heroes 6ch (PS3) */ + layout_blocked_xwav, layout_blocked_xvag_subsong, /* XVAG subsongs [God of War III (PS4)] */ layout_blocked_ea_wve_au00, /* EA WVE au00 blocks */ layout_blocked_ea_wve_ad10, /* EA WVE Ad10 blocks */ @@ -566,7 +567,7 @@ typedef enum { meta_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */ meta_VS_STR, /* The Bouncer */ meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ - meta_VAWX, /* feelplus: No More Heroes Heroes Paradise, Moon Diver */ + meta_XWAV, meta_RAW_SNDS, meta_PS2_WMUS, /* The Warriors (PS2) */ meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */ @@ -609,9 +610,7 @@ typedef enum { meta_OGL, /* Shin'en Wii/WiiU (Jett Rocket (Wii), FAST Racing NEO (WiiU)) */ meta_MC3, /* Paradigm games (T3 PS2, MX Rider PS2, MI: Operation Surma PS2) */ meta_GTD, /* Knights Contract (X360/PS3), Valhalla Knights 3 (PSV) */ - meta_TA_AAC_X360, /* tri-Ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ - meta_TA_AAC_PS3, /* tri-Ace AAC (Star Ocean International, Resonance of Fate) */ - meta_TA_AAC_MOBILE, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */ + meta_TA_AAC, meta_MTA2, meta_NGC_ULW, /* Burnout 1 (GC only) */ meta_XA_XA30, @@ -661,10 +660,9 @@ typedef enum { meta_TXTP, /* generic text playlist */ meta_SMC_SMH, /* Wangan Midnight (System 246) */ meta_PPST, /* PPST [Parappa the Rapper (PSP)] */ - meta_OPUS_PPP, /* .at9 Opus [Penny-Punching Princess (Switch)] */ + meta_SPS_N1, meta_UBI_BAO, /* Ubisoft BAO */ meta_DSP_SWITCH_AUDIO, /* Gal Gun 2 (Switch) */ - meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */ meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */ meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */ meta_XMD, /* Konami XMD [Silent Hill 4 (Xbox), Castlevania: Curse of Darkness (Xbox)] */ @@ -743,6 +741,10 @@ typedef enum { meta_KAT, meta_PCM_SUCCESS, meta_ADP_KONAMI, + meta_SDRH, + meta_WADY, + meta_DSP_SQEX, + meta_DSP_WIIVOICE, } meta_t; /* standard WAVEFORMATEXTENSIBLE speaker positions */ @@ -825,6 +827,9 @@ typedef struct { int fade_time_set; int pad_end_set; + /* for lack of a better place... */ + int is_txtp; + int is_mini_txtp; } play_config_t; @@ -847,6 +852,7 @@ typedef struct { int32_t play_duration; /* total samples that the stream lasts (after applying all config) */ int32_t play_position; /* absolute sample where stream is */ + } play_state_t; @@ -1098,6 +1104,36 @@ typedef struct { #endif #endif //VGM_USE_MP4V2 +// VGMStream description in structure format +typedef struct { + int sample_rate; + int channels; + struct mixing_info { + int input_channels; + int output_channels; + } mixing_info; + int channel_layout; + struct loop_info { + int start; + int end; + } loop_info; + size_t num_samples; + char encoding[128]; + char layout[128]; + struct interleave_info { + int value; + int first_block; + int last_block; + } interleave_info; + int frame_size; + char metadata[128]; + int bitrate; + struct stream_info { + int current; + int total; + char name[128]; + } stream_info; +} vgmstream_info; /* -------------------------------------------------------------------------*/ /* vgmstream "public" API */ @@ -1127,6 +1163,7 @@ void seek_vgmstream(VGMSTREAM* vgmstream, int32_t seek_sample); /* Write a description of the stream into array pointed by desc, which must be length bytes long. * Will always be null-terminated if length > 0 */ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length); +void describe_vgmstream_info(VGMSTREAM* vgmstream, vgmstream_info* desc); /* Return the average bitrate in bps of all unique files contained within this stream. */ int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream);