Updated VGMStream to r1050-3424-gd7bd5a2a

CQTexperiment
Christopher Snowhill 2020-11-19 02:07:36 -08:00
parent 85907a48d9
commit 55ebd62eba
71 changed files with 4973 additions and 2737 deletions

View File

@ -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 */,

View File

@ -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 */

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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 */

View File

@ -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

View File

@ -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];
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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:

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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) {

View File

@ -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:

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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" */

View File

@ -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 */

View File

@ -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;

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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*/

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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 */

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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};

View File

@ -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) */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */
/*

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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. */

View File

@ -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);