Updated VGMStream to r1050-3424-gd7bd5a2a
parent
85907a48d9
commit
55ebd62eba
|
@ -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 = "<group>"; };
|
||||
8306B0CA2098458E000302D4 /* ubi_lyn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_lyn.c; sourceTree = "<group>"; };
|
||||
8306B0CB2098458E000302D4 /* msb_msh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msb_msh.c; sourceTree = "<group>"; };
|
||||
8306B0CC2098458E000302D4 /* opus_ppp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opus_ppp.c; sourceTree = "<group>"; };
|
||||
8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opus_interleave_streamfile.h; sourceTree = "<group>"; };
|
||||
8306B0CE2098458E000302D4 /* opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opus.c; sourceTree = "<group>"; };
|
||||
8306B0CF2098458F000302D4 /* caf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = caf.c; sourceTree = "<group>"; };
|
||||
|
@ -836,7 +839,6 @@
|
|||
831BA6111EAC61A500CF89B0 /* sgxd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sgxd.c; sourceTree = "<group>"; };
|
||||
831BA6121EAC61A500CF89B0 /* sxd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sxd.c; sourceTree = "<group>"; };
|
||||
831BA6131EAC61A500CF89B0 /* ubi_raki.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_raki.c; sourceTree = "<group>"; };
|
||||
831BA6141EAC61A500CF89B0 /* vawx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vawx.c; sourceTree = "<group>"; };
|
||||
831BA6151EAC61A500CF89B0 /* x360_cxs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_cxs.c; sourceTree = "<group>"; };
|
||||
831BA6171EAC61A500CF89B0 /* x360_pasx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_pasx.c; sourceTree = "<group>"; };
|
||||
831BA6221EAC61CB00CF89B0 /* coding_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coding_utils.c; sourceTree = "<group>"; };
|
||||
|
@ -884,7 +886,6 @@
|
|||
8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_1snh.c; sourceTree = "<group>"; };
|
||||
8349A8E41FE6253800E26435 /* blocked_ea_schl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_schl.c; sourceTree = "<group>"; };
|
||||
8349A8E51FE6253800E26435 /* blocked_ivaud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ivaud.c; sourceTree = "<group>"; };
|
||||
8349A8E61FE6253900E26435 /* blocked_vawx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vawx.c; sourceTree = "<group>"; };
|
||||
8349A8E71FE6253900E26435 /* blocked_ea_sns.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_sns.c; sourceTree = "<group>"; };
|
||||
8349A8EE1FE6257C00E26435 /* dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dec.c; sourceTree = "<group>"; };
|
||||
8349A8EF1FE6257C00E26435 /* ezw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ezw.c; sourceTree = "<group>"; };
|
||||
|
@ -1275,6 +1276,13 @@
|
|||
83A3F0711E3AD8B900D6A794 /* formats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = formats.c; sourceTree = "<group>"; };
|
||||
83A3F0731E3AD8B900D6A794 /* stack_alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_alloc.h; sourceTree = "<group>"; };
|
||||
83A5F75E198DF021009AF94C /* bfwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfwav.c; sourceTree = "<group>"; };
|
||||
83A8BADC256679C5000F5F3F /* wady_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wady_decoder.c; sourceTree = "<group>"; };
|
||||
83A8BADE256679E3000F5F3F /* blocked_xwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xwav.c; sourceTree = "<group>"; };
|
||||
83A8BAE025667AA7000F5F3F /* wady.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wady.c; sourceTree = "<group>"; };
|
||||
83A8BAE125667AA7000F5F3F /* ps2_enth_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ps2_enth_streamfile.h; sourceTree = "<group>"; };
|
||||
83A8BAE225667AA7000F5F3F /* xse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xse.c; sourceTree = "<group>"; };
|
||||
83A8BAE325667AA7000F5F3F /* xwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwav.c; sourceTree = "<group>"; };
|
||||
83A8BAE425667AA7000F5F3F /* cpk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cpk.c; sourceTree = "<group>"; };
|
||||
83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_xa_decoder.c; sourceTree = "<group>"; };
|
||||
83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_ealayer3.c; sourceTree = "<group>"; };
|
||||
83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_awc.c; sourceTree = "<group>"; };
|
||||
|
@ -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 */,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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" */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
||||
|
|
|
@ -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)) {
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
#include <pstdint.h>
|
||||
#endif
|
||||
|
||||
#ifndef inline /* (_MSC_VER < 1900)? */
|
||||
#define inline _inline
|
||||
#if (_MSC_VER < 1800) && !defined(__cplusplus)
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#define strcasecmp _stricmp
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue