Updated VGMStream to r1050-1964-g9346ae50

This commit is contained in:
Chris Moeller 2019-01-04 19:15:04 -08:00
parent 16d0d9f5cc
commit 3a93a0083c
132 changed files with 10911 additions and 2737 deletions

View file

@ -28,12 +28,10 @@
8306B0AD20984552000302D4 /* blocked_caf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0922098454E000302D4 /* blocked_caf.c */; };
8306B0AE20984552000302D4 /* blocked_filp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0932098454F000302D4 /* blocked_filp.c */; };
8306B0AF20984552000302D4 /* blocked_rws.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0942098454F000302D4 /* blocked_rws.c */; };
8306B0B020984552000302D4 /* blocked_ps2_strlr.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0952098454F000302D4 /* blocked_ps2_strlr.c */; };
8306B0B120984552000302D4 /* blocked_halpst.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0962098454F000302D4 /* blocked_halpst.c */; };
8306B0B220984552000302D4 /* blocked_mxch.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0972098454F000302D4 /* blocked_mxch.c */; };
8306B0B320984552000302D4 /* blocked_thp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B09820984550000302D4 /* blocked_thp.c */; };
8306B0B420984552000302D4 /* blocked_tra.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B09920984550000302D4 /* blocked_tra.c */; };
8306B0B520984552000302D4 /* blocked_emff.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B09A20984550000302D4 /* blocked_emff.c */; };
8306B0B620984552000302D4 /* blocked_hwas.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B09B20984550000302D4 /* blocked_hwas.c */; };
8306B0B720984552000302D4 /* blocked_str_snds.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B09C20984550000302D4 /* blocked_str_snds.c */; };
8306B0B820984552000302D4 /* blocked_ws_aud.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B09D20984551000302D4 /* blocked_ws_aud.c */; };
@ -93,6 +91,31 @@
832389521D224C0800482226 /* hca_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832389511D224C0800482226 /* hca_decoder.c */; };
83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.c */; };
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; };
832BF7FF21E050B7006F50F1 /* circus_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FC21E050B6006F50F1 /* circus_decoder.c */; };
832BF80021E050B7006F50F1 /* mpeg_custom_utils_eamp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */; };
832BF80121E050B7006F50F1 /* pcfx_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */; };
832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80221E050DB006F50F1 /* blocked_mul.c */; };
832BF80621E050DC006F50F1 /* blocked_vs_square.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80321E050DC006F50F1 /* blocked_vs_square.c */; };
832BF80721E050DC006F50F1 /* blocked_vs_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80421E050DC006F50F1 /* blocked_vs_str.c */; };
832BF80921E05135006F50F1 /* fag.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80821E05135006F50F1 /* fag.c */; };
832BF81C21E0514B006F50F1 /* xpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80A21E05148006F50F1 /* xpcm.c */; };
832BF81D21E0514B006F50F1 /* msf_tamasoft.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80B21E05148006F50F1 /* msf_tamasoft.c */; };
832BF81E21E0514B006F50F1 /* xps.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80C21E05148006F50F1 /* xps.c */; };
832BF81F21E0514B006F50F1 /* ps2_va3.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80D21E05148006F50F1 /* ps2_va3.c */; };
832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 832BF80E21E05149006F50F1 /* zsnd_streamfile.h */; };
832BF82121E0514B006F50F1 /* zsnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80F21E05149006F50F1 /* zsnd.c */; };
832BF82221E0514B006F50F1 /* vs_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81021E05149006F50F1 /* vs_str.c */; };
832BF82321E0514B006F50F1 /* imc.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81121E05149006F50F1 /* imc.c */; };
832BF82421E0514B006F50F1 /* mul.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81221E05149006F50F1 /* mul.c */; };
832BF82521E0514B006F50F1 /* ogg_opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81321E05149006F50F1 /* ogg_opus.c */; };
832BF82621E0514B006F50F1 /* nwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81421E0514A006F50F1 /* nwav.c */; };
832BF82721E0514B006F50F1 /* xa.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81521E0514A006F50F1 /* xa.c */; };
832BF82821E0514B006F50F1 /* xwma.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81621E0514A006F50F1 /* xwma.c */; };
832BF82921E0514B006F50F1 /* msf_banpresto.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81721E0514A006F50F1 /* msf_banpresto.c */; };
832BF82A21E0514B006F50F1 /* vs_square.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81821E0514A006F50F1 /* vs_square.c */; };
832BF82B21E0514B006F50F1 /* xopus.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81921E0514A006F50F1 /* xopus.c */; };
832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */ = {isa = PBXBuildFile; fileRef = 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */; };
832BF82D21E0514B006F50F1 /* nus3audio.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81B21E0514B006F50F1 /* nus3audio.c */; };
832C70BF1E9335E400BD7B4E /* Vorbis.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F4128F1E932F9A002E37D0 /* Vorbis.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
83345A4F1F8AEB2800B2EAA4 /* nub_xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */; };
83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */; };
@ -138,7 +161,6 @@
834FE0B4215C798C000A5D3D /* ffmpeg_decoder_custom_opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */; };
834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0AC215C798B000A5D3D /* acm_decoder_libacm.h */; };
834FE0B6215C798C000A5D3D /* derf_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0AD215C798B000A5D3D /* derf_decoder.c */; };
834FE0B7215C798C000A5D3D /* atrac9_decoder.c.orig in Resources */ = {isa = PBXBuildFile; fileRef = 834FE0AE215C798B000A5D3D /* atrac9_decoder.c.orig */; };
834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0AF215C798C000A5D3D /* acm_decoder_decode.c */; };
834FE0B9215C798C000A5D3D /* xmd_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0B0215C798C000A5D3D /* xmd_decoder.c */; };
834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */; };
@ -247,7 +269,6 @@
836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4318BDC2180095E648 /* dmsg_segh.c */; };
836F6F8018BDC2190095E648 /* dsp_bdsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4418BDC2180095E648 /* dsp_bdsp.c */; };
836F6F8218BDC2190095E648 /* ea_schl.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4618BDC2180095E648 /* ea_schl.c */; };
836F6F8418BDC2190095E648 /* emff.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4818BDC2180095E648 /* emff.c */; };
836F6F8518BDC2190095E648 /* exakt_sc.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4918BDC2180095E648 /* exakt_sc.c */; };
836F6F8618BDC2190095E648 /* excitebots.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4A18BDC2180095E648 /* excitebots.c */; };
836F6F8718BDC2190095E648 /* ffw.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4B18BDC2180095E648 /* ffw.c */; };
@ -352,7 +373,6 @@
836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBE18BDC2190095E648 /* ps2_spm.c */; };
836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBF18BDC2190095E648 /* ps2_sps.c */; };
836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC018BDC2190095E648 /* ps2_ster.c */; };
836F6FFF18BDC2190095E648 /* ps2_strlr.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC318BDC2190095E648 /* ps2_strlr.c */; };
836F700018BDC2190095E648 /* ps2_svag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC418BDC2190095E648 /* ps2_svag.c */; };
836F700118BDC2190095E648 /* ps2_tec.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC518BDC2190095E648 /* ps2_tec.c */; };
836F700218BDC2190095E648 /* ps2_tk5.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC618BDC2190095E648 /* ps2_tk5.c */; };
@ -371,8 +391,6 @@
836F701218BDC2190095E648 /* ps3_ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED618BDC2190095E648 /* ps3_ivag.c */; };
836F701418BDC2190095E648 /* ps3_msf.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED818BDC2190095E648 /* ps3_msf.c */; };
836F701518BDC2190095E648 /* ps3_past.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED918BDC2190095E648 /* ps3_past.c */; };
836F701918BDC2190095E648 /* psx_cdxa.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EDD18BDC2190095E648 /* psx_cdxa.c */; };
836F701A18BDC2190095E648 /* psx_fag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EDE18BDC2190095E648 /* psx_fag.c */; };
836F701B18BDC2190095E648 /* psx_gms.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EDF18BDC2190095E648 /* psx_gms.c */; };
836F701D18BDC2190095E648 /* raw.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE118BDC2190095E648 /* raw.c */; };
836F701E18BDC2190095E648 /* redspark.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE218BDC2190095E648 /* redspark.c */; };
@ -652,12 +670,10 @@
8306B0922098454E000302D4 /* blocked_caf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_caf.c; sourceTree = "<group>"; };
8306B0932098454F000302D4 /* blocked_filp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_filp.c; sourceTree = "<group>"; };
8306B0942098454F000302D4 /* blocked_rws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_rws.c; sourceTree = "<group>"; };
8306B0952098454F000302D4 /* blocked_ps2_strlr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ps2_strlr.c; sourceTree = "<group>"; };
8306B0962098454F000302D4 /* blocked_halpst.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_halpst.c; sourceTree = "<group>"; };
8306B0972098454F000302D4 /* blocked_mxch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_mxch.c; sourceTree = "<group>"; };
8306B09820984550000302D4 /* blocked_thp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_thp.c; sourceTree = "<group>"; };
8306B09920984550000302D4 /* blocked_tra.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_tra.c; sourceTree = "<group>"; };
8306B09A20984550000302D4 /* blocked_emff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_emff.c; sourceTree = "<group>"; };
8306B09B20984550000302D4 /* blocked_hwas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_hwas.c; sourceTree = "<group>"; };
8306B09C20984550000302D4 /* blocked_str_snds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_str_snds.c; sourceTree = "<group>"; };
8306B09D20984551000302D4 /* blocked_ws_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ws_aud.c; sourceTree = "<group>"; };
@ -716,6 +732,31 @@
832389511D224C0800482226 /* hca_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca_decoder.c; sourceTree = "<group>"; };
83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = "<group>"; };
83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = "<group>"; };
832BF7FC21E050B6006F50F1 /* circus_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = circus_decoder.c; sourceTree = "<group>"; };
832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_eamp3.c; sourceTree = "<group>"; };
832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcfx_decoder.c; sourceTree = "<group>"; };
832BF80221E050DB006F50F1 /* blocked_mul.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_mul.c; sourceTree = "<group>"; };
832BF80321E050DC006F50F1 /* blocked_vs_square.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vs_square.c; sourceTree = "<group>"; };
832BF80421E050DC006F50F1 /* blocked_vs_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vs_str.c; sourceTree = "<group>"; };
832BF80821E05135006F50F1 /* fag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fag.c; sourceTree = "<group>"; };
832BF80A21E05148006F50F1 /* xpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xpcm.c; sourceTree = "<group>"; };
832BF80B21E05148006F50F1 /* msf_tamasoft.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msf_tamasoft.c; sourceTree = "<group>"; };
832BF80C21E05148006F50F1 /* xps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xps.c; sourceTree = "<group>"; };
832BF80D21E05148006F50F1 /* ps2_va3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_va3.c; sourceTree = "<group>"; };
832BF80E21E05149006F50F1 /* zsnd_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zsnd_streamfile.h; sourceTree = "<group>"; };
832BF80F21E05149006F50F1 /* zsnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zsnd.c; sourceTree = "<group>"; };
832BF81021E05149006F50F1 /* vs_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vs_str.c; sourceTree = "<group>"; };
832BF81121E05149006F50F1 /* imc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imc.c; sourceTree = "<group>"; };
832BF81221E05149006F50F1 /* mul.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mul.c; sourceTree = "<group>"; };
832BF81321E05149006F50F1 /* ogg_opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_opus.c; sourceTree = "<group>"; };
832BF81421E0514A006F50F1 /* nwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nwav.c; sourceTree = "<group>"; };
832BF81521E0514A006F50F1 /* xa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa.c; sourceTree = "<group>"; };
832BF81621E0514A006F50F1 /* xwma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwma.c; sourceTree = "<group>"; };
832BF81721E0514A006F50F1 /* msf_banpresto.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msf_banpresto.c; sourceTree = "<group>"; };
832BF81821E0514A006F50F1 /* vs_square.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vs_square.c; sourceTree = "<group>"; };
832BF81921E0514A006F50F1 /* xopus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xopus.c; sourceTree = "<group>"; };
832BF81A21E0514A006F50F1 /* hca_keys_awb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys_awb.h; sourceTree = "<group>"; };
832BF81B21E0514B006F50F1 /* nus3audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3audio.c; sourceTree = "<group>"; };
83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_xma.c; sourceTree = "<group>"; };
83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = "<group>"; };
83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = "<group>"; };
@ -759,7 +800,6 @@
834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_custom_opus.c; sourceTree = "<group>"; };
834FE0AC215C798B000A5D3D /* acm_decoder_libacm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acm_decoder_libacm.h; sourceTree = "<group>"; };
834FE0AD215C798B000A5D3D /* derf_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = derf_decoder.c; sourceTree = "<group>"; };
834FE0AE215C798B000A5D3D /* atrac9_decoder.c.orig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = atrac9_decoder.c.orig; sourceTree = "<group>"; };
834FE0AF215C798C000A5D3D /* acm_decoder_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm_decoder_decode.c; sourceTree = "<group>"; };
834FE0B0215C798C000A5D3D /* xmd_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmd_decoder.c; sourceTree = "<group>"; };
834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_mt_decoder_utk.h; sourceTree = "<group>"; };
@ -870,7 +910,6 @@
836F6E4318BDC2180095E648 /* dmsg_segh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dmsg_segh.c; sourceTree = "<group>"; };
836F6E4418BDC2180095E648 /* dsp_bdsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_bdsp.c; sourceTree = "<group>"; };
836F6E4618BDC2180095E648 /* ea_schl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl.c; sourceTree = "<group>"; };
836F6E4818BDC2180095E648 /* emff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emff.c; sourceTree = "<group>"; };
836F6E4918BDC2180095E648 /* exakt_sc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exakt_sc.c; sourceTree = "<group>"; };
836F6E4A18BDC2180095E648 /* excitebots.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = excitebots.c; sourceTree = "<group>"; };
836F6E4B18BDC2180095E648 /* ffw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffw.c; sourceTree = "<group>"; };
@ -975,7 +1014,6 @@
836F6EBE18BDC2190095E648 /* ps2_spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_spm.c; sourceTree = "<group>"; };
836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = "<group>"; };
836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = "<group>"; };
836F6EC318BDC2190095E648 /* ps2_strlr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_strlr.c; sourceTree = "<group>"; };
836F6EC418BDC2190095E648 /* ps2_svag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag.c; sourceTree = "<group>"; };
836F6EC518BDC2190095E648 /* ps2_tec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tec.c; sourceTree = "<group>"; };
836F6EC618BDC2190095E648 /* ps2_tk5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tk5.c; sourceTree = "<group>"; };
@ -994,8 +1032,6 @@
836F6ED618BDC2190095E648 /* ps3_ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_ivag.c; sourceTree = "<group>"; };
836F6ED818BDC2190095E648 /* ps3_msf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_msf.c; sourceTree = "<group>"; };
836F6ED918BDC2190095E648 /* ps3_past.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_past.c; sourceTree = "<group>"; };
836F6EDD18BDC2190095E648 /* psx_cdxa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psx_cdxa.c; sourceTree = "<group>"; };
836F6EDE18BDC2190095E648 /* psx_fag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psx_fag.c; sourceTree = "<group>"; };
836F6EDF18BDC2190095E648 /* psx_gms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psx_gms.c; sourceTree = "<group>"; };
836F6EE118BDC2190095E648 /* raw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw.c; sourceTree = "<group>"; };
836F6EE218BDC2190095E648 /* redspark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = redspark.c; sourceTree = "<group>"; };
@ -1284,8 +1320,8 @@
8315958320FEC831007002F0 /* asf_decoder.c */,
8306B08120984517000302D4 /* at3plus_decoder.c */,
830EBE0F2004655D0023AA10 /* atrac9_decoder.c */,
834FE0AE215C798B000A5D3D /* atrac9_decoder.c.orig */,
834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */,
832BF7FC21E050B6006F50F1 /* circus_decoder.c */,
831BA6221EAC61CB00CF89B0 /* coding_utils.c */,
836F6DE518BDC2180095E648 /* coding.h */,
834FE0AD215C798B000A5D3D /* derf_decoder.c */,
@ -1309,6 +1345,7 @@
839E21D91F2EDAF000EE54D7 /* mpeg_custom_utils_ahx.c */,
83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */,
83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */,
832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */,
839E21DD1F2EDAF000EE54D7 /* mpeg_custom_utils.c */,
836F6DEF18BDC2180095E648 /* mpeg_decoder.c */,
839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */,
@ -1322,6 +1359,7 @@
836F6DF618BDC2180095E648 /* nwa_decoder.c */,
836F6DF718BDC2180095E648 /* nwa_decoder.h */,
836F6DF818BDC2180095E648 /* ogg_vorbis_decoder.c */,
832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */,
836F6DF918BDC2180095E648 /* pcm_decoder.c */,
83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */,
836F6DFA18BDC2180095E648 /* psx_decoder.c */,
@ -1360,7 +1398,6 @@
83EED5D5203A8BD7008BEB45 /* blocked_ea_swvr.c */,
8306B08B2098454D000302D4 /* blocked_ea_wve_ad10.c */,
8306B08A2098454D000302D4 /* blocked_ea_wve_au00.c */,
8306B09A20984550000302D4 /* blocked_emff.c */,
8306B0932098454F000302D4 /* blocked_filp.c */,
8306B0A020984551000302D4 /* blocked_gsb.c */,
8342469520C4D23D00926E48 /* blocked_h4m.c */,
@ -1368,9 +1405,9 @@
8306B09B20984550000302D4 /* blocked_hwas.c */,
8349A8E51FE6253800E26435 /* blocked_ivaud.c */,
8306B09E20984551000302D4 /* blocked_matx.c */,
832BF80221E050DB006F50F1 /* blocked_mul.c */,
8306B0972098454F000302D4 /* blocked_mxch.c */,
8306B08D2098454D000302D4 /* blocked_ps2_iab.c */,
8306B0952098454F000302D4 /* blocked_ps2_strlr.c */,
8306B0942098454F000302D4 /* blocked_rws.c */,
8306B08F2098454E000302D4 /* blocked_sthd.c */,
8306B09C20984550000302D4 /* blocked_str_snds.c */,
@ -1378,6 +1415,8 @@
8306B09920984550000302D4 /* blocked_tra.c */,
8349A8E61FE6253900E26435 /* blocked_vawx.c */,
83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */,
832BF80321E050DC006F50F1 /* blocked_vs_square.c */,
832BF80421E050DC006F50F1 /* blocked_vs_str.c */,
8306B0A120984551000302D4 /* blocked_vs.c */,
8306B09D20984551000302D4 /* blocked_ws_aud.c */,
8306B09F20984551000302D4 /* blocked_wsi.c */,
@ -1462,10 +1501,10 @@
83EED5D1203A8BC7008BEB45 /* ea_swvr.c */,
8306B0C42098458D000302D4 /* ea_wve_ad10.c */,
8306B0BF2098458C000302D4 /* ea_wve_au00.c */,
836F6E4818BDC2180095E648 /* emff.c */,
836F6E4918BDC2180095E648 /* exakt_sc.c */,
836F6E4A18BDC2180095E648 /* excitebots.c */,
8349A8EF1FE6257C00E26435 /* ezw.c */,
832BF80821E05135006F50F1 /* fag.c */,
838BDB6D1D3B043C0022CA6F /* ffmpeg.c */,
836F6E4B18BDC2180095E648 /* ffw.c */,
8349A8FD1FE6257F00E26435 /* flx.c */,
@ -1483,11 +1522,13 @@
83709DFF1ECBC1A4005C03D3 /* gtd.c */,
8342469020C4D22F00926E48 /* h4m.c */,
836F6E5218BDC2180095E648 /* halpst.c */,
832BF81A21E0514A006F50F1 /* hca_keys_awb.h */,
83AA5D211F6E2F9C0020821C /* hca_keys.h */,
8323894F1D2246C300482226 /* hca.c */,
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */,
836F6E5318BDC2180095E648 /* his.c */,
834FE0E0215C79EB000A5D3D /* idsp_ie.c */,
832BF81121E05149006F50F1 /* imc.c */,
836F6E5518BDC2180095E648 /* ios_psnd.c */,
836F6E5618BDC2180095E648 /* ish_isd.c */,
836F6E5718BDC2180095E648 /* ivaud.c */,
@ -1507,9 +1548,12 @@
8349A9031FE6258100E26435 /* mogg.c */,
836F6E6018BDC2180095E648 /* mp4.c */,
8306B0CB2098458E000302D4 /* msb_msh.c */,
832BF81721E0514A006F50F1 /* msf_banpresto.c */,
832BF80B21E05148006F50F1 /* msf_tamasoft.c */,
83709E011ECBC1A4005C03D3 /* mss.c */,
834FE0E7215C79EC000A5D3D /* msv.c */,
836F6E6118BDC2180095E648 /* msvp.c */,
832BF81221E05149006F50F1 /* mul.c */,
836F6E6218BDC2180095E648 /* mus_acm.c */,
836F6E6318BDC2180095E648 /* musc.c */,
836F6E6418BDC2180095E648 /* musx.c */,
@ -1545,10 +1589,13 @@
834FE0E1215C79EB000A5D3D /* nub_idsp.c */,
83AB8C731E8072A100086084 /* nub_vag.c */,
83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */,
832BF81B21E0514B006F50F1 /* nus3audio.c */,
834FE0D1215C79E9000A5D3D /* nus3bank.c */,
836F6E8118BDC2180095E648 /* nwa.c */,
832BF81421E0514A006F50F1 /* nwav.c */,
834FE0DB215C79EA000A5D3D /* nxa.c */,
8306B0C02098458C000302D4 /* nxap.c */,
832BF81321E05149006F50F1 /* ogg_opus.c */,
83A21F7F201D8980000F04B9 /* ogg_vorbis.c */,
831BA60F1EAC61A500CF89B0 /* ogl.c */,
8349A8FB1FE6257F00E26435 /* omu.c */,
@ -1619,11 +1666,11 @@
836F6EBE18BDC2190095E648 /* ps2_spm.c */,
836F6EBF18BDC2190095E648 /* ps2_sps.c */,
836F6EC018BDC2190095E648 /* ps2_ster.c */,
836F6EC318BDC2190095E648 /* ps2_strlr.c */,
8350C0591E071990009E0A93 /* ps2_svag_snk.c */,
836F6EC418BDC2190095E648 /* ps2_svag.c */,
836F6EC518BDC2190095E648 /* ps2_tec.c */,
836F6EC618BDC2190095E648 /* ps2_tk5.c */,
832BF80D21E05148006F50F1 /* ps2_va3.c */,
836F6EC818BDC2190095E648 /* ps2_vas.c */,
836F6EC918BDC2190095E648 /* ps2_vbk.c */,
831BA6101EAC61A500CF89B0 /* ps2_vds_vdm.c */,
@ -1642,8 +1689,6 @@
836F6ED818BDC2190095E648 /* ps3_msf.c */,
8350270C1ED119D200C25929 /* ps3_mta2.c */,
836F6ED918BDC2190095E648 /* ps3_past.c */,
836F6EDD18BDC2190095E648 /* psx_cdxa.c */,
836F6EDE18BDC2190095E648 /* psx_fag.c */,
836F6EDF18BDC2190095E648 /* psx_gms.c */,
836F6EE118BDC2190095E648 /* raw.c */,
836F6EE218BDC2190095E648 /* redspark.c */,
@ -1707,6 +1752,8 @@
836F6EFD18BDC2190095E648 /* vgs.c */,
834FE0CE215C79E8000A5D3D /* vis.c */,
834FE0D4215C79E9000A5D3D /* vpk.c */,
832BF81821E0514A006F50F1 /* vs_square.c */,
832BF81021E05149006F50F1 /* vs_str.c */,
836F6EFE18BDC2190095E648 /* vs.c */,
8349A8F91FE6257E00E26435 /* vsf_tta.c */,
836F6EFF18BDC2190095E648 /* vsf.c */,
@ -1733,6 +1780,7 @@
831BA6151EAC61A500CF89B0 /* x360_cxs.c */,
831BA6171EAC61A500CF89B0 /* x360_pasx.c */,
836F6F0A18BDC2190095E648 /* x360_tra.c */,
832BF81521E0514A006F50F1 /* xa.c */,
834FE0D2215C79E9000A5D3D /* xau_konami.c */,
833A7A2D1ED11961003EC53E /* xau.c */,
836F6F0B18BDC2190095E648 /* xbox_hlwav.c */,
@ -1743,13 +1791,19 @@
8350C0541E071881009E0A93 /* xma.c */,
834FE0DC215C79EA000A5D3D /* xmd.c */,
830EBE112004656E0023AA10 /* xnb.c */,
832BF81921E0514A006F50F1 /* xopus.c */,
832BF80A21E05148006F50F1 /* xpcm.c */,
832BF80C21E05148006F50F1 /* xps.c */,
836F6F1218BDC2190095E648 /* xss.c */,
834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */,
83345A4E1F8AEB2800B2EAA4 /* xvag.c */,
836F6F1318BDC2190095E648 /* xwb.c */,
83A21F7D201D8980000F04B9 /* xwc.c */,
832BF81621E0514A006F50F1 /* xwma.c */,
836F6F1418BDC2190095E648 /* ydsp.c */,
836F6F1518BDC2190095E648 /* zsd.c */,
832BF80E21E05149006F50F1 /* zsnd_streamfile.h */,
832BF80F21E05149006F50F1 /* zsnd.c */,
836F6F1618BDC2190095E648 /* zwdsp.c */,
);
path = meta;
@ -1797,8 +1851,10 @@
834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */,
8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */,
836F6F2718BDC2190095E648 /* g72x_state.h in Headers */,
832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */,
8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */,
834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */,
832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */,
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
836F705418BDC2190095E648 /* streamfile.h in Headers */,
83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */,
@ -1974,7 +2030,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
834FE0B7215C798C000A5D3D /* atrac9_decoder.c.orig in Resources */,
836F6B4718BDB8880095E648 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1989,9 +2044,9 @@
839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */,
83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */,
834FE104215C79ED000A5D3D /* nxa.c in Sources */,
8306B0B020984552000302D4 /* blocked_ps2_strlr.c in Sources */,
8349A9071FE6258200E26435 /* dec.c in Sources */,
839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */,
832BF80721E050DC006F50F1 /* blocked_vs_str.c in Sources */,
839E21E41F2EDAF100EE54D7 /* vorbis_custom_utils_fsb.c in Sources */,
839E21E11F2EDAF100EE54D7 /* vorbis_custom_decoder.c in Sources */,
839E21E71F2EDAF100EE54D7 /* mpeg_custom_utils.c in Sources */,
@ -2026,9 +2081,11 @@
836F703218BDC2190095E648 /* str_asr.c in Sources */,
836F6FB218BDC2190095E648 /* ngc_gcub.c in Sources */,
836F702818BDC2190095E648 /* sat_dvi.c in Sources */,
832BF82D21E0514B006F50F1 /* nus3audio.c in Sources */,
836F6F2F18BDC2190095E648 /* mtaf_decoder.c in Sources */,
83AA5D161F6E2F600020821C /* ea_xa_decoder.c in Sources */,
836F6F9B18BDC2190095E648 /* mn_str.c in Sources */,
832BF82821E0514B006F50F1 /* xwma.c in Sources */,
8306B0EB20984590000302D4 /* wave_segmented.c in Sources */,
836F6F9F18BDC2190095E648 /* musc.c in Sources */,
8349A9121FE6258200E26435 /* vsf_tta.c in Sources */,
@ -2043,6 +2100,7 @@
836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */,
8306B0DD20984590000302D4 /* waf.c in Sources */,
8306B0B320984552000302D4 /* blocked_thp.c in Sources */,
832BF80921E05135006F50F1 /* fag.c in Sources */,
834FE0B6215C798C000A5D3D /* derf_decoder.c in Sources */,
836F702318BDC2190095E648 /* rsf.c in Sources */,
834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */,
@ -2100,11 +2158,13 @@
831BA61A1EAC61A500CF89B0 /* ps2_vds_vdm.c in Sources */,
836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */,
83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */,
832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */,
836F6FA818BDC2190095E648 /* nds_swav.c in Sources */,
8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */,
834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */,
833A7A2E1ED11961003EC53E /* xau.c in Sources */,
836F6FB518BDC2190095E648 /* ngc_pdt.c in Sources */,
832BF81E21E0514B006F50F1 /* xps.c in Sources */,
836F6F6C18BDC2190095E648 /* ahx.c in Sources */,
83AB8C751E8072A100086084 /* nub_vag.c in Sources */,
836F702D18BDC2190095E648 /* sfl.c in Sources */,
@ -2139,6 +2199,8 @@
8349A9181FE6258200E26435 /* ea_1snh.c in Sources */,
83EED5D4203A8BC7008BEB45 /* aus.c in Sources */,
836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */,
832BF81D21E0514B006F50F1 /* msf_tamasoft.c in Sources */,
832BF7FF21E050B7006F50F1 /* circus_decoder.c in Sources */,
83709E071ECBC1A4005C03D3 /* mss.c in Sources */,
836F6F8F18BDC2190095E648 /* his.c in Sources */,
834FE0E9215C79ED000A5D3D /* ao.c in Sources */,
@ -2161,7 +2223,6 @@
836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */,
836F6F7918BDC2190095E648 /* dc_asd.c in Sources */,
836F6FC118BDC2190095E648 /* pc_adp.c in Sources */,
836F701A18BDC2190095E648 /* psx_fag.c in Sources */,
836F703B18BDC2190095E648 /* vsf.c in Sources */,
836F6F8218BDC2190095E648 /* ea_schl.c in Sources */,
836F6F7318BDC2190095E648 /* bcstm.c in Sources */,
@ -2181,14 +2242,17 @@
836F6F8518BDC2190095E648 /* exakt_sc.c in Sources */,
836F6FA618BDC2190095E648 /* nds_sad.c in Sources */,
8306B0F120984590000302D4 /* ppst.c in Sources */,
832BF81C21E0514B006F50F1 /* xpcm.c in Sources */,
836F702B18BDC2190095E648 /* sdt.c in Sources */,
836F6FDA18BDC2190095E648 /* ps2_hgc1.c in Sources */,
836F702C18BDC2190095E648 /* seg.c in Sources */,
836F700918BDC2190095E648 /* ps2_voi.c in Sources */,
836F6F3E18BDC2190095E648 /* aix_layout.c in Sources */,
836F6FE018BDC2190095E648 /* ps2_joe.c in Sources */,
832BF82721E0514B006F50F1 /* xa.c in Sources */,
8306B0A220984552000302D4 /* blocked_bdsp.c in Sources */,
836F700118BDC2190095E648 /* ps2_tec.c in Sources */,
832BF82121E0514B006F50F1 /* zsnd.c in Sources */,
836F703018BDC2190095E648 /* sqex_scd.c in Sources */,
8306B0A320984552000302D4 /* blocked_ast.c in Sources */,
836F6FD518BDC2190095E648 /* ps2_enth.c in Sources */,
@ -2197,7 +2261,6 @@
8315958720FEC832007002F0 /* asf_decoder.c in Sources */,
836F705218BDC2190095E648 /* zwdsp.c in Sources */,
836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */,
836F6FFF18BDC2190095E648 /* ps2_strlr.c in Sources */,
836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */,
8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */,
834FE0EA215C79ED000A5D3D /* aif_asobo.c in Sources */,
@ -2225,7 +2288,6 @@
836F6F3118BDC2190095E648 /* ngc_afc_decoder.c in Sources */,
836F6F9918BDC2190095E648 /* maxis_xa.c in Sources */,
836F702118BDC2190095E648 /* rs03.c in Sources */,
836F6F8418BDC2190095E648 /* emff.c in Sources */,
836F6F8818BDC2190095E648 /* fsb.c in Sources */,
836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */,
836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */,
@ -2241,12 +2303,14 @@
834FE106215C79ED000A5D3D /* utk.c in Sources */,
831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */,
836F700218BDC2190095E648 /* ps2_tk5.c in Sources */,
832BF80121E050B7006F50F1 /* pcfx_decoder.c in Sources */,
83AA5D271F6E2F9C0020821C /* stm.c in Sources */,
831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */,
836F703F18BDC2190095E648 /* wii_smp.c in Sources */,
8306B0A520984552000302D4 /* blocked_ea_wve_au00.c in Sources */,
836F6FB818BDC2190095E648 /* ngc_tydsp.c in Sources */,
836F701518BDC2190095E648 /* ps3_past.c in Sources */,
832BF80621E050DC006F50F1 /* blocked_vs_square.c in Sources */,
836F6F7C18BDC2190095E648 /* dc_kcey.c in Sources */,
836F6FED18BDC2190095E648 /* ps2_npsf.c in Sources */,
83A21F8B201D8982000F04B9 /* sps_n1.c in Sources */,
@ -2269,12 +2333,13 @@
836F704618BDC2190095E648 /* x360_tra.c in Sources */,
834FE0F0215C79ED000A5D3D /* apc.c in Sources */,
836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */,
836F701918BDC2190095E648 /* psx_cdxa.c in Sources */,
834D3A6E19F47C98001C54F6 /* g1l.c in Sources */,
836F6FC318BDC2190095E648 /* pc_smp.c in Sources */,
836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */,
832BF82A21E0514B006F50F1 /* vs_square.c in Sources */,
834FE0B4215C798C000A5D3D /* ffmpeg_decoder_custom_opus.c in Sources */,
8349A9091FE6258200E26435 /* pc_ast.c in Sources */,
832BF81F21E0514B006F50F1 /* ps2_va3.c in Sources */,
8349A91C1FE6258200E26435 /* mogg.c in Sources */,
834FE0F5215C79ED000A5D3D /* wv2.c in Sources */,
836F703D18BDC2190095E648 /* wii_mus.c in Sources */,
@ -2298,10 +2363,12 @@
836F6F7718BDC2190095E648 /* capdsp.c in Sources */,
836F6FB018BDC2190095E648 /* ngc_dsp_ygo.c in Sources */,
836F703318BDC2190095E648 /* str_snds.c in Sources */,
832BF82221E0514B006F50F1 /* vs_str.c in Sources */,
8349A9191FE6258200E26435 /* afc.c in Sources */,
836F703718BDC2190095E648 /* tun.c in Sources */,
836F700B18BDC2190095E648 /* ps2_wad.c in Sources */,
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 */,
@ -2315,6 +2382,7 @@
836F6F9518BDC2190095E648 /* kraw.c in Sources */,
836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */,
8306B0E920984590000302D4 /* opus.c in Sources */,
832BF80021E050B7006F50F1 /* mpeg_custom_utils_eamp3.c in Sources */,
83709E051ECBC1A4005C03D3 /* gtd.c in Sources */,
8306B0A420984552000302D4 /* segmented.c in Sources */,
83A21F86201D8981000F04B9 /* xwc.c in Sources */,
@ -2330,6 +2398,7 @@
836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */,
836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */,
836F6F3C18BDC2190095E648 /* xa_decoder.c in Sources */,
832BF82521E0514B006F50F1 /* ogg_opus.c in Sources */,
834FE10A215C79ED000A5D3D /* nub_idsp.c in Sources */,
83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */,
836F6F9118BDC2190095E648 /* ios_psnd.c in Sources */,
@ -2338,9 +2407,11 @@
834FE0FF215C79ED000A5D3D /* sqex_scd_sscf.c in Sources */,
836F6FAA18BDC2190095E648 /* ngc_bh2pcm.c in Sources */,
831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */,
832BF82621E0514B006F50F1 /* nwav.c in Sources */,
836F6F3018BDC2190095E648 /* nds_procyon_decoder.c in Sources */,
8349A8E81FE6253900E26435 /* blocked_dec.c in Sources */,
831BA6181EAC61A500CF89B0 /* adx.c in Sources */,
832BF82321E0514B006F50F1 /* imc.c in Sources */,
836F6FB118BDC2190095E648 /* ngc_ffcc_str.c in Sources */,
8306B0B620984552000302D4 /* blocked_hwas.c in Sources */,
836F6FC218BDC2190095E648 /* pc_mxst.c in Sources */,
@ -2387,7 +2458,6 @@
8306B0B420984552000302D4 /* blocked_tra.c in Sources */,
836F6FDD18BDC2190095E648 /* ps2_ikm.c in Sources */,
834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */,
8306B0B520984552000302D4 /* blocked_emff.c in Sources */,
836F6FBD18BDC2190095E648 /* nwa.c in Sources */,
834FE0F7215C79ED000A5D3D /* vis.c in Sources */,
83A21F8C201D8982000F04B9 /* kma9.c in Sources */,
@ -2399,6 +2469,7 @@
836F6F4118BDC2190095E648 /* blocked.c in Sources */,
836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */,
838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */,
832BF82B21E0514B006F50F1 /* xopus.c in Sources */,
836F6F3418BDC2190095E648 /* nwa_decoder.c in Sources */,
836F6F2E18BDC2190095E648 /* msadpcm_decoder.c in Sources */,
836F6F9218BDC2190095E648 /* ish_isd.c in Sources */,
@ -2417,6 +2488,7 @@
832389501D2246C300482226 /* hca.c in Sources */,
836F701B18BDC2190095E648 /* psx_gms.c in Sources */,
8306B0F020984590000302D4 /* atsl.c in Sources */,
832BF82421E0514B006F50F1 /* mul.c in Sources */,
836F700518BDC2190095E648 /* ps2_vbk.c in Sources */,
836F6FDF18BDC2190095E648 /* ps2_int.c in Sources */,
8306B0AC20984552000302D4 /* blocked_xa.c in Sources */,

View file

@ -26,7 +26,7 @@
#include <stdlib.h>
#include <memory.h>
#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted (ciph type > 0) */
#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted with key */
#define HCA_SUBFRAMES_PER_FRAME 8
#define HCA_SAMPLES_PER_SUBFRAME 128
#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES_PER_FRAME*HCA_SAMPLES_PER_SUBFRAME)
@ -34,6 +34,13 @@
#define HCA_MAX_CHANNELS 16 /* internal max? in practice only 8 can be encoded */
#define HCA_ERROR_PARAMS -1
#define HCA_ERROR_HEADER -2
#define HCA_ERROR_CHECKSUM -3
#define HCA_ERROR_SYNC -4
#define HCA_ERROR_UNPACK -5
#define HCA_ERROR_BITREADER -6
//--------------------------------------------------
// Decoder config/state
//--------------------------------------------------
@ -239,7 +246,7 @@ int clHCA_isOurFile(const void *data, unsigned int size) {
unsigned int header_size = 0;
if (!data || size < 0x08)
return -1;
return HCA_ERROR_PARAMS;
bitreader_init(&br, data, 8);
if ((bitreader_peek(&br, 32) & HCA_MASK) == 0x48434100) {/*'HCA\0'*/
@ -248,13 +255,13 @@ int clHCA_isOurFile(const void *data, unsigned int size) {
}
if (header_size == 0)
return -1;
return HCA_ERROR_HEADER;
return header_size;
}
int clHCA_getInfo(clHCA *hca, clHCA_stInfo *info) {
if (!hca || !info || !hca->is_valid)
return -1;
return HCA_ERROR_PARAMS;
info->version = hca->version;
info->headerSize = hca->header_size;
@ -286,10 +293,10 @@ void clHCA_ReadSamples16(clHCA *hca, signed short *samples) {
for (k = 0; k < hca->channels; k++) {
f = hca->channel[k].wave[i][j];
//f = f * hca->rva_volume; /* rare, won't apply for now */
if (f > 1) {
f = 1;
} else if (f < -1) {
f = -1;
if (f > 1.0f) {
f = 1.0f;
} else if (f < -1.0f) {
f = -1.0f;
}
s = (signed int) (f * scale);
if ((unsigned) (s + 0x8000) & 0xFFFF0000)
@ -424,7 +431,7 @@ static int ath_init(unsigned char *ath_curve, int type, unsigned int sample_rate
ath_init1(ath_curve, sample_rate);
break;
default:
return -1;
return HCA_ERROR_HEADER;
}
return 0;
}
@ -455,7 +462,7 @@ static void cipher_init1(unsigned char *cipher_table) {
const int add = 11;
unsigned int i, v = 0;
/* keyless encryption (unused?) */
/* keyless encryption (rare) */
for (i = 1; i < 256 - 1; i++) {
v = (v * mul + add) & 0xFF;
if (v == 0 || v == 0xFF)
@ -542,7 +549,7 @@ static void cipher_init56(unsigned char *cipher_table, unsigned long long keycod
}
static int cipher_init(unsigned char *cipher_table, int type, unsigned long long keycode) {
if (!(keycode))
if (type == 56 && !(keycode))
type = 0;
switch (type) {
@ -556,7 +563,7 @@ static int cipher_init(unsigned char *cipher_table, int type, unsigned long long
cipher_init56(cipher_table, keycode);
break;
default:
return -1;
return HCA_ERROR_HEADER;
}
return 0;
}
@ -568,16 +575,17 @@ static unsigned int header_ceil2(unsigned int a, unsigned int b) {
return (b > 0) ? (a / b + ((a % b) ? 1 : 0)) : 0;
}
int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) {
clData br;
int res;
if (!hca || !data)
return -1;
return HCA_ERROR_PARAMS;
hca->is_valid = 0;
if (size < 0x08)
return -1;
return HCA_ERROR_PARAMS;
bitreader_init(&br, data, size);
@ -594,18 +602,18 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
hca->version != 0x0102 &&
hca->version != 0x0103 &&
hca->version != 0x0200)
return -1;
return HCA_ERROR_HEADER;
#endif
if (size < hca->header_size)
return -1;
return HCA_ERROR_PARAMS;
if (crc16_checksum(data,hca->header_size))
return -1;
return HCA_ERROR_CHECKSUM;
size -= 0x08;
}
else {
return -1;
return HCA_ERROR_HEADER;
}
/* format info */
@ -618,18 +626,18 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
hca->encoder_padding = bitreader_read(&br, 16);
if (!(hca->channels >= 1 && hca->channels <= HCA_MAX_CHANNELS))
return -1;
return HCA_ERROR_HEADER;
if (hca->frame_count == 0)
return -1;
return HCA_ERROR_HEADER;
if (!(hca->sample_rate >= 1 && hca->sample_rate <= 0x7FFFFF)) /* encoder max seems 48000 */
return -1;
return HCA_ERROR_HEADER;
size -= 0x10;
}
else {
return -1;
return HCA_ERROR_HEADER;
}
/* compression (v2.0) or decode (v1.x) info */
@ -668,7 +676,7 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
size -= 0x0c;
}
else {
return -1;
return HCA_ERROR_HEADER;
}
/* VBR (variable bit rate) info */
@ -678,7 +686,7 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
hca->vbr_noise_Level = bitreader_read(&br, 16);
if (!(hca->frame_size == 0 && hca->vbr_max_frame_size > 8 && hca->vbr_max_frame_size <= 0x1FF))
return -1;
return HCA_ERROR_HEADER;
size -= 0x08;
}
@ -710,7 +718,7 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
if (!(hca->loop_start_frame >= 0 && hca->loop_start_frame <= hca->loop_end_frame
&& hca->loop_end_frame < hca->frame_count))
return -1;
return HCA_ERROR_HEADER;
size -= 0x10;
}
@ -729,7 +737,7 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
hca->ciph_type = bitreader_read(&br, 16);
if (!(hca->ciph_type == 0 || hca->ciph_type == 1 || hca->ciph_type == 56))
return -1;
return HCA_ERROR_HEADER;
size -= 0x06;
}
@ -760,11 +768,11 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
hca->comment_len = bitreader_read(&br, 8);
if (hca->comment_len > size)
return -1;
return HCA_ERROR_HEADER;
temp = realloc(hca->comment, hca->comment_len + 1);
if (!temp)
return -1;
return HCA_ERROR_HEADER;
hca->comment = temp;
for (i = 0; i < hca->comment_len; ++i)
hca->comment[i] = bitreader_read(&br, 8);
@ -782,17 +790,17 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
size -= (size - 0x02); /* fills up to header_size, sans checksum */
}
/* should be fully read, but allow data buffer may be bigger than header_size */
/* should be fully read, but allow as data buffer may be bigger than header_size */
//if (size != 0x02)
// return -1;
// return HCA_ERROR_HEADER;
/* extra validations */
if (!(hca->frame_size >= 0x08 && hca->frame_size <= 0xFFFF)) /* actual max seems 0x155*channels */
return -1; /* theoretically can be 0 if VBR (not seen) */
return HCA_ERROR_HEADER; /* theoretically can be 0 if VBR (not seen) */
if (!(hca->min_resolution == 1 && hca->max_resolution == 15))
return -1;
return HCA_ERROR_HEADER;
/* inits state */
@ -803,10 +811,12 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
hca->total_band_count - hca->base_band_count - hca->stereo_band_count,
hca->bands_per_hfr_group);
if (ath_init(hca->ath_curve, hca->ath_type, hca->sample_rate) < 0)
return -1;
if (cipher_init(hca->cipher_table, hca->ciph_type, hca->keycode) < 0)
return -1;
res = ath_init(hca->ath_curve, hca->ath_type, hca->sample_rate);
if (res < 0)
return res;
res = cipher_init(hca->cipher_table, hca->ciph_type, hca->keycode);
if (res < 0)
return res;
/* init channels */
@ -919,43 +929,62 @@ int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size) {
unsigned int ch, sf, s;
int status;
int clips = 0, blanks = 0;
float fsample;
signed int psample;
/* return if decode fails (happens sometimes with wrong keys) */
/* first blocks can be empty/silent, check all bytes but sync/crc */
{
int i;
int is_empty = 1;
const unsigned char *buf = data;
for (i = 2; i < size - 0x02; i++) {
if (buf[i] != 0) {
is_empty = 0;
break;
}
}
if (is_empty) {
return 0;
}
}
/* return if decode fails (happens often with wrong keys due to bad bitstream values) */
status = clHCA_DecodeBlock(hca, data, size);
if (status < 0)
return -1;
/* check decode results */
/* check decode results as bad keys may still get here */
for (ch = 0; ch < hca->channels; ch++) {
for (sf = 0; sf < HCA_SUBFRAMES_PER_FRAME; sf++) {
for (s = 0; s < HCA_SAMPLES_PER_SUBFRAME; s++) {
fsample = hca->channel[ch].wave[sf][s];
psample = (signed int) (fsample * scale);
if (fsample > 1.0f || fsample < -1.0f)
float fsample = hca->channel[ch].wave[sf][s];
if (fsample > 1.0f || fsample < -1.0f) { //improve?
clips++;
else if (psample == 0 || psample == -1)
}
else {
signed int psample = (signed int) (fsample * scale);
if (psample == 0 || psample == -1)
blanks++;
}
}
}
}
/* the more clips the less likely block was correctly decrypted */
if (clips > 0)
if (clips == 1)
clips++;
if (clips > 1)
return clips;
/* if block is silent result is not useful */
if (blanks == hca->channels * HCA_SUBFRAMES_PER_FRAME * HCA_SAMPLES_PER_SUBFRAME)
return 0;
/* block may be correct (but wrong keys can get this too) */
/* block may be correct (but wrong keys can get this too and should test more blocks) */
return 1;
}
#if 0
// it'd seem like resetting IMDCT (others get overwritten) would matter when restarting the
// stream from 0, but doesn't seem any different, maybe because the first frame acts as setup/empty
void clHCA_DecodeReset(clHCA * hca) {
unsigned int i;
@ -965,18 +994,18 @@ void clHCA_DecodeReset(clHCA * hca) {
for (i = 0; i < hca->channels; i++) {
stChannel *ch = &hca->channel[i];
memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME);
memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME);
memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME);
memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME);
memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME);
memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME);
memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME);
/* most values get overwritten during decode */
//memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME);
//memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME);
memset(ch->imdct_previous, 0, sizeof(ch->imdct_previous[0]) * HCA_SAMPLES_PER_SUBFRAME);
memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME);
//memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME);
}
}
#endif
//--------------------------------------------------
// Decode
@ -1002,19 +1031,19 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) {
unsigned int subframe, ch;
if (!data || !hca || !hca->is_valid)
return -1;
return HCA_ERROR_PARAMS;
if (size < hca->frame_size)
return -1;
return HCA_ERROR_PARAMS;
bitreader_init(&br, data, hca->frame_size);
/* test sync (not encrypted) */
sync = bitreader_read(&br, 16);
if (sync != 0xFFFF)
return -1;
return HCA_ERROR_SYNC;
if (crc16_checksum(data, hca->frame_size))
return -1;
return HCA_ERROR_CHECKSUM;
cipher_decrypt(hca->cipher_table, data, hca->frame_size);
@ -1029,7 +1058,7 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) {
int unpack = decode1_unpack_channel(&hca->channel[ch], &br,
hca->hfr_group_count, packed_noise_level, hca->ath_curve);
if (unpack < 0)
return -1;
return unpack;
}
}
@ -1059,9 +1088,12 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) {
}
}
/* should read all frame sans checksum at most */
if (br.bit > br.size - 16) {
return -1;
/* should read all frame sans checksum (16b) at most */
/* one frame was found to read up to 14b left (cross referenced with CRI's tools),
* perhaps some encoding hiccup [World of Final Fantasy Maxima (Switch) am_ev21_0170 video],
* though this validation makes more sense when testing keys and isn't normally done on decode */
if (br.bit + 14 > br.size) { /* relax validation a bit for that case */
return HCA_ERROR_BITREADER;
}
return 0;
@ -1131,8 +1163,8 @@ static int decode1_unpack_channel(stChannel *ch, clData *br,
if (delta != expected_delta) {
/* may happen with bad keycodes, scalefactors must be 6b indexes */
int scalefactor_test = (int)scalefactor_prev + ((int)delta - (int)extra_delta);
if (scalefactor_test < 0 || scalefactor_test > 64) {
return -1;
if (scalefactor_test < 0 || scalefactor_test >= 64) {
return HCA_ERROR_UNPACK;
}
scalefactor_prev += delta - extra_delta;
@ -1160,7 +1192,7 @@ static int decode1_unpack_channel(stChannel *ch, clData *br,
}
/* 15 may be an invalid value? */
//else {
// return -1;
// return HCA_ERROR_INSENSITY;
//}
}
else {
@ -1519,9 +1551,9 @@ static const unsigned int decode5_imdct_window_int[128] = {
static const float *decode5_imdct_window = (const float *)decode5_imdct_window_int;
static void decoder5_run_imdct(stChannel *ch, int subframe) {
const static unsigned int size = HCA_SAMPLES_PER_SUBFRAME;
const static unsigned int half = HCA_SAMPLES_PER_SUBFRAME / 2;
const static unsigned int mdct_bits = HCA_MDCT_BITS;
static const unsigned int size = HCA_SAMPLES_PER_SUBFRAME;
static const unsigned int half = HCA_SAMPLES_PER_SUBFRAME / 2;
static const unsigned int mdct_bits = HCA_MDCT_BITS;
/* apply DCT-IV to dequantized spectra */

View file

@ -27,7 +27,7 @@ void clHCA_delete(clHCA *);
* the header length with clHCA_isOurFile, then read data and call this.
* May be called multiple times to reset decoder state.
* Returns 0 on success, <0 on failure. */
int clHCA_DecodeHeader(clHCA *, void *data, unsigned int size);
int clHCA_DecodeHeader(clHCA *, const void *data, unsigned int size);
typedef struct clHCA_stInfo {
unsigned int version;
@ -61,6 +61,7 @@ int clHCA_getInfo(clHCA *, clHCA_stInfo *out);
/* Decodes a single frame, from data after headerSize. Should be called after
* clHCA_DecodeHeader and size must be at least blockSize long.
* Data may be modified if encrypted.
* Returns 0 on success, <0 on failure. */
int clHCA_DecodeBlock(clHCA *, void *data, unsigned int size);
@ -81,6 +82,10 @@ void clHCA_SetKey(clHCA *, unsigned long long keycode);
* and select the key with scores closer to 1. */
int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size);
/* Resets the internal decode state, used when restarting to decode the file from the beginning.
* Without it there are minor differences, mainly useful when testing a new key. */
void clHCA_DecodeReset(clHCA * hca);
#ifdef __cplusplus
}
#endif

View file

@ -128,7 +128,7 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do,
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("ATRAC9: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done));
VGM_LOG("ATRAC9: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels);
}

View file

@ -1,259 +0,0 @@
#include "coding.h"
#ifdef VGM_USE_ATRAC9
#include "libatrac9.h"
/* opaque struct */
struct atrac9_codec_data {
uint8_t *data_buffer;
size_t data_buffer_size;
sample *sample_buffer;
size_t samples_filled; /* number of samples in the buffer */
size_t samples_used; /* number of samples extracted from the buffer */
int samples_to_discard;
atrac9_config config;
void *handle; /* decoder handle */
Atrac9CodecInfo info; /* decoder info */
};
atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
int status;
uint8_t config_data[4];
atrac9_codec_data *data = NULL;
data = calloc(1, sizeof(atrac9_codec_data));
if (!data) goto fail;
data->handle = Atrac9GetHandle();
if (!data->handle) goto fail;
put_32bitBE(config_data, cfg->config_data);
status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail;
status = Atrac9GetCodecInfo(data->handle, &data->info);
if (status < 0) goto fail;
//;VGM_LOG("ATRAC9: config=%x, sf-size=%x, sub-frames=%i x %i samples\n", cfg->config_data, info.superframeSize, info.framesInSuperframe, info.frameSamples);
if (cfg->channels && cfg->channels != data->info.channels) {
VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, data->info.channels);
goto fail; /* unknown multichannel layout */
}
/* must hold at least one superframe and its samples */
data->data_buffer_size = data->info.superframeSize;
data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size);
data->sample_buffer = calloc(sizeof(sample), data->info.channels * data->info.frameSamples * data->info.framesInSuperframe);
data->samples_to_discard = cfg->encoder_delay;
memcpy(&data->config, cfg, sizeof(atrac9_config));
return data;
fail:
free_atrac9(data);
return NULL;
}
void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
VGMSTREAMCHANNEL *stream = &vgmstream->ch[0];
atrac9_codec_data * data = vgmstream->codec_data;
int samples_done = 0;
while (samples_done < samples_to_do) {
if (data->samples_filled) { /* consume samples */
int samples_to_get = data->samples_filled;
if (data->samples_to_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
memcpy(outbuf + samples_done*channels,
data->sample_buffer + data->samples_used*channels,
samples_to_get*channels * sizeof(sample));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_used += samples_to_get;
data->samples_filled -= samples_to_get;
}
else { /* decode data */
int iframe, status;
int bytes_used = 0;
uint8_t *buffer = data->data_buffer;
size_t bytes;
data->samples_used = 0;
/* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
* superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */
/* read one raw block (superframe) and advance offsets */
bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile);
if (bytes != data->data_buffer_size) goto decode_fail;
stream->offset += bytes;
/* decode all frames in the superframe block */
for (iframe = 0; iframe < data->info.framesInSuperframe; iframe++) {
status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used);
if (status < 0) goto decode_fail;
buffer += bytes_used;
data->samples_filled += data->info.frameSamples;
}
}
}
return;
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("ATRAC9: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels);
}
void reset_atrac9(VGMSTREAM *vgmstream) {
atrac9_codec_data *data = vgmstream->codec_data;
if (!data) return;
if (!data->handle)
goto fail;
#if 0
/* reopen/flush, not needed as superframes decode separatedly and there is no carried state */
{
int status;
uint8_t config_data[4];
Atrac9ReleaseHandle(data->handle);
data->handle = Atrac9GetHandle();
if (!data->handle) goto fail;
put_32bitBE(config_data, data->config.config_data);
status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail;
}
#endif
data->samples_used = 0;
data->samples_filled = 0;
data->samples_to_discard = data->config.encoder_delay;
return;
fail:
return; /* decode calls should fail... */
}
void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) {
atrac9_codec_data *data = vgmstream->codec_data;
if (!data) return;
reset_atrac9(vgmstream);
/* find closest offset to desired sample, and samples to discard after that offset to reach loop */
{
int32_t seek_sample = data->config.encoder_delay + num_sample;
off_t seek_offset;
int32_t seek_discard;
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
size_t superframe_number, superframe_back;
superframe_number = (seek_sample / superframe_samples); /* closest */
/* decoded frames affect each other slightly, so move offset back to make PCM stable
* and equivalent to a full discard loop */
superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */
if (superframe_back > superframe_number)
superframe_back = superframe_number;
seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
data->samples_to_discard = seek_discard; /* already includes encoder delay */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset;
}
#if 0
//old full discard loop
{
data->samples_to_discard = num_sample;
data->samples_to_discard += data->config.encoder_delay;
/* loop offsets are set during decode; force them to stream start so discard works */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
#endif
}
void free_atrac9(atrac9_codec_data *data) {
if (!data) return;
if (data->handle) Atrac9ReleaseHandle(data->handle);
free(data->data_buffer);
free(data->sample_buffer);
free(data);
}
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) {
return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe);
}
#if 0 //not needed (for now)
int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_channels, size_t *out_frame_size) {
static const int sample_rate_table[16] = {
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
44100, 48000, 64000, 88200, 96000,128000,176400,192000
};
static const int channel_table[8] = {
1, 2, 2, 6, 8, 4, 0, 0
};
uint32_t sync = (atrac9_config >> 24) & 0xff; /* 8b */
uint8_t sample_rate_index = (atrac9_config >> 20) & 0x0f; /* 4b */
uint8_t channels_index = (atrac9_config >> 17) & 0x07; /* 3b */
/* uint8_t validation bit = (atrac9_config >> 16) & 0x01; */ /* 1b */
size_t frame_size = (atrac9_config >> 5) & 0x7FF; /* 11b */
size_t superframe_index = (atrac9_config >> 3) & 0x3; /* 2b */
/* uint8_t unused = (atrac9_config >> 0) & 0x7);*/ /* 3b */
if (sync != 0xFE)
goto fail;
if (out_sample_rate)
*out_sample_rate = sample_rate_table[sample_rate_index];
if (out_channels)
*out_channels = channel_table[channels_index];
if (out_frame_size)
*out_frame_size = (frame_size+1) * (1 << superframe_index);
return 1;
fail:
return 0;
}
#endif
#endif

View file

@ -157,7 +157,7 @@ void decode_celt_fsb(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_d
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("CELT: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done));
VGM_LOG("CELT: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels);
}

View file

@ -0,0 +1,29 @@
#include "coding.h"
/* Circus XPCM mode 2 decoding, verified vs EF.exe (info from foo_adpcm/libpcm and https://github.com/lioncash/ExtractData) */
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_pos = 0;
int32_t hist = stream->adpcm_history1_32;
int scale = stream->adpcm_scale;
off_t frame_offset = stream->offset; /* frame size is 1 */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int8_t code = read_8bit(frame_offset+i,stream->streamfile);
hist += code << scale;
if (code == 0) {
if (scale > 0)
scale--;
}
else if (code == 127 || code == -128) {
if (scale < 8)
scale++;
}
outbuf[sample_pos] = hist;
}
stream->adpcm_history1_32 = hist;
stream->adpcm_scale = scale;
}

View file

@ -21,6 +21,7 @@ void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_alp_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ffta2_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
@ -164,6 +165,12 @@ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* derf_decoder */
void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* circus_decoder */
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* pcfx_decoder */
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode);
size_t pcfx_bytes_to_samples(size_t bytes, int channels);
/* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channels, int type);
@ -267,6 +274,7 @@ void free_celt_fsb(celt_codec_data *data);
/* ffmpeg_decoder */
ffmpeg_codec_data *init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
ffmpeg_codec_data *init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size);
ffmpeg_codec_data *init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong);
void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels);
void reset_ffmpeg(VGMSTREAM *vgmstream);
@ -278,9 +286,14 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
/* ffmpeg_decoder_custom_opus.c (helper-things) */
ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
size_t ffmpeg_make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate);
size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile);
size_t ue4_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile);
ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
size_t switch_opus_get_samples(off_t offset, size_t data_size, STREAMFILE *streamFile);
size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
#endif
@ -314,7 +327,6 @@ typedef struct {
/* output */
int32_t num_samples;
int32_t skip_samples;
int32_t loop_start_sample;
int32_t loop_end_sample;
} ms_sample_data;
@ -325,6 +337,9 @@ void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_ali
void xma1_parse_fmt_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * loop_start_b, int32_t * loop_end_b, int32_t * loop_subframe, int be);
void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int * loop_flag, int32_t * out_num_samples, int32_t * out_loop_start_sample, int32_t * out_loop_end_sample, int be);
void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample);
void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples);
int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset);
size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align);
@ -345,4 +360,8 @@ typedef struct {
int r_bits(vgm_bitstream * ib, int num_bits, uint32_t * value);
int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value);
/* helper to pass a wrapped, clamped, fake extension-ed, SF to another meta */
STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* extension);
#endif /*_CODING_H*/

View file

@ -328,6 +328,35 @@ int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t data
if (buf_size < riff_size)
return -1;
/* XWMA encoder only allows a few channel/sample rate/bitrate combinations,
* but some create identical files with fake bitrate (1ch 22050hz at
* 20/48/192kbps are all 20kbps, with the exact same codec data).
* Decoder needs correct bitrate to work, so it's normalized here. */
/* (may be removed once FFmpeg fixes this) */
if (codec == 0x161) { /* WMAv2 only */
int ch = channels;
int sr = sample_rate;
int br = avg_bps * 8;
/* Must be a bug in MS's encoder, as later versions of xWMAEncode remove these bitrates */
if (ch == 1) {
if (sr == 22050 && (br==48000 || br==192000))
br = 20000;
else if (sr == 32000 && (br==48000 || br==192000))
br = 20000;
else if (sr == 44100 && (br==96000 || br==192000))
br = 48000;
}
else if (ch == 2) {
if (sr == 22050 && (br==48000 || br==192000))
br = 32000;
else if (sr == 32000 && (br==192000))
br = 48000;
}
avg_bps = br / 8;
}
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "XWMA", 4);
@ -341,7 +370,7 @@ int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t data
put_16bitLE(buf+0x20, block_align); /* block align */
put_16bitLE(buf+0x22, 16); /* bits per sample */
put_16bitLE(buf+0x24, 0); /* extra size */
/* here goes the "dpds" table, but it's optional and not needed by FFmpeg */
/* here goes the "dpds" seek table, but it's optional and not needed by FFmpeg (and also buggy) */
memcpy(buf+0x26, "data", 4);
put_32bitLE(buf+0x2a, data_size); /* data size */
@ -410,85 +439,93 @@ fail:
/* XMA PARSING */
/* ******************************************** */
static void ms_audio_parse_header(STREAMFILE *streamFile, int xma_version, off_t offset_b, int bits_frame_size, size_t *first_frame_b, size_t *packet_skip_count, size_t *header_size_b) {
if (xma_version == 1) { /* XMA1 */
//packet_sequence = read_bitsBE_b(offset_b+0, 4, streamFile); /* numbered from 0 to N */
//unknown = read_bitsBE_b(offset_b+4, 2, streamFile); /* packet_metadata? (always 2) */
*first_frame_b = read_bitsBE_b(offset_b+6, bits_frame_size, streamFile); /* offset in bits inside the packet */
*packet_skip_count = read_bitsBE_b(offset_b+21, 11, streamFile); /* packets to skip for next packet of this stream */
*header_size_b = 32;
} else if (xma_version == 2) { /* XMA2 */
//frame_count = read_bitsBE_b(offset_b+0, 6, streamFile); /* frames that begin in this packet */
*first_frame_b = read_bitsBE_b(offset_b+6, bits_frame_size, streamFile); /* offset in bits inside this packet */
//packet_metadata = read_bitsBE_b(offset_b+21, 3, streamFile); /* packet_metadata (always 1) */
*packet_skip_count = read_bitsBE_b(offset_b+24, 8, streamFile); /* packets to skip for next packet of this stream */
*header_size_b = 32;
} else { /* WMAPRO(v3) */
//packet_sequence = read_bitsBE_b(offset_b+0, 4, streamFile); /* numbered from 0 to N */
//unknown = read_bitsBE_b(offset_b+4, 2, streamFile); /* packet_metadata? (always 2) */
*first_frame_b = read_bitsBE_b(offset_b+6, bits_frame_size, streamFile); /* offset in bits inside the packet */
*packet_skip_count = 0; /* xwma has no need to skip packets since it uses real multichannel audio */
*header_size_b = 4+2+bits_frame_size; /* variable-sized header */
}
/* XMA2 packets with XMA1 RIFF (transmogrified), remove the packet metadata flag */
if (xma_version == 1 && (*packet_skip_count & 0x700) == 0x100) {
//VGM_LOG("MS_SAMPLES: XMA1 transmogrified packet header at 0x%lx\n", (off_t)offset_b/8);
*packet_skip_count &= ~0x100;
}
/* full packet skip, no new frames start in this packet (prev frames can end here)
* standardized to some value */
if (*packet_skip_count == 0x7FF) { /* XMA1, 11b */
VGM_LOG("MS_SAMPLES: XMA1 full packet_skip at 0x%x\n", (uint32_t)offset_b/8);
*packet_skip_count = 0x800;
}
else if (*packet_skip_count == 0xFF) { /* XMA2, 8b*/
VGM_LOG("MS_SAMPLES: XMA2 full packet_skip at 0x%x\n", (uint32_t)offset_b/8);
*packet_skip_count = 0x800;
}
/* unusual but not impossible, as the encoder can interleave packets in any way */
VGM_ASSERT((*packet_skip_count > 10 && *packet_skip_count < 0x800),
"MS_SAMPLES: found big packet skip %i at 0x%x\n", *packet_skip_count, (uint32_t)offset_b/8);
}
/**
* Find total and loop samples of Microsoft audio formats (WMAPRO/XMA1/XMA2) by reading frame headers.
*
* The stream is made of packets, each containing N small frames of X samples. Frames are further divided into subframes.
* XMA1/XMA2 can divided into streams for multichannel (1/2ch ... 1/2ch). From the file start, packet 1..N is owned by
* stream 1..N. Then must follow "packet_skip" value to find the stream next packet, as they are arbitrarily interleaved.
* We only need to follow the first stream, as all must contain the same number of samples.
*
* XMA1/XMA2/WMAPRO data only differs in the packet headers.
*/
static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int start_packet, int channels_per_packet, int bytes_per_packet, int samples_per_frame, int samples_per_subframe, int bits_frame_size) {
int frames = 0, samples = 0, loop_start_frame = 0, loop_end_frame = 0, start_skip = 0, end_skip = 0;
static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int channels_per_packet, int bytes_per_packet, int samples_per_frame, int samples_per_subframe, int bits_frame_size) {
int frames = 0, samples = 0, loop_start_frame = 0, loop_end_frame = 0;
size_t first_frame_b, packet_skip_count = 0, frame_size_b, packet_size_b, header_size_b;
size_t first_frame_b, packet_skip_count, header_size_b, frame_size_b;
off_t offset_b, packet_offset_b, frame_offset_b;
size_t packet_size = bytes_per_packet;
size_t packet_size_b = packet_size * 8;
off_t offset = msd->data_offset;
off_t max_offset = msd->data_offset + msd->data_size;
off_t stream_offset_b = msd->data_offset * 8;
offset += start_packet * packet_size;
packet_size_b = packet_size * 8;
/* read packets */
while (offset < max_offset) {
offset_b = offset * 8; /* global offset in bits */
offset += packet_size; /* global offset in bytes */
/* packet header */
if (msd->xma_version == 1) { /* XMA1 */
//packet_sequence = read_bitsBE_b(offset_b+0, 4, streamFile); /* numbered from 0 to N */
//unknown = read_bitsBE_b(offset_b+4, 2, streamFile); /* packet_metadata? (always 2) */
first_frame_b = read_bitsBE_b(offset_b+6, bits_frame_size, streamFile); /* offset in bits inside the packet */
packet_skip_count = read_bitsBE_b(offset_b+21, 11, streamFile); /* packets to skip for next packet of this stream */
header_size_b = 32;
} else if (msd->xma_version == 2) { /* XMA2 */
//frame_count = read_bitsBE_b(offset_b+0, 6, streamFile); /* frames that begin in this packet */
first_frame_b = read_bitsBE_b(offset_b+6, bits_frame_size, streamFile); /* offset in bits inside this packet */
//packet_metadata = read_bitsBE_b(offset_b+21, 3, streamFile); /* packet_metadata (always 1) */
packet_skip_count = read_bitsBE_b(offset_b+24, 8, streamFile); /* packets to skip for next packet of this stream */
header_size_b = 32;
} else { /* WMAPRO(v3) */
//packet_sequence = read_bitsBE_b(offset_b+0, 4, streamFile); /* numbered from 0 to N */
//unknown = read_bitsBE_b(offset_b+4, 2, streamFile); /* packet_metadata? (always 2) */
first_frame_b = read_bitsBE_b(offset_b+6, bits_frame_size, streamFile); /* offset in bits inside the packet */
packet_skip_count = 0; /* xwma has no need to skip packets since it uses real multichannel audio */
header_size_b = 4+2+bits_frame_size; /* variable-sized header */
ms_audio_parse_header(streamFile, msd->xma_version, offset_b, bits_frame_size, &first_frame_b, &packet_skip_count, &header_size_b);
if (packet_skip_count > 0x7FF) {
continue; /* full skip */
}
/* XMA2 packets with XMA1 RIFF (transmogrified), remove the packet metadata flag */
if (msd->xma_version == 1 && (packet_skip_count & 0x700) == 0x100) {
//VGM_LOG("MS_SAMPLES: XMA1 transmogrified packet header at 0x%lx\n", (off_t)offset_b/8);
packet_skip_count &= ~0x100;
}
/* full packet skip, no new frames start in this packet (prev frames can end here) */
if (packet_skip_count == 0x7FF) { /* XMA1, 11b */
VGM_LOG("MS_SAMPLES: XMA1 full packet_skip %i at 0x%"PRIx64"\n", packet_skip_count, (off64_t)offset_b/8);
packet_skip_count = 0;
continue;
}
else if (packet_skip_count == 0xFF) { /* XMA2, 8b*/
VGM_LOG("MS_SAMPLES: XMA2 full packet_skip %i at 0x%"PRIx64"\n", packet_skip_count, (off64_t)offset_b/8);
packet_skip_count = 0;
continue;
}
offset += packet_size * (packet_skip_count); /* skip packets not owned by the first stream, since we only need samples from it */
/* unusual but not impossible, as the encoder can interleave packets in any way */
VGM_ASSERT(packet_skip_count > 10, "MS_SAMPLES: found big packet skip %i at 0x%"PRIx64"\n", packet_skip_count, (off64_t)offset_b/8);
packet_offset_b = header_size_b + first_frame_b;
/* skip packets not owned by the first stream for next time */
offset += packet_size * (packet_skip_count);
/* read packet frames */
while (packet_offset_b < packet_size_b) {
frame_offset_b = offset_b + packet_offset_b; /* in bits for aligment stuff */
/* loops, later adjusted with subframe (seems correct vs tests) */
/* frame loops, later adjusted with subframes (seems correct vs tests) */
if (msd->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == msd->loop_start_b)
loop_start_frame = frames;
if (msd->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == msd->loop_end_b)
@ -497,7 +534,6 @@ static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, i
/* frame header */
frame_size_b = read_bitsBE_b(frame_offset_b, bits_frame_size, streamFile);
frame_offset_b += bits_frame_size;
//;VGM_LOG("MS_SAMPLES: frame_offset=0x%lx (0b%lx), frame_size=0x%x (0b%x)\n", (off_t)frame_offset_b/8,(off_t)frame_offset_b, frame_size_b/8, frame_size_b);
/* stop when packet padding starts (0x00 for XMA1 or 0xFF in XMA2) */
if (frame_size_b == 0 || frame_size_b == (0xffffffff >> (32 - bits_frame_size))) {
@ -506,12 +542,77 @@ static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, i
packet_offset_b += frame_size_b; /* including header */
/* find skips (info from FFmpeg) */
if (channels_per_packet && (msd->xma_version == 1 || msd->xma_version == 2)) {
int flag;
int tilehdr_size = 15; //todo incorrect but usable for XMA, fix for WMAPro (complex calcs, see ffmpeg decode_tilehdr)
samples += samples_per_frame;
frames++;
frame_offset_b += tilehdr_size;
/* last bit in frame = more frames flag, end packet to avoid reading garbage in some cases
* (last frame spilling to other packets also has this flag, though it's ignored here) */
if (packet_offset_b < packet_size_b && !read_bitsBE_b(offset_b + packet_offset_b - 1, 1, streamFile)) {
break;
}
}
}
/* result */
msd->num_samples = samples;
if (msd->loop_flag && loop_end_frame > loop_start_frame) {
msd->loop_start_sample = loop_start_frame * samples_per_frame + msd->loop_start_subframe * samples_per_subframe;
msd->loop_end_sample = loop_end_frame * samples_per_frame + (msd->loop_end_subframe) * samples_per_subframe;
}
/* the above can't properly read skips for WMAPro ATM, but should fixed to 1 frame anyway */
if (msd->xma_version == 0) {
msd->num_samples -= samples_per_frame; /* FFmpeg does skip this */
#if 0
msd->num_samples += (samples_per_frame / 2); /* but doesn't add extra samples */
#endif
}
}
/* simlar to the above but only gets skips */
static void ms_audio_get_skips(STREAMFILE *streamFile, int xma_version, off_t data_offset, int channels_per_packet, int bytes_per_packet, int samples_per_frame, int bits_frame_size, int *out_start_skip, int *out_end_skip) {
int start_skip = 0, end_skip = 0;
size_t first_frame_b, packet_skip_count, header_size_b, frame_size_b;
off_t offset_b, packet_offset_b, frame_offset_b;
size_t packet_size = bytes_per_packet;
size_t packet_size_b = packet_size * 8;
off_t offset = data_offset;
/* read packet */
{
offset_b = offset * 8; /* global offset in bits */
offset += packet_size; /* global offset in bytes */
/* packet header */
ms_audio_parse_header(streamFile, 2, offset_b, bits_frame_size, &first_frame_b, &packet_skip_count, &header_size_b);
if (packet_skip_count > 0x7FF) {
return; /* full skip */
}
packet_offset_b = header_size_b + first_frame_b;
/* read packet frames */
while (packet_offset_b < packet_size_b) {
frame_offset_b = offset_b + packet_offset_b; /* in bits for aligment stuff */
/* frame header */
frame_size_b = read_bitsBE_b(frame_offset_b, bits_frame_size, streamFile);
frame_offset_b += bits_frame_size;
/* stop when packet padding starts (0x00 for XMA1 or 0xFF in XMA2) */
if (frame_size_b == 0 || frame_size_b == (0xffffffff >> (32 - bits_frame_size))) {
break;
}
packet_offset_b += frame_size_b; /* including header */
/* find skips (info from FFmpeg) */
if (channels_per_packet && (xma_version == 1 || xma_version == 2)) {
int flag;
int len_tilehdr_size = 15; //todo incorrect but usable for XMA, fix for WMAPro (complex, see ffmpeg decode_tilehdr)
frame_offset_b += len_tilehdr_size;
/* ignore "postproc transform" */
if (channels_per_packet > 1) {
@ -526,7 +627,7 @@ static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, i
}
}
/* get start/end skips to get the proper number of samples */
/* get start/end skips to get the proper number of samples (both can be 0) */
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
frame_offset_b += 1;
if (flag) {
@ -535,14 +636,13 @@ static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, i
frame_offset_b += 1;
if (flag) {
int new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile);
VGM_LOG("MS_SAMPLES: start_skip %i at 0x%"PRIx64" (bit 0x%"PRIx64")\n", new_skip, (off64_t)frame_offset_b/8, (off64_t)frame_offset_b);
VGM_ASSERT(start_skip, "MS_SAMPLES: more than one start_skip (%i)\n", new_skip); //ignore, happens due to incorrect tilehdr_size
//;VGM_LOG("MS_SAMPLES: start_skip %i at 0x%x (bit 0x%x)\n", new_skip, (uint32_t)frame_offset_b/8, (uint32_t)frame_offset_b);
frame_offset_b += 10;
if (new_skip > samples_per_frame) /* from xmaencode */
new_skip = samples_per_frame;
if (start_skip==0)
if (start_skip==0) /* only use first skip */
start_skip = new_skip;
}
@ -551,46 +651,25 @@ static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, i
frame_offset_b += 1;
if (flag) {
int new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile);
VGM_LOG("MS_SAMPLES: end_skip %i at 0x%"PRIx64" (bit 0x%"PRIx64")\n", new_skip, (off64_t)frame_offset_b/8, (off64_t)frame_offset_b);
VGM_ASSERT(end_skip, "MS_SAMPLES: more than one end_skip (%i)\n", new_skip);//ignore, happens due to incorrect tilehdr_size
//;VGM_LOG("MS_SAMPLES: end_skip %i at 0x%x (bit 0x%x)\n", new_skip, (uint32_t)frame_offset_b/8, (uint32_t)frame_offset_b);
frame_offset_b += 10;
if (new_skip > samples_per_frame) /* from xmaencode */
new_skip = samples_per_frame;
end_skip = new_skip;
end_skip = new_skip; /* always use last skip */
}
}
}
}
}
samples += samples_per_frame;
frames++;
/* last bit in frame = more frames flag, end packet to avoid reading garbage in some cases
* (last frame spilling to other packets also has this flag, though it's ignored here) */
if (packet_offset_b < packet_size_b && !read_bitsBE_b(offset_b + packet_offset_b - 1, 1, streamFile)) {
break;
}
}
/* output results */
if (out_start_skip) *out_start_skip = start_skip;
if (out_end_skip) *out_end_skip = end_skip;
}
//todo FFmpeg seems to decode 1 subframe late vs xmaencode, and doesn't write 128 init samples, so skips are not useful ATM
//samples = samples + 128 - start_skip - end_skip; /* 128 init samples added by xmaencode */
msd->num_samples = samples;
msd->skip_samples = start_skip;
if (msd->loop_flag && loop_end_frame > loop_start_frame) {
msd->loop_start_sample = loop_start_frame * samples_per_frame + msd->loop_start_subframe * samples_per_subframe;
msd->loop_end_sample = loop_end_frame * samples_per_frame + msd->loop_end_subframe * samples_per_subframe;
//todo maybe this is needed
//msd->loop_start_sample -= start_skip;
//msd->loop_end_sample -= start_skip;
}
}
static int wma_get_samples_per_frame(int version, int sample_rate, uint32_t decode_flags) {
int frame_len_bits;
@ -618,33 +697,40 @@ static int wma_get_samples_per_frame(int version, int sample_rate, uint32_t deco
return 1 << frame_len_bits;
}
void xma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile) {
const int bytes_per_packet = 2048;
const int samples_per_frame = 512;
const int samples_per_subframe = 128;
static int xma_get_channels_per_stream(STREAMFILE* streamFile, off_t chunk_offset, int channels) {
int start_stream = 0;
int channels_per_stream = 0;
/* get from stream config (needed to find skips) */
if (msd->chunk_offset) {
int format = read_16bitLE(msd->chunk_offset,streamFile);
if (chunk_offset) {
int format = read_16bitLE(chunk_offset,streamFile);
if (format == 0x0165 || format == 0x6501) { /* XMA1 */
channels_per_stream = read_8bit(msd->chunk_offset + 0x0C + 0x14*start_stream + 0x11,streamFile);
channels_per_stream = read_8bit(chunk_offset + 0x0C + 0x14*start_stream + 0x11,streamFile);
} else if (format == 0x0166 || format == 0x6601) { /* new XMA2 */
channels_per_stream = msd->channels > 1 ? 2 : 1;
channels_per_stream = channels > 1 ? 2 : 1;
} else { /* old XMA2 */
int version = read_8bit(msd->chunk_offset,streamFile);
channels_per_stream = read_8bit(msd->chunk_offset + 0x20 + (version==3 ? 0x00 : 0x08) + 0x4*start_stream + 0x00,streamFile);
int version = read_8bit(chunk_offset,streamFile);
channels_per_stream = read_8bit(chunk_offset + 0x20 + (version==3 ? 0x00 : 0x08) + 0x4*start_stream + 0x00,streamFile);
}
}
else if (msd->channels) {
channels_per_stream = msd->channels > 1 ? 2 : 1;
else if (channels) {
channels_per_stream = channels == 1 ? 1 : 2; /* default for XMA without RIFF chunks, most common */
}
if (channels_per_stream > 2)
channels_per_stream = 0;
ms_audio_get_samples(msd, streamFile, start_stream, channels_per_stream, bytes_per_packet, samples_per_frame, samples_per_subframe, 15);
return channels_per_stream;
}
void xma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile) {
const int bytes_per_packet = 2048;
const int samples_per_frame = 512;
const int samples_per_subframe = 128;
const int bits_frame_size = 15;
int channels_per_stream = xma_get_channels_per_stream(streamFile, msd->chunk_offset, msd->channels);
ms_audio_get_samples(msd, streamFile, channels_per_stream, bytes_per_packet, samples_per_frame, samples_per_subframe, bits_frame_size);
}
void wmapro_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_align, int sample_rate, uint32_t decode_flags) {
@ -653,7 +739,6 @@ void wmapro_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_
int samples_per_frame = 0;
int samples_per_subframe = 0;
int bits_frame_size = 0;
int start_packet = 0;
int channels_per_stream = msd->channels;
if (!(decode_flags & 0x40)) {
@ -663,10 +748,10 @@ void wmapro_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_
}
samples_per_frame = wma_get_samples_per_frame(version, sample_rate, decode_flags);
bits_frame_size = (int)floor(log(block_align) / log(2)) + 4; /* max bits needed to represent this block_align */
samples_per_subframe = 0; /* not really needed WMAPro can't use loop subframes (complex subframe lengths) */
samples_per_subframe = 0; /* not needed as WMAPro can't use loop subframes (complex subframe lengths) */
msd->xma_version = 0; /* signal it's not XMA */
ms_audio_get_samples(msd, streamFile, start_packet, channels_per_stream, bytes_per_packet, samples_per_frame, samples_per_subframe, bits_frame_size);
ms_audio_get_samples(msd, streamFile, channels_per_stream, bytes_per_packet, samples_per_frame, samples_per_subframe, bits_frame_size);
}
void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_align, int sample_rate, uint32_t decode_flags) {
@ -703,9 +788,89 @@ void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_ali
}
msd->num_samples = num_frames * samples_per_frame;
#if 0 //todo apply once FFmpeg decode is ok
msd->num_samples += (samples_per_frame / 2); /* last IMDCT samples */
msd->num_samples -= (samples_per_frame * 2); /* WMA default encoder delay */
#endif
}
/* XMA hell for precise looping and gapless support, fixes raw sample values from headers
* that don't count XMA's final subframe/encoder delay/encoder padding, and FFmpeg stuff.
* Configurable since different headers vary for maximum annoyance. */
void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) {
const int bytes_per_packet = 2048;
const int samples_per_frame = 512;
const int samples_per_subframe = 128;
const int bits_frame_size = 15;
int xma_version = 2; /* works ok even for XMA1 */
int channels_per_stream = xma_get_channels_per_stream(streamFile, chunk_offset, vgmstream->channels);
off_t first_packet = stream_offset;
off_t last_packet = stream_offset + stream_size - bytes_per_packet;
int32_t start_skip = 0, end_skip = 0;
if (stream_offset + stream_size > get_streamfile_size(streamFile)) {
VGM_LOG("XMA SKIPS: ignoring bad stream offset+size vs real size\n");
return;
}
/* find delay/padding values in the bitstream (should be safe even w/ multistreams
* as every stream repeats them). Theoretically every packet could contain skips,
* doesn't happen in practice though. */
ms_audio_get_skips(streamFile, xma_version, first_packet, channels_per_stream, bytes_per_packet, samples_per_frame, bits_frame_size, &start_skip, NULL);
ms_audio_get_skips(streamFile, xma_version, last_packet, channels_per_stream, bytes_per_packet, samples_per_frame, bits_frame_size, NULL, &end_skip);
//;VGM_LOG("XMA SKIPS: apply start=%i, end=%i\n", start_skip, end_skip);
VGM_ASSERT(start_skip < samples_per_frame, "XMA SKIPS: small start skip\n");
if (end_skip == 512) { /* most likely a read bug */
VGM_LOG("XMA SKIPS: ignoring big end_skip\n");
end_skip = 0;
}
/* apply XMA extra samples */
if (fix_num_samples) {
vgmstream->num_samples += samples_per_subframe; /* final extra IMDCT samples */
vgmstream->num_samples -= start_skip; /* first samples skipped at the beginning */
vgmstream->num_samples -= end_skip; /* last samples discarded at the end */
}
/* from xma2encode tests this is correct (probably encodes/decodes loops considering all skips), ex.-
* full loop wav to xma makes start=384 (0 + ~512 delay - 128 padding), then xma to wav has "smpl" start=0 */
if (fix_loop_samples && vgmstream->loop_flag) {
vgmstream->loop_start_sample += samples_per_subframe;
vgmstream->loop_start_sample -= start_skip;
vgmstream->loop_end_sample += samples_per_subframe;
vgmstream->loop_end_sample -= start_skip;
/* since loops are adjusted this shouldn't happen (often loop_end == num_samples after applying all) */
if (vgmstream->loop_end_sample > vgmstream->num_samples &&
vgmstream->loop_end_sample - end_skip <= vgmstream->loop_end_sample) {
VGM_LOG("XMA SAMPLES: adjusted loop end\n");
vgmstream->loop_end_sample -= end_skip;
}
}
#ifdef VGM_USE_FFMPEG
/* also fix FFmpeg, since we now know exact skips */
{
ffmpeg_codec_data* data = vgmstream->codec_data;
/* FFmpeg doesn't XMA apply encoder delay ATM so here we fix it manually.
* XMA delay is part if the bitstream and while theoretically it could be any
* value (and is honored by xmaencoder), basically it's always 512.
*
* Somehow also needs to skip 64 extra samples (looks like another FFmpeg bug
* where XMA outputs half a subframe samples late, WMAPRO isn't affected),
* which sometimes makes FFmpeg complain (=reads after end) but doesn't seem audible. */
if (data->skipSamples == 0) {
ffmpeg_set_skip_samples(data, start_skip+64);
}
}
#endif
}
/* ******************************************** */
/* HEADER PARSING */
@ -718,7 +883,8 @@ void xma1_parse_fmt_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * chan
int32_t (*read_32bit)(off_t,STREAMFILE*) = be ? read_32bitBE : read_32bitLE;
int i, num_streams, total_channels = 0;
if (read_16bit(chunk_offset+0x00,streamFile) != 0x165) return;
if (read_16bit(chunk_offset+0x00,streamFile) != 0x165)
return;
num_streams = read_16bit(chunk_offset+0x08,streamFile);
if(loop_flag) *loop_flag = (uint8_t)read_8bit(chunk_offset+0xA,streamFile) > 0;
@ -737,71 +903,79 @@ void xma1_parse_fmt_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * chan
}
/* Read values from a 'new' XMA2 RIFF "fmt" chunk (XMA2WAVEFORMATEX), starting from an offset *after* chunk type+size.
* Useful as custom X360 headers commonly have it lurking inside. Only the extra data, the first part is a normal WAVEFORMATEX. */
* Useful as custom X360 headers commonly have it lurking inside. Only parses the extra data (before is a normal WAVEFORMATEX). */
void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int * out_loop_flag, int32_t * out_num_samples, int32_t * out_loop_start_sample, int32_t * out_loop_end_sample, int be) {
int16_t (*read_16bit)(off_t,STREAMFILE*) = be ? read_16bitBE : read_16bitLE;
int32_t (*read_32bit)(off_t,STREAMFILE*) = be ? read_32bitBE : read_32bitLE;
int num_samples, loop_start_sample, loop_end_sample, loop_flag;
if (read_16bit(chunk_offset+0x00,streamFile) != 0x166) return;
/* up to extra data is a WAVEFORMATEX */
if (read_16bit(chunk_offset+0x10,streamFile) < 0x22) return; /* expected extra data size */
if (read_16bit(chunk_offset+0x00,streamFile) != 0x166)
return;
if (read_16bit(chunk_offset+0x10,streamFile) < 0x22)
return; /* expected extra data size */
num_samples = read_32bit(chunk_offset+0x18,streamFile); /* max samples from all frames */
num_samples = read_32bit(chunk_offset+0x18,streamFile);
loop_start_sample = read_32bit(chunk_offset+0x28,streamFile);
loop_end_sample = loop_start_sample + read_32bit(chunk_offset+0x2C,streamFile);
loop_flag = (uint8_t)read_8bit(chunk_offset+0x30,streamFile) != 0;
/* num_samples isn't used by xmaencode, so final_num_samples = num_samples + setup_samples (128) - start_skip (~512) - end_skip (0..512) */
/* loop values seem to be after applying skips, so loop_end wouldn't be higher than final_num_samples */
/* may need loop end +1, though some header doesn't need it (ex.- Sonic and Sega All Stars Racing .str) */
/* flag rarely set, use loop_end as marker */
if (!loop_flag) {
loop_flag = loop_end_sample > 0;
/* loop_end_sample - 128 + start_skip + end_skip = num_samples, use approx */
if (loop_start_sample == 384 && loop_end_sample - 128 + 512 + 512 >= num_samples) {
/* some XMA incorrectly do full loops for every song/jingle [Shadows of the Damned (X360)] */
if ((loop_start_sample + 128 - 512) == 0 && (loop_end_sample + 128 - 512) + 256 >= (num_samples + 128 - 512)) {
VGM_LOG("XMA2 PARSE: disabling full loop\n");
loop_flag = 0; /* some XMA have full loop set without loop flag and shouldn't loop */
loop_flag = 0;
}
}
/* samples are "raw" values, must be fixed externally (see xma_fix_raw_samples) */
if(out_num_samples) *out_num_samples = num_samples;
if(out_loop_start_sample) *out_loop_start_sample = loop_start_sample;
if(out_loop_end_sample) *out_loop_end_sample = loop_end_sample;
if(out_loop_flag) *out_loop_flag = loop_flag;
/* play_begin+end = pcm_samples in original sample rate (not usable when resampled = looped) */
/* play_begin+end = pcm_samples in original sample rate (not usable as file may be resampled) */
/* int32_t play_begin_sample = read_32bit(xma->chunk_offset+0x20,streamFile); */
/* int32_t play_end_sample = play_begin_sample + read_32bit(xma->chunk_offset+0x24,streamFile); */
}
/* Read values from an 'old' XMA2 RIFF "XMA2" chunk (XMA2WAVEFORMAT), starting from an offset *after* chunk type+size.
* Useful as custom X360 headers commonly have it lurking inside. */
void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample) {
void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * out_channels, int * out_sample_rate, int * out_loop_flag, int32_t * out_num_samples, int32_t * out_loop_start_sample, int32_t * out_loop_end_sample) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = read_32bitBE; /* XMA2WAVEFORMAT is always big endian */
int i, xma2_chunk_version, num_streams, total_channels = 0;
off_t off;
int i, xma2_chunk_version, num_streams;
int channels, sample_rate, loop_flag, num_samples, loop_start_sample, loop_end_sample;
off_t offset;
xma2_chunk_version = read_8bit(chunk_offset+0x00,streamFile);
num_streams = read_8bit(chunk_offset+0x01,streamFile);
if(loop_start_sample) *loop_start_sample = read_32bit(chunk_offset+0x04,streamFile);
if(loop_end_sample) *loop_end_sample = read_32bit(chunk_offset+0x08,streamFile);
if(loop_flag) *loop_flag = (uint8_t)read_8bit(chunk_offset+0x03,streamFile) > 0 /* rarely not set, encoder default */
|| read_32bit(chunk_offset+0x08,streamFile); /* loop_end_sample */
if(sample_rate) *sample_rate = read_32bit(chunk_offset+0x0c,streamFile);;
loop_start_sample = read_32bit(chunk_offset+0x04,streamFile);
loop_end_sample = read_32bit(chunk_offset+0x08,streamFile);
loop_flag = (uint8_t)read_8bit(chunk_offset+0x03,streamFile) > 0 || loop_end_sample; /* rarely not set, encoder default */
sample_rate = read_32bit(chunk_offset+0x0c,streamFile);
/* may need loop end +1 */
off = xma2_chunk_version == 3 ? 0x14 : 0x1C;
if(num_samples) *num_samples = read_32bit(chunk_offset+off,streamFile);
/*xma->pcm_samples = read_32bitBE(xma->chunk_offset+off+0x04,streamFile)*/
/* num_samples is the max samples in the file (apparently not including encoder delay) */
/* pcm_samples are original WAV's; not current since samples and sample rate may be adjusted for looping purposes */
offset = xma2_chunk_version == 3 ? 0x14 : 0x1C;
num_samples = read_32bit(chunk_offset+offset+0x00,streamFile);
/* pcm_samples in original sample rate (not usable as file may be resampled) */
/* pcm_samples = read_32bitBE(chunk_offset+offset+0x04,streamFile)*/
off = xma2_chunk_version == 3 ? 0x20 : 0x28;
/* channels is the sum of all streams */
offset = xma2_chunk_version == 3 ? 0x20 : 0x28;
channels = 0; /* channels is the sum of all streams */
for (i = 0; i < num_streams; i++) {
total_channels += read_8bit(chunk_offset+off+i*0x04,streamFile);
channels += read_8bit(chunk_offset+offset+i*0x04,streamFile);
}
if (channels) *channels = total_channels;
/* samples are "raw" values, must be fixed externally (see xma_fix_raw_samples) */
if(out_channels) *out_channels = channels;
if(out_sample_rate) *out_sample_rate = sample_rate;
if(out_num_samples) *out_num_samples = num_samples;
if(out_loop_start_sample) *out_loop_start_sample = loop_start_sample;
if(out_loop_end_sample) *out_loop_end_sample = loop_end_sample;
if(out_loop_flag) *out_loop_flag = loop_flag;
}
/* manually read from "fact" chunk */
@ -983,3 +1157,31 @@ int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value) {
else
return w_bits_msf(ob,num_bits,value);
}
/* ******************************************** */
/* CUSTOM STREAMFILES */
/* ******************************************** */
STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* extension) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
if (extension) {
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
}
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View file

@ -83,6 +83,106 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
}
}
#if 0
/* later PC games use float math, though in the end sounds basically the same (decompiled from various exes) */
static const double XA_K0[16] = { 0.0, 0.9375, 1.796875, 1.53125 };
static const double XA_K1[16] = { 0.0, 0.0, -0.8125, -0.859375 };
/* code uses look-up table but it's be equivalent to:
* (double)((nibble << 28) >> (shift + 8) >> 8) or (double)(signed_nibble << (12 - shift)) */
static const uint32_t FLOAT_TABLE_INT[256] = {
0x00000000,0x45800000,0x46000000,0x46400000,0x46800000,0x46A00000,0x46C00000,0x46E00000,
0xC7000000,0xC6E00000,0xC6C00000,0xC6A00000,0xC6800000,0xC6400000,0xC6000000,0xC5800000,
0x00000000,0x45000000,0x45800000,0x45C00000,0x46000000,0x46200000,0x46400000,0x46600000,
0xC6800000,0xC6600000,0xC6400000,0xC6200000,0xC6000000,0xC5C00000,0xC5800000,0xC5000000,
0x00000000,0x44800000,0x45000000,0x45400000,0x45800000,0x45A00000,0x45C00000,0x45E00000,
0xC6000000,0xC5E00000,0xC5C00000,0xC5A00000,0xC5800000,0xC5400000,0xC5000000,0xC4800000,
0x00000000,0x44000000,0x44800000,0x44C00000,0x45000000,0x45200000,0x45400000,0x45600000,
0xC5800000,0xC5600000,0xC5400000,0xC5200000,0xC5000000,0xC4C00000,0xC4800000,0xC4000000,
0x00000000,0x43800000,0x44000000,0x44400000,0x44800000,0x44A00000,0x44C00000,0x44E00000,
0xC5000000,0xC4E00000,0xC4C00000,0xC4A00000,0xC4800000,0xC4400000,0xC4000000,0xC3800000,
0x00000000,0x43000000,0x43800000,0x43C00000,0x44000000,0x44200000,0x44400000,0x44600000,
0xC4800000,0xC4600000,0xC4400000,0xC4200000,0xC4000000,0xC3C00000,0xC3800000,0xC3000000,
0x00000000,0x42800000,0x43000000,0x43400000,0x43800000,0x43A00000,0x43C00000,0x43E00000,
0xC4000000,0xC3E00000,0xC3C00000,0xC3A00000,0xC3800000,0xC3400000,0xC3000000,0xC2800000,
0x00000000,0x42000000,0x42800000,0x42C00000,0x43000000,0x43200000,0x43400000,0x43600000,
0xC3800000,0xC3600000,0xC3400000,0xC3200000,0xC3000000,0xC2C00000,0xC2800000,0xC2000000,
0x00000000,0x41800000,0x42000000,0x42400000,0x42800000,0x42A00000,0x42C00000,0x42E00000,
0xC3000000,0xC2E00000,0xC2C00000,0xC2A00000,0xC2800000,0xC2400000,0xC2000000,0xC1800000,
0x00000000,0x41000000,0x41800000,0x41C00000,0x42000000,0x42200000,0x42400000,0x42600000,
0xC2800000,0xC2600000,0xC2400000,0xC2200000,0xC2000000,0xC1C00000,0xC1800000,0xC1000000,
0x00000000,0x40800000,0x41000000,0x41400000,0x41800000,0x41A00000,0x41C00000,0x41E00000,
0xC2000000,0xC1E00000,0xC1C00000,0xC1A00000,0xC1800000,0xC1400000,0xC1000000,0xC0800000,
0x00000000,0x40000000,0x40800000,0x40C00000,0x41000000,0x41200000,0x41400000,0x41600000,
0xC1800000,0xC1600000,0xC1400000,0xC1200000,0xC1000000,0xC0C00000,0xC0800000,0xC0000000,
0x00000000,0x3F800000,0x40000000,0x40400000,0x40800000,0x40A00000,0x40C00000,0x40E00000,
0xC1000000,0xC0E00000,0xC0C00000,0xC0A00000,0xC0800000,0xC0400000,0xC0000000,0xBF800000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
static const float *FLOAT_TABLE = (const float *)FLOAT_TABLE_INT;
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;
int i, sample_count, shift;
int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02;
int xa_frame_size = 0x0f;
int frame_samples = 28;
first_sample = first_sample % frame_samples;
/* header */
frame_info = read_8bit(stream->offset,stream->streamfile);
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */
stream->adpcm_history1_double = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile);
stream->adpcm_history2_double = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile);
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
outbuf[sample_count] = read_16bitBE(stream->offset + 0x01 + 2*0x02 + i*0x02,stream->streamfile);
}
/* only increment offset on complete frame */
if (i == frame_samples)
stream->offset += pcm_frame_size;
}
else { /* ADPCM frame */
double coef1, coef2, hist1, hist2, new_sample;
coef1 = XA_K0[(frame_info >> 4)];
coef2 = XA_K1[(frame_info >> 4)];
shift = (frame_info & 0x0F) + 8;// << 4;
hist1 = stream->adpcm_history1_double;
hist2 = stream->adpcm_history2_double;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte, sample_nibble;
off_t byte_offset = (stream->offset + 0x01 + i/2);
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
new_sample = (double)FLOAT_TABLE[sample_nibble + shift];
new_sample = new_sample + coef1 * hist1 + coef2 * hist2;
outbuf[sample_count] = clamp16((int)new_sample);
hist2 = hist1;
hist1 = new_sample;
}
stream->adpcm_history1_double = hist1;
stream->adpcm_history2_double = hist2;
/* only increment offset on complete frame */
if (i == frame_samples)
stream->offset += xa_frame_size;
}
}
#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) {
uint8_t frame_info;

View file

@ -9,8 +9,9 @@ static const int EA_XA_TABLE[20] = {
0, -1, -3, -4
};
/* EA-XAS, evolution of EA-XA and cousin of MTA2. Layout: blocks of 0x4c per channel (128 samples),
* divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?).
/* EA-XAS, evolution of EA-XA and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding)
*
* Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?).
* To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */
void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int group, row, i;

View file

@ -29,35 +29,32 @@ static void g_init_ffmpeg() {
}
/* converts codec's samples (can be in any format, ex. Ogg's float32) to PCM16 */
static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount, int bitsPerSample, int floatingPoint) {
static void convert_audio_pcm16(sample *outbuf, const uint8_t *inbuf, int fullSampleCount, int bitsPerSample, int floatingPoint) {
int s;
switch (bitsPerSample) {
case 8:
{
for (s = 0; s < sampleCount; ++s) {
case 8: {
for (s = 0; s < fullSampleCount; s++) {
*outbuf++ = ((int)(*(inbuf++))-0x80) << 8;
}
}
break;
case 16:
{
}
case 16: {
int16_t *s16 = (int16_t *)inbuf;
for (s = 0; s < sampleCount; ++s) {
for (s = 0; s < fullSampleCount; s++) {
*outbuf++ = *(s16++);
}
}
break;
case 32:
{
}
case 32: {
if (!floatingPoint) {
int32_t *s32 = (int32_t *)inbuf;
for (s = 0; s < sampleCount; ++s) {
for (s = 0; s < fullSampleCount; s++) {
*outbuf++ = (*(s32++)) >> 16;
}
}
else {
float *s32 = (float *)inbuf;
for (s = 0; s < sampleCount; ++s) {
for (s = 0; s < fullSampleCount; s++) {
float sample = *s32++;
int s16 = (int)(sample * 32768.0f);
if ((unsigned)(s16 + 0x8000) & 0xFFFF0000) {
@ -66,13 +63,12 @@ static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount,
*outbuf++ = s16;
}
}
}
break;
case 64:
{
}
case 64: {
if (floatingPoint) {
double *s64 = (double *)inbuf;
for (s = 0; s < sampleCount; ++s) {
for (s = 0; s < fullSampleCount; s++) {
double sample = *s64++;
int s16 = (int)(sample * 32768.0f);
if ((unsigned)(s16 + 0x8000) & 0xFFFF0000) {
@ -81,10 +77,10 @@ static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount,
*outbuf++ = s16;
}
}
}
break;
}
}
}
/**
* Special patching for FFmpeg's buggy seek code.
@ -97,30 +93,31 @@ static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount,
* Fortunately seek_frame_generic can use an index to find the correct position. This function reads the
* first frame/packet and sets up index to timestamp 0. This ensures faulty demuxers will seek to 0 correctly.
* Some formats may not seek to 0 even with this, though.
*
* todo: some formats don't work with the current index values
*/
static int init_seek(ffmpeg_codec_data * data) {
int ret, ts_index, found_first = 0;
int64_t ts = 0;
int64_t pos = 0; /* offset */
int size = 0; /* coded size */
int distance = 0; /* always? */
int64_t ts = 0; /* seek timestamp */
int64_t pos = 0; /* data offset */
int size = 0; /* data size (block align) */
int distance = 0; /* always 0 ("duration") */
AVStream * stream;
AVPacket * pkt;
AVStream * stream = data->formatCtx->streams[data->streamIndex];
AVPacket * pkt = data->lastReadPacket;
stream = data->formatCtx->streams[data->streamIndex];
pkt = data->lastReadPacket;
/* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */
/* if (data->formatCtx->iformat->read_seek || data->formatCtx->iformat->read_seek2)
return 0; */
/* some formats already have a proper index (e.g. M4A) */
ts_index = av_index_search_timestamp(stream, ts, AVSEEK_FLAG_ANY);
if (ts_index>=0)
/* A few formats may have a proper index (e.g. CAF/MP4/MPC/ASF/WAV/XWMA/FLAC/MP3), but some don't
* work with our custom index (CAF/MPC/MP4) and must skip it. Most formats need flag AVSEEK_FLAG_ANY,
* while XWMA (with index 0 not pointing to ts 0) needs AVSEEK_FLAG_BACKWARD to seek properly, but it
* makes OGG use the index and seek wrong instead. So for XWMA we forcefully remove the index on it's own meta. */
ts_index = av_index_search_timestamp(stream, 0, /*AVSEEK_FLAG_BACKWARD |*/ AVSEEK_FLAG_ANY);
if (ts_index >= 0) {
VGM_LOG("FFMPEG: index found for init_seek\n");
goto test_seek;
}
/* find the first + second packets to get pos/size */
@ -132,7 +129,7 @@ static int init_seek(ffmpeg_codec_data * data) {
if (pkt->stream_index != data->streamIndex)
continue; /* ignore non-selected streams */
if (!found_first) { /* first found */
if (!found_first) {
found_first = 1;
pos = pkt->pos;
ts = pkt->dts;
@ -144,28 +141,28 @@ static int init_seek(ffmpeg_codec_data * data) {
}
if (!found_first)
goto fail;
/* in rare cases there is only one packet */
/* if (size == 0) { size = data_end - pos; } */ /* no easy way to know, ignore (most formats don's need size) */
//if (size == 0) size = data_end - pos; /* no easy way to know, ignore (most formats don's need size) */
/* some formats (XMA1) don't seem to have packet.dts, pretend it's 0 */
/* some formats don't seem to have packet.dts, pretend it's 0 */
if (ts == INT64_MIN)
ts = 0;
/* Some streams start with negative DTS (observed in Ogg). For Ogg seeking to negative or 0 doesn't alter the output.
* It does seem seeking before decoding alters a bunch of (inaudible) +-1 lower bytes though. */
/* Some streams start with negative DTS (OGG/OPUS). For Ogg seeking to negative or 0 doesn't seem different.
* It does seem seeking before decoding alters a bunch of (inaudible) +-1 lower bytes though.
* Output looks correct (encoder delay, num_samples, etc) compared to libvorbis's output. */
VGM_ASSERT(ts != 0, "FFMPEG: negative start_ts (%li)\n", (long)ts);
if (ts != 0)
ts = 0;
/* add index 0 */
ret = av_add_index_entry(stream, pos, ts, size, distance, AVINDEX_KEYFRAME);
if ( ret < 0 )
return ret;
test_seek:
/* seek to 0 test / move back to beginning, since we just consumed packets */
/* seek to 0 test + move back to beginning, since we just consumed packets */
ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY);
if ( ret < 0 )
return ret; /* we can't even reset_vgmstream the file */
@ -275,6 +272,10 @@ ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, u
return init_ffmpeg_header_offset(streamFile, NULL,0, start,size);
}
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
return init_ffmpeg_header_offset_subsong(streamFile, header, header_size, start, size, 0);
}
/**
* Manually init FFmpeg, from a fake header / offset.
*
@ -282,13 +283,12 @@ ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, u
* This header will be seamlessly inserted before 'start' offset, and total filesize will be 'header_size' + 'size'.
* The header buffer will be copied and memory-managed internally.
* NULL header can used given if the stream has internal data recognized by FFmpeg at offset.
* Stream index can be passed to FFmpeg in the streamFile, if the format has multiple streams (1=first).
* Stream index can be passed if the file has multiple audio streams that FFmpeg can demux (1=first).
*/
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong) {
char filename[PATH_LIMIT];
ffmpeg_codec_data * data;
int errcode, i;
int targetSubsong = streamFile->stream_index;
int streamIndex, streamCount;
AVStream *stream;
@ -355,7 +355,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t *
streamCount++;
/* select Nth audio stream if specified, or first one */
if (streamIndex < 0 || (targetSubsong > 0 && streamCount == targetSubsong)) {
if (streamIndex < 0 || (target_subsong > 0 && streamCount == target_subsong)) {
codecPar = stream->codecpar;
streamIndex = i;
}
@ -364,7 +364,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t *
if (i != streamIndex)
stream->discard = AVDISCARD_ALL; /* disable demuxing for other streams */
}
if (streamCount < targetSubsong) goto fail;
if (streamCount < target_subsong) goto fail;
if (streamIndex < 0 || !codecPar) goto fail;
data->streamIndex = streamIndex;
@ -459,7 +459,25 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t *
/* setup decent seeking for faulty formats */
errcode = init_seek(data);
if (errcode < 0) goto fail;
if (errcode < 0) {
VGM_LOG("FFMPEG: can't init_seek\n");
goto fail;
}
/* check ways to skip encoder delay/padding, for debugging purposes (some may be old/unused/encoder only/etc) */
VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS
//VGM_ASSERT(data->codecCtx->internal->skip_samples > 0, ...); /* for codec use, not accessible */
VGM_ASSERT(stream->codecpar->initial_padding > 0, "FFMPEG: initial_padding %i\n", (int)stream->codecpar->initial_padding);//delay: OPUS
VGM_ASSERT(stream->codecpar->trailing_padding > 0, "FFMPEG: trailing_padding %i\n", (int)stream->codecpar->trailing_padding);
VGM_ASSERT(stream->codecpar->seek_preroll > 0, "FFMPEG: seek_preroll %i\n", (int)stream->codecpar->seek_preroll);//seek delay: OPUS
VGM_ASSERT(stream->skip_samples > 0, "FFMPEG: skip_samples %i\n", (int)stream->skip_samples); //delay: MP4
VGM_ASSERT(stream->start_skip_samples > 0, "FFMPEG: start_skip_samples %i\n", (int)stream->start_skip_samples); //delay: MP3
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
/* also negative timestamp for formats like OGG/OPUS */
/* not using it: BINK, FLAC, ATRAC3, XMA, MPC, WMA (may use internal skip samples) */
//todo: double check Opus behavior
/* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc)
* get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */
@ -478,152 +496,151 @@ fail:
/* decode samples of any kind of FFmpeg format */
void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
ffmpeg_codec_data *data = vgmstream->codec_data;
int samplesReadNow;
//todo use either channels / data->channels / codecCtx->channels
int bytesPerSample, bytesPerFrame, frameSize;
int bytesToRead, bytesRead;
AVFormatContext *formatCtx = data->formatCtx;
AVCodecContext *codecCtx = data->codecCtx;
AVPacket *packet = data->lastReadPacket;
AVFrame *frame = data->lastDecodedFrame;
int planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt);
uint8_t *targetBuf;
int readNextPacket = data->readNextPacket;
int endOfStream = data->endOfStream;
int endOfAudio = data->endOfAudio;
int bytesConsumedFromDecodedFrame = data->bytesConsumedFromDecodedFrame;
AVFormatContext *formatCtx;
AVCodecContext *codecCtx;
AVPacket *lastReadPacket;
AVFrame *lastDecodedFrame;
int bytesConsumedFromDecodedFrame;
int readNextPacket, endOfStream, endOfAudio;
int framesReadNow;
int bytesPerSample = data->bitsPerSample / 8;
int bytesRead, bytesToRead;
/* ignore decode attempts at EOF */
if (data->endOfStream || data->endOfAudio) {
/* ignore once file is done (but not at endOfStream as FFmpeg can still output samples until endOfAudio) */
if (/*endOfStream ||*/ endOfAudio) {
VGM_LOG("FFMPEG: decode after end of audio\n");
memset(outbuf, 0, samples_to_do * channels * sizeof(sample));
return;
}
bytesPerSample = data->bitsPerSample / 8;
bytesPerFrame = channels * bytesPerSample;
frameSize = data->channels * bytesPerSample;
bytesToRead = samples_to_do * frameSize;
bytesRead = 0;
bytesToRead = samples_to_do * (bytesPerSample * codecCtx->channels);
targetBuf = data->sampleBuffer;
memset(targetBuf, 0, bytesToRead);
formatCtx = data->formatCtx;
codecCtx = data->codecCtx;
lastReadPacket = data->lastReadPacket;
lastDecodedFrame = data->lastDecodedFrame;
bytesConsumedFromDecodedFrame = data->bytesConsumedFromDecodedFrame;
readNextPacket = data->readNextPacket;
endOfStream = data->endOfStream;
endOfAudio = data->endOfAudio;
/* keep reading and decoding packets until the requested number of samples (in bytes) */
/* keep reading and decoding packets until the requested number of samples (in bytes for FFmpeg calcs) */
while (bytesRead < bytesToRead) {
int planeSize, planar, dataSize, toConsume, errcode;
int dataSize, toConsume, errcode;
/* size of previous frame */
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, lastDecodedFrame->nb_samples, codecCtx->sample_fmt, 1);
/* get sample data size from current frame (dataSize will be < 0 when nb_samples = 0) */
dataSize = av_samples_get_buffer_size(NULL, codecCtx->channels, frame->nb_samples, codecCtx->sample_fmt, 1);
if (dataSize < 0)
dataSize = 0;
/* read new frame + packets when requested */
/* read new data packet when requested */
while (readNextPacket && !endOfAudio) {
if (!endOfStream) {
av_packet_unref(lastReadPacket);
if ((errcode = av_read_frame(formatCtx, lastReadPacket)) < 0) {
/* reset old packet */
av_packet_unref(packet);
/* get compressed data from demuxer into packet */
errcode = av_read_frame(formatCtx, packet);
if (errcode < 0) {
if (errcode == AVERROR_EOF) {
endOfStream = 1;
endOfStream = 1; /* no more data, but may still output samples */
}
if (formatCtx->pb && formatCtx->pb->error)
else {
VGM_LOG("FFMPEG: av_read_frame errcode %i\n", errcode);
}
if (formatCtx->pb && formatCtx->pb->error) {
break;
}
if (lastReadPacket->stream_index != data->streamIndex)
}
if (packet->stream_index != data->streamIndex)
continue; /* ignore non-selected streams */
}
/* send compressed packet to decoder (NULL at EOF to "drain") */
if ((errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : lastReadPacket)) < 0) {
/* send compressed data to decoder in packet (NULL at EOF to "drain") */
errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : packet);
if (errcode < 0) {
if (errcode != AVERROR(EAGAIN)) {
VGM_LOG("FFMPEG: avcodec_send_packet errcode %i\n", errcode);
goto end;
}
}
readNextPacket = 0;
readNextPacket = 0; /* got compressed data */
}
/* decode packets into frame (checking if we have bytes to consume from previous frame) */
/* decode packet into frame's sample data (if we don't have bytes to consume from previous frame) */
if (dataSize <= bytesConsumedFromDecodedFrame) {
if (endOfStream && endOfAudio)
if (endOfAudio) {
break;
}
bytesConsumedFromDecodedFrame = 0;
/* receive uncompressed data from decoder */
if ((errcode = avcodec_receive_frame(codecCtx, lastDecodedFrame)) < 0) {
/* receive uncompressed sample data from decoder in frame */
errcode = avcodec_receive_frame(codecCtx, frame);
if (errcode < 0) {
if (errcode == AVERROR_EOF) {
endOfAudio = 1;
endOfAudio = 1; /* no more samples, file is fully decoded */
break;
}
else if (errcode == AVERROR(EAGAIN)) {
readNextPacket = 1;
readNextPacket = 1; /* request more compressed data */
continue;
}
else {
VGM_LOG("FFMPEG: avcodec_receive_frame errcode %i\n", errcode);
goto end;
}
}
/* size of current frame */
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, lastDecodedFrame->nb_samples, codecCtx->sample_fmt, 1);
/* get sample data size of current frame */
dataSize = av_samples_get_buffer_size(NULL, codecCtx->channels, frame->nb_samples, codecCtx->sample_fmt, 1);
if (dataSize < 0)
dataSize = 0;
}
toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
/* discard decoded frame if needed (fully or partially) */
if (data->samplesToDiscard) {
int samplesDataSize = dataSize / bytesPerFrame;
int samplesDataSize = dataSize / (bytesPerSample * channels);
if (data->samplesToDiscard >= samplesDataSize) {
/* discard all of the frame's samples and continue to the next */
bytesConsumedFromDecodedFrame = dataSize;
data->samplesToDiscard -= samplesDataSize;
continue;
}
else {
/* discard part of the frame and copy the rest below */
int bytesToDiscard = data->samplesToDiscard * bytesPerFrame;
int bytesToDiscard = data->samplesToDiscard * (bytesPerSample * channels);
int dataSizeLeft = dataSize - bytesToDiscard;
bytesConsumedFromDecodedFrame += bytesToDiscard;
data->samplesToDiscard = 0;
if (toConsume > dataSizeLeft)
toConsume = dataSizeLeft; /* consume at most dataSize left */
toConsume = dataSizeLeft;
}
}
/* copy decoded frame to buffer (mux channels if needed) */
planar = av_sample_fmt_is_planar(codecCtx->sample_fmt);
if (!planar || channels == 1) {
memmove(targetBuf + bytesRead, (lastDecodedFrame->data[0] + bytesConsumedFromDecodedFrame), toConsume);
/* copy decoded sample data to buffer */
if (!planar || channels == 1) { /* 1 sample per channel, already mixed */
memmove(data->sampleBuffer + bytesRead, (frame->data[0] + bytesConsumedFromDecodedFrame), toConsume);
}
else {
uint8_t * out = (uint8_t *) targetBuf + bytesRead;
else { /* N samples per channel, mix to 1 sample per channel */
uint8_t * out = (uint8_t *) data->sampleBuffer + bytesRead;
int bytesConsumedPerPlane = bytesConsumedFromDecodedFrame / channels;
int toConsumePerPlane = toConsume / channels;
int s, ch;
for (s = 0; s < toConsumePerPlane; s += bytesPerSample) {
for (ch = 0; ch < channels; ++ch) {
memcpy(out, lastDecodedFrame->extended_data[ch] + bytesConsumedPerPlane + s, bytesPerSample);
memcpy(out, frame->extended_data[ch] + bytesConsumedPerPlane + s, bytesPerSample);
out += bytesPerSample;
}
}
@ -634,17 +651,23 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do,
bytesRead += toConsume;
}
end:
framesReadNow = bytesRead / frameSize;
/* convert native sample format into PCM16 outbuf */
samplesReadNow = bytesRead / (bytesPerSample * channels);
convert_audio_pcm16(outbuf, data->sampleBuffer, samplesReadNow * channels, data->bitsPerSample, data->floatingPoint);
/* Convert the audio */
convert_audio(outbuf, data->sampleBuffer, framesReadNow * channels, data->bitsPerSample, data->floatingPoint);
/* clean buffer when requested more samples than possible */
if (endOfAudio && samplesReadNow < samples_to_do) {
VGM_LOG("FFMPEG: decode after end of audio %i samples\n", (samples_to_do - samplesReadNow));
memset(outbuf + (samplesReadNow * channels), 0, (samples_to_do - samplesReadNow) * channels * sizeof(sample));
}
/* Output the state back to the structure */
data->bytesConsumedFromDecodedFrame = bytesConsumedFromDecodedFrame;
/* copy state back */
data->readNextPacket = readNextPacket;
data->endOfStream = endOfStream;
data->endOfAudio = endOfAudio;
data->bytesConsumedFromDecodedFrame = bytesConsumedFromDecodedFrame;
}
@ -682,7 +705,8 @@ void reset_ffmpeg(VGMSTREAM *vgmstream) {
void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
int64_t ts;
if (!data) return;
if (!data)
return;
/* Start from 0 and discard samples until loop_start (slower but not too noticeable).
* Due to various FFmpeg quirks seeking to a sample is erratic in many formats (would need extra steps). */

View file

@ -16,8 +16,9 @@
static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate);
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 get_xopus_packet_size(int packet, STREAMFILE * streamfile);
typedef enum { OPUS_SWITCH, OPUS_UE4 } opus_type_t;
typedef enum { OPUS_SWITCH, OPUS_UE4, OPUS_EA, OPUS_X } opus_type_t;
typedef struct {
/* config */
opus_type_t type;
@ -100,6 +101,14 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
data_size = (uint16_t)read_16bitLE(data->physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_EA:
data_size = (uint16_t)read_16bitBE(data->physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_X:
data_size = get_xopus_packet_size(data->sequence - 2, streamfile);
skip_size = 0;
break;
default:
return 0;
}
@ -110,7 +119,7 @@ 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 %"PRIx64"\n", (off64_t)data->physical_offset);
VGM_LOG("OPUS: buffer can't hold OggS at %x\n", (uint32_t)data->physical_offset);
data->page_size = 0;
break;
}
@ -158,12 +167,13 @@ 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) {
off_t physical_offset, max_physical_offset;
size_t logical_size = 0;
int packet = 0;
if (data->logical_size)
return data->logical_size;
if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) {
VGM_LOG("OPUS: wrong streamsize %"PRIx64" + %x vs %x\n", (off64_t)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));
return 0;
}
@ -184,13 +194,28 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
data_size = (uint16_t)read_16bitLE(physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_EA:
data_size = (uint16_t)read_16bitBE(physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_X:
data_size = get_xopus_packet_size(packet, streamfile);
skip_size = 0x00;
break;
default:
return 0;
}
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 */
}
oggs_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */
physical_offset += data_size + skip_size;
logical_size += oggs_size + data_size;
packet++;
}
/* logical size can be bigger though */
@ -382,6 +407,24 @@ fail:
static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) {
size_t header_size = 0x13;
int mapping_family = 0; /* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */
#if 0
int stream_count = 1; /* number of internal mono/stereo streams (N mono/stereo streams form M channels) */
int coupled_count = channels - 1; /* number of stereo streams (packet has which one is mono or stereo) */
/* special multichannel config */
if (channels > 2) {
header_size += 0x01+0x01+channels;
switch(type) {
case ...:
...
break;
default:
goto fail;
}
}
#endif
if (header_size > buf_size) {
VGM_LOG("OPUS: buffer can't hold header\n");
@ -395,7 +438,19 @@ static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int sk
put_16bitLE(buf+0x0A, skip);
put_32bitLE(buf+0x0c, sample_rate);
put_16bitLE(buf+0x10, 0); /* output gain */
put_8bit (buf+0x12, 0); /* channel mapping family */
put_8bit (buf+0x12, mapping_family);
#if 0
if (mapping_family > 0) {
int 0;
put_8bit(buf+0x13, stream_count);
put_8bit(buf+0x14, coupled_count);
for (i = 0; i < channels; i++) {
put_8bit(buf+0x15+i, i); /* channel mapping (may need to change per family) */
}
}
#endif
return header_size;
fail:
@ -452,17 +507,26 @@ fail:
return 0;
}
/************************** */
#ifdef VGM_USE_FFMPEG
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 custom_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile, opus_type_t type) {
/************************** */
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 (uint16_t)read_16bitLE(0x20 + packet*0x02, streamfile);
}
#ifdef VGM_USE_FFMPEG
static size_t custom_opus_get_samples(off_t offset, size_t data_size, STREAMFILE *streamFile, opus_type_t type) {
size_t num_samples = 0;
off_t end_offset = offset + data_size;
int packet = 0;
if (end_offset > get_streamfile_size(streamFile)) {
VGM_LOG("OPUS: wrong end offset found\n");
@ -483,6 +547,14 @@ static size_t custom_opus_get_samples(off_t offset, size_t data_size, int sample
data_size = (uint16_t)read_16bitLE(offset, streamFile);
skip_size = 0x02;
break;
case OPUS_EA:
data_size = (uint16_t)read_16bitBE(offset, streamFile);
skip_size = 0x02;
break;
case OPUS_X:
data_size = get_xopus_packet_size(packet, streamFile);
skip_size = 0x00;
break;
default:
return 0;
}
@ -491,19 +563,56 @@ static size_t custom_opus_get_samples(off_t offset, size_t data_size, int sample
num_samples += opus_get_packet_samples(buf, 0x04);
offset += skip_size + data_size;
packet++;
}
return num_samples;
}
size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile) {
return custom_opus_get_samples(offset, data_size, sample_rate, streamFile, OPUS_SWITCH);
}
size_t ue4_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile) {
return custom_opus_get_samples(offset, data_size, sample_rate, streamFile, OPUS_UE4);
size_t switch_opus_get_samples(off_t offset, size_t data_size, STREAMFILE *streamFile) {
return custom_opus_get_samples(offset, data_size, streamFile, OPUS_SWITCH);
}
static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile, opus_type_t type) {
uint8_t buf[4];
size_t skip_size;
switch(type) {
case OPUS_SWITCH:
skip_size = 0x08;
break;
case OPUS_UE4:
skip_size = 0x02;
break;
case OPUS_EA:
skip_size = 0x02;
break;
case OPUS_X:
skip_size = 0x00;
break;
default:
return 0;
}
/* encoder delay seems fixed to 1/8 of samples per frame, but may need more testing */
read_streamfile(buf, offset+skip_size, 0x04, streamFile); /* at least 0x02 */
return opus_get_packet_samples(buf, 0x04) / 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 ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_UE4);
}
size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_EA);
}
/* ******************************************************* */
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) {
ffmpeg_codec_data * ffmpeg_data = NULL;
STREAMFILE *temp_streamFile = NULL;
@ -514,9 +623,12 @@ static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t
ffmpeg_data = init_ffmpeg_offset(temp_streamFile, 0x00,get_streamfile_size(temp_streamFile));
if (!ffmpeg_data) goto fail;
if (ffmpeg_data->skipSamples <= 0) {
ffmpeg_set_skip_samples(ffmpeg_data, skip);
}
/* FFmpeg + libopus: skips samples, notifies skip in codecCtx->delay (not in stream->skip_samples)
* FFmpeg + opus: *doesn't* skip, also notifies skip in codecCtx->delay, hurray (possibly fixed in recent versions)
* FFmpeg + opus is audibly buggy with some low bitrate SSB Ultimate files too */
//if (ffmpeg_data->skipSamples <= 0) {
// ffmpeg_set_skip_samples(ffmpeg_data, skip);
//}
close_streamfile(temp_streamFile);
return ffmpeg_data;
@ -529,9 +641,14 @@ fail:
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, OPUS_UE4);
}
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);
}
#endif

View file

@ -2,15 +2,14 @@
#ifdef VGM_USE_G719
#include <g719/g719.h>
#define G719_MAX_FRAME_SIZE 0x1000 /* arbitrary max (samples per frame seems to always be 960) */
#define G719_MAX_CODES ((1280/8)) /* in int16, so max frame size is (value/8)*2 (0xF0=common, 0x140=decoder max 2560b, rare) */
g719_codec_data *init_g719(int channel_count, int frame_size) {
int i;
g719_codec_data *data = NULL;
if (frame_size / sizeof(int16_t) > G719_MAX_FRAME_SIZE)
if (frame_size / sizeof(int16_t) > G719_MAX_CODES)
goto fail;
data = calloc(channel_count, sizeof(g719_codec_data)); /* one decoder per channel */
@ -19,10 +18,6 @@ g719_codec_data *init_g719(int channel_count, int frame_size) {
for (i = 0; i < channel_count; i++) {
data[i].handle = g719_init(frame_size); /* Siren 22 == 22khz bandwidth */
if (!data[i].handle) goto fail;
/* known values: 0xF0=common (sizeof(int16) * 960/8), 0x140=rare (sizeof(int16) * 1280/8) */
data[i].code_buffer = malloc(sizeof(int16_t) * frame_size);
if (!data[i].code_buffer) goto fail;
}
return data;
@ -46,8 +41,10 @@ void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int
int i;
if (0 == vgmstream->samples_into_block) {
read_streamfile((uint8_t*)ch_data->code_buffer, ch->offset, vgmstream->interleave_block_size, ch->streamfile);
g719_decode_frame(ch_data->handle, ch_data->code_buffer, ch_data->buffer);
int16_t code_buffer[G719_MAX_CODES];
read_streamfile((uint8_t*)code_buffer, ch->offset, vgmstream->interleave_block_size, ch->streamfile);
g719_decode_frame(ch_data->handle, code_buffer, ch_data->buffer);
}
for (i = 0; i < samples_to_do; i++) {
@ -71,7 +68,6 @@ void free_g719(g719_codec_data * data, int channels) {
for (i = 0; i < channels; i++) {
g719_free(data[i].handle);
free(data[i].code_buffer);
}
free(data);
}

View file

@ -99,14 +99,14 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) {
/* read frame */
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
if (bytes != blockSize) {
VGM_LOG("HCA: read %x vs expected %x bytes at %"PRIx64"\n", bytes, blockSize, (off64_t)offset);
VGM_LOG("HCA: read %x vs expected %x bytes at %x\n", bytes, blockSize, (uint32_t)offset);
break;
}
/* decode frame */
status = clHCA_DecodeBlock(data->handle, (void*)(data->data_buffer), blockSize);
if (status < 0) {
VGM_LOG("HCA: decode fail at %"PRIx64", code=%i\n", (off64_t)offset, status);
VGM_LOG("HCA: decode fail at %x, code=%i\n", (uint32_t)offset, status);
break;
}
@ -123,6 +123,7 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) {
void reset_hca(hca_codec_data * data) {
if (!data) return;
clHCA_DecodeReset(data->handle);
data->current_block = 0;
data->samples_filled = 0;
data->samples_consumed = 0;
@ -150,59 +151,83 @@ void free_hca(hca_codec_data * data) {
}
#define HCA_KEY_MAX_BLANK_FRAMES 15 /* ignored up to N blank frames (not uncommon to have ~10, if more something is off) */
#define HCA_KEY_MAX_TEST_FRAMES 10 /* 5~15 should be enough, but mostly silent or badly mastered files may need more */
#define HCA_KEY_MAX_ACCEPTABLE_SCORE 300 /* unlikely to work correctly, 10~30 may be ok */
/* arbitrary scale to simplify score comparisons */
#define HCA_KEY_SCORE_SCALE 10
/* ignores beginning frames (~10 is not uncommon, Dragalia Lost vocal layers have lots) */
#define HCA_KEY_MAX_SKIP_BLANKS 1200
/* 5~15 should be enough, but almost silent or badly mastered files may need tweaks */
#define HCA_KEY_MIN_TEST_FRAMES 5
#define HCA_KEY_MAX_TEST_FRAMES 10
/* score of 10~30 isn't uncommon in a single frame, too many frames over that is unlikely */
#define HCA_KEY_MAX_FRAME_SCORE 150
#define HCA_KEY_MAX_TOTAL_SCORE (HCA_KEY_MAX_TEST_FRAMES * 50*HCA_KEY_SCORE_SCALE)
/* Test a number of frames if key decrypts correctly.
* Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better) */
* Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better). */
int test_hca_key(hca_codec_data * data, unsigned long long keycode) {
size_t test_frame = 0, current_frame = 0, blank_frames = 0;
int total_score = 0;
size_t test_frames = 0, current_frame = 0, blank_frames = 0;
int total_score = 0, found_regular_frame = 0;
const unsigned int blockSize = data->info.blockSize;
/* Due to the potentially large number of keys this must be tuned for speed.
* Buffered IO seems fast enough (not very different reading a large block once vs frame by frame).
* clHCA_TestBlock could be optimized a bit more. */
clHCA_SetKey(data->handle, keycode);
while (test_frame < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) {
/* Test up to N non-blank frames or until total frames. */
/* A final score of 0 (=silent) is only possible for short files with all blank frames */
while (test_frames < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) {
off_t offset = data->info.headerSize + current_frame * blockSize;
int score;
size_t bytes;
/* read frame */
/* read and test frame */
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
if (bytes != blockSize) {
total_score = -1;
break;
}
/* test frame */
score = clHCA_TestBlock(data->handle, (void*)(data->data_buffer), blockSize);
if (score < 0) {
if (score < 0 || score > HCA_KEY_MAX_FRAME_SCORE) {
total_score = -1;
break;
}
current_frame++;
/* skip blank block at the beginning */
if (score == 0 && blank_frames < HCA_KEY_MAX_BLANK_FRAMES) {
/* ignore silent frames at the beginning, up to a point */
if (score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS && !found_regular_frame) {
blank_frames++;
continue;
}
test_frame++;
total_score += score;
found_regular_frame = 1;
test_frames++;
/* too far, don't bother checking more frames */
if (total_score > HCA_KEY_MAX_ACCEPTABLE_SCORE)
break;
/* scale values to make scores of perfect frames more detectable */
switch(score) {
case 1: score = 1; break;
case 0: score = 3*HCA_KEY_SCORE_SCALE; break; /* blanks after non-blacks aren't very trustable */
default: score = score*HCA_KEY_SCORE_SCALE;
}
/* signal best possible score */
if (total_score > 0 && total_score <= HCA_KEY_MAX_TEST_FRAMES) {
total_score += score;
/* don't bother checking more frames, other keys will get better scores */
if (total_score > HCA_KEY_MAX_TOTAL_SCORE)
break;
}
//;VGM_LOG("HCA KEY: blanks=%i, tests=%i, score=%i\n", blank_frames, test_frames, total_score);
/* signal best possible score (many perfect frames and few blank frames) */
if (test_frames > HCA_KEY_MIN_TEST_FRAMES && total_score > 0 && total_score <= test_frames) {
total_score = 1;
}
clHCA_DecodeReset(data->handle);
return total_score;
}

View file

@ -207,6 +207,35 @@ static void alp_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
if (*step_index > 88) *step_index=88;
}
/* FFTA2 IMA, different hist and sample rounding, reverse engineered from the ROM */
static void ffta2_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) {
int sample_nibble, sample_decoded, step, delta;
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */
sample_decoded = *hist1; /* predictor value */
step = ADPCMTable[*step_index] * 0x100; /* current step (table in ROM is pre-multiplied though) */
delta = step >> 3;
if (sample_nibble & 1) delta += step >> 2;
if (sample_nibble & 2) delta += step >> 1;
if (sample_nibble & 4) delta += step;
if (sample_nibble & 8) delta = -delta;
sample_decoded += delta;
/* custom clamp16 */
if (sample_decoded > 0x7FFF00)
sample_decoded = 0x7FFF00;
else if (sample_decoded < -0x800000)
sample_decoded = -0x800000;
*hist1 = sample_decoded;
*out_sample = (short)((sample_decoded + 128) / 256); /* int16 sample rounding, hist is kept as int32 */
*step_index += IMA_IndexTable[sample_nibble];
if (*step_index < 0) *step_index=0;
if (*step_index > 88) *step_index=88;
}
/* ************************************ */
/* DVI/IMA */
/* ************************************ */
@ -351,6 +380,29 @@ void decode_alp_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
stream->adpcm_step_index = step_index;
}
/* FFTA2 IMA, DVI IMA with custom nibble expand/rounding */
void decode_ffta2_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
int16_t out_sample;
//external interleave
//no header
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
off_t byte_offset = stream->offset + i/2;
int nibble_shift = (i&1?0:4); //high nibble first
ffta2_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample);
outbuf[sample_count] = out_sample;
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* ************************************ */
/* MS-IMA */
/* ************************************ */

View file

@ -157,7 +157,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
}
VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding,
"MPEG FSB: %i streams with non-constant interleave found @ 0x%08"PRIx64"\n", data->streams_size, (off64_t)stream->offset);
"MPEG FSB: %i streams with non-constant interleave found @ 0x%08x\n", data->streams_size, (uint32_t)stream->offset);
break;
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */

View file

@ -59,7 +59,7 @@ static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, o
}
fail:
VGM_LOG("AWC: can't find repeat size, new=0x%08"PRIx64", last=0x%08"PRIx64"\n", (off64_t)new_offset, (off64_t)last_offset);
VGM_LOG("AWC: can't find repeat size, new=0x%08x, last=0x%08x\n", (uint32_t)new_offset, (uint32_t)last_offset);
return 0; /* keep on truckin' */
}

View file

@ -529,7 +529,7 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream* is_0, ealayer3_frame_info*
if (os->b_off/8 > expected_frame_size) {
/* bit reservoir! shouldn't happen with free bitrate, otherwise it's hard to fix as needs complex buffering/calcs */
VGM_LOG("MPEG EAL3: written 0x%"PRIx64" but expected less than 0x%x at 0x%"PRIx64"\n", (off64_t)(os->b_off/8), expected_frame_size, (off64_t)os->info_offset);
VGM_LOG("MPEG EAL3: written 0x%x but expected less than 0x%x at 0x%x\n", (uint32_t)(os->b_off/8), expected_frame_size, (uint32_t)os->info_offset);
}
else {
/* fill ancillary data (should be ignored, but 0x00 seems to improve mpg123's free bitrate detection) */
@ -564,8 +564,8 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
if (!eaf->pcm_size)
return 1;
VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%"PRIx64"\n", eaf->v1_pcm_decode_discard, (off64_t)stream->offset);
VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%"PRIx64"\n", eaf->v1_pcm_number, (off64_t)stream->offset);
VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%x\n", eaf->v1_pcm_decode_discard, (uint32_t)stream->offset);
VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%x\n", eaf->v1_pcm_number, (uint32_t)stream->offset);
/* read + write PCM block samples (always BE) */
for (i = 0; i < eaf->v1_pcm_number * data->channels_per_frame; i++) {

View file

@ -0,0 +1,175 @@
#include "mpeg_decoder.h"
#ifdef VGM_USE_MPEG
/* parsed info from a single EAMP3 frame */
typedef struct {
uint32_t extended_flag;
uint32_t stereo_flag; /* assumed */
uint32_t unknown_flag; /* unused? */
uint32_t frame_size; /* full size including headers and pcm block */
uint32_t pcm_number; /* samples in the PCM block (typically 1 MPEG frame, 1152) */
uint32_t pre_size; /* size of the header part */
uint32_t mpeg_size; /* size of the MPEG part */
uint32_t pcm_size; /* size of the PCM block */
} eamp3_frame_info;
static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, eamp3_frame_info * eaf);
static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf);
static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start);
/* init config and validate */
int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) {
mpeg_frame_info info;
uint16_t frame_header;
size_t header_size;
/* test unknown stuff */
frame_header = (uint16_t)read_16bitLE(start_offset+0x00, streamFile);
if (frame_header & 0x2000) {
VGM_LOG("EAMP3: found unknown bit 13\n");
goto fail;
}
if ((frame_header & 0x8000) && (uint32_t)read_32bitLE(start_offset+0x02, streamFile) > 0xFFFF) {
VGM_LOG("EAMP3: found big PCM block\n");
goto fail;
}
/* get frame info at offset */
header_size = (frame_header & 0x8000) ? 0x06 : 0x02;
if (!mpeg_get_frame_info(streamFile, start_offset+header_size, &info))
goto fail;
switch(info.layer) {
case 1: *coding_type = coding_MPEG_layer1; break;
case 2: *coding_type = coding_MPEG_layer2; break;
case 3: *coding_type = coding_MPEG_layer3; break;
default: goto fail;
}
data->channels_per_frame = info.channels;
data->samples_per_frame = info.frame_samples;
data->bitrate_per_frame = info.bit_rate;
data->sample_rate_per_frame = info.sample_rate;
return 1;
fail:
return 0;
}
/* reads custom frame header + MPEG data + (optional) PCM block */
int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
mpeg_custom_stream *ms = data->streams[num_stream];
eamp3_frame_info eaf;
int ok;
if (!eamp3_skip_data(stream, data, num_stream, 1))
goto fail;
ok = eamp3_parse_frame(stream, data, &eaf);
if (!ok) goto fail;
ok = eamp3_write_pcm_block(stream, data, num_stream, &eaf);
if (!ok) goto fail;
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset + eaf.pre_size, eaf.mpeg_size, stream->streamfile);
stream->offset += eaf.frame_size;
if (!eamp3_skip_data(stream, data, num_stream, 0))
goto fail;
return 1;
fail:
return 0;
}
static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, eamp3_frame_info * eaf) {
uint16_t current_header = (uint16_t)read_16bitLE(stream->offset+0x00, stream->streamfile);
eaf->extended_flag = (current_header & 0x8000);
eaf->stereo_flag = (current_header & 0x4000);
eaf->unknown_flag = (current_header & 0x2000);
eaf->frame_size = (current_header & 0x1FFF); /* full size including PCM block */
eaf->pcm_number = 0;
if (eaf->extended_flag > 0) {
eaf->pcm_number = (uint32_t)read_32bitLE(stream->offset+0x02, stream->streamfile);
eaf->pcm_size = sizeof(sample) * eaf->pcm_number * data->channels_per_frame;
eaf->pre_size = 0x06;
eaf->mpeg_size = eaf->frame_size - eaf->pre_size - eaf->pcm_size;
if (eaf->frame_size < eaf->pre_size + eaf->pcm_size) {
VGM_LOG("EAMP3: bad pcm size at %x\n", (uint32_t)stream->offset);
goto fail;
}
}
else {
eaf->pcm_size = 0;
eaf->pre_size = 0x02;
eaf->mpeg_size = eaf->frame_size - eaf->pre_size;
}
return 1;
fail:
return 0;
}
/* write PCM block directly to sample buffer and setup decode discard (see EALayer3). */
static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf) {
mpeg_custom_stream *ms = data->streams[num_stream];
size_t bytes_filled;
int i;
bytes_filled = sizeof(sample) * ms->samples_filled * data->channels_per_frame;
if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) {
VGM_LOG("EAMP3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
goto fail;
}
if (eaf->pcm_number) {
/* read + write PCM block samples (always LE) */
for (i = 0; i < eaf->pcm_number * data->channels_per_frame; i++) {
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->mpeg_size + sizeof(sample)*i;
int16_t pcm_sample = read_16bitLE(pcm_offset,stream->streamfile);
put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample);
}
ms->samples_filled += eaf->pcm_number;
/* modify decoded samples */
{
size_t decode_to_discard = eaf->pcm_number; //todo guessed
ms->decode_to_discard += decode_to_discard;
}
}
return 1;
fail:
return 0;
}
/* Skip EA-frames from other streams for .sns/sps multichannel (see EALayer3). */
static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) {
int ok, i;
eamp3_frame_info eaf;
int skips = at_start ? num_stream : data->streams_size - 1 - num_stream;
for (i = 0; i < skips; i++) {
ok = eamp3_parse_frame(stream, data, &eaf);
if (!ok) goto fail;
//;VGM_LOG("s%i: skipping %x, now at %lx\n", num_stream,eaf.frame_size,stream->offset);
stream->offset += eaf.frame_size;
}
//;VGM_LOG("s%i: skipped %i frames, now at %lx\n", num_stream,skips,stream->offset);
return 1;
fail:
return 0;
}
#endif

View file

@ -3,6 +3,7 @@
#include "../vgmstream.h"
#ifdef VGM_USE_MPEG
#include <mpg123/mpg123.h>
#include "mpeg_decoder.h"
@ -139,6 +140,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co
case MPEG_EAL32P:
case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
case MPEG_AWC: ok = mpeg_custom_setup_init_awc(streamFile, start_offset, data, coding_type); break;
case MPEG_EAMP3: ok = mpeg_custom_setup_init_eamp3(streamFile, start_offset, data, coding_type); break;
default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break;
}
if (!ok)
@ -398,10 +400,11 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break;
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data, num_stream); break;
case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break;
case MPEG_EAMP3: ok = mpeg_custom_parse_frame_eamp3(stream, data, num_stream); break;
default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break;
}
if (!ok) {
VGM_LOG("MPEG: cannot parse frame @ around %"PRIx64"\n",(off64_t)stream->offset);
VGM_LOG("MPEG: cannot parse frame @ around %x\n",(uint32_t)stream->offset);
goto decode_fail; /* mpg123 could resync but custom MPEGs wouldn't need that */
}
//;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", ms->bytes_in_buffer, stream->offset);
@ -500,24 +503,29 @@ void free_mpeg(mpeg_codec_data *data) {
* someone else in another thread is using it. */
}
/* seeks stream to 0 */
void reset_mpeg(VGMSTREAM *vgmstream) {
off_t input_offset;
mpeg_codec_data *data = vgmstream->codec_data;
if (!data) return;
/* reset multistream */ //todo check if stream offsets are properly reset
if (!data->custom) {
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
mpg123_feedseek(data->m,0,SEEK_SET,&input_offset);
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
}
else {
int i;
/* re-start from 0 */
for (i = 0; i < data->streams_size; i++) {
mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset);
data->streams[i]->bytes_in_buffer = 0;
data->streams[i]->buffer_full = 0;
data->streams[i]->buffer_used = 0;
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->current_size_count = 0;
data->streams[i]->current_size_target = 0;
data->streams[i]->decode_to_discard = 0;
}
@ -525,16 +533,17 @@ void reset_mpeg(VGMSTREAM *vgmstream) {
}
}
/* seeks to a point */
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
off_t input_offset;
mpeg_codec_data *data = vgmstream->codec_data;
if (!data) return;
/* seek multistream */
if (!data->custom) {
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
/* force first offset as discard-looping needs to start from the beginning */
/* adjust loop with mpg123's offset (useful?) */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
}
@ -543,11 +552,14 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
/* re-start from 0 */
for (i = 0; i < data->streams_size; i++) {
mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset);
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->decode_to_discard = 0;
data->streams[i]->bytes_in_buffer = 0;
data->streams[i]->buffer_full = 0;
data->streams[i]->buffer_used = 0;
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->current_size_count = 0;
data->streams[i]->current_size_target = 0;
data->streams[i]->decode_to_discard = 0;
/* force first offset as discard-looping needs to start from the beginning */
if (vgmstream->loop_ch)
@ -559,33 +571,36 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
data->samples_to_discard += data->skip_samples;
}
data->bytes_in_buffer = 0;
data->buffer_full = 0;
data->buffer_used = 0;
}
/* resets mpg123 decoder and its internals (with mpg123_open_feed as mpg123_feedseek won't work) */
/* resets mpg123 decoder and its internals without seeking, useful when a new MPEG substream starts */
void flush_mpeg(mpeg_codec_data * data) {
if (!data)
return;
if (!data->custom) {
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
mpg123_open_feed(data->m);
mpg123_open_feed(data->m); /* mpg123_feedseek won't work */
}
else {
int i;
/* re-start from 0 */
for (i=0; i < data->streams_size; i++) {
mpg123_open_feed(data->streams[i]->m);
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->decode_to_discard = 0;
data->streams[i]->bytes_in_buffer = 0;
data->streams[i]->buffer_full = 0;
data->streams[i]->buffer_used = 0;
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->current_size_count = 0;
data->streams[i]->current_size_target = 0;
data->streams[i]->decode_to_discard = 0;
}
data->samples_to_discard = data->skip_samples; /* initial delay */
data->samples_to_discard = data->skip_samples;
}
data->bytes_in_buffer = 0;

View file

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

View file

@ -79,7 +79,7 @@ static void mta2_block_update(VGMSTREAMCHANNEL * stream) {
}
if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */
VGM_LOG("MTA2: bad block @ 0x%"PRIx64"\n", (off64_t)stream->offset);
VGM_LOG("MTA2: bad block @ 0x%x\n", (uint32_t)stream->offset);
stream->offset += 0x10;
repeat = 0;
}
@ -113,20 +113,28 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */
/* 0x08(8): null */
/* EOF: 0-fill buffer (or, as track_channels = 0 > divs by 0) */
if (num_track < 0) {
VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", (uint32_t)stream->offset);
/* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others)
* negative track only happens for truncated files (EOF) */
if (frame_size == 0 || num_track < 0) {
for (i = 0; i < samples_to_do; i++)
outbuf[i * channelspacing] = 0;
stream->offset += 0x10;
return;
}
track_channels = 0;
for (i = 0; i < 8; i++) {
if ((channel_layout >> i) & 0x01)
track_channels++;
}
if (track_channels == 0) { /* bad data, avoid div by 0 */
VGM_LOG("track_channels 0 at %x\n", (uint32_t)stream->offset);
return;
}
/* assumes tracks channels are divided evenly in all tracks (ex. not 2ch + 1ch + 1ch) */
if (channel / track_channels == num_track)
break; /* channel belongs to this track */

View file

@ -109,7 +109,7 @@ static void mtaf_block_update(VGMSTREAMCHANNEL * stream) {
}
if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */
VGM_LOG("MTAF: bad block @ %"PRIx64"\n", (off64_t)stream->offset);
VGM_LOG("MTAF: bad block @ %x\n", (uint32_t)stream->offset);
stream->offset += 0x10;
repeat = 0;
}
@ -146,7 +146,7 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
int32_t init_idx = read_16bitLE(stream->offset+4+0+c*2, stream->streamfile); /* step-L/R */
int32_t init_hist = read_16bitLE(stream->offset+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */
VGM_ASSERT(init_idx < 0 || init_idx > 31, "MTAF: bad header idx @ 0x%"PRIx64"\n", (off64_t)stream->offset);
VGM_ASSERT(init_idx < 0 || init_idx > 31, "MTAF: bad header idx @ 0x%x\n", (uint32_t)stream->offset);
/* avoid index out of range in corrupt files */
if (init_idx < 0) {
init_idx = 0;

View file

@ -23,7 +23,7 @@ void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
coef_index = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 4) & 0xf;
shift_factor = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 0) & 0xf;
/* rare but happens, also repeated headers don't match (ex. Ikaruga (GC) SONG02.adp) */
VGM_ASSERT_ONCE(coef_index > 4 || shift_factor > 12, "DTK: incorrect coefs/shift at %"PRIx64"\n", (off64_t)frame_offset);
VGM_ASSERT_ONCE(coef_index > 4 || shift_factor > 12, "DTK: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
/* decode nibbles */
for (i = first_sample; i < first_sample+samples_to_do; i++) {

View file

@ -0,0 +1,92 @@
#include "coding.h"
static const int step_sizes[49] = { /* OKI table */
16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50,
55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157,
173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
};
static const int stex_indexes[16] = { /* IMA table */
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};
static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample, int mode) {
int code, step, delta;
code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf;
step = step_sizes[*step_index];
delta = (code & 0x7);
if (mode & 1) {
if (step == 1552) /* bad last step_sizes value from OKI table */
step = 1522;
delta = step * (delta + 1) * 2;
}
else {
delta = step * (delta + 1);
}
if (code & 0x8)
delta = -delta;
*step_index += stex_indexes[code];
if (*step_index < 0) *step_index = 0;
if (*step_index > 48) *step_index = 48;
*hist1 += delta;
if (*hist1 > 16383) *hist1 = 16383;
if (*hist1 < -16384) *hist1 = -16384;
if (mode & 1) {
*out_sample = *hist1;
} else {
*out_sample = *hist1 << 1;
}
/* seems real HW does filtering here too */
/* double volume since it clips at half */
if (mode & 2) {
*out_sample = *hist1 << 1;
}
}
/* PC-FX ADPCM decoding, variation of OKI/Dialogic/VOX ADPCM. Based on mednafen/pcfx-music-dump.
* Apparently most ADPCM was made with a buggy encoder, resulting in incorrect sound in real hardware
* and sound clipped at half. Decoding can be controlled with modes:
* - 0: hardware decoding (waveforms in many games will look wrong, ex. Der Langrisser track 032)
* - 1: 'buggy encoder' decoding (waveforms will look fine)
* - 2: hardware decoding with double volume (may clip?)
* - 3: 'buggy encoder' decoding with double volume
*
* PC-FX ISOs don't have a standard filesystem nor file formats (raw data must be custom-ripped),
* so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where
* base_value is approximately ~31468.5 (follows hardware clocks), mono or stereo-interleaved.
*/
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
int16_t out_sample;
for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = stream->offset + i/2;
int nibble_shift = (i&1?4:0); /* low nibble first */
pcfx_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample, mode);
outbuf[sample_count] = out_sample;
sample_count += channelspacing;
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
size_t pcfx_bytes_to_samples(size_t bytes, int channels) {
/* 2 samples per byte (2 nibbles) in stereo or mono config */
return bytes * 2 / channels;
}

View file

@ -63,7 +63,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf;
flag = (uint8_t)read_8bit(frame_offset+0x01,stream->streamfile); /* only lower nibble needed */
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %"PRIx64"\n", (off64_t)frame_offset);
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
if (coef_index > 5) /* needed by inFamous (PS3) (maybe it's supposed to use more filters?) */
coef_index = 0; /* upper filters aren't used in PS1/PS2, maybe in PSP/PS3? */
if (shift_factor > 12)
@ -71,7 +71,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
if (is_badflags) /* some games store garbage or extra internal logic in the flags, must be ignored */
flag = 0;
VGM_ASSERT_ONCE(flag > 7,"PS-ADPCM: unknown flag at %"PRIx64"\n", (off64_t)frame_offset); /* meta should use PSX-badflags */
VGM_ASSERT_ONCE(flag > 7,"PS-ADPCM: unknown flag at %x\n", (uint32_t)frame_offset); /* meta should use PSX-badflags */
/* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
@ -123,7 +123,7 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int cha
coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf;
shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf;
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %"PRIx64"\n", (off64_t)frame_offset);
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
if (coef_index > 5) /* needed by Afrika (PS3) (maybe it's supposed to use more filters?) */
coef_index = 0; /* upper filters aren't used in PS1/PS2, maybe in PSP/PS3? */
if (shift_factor > 12)
@ -180,7 +180,7 @@ static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_off
uint8_t flag = (uint8_t)read_8bit(offset+0x01,streamFile) & 0x0F; /* lower nibble only (for HEVAG) */
/* theoretically possible and would use last 0x06 */
VGM_ASSERT_ONCE(loop_start_found && flag == 0x06, "PS LOOPS: multiple loop start found at %"PRIx64"\n", (off64_t)offset);
VGM_ASSERT_ONCE(loop_start_found && flag == 0x06, "PS LOOPS: multiple loop start found at %x\n", (uint32_t)offset);
if (flag == 0x06 && !loop_start_found) {
loop_start = num_samples; /* loop start before this frame */

View file

@ -69,7 +69,7 @@ vorbis_custom_codec_data * init_vorbis_custom(STREAMFILE *streamFile, off_t star
return data;
fail:
VGM_LOG("VORBIS: init fail at around 0x%"PRIx64"\n", (off64_t)start_offset);
VGM_LOG("VORBIS: init fail at around 0x%x\n", (uint32_t)start_offset);
free_vorbis_custom(data);
return NULL;
}
@ -144,7 +144,7 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
/* parse the fake ogg packet into a logical vorbis block */
rc = vorbis_synthesis(&data->vb,&data->op);
if (rc == OV_ENOTAUDIO) {
VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %"PRIx64"\n",(size_t)data->op.bytes,(off64_t)stream->offset);
VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %x\n",(size_t)data->op.bytes,(uint32_t)stream->offset);
//VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0);
continue; /* rarely happens, seems ok? */
} else if (rc != 0) goto decode_fail;
@ -162,7 +162,7 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("VORBIS: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done));
VGM_LOG("VORBIS: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample));
}

View file

@ -76,7 +76,7 @@ void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, i
if (read_32bitBE(frame_offset+0x00,stream->streamfile) != read_32bitBE(frame_offset+0x04,stream->streamfile) ||
read_32bitBE(frame_offset+0x08,stream->streamfile) != read_32bitBE(frame_offset+0x0c,stream->streamfile)) {
VGM_LOG("bad frames at %"PRIx64"\n", (off64_t)frame_offset);
VGM_LOG("bad frames at %x\n", (uint32_t)frame_offset);
}
@ -90,7 +90,7 @@ void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, i
coef_index = ((uint8_t)read_8bit(sp_offset,stream->streamfile) >> 4) & 0xf;
shift_factor = ((uint8_t)read_8bit(sp_offset,stream->streamfile) >> 0) & 0xf;
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %"PRIx64"\n", (off64_t)sp_offset);
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)sp_offset);
if (coef_index > 4)
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
if (shift_factor > 12)

View file

@ -1,11 +1,13 @@
#include "vgmstream.h"
/* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
/* Defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
* to inform plugins that need it. Common extensions are commented out to avoid stealing them. */
/* some extensions require external libraries and could be #ifdef, not really needed */
/* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */
/* Some extensions require external libraries and could be #ifdef, not worth. */
/* Formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension isn't
* parsed by vgmstream and typically won't not be fully accurate. May have a .ext.pos pair for fun. */
static const char* extension_list[] = {
@ -16,19 +18,22 @@ static const char* extension_list[] = {
"2pfs",
"800",
//"aac", //common, also tri-Ace's
"aa3", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
//"aac", //common
"aa3", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
"aaap",
"aax",
"abk",
//"ac3", //FFmpeg, not parsed //common?
"ace", //fake, for tri-Ace's formats (to be removed)
//"ac3", //common, FFmpeg/not parsed (AC3)
"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)]
"adm",
"adp",
"adpcm",
"adpcmx",
"ads",
"adw",
"adx",
"afc",
"agsc",
@ -37,13 +42,13 @@ static const char* extension_list[] = {
"ai",
//"aif", //common
"aifc", //common?
"aifcl", //fake extension, for AIF???
"aifcl", //fake extension for .aif???
//"aiff", //common
"aiffl", //fake extension, for AIF???
"aiffl", //fake extension for .aif???
"aix",
"akb",
"al2",
"amts", //fake extension/header id for .stm (to be removed)
"amts", //fake extension/header id for .stm (renamed? to be removed?)
"ao",
"apc",
"as4",
@ -72,7 +77,7 @@ static const char* extension_list[] = {
"bdsp",
"bfstm",
"bfwav",
"bfwavnsmbu",
"bfwavnsmbu", //fake extension for New Super Smash Bros U (renamed to fix bug)
"bg00",
"bgm",
"bgw",
@ -97,12 +102,13 @@ static const char* extension_list[] = {
"cbd2",
"ccc",
"cd",
"cfn", //fake extension/header id for .caf (to be removed)
"cfn", //fake extension for CAF (renamed, to be removed?)
"ckb",
"ckd",
"cks",
"cnk",
"cps",
"csa", //txth/reserved [LEGO Racers 2 (PS2)]
"csmp",
"cvs",
"cxs",
@ -123,8 +129,12 @@ static const char* extension_list[] = {
"e4x",
"eam",
"emff",
"eas",
"eda", //txth/reserved [Project Eden (PS2)]
"emff", //fake extension for .mul (to be removed)
"enm",
"eno",
"ens",
"enth",
"exa",
"ezw",
@ -132,6 +142,7 @@ static const char* extension_list[] = {
"fag",
"ffw",
"filp",
//"flac", //common
"flx",
"fsb",
"fsv",
@ -146,10 +157,12 @@ static const char* extension_list[] = {
"genh",
"gms",
"gsb",
//"gsf", //conflicts with GBA gsf plugins?
"gtd",
"gwm",
"h4m",
"hab",
"hca",
"hdr",
"hgc1",
@ -162,10 +175,11 @@ static const char* extension_list[] = {
"iab",
"iadp",
"idsp",
"idvi", //fake extension for .pcm (to be removed)
"idvi", //fake extension/header id for .pcm (renamed, to be removed)
"idx",
"ikm",
"ild",
"imc",
"int",
"isd",
"isws",
@ -179,31 +193,46 @@ static const char* extension_list[] = {
"jstm",
"kces",
"kcey", //fake extension/header id (to be removed)
"kcey", //fake extension/header id for .pcm (renamed, to be removed)
"khv",
"km9",
"kovs", //.kvs header id
"kovs", //fake extension/header id for .kvs
"kns",
"kraw",
"ktss", //.kns header id
"ktss", //fake extension/header id for .kns
"kvs",
"l",
"laac", //fake extension, for AAC (tri-Ace/FFmpeg)
"lac3", //fake extension, for AC3
"laac", //fake extension for .aac (tri-Ace)
"lac3", //fake extension for .ac3, FFmpeg/not parsed
"leg",
"lflac", //fake extension, FFmpeg, not parsed, use with .pos pair for fun
"lmp4", //fake extension, for MP4s
"logg", //fake extension, for OGGs
"lopus", //fake extension, for OPUS
"lflac", //fake extension for .flac, FFmpeg/not parsed
"lin",
"lm0",
"lm1",
"lm2",
"lm3",
"lm4",
"lm5",
"lm6",
"lm7",
"lmp2", //fake extension for .mp2, FFmpeg/not parsed
"lmp3", //fake extension for .mp3, FFmpeg/not parsed
"lmp4", //fake extension for .mp4
"lmpc", //fake extension for .mpc, FFmpeg/not parsed
"logg", //fake extension for .ogg
"lopus", //fake extension for .opus
"lpcm",
"lpk",
"lps",
"lse",
"lsf",
"lstm", //fake extension, for STMs
"lwav", //fake extension, for WAVs
"lstm", //fake extension for .stm
"lwav", //fake extension for .wav
"lwma", //fake extension for .wma, FFmpeg/not parsed
"mab",
"map",
"matx",
"mc3",
"mca",
@ -218,12 +247,14 @@ static const char* extension_list[] = {
"mihb",
"mnstr",
"mogg",
//"mp2", //common
//"mp3", //common
//"mp4", //common
//"mpc", //FFmpeg, not parsed (musepack) //common
//"mpc", //common
"mpdsp",
"mpds",
"mps", //txh/reserved [Scandal (PS2)]
"mpf",
"mps", //txth/reserved [Scandal (PS2)]
"ms",
"msa",
"msb",
@ -234,9 +265,11 @@ static const char* extension_list[] = {
"msvp",
"mta2",
"mtaf",
"mul",
"mus",
"musc",
"musx",
"mvb", //txth/reserved [Porsche Challenge (PS1)]
"mwv",
"mxst",
"myspd",
@ -247,20 +280,24 @@ static const char* extension_list[] = {
"nlsd",
"nop",
"nps",
"npsf", //fake extension/header id for .nps (to be removed)
"npsf", //fake extension/header id for .nps (in bigfiles)
"nus3audio",
"nus3bank",
"nwa",
"nwav",
"nxa",
//"ogg", //common
"ogl",
"oma", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
"oma", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
"omu",
//"opus", //common
"opusx",
"otm",
"ovb",
"p1d", //txth/reserved [Farming Simulator 18 (3DS)]
"p2a", //txth/reserved [Thunderhawk Operation Phoenix (PS2)]
"p2bt",
"p3d",
"past",
@ -270,10 +307,10 @@ static const char* extension_list[] = {
"pnb",
"pona",
"pos",
"ps2stm", //fake extension for .stm (to be removed)
"ps2stm", //fake extension for .stm (renamed? to be removed?)
"psh", // fake extension for VSV(?) Dawn of Mana needs to be checked again
"psnd",
"psw", //fake extension for .wam
"psw", //fake extension for .wam (renamed, to be removed)
"r",
"rac", //txth/reserved [Manhunt (Xbox)]
@ -289,7 +326,7 @@ static const char* extension_list[] = {
"rsd",
"rsf",
"rsm",
"rstm", //rsm header id
"rstm", //fake extension/header id for .rstm (in bigfiles)
"rvws",
"rwar",
"rwav",
@ -311,6 +348,14 @@ static const char* extension_list[] = {
"sb5",
"sb6",
"sb7",
"sm0",
"sm1",
"sm2",
"sm3",
"sm4",
"sm5",
"sm6",
"sm7",
"sbin",
"sc",
"scd",
@ -331,7 +376,7 @@ static const char* extension_list[] = {
"sli",
"smc",
"smp",
"smpl", //fake extension (to be removed)
"smpl", //fake extension/header id for .v0/v1 (renamed, to be removed)
"smv",
"snd",
"snds",
@ -353,7 +398,7 @@ static const char* extension_list[] = {
"ster",
"sth",
//"stm", //common
"stma", //fake extension (to be removed)
"stma", //fake extension/header id for .stm
"str",
"stream",
"strm",
@ -375,9 +420,8 @@ static const char* extension_list[] = {
"thp",
"tk5",
"tra",
"trj",
"trm",
"tun",
"txth",
"txtp",
"tydsp",
@ -389,6 +433,7 @@ static const char* extension_list[] = {
"v0",
//"v1", //dual channel with v0
"va3",
"vag",
"vai",
"vas",
@ -422,9 +467,11 @@ static const char* extension_list[] = {
"wavebatch",
"wavm",
"wb",
"wd",
"wem",
"wii",
"wip", //txth/reserved [Colin McRae DiRT (PC)]
"wma", //common
"wmus",
"wp2",
"wpd",
@ -451,12 +498,14 @@ static const char* extension_list[] = {
"xss",
"xvag",
"xvas",
"xwav",//fake, to be removed
"xwav",//fake extension for .wav (renamed, to be removed)
"xwb",
"xmd",
"xopus",
"xps",
"xwc",
"xwm", //FFmpeg, not parsed (XWMA)
"xwma", //FFmpeg, not parsed (XWMA)
"xwm",
"xwma",
"xws",
"xwv",
@ -464,6 +513,8 @@ static const char* extension_list[] = {
"ymf",
"zsd",
"zsm",
"zss",
"zwdsp",
"vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */
@ -544,6 +595,7 @@ static const coding_info coding_info_list[] = {
{coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"},
{coding_WV6_IMA, "Gorilla Systems WV6 4-bit IMA ADPCM"},
{coding_ALP_IMA, "High Voltage ALP 4-bit IMA ADPCM"},
{coding_FFTA2_IMA, "Final Fantasy Tactics A2 4-bit IMA ADPCM"},
{coding_MS_IMA, "Microsoft 4-bit IMA ADPCM"},
{coding_XBOX_IMA, "XBOX 4-bit IMA ADPCM"},
@ -578,6 +630,7 @@ static const coding_info coding_info_list[] = {
{coding_FADPCM, "FMOD FADPCM 4-bit ADPCM"},
{coding_ASF, "Argonaut ASF 4-bit ADPCM"},
{coding_XMD, "Konami XMD 4-bit ADPCM"},
{coding_PCFX, "PC-FX 4-bit ADPCM"},
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
@ -587,6 +640,7 @@ static const coding_info coding_info_list[] = {
{coding_DERF, "Xilam DERF 8-bit DPCM"},
{coding_ACM, "InterPlay ACM"},
{coding_NWA, "VisualArt's NWA DPCM"},
{coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"},
{coding_EA_MT, "Electronic Arts MicroTalk"},
@ -644,9 +698,8 @@ static const layout_info layout_info_list[] = {
{layout_blocked_ws_aud, "blocked (Westwood Studios .aud)"},
{layout_blocked_matx, "blocked (Matrix .matx)"},
{layout_blocked_dec, "blocked (DEC)"},
{layout_blocked_vs, "blocked (vs)"},
{layout_blocked_emff_ps2, "blocked (EMFF PS2)"},
{layout_blocked_emff_ngc, "blocked (EMFF NGC)"},
{layout_blocked_vs, "blocked (Melbourne House VS)"},
{layout_blocked_mul, "blocked (MUL)"},
{layout_blocked_gsb, "blocked (GSB)"},
{layout_blocked_thp, "blocked (THP Movie Audio)"},
{layout_blocked_filp, "blocked (FILP)"},
@ -655,7 +708,7 @@ static const layout_info layout_info_list[] = {
{layout_blocked_bdsp, "blocked (BDSP)"},
{layout_blocked_ivaud, "blocked (IVAUD)"},
{layout_blocked_ps2_iab, "blocked (IAB)"},
{layout_blocked_ps2_strlr, "blocked (The Bouncer STR)"},
{layout_blocked_vs_str, "blocked (STR VS)"},
{layout_blocked_rws, "blocked (RWS)"},
{layout_blocked_hwas, "blocked (HWAS)"},
{layout_blocked_tra, "blocked (TRA)"},
@ -669,6 +722,7 @@ static const layout_info layout_info_list[] = {
{layout_blocked_sthd, "blocked (STHD)"},
{layout_blocked_h4m, "blocked (H4M)"},
{layout_blocked_xa_aiff, "blocked (XA AIFF)"},
{layout_blocked_vs_square, "blocked (Square VS)"},
};
static const meta_info meta_info_list[] = {
@ -699,7 +753,7 @@ static const meta_info meta_info_list[] = {
{meta_RWAV, "Nintendo RWAV header"},
{meta_CWAV, "Nintendo CWAV header"},
{meta_FWAV, "Nintendo FWAV header"},
{meta_PSX_XA, "RIFF/CDXA header"},
{meta_XA, "Sony XA header"},
{meta_PS2_RXWS, "Sony RXWS header"},
{meta_PS2_RAW, ".int PCM raw header"},
{meta_PS2_OMU, "Alter Echo OMU Header"},
@ -740,7 +794,7 @@ static const meta_info meta_info_list[] = {
{meta_WS_AUD, "Westwood Studios .aud header"},
{meta_WS_AUD_old, "Westwood Studios .aud (old) header"},
{meta_PS2_IVB, "IVB/BVII header"},
{meta_PS2_SVS, "Square SVS header"},
{meta_SVS, "Square SVS header"},
{meta_RIFF_WAVE, "RIFF WAVE header"},
{meta_RIFF_WAVE_POS, "RIFF WAVE header and .pos for looping"},
{meta_NWA, "VisualArt's NWA header"},
@ -750,7 +804,8 @@ static const meta_info meta_info_list[] = {
{meta_HGC1, "Knights of the Temple 2 hgC1 Header"},
{meta_AUS, "Capcom AUS Header"},
{meta_RWS, "RenderWare RWS header"},
{meta_EA_1SNH, "Electronic Arts 1SNh/EACS header"},
{meta_EA_1SNH, "Electronic Arts 1SNh header"},
{meta_EA_EACS, "Electronic Arts EACS header"},
{meta_SL3, "Atari Melbourne House SL3 header"},
{meta_FSB1, "FMOD Sample Bank (FSB1) Header"},
{meta_FSB2, "FMOD Sample Bank (FSB2) Header"},
@ -790,11 +845,14 @@ static const meta_info meta_info_list[] = {
{meta_PS2_RKV, "Legacy of Kain - Blood Omen 2 RKV PS2 header"},
{meta_PS2_VAS, "Pro Baseball Spirits 5 VAS Header"},
{meta_PS2_TEC, "assumed TECMO badflagged stream by .tec extension"},
{meta_PS2_ENTH, ".enth Header"},
{meta_SDT, "High Voltage .sdt header"},
{meta_NGC_TYDSP, ".tydsp Header"},
{meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"},
{meta_NGC_WVS, "Metal Arms WVS Header (GameCube)"},
{meta_XBOX_MATX, "assumed Matrix file by .matx extension"},
{meta_DEC, "Falcom DEC RIFF header"},
{meta_VS, "Men in Black VS Header"},
{meta_VS, "Melbourne House .VS header"},
{meta_DC_STR, "Sega Stream Asset Builder header"},
{meta_DC_STR_V2, "variant of Sega Stream Asset Builder header"},
{meta_XBOX_XMU, "XMU header"},
@ -804,7 +862,7 @@ static const meta_info meta_info_list[] = {
{meta_KRAW, "Geometry Wars: Galaxies KRAW header"},
{meta_NGC_YMF, "YMF DSP Header"},
{meta_PS2_CCC, "CCC Header"},
{meta_PSX_FAG, "FAG Header"},
{meta_FAG, "Radical .FAG Header"},
{meta_PS2_MIHB, "Sony MultiStream MIC header"},
{meta_DSP_WII_MUS, "mus header"},
{meta_WII_SNG, "SNG DSP Header"},
@ -835,8 +893,7 @@ static const meta_info meta_info_list[] = {
{meta_IDSP_NL, "Next Level IDSP header"},
{meta_IDSP_IE, "Inevitable Entertainment IDSP Header"},
{meta_UBI_JADE, "Ubisoft Jade RIFF header"},
{meta_PS2_SEG, "SEG (PS2) Header"},
{meta_XBOX_SEG, "SEG (XBOX) Header"},
{meta_SEG, "Stormfront SEG header"},
{meta_NDS_STRM_FFTA2, "Final Fantasy Tactics A2 RIFF Header"},
{meta_STR_ASR, "Donkey Kong Jet Race KNON/WII Header"},
{meta_ZWDSP, "Zack and Wiki custom DSP Header"},
@ -851,8 +908,7 @@ static const meta_info meta_info_list[] = {
{meta_VGS, "Guitar Hero VGS Header"},
{meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"},
{meta_WII_SMP, "SMP DSP Header"},
{meta_EMFF_PS2, "Eidos Music File Format Header"},
{meta_EMFF_NGC, "Eidos Music File Format Header"},
{meta_MUL, "Crystal Dynamics .MUL header"},
{meta_THP, "THP Movie File Format Header"},
{meta_STS_WII, "Shikigami no Shiro (WII) Header"},
{meta_PS2_P2BT, "Pop'n'Music 7 Header"},
@ -871,7 +927,7 @@ static const meta_info meta_info_list[] = {
{meta_ADS, "dhSS Header"},
{meta_PS2_MCG, "Gunvari MCG Header"},
{meta_ZSD, "ZSD Header"},
{meta_RedSpark, "RedSpark Header"},
{meta_REDSPARK, "RedSpark Header"},
{meta_IVAUD, "Rockstar .ivaud header"},
{meta_DSP_WII_WSD, ".WSD header"},
{meta_WII_NDP, "Icon Games NDP header"},
@ -949,7 +1005,7 @@ static const meta_info meta_info_list[] = {
{meta_X360_TRA, "Terminal Reality .TRA raw header"},
{meta_PS2_VGS, "Princess Soft VGS header"},
{meta_PS2_IAB, "Runtime .IAB header"},
{meta_PS2_STRLR, "The Bouncer STR header"},
{meta_VS_STR, "Square .VS STR* header"},
{meta_LSF_N1NJ4N, ".lsf !n1nj4n header"},
{meta_VAWX, "feelplus VAWX header"},
{meta_PC_SNDS, "assumed Heavy Iron IMA by .snds extension"},
@ -1025,20 +1081,16 @@ static const meta_info meta_info_list[] = {
{meta_NGC_VID1, "Neversoft VID1 header"},
{meta_PC_FLX, "Ultima IX .FLX header"},
{meta_MOGG, "Harmonix Music Systems MOGG Vorbis"},
{meta_OGG_VORBIS, "Ogg Vorbis"},
{meta_OGG_SLI, "Ogg Vorbis with .sli (start,length) for looping"},
{meta_OGG_SLI2, "Ogg Vorbis with .sli (from,to) for looping"},
{meta_OGG_SFL, "Ogg Vorbis with SFPL for looping"},
{meta_OGG_UM3, "Ogg Vorbis (Ultramarine3)"},
{meta_OGG_KOVS, "Ogg Vorbis (KOVS header)"},
{meta_OGG_PSYCHIC, "Ogg Vorbis (Psychic Software)"},
{meta_OGG_SNGW, "Ogg Vorbis (Capcom)"},
{meta_OGG_ISD, "Ogg Vorbis (ISD)"},
{meta_OGG_VORBIS, "Ogg Vorbis header"},
{meta_OGG_SLI, "Ogg Vorbis header (.sli looping)"},
{meta_OPUS_SLI, "Ogg Opus header (.sli looping)"},
{meta_OGG_SFL, "Ogg Vorbis header (SFPL looping)"},
{meta_OGG_KOVS, "Ogg Vorbis header (KOVS)"},
{meta_OGG_encrypted, "Ogg Vorbis header (encrypted)"},
{meta_KMA9, "Koei Tecmo KMA9 header"},
{meta_XWC, "Starbreeze XWC header"},
{meta_SQEX_SAB, "Square-Enix SAB header"},
{meta_SQEX_MAB, "Square-Enix MAB header"},
{meta_OGG_L2SD, "Ogg Vorbis (L2SD)"},
{meta_WAF, "KID WAF header"},
{meta_WAVE, "EngineBlack .WAVE header"},
{meta_WAVE_segmented, "EngineBlack .WAVE header (segmented)"},
@ -1052,20 +1104,15 @@ static const meta_info meta_info_list[] = {
{meta_DSP_MCADPCM, "Bethesda .mcadpcm header"},
{meta_UBI_LYN, "Ubisoft LyN RIFF header"},
{meta_MSB_MSH, "Sony MultiStream MSH+MSB header"},
{meta_OGG_RPGMV, "Ogg Vorbis (RPGMV header)"},
{meta_OGG_ENO, "Ogg Vorbis (ENO header)"},
{meta_TXTP, "TXTP generic header"},
{meta_SMC_SMH, "Genki SMC+SMH header"},
{meta_OGG_YS8, "Ogg Vorbis (Ys VIII header)"},
{meta_PPST, "Parappa PPST header"},
{meta_OPUS_PPP, "AT9 OPUS 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_OGG_GWM, "Ogg Vorbis (GWM header)"},
{meta_DSP_SADF, "Procyon Studio SADF header"},
{meta_H4M, "Hudson HVQM4 header"},
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
{meta_ASF, "Argonaut ASF header"},
{meta_XMD, "Konami XMD header"},
{meta_CKS, "Cricket Audio CKS header"},
@ -1095,6 +1142,18 @@ static const meta_info meta_info_list[] = {
{meta_NXA, "Entergram NXA header"},
{meta_ADPCM_CAPCOM, "Capcom .ADPCM header"},
{meta_UE4OPUS, "Epic Games UE4OPUS header"},
{meta_XWMA, "Microsoft XWMA RIFF header"},
{meta_VA3, "Konami VA3 header" },
{meta_XOPUS, "Exient XOPUS header"},
{meta_VS_SQUARE, "Square VS header"},
{meta_NWAV, "Chunsoft NWAV header"},
{meta_XPCM, "Circus XPCM header"},
{meta_MSF_TAMASOFT, "Tama-Soft MSF header"},
{meta_XPS_DAT, "From Software .XPS+DAT header"},
{meta_ZSND, "Vicarious Visions ZSND header"},
{meta_DSP_ADPCMX, "AQUASTYLE ADPY header"},
{meta_OGG_OPUS, "Ogg Opus header"},
{meta_IMC, "iNiS .IMC header"},
};

View file

@ -40,7 +40,7 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
if (samples_this_block < 0) {
/* probably block bug or EOF, next calcs would give wrong values/segfaults/infinite loop */
VGM_LOG("layout_blocked: wrong block samples at 0x%"PRIx64"\n", (off64_t)vgmstream->current_block_offset);
VGM_LOG("layout_blocked: wrong block samples at 0x%x\n", (uint32_t)vgmstream->current_block_offset);
memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample));
break;
}
@ -127,11 +127,8 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
case layout_blocked_dec:
block_update_dec(block_offset,vgmstream);
break;
case layout_blocked_emff_ps2:
block_update_emff_ps2(block_offset,vgmstream);
break;
case layout_blocked_emff_ngc:
block_update_emff_ngc(block_offset,vgmstream);
case layout_blocked_mul:
block_update_mul(block_offset,vgmstream);
break;
case layout_blocked_gsb:
block_update_gsb(block_offset,vgmstream);
@ -166,8 +163,8 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
case layout_blocked_ps2_iab:
block_update_ps2_iab(block_offset,vgmstream);
break;
case layout_blocked_ps2_strlr:
block_update_ps2_strlr(block_offset,vgmstream);
case layout_blocked_vs_str:
block_update_vs_str(block_offset,vgmstream);
break;
case layout_blocked_rws:
block_update_rws(block_offset,vgmstream);
@ -205,6 +202,9 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
case layout_blocked_xa_aiff:
block_update_xa_aiff(block_offset,vgmstream);
break;
case layout_blocked_vs_square:
block_update_vs_square(block_offset,vgmstream);
break;
default: /* not a blocked layout */
break;
}

View file

@ -33,10 +33,15 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
case 0x5344454E: /* "SDEN" */
case 0x53444652: /* "SDFR" */
case 0x53444745: /* "SDGE" */
case 0x53444445: /* "SDDE" */
case 0x53444954: /* "SDIT" */
case 0x53445350: /* "SDSP" */
case 0x53444553: /* "SDES" */
case 0x53444D58: /* "SDMX" */
case 0x53445255: /* "SDRU" */
case 0x53444A41: /* "SDJA" */
case 0x53444A50: /* "SDJP" */
case 0x5344504C: /* "SDPL" */
/* audio chunk */
if (vgmstream->coding_type == coding_PSX)
block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels);

View file

@ -1,34 +0,0 @@
#include "layout.h"
#include "../vgmstream.h"
/* set up for the block at the given offset */
void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitLE(
vgmstream->current_block_offset+0x10,
vgmstream->ch[0].streamfile);
vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size+0x20;
vgmstream->current_block_size/=vgmstream->channels;
for (i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset+0x20+(vgmstream->current_block_size*i);
}
}
void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitBE(
vgmstream->current_block_offset+0x20,
vgmstream->ch[0].streamfile);
vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size+0x40;
vgmstream->current_block_size/=vgmstream->channels;
for (i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset+0x40+(vgmstream->current_block_size*i);
}
}

View file

@ -48,7 +48,7 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) {
block_skip += (audio_bytes / vgmstream->num_streams) * (vgmstream->stream_index-1);
}
VGM_ASSERT(frame_format == 1, "H4M: unknown frame_format %x at %"PRIx64"\n", frame_format, (off64_t)block_offset);
VGM_ASSERT(frame_format == 1, "H4M: unknown frame_format %x at %x\n", frame_format, (uint32_t)block_offset);
/* pass current mode to the decoder */
vgmstream->codec_config = (frame_format << 8) | (vgmstream->codec_config & 0xFF);

View file

@ -0,0 +1,50 @@
#include "layout.h"
#include "../vgmstream.h"
/* process headered blocks with sub-headers */
void block_update_mul(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i, block_type;
size_t block_size, block_header, data_size, data_header;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
block_type = read_32bit(block_offset + 0x00,streamFile);
block_size = read_32bit(block_offset + 0x04,streamFile); /* not including main header */
switch(vgmstream->coding_type) {
case coding_NGC_DSP:
block_header = 0x20;
data_header = 0x20;
break;
default:
block_header = 0x10;
data_header = 0x10;
break;
}
if (block_type == 0x00 && block_size == 0) {
/* oddity in some vid+audio files? bad extraction? */
block_header = 0x10;
data_header = 0x00;
data_size = 0;
}
if (block_type == 0x00 && block_size != 0) {
/* read audio sub-header */
data_size = read_32bit(block_offset + block_header + 0x00,streamFile);
}
else {
/* non-audio or empty audio block */
data_header = 0x00;
data_size = 0;
}
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = data_size / vgmstream->channels;
vgmstream->next_block_offset = block_offset + block_header + block_size;
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + block_header + data_header + vgmstream->current_block_size*i;
//VGM_LOG("ch%i of=%lx\n", i, vgmstream->ch[i].offset);
}
//getchar();
}

View file

@ -1,17 +1,16 @@
#include "layout.h"
#include "../vgmstream.h"
/* set up for the block at the given offset */
/* mini-blocks of size + data */
void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i;
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitLE(
vgmstream->current_block_offset,
vgmstream->ch[0].streamfile);
vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size + 0x4;
vgmstream->ch[i].offset = vgmstream->current_block_offset + 0x4;
vgmstream->current_block_size = read_32bitLE(vgmstream->current_block_offset,streamFile);
vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size + 0x04;
vgmstream->ch[i].offset = vgmstream->current_block_offset + 0x04;
if (i == 0) block_offset=vgmstream->next_block_offset;
}
}

View file

@ -0,0 +1,17 @@
#include "layout.h"
#include "../vgmstream.h"
/* Square "VS" headered blocks */
void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
size_t block_size = 0x800;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = block_size - 0x20;
vgmstream->next_block_offset = block_offset + block_size*vgmstream->channels;
/* 0x08: number of remaning blocks, 0x0c: blocks left */
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + 0x20 + 0x800*i;
}
}

View file

@ -2,7 +2,7 @@
#include "../vgmstream.h"
/* The Bouncer STRx blocks, one block per channel when stereo */
void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream) {
void block_update_vs_str(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i;

View file

@ -26,7 +26,7 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
VGM_ASSERT(block_offset + 0x930 < get_streamfile_size(streamFile) &&
(uint8_t)read_8bit(block_offset + 0x000 + 0x11,streamFile) !=
(uint8_t)read_8bit(block_offset + 0x930 + 0x11,streamFile),
"XA block: subchannel change at %"PRIx64"\n", (off64_t)block_offset);
"XA block: subchannel change at %x\n", (uint32_t)block_offset);
/* submode flag bits (typical audio value = 0x64 01100100)
* - 7 (0x80 10000000): end of file
@ -53,7 +53,7 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
}
else {
;VGM_ASSERT_ONCE(block_offset < get_streamfile_size(streamFile),
"XA block: non audio block found at %"PRIx64"\n", (off64_t)block_offset);
"XA block: non audio block found at %x\n", (uint32_t)block_offset);
block_samples = 0; /* not an audio sector */
}

View file

@ -21,8 +21,7 @@ void block_update_ws_aud(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_dec(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_mul(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream);
@ -33,7 +32,7 @@ void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_bdsp(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_tra(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_ps2_iab(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_vs_str(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream);
@ -46,6 +45,7 @@ void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream);
/* other layouts */
void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View file

@ -3,8 +3,6 @@
#include "../coding/coding.h"
#include "aax_utf.h"
static STREAMFILE* setup_aax_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
#define MAX_SEGMENTS 2 /* usually segment0=intro, segment1=loop/main */
@ -77,7 +75,7 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
/* open each segment subfile */
for (i = 0; i < segment_count; i++) {
STREAMFILE* temp_streamFile = setup_aax_streamfile(streamFile, segment_offset[i],segment_size[i], (is_hca ? "hca" : "adx"));
STREAMFILE* temp_streamFile = setup_subfile_streamfile(streamFile, segment_offset[i],segment_size[i], (is_hca ? "hca" : "adx"));
if (!temp_streamFile) goto fail;
data->segments[i] = is_hca ?
@ -138,29 +136,6 @@ fail:
return NULL;
}
static STREAMFILE* setup_aax_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
/* CRI's UTF wrapper around DSP [Sonic Colors sfx (Wii), NiGHTS: Journey of Dreams sfx (Wii)] */
VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) {

View file

@ -185,6 +185,9 @@ static const adxkey_info adxkey8_list[] = {
/* Tensei Hakkenshi - Fuumaroku (PS2) */
{0x5761,0x6283,0x4531, "HAKKEN",0},
/* Lucky Star - Ryouou Gakuen Outousai (PS2) */
{0x481D,0x44F9,0x4E35, "LSTARPS2",0},
};
static const adxkey_info adxkey9_list[] = {
@ -192,10 +195,10 @@ static const adxkey_info adxkey9_list[] = {
/* Phantasy Star Online 2 */
{0x07d2,0x1ec5,0x0c7f, NULL,0}, // guessed with degod
/* Dragon Ball Z: Dokkan Battle */
/* Dragon Ball Z: Dokkan Battle (Android/iOS) */
{0x0003,0x0d19,0x043b, NULL,416383518}, // 0000000018D1821E
/* Kisou Ryouhei Gunhound EX (2013-01-31)(Dracue)[PSP] */
/* Kisou Ryouhei Gunhound EX (PSP) */
{0x0005,0x0bcd,0x1add, NULL,683461999}, // 0000000028BCCD6F
/* Raramagi (Android) */
@ -210,12 +213,16 @@ static const adxkey_info adxkey9_list[] = {
/* Yuuki Yuuna wa Yuusha de aru - Hanayui no Kirameki / Yuyuyui (iOS/Android) */
{0x3f10,0x3651,0x6d31, NULL,4867249871962584729}, // 438BF1F883653699
// Super Robot Wars X-Omega (voices) [iOS/Android]
/* Super Robot Wars X-Omega (iOS/Android) voices */
{0x5152,0x7979,0x152b, NULL,165521992944278}, // 0000968A97978A96
// AKA to BLUE (Android)
/* AKA to BLUE (Android) */
{0x03fc,0x0749,0x12EF, NULL,0}, // guessed with VGAudio (possible key: 1FE0748978 / 136909719928)
//{0x0c03,0x0749,0x1459, NULL,0}, // 2nd guess (possible key: 6018748A2D / 412727151149)
/* Mashiro Witch (Android) */
{0x2669,0x1495,0x2407, NULL,0x55D11D3349495204}, // 55D11D3349495204
};
static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]);

View file

@ -51,7 +51,7 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
if (read_32bitLE(0x08,streamFile) != get_streamfile_size(streamFile))
goto fail;
/* 0x04(2): version? (iPad/IPhone?) */
/* 0x04(1): version */
header_size = read_16bitLE(0x06,streamFile);
codec = read_8bit(0x0c,streamFile);
@ -62,10 +62,12 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
loop_end = read_32bitLE(0x18,streamFile);
/* possibly more complex, see AKB2 */
if (header_size >= 0x44) { /* v2 */
if (header_size >= 0x44) { /* v2+ */
extradata_size = read_16bitLE(0x1c,streamFile);
/* 0x20+: config? (pan, volume) */
subheader_size = read_16bitLE(0x28,streamFile);
/* 0x20+: config? (pan, volume), 0x24: file_id? */
/* 0x24: file_id? */
/* 0x2b: encryption bitflag if version > 2? */
extradata_offset = header_size + subheader_size;
start_offset = extradata_offset + extradata_size;
}
@ -74,7 +76,7 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
}
stream_size = get_streamfile_size(streamFile) - start_offset;
loop_flag = read_32bitLE(0x18,streamFile) > 0; /* loop end */
loop_flag = (loop_end > loop_start);
/* build the VGMSTREAM */
@ -86,12 +88,12 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
switch (codec) {
case 0x02: { /* MSAPDCM [Dragon Quest II (iOS) sfx] */
case 0x02: { /* MSADPCM [Dragon Quest II (iOS) sfx] */
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(extradata_offset + 0x02,streamFile);
/* adjusted samples; bigger or smaller than base samples, but seems more accurate
/* adjusted samples; bigger or smaller than base samples, akb lib uses these fields instead
* (base samples may have more than possible and read over file size otherwise, very strange)
* loop_end seems to exist even with loop disabled */
vgmstream->num_samples = read_32bitLE(extradata_offset + 0x04, streamFile);
@ -107,6 +109,8 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
ovmi.meta_type = vgmstream->meta_type;
ovmi.stream_size = stream_size;
/* extradata + 0x04: Ogg loop start offset */
/* oggs have loop info in the comments */
ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
if (ogg_vgmstream) {
@ -143,7 +147,7 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
#endif
#ifdef VGM_USE_FFMPEG
case 0x06: { /* aac [The World Ends with You (iPad)] */
case 0x06: { /* M4A with AAC [The World Ends with You (iPad)] */
/* init_vgmstream_akb_mp4 above has priority, but this works fine too */
ffmpeg_codec_data *ffmpeg_data;
@ -157,16 +161,16 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
/* bad total samples (some kind of duration? probably should be loop_end though) */
if (loop_flag)
vgmstream->num_samples = loop_end+1;
/* remove encoder delay from the "global" sample values */
vgmstream->num_samples -= ffmpeg_data->skipSamples;
vgmstream->loop_start_sample -= ffmpeg_data->skipSamples;
vgmstream->loop_end_sample -= ffmpeg_data->skipSamples;
/* loops are pre-adjusted with 2112 encoder delay (ex. TWEWY B04's loop_start=45) */
break;
}
#endif
case 0x01: /* PCM16LE */
default:
goto fail;
}
@ -188,7 +192,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, material_offset, extradata_offset;
size_t material_size, extradata_size, stream_size;
int loop_flag = 0, channel_count, encryption_flag, codec, sample_rate, /*num_samples, loop_start,*/ loop_end;
int loop_flag = 0, channel_count, encryption_flag, codec, sample_rate, /*num_samples,*/ loop_start, loop_end;
int total_subsongs, target_subsong = streamFile->stream_index;
/* check extensions */
@ -201,6 +205,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
goto fail;
if (read_32bitLE(0x08,streamFile) != get_streamfile_size(streamFile))
goto fail;
/* 0x04: version */
/* parse tables */
{
@ -224,7 +229,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
material_offset = table_offset + read_32bitLE(table_offset + table_size + (target_subsong-1)*entry_size + 0x04, streamFile);
}
/** stream header **/
/** stream header (material) **/
/* 0x00: 0? */
codec = read_8bit(material_offset+0x01,streamFile);
channel_count = read_8bit(material_offset+0x02,streamFile);
@ -234,15 +239,18 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
stream_size = read_32bitLE(material_offset+0x08,streamFile);
//num_samples = read_32bitLE(material_offset+0x0c,streamFile);
//loop_start = read_32bitLE(material_offset+0x10,streamFile);
loop_start = read_32bitLE(material_offset+0x10,streamFile);
loop_end = read_32bitLE(material_offset+0x14,streamFile);
extradata_size = read_32bitLE(material_offset+0x18,streamFile);
/* rest: ? (empty or 0x3f80) */
loop_flag = (loop_end > 0);
loop_flag = (loop_end > loop_start);
extradata_offset = material_offset + material_size;
start_offset = material_offset + material_size + extradata_size;
if (encryption_flag & 0x08)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
@ -256,12 +264,11 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
switch (codec) {
case 0x02: { /* MSADPCM [The Irregular at Magic High School Lost Zero (Android)] */
if (encryption_flag & 0x08) goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(extradata_offset + 0x02, streamFile);
/* adjusted samples; bigger or smaller than base samples, but seems more accurate
/* adjusted samples; bigger or smaller than base samples, akb lib uses these fields instead
* (base samples may have more than possible and read over file size otherwise, very strange)
* loop_end seems to exist even with loop disabled */
vgmstream->num_samples = read_32bitLE(extradata_offset + 0x04, streamFile);
@ -316,6 +323,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
}
#endif
case 0x01: /* PCM16LE */
default:
goto fail;
}

View file

@ -1,8 +1,7 @@
#include "meta.h"
#include "../coding/coding.h"
static STREAMFILE* setup_atsl_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
typedef enum { ATRAC3, ATRAC9, KOVS, KTSS } atsl_codec;
typedef enum { ATRAC3, ATRAC9, KOVS, KTSS, KTAC } atsl_codec;
/* .ATSL - Koei Tecmo audio container [One Piece Pirate Warriors (PS3), Warriors All-Stars (PC)] */
VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
@ -13,7 +12,7 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
atsl_codec codec;
const char* fake_ext;
off_t subfile_offset = 0;
size_t subfile_size = 0, header_size;
size_t subfile_size = 0, header_size, entry_size;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
@ -40,24 +39,31 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
* - 00060301 00010301 atsl in G1L from One Piece Pirate Warriors 3 (PC)[KOVS]
* - 000A0301 00010501 atsl in G1L from Warriors All-Stars (PC)[KOVS]
* - 000B0301 00080601 atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS]
* - 010C0301 01060601 .atsl from Dynasty Warriors 9 (PS4)[KTAC]
*/
type = read_8bit(0x0d, streamFile);
entry_size = 0x28;
type = read_16bitLE(0x0c, streamFile);
switch(type) {
case 0x01:
case 0x0100:
codec = KOVS;
fake_ext = "kvs";
break;
case 0x02:
case 0x0200:
codec = ATRAC3;
fake_ext = "at3";
big_endian = 1;
break;
case 0x04:
case 0x06:
case 0x0400:
case 0x0600:
codec = ATRAC9;
fake_ext = "at9";
break;
case 0x08:
case 0x0601:
codec = KTAC;
fake_ext = "ktac";
entry_size = 0x3c;
break;
case 0x0800:
codec = KTSS;
fake_ext = "ktss";
break;
@ -79,14 +85,15 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
for (i = 0; i < entries; i++) {
int is_unique = 1;
off_t entry_offset = read_32bit(header_size + i*0x28 + 0x04,streamFile);
size_t entry_size = read_32bit(header_size + i*0x28 + 0x08,streamFile);
/* 0x00: id?, 0x08+: sample rate/num_samples/loop_start/etc, matching subfile header */
/* 0x00: id */
off_t entry_subfile_offset = read_32bit(header_size + i*entry_size + 0x04,streamFile);
size_t entry_subfile_size = read_32bit(header_size + i*entry_size + 0x08,streamFile);
/* 0x08+: channels/sample rate/num_samples/loop_start/etc (match subfile header) */
/* check if current entry was repeated in a prev entry */
for (j = 0; j < i; j++) {
off_t prev_offset = read_32bit(header_size + j*0x28 + 0x04,streamFile);
if (prev_offset == entry_offset) {
off_t prev_offset = read_32bit(header_size + j*entry_size + 0x04,streamFile);
if (prev_offset == entry_subfile_offset) {
is_unique = 0;
break;
}
@ -98,8 +105,8 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
/* target GET, but keep going to count subsongs */
if (!subfile_offset && target_subsong == total_subsongs) {
subfile_offset = entry_offset;
subfile_size = entry_size;
subfile_offset = entry_subfile_offset;
subfile_size = entry_subfile_size;
}
}
}
@ -110,7 +117,7 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
/* some kind of seek/switch table may follow (optional, found in .atsl3) */
temp_streamFile = setup_atsl_streamfile(streamFile, subfile_offset,subfile_size, fake_ext);
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, fake_ext);
if (!temp_streamFile) goto fail;
/* init the VGMSTREAM */
@ -130,6 +137,10 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
vgmstream = init_vgmstream_ktss(temp_streamFile);
if (!vgmstream) goto fail;
break;
case KTAC:
//vgmstream = init_vgmstream_ktac(temp_streamFile); //Koei Tecto VBR-like ATRAC9
//if (!vgmstream) goto fail;
//break;
default:
goto fail;
}
@ -144,27 +155,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
static STREAMFILE* setup_atsl_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View file

@ -110,6 +110,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, substream_size, layer_channels, awc.sample_rate, block_count, block_size);
data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, substream_offset,substream_size);
xma_fix_raw_samples(data->layers[i], temp_streamFile, substream_offset,substream_size, 0, 0,0); /* samples are ok? */
close_streamfile(temp_streamFile);
if (!data->layers[i]->codec_data) goto fail;
}
@ -128,6 +130,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, awc.stream_offset,awc.stream_size, 0, 0,0); /* samples are ok? */
}
break;
@ -333,7 +337,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
/* If music, data is divided into blocks of block_chunk size with padding.
* Each block has a header/seek table and interleaved data for all channels */
if (awc->is_music && read_32bit(awc->stream_offset, streamFile) != 0) {
VGM_LOG("AWC: music found, but block doesn't start with seek table at %"PRIx64"\n", (off64_t)awc->stream_offset);
VGM_LOG("AWC: music found, but block doesn't start with seek table at %x\n", (uint32_t)awc->stream_offset);
goto fail;
}

View file

@ -38,9 +38,9 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
#ifdef VGM_USE_FFMPEG
{
/* target_subsong should be passed with the streamFile */
/* target_subsong should be passed manually */
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile));
vgmstream->codec_data = init_ffmpeg_header_offset_subsong(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), target_subsong);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
}

View file

@ -1,7 +1,7 @@
#include "meta.h"
#include "../coding/coding.h"
typedef enum { PSX, PCM16, ATRAC9 } bnk_codec;
typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec;
/* BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */
VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
@ -11,7 +11,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
size_t stream_size, interleave = 0;
off_t sblk_offset, data_offset;
size_t data_size;
int channel_count = 0, loop_flag, sample_rate, version;
int channel_count = 0, loop_flag, sample_rate, parts, version;
int loop_start = 0, loop_end = 0;
uint32_t atrac9_info = 0;
@ -25,11 +25,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
if (!check_extensions(streamFile, "bnk"))
goto fail;
if (read_32bitBE(0x00,streamFile) == 0x00000003 && read_32bitBE(0x04,streamFile) == 0x00000002) { /* PS3 */
if (read_32bitBE(0x00,streamFile) == 0x00000003) { /* PS3 */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
}
else if (read_32bitBE(0x00,streamFile) == 0x03000000 && read_32bitBE(0x04,streamFile) == 0x02000000) { /* Vita/PS4 */
else if (read_32bitBE(0x00,streamFile) == 0x03000000) { /* Vita/PS4 */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
@ -37,17 +37,22 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
goto fail;
}
parts = read_32bit(0x04,streamFile);
if (parts < 2 || parts > 3) goto fail;
sblk_offset = read_32bit(0x08,streamFile);
/* 0x0c: sklb size */
data_offset = read_32bit(0x10,streamFile);
data_size = read_32bit(0x14,streamFile);
/* 0x18: ZLSD small footer, rare [Yakuza 6's Puyo Puyo (PS4)] */
/* 0x1c: ZLSD size */
/* SE banks, also used for music. Most table fields seems reserved/defaults and
* don't change much between subsongs or files, so they aren't described in detail */
/* SBlk part: parse header */
if (read_32bit(sblk_offset+0x00,streamFile) != 0x6B6C4253) /* "SBlk" (sample block?) */
if (read_32bit(sblk_offset+0x00,streamFile) != 0x6B6C4253) /* "klBS" (SBlk = sample block?) */
goto fail;
version = read_32bit(sblk_offset+0x04,streamFile);
/* 0x08: possibly when version=0x0d, 0x03=Vita, 0x06=PS4 */
@ -83,6 +88,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
break;
case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */
case 0x0e: /* Yakuza 6's Puyo Puyo (PS4) */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,streamFile);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile);
table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,streamFile);
@ -159,6 +165,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
//case 0x04: /* different format? */
case 0x09:
case 0x0d:
case 0x0e:
/* find if this sound has an assigned name in table1 */
for (i = 0; i < section_entries; i++) {
off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,streamFile);
@ -255,6 +262,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
break;
case 0x0d:
case 0x0e:
type = read_16bit(start_offset+0x00,streamFile);
if (read_32bit(start_offset+0x04,streamFile) != 0x01) /* type? */
goto fail;
@ -305,7 +313,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
loop_length = read_32bit(start_offset+0x1c,streamFile);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = PSX;
codec = HEVAG;
break;
default:
@ -377,6 +385,20 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break;
case HEVAG:
vgmstream->sample_rate = 48000;
vgmstream->coding_type = coding_HEVAG;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break;
default:
goto fail;
}
if (name_offset)

View file

@ -17,27 +17,33 @@ typedef struct {
int32_t loop_start;
int32_t loop_end;
int32_t loop_start_offset;
int32_t data_offset;
int big_endian;
int loop_flag;
int is_sead;
int codec_config;
int is_bank;
int total_subsongs;
} ea_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 void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea);
static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
/* EA 1SNh - from early EA games (~1996, ex. Need for Speed) */
/* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */
VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
ea_header ea = {0};
off_t eacs_offset;
/* checks */
/* .asf/as4: common, cnk: some PS games, .sng: fake for plugins (to mimic EA SCHl's common extension) */
/* .uv, .tgq: some SAT games */
/* .asf/as4: common
* .cnk: some PS games
* .sng: fake for plugins (to mimic EA SCHl's common extension)
* .uv/tgq: some SAT games (video only?) */
if (!check_extensions(streamFile,"asf,as4,cnk,sng,uv,tgq"))
goto fail;
@ -47,45 +53,117 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
/* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end.
* Video uses various blocks (kVGT/fVGT/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */
ea.is_sead = read_32bitBE(0x00,streamFile) == 0x53454144;
/* use block size as endian marker (Saturn = BE) */
ea.big_endian = !(read_32bitLE(0x04,streamFile) < 0x0000FFFF);
ea.big_endian = guess_endianness32bit(0x04,streamFile);
if (!parse_header(streamFile,&ea, 0x08))
eacs_offset = 0x08; /* after 1SNh block id+size */
if (!parse_header(streamFile,&ea, eacs_offset))
goto fail;
return init_vgmstream_main(streamFile, &ea);
fail:
return NULL;
}
/* EA EACS - from early EA games, bank (~1996, ex. Need for Speed) */
VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE *streamFile) {
ea_header ea = {0};
off_t eacs_offset;
/* checks */
/* .eas: single bank [Need for Speed (PC)]
* .bnk: multi bank [Need for Speed (PC)] */
if (!check_extensions(streamFile,"eas,bnk"))
goto fail;
start_offset = 0x00;
/* plain data without blocks, can contain N*(EACS header) + N*(data), or N (EACS header + data) */
ea.is_bank = 1;
/* use ??? as endian marker (Saturn = BE) */
//ea.big_endian = guess_endianness32bit(0x04,streamFile);
if (read_32bitBE(0x00,streamFile) == 0x45414353) { /* "EACS" */
/* single bank variant */
eacs_offset = 0x00;
}
else if (read_32bitBE(0x00,streamFile) == 0x00) {
/* multi bank variant */
int i;
int target_subsong = streamFile->stream_index;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0) goto fail;
/* offsets to EACSs are scattered in the first 0x200
* this looks dumb but seems like the only way */
eacs_offset = 0;
for (i = 0x00; i < 0x200; i += 0x04) {
off_t bank_offset = read_32bitLE(i, streamFile);
if (bank_offset == 0)
continue;
ea.total_subsongs++;
/* parse mini bank header */
if (ea.total_subsongs == target_subsong) {
/* 0x00: some id or flags? */
eacs_offset = read_32bitLE(bank_offset + 0x04, streamFile);
/* rest: not sure if part of this header */
}
}
if (eacs_offset == 0)
goto fail;
}
else {
goto fail;
}
if (!parse_header(streamFile,&ea, eacs_offset))
goto fail;
return init_vgmstream_main(streamFile, &ea);
fail:
return NULL;
}
static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea) {
VGMSTREAM * vgmstream = NULL;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag);
vgmstream = allocate_vgmstream(ea->channels, ea->loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = ea.sample_rate;
vgmstream->num_samples = ea.num_samples;
vgmstream->loop_start_sample = ea.loop_start;
vgmstream->loop_end_sample = ea.loop_end;
vgmstream->sample_rate = ea->sample_rate;
vgmstream->num_samples = ea->num_samples;
vgmstream->loop_start_sample = ea->loop_start;
vgmstream->loop_end_sample = ea->loop_end;
vgmstream->codec_endian = ea.big_endian;
vgmstream->layout_type = layout_blocked_ea_1snh;
vgmstream->meta_type = meta_EA_1SNH;
vgmstream->codec_endian = ea->big_endian;
vgmstream->layout_type = ea->is_bank ? layout_none : layout_blocked_ea_1snh;
vgmstream->meta_type = ea->is_bank ? meta_EA_EACS : meta_EA_1SNH;
vgmstream->num_streams = ea->total_subsongs;
switch (ea.codec) {
switch (ea->codec) {
case EA_CODEC_PCM: /* Need for Speed (PC) */
vgmstream->coding_type = ea.bits==1 ? coding_PCM8_int : coding_PCM16_int;
vgmstream->coding_type = ea->bits==1 ? coding_PCM8_int : coding_PCM16_int;
break;
case EA_CODEC_ULAW: /* Crusader: No Remorse movies (SAT), FIFA 96 movies (SAT) */
if (ea.bits && ea.bits!=2) goto fail; /* only set in EACS */
if (ea->bits && ea->bits != 2) goto fail; /* only set in EACS */
vgmstream->coding_type = coding_ULAW_int;
break;
case EA_CODEC_IMA: /* Need for Speed II (PC) */
if (ea.bits && ea.bits!=2) goto fail; /* only in EACS */
if (ea->bits && ea->bits != 2) goto fail; /* only in EACS */
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
vgmstream->codec_config = ea.codec_config;
vgmstream->codec_config = ea->codec_config;
break;
case EA_CODEC_PSX: /* Need for Speed (PS) */
@ -93,12 +171,12 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
break;
default:
VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec);
VGM_LOG("EA EACS: unknown codec 0x%02x\n", ea->codec);
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream,streamFile,ea->data_offset))
goto fail;
return vgmstream;
@ -116,15 +194,18 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) {
ea->bits = read_8bit(offset+0x08, streamFile);
ea->channels = read_8bit(offset+0x09, streamFile);
ea->codec = read_8bit(offset+0x0a, streamFile);
ea->type = read_8bit(offset+0x0b, streamFile);
ea->type = read_8bit(offset+0x0b, streamFile); /* block type? 0=1SNh, -1=bank */
ea->num_samples = read_32bit(offset+0x0c, streamFile);
ea->loop_start = read_32bit(offset+0x10, streamFile);
ea->loop_end = read_32bit(offset+0x14, streamFile) + ea->loop_start; /* loop length */
/* 0x18: data start? (0x00), 0x1c: pan/volume/etc? (0x7F), rest can be padding/garbage */
VGM_ASSERT(ea->type != 0, "EA EACS: unknown type\n"); /* block type? */
ea->data_offset = read_32bit(offset+0x18, streamFile); /* 0 when blocked */
/* 0x1c: pan/volume/etc? (0x7F)
* rest may be padding/garbage */
//VGM_ASSERT(ea->type != 0, "EA EACS: unknown type %i\n", ea->type);
if (ea->codec == EA_CODEC_IMA)
ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea);
/* EACS banks with empty values exist but will be rejected later */
}
else if (ea->is_sead) {
/* alt subheader (found in some PC videos) */

View file

@ -19,6 +19,7 @@
#define EAAC_CODEC_DSP 0x08
#define EAAC_CODEC_EASPEEX 0x09
#define EAAC_CODEC_EATRAX 0x0a
#define EAAC_CODEC_EAMP3 0x0b
#define EAAC_CODEC_EAOPUS 0x0c
#define EAAC_FLAG_NONE 0x00
@ -461,6 +462,85 @@ fail:
return NULL;
}
/* EA MPF/MUS combo - used in older 7th gen games for storing music */
VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE *streamFile) {
uint32_t num_sounds;
uint8_t version, sub_version, block_id;
off_t table_offset, entry_offset, snr_offset, sns_offset;
size_t snr_size, sns_size;
int32_t(*read_32bit)(off_t, STREAMFILE*);
int16_t(*read_16bit)(off_t, STREAMFILE*);
STREAMFILE *musFile = NULL;
VGMSTREAM *vgmstream = NULL;
int target_stream = streamFile->stream_index;
/* check extension */
if (!check_extensions(streamFile, "mpf"))
goto fail;
/* detect endianness */
if (read_32bitBE(0x00, streamFile) == 0x50464478) { /* "PFDx" */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else if (read_32bitBE(0x00, streamFile) == 0x78444650) { /* "xDFP" */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
} else {
goto fail;
}
musFile = open_streamfile_by_ext(streamFile, "mus");
if (!musFile) goto fail;
/* MPF format is unchanged but we don't really care about its contents since
* MUS conveniently contains sound offset table */
version = read_8bit(0x04, streamFile);
sub_version = read_8bit(0x05, streamFile);
if (version != 0x05 || sub_version != 0x03) goto fail;
/* number of files is always little endian */
num_sounds = read_32bitLE(0x04, musFile);
table_offset = 0x28;
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds)
goto fail;
/*
* 0x00: hash?
* 0x04: index
* 0x06: zero
* 0x08: SNR offset
* 0x0c: SNS offset
* 0x10: SNR size
* 0x14: SNS size
* 0x18: zero
*/
entry_offset = table_offset + (target_stream - 1) * 0x1c;
snr_offset = read_32bit(entry_offset + 0x08, musFile) * 0x10;
sns_offset = read_32bit(entry_offset + 0x0c, musFile) * 0x80;
snr_size = read_32bit(entry_offset + 0x10, musFile);
sns_size = read_32bit(entry_offset + 0x14, musFile);
block_id = read_8bit(sns_offset, musFile);
if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END)
goto fail;
vgmstream = init_vgmstream_eaaudiocore_header(musFile, musFile, snr_offset, sns_offset, meta_EA_SNR_SNS);
if (!vgmstream)
goto fail;
vgmstream->num_streams = num_sounds;
vgmstream->stream_size = sns_size;
close_streamfile(musFile);
return vgmstream;
fail:
close_streamfile(musFile);
return NULL;
}
/* ************************************************************************* */
typedef struct {
@ -526,7 +606,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
/* get loops (fairly involved due to the multiple layouts and mutant streamfiles)
* full loops aren't too uncommon [Dead Space (PC) stream sfx/ambiance, FIFA 98 (PS3) RAM sfx],
* while actual looping is very rare [Need for Speed: World (PC)] */
* while actual looping is very rare [Need for Speed: World (PC)-EAL3, The Simpsons Game (X360)-EAXMA] */
if (eaac.flags & EAAC_FLAG_LOOPED) {
eaac.loop_flag = 1;
eaac.loop_start = read_32bitBE(header_offset+0x08, streamHead);
@ -545,9 +625,12 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
}
//todo need more cases to test how layout/streamfiles react
if (eaac.loop_start > 0 && !(eaac.codec == EAAC_CODEC_EALAYER3_V1 ||
eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM || eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE)) {
VGM_LOG("EA EAAC: unknown actual looping for non-EALayer3\n");
if (eaac.loop_start > 0 && !(
eaac.codec == EAAC_CODEC_EALAYER3_V1 ||
eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM ||
eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE ||
eaac.codec == EAAC_CODEC_EAXMA)) {
VGM_LOG("EA EAAC: unknown actual looping for codec %x\n", eaac.codec);
goto fail;
}
}
@ -586,10 +669,22 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
#ifdef VGM_USE_FFMPEG
case EAAC_CODEC_EAXMA: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */
/* special (if hacky) loop handling, see comments */
if (eaac.streamed && eaac.loop_start > 0) {
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
vgmstream->coding_type = data->segments[0]->coding_type;
vgmstream->layout_type = layout_segmented;
}
else {
vgmstream->layout_data = build_layered_eaaudiocore_eaxma(streamData, &eaac);
if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_layered;
}
break;
}
#endif
@ -612,7 +707,8 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
start_offset = 0x00; /* must point to the custom streamfile's beginning */
if (eaac.streamed && eaac.loop_start > 0) { /* special (if hacky) loop handling, see comments */
/* special (if hacky) loop handling, see comments */
if (eaac.streamed && eaac.loop_start > 0) {
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
@ -664,10 +760,47 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
}
#endif
case EAAC_CODEC_EASPEEX: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */
/* TODO */
case EAAC_CODEC_EAOPUS: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */
/* TODO */
#ifdef VGM_USE_MPEG
case EAAC_CODEC_EAMP3: { /* "EM30"?: EAMP3 [Need for Speed 2015 (PS4)] */
mpeg_custom_config cfg = {0};
start_offset = 0x00; /* must point to the custom streamfile's beginning */
temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset);
if (!temp_streamFile) goto fail;
vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAMP3, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
break;
}
#endif
#ifdef VGM_USE_FFMPEG
case EAAC_CODEC_EAOPUS: { /* EAOpus (unknown FourCC) [FIFA 17 (PC), FIFA 19 (Switch)]*/
int skip = 0;
size_t data_size;
/* We'll remove EA blocks and pass raw data to FFmpeg Opus decoder */
temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset);
if (!temp_streamFile) goto fail;
skip = ea_opus_get_encoder_delay(0x00, temp_streamFile);
data_size = get_streamfile_size(temp_streamFile);
vgmstream->codec_data = init_ffmpeg_ea_opus(temp_streamFile, 0x00,data_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
case EAAC_CODEC_EASPEEX: /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ //todo
default:
VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec);
goto fail;
@ -696,17 +829,13 @@ static size_t get_snr_size(STREAMFILE *streamFile, off_t offset) {
/* Actual looping uses 2 block sections, separated by a block end flag *and* padded.
*
* We use the segmented layout, since the eaac_streamfile doesn't handle padding properly ATM
* (getting EALayer3 frame sizes + skip sizes can be fairly involved), plus seems likely
* that after a block end the decoder needs to be reset (not possible from a streamfile).
*
* Or could fix the blocked_layout+L32P bug, though that involves a lot of rewrites.
* So this is the simplest, surest way ATM (if very ugly). */
* We use the segmented layout, since the eaac_streamfile doesn't handle padding,
* and the segments seem fully separate (so even skipping would probably decode wrong). */
// todo consider better ways to handle this once more looped files for other codecs are found
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac) {
segmented_layout_data *data = NULL;
STREAMFILE* temp_streamFile[2] = {0};
off_t offsets[2] = { eaac->stream_offset, eaac->loop_offset };
off_t offsets[2] = { eaac->stream_offset, eaac->stream_offset + eaac->loop_offset };
int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start};
int segment_count = 2; /* intro/loop */
int i;
@ -717,9 +846,6 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st
if (!data) goto fail;
for (i = 0; i < segment_count; i++) {
temp_streamFile[i] = setup_eaac_streamfile(streamData, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]);
if (!temp_streamFile[i]) goto fail;
data->segments[i] = allocate_vgmstream(eaac->channels, 0);
if (!data->segments[i]) goto fail;
data->segments[i]->sample_rate = eaac->sample_rate;
@ -727,6 +853,22 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st
//data->segments[i]->meta_type = eaac->meta_type; /* bleh */
switch(eaac->codec) {
#ifdef VGM_USE_FFMPEG
case EAAC_CODEC_EAXMA: {
eaac_header temp_eaac = *eaac; /* equivalent to memcpy... I think */
temp_eaac.loop_flag = 0;
temp_eaac.num_samples = num_samples[i];
temp_eaac.stream_offset = offsets[i];
/* layers inside segments, how trippy */
data->segments[i]->layout_data = build_layered_eaaudiocore_eaxma(streamData, &temp_eaac);
if (!data->segments[i]->layout_data) goto fail;
data->segments[i]->coding_type = coding_FFmpeg;
data->segments[i]->layout_type = layout_layered;
break;
}
#endif
#ifdef VGM_USE_MPEG
case EAAC_CODEC_EALAYER3_V1:
case EAAC_CODEC_EALAYER3_V2_PCM:
@ -734,6 +876,9 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st
mpeg_custom_config cfg = {0};
mpeg_custom_t type = (eaac->codec == 0x05 ? MPEG_EAL31b : (eaac->codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
temp_streamFile[i] = setup_eaac_streamfile(streamData, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]);
if (!temp_streamFile[i]) goto fail;
data->segments[i]->codec_data = init_mpeg_custom(temp_streamFile[i], 0x00, &data->segments[i]->coding_type, eaac->channels, type, &cfg);
if (!data->segments[i]->codec_data) goto fail;
data->segments[i]->layout_type = layout_none;
@ -748,7 +893,6 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st
goto fail;
}
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data))
goto fail;
return data;
@ -804,6 +948,8 @@ static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamDa
data->layers[i]->coding_type = coding_FFmpeg;
data->layers[i]->layout_type = layout_none;
xma_fix_raw_samples(data->layers[i], temp_streamFile, 0x00,stream_size, 0, 0,0); /* samples are ok? */
}
#else
goto fail;
@ -814,7 +960,6 @@ static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamDa
}
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
close_streamfile(temp_streamFile);

View file

@ -85,6 +85,8 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
case 0x05: /* EALayer3 v1 */
case 0x06: /* EALayer3 v2 "PCM" */
case 0x07: /* EALayer3 v2 "Spike" */
case 0x0b: /* EAMP3 */
case 0x0c: /* EAOpus */
data->skip_size = 0x08;
data->data_size = data->block_size - data->skip_size;
break;
@ -93,12 +95,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
data->skip_size = 0x08;
data->data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* also block_size - 0x08 */
break;
#if 0
case 0x0c: /* EA Opus */
data->skip_size = 0x08;
data->data_size = data->block_size - data->skip_size;
break;
#endif
default:
return total_read;
}
@ -178,6 +175,9 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
block_flag = (uint8_t)read_8bit(physical_offset+0x00,streamfile);
block_size = read_32bitBE(physical_offset+0x00,streamfile) & 0x00FFFFFF;
if (block_size == 0)
break; /* bad data */
if (data->version == 0 && block_flag != 0x00 && block_flag != 0x80)
break; /* unknown block */
@ -206,24 +206,14 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
case 0x05: /* EALayer3 v1 */
case 0x06: /* EALayer3 v2 "PCM" */
case 0x07: /* EALayer3 v2 "Spike" */
case 0x0b: /* EAMP3 */
case 0x0c: /* EAOpus */
data_size = block_size - 0x08;
break;
case 0x0a: /* EATrax */
data_size = read_32bitBE(physical_offset+0x04,streamfile); /* also block_size - 0x08 */
break;
#if 0
case 0x0c: { /* EAOpus */
size_t done;
data_size = 0;
while (done < block_size - 0x08) {
size_t packet_size = read_16bitBE(physical_offset+0x08+done,streamfile);
done += 0x02 + packet_size;
data_size = 0x1a + packet_size; /* OggS page per Opus packet */
}
break;
}
#endif
default:
return 0;
@ -251,7 +241,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
* - EA-XMA: deflated XMA in multistreams (separate 1/2ch packets)
* - EALayer3: MPEG granule 1 can go in the next block (in V2"P" mainly, others could use layout blocked_sns)
* - EATrax: ATRAC9 frames can be split between blooks
* - EAOpus:
* - EAOpus: multiple Opus packets of frame size + Opus data per block
*/
static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, int streamed, int stream_number, int stream_count, off_t stream_offset) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;

View file

@ -5,24 +5,24 @@
/* 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 // ~PS era
#define EA_VERSION_V3 0x03 // ~PS2 era
#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 (unasigned values seem internal only) */
#define EA_PLATFORM_GENERIC -1 // typically Wii/X360/PS3/videos
/* 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 // reused later for Wii
#define EA_PLATFORM_GC_WII 0x06
#define EA_PLATFORM_XBOX 0x07
#define EA_PLATFORM_X360 0x09 // also "Xenon"
#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_3DS 0x14
/* codec constants (undefined are probably reserved, ie.- sx.exe encodes PCM24/DVI but no platform decodes them) */
@ -62,13 +62,18 @@
#define EA_BLOCKID_LOC_DATA 0x53440000 /* "SD" */
#define EA_BLOCKID_LOC_END 0x53450000 /* "SE" */
#define EA_BLOCKID_LOC_EN 0x0000454E
#define EA_BLOCKID_LOC_FR 0x00004652
#define EA_BLOCKID_LOC_GE 0x00004745
#define EA_BLOCKID_LOC_IT 0x00004954
#define EA_BLOCKID_LOC_SP 0x00005350
#define EA_BLOCKID_LOC_RU 0x00005255
#define EA_BLOCKID_LOC_JA 0x00004A41
#define EA_BLOCKID_LOC_EN 0x0000454E /* English */
#define EA_BLOCKID_LOC_FR 0x00004652 /* French */
#define EA_BLOCKID_LOC_GE 0x00004745 /* German, older */
#define EA_BLOCKID_LOC_DE 0x00004445 /* German, newer */
#define EA_BLOCKID_LOC_IT 0x00004954 /* Italian */
#define EA_BLOCKID_LOC_SP 0x00005350 /* Castilian Spanish, older */
#define EA_BLOCKID_LOC_ES 0x00004553 /* Castilian Spanish, newer */
#define EA_BLOCKID_LOC_MX 0x00004D58 /* Mexican Spanish */
#define EA_BLOCKID_LOC_RU 0x00005255 /* Russian */
#define EA_BLOCKID_LOC_JA 0x00004A41 /* Japanese, older */
#define EA_BLOCKID_LOC_JP 0x00004A50 /* Japanese, newer */
#define EA_BLOCKID_LOC_PL 0x0000504C /* Polish */
#define EA_BNK_HEADER_LE 0x424E4B6C /* "BNKl" */
#define EA_BNK_HEADER_BE 0x424E4B62 /* "BNKb" */
@ -97,19 +102,34 @@ typedef struct {
int codec_config;
} ea_header;
static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams);
static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int total_streams);
static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone);
static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int is_embedded);
static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length, int bnk_version);
static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset);
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream);
static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int total_streams);
static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int standalone);
static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream, int standalone);
/* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */
VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
/* check extension; exts don't seem enforced by EA's tools, but usually:
* STR/ASF/MUS ~early, EAM ~mid, SNG/AUD ~late, rest uncommon/one game (ex. STRM: MySims Kingdom Wii) */
if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm,trj,trm"))
/* check extension */
/* they don't seem enforced by EA's tools but usually:
* .asf: ~early [ex. Need for Speed (PC)]
* .str: ~early [ex. FIFA 2002 (PS1)]
* .eam: ~mid (fake?)
* .exa: ~mid [ex. 007 - From Russia with Love]
* .sng: ~late (fake?)
* .aud: ~late [ex. FIFA 14 (3DS)]
* .strm: MySims Kingdom (Wii)
* .stm: FIFA 12 (3DS)
* .sx/xa: fake?
* .hab: GoldenEye - Rogue Agent (inside .big)
* .xsf: 007 - Agent Under Fire (Xbox)
* .gsf: 007 - Everything or Nothing (GC)
* .mus: map/mpf+mus only?
* (extensionless): SSX (PS2) (inside .big) */
if (!check_extensions(streamFile,"asf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,"))
goto fail;
/* check header */
@ -117,16 +137,21 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_DE) && /* "SHDE" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_ES) && /* "SHES" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_MX) && /* "SHMX" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA)) /* "SHJA" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA) && /* "SHJA" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JP) && /* "SHJP" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_PL)) /* "SHPL" */
goto fail;
/* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end.
* Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language=EN/FR/GE/IT/SP/RU/JA).
* Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language).
* The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */
return parse_schl_block(streamFile, 0x00, 0);
return parse_schl_block(streamFile, 0x00, 1);
fail:
return NULL;
@ -137,8 +162,12 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
off_t offset;
/* check extension */
/* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */
if (!check_extensions(streamFile,"bnk,sdt,mus"))
/* .bnk: common
* .sdt: Harry Potter games
* .mus: streams/jingles (rare)
* .abk: GoldenEye - Rogue Agent
* .ast: FIFA 2004 (inside .big) */
if (!check_extensions(streamFile,"bnk,sdt,mus,abk,ast"))
goto fail;
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
@ -261,7 +290,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
goto fail;
bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1;
vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, total_sounds);
vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, 1);
if (!vgmstream)
goto fail;
break;
@ -280,7 +309,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER)
goto fail;
vgmstream = parse_schl_block(astData, schl_offset, total_sounds);
vgmstream = parse_schl_block(astData, schl_offset, 0);
if (!vgmstream)
goto fail;
break;
@ -290,6 +319,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
break;
}
vgmstream->num_streams = total_sounds;
close_streamfile(astData);
return vgmstream;
@ -339,10 +369,11 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) {
if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER)
goto fail;
vgmstream = parse_schl_block(datFile, schl_offset, total_sounds);
vgmstream = parse_schl_block(datFile, schl_offset, 0);
if (!vgmstream)
goto fail;
vgmstream->num_streams = total_sounds;
close_streamfile(datFile);
return vgmstream;
@ -351,97 +382,279 @@ fail:
return NULL;
}
/* EA IDX/BIG combo - basically a set of HDR/DAT compiled into one file */
VGMSTREAM * init_vgmstream_ea_idx_big(STREAMFILE *streamFile) {
int target_stream = streamFile->stream_index, total_sounds, subsound_index;
uint32_t i, num_hdr;
uint16_t hdr_id, hdr_subid;
uint8_t userdata_size, hdr_sounds;
off_t entry_offset, hdr_offset, base_offset, schl_offset, offset_mult;
//size_t hdr_size;
char stream_name[STREAM_NAME_SIZE];
STREAMFILE *bigFile = NULL;
VGMSTREAM *vgmstream = NULL;
int32_t (*read_32bit)(off_t,STREAMFILE*);
int16_t (*read_16bit)(off_t,STREAMFILE*);
/* seems to always start with 0x00000001 */
if (read_32bitLE(0x00, streamFile) != 0x00000001 &&
read_32bitBE(0x00, streamFile) != 0x00000001)
goto fail;
/* open map/mpf+mus pairs that aren't exact pairs, since EA's games can load any combo */
static STREAMFILE * open_mapfile_pair(STREAMFILE *streamFile) {
static const char *const mapfile_pairs[][2] = {
/* standard cases, replace map part with mus part (from the end to preserve prefixes) */
{"MUS_CTRL.MPF","MUS_STR.MUS"}, /* GoldenEye - Rogue Agent (PS2) */
{"mus_ctrl.mpf","mus_str.mus"}, /* GoldenEye - Rogue Agent (others) */
{".mpf","_main.mus"}, /* 007 - Everything or Nothing (GC) */
/* hack when when multiple maps point to the same mus, uses name before "+"
* ex. ZZZTR00A.TRJ+ZTR00PGR.MAP or ZZZTR00A.TRJ+ZTR00R0A.MAP both point to ZZZTR00A.TRJ */
{"+",""}, /* Need for Speed III (PS1) */
};
STREAMFILE *musFile = NULL;
char file_name[PATH_LIMIT];
int pair_count = (sizeof(mapfile_pairs)/sizeof(mapfile_pairs[0]));
int i;
size_t file_len, map_len;
bigFile = open_streamfile_by_ext(streamFile, "big");
if (!bigFile)
goto fail;
get_streamfile_filename(streamFile, file_name, PATH_LIMIT);
file_len = strlen(file_name);
if (read_32bitBE(0x00, bigFile) != EA_BLOCKID_HEADER)
goto fail;
for (i = 0; i < pair_count; i++) {
const char *map_name = mapfile_pairs[i][0];
const char *mus_name = mapfile_pairs[i][1];
map_len = strlen(map_name);
/* use number of files for endianness check */
if (guess_endianness32bit(0x04,streamFile)) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
if (map_name[0] == '+') {
/* use name before "+" */
char *mod_name = strchr(file_name, '+');
if (mod_name == NULL)
continue;
mod_name[0] = '\0';
}
else {
/* replace map_name with expected mus_name */
if (file_len < map_len)
continue;
if (strncmp(file_name+(file_len - map_len), map_name, map_len) != 0)
continue;
file_name[file_len - map_len] = '\0';
strcat(file_name, mus_name);
}
num_hdr = read_32bit(0x04, streamFile);
if (read_32bit(0x54,streamFile) != num_hdr)
musFile = open_streamfile_by_filename(streamFile, file_name);
if (musFile) return musFile;
get_streamfile_filename(streamFile, file_name, PATH_LIMIT); /* reset for next loop */
}
return NULL;
}
/* EA MAP/MUS combo - used in older games for interactive music (for EA's PathFinder tool) */
VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) {
uint8_t version, num_sounds, num_userdata, userdata_size;
off_t section_offset, schl_offset;
STREAMFILE *musFile = NULL;
VGMSTREAM *vgmstream = NULL;
int target_stream = streamFile->stream_index;
/* check extension */
if (!check_extensions(streamFile, "map,lin,mpf"))
goto fail;
/* always big endian */
if (read_32bitBE(0x00, streamFile) != 0x50464478) /* "PFDx" */
goto fail;
version = read_8bit(0x04, streamFile);
if (version > 1) goto fail;
musFile = open_streamfile_by_ext(streamFile, "mus");
if (!musFile) {
musFile = open_mapfile_pair(streamFile);
if (!musFile) goto fail;
}
/*
* 0x04: ???
* 0x05: intro segment
* 0x06: number of segments
* 0x07: userdata entry size (incorrect?)
* 0x08: three zeroes
* 0x0b: number of userdata entries
* 0x0c: section 1 start
*/
num_sounds = read_8bit(0x06, streamFile);
userdata_size = read_8bit(0x07, streamFile);
num_userdata = read_8bit(0x0b, streamFile);
section_offset = 0x0c;
/* section 1: contains information about segment playback order */
section_offset += num_sounds * 0x1c;
/* section 2: userdata, specific to game and track */
section_offset += num_userdata * userdata_size;
if (target_stream == 0) target_stream = 1;
schl_offset = 0;
total_sounds = 0;
schl_offset = 0xFFFFFFFF;
for (i = 0; i < num_hdr; i++) {
entry_offset = 0x58 + 0x10 * i;
//hdr_size = read_32bit(entry_offset + 0x04, streamFile);
hdr_offset = read_32bit(entry_offset + 0x08, streamFile);
base_offset = read_32bit(entry_offset + 0x0C, streamFile);
hdr_id = read_16bit(hdr_offset + 0x00, streamFile);
hdr_subid = read_16bit(hdr_offset + 0x02, streamFile);
userdata_size = read_8bit(hdr_offset + 0x04, streamFile) & 0x0F;
hdr_sounds = read_8bit(hdr_offset + 0x05, streamFile);
offset_mult = (off_t)read_8bit(hdr_offset + 0x07, streamFile) * 0x0100 + 0x0100;
if (target_stream > total_sounds && target_stream <= total_sounds + hdr_sounds) {
schl_offset = base_offset + (off_t)read_16bitBE(hdr_offset + 0x0C + (0x02+userdata_size) * (target_stream-total_sounds-1), streamFile) * offset_mult;
subsound_index = target_stream - total_sounds;
/* There are no filenames but we can add IDs to stream name for better organization */
if (hdr_subid != 0xFFFF)
snprintf(stream_name, STREAM_NAME_SIZE, "%03d_%02d_%d", hdr_id, hdr_subid, subsound_index);
else
snprintf(stream_name, STREAM_NAME_SIZE, "%03d_%d", hdr_id, subsound_index);
}
total_sounds += hdr_sounds;
}
if (schl_offset == 0xFFFFFFFF)
if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds)
goto fail;
if (read_32bitBE(schl_offset, bigFile) != EA_BLOCKID_HEADER)
/* section 3: sound offset table */
schl_offset = read_32bitBE(section_offset + (target_stream - 1) * 0x04, streamFile);
if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER)
goto fail;
vgmstream = parse_schl_block(bigFile, schl_offset, total_sounds);
vgmstream = parse_schl_block(musFile, schl_offset, 0);
if (!vgmstream)
goto fail;
strncpy(vgmstream->stream_name, stream_name, STREAM_NAME_SIZE);
close_streamfile(bigFile);
vgmstream->num_streams = num_sounds;
close_streamfile(musFile);
return vgmstream;
fail:
close_streamfile(bigFile);
close_streamfile(musFile);
return NULL;
}
/* EA MPF/MUS combo - used in newer 6th gen games for interactive music (for EA's PathFinder tool) */
VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) {
off_t section_offset, entry_offset, subentry_num, eof_offset, off_mult, schl_offset;
size_t sec2_size;
uint16_t sec1_num;
uint8_t version, sub_version, sec2_num, sec3_num, sec4_num;
int32_t(*read_32bit)(off_t, STREAMFILE*);
int16_t(*read_16bit)(off_t, STREAMFILE*);
STREAMFILE *musFile = NULL;
VGMSTREAM *vgmstream = NULL;
int target_stream = streamFile->stream_index, total_streams, big_endian;
/* check extension */
if (!check_extensions(streamFile, "mpf"))
goto fail;
/* detect endianness */
if (read_32bitBE(0x00, streamFile) == 0x50464478) { /* "PFDx" */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
big_endian = 1;
} else if (read_32bitBE(0x00, streamFile) == 0x78444650) { /* "xDFP" */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
big_endian = 0;
} else {
goto fail;
}
version = read_8bit(0x04, streamFile);
sub_version = read_8bit(0x05, streamFile);
if (version < 3 || version > 5) goto fail;
if (version == 5 && sub_version > 2) goto fail; /* newer version using SNR/SNS */
musFile = open_streamfile_by_ext(streamFile, "mus");
if (!musFile) {
musFile = open_mapfile_pair(streamFile);
if (!musFile) goto fail;
}
/* HACK: number of sub-entries 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 */
if (version == 3 && sub_version == 1) { /* SSX Tricky */
/* we need to go through the first two sections to find sound table */
sec1_num = read_16bit(0x12, streamFile);
sec2_size = read_8bit(0x0e, streamFile);
sec2_num = read_8bit(0x0f, streamFile);
sec3_num = read_8bit(0x10, streamFile);
sec4_num = read_8bit(0x11, streamFile);
/* get the last entry offset */
section_offset = 0x24;
entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
subentry_num = read_8bit(entry_offset + 0x0b, streamFile);
section_offset = entry_offset + 0x0c + subentry_num * 0x04;
section_offset += align_size_to_block(sec2_num * sec2_size, 0x04);
section_offset += sec3_num * 0x04;
section_offset += sec4_num * 0x04;
entry_offset = read_32bit(section_offset, streamFile) * 0x04;
section_offset = read_32bit(entry_offset + 0x00, streamFile) * 0x04;
eof_offset = get_streamfile_size(streamFile);
total_streams = (eof_offset - section_offset) / 0x08;
off_mult = 0x04;
} else if (version == 3 && sub_version == 4) { /* Harry Potter and the Chamber of Secrets */
sec1_num = read_16bit(0x12, streamFile);
sec2_size = read_8bit(0x0e, streamFile);
sec2_num = read_8bit(0x0f, streamFile);
sec3_num = read_8bit(0x10, streamFile);
sec4_num = read_8bit(0x11, streamFile);
/* get the last entry offset */
section_offset = 0x24;
entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
if (big_endian) {
subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 19) & 0xFF;
} else {
subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 16) & 0xFF;
}
section_offset = entry_offset + 0x0c + subentry_num * 0x04;
section_offset += align_size_to_block(sec2_num * sec2_size, 0x04);
section_offset += sec3_num * 0x04;
section_offset += sec4_num * 0x04;
entry_offset = read_32bit(section_offset, streamFile) * 0x04;
section_offset = read_32bit(entry_offset + 0x00, streamFile) * 0x04;
eof_offset = read_32bit(entry_offset + 0x04, streamFile) * 0x04;
total_streams = (eof_offset - section_offset) / 0x08;
off_mult = 0x04;
} else if (version == 4) { /* SSX 3, Need for Speed: Underground 2 */
/* we need to go through the first two sections to find sound table */
sec1_num = read_16bit(0x12, streamFile);
sec2_num = read_8bit(0x0f, streamFile);
/* get the last entry offset */
section_offset = 0x20;
entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
if (big_endian) {
subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 15) & 0xFF;
} else {
subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 20) & 0xFF;
}
section_offset = entry_offset + 0x10 + subentry_num * 0x04;
entry_offset = read_16bit(section_offset + (sec2_num - 1) * 0x02, streamFile) * 0x04;
if (big_endian) {
subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 10) & 0xFF;
} else {
subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 8) & 0xFF;
}
section_offset = entry_offset + 0x10 + subentry_num * 0x10;
entry_offset = read_32bit(section_offset, streamFile) * 0x04;
section_offset = read_32bit(entry_offset + 0x00, streamFile) * 0x04;
eof_offset = read_32bit(entry_offset + 0x04, streamFile) * 0x04;
total_streams = (eof_offset - section_offset) / 0x08;
off_mult = 0x80;
} else if (version == 5) { /* Need for Speed: Most Wanted, Need for Speed: Carbon */
section_offset = read_32bit(0x34, streamFile);
eof_offset = read_32bit(0x38, streamFile);
total_streams = (eof_offset - section_offset) / 0x08;
off_mult = 0x80;
} else {
goto fail;
}
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || total_streams == 0 || target_stream > total_streams)
goto fail;
schl_offset = read_32bit(section_offset + (target_stream - 1) * 0x08 + 0x00, streamFile) * off_mult;
if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER)
goto fail;
vgmstream = parse_schl_block(musFile, schl_offset, 0);
if (!vgmstream)
goto fail;
vgmstream->num_streams = total_streams;
close_streamfile(musFile);
return vgmstream;
fail:
close_streamfile(musFile);
return NULL;
}
/* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */
static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams) {
static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone) {
off_t start_offset, header_offset;
size_t header_size;
ea_header ea = { 0 };
@ -462,19 +675,20 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int to
start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */
/* rest is common */
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, total_streams);
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, standalone);
fail:
return NULL;
}
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int total_streams) {
static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int is_embedded) {
off_t header_offset, start_offset, test_offset, table_offset;
size_t header_size;
ea_header ea = {0};
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
VGMSTREAM *vgmstream = NULL;
int i, bnk_version;
int total_bnk_sounds, real_bnk_sounds = 0;
@ -525,7 +739,7 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta
real_bnk_sounds++;
/* ABK points at absolute indexes, i.e. with dummies included */
if (total_streams != 0) {
if (is_embedded != 0) {
if (target_stream - 1 == i)
header_offset = offset + table_offset + 0x04 * i + test_offset;
}
@ -552,14 +766,20 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta
start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */
/* rest is common */
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams ? total_streams : real_bnk_sounds);
vgmstream = init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, 0);
if (!vgmstream) goto fail;
if (!is_embedded) {
vgmstream->num_streams = real_bnk_sounds;
}
return vgmstream;
fail:
return NULL;
}
/* inits VGMSTREAM from a EA header */
static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header * ea, off_t start_offset, int bnk_version, int total_streams) {
static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header * ea, off_t start_offset, int bnk_version, int standalone) {
VGMSTREAM * vgmstream = NULL;
int i, ch;
int is_bnk = bnk_version;
@ -606,9 +826,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
vgmstream->layout_type = layout_blocked_ea_schl;
}
vgmstream->num_streams = total_streams;
//vgmstream->stream_size = ; //todo needed for kbps info
/* 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). */
@ -790,15 +1007,11 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
vgmstream->ch[i].offset = ea->offsets[i];
}
}
/* TODO: Figure out how to get stream size for BNK sounds */
}
else if (vgmstream->layout_type == layout_blocked_ea_schl) {
/* regular SCHls, except ATRAC3plus */
if (total_streams == 0) {
/* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this */
int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream);
if (total_samples > vgmstream->num_samples)
vgmstream->num_samples = total_samples;
}
else {
update_ea_stream_size_and_samples(streamFile, start_offset, vgmstream, standalone);
}
return vgmstream;
@ -1188,38 +1401,52 @@ fail:
return 0;
}
/* Get total samples by parsing block headers, needed when multiple files are stitched together.
static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream, int standalone) {
uint32_t block_id;
int32_t num_samples;
size_t stream_size, file_size;
int multiple_schl;
stream_size = 0, num_samples = 0, multiple_schl = 0;
file_size = get_streamfile_size(streamFile);
vgmstream->next_block_offset = start_offset;
while (vgmstream->next_block_offset < file_size) {
block_update_ea_schl(vgmstream->next_block_offset, vgmstream);
block_id = read_32bitBE(vgmstream->current_block_offset + 0x00, streamFile);
if (block_id == EA_BLOCKID_END) { /* banks should never contain movie "SHxx" */
if (!standalone)
break;
}
else if (block_id == EA_BLOCKID_HEADER) { /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
multiple_schl = 1;
}
/* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this.
* Get total samples by parsing block headers, needed when multiple files are stitched together.
* Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped
* music (.map/lin). Subfiles always share header, except num_samples. */
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream) {
int num_samples = 0;
int multiple_schl = 0;
/* calc num_samples as playable data size varies between files/blocks */
{
vgmstream->next_block_offset = start_offset;
do {
uint32_t block_id = read_32bitBE(vgmstream->next_block_offset+0x00,streamFile);
if (block_id == EA_BLOCKID_HEADER) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
multiple_schl = 1;
block_update_ea_schl(vgmstream->next_block_offset,vgmstream);
num_samples += vgmstream->current_block_samples;
}
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
/* reset after getting samples */
block_update(start_offset,vgmstream);
/* Stream size is almost never provided in bank files so we have to calc it manually */
if (vgmstream->current_block_samples != 0) {
stream_size += vgmstream->next_block_offset - vgmstream->current_block_offset - 0x0c;
}
}
/* reset once we're done */
block_update(start_offset, vgmstream);
/* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */
if (multiple_schl) {
;VGM_LOG("EA SCHl: multiple SCHl found\n");
return num_samples;
if (standalone && multiple_schl) {
VGM_LOG("EA SCHl: multiple SCHl found\n");
if (num_samples > vgmstream->num_samples) {
vgmstream->num_samples = num_samples;
}
else {
return 0;
}
vgmstream->stream_size = stream_size;
}
/* find data start offset inside the first SCDl; not very elegant but oh well */
@ -1243,10 +1470,15 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_EN: /* "SDEN" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_FR: /* "SDFR" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_GE: /* "SDGE" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_DE: /* "SDDE" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_IT: /* "SDIT" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_SP: /* "SDSP" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_ES: /* "SDES" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_MX: /* "SDMX" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_RU: /* "SDRU" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_JA: /* "SDJA" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_JP: /* "SDJP" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_PL: /* "SDPL" */
offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */
return block_offset + 0x0c + ea->channels*0x04 + offset;
case 0x00000000:

View file

@ -1,179 +0,0 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../util.h"
/* EMFF - Eidos Music File Format (PS2),
Legacy of Kain - Defiance, possibly more... */
VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
int frequency;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("emff",filename_extension(filename))) goto fail;
/* do some checks on the file, cause we have no magic words to check the header...
it seems if 0x800 and 0x804 = 0 then the file has only audio, if 0x800 = 1
it has a text section, if both are 1 it's video with a text section included... */
if (read_32bitBE(0x800,streamFile) == 0x01000000 || /* "0x01000000" */
read_32bitBE(0x804,streamFile) == 0x01000000) /* "0x01000000" */
goto fail;
frequency = read_32bitLE(0x0,streamFile);
channel_count = read_32bitLE(0xC,streamFile);
if (frequency > 48000 ||
channel_count > 8) {
goto fail;
}
loop_flag = (read_32bitLE(0x4,streamFile) != 0xFFFFFFFF);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x800;
vgmstream->sample_rate = frequency;
vgmstream->channels = channel_count;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_emff_ps2;
vgmstream->interleave_block_size = 0x10;
vgmstream->meta_type = meta_EMFF_PS2;
/* open the file for reading */
{
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;
}
}
/* Calc num_samples */
block_update_emff_ps2(start_offset,vgmstream);
vgmstream->num_samples = read_32bitLE(0x8,streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitLE(0x28,streamFile)-start_offset)*28/16/channel_count;
vgmstream->loop_end_sample = read_32bitLE(0x8,streamFile);
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
/* EMFF - Eidos Music File Format (NGC/WII),
found in Tomb Raider Legend/Anniversary/Underworld, possibly more... */
VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
int frequency;
int i;
int j;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("emff",filename_extension(filename))) goto fail;
/* do some checks on the file, cause we have no magic words to check the header...
it seems if 0x800 and 0x804 = 0 then the file has only audio, if 0x800 = 1
it has a text section, if both are 1 it's video with a text section included... */
if (read_32bitBE(0x800,streamFile) == 0x00000001 || /* "0x00000001" */
read_32bitBE(0x804,streamFile) == 0x00000001) /* "0x00000001" */
goto fail;
frequency = read_32bitBE(0x0,streamFile);
channel_count = read_32bitBE(0xC,streamFile);
if (frequency > 48000 ||
channel_count > 8) {
goto fail;
}
loop_flag = (read_32bitBE(0x4,streamFile) != 0xFFFFFFFF);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x800;
vgmstream->sample_rate = frequency;
vgmstream->channels = channel_count;
vgmstream->coding_type = coding_NGC_DSP;
/* Retrieving coefs and loops, depending on the file layout... */
/* Found in Tomb Raider - Legend for GameCube */
if (read_32bitBE(0xC8,streamFile) > 0x0) {
off_t coef_table[8] = {0xC8,0xF6,0x124,0x152,0x180,0x1AE,0x1DC,0x20A};
for (j=0;j<vgmstream->channels;j++) {
for (i=0;i<16;i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
/* Found in Tomb Raider - Anniversary for WII */
} else if (read_32bitBE(0xCC,streamFile) > 0x0) {
off_t coef_table[8] = {0xCC,0xFA,0x128,0x156,0x184,0x1B2,0x1E0,0x20E};
for (j=0;j<vgmstream->channels;j++) {
for (i=0;i<16;i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
/* Found in Tomb Raider - Underworld for WII */
} else if (read_32bitBE(0x2D0,streamFile) > 0x0) {
off_t coef_table[8] = {0x2D0,0x2FE,0x32C,0x35A,0x388,0x3B6,0x3E4,0x412};
for (j=0;j<vgmstream->channels;j++) {
for (i=0;i<16;i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
} else {
goto fail;
}
vgmstream->layout_type = layout_blocked_emff_ngc;
vgmstream->interleave_block_size = 0x10;
vgmstream->meta_type = meta_EMFF_NGC;
/* open the file for reading */
{
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;
}
}
/* Calc num_samples */
block_update_emff_ngc(start_offset,vgmstream);
vgmstream->num_samples = read_32bitBE(0x8,streamFile);;
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitBE(0x28,streamFile))*14/8/channel_count;
vgmstream->loop_end_sample = read_32bitBE(0x8,streamFile);
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View file

@ -0,0 +1,52 @@
#include "meta.h"
#include "../coding/coding.h"
/* .FAG - from Jackie Chan: Stuntmaster (PS1) */
VGMSTREAM * init_vgmstream_fag(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t stream_size;
int loop_flag, channel_count;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile,"fag"))
goto fail;
total_subsongs = read_32bitLE(0x00,streamFile);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
if (total_subsongs > 2)
goto fail;
loop_flag = 0;
channel_count = 2;
start_offset = read_32bitLE(0x04 + 0x04*(target_subsong-1),streamFile);
stream_size = read_32bitLE(0x04 + 0x04*total_subsongs + 0x04*(target_subsong-1),streamFile) - start_offset; /* end offset */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_FAG;
vgmstream->sample_rate = 24000;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8000;
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -363,6 +363,8 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, fsb.stream_offset,fsb.stream_size, 0, 0,0); /* samples look ok */
break;
}
#endif

View file

@ -150,6 +150,12 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
/* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */
fsb5.loop_flag = (fsb5.loop_start != 0x00);
/* ignore wrong loops in some files [Pac-Man CE2 Plus (Switch) pce2p_bgm_ajurika_*.fsb] */
if (fsb5.loop_start == 0x3c && fsb5.loop_end == 0x007F007F &&
fsb5.num_samples > fsb5.loop_end + 100000) { /* arbitrary limit */
fsb5.loop_flag = 0;
}
break;
case 0x04: /* free comment, or maybe SFX info */
break;
@ -182,7 +188,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
// /* found in some XMA2/Vorbis/FADPCM */
// break;
default:
VGM_LOG("FSB5: unknown extraflag 0x%x at %"PRIx64" + 0x04 (size 0x%x)\n", extraflag_type, (off64_t)extraflag_offset, extraflag_size);
VGM_LOG("FSB5: unknown extraflag 0x%x at %x + 0x04 (size 0x%x)\n", extraflag_type, (uint32_t)extraflag_offset, extraflag_size);
break;
}
@ -308,7 +314,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
break;
#ifdef VGM_USE_FFMPEG
case 0x0A: {/* FMOD_SOUND_FORMAT_XMA [Dark Souls 2 (X360)] */
case 0x0A: {/* FMOD_SOUND_FORMAT_XMA [Minecraft Story Mode (X360)] */
uint8_t buf[0x100];
int bytes, block_size, block_count;
@ -320,6 +326,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, fsb5.stream_offset,fsb5.stream_size, 0, 0,0); /* samples look ok */
break;
}
#endif

View file

@ -4,7 +4,6 @@
#include "../util.h"
/* known GENH types */
typedef enum {
PSX = 0, /* PSX ADPCM */
@ -31,6 +30,7 @@ typedef enum {
XMA2 = 21, /* raw XMA2 */
FFMPEG = 22, /* any headered FFmpeg format */
AC3 = 23, /* AC3/SPDIF */
PCFX = 24, /* PC-FX ADPCM */
} genh_type;
typedef struct {
@ -52,8 +52,10 @@ typedef struct {
int loop_flag;
int32_t coef[2];
int32_t coef_splitted[2];
int32_t coef_offset;
int32_t coef_spacing;
int32_t coef_split_offset;
int32_t coef_split_spacing;
int32_t coef_type;
int32_t coef_interleave_type;
int coef_big_endian;
@ -111,6 +113,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case AC3:
case FFMPEG: coding = coding_FFmpeg; break;
#endif
case PCFX: coding = coding_PCFX; break;
default:
goto fail;
}
@ -183,6 +186,14 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
}
break;
case coding_PCFX:
vgmstream->interleave_block_size = genh.interleave;
vgmstream->layout_type = layout_interleave;
if (genh.codec_mode >= 0 && genh.codec_mode <= 3)
vgmstream->codec_config = genh.codec_mode;
break;
case coding_MS_IMA:
if (!genh.interleave) goto fail; /* creates garbage */
@ -233,16 +244,16 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
for (i=0;i<vgmstream->channels;i++) {
int16_t (*read_16bit)(off_t , STREAMFILE*) = genh.coef_big_endian ? read_16bitBE : read_16bitLE;
/* normal/split coefs bit flag */
if ((genh.coef_type & 1) == 0) { /* not set: normal coefs, all 16 interleaved into one array */
/* normal/split coefs */
if ((genh.coef_type & 1) == 0) { /* normal mode */
for (j = 0; j < 16; j++) {
vgmstream->ch[i].adpcm_coef[j] = read_16bit(genh.coef[i]+j*2,streamFile);
vgmstream->ch[i].adpcm_coef[j] = read_16bit(genh.coef_offset + i*genh.coef_spacing + j*2, streamFile);
}
}
else { /* set: split coefs, 8 coefs in the main array, additional offset to 2nd array given at 0x34 for left, 0x38 for right */
else { /* split coefs, 8 coefs in the main array, additional offset to 2nd array given at 0x34 for left, 0x38 for right */
for (j = 0; j < 8; j++) {
vgmstream->ch[i].adpcm_coef[j*2]=read_16bit(genh.coef[i]+j*2,streamFile);
vgmstream->ch[i].adpcm_coef[j*2+1]=read_16bit(genh.coef_splitted[i]+j*2,streamFile);
vgmstream->ch[i].adpcm_coef[j*2] = read_16bit(genh.coef_offset + i*genh.coef_spacing + j*2, streamFile);
vgmstream->ch[i].adpcm_coef[j*2+1] = read_16bit(genh.coef_split_offset + i*genh.coef_split_spacing + j*2, streamFile);
}
}
}
@ -304,7 +315,6 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
else {
goto fail;
}
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, genh.start_offset,genh.data_size);
if ( !ffmpeg_data ) goto fail;
@ -313,8 +323,9 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
vgmstream->codec_data = ffmpeg_data;
vgmstream->layout_type = layout_none;
/* force encoder delay */
if (genh.skip_samples_mode && genh.skip_samples >= 0) {
if (genh.codec == XMA1 || genh.codec == XMA2) {
xma_fix_raw_samples(vgmstream, streamFile, genh.start_offset,genh.data_size, 0, 0,0);
} else if (genh.skip_samples_mode && genh.skip_samples >= 0) { /* force encoder delay */
ffmpeg_set_skip_samples(ffmpeg_data, genh.skip_samples);
}
@ -344,10 +355,10 @@ fail:
static int parse_genh(STREAMFILE * streamFile, genh_header * genh) {
size_t header_size;
genh->channels = read_32bitLE(0x4,streamFile);
genh->channels = read_32bitLE(0x04,streamFile);
genh->interleave = read_32bitLE(0x8,streamFile);
genh->sample_rate = read_32bitLE(0xc,streamFile);
genh->interleave = read_32bitLE(0x08,streamFile);
genh->sample_rate = read_32bitLE(0x0c,streamFile);
genh->loop_start_sample = read_32bitLE(0x10,streamFile);
genh->loop_end_sample = read_32bitLE(0x14,streamFile);
@ -359,24 +370,41 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) {
genh->start_offset = 0x800;
header_size = 0x800;
}
/* check for audio data start past header end */
if (header_size > genh->start_offset) goto fail;
genh->coef[0] = read_32bitLE(0x24,streamFile);
genh->coef[1] = read_32bitLE(0x28,streamFile);
if (header_size > genh->start_offset) /* audio data start past header end */
goto fail;
if (header_size < 0x24) /* absolute minimum for GENH */
goto fail;
/* DSP coefficients */
if (header_size >= 0x30) {
genh->coef_offset = read_32bitLE(0x24,streamFile);
if (genh->channels == 2) /* old meaning, "coef right offset" */
genh->coef_spacing = read_32bitLE(0x28,streamFile) - genh->coef_offset;
else if (genh->channels > 2) /* new meaning, "coef spacing" */
genh->coef_spacing = read_32bitLE(0x28,streamFile);
genh->coef_interleave_type = read_32bitLE(0x2C,streamFile);
}
/* DSP coefficient variants */
if (header_size >= 0x34) {
/* bit 0 flag - split coefs (2 arrays) */
/* bit 1 flag - little endian coefs (for some 3DS) */
genh->coef_type = read_32bitLE(0x30,streamFile);
genh->coef_big_endian = ((genh->coef_type & 2) == 0);
}
/* when using split coefficients, 2nd array is at: */
genh->coef_splitted[0] = read_32bitLE(0x34,streamFile);
genh->coef_splitted[1] = read_32bitLE(0x38,streamFile);
/* DSP split coefficients' 2nd array */
if (header_size >= 0x3c) {
genh->coef_split_offset = read_32bitLE(0x34,streamFile);
if (genh->channels == 2) /* old meaning, "coef right offset" */
genh->coef_split_spacing = read_32bitLE(0x38,streamFile) - genh->coef_split_offset;
else if (genh->channels > 2) /* new meaning, "coef spacing" */
genh->coef_split_spacing = read_32bitLE(0x38,streamFile);
}
/* other fields */
/* extended fields */
if (header_size >= 0x54) {
genh->num_samples = read_32bitLE(0x40,streamFile);
genh->skip_samples = read_32bitLE(0x44,streamFile); /* for FFmpeg based codecs */
genh->skip_samples_mode = read_8bit(0x48,streamFile); /* 0=autodetect, 1=force manual value @ 0x44 */
@ -386,10 +414,10 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) {
if ((genh->codec == XMA1 || genh->codec == XMA2) && genh->codec_mode==0)
genh->codec_mode = read_8bit(0x4a,streamFile);
genh->data_size = read_32bitLE(0x50,streamFile);
}
if (genh->data_size == 0)
genh->data_size = get_streamfile_size(streamFile) - genh->start_offset;
genh->num_samples = genh->num_samples > 0 ? genh->num_samples : genh->loop_end_sample;
genh->loop_flag = genh->loop_start_sample != -1;

View file

@ -12,7 +12,7 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) {
int codec_id;
/* check extensions */
/* checks */
if (!check_extensions(streamFile,"gsb"))
goto fail;
@ -116,14 +116,13 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) {
/* 0x00: fmt0x166 header (BE), 0x34: seek table */
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, datasize, streamHeader, 1);
if (bytes <= 0) goto fail;
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, 0,0); /* samples are ok */
break;
}
#endif

View file

@ -94,6 +94,8 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_none;
vgmstream->num_samples = num_samples;
xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1);
break;
}
#endif

View file

@ -21,10 +21,19 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
/* find decryption key in external file or preloaded list */
if (hca_data->info.encryptionEnabled) {
uint8_t keybuf[8];
if (read_key_file(keybuf, 8, streamFile) == 8) {
uint8_t keybuf[0x08+0x02];
size_t keysize;
keysize = read_key_file(keybuf, 0x08+0x04, streamFile);
if (keysize == 0x08) { /* standard */
keycode = (uint64_t)get_64bitBE(keybuf+0x00);
} else {
}
else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */
uint64_t key = (uint64_t)get_64bitBE(keybuf+0x00);
uint16_t sub = (uint16_t)get_16bitBE(keybuf+0x08);
keycode = key * ( ((uint64_t)sub << 16u) | ((uint16_t)~sub + 2u) );
}
else {
find_hca_key(hca_data, &keycode);
}
@ -63,46 +72,61 @@ fail:
}
/* Try to find the decryption key from a list. */
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode) {
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
unsigned long long best_keycode;
int best_score = -1;
int i;
best_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
/* find a candidate key */
for (i = 0; i < keys_length; i++) {
static inline void test_key(hca_codec_data * hca_data, uint64_t key, uint16_t subkey, int *best_score, uint64_t *best_keycode) {
int score;
unsigned long long keycode = (unsigned long long)hcakey_list[i].key;
score = test_hca_key(hca_data, keycode);
if (subkey) {
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
}
//;VGM_LOG("HCA: test key=%08x%08x, score=%i\n",
// (uint32_t)((keycode >> 32) & 0xFFFFFFFF), (uint32_t)(keycode & 0xFFFFFFFF), score);
score = test_hca_key(hca_data, (unsigned long long)key);
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
/* wrong key */
if (score < 0)
continue;
return;
/* score 0 is not trustable, update too if something better is found */
if (best_score < 0 || score < best_score || (best_score == 0 && score == 1)) {
best_score = score;
best_keycode = keycode;
}
/* best possible score */
if (score == 1) {
break;
/* update if something better is found */
if (*best_score <= 0 || (score < *best_score && score > 0)) {
*best_score = score;
*best_keycode = key;
}
}
/* Try to find the decryption key from a list. */
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode) {
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
int best_score = -1;
int i,j;
*out_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
/* find a candidate key */
for (i = 0; i < keys_length; i++) {
uint64_t key = hcakey_list[i].key;
size_t subkeys_size = hcakey_list[i].subkeys_size;
const uint16_t *subkeys = hcakey_list[i].subkeys;
if (subkeys_size > 0) {
for (j = 0; j < subkeys_size; j++) {
test_key(hca_data, key, subkeys[j], &best_score, out_keycode);
if (best_score == 1) /* best possible score */
goto done;
}
}
else {
test_key(hca_data, key, 0, &best_score, out_keycode);
if (best_score == 1) /* best possible score */
goto done;
}
}
done:
//;VGM_LOG("HCA: best key=%08x%08x (score=%i)\n",
// (uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score);
// (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score);
*out_keycode = best_keycode;
(uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
}

View file

@ -1,14 +1,23 @@
#ifndef _HCA_KEYS_H_
#define _HCA_KEYS_H_
#include "hca_keys_awb.h"
typedef struct {
uint64_t key;
uint64_t key; /* hca key or seed ('user') key */
const uint16_t *subkeys; /* scramble subkey table for seed key */
size_t subkeys_size; /* size of the derivation subkey table */
} hcakey_info;
/**
* List of known keys, extracted from the game files (mostly found in 2ch.net).
* CRI's tools expect an unsigned 64 bit number string, but keys are commonly found online in hex form.
* Keys only use 56 bits though, so the upper 8 bits can be ignored.
*
* ACB+AWB after mid 2018 use a user seed key + a scramble subkey in the AWB (normally 16b LE at 0x0e)
* to create the final HCA key, which means there is one key per AWB (so most HCA have a unique key).
* vgmstream derives the key if subkey table is provided.
*/
static const hcakey_info hcakey_list[] = {
@ -219,7 +228,7 @@ static const hcakey_info hcakey_list[] = {
// Taga Tame no Alchemist (iOS/Android)
{5047159794308}, // 00000497222AAA84
// Shin Tennis no Ouji-sama: Rising Beat (iOS/Android)
// Shin Tennis no Ouji-sama: Rising Beat (iOS/Android) voices?
{4902201417679}, // 0000047561F95FCF
// Kai-ri-Sei Million Arthur (Vita)
@ -249,6 +258,15 @@ static const hcakey_info hcakey_list[] = {
// Onsen Musume: Yunohana Kore Kushon (Android) voices
{6667}, // 0000000000001A0B
/* Dragalia Lost (Cygames) [iOS/Android] */
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
/* Libra of Precatus (Android) */
{0x6D8EFB700870FCD4}, // 6D8EFB700870FCD4
/* Mashiro Witch (Android) */
{0x55D11D3349495204}, // 55D11D3349495204
};
#endif/*_HCA_KEYS_H_*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,121 @@
#include "meta.h"
#include "../coding/coding.h"
/* .IMC - from iNiS Gitaroo Man (PS2) */
VGMSTREAM * init_vgmstream_imc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate, interleave, blocks;
size_t file_size, data_size;
/* checks */
/* .imc: extension from the main container */
if (!check_extensions(streamFile, "imc"))
goto fail;
channel_count = read_32bitLE(0x00, streamFile);
sample_rate = read_32bitLE(0x04, streamFile);
interleave = read_32bitLE(0x08, streamFile) * 0x10; /* number of frames in a block */
blocks = read_32bitLE(0x0c, streamFile); /* number of interleave blocks (even in mono) */
file_size = get_streamfile_size(streamFile);
loop_flag = 0;
start_offset = 0x10;
/* extra checks since the header is so simple */
if (channel_count < 1 || channel_count > 8 || sample_rate < 22000 || sample_rate > 48000)
goto fail;
if (interleave*blocks + start_offset != file_size)
goto fail;
/* remove padding (important to play gapless subsongs, happens even for mono) */
{
off_t min_offset = file_size - interleave;
off_t offset = file_size - 0x10;
data_size = file_size - start_offset;
while (offset > min_offset) {
if (read_32bitLE(offset, streamFile) != 0)
break;
data_size -= 0x10*channel_count;
offset -= 0x10;
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_IMC;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* ****************************************************************************** */
/* .IMC in containers */
VGMSTREAM * init_vgmstream_imc_container(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t header_offset, subfile_offset, next_offset, name_offset;
size_t subfile_size;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "imc"))
goto fail;
total_subsongs = read_32bitLE(0x00, streamFile);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
header_offset = 0x04 + 0x20*(target_subsong-1);
name_offset = header_offset + 0x00;
/* 0x08: flags? (0x702ADE77|0x002ADE77|0x20000000|etc) */
/* 0x0c: same for all songs in single .imc but varies between .imc */
subfile_offset = read_32bitLE(header_offset + 0x10,streamFile);
/* 0x14: flags/size? (0xF0950000|0x3CFA1200|etc) */
/* 0x18: same for all songs in single .imc but varies between .imc */
/* 0x1c: flags? (0 or 2) */
if (target_subsong == total_subsongs) {
next_offset = get_streamfile_size(streamFile);
}
else {
next_offset = read_32bitLE(header_offset + 0x20 + 0x10,streamFile);
}
subfile_size = next_offset - subfile_offset;
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_imc(temp_streamFile);
if (!vgmstream) goto fail;
close_streamfile(temp_streamFile);
vgmstream->num_streams = total_subsongs;
read_string(vgmstream->stream_name,0x08+1, name_offset,streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -46,8 +46,9 @@ 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_vag(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile);
@ -64,7 +65,7 @@ VGMSTREAM * init_vgmstream_rsf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile);
@ -173,6 +174,7 @@ VGMSTREAM * init_vgmstream_pos(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nwa(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xss(STREAMFILE * streamFile);
@ -289,7 +291,7 @@ VGMSTREAM * init_vgmstream_sadl(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_ccc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_psx_fag(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_fag(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_mihb(STREAMFILE * streamFile);
@ -360,8 +362,7 @@ VGMSTREAM * init_vgmstream_dc_dcsw_dcs(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_wii_smp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_mul(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile);
@ -400,7 +401,7 @@ VGMSTREAM * init_vgmstream_zsd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_RedSpark(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_redspark(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile);
@ -533,7 +534,7 @@ VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_iab(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_vs_str(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE* streamFile);
@ -629,6 +630,8 @@ 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_va3(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_ulw(STREAMFILE * streamFile);
@ -643,7 +646,8 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_idx_big(STREAMFILE * steeamFile);
VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE * steeamFile);
VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE * steeamFile);
VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile);
@ -659,8 +663,9 @@ 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_nlsd(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_pc_al2(STREAMFILE * streamFile);
@ -669,6 +674,7 @@ VGMSTREAM * init_vgmstream_pc_ast(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_naac(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ezw(STREAMFILE * streamFile);
@ -679,6 +685,7 @@ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE * streamFile);
@ -733,7 +740,7 @@ VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_opus_sps_n1_segmented(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile);
@ -793,4 +800,31 @@ VGMSTREAM * init_vgmstream_adpcm_capcom(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xwma(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xopus(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_vs_square(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_msf_banpresto_2msf(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nwav(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xpcm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_msf_tamasoft(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xps_dat(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xps(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_zsnd(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ogg_opus(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nus3audio(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_imc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_imc_container(STREAMFILE * streamFile);
#endif /*_META_H*/

View file

@ -0,0 +1,62 @@
#include "meta.h"
#include "../coding/coding.h"
/* WMSF - Banpresto MSFx wrapper [Dai-2-Ji Super Robot Taisen OG: The Moon Dwellers (PS3)] */
VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t subfile_offset = 0x10;
size_t subfile_size = get_streamfile_size(streamFile) - subfile_offset;
/* checks */
if ( !check_extensions(streamFile,"msf"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x574D5346) /* "WMSF" */
goto fail;
/* 0x04: size, 0x08: flags? 0x0c: null? */
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_ps3_msf(temp_streamFile);
if (!vgmstream) goto fail;
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
/* 2MSF - Banpresto RIFF wrapper [Dai-2-Ji Super Robot Taisen OG: The Moon Dwellers (PS4)] */
VGMSTREAM * init_vgmstream_msf_banpresto_2msf(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t subfile_offset = 0x14;
size_t subfile_size = get_streamfile_size(streamFile) - subfile_offset;
/* checks */
if ( !check_extensions(streamFile,"at9"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x324D5346) /* "2MSF" */
goto fail;
/* 0x04: size, 0x08: flags? 0x0c: null?, 0x10: 0x01? (BE values even though RIFF is LE) */
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_riff(temp_streamFile);
if (!vgmstream) goto fail;
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -0,0 +1,74 @@
#include "meta.h"
#include "../coding/coding.h"
/* MSF - TamaSoft games [Abandoner: The Severed Dreams (PC)] */
VGMSTREAM * init_vgmstream_msf_tamasoft(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate, codec, loop_start = 0, loop_end = 0;
size_t data_size;
uint16_t xor16;
uint32_t xor32;
/* checks */
/* .msf: extension referenced in .EXE (bigfiles don't have filenames) */
if (!check_extensions(streamFile, "msf"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4D534620) /* "MSF " */
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x00) /* just in case since there are other .msf */
goto fail;
/* header from 0x10 to 0x30 is encrypted (though value at 0x10 doubles as key...) */
xor16 = (uint16_t)(((uint32_t)read_32bitLE(0x04,streamFile) * 0x65u) + 0x30Au); /* from the exe */
xor32 = (uint32_t)(((uint32_t)xor16 << 16u) | (uint32_t)xor16);
/* 0x10: null */
loop_flag = (uint32_t)read_32bitLE(0x14,streamFile) ^ xor32;
data_size = (uint32_t)read_32bitLE(0x18,streamFile) ^ xor32;
codec = (uint16_t)read_16bitLE(0x1c,streamFile) ^ xor16;
channel_count = (uint16_t)read_16bitLE(0x1e,streamFile) ^ xor16;
sample_rate = (uint32_t)read_32bitLE(0x20,streamFile) ^ xor32;
/* 0x24: average bitrate? */
/* 0x28: block size */
/* 0x2a: bps */
/* 0x2c: unknown (fixed) */
if (loop_flag) {
loop_start = read_32bitLE(0x30,streamFile);
loop_end = read_32bitLE(0x34,streamFile);
/* 0x38/3c: null */
start_offset = 0x40;
}
else {
start_offset = 0x30;
}
if (codec != 0x01)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_MSF_TAMASOFT;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
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->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -0,0 +1,164 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
typedef enum { NONE, PSX, DSP, XBOX } mul_codec;
/* .MUL - from Crystal Dynamics games [Legacy of Kain: Defiance (PS2), Tomb Raider Underworld (multi)] */
VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, coefs_offset = 0;
int loop_flag, channel_count, sample_rate, num_samples, loop_start;
int big_endian;
mul_codec codec = NONE;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
/* checks */
/* .mul: found in the exe, used by the bigfile extractor (Gibbed.TombRaider)
* (some files have companion .mus/sam files but seem to be sequences/control stuff)
* .(extensionless): filenames as found in the bigfile
* .emff: fake extension ('Eidos Music File Format') */
if (!check_extensions(streamFile, "mul,,emff"))
goto fail;
if (read_32bitBE(0x10,streamFile) != 0 ||
read_32bitBE(0x14,streamFile) != 0 ||
read_32bitBE(0x18,streamFile) != 0 ||
read_32bitBE(0x1c,streamFile) != 0)
goto fail;
big_endian = guess_endianness32bit(0x00, streamFile);
read_32bit = big_endian ? read_32bitBE : read_32bitLE;
sample_rate = read_32bit(0x00,streamFile);
loop_start = read_32bit(0x04,streamFile);
num_samples = read_32bit(0x08,streamFile);
channel_count = read_32bit(0x0C,streamFile);
if (sample_rate < 8000 || sample_rate > 48000 || channel_count > 8)
goto fail;
/* 0x20: flag when file has non-audio blocks (ignored by the layout) */
/* 0x24: same? */
/* 0x28: loop offset within audio data (not file offset) */
/* 0x2c: some value related to loop? */
/* 0x34: id? */
/* 0x38+: channel config until ~0x100? (multiple 0x3F800000 depending on the number of channels) */
/* test known versions (later versions start from 0x24 instead of 0x20) */
if (!(read_32bit(0x38,streamFile) == 0x3F800000 ||
read_32bit(0x3c,streamFile) == 0x3F800000)) /* Tomb Raider Underworld */
goto fail;
loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */
start_offset = 0x800;
/* format is pretty limited so we need to guess codec */
if (big_endian) {
/* test DSP (GC/Wii): check known coef locations */
if (read_32bitBE(0xC8,streamFile) != 0) { /* Tomb Raider Legend (GC) */
codec = DSP;
coefs_offset = 0xC8;
}
else if (read_32bitBE(0xCC,streamFile) != 0) { /* Tomb Raider Anniversary (Wii) */
codec = DSP;
coefs_offset = 0xCC;
}
else if (read_32bitBE(0x2D0,streamFile) != 0) { /* Tomb Raider Underworld (Wii) */
codec = DSP;
coefs_offset = 0x2D0;
}
// todo test XMA1 (X360): mono streams, each block has 1 sub-blocks of 0x800 packet per channel
// todo test ? (PS3)
}
else {
int i;
off_t offset = start_offset;
size_t file_size = get_streamfile_size(streamFile);
size_t frame_size;
/* check first audio frame */
while (offset < file_size) {
uint32_t block_type = read_32bit(offset+0x00, streamFile);
uint32_t block_size = read_32bit(offset+0x04, streamFile);
uint32_t data_size = read_32bit(offset+0x10, streamFile);
if (block_type != 0x00) {
offset += 0x10 + block_size;
continue; /* not audio */
}
/* test PS-ADPCM (PS2/PSP): flag is always 2 in .mul */
frame_size = 0x10;
for (i = 0; i < data_size / frame_size; i++) {
if (read_8bit(offset + 0x20 + frame_size*i + 0x01, streamFile) != 0x02)
break;
}
if (i == data_size / frame_size) {
codec = PSX;
break;
}
/* test XBOX-IMA (PC/Xbox): reserved frame header value is always 0 */
frame_size = 0x24;
for (i = 0; i < data_size / frame_size; i++) {
if (read_8bit(offset + 0x20 + frame_size*i + 0x03, streamFile) != 0x00)
break;
}
if (i == data_size / frame_size) {
codec = XBOX;
break;
}
break;
}
}
if (codec == NONE) {
VGM_LOG("MUL: unknown codec\n");
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_MUL;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = num_samples;
vgmstream->codec_endian = big_endian;
switch(codec) {
case PSX:
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_mul;
break;
case DSP:
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_blocked_mul;
dsp_read_coefs_be(vgmstream,streamFile,coefs_offset+0x00,0x2e);
dsp_read_hist_be (vgmstream,streamFile,coefs_offset+0x24,0x2e);
break;
case XBOX:
vgmstream->coding_type = coding_XBOX_IMA_int;
vgmstream->layout_type = layout_blocked_mul;
break;
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -1,7 +1,7 @@
#include "meta.h"
#include "../coding/coding.h"
/* SPSD - Naomi (arcade) streams [Guilty Gear X (Naomi), Crazy Taxi (Naomi), Virtua Tennis 2 (Naomi)] */
/* SPSD - Naomi (arcade) and early Dreamcast streams [Guilty Gear X (Naomi), Crazy Taxi (Naomi), Virtua Tennis 2 (Naomi)] */
VGMSTREAM * init_vgmstream_naomi_spsd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
@ -10,8 +10,9 @@ VGMSTREAM * init_vgmstream_naomi_spsd(STREAMFILE *streamFile) {
/* checks */
/* .spsd: header id */
if (!check_extensions(streamFile, "spsd"))
/* .str: actual extension, rare [Shenmue (DC)]
* .spsd: header id */
if (!check_extensions(streamFile, "str,spsd"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53505344) /* "SPSD" */
goto fail;

View file

@ -6,8 +6,11 @@ VGMSTREAM * init_vgmstream_nds_strm_ffta2(STREAMFILE *streamFile) {
off_t start_offset;
int loop_flag, channel_count;
/* check extension, case insensitive (the ROM seems to use simply .bin though) */
if (!check_extensions(streamFile,"strm"))
/* checks*/
/* .bin: actual extension
* .strm: header id */
if (!check_extensions(streamFile,"bin,strm"))
goto fail;
/* check header */
@ -31,13 +34,12 @@ VGMSTREAM * init_vgmstream_nds_strm_ffta2(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_NDS_STRM_FFTA2;
vgmstream->coding_type = coding_DVI_IMA_int;
vgmstream->coding_type = coding_FFTA2_IMA;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x80;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:

View file

@ -9,30 +9,30 @@ VGMSTREAM * init_vgmstream_ngc_dsp_mpds(STREAMFILE *streamFile) {
int loop_flag = 0, channel_count, short_mpds;
/* check extension, case insensitive */
/* checks */
/* .dsp: Big Air Freestyle */
/* .mds: Terminator 3 The Redemption, Mission Impossible: Operation Surma */
if (!check_extensions(streamFile, "dsp,mds")) goto fail;
/* check header */
if (!check_extensions(streamFile, "dsp,mds"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4D504453) /* "MPDS" */
goto fail;
short_mpds = read_32bitBE(0x04,streamFile) != 0x00010000 && check_extensions(streamFile, "mds"); /* version byte? */
short_mpds = read_32bitBE(0x04,streamFile) != 0x00010000 && /* version byte? */
check_extensions(streamFile, "mds");
channel_count = short_mpds ?
read_16bitBE(0x0a, streamFile) :
read_32bitBE(0x14, streamFile);
if (channel_count > 2) goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->meta_type = meta_NGC_DSP_MPDS;
vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
if (!short_mpds) { /* Big Air Freestyle */
start_offset = 0x80;
@ -50,17 +50,22 @@ VGMSTREAM * init_vgmstream_ngc_dsp_mpds(STREAMFILE *streamFile) {
vgmstream->num_samples = read_32bitBE(0x04,streamFile);
vgmstream->sample_rate = (uint16_t)read_16bitBE(0x08,streamFile);
vgmstream->interleave_block_size = channel_count==1 ? 0 : 0x200;
/* some kind of hist after 0x0c? */
#if 0 //unknown coeffs/hist, related to data after 0x0c? (only coefs 0..7 seem to be needed)
/* set coefs, debugged from the MI:OS ELF (helpfully marked as "sMdsCoefs") */
{
off_t offset = 0x0c;
static const int16_t coefs[16] = {
0x0000,0x0000,0x0780,0x0000,0x0e60,0xf980,0x0c40,0xf920,
0x0f40,0xf880,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
};
int i, ch;
for (ch=0; ch < vgmstream->channels; ch++) {
for (i=0; i < 16; i++)
vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(offset + i*2);
for (ch = 0; ch < channel_count; ch++) {
for (i = 0; i < 16; i++) {
vgmstream->ch[ch].adpcm_coef[i] = coefs[i];
}
}
}
#endif
}

View file

@ -1114,16 +1114,18 @@ fail:
return NULL;
}
/* .vag - from Penny-Punching Princess (Switch) sfx */
VGMSTREAM * init_vgmstream_dsp_vag(STREAMFILE *streamFile) {
/* .vag - Nippon Ichi SPS wrapper [Penny-Punching Princess (Switch), Ys VIII (Switch)] */
VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile) {
dsp_meta dspm = {0};
/* checks */
if (!check_extensions(streamFile, "vag"))
/* .vag: Penny-Punching Princess (Switch)
* .nlsd: Ys VIII (Switch) */
if (!check_extensions(streamFile, "vag,nlsd"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x08000000) /* file type? OPUSs had 09 */
if (read_32bitBE(0x00,streamFile) != 0x08000000) /* file type (see other N1 SPS) */
goto fail;
if (read_32bitLE(0x08,streamFile) != read_32bitLE(0x24,streamFile)) /* header has various repeated values */
if ((uint16_t)read_16bitLE(0x08,streamFile) != read_32bitLE(0x24,streamFile)) /* header has various repeated values */
goto fail;
dspm.channel_count = 1;
@ -1166,3 +1168,31 @@ VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile) {
fail:
return NULL;
}
/* .adpcmx - AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */
VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile) {
dsp_meta dspm = {0};
/* checks */
if (!check_extensions(streamFile, "adpcmx"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41445059) /* "ADPY" */
goto fail;
/* 0x04(2): 1? */
/* 0x08: some size? */
/* 0x0c: null */
dspm.channel_count = read_16bitLE(0x06,streamFile);
dspm.max_channels = 2;
dspm.little_endian = 1;
dspm.header_offset = 0x10;
dspm.header_spacing = 0x60;
dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count;
dspm.interleave = 0x08;
dspm.meta_type = meta_DSP_ADPCMX;
return init_vgmstream_dsp_common(streamFile, &dspm);
fail:
return NULL;
}

View file

@ -17,7 +17,8 @@ VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE *streamFile) {
if (read_16bitBE(0x00,streamFile) != 0x01) /* version? */
goto fail;
if (read_32bitBE(0x04,streamFile) != 0x04) /* entry size? */
if (read_32bitBE(0x04,streamFile) != 0x02 && /* Mario Party 4 (GC) */
read_32bitBE(0x04,streamFile) != 0x04) /* Cubic Lode Runner (GC) */
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x7d00) /* not-sample rate? */
goto fail;

View file

@ -10,10 +10,9 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
int num_samples, loop_start_sample, loop_end_sample;
/* check extension, case insensitive */
/* checks */
if ( !check_extensions(streamFile,"xma")) /* (probably meant to be .nub) */
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x786D6100) /* "xma\0" */
goto fail;
@ -55,19 +54,17 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
} else { /* "fmt " */
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1);
}
if (bytes <= 0) goto fail;
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);;
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */
}
#else
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;

View file

@ -0,0 +1,119 @@
#include "meta.h"
#include "../coding/coding.h"
typedef enum { IDSP, OPUS, } nus3audio_codec;
/* .nus3audio - Namco's newest newest audio container [Super Smash Bros. Ultimate (Switch)] */
VGMSTREAM * init_vgmstream_nus3audio(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t subfile_offset = 0, name_offset = 0;
size_t subfile_size = 0;
nus3audio_codec codec;
const char* fake_ext = NULL;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "nus3audio"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4E555333) /* "NUS3" */
goto fail;
if (read_32bitLE(0x04,streamFile) + 0x08 != get_streamfile_size(streamFile))
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x41554449) /* "AUDI" */
goto fail;
/* parse existing chunks */
{
off_t offset = 0x0c;
size_t file_size = get_streamfile_size(streamFile);
uint32_t codec_id = 0;
total_subsongs = 0;
while (offset < file_size) {
uint32_t chunk_id = (uint32_t)read_32bitBE(offset+0x00, streamFile);
size_t chunk_size = (size_t)read_32bitLE(offset+0x04, streamFile);
switch(chunk_id) {
case 0x494E4458: /* "INDX": audio index */
total_subsongs = read_32bitLE(offset+0x08 + 0x00,streamFile);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
break;
case 0x4E4D4F46: /* "NMOF": name offsets (absolute, inside TNNM) */
name_offset = read_32bitLE(offset+0x08 + 0x04*(target_subsong-1),streamFile);
break;
case 0x41444F46: /* "ADOF": audio offsets (absolute, inside PACK) */
subfile_offset = read_32bitLE(offset+0x08 + 0x08*(target_subsong-1) + 0x00,streamFile);
subfile_size = read_32bitLE(offset+0x08 + 0x08*(target_subsong-1) + 0x04,streamFile);
break;
case 0x544E4944: /* "TNID": tone ids? */
case 0x544E4E4D: /* "TNNM": tone names */
case 0x4A554E4B: /* "JUNK": padding */
case 0x5041434B: /* "PACK": main data */
default:
break;
}
offset += 0x08 + chunk_size;
}
if (total_subsongs == 0 || subfile_offset == 0 || subfile_size == 0) {
VGM_LOG("NUS3AUDIO: subfile not found\n");
goto fail;
}
codec_id = read_32bitBE(subfile_offset, streamFile);
switch(codec_id) {
case 0x49445350: /* "IDSP" */
codec = IDSP;
fake_ext = "idsp";
break;
case 0x4F505553: /* "OPUS" */
codec = OPUS;
fake_ext = "opus";
break;
default:
VGM_LOG("NUS3AUDIO: unknown codec %x\n", codec_id);
goto fail;
}
}
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, fake_ext);
if (!temp_streamFile) goto fail;
/* init the VGMSTREAM */
switch(codec) {
case IDSP:
vgmstream = init_vgmstream_idsp_nus3(temp_streamFile);
if (!vgmstream) goto fail;
break;
case OPUS:
vgmstream = init_vgmstream_opus_nus3(temp_streamFile);
if (!vgmstream) goto fail;
break;
default:
goto fail;
}
vgmstream->num_streams = total_subsongs;
if (name_offset) /* null-terminated */
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -1,7 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
static STREAMFILE* setup_nus3bank_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
typedef enum { /*XMA_RAW, ATRAC3,*/ IDSP, ATRAC9, OPUS, BNSF, /*PCM, XMA_RIFF*/ } nus3bank_codec;
/* .nus3bank - Namco's newest audio container [Super Smash Bros (Wii U), idolmaster (PS4))] */
@ -144,7 +143,7 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) {
//;VGM_LOG("NUS3BANK: subfile=%lx, size=%x\n", subfile_offset, subfile_size);
temp_streamFile = setup_nus3bank_streamfile(streamFile, subfile_offset,subfile_size, fake_ext);
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, fake_ext);
if (!temp_streamFile) goto fail;
/* init the VGMSTREAM */
@ -182,27 +181,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
static STREAMFILE* setup_nus3bank_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View file

@ -0,0 +1,57 @@
#include "meta.h"
#include "../coding/coding.h"
/* NWAV - from Chunsoft games [Fuurai no Shiren Gaiden: Onnakenshi Asuka Kenzan! (PC)] */
VGMSTREAM * init_vgmstream_nwav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
/* checks */
/* .nwav: header id (no filenames in bigfiles) */
if ( !check_extensions(streamFile,"nwav") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4E574156) /* "NWAV" */
goto fail;
#ifdef VGM_USE_VORBIS
{
ogg_vorbis_meta_info_t ovmi = {0};
int channels;
/* 0x04: version? */
/* 0x08: crc? */
ovmi.stream_size = read_32bitLE(0x0c, streamFile);
ovmi.loop_end = read_32bitLE(0x10, streamFile); /* num_samples, actually */
/* 0x14: sample rate */
/* 0x18: bps? (16) */
channels = read_8bit(0x19, streamFile);
start_offset = read_16bitLE(0x1a, streamFile);
ovmi.loop_flag = read_16bitLE(0x1c, streamFile) != 0; /* loop count? -1 = loops */
/* 0x1e: always 2? */
/* 0x20: always 1? */
ovmi.loop_start = read_32bitLE(0x24, streamFile);
/* 0x28: always 1? */
/* 0x2a: always 1? */
/* 0x2c: always null? */
ovmi.meta_type = meta_NWAV;
/* values are in resulting bytes */
ovmi.loop_start = ovmi.loop_start / sizeof(int16_t) / channels;
ovmi.loop_end = ovmi.loop_end / sizeof(int16_t) / channels;
vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
}
#else
goto fail;
#endif
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -40,7 +40,7 @@ VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_none;
if (vgmstream->num_samples == 0) {
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, vgmstream->sample_rate, streamFile) - skip;
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip;
}
}
#else

View file

@ -0,0 +1,173 @@
#include "meta.h"
#include "../coding/coding.h"
static int get_ogg_page_size(STREAMFILE *streamFile, off_t page_offset, off_t *out_data_offset, size_t *out_page_size);
static int ogg_get_num_samples(STREAMFILE *streamFile, off_t start_offset);
/* Ogg Opus - standard Opus with optional looping comments [The Pillars of Earth (PC), Monster Boy and the Cursed Kingdom (Switch)] */
VGMSTREAM * init_vgmstream_ogg_opus(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, data_offset = 0;
size_t page_size = 0;
int loop_flag, channel_count, original_rate;
int loop_start = 0, loop_end = 0;
/* checks */
/* .opus: standard, .lopus: fake extension for plugins
* .ogg: less common, .logg: same */
if (!check_extensions(streamFile, "opus,lopus,ogg,logg"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4F676753) /* "OggS" */
goto fail;
/* see: https://tools.ietf.org/html/rfc7845.html */
start_offset = 0x00;
/* parse 1st page: opus head */
if (!get_ogg_page_size(streamFile, start_offset, &data_offset, &page_size))
goto fail;
if (read_32bitBE(data_offset+0x00,streamFile) != 0x4F707573 && /* "Opus" */
read_32bitBE(data_offset+0x04,streamFile) != 0x48656164) /* "Head" */
goto fail;
/* 0x01: version 1, fixed */
channel_count = read_8bit(data_offset+0x09,streamFile);
/* 0x0A: skip samples */
original_rate = read_32bitLE(data_offset+0x0c,streamFile);
/* 0x10: gain */
/* 0x12: mapping family */
/* parse 2nd page: opus tags (also mandatory) */
if (!get_ogg_page_size(streamFile, start_offset+page_size, &data_offset, &page_size))
goto fail;
if (read_32bitBE(data_offset+0x00,streamFile) != 0x4F707573 && /* "Opus" */
read_32bitBE(data_offset+0x04,streamFile) != 0x54616773) /* "Tags" */
goto fail;
loop_flag = 0;
{
char user_comment[1024+1];
off_t offset;
int vendor_size, comment_count, user_comment_size, user_comment_max;
int i;
int has_encoder_options = 0, has_title = 0;
vendor_size = read_32bitLE(data_offset+0x08,streamFile);
comment_count = read_32bitLE(data_offset+0x0c+vendor_size,streamFile);
/* parse comments */
offset = data_offset + 0x0c + vendor_size + 0x04;
for (i = 0; i < comment_count; i++) {
user_comment_size = read_32bitLE(offset+0x00,streamFile);
user_comment_max = user_comment_size > 1024 ? 1024 : user_comment_size;
read_string(user_comment,user_comment_max+1, offset+0x04,streamFile);
/* parse loop strings */
if (strstr(user_comment,"LOOP_START=")==user_comment) { /* Monster Boy and the Cursed Kingdom (Switch) */
loop_start = atol(strrchr(user_comment,'=')+1);
loop_flag = (loop_start >= 0);
}
else if (strstr(user_comment,"LOOP_END=")==user_comment) { /* LOOP_START pair */
loop_end = atol(strrchr(user_comment,'=')+1);
}
else if (strstr(user_comment,"ENCODER_OPTIONS=")==user_comment) { /* for detection */
has_encoder_options = 1;
}
else if (strstr(user_comment,"TITLE=")==user_comment) { /* for detection */
has_title = 1;
}
//;VGM_LOG("OggOpus: user_comment=%s\n", user_comment);
offset += 0x04 + user_comment_size;
}
/* Monster Boy has loop points for 44100hz (what), but Opus is resampled so
* they must be adjusted (with extra checks just in case). */
if (loop_flag && original_rate < 48000 && has_encoder_options && has_title) {
float modifier = 48000.0f / (float)original_rate;
loop_start = loop_start * modifier;
loop_end = loop_end * modifier;
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_OGG_OPUS;
vgmstream->sample_rate = 48000; /* Opus always resamples to this */
vgmstream->num_samples = ogg_get_num_samples(streamFile, 0x00);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
#ifdef VGM_USE_FFMPEG
{
vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset, get_streamfile_size(streamFile));
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* FFmpeg+libopus handles skip samples ok, FFmpeg+opus doesn't */
}
#else
goto fail;
#endif
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* parse OggS's bizarre segment table */
static int get_ogg_page_size(STREAMFILE *streamFile, off_t page_offset, off_t *out_data_offset, size_t *out_page_size) {
uint8_t segments;
size_t page_size = 0;
int i;
if (read_32bitBE(page_offset+0x00,streamFile) != 0x4F676753) /* "OggS" */
goto fail;
/* read all segment sizes */
segments = (uint8_t)read_8bit(page_offset+0x1a, streamFile);
for (i = 0; i < segments; i++) {
page_size += (uint8_t)read_8bit(page_offset + 0x1b + i, streamFile);
}
page_size += 0x1b + segments;
if (out_data_offset) *out_data_offset = page_offset + 0x1b + segments;
if (out_page_size) *out_page_size = page_size;
return 1;
fail:
return 0;
}
/* Ogg doesn't have num_samples info, must manually seek+read last granule
* (Xiph is insistent this is the One True Way). */
static int ogg_get_num_samples(STREAMFILE *streamFile, off_t start_offset) {
uint32_t expected_id = 0x4F676753;
off_t offset = get_streamfile_size(streamFile) - 0x04-0x01-0x01-0x08-0x04-0x04-0x04;
//todo better buffer reads (Ogg page max is 0xFFFF)
//lame way to force buffer, assuming it's around that
read_32bitBE(offset - 0x4000, streamFile);
while (offset >= start_offset) {
uint32_t current_id = read_32bitBE(offset, streamFile);
if (current_id == expected_id) { /* if more checks are needed last page starts with 0x0004 */
return read_32bitLE(offset+0x04+0x01+0x01, streamFile); /* get last granule = total samples (64b but whatevs) */
}
offset--;
}
return 0;
}

View file

@ -78,7 +78,7 @@ static void um3_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* first 0x800 bytes are xor'd with 0xff */
/* first 0x800 bytes are xor'd */
if (ov_streamfile->offset < 0x800) {
int num_crypt = 0x800 - ov_streamfile->offset;
if (num_crypt > bytes_read)
@ -94,7 +94,7 @@ static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* first 0x100 bytes are xor'd with offset */
/* first 0x100 bytes are xor'd */
if (ov_streamfile->offset < 0x100) {
int max_offset = ov_streamfile->offset + bytes_read;
if (max_offset > 0x100)
@ -110,7 +110,7 @@ static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb
size_t bytes_read = size*nmemb;
int i;
/* add 0x23 ('#') */
/* bytes add 0x23 ('#') */
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] += 0x23;
}
@ -125,7 +125,7 @@ static void sngw_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
put_32bitBE(key, ov_streamfile->xor_value);
/* bytes are xor'd with key and nibble-swapped, first "OggS" is changed */
/* first "OggS" is changed and bytes are xor'd and nibble-swapped */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
@ -145,7 +145,7 @@ static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* bytes are xor'd with key */
/* bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16];
}
@ -160,7 +160,6 @@ static void l2sd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
/* first "OggS" is changed */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
/* replace key in the first 4 bytes with "OggS" */
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
@ -177,7 +176,7 @@ static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb,
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* first 0x10 are xor'd with a key, but the header can be easily reconstructed
/* first 0x10 are xor'd, but header can be easily reconstructed
* (key is also in (game)/www/data/System.json "encryptionKey") */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x10) {
@ -197,7 +196,7 @@ static void eno_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* bytes are xor'd with key */
/* bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] ^= (uint8_t)ov_streamfile->xor_value;
}
@ -208,7 +207,7 @@ static void ys8_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* bytes are xor'd with key and nibble-swapped */
/* bytes are xor'd and nibble-swapped */
for (i = 0; i < bytes_read; i++) {
uint8_t val = ((uint8_t*)ptr)[i] ^ ov_streamfile->xor_value;
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
@ -220,7 +219,7 @@ static void gwm_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* bytes are xor'd with key */
/* bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] ^= (uint8_t)ov_streamfile->xor_value;
}
@ -236,7 +235,7 @@ static void mus_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
int i;
char *header_id = "OggS";
/* bytes are xor'd with key, first "OggS" is changed */
/* first "OggS" is changed and bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) { /* if decrypted gives "Mus " */
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
@ -247,6 +246,35 @@ static void mus_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
}
}
static void lse_add_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
int key = (uint8_t)ov_streamfile->xor_value + ((ov_streamfile->offset + i) % 256);
((uint8_t*)ptr)[i] ^= key;
}
}
static void lse_ff_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
char *header_id = "OggS";
/* first "OggS" is changed and bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
((uint8_t*)ptr)[i] ^= 0xFF;
}
}
}
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
@ -262,6 +290,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
int is_eno = 0;
int is_gwm = 0;
int is_mus = 0;
int is_lse = 0;
/* check extension */
@ -288,26 +317,27 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_gwm = 1;
} else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */
is_mus = 1;
} else if (check_extensions(streamFile,"lse")) { /* .lse: Labyrinth of Refrain: Coven of Dusk (PC) */
is_lse = 1;
} else {
goto fail;
}
/* check standard Ogg Vorbis and variations */
if (is_ogg) {
if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */
ovmi.decryption_callback = psychic_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_PSYCHIC;
ovmi.meta_type = meta_OGG_encrypted;
}
else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" [Lineage II Chronicle 4 (PC)] */
ovmi.decryption_callback = l2sd_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_L2SD;
ovmi.meta_type = meta_OGG_encrypted;
}
else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* XOR'ed + bitswapped "OggS" [Ys VIII (PC)] */
else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */
ovmi.xor_value = 0xF0;
ovmi.decryption_callback = ys8_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_YS8;
ovmi.meta_type = meta_OGG_encrypted;
}
else if (read_32bitBE(0x00,streamFile) == 0x4f676753) { /* "OggS" */
else if (read_32bitBE(0x00,streamFile) == 0x4f676753) { /* "OggS" (standard) */
ovmi.meta_type = meta_OGG_VORBIS;
}
else {
@ -315,16 +345,14 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
}
}
/* check "Ultramarine3" (???), may be encrypted */
if (is_um3) {
if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */
if (is_um3) { /* ["Ultramarine3" (???)] */
if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */
ovmi.decryption_callback = um3_ogg_decryption_callback;
}
ovmi.meta_type = meta_OGG_UM3;
ovmi.meta_type = meta_OGG_encrypted;
}
/* check KOVS (Koei Tecmo games), header + encrypted */
if (is_kovs) {
if (is_kovs) { /* Koei Tecmo PC games] */
if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */
goto fail;
}
@ -336,19 +364,17 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
start_offset = 0x20;
}
/* check SNGW (Capcom's MT Framework PC games), may be encrypted */
if (is_sngw) {
if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */
if (is_sngw) { /* [Capcom's MT Framework PC games] */
if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */
ovmi.xor_value = read_32bitBE(0x00,streamFile);
ovmi.decryption_callback = sngw_ogg_decryption_callback;
}
ovmi.meta_type = meta_OGG_SNGW;
ovmi.meta_type = meta_OGG_encrypted;
}
/* check ISD [Gunvolt (PC)], encrypted */
if (is_isd) {
if (is_isd) { /* [Gunvolt (PC)] */
ovmi.decryption_callback = isd_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_ISD;
ovmi.meta_type = meta_OGG_encrypted;
//todo looping unknown, not in Ogg comments
// game has sound/GV_steam.* files with info about sound/stream/*.isd
@ -359,39 +385,48 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
// 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes)
}
/* check RPGMKVO [RPG Maker MV (PC)], header + partially encrypted */
if (is_rpgmvo) {
if (is_rpgmvo) { /* [RPG Maker MV (PC)] */
if (read_32bitBE(0x00,streamFile) != 0x5250474D && /* "RPGM" */
read_32bitBE(0x00,streamFile) != 0x56000000) { /* "V\0\0\0" */
goto fail;
}
ovmi.decryption_callback = rpgmvo_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_RPGMV;
ovmi.meta_type = meta_OGG_encrypted;
start_offset = 0x10;
}
/* check ENO [Metronomicon (PC)], key + encrypted */
if (is_eno) {
if (is_eno) { /* [Metronomicon (PC)] */
/* first byte probably derives into xor key, but this works too */
ovmi.xor_value = read_8bit(0x05,streamFile); /* always zero = easy key */
ovmi.decryption_callback = eno_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_ENO;
ovmi.meta_type = meta_OGG_encrypted;
start_offset = 0x01;
}
/* check GWM [Adagio: Cloudburst (PC)], encrypted */
if (is_gwm) {
if (is_gwm) { /* [Adagio: Cloudburst (PC)] */
ovmi.xor_value = 0x5D;
ovmi.decryption_callback = gwm_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_GWM;
ovmi.meta_type = meta_OGG_encrypted;
}
/* check .mus [Redux - Dark Matters (PC)], encrypted */
if (is_mus) {
if (is_mus) { /* [Redux - Dark Matters (PC)] */
ovmi.decryption_callback = mus_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_MUS;
ovmi.meta_type = meta_OGG_encrypted;
}
if (is_lse) { /* [Nippon Ichi PC games] */
if (read_32bitBE(0x00,streamFile) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */
ovmi.decryption_callback = lse_ff_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_encrypted;
}
else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */
ovmi.decryption_callback = lse_add_ogg_decryption_callback;
ovmi.xor_value = (uint8_t)read_8bit(0x04,streamFile) - 0x04;
ovmi.meta_type = meta_OGG_encrypted;
/* key is found at file_size-1 but this works too (same key for most files but can vary) */
}
}
@ -566,7 +601,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
}
}
//;VGM_LOG("OGG: user_comment=%s\n", user_comment);
;VGM_LOG("OGG: user_comment=%s\n", user_comment);
}
}

View file

@ -50,14 +50,13 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
vgmstream->layout_type = layout_none;
if (vgmstream->num_samples == 0) {
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, vgmstream->sample_rate, streamFile) - skip;
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip;
}
}
#else
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
@ -71,8 +70,8 @@ fail:
/* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */
VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) {
STREAMFILE * PSIFile = NULL;
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
off_t offset;
int num_samples, loop_start, loop_end;
/* checks */
if (!check_extensions(streamFile,"opus,lopus"))
@ -102,13 +101,12 @@ fail:
/* Nippon1 variation [Disgaea 5 (Switch)] */
VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE *streamFile) {
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
off_t offset;
int num_samples, loop_start, loop_end;
/* checks */
if ( !check_extensions(streamFile,"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)))
goto fail;
@ -126,8 +124,8 @@ fail:
/* Capcom variation [Ultra Street Fighter II (Switch), Resident Evil: Revelations (Switch)] */
VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
off_t offset;
int num_samples, loop_start, loop_end;
int channel_count;
/* checks */
@ -205,8 +203,8 @@ fail:
/* Procyon Studio variation [Xenoblade Chronicles 2 (Switch)] */
VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE *streamFile) {
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
off_t offset;
int num_samples, loop_start = 0, loop_end = 0, loop_flag;
/* checks */
if (!check_extensions(streamFile,"nop"))
@ -231,12 +229,11 @@ fail:
/* Shin'en variation [Fast RMX (Switch)] */
VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE *streamFile) {
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
int num_samples, loop_start, loop_end;
/* checks */
if ( !check_extensions(streamFile,"opus,lopus"))
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x01000080)
goto fail;
@ -259,6 +256,7 @@ VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE *streamFile) {
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"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x4F505553) /* "OPUS" */
@ -281,32 +279,61 @@ fail:
return NULL;
}
/* Nihon Falcom NLSD Opus [Ys VIII: Lacrimosa of Dana (Switch)]
Similar to Nippon Ichi Software "AT9" Opus (Penny Punching Princess (Switch)
Unlike Penny Punching Princess, this is not segmented */
VGMSTREAM * init_vgmstream_opus_nlsd(STREAMFILE *streamFile) {
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
/* Nippon Ichi SPS wrapper (non-segmented) [Ys VIII: Lacrimosa of Dana (Switch)] */
VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE *streamFile) {
off_t offset;
int num_samples, loop_start = 0, loop_end = 0, loop_flag;
/* checks */
if (!check_extensions(streamFile, "nlsd"))
/* .sps: Labyrinth of Refrain - Coven of Dusk (Switch)
* .nlsd: Disgaea Refine (Switch), Ys VIII (Switch) */
if (!check_extensions(streamFile, "sps,nlsd"))
goto fail;
/* File type check - DSP is 0x8, Opus is 0x9 */
if (read_32bitBE(0x00, streamFile) != 0x09000000)
if (read_32bitBE(0x00, streamFile) != 0x09000000) /* file type (see other N1 SPS) */
goto fail;
offset = 0x1C;
num_samples = read_32bitLE(0x0C, streamFile);
/* Check if there's a loop_end "adjuster" value to determine loop_flag
Setting loop_flag to loop_start would work too but it may cause conflicts
With files that may have a 0 sample loop_start. */
// Not sure why this is a thing but let's roll with it.
loop_flag = read_32bitLE(0x18, streamFile);
/* sections num_samples (remnant of segmented opus_sps_n1):
* 0x10: intro, 0x14: loop, 0x18: end (all must add up to num_samples) */
loop_flag = read_32bitLE(0x18, streamFile); /* with loop disabled only loop section has samples */
if (loop_flag) {
loop_start = read_32bitLE(0x10, streamFile);
/* They were being really creative here */
loop_end = (num_samples - read_32bitLE(0x18, streamFile));
loop_end = loop_start + read_32bitLE(0x14, streamFile);
}
return init_vgmstream_opus(streamFile, 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) {
off_t offset;
int num_samples, loop_start = 0, loop_end = 0;
float modifier;
/* checks */
if (!check_extensions(streamFile, "opusx"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 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;
/* resampling calcs are slighly off and may to over num_samples, but by removing delay seems ok */
if (loop_start >= 120) {
loop_start -= 128;
loop_end -= 128;
}
else {
loop_end = 0;
}
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end);

View file

@ -2,10 +2,8 @@
#include "../coding/coding.h"
#include "../layout/layout.h"
static STREAMFILE* setup_opus_ppp_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
/* .AT9 Opus - from Penny-Punching Princess (Switch) */
VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile) {
/* 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;
@ -19,11 +17,11 @@ VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile) {
/* checks */
if (!check_extensions(streamFile, "at9"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x09000000) /* file type? DSPs had 08 */
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): loop flag?, 0x0c: num_samples (slightly smaller than added samples) */
/* 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;
@ -43,7 +41,7 @@ VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile) {
if (!segment_size)
goto fail;
temp_streamFile = setup_opus_ppp_streamfile(streamFile, segment_offset,segment_size, "opus");
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);
@ -93,26 +91,3 @@ fail:
free_layout_segmented(data);
return NULL;
}
static STREAMFILE* setup_opus_ppp_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View file

@ -11,11 +11,9 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
/* check extension, case insensitive */
/* checks */
if (!check_extensions(streamFile,"p3d"))
goto fail;
/* check header */
if (read_32bitBE(0x0,streamFile) != 0x503344FF && /* "P3D"\FF (LE: PC) */
read_32bitBE(0x0,streamFile) != 0xFF443350) /* \FF"D3P" (BE: PS3, X360) */
goto fail;
@ -164,12 +162,12 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
size_t bytes;
bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
if (bytes <= 0) goto fail;
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, 0, 1,1); /* samples needs adjustment */
break;
}
#endif

View file

@ -1,127 +1,91 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* PONA (from Policenauts [3DO]) */
/* PONA - from Policenauts (3DO) */
VGMSTREAM * init_vgmstream_pona_3do(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
int loop_flag, channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("pona",filename_extension(filename))) goto fail;
/* check header */
/* checks */
/* .pona: fake extension?
* .sxd: Policenauts Pilot Disc (3DO) */
if (!check_extensions(streamFile, "pona,sxd"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x13020000)
goto fail;
if ((read_32bitBE(0x06,streamFile)+0x800) != (get_streamfile_size(streamFile)))
if (read_32bitBE(0x06,streamFile)+0x800 != get_streamfile_size(streamFile))
goto fail;
loop_flag = (read_32bitBE(0x0A,streamFile) != 0xFFFFFFFF);
channel_count = 1;
start_offset = (uint16_t)(read_16bitBE(0x04,streamFile));
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = (uint16_t)(read_16bitBE(0x04,streamFile));
vgmstream->channels = channel_count;
vgmstream->sample_rate = 22050;
vgmstream->coding_type = coding_SDX2;
vgmstream->num_samples = (get_streamfile_size(streamFile))-start_offset;
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitBE(0x0A,streamFile));
vgmstream->loop_end_sample = (read_32bitBE(0x06,streamFile));
}
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_PONA_3DO;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
vgmstream->sample_rate = 22050;
vgmstream->num_samples = get_streamfile_size(streamFile) - start_offset;
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitBE(0x0A,streamFile);
vgmstream->loop_end_sample = read_32bitBE(0x06,streamFile);
}
vgmstream->coding_type = coding_SDX2;
vgmstream->layout_type = layout_none;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}
/* PONA (from Policenauts [PSX]) */
/* PONA - from Policenauts (PSX) */
VGMSTREAM * init_vgmstream_pona_psx(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
int loop_flag, channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("pona",filename_extension(filename))) goto fail;
/* check header */
/* checks */
/* .pona: fake extension? */
if (!check_extensions(streamFile, "pona"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x00000800)
goto fail;
if ((read_32bitBE(0x08,streamFile)+0x800) != (get_streamfile_size(streamFile)))
if (read_32bitBE(0x08,streamFile)+0x800 != get_streamfile_size(streamFile))
goto fail;
loop_flag = (read_32bitBE(0xC,streamFile) != 0xFFFFFFFF);
channel_count = 1;
start_offset = read_32bitBE(0x04,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = read_32bitBE(0x04,streamFile);
vgmstream->channels = channel_count;
vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = ((get_streamfile_size(streamFile))-start_offset)/16/channel_count*28;
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitBE(0x0C,streamFile)/16/channel_count*28);
vgmstream->loop_end_sample = (read_32bitBE(0x08,streamFile)/16/channel_count*28);
}
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_PONA_PSX;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
vgmstream->sample_rate = 44100;
vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count);
if (loop_flag) {
vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitBE(0x0C,streamFile), channel_count);
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitBE(0x08,streamFile), channel_count);
}
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_none;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -20,6 +20,7 @@ VGMSTREAM * init_vgmstream_pos(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_RIFF_WAVE_POS;
}
else {
#ifdef VGM_USE_VORBIS
/* hack for Ogg with external loops */
streamData = open_streamfile_by_ext(streamFile, "ogg");
if (streamData) {
@ -29,6 +30,9 @@ VGMSTREAM * init_vgmstream_pos(STREAMFILE *streamFile) {
else {
goto fail;
}
#else
goto fail;
#endif
}
close_streamfile(streamData);

View file

@ -150,10 +150,11 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) {
loop_start_sample = loop_start / 2 / channel_count;
is_loop_samples = 1;
}
else if ((loop_start % 0x800 == 0) && loop_start > 0) {/* sector-aligned, min is 0x800 */
else if ((loop_start % 0x800 == 0) && loop_start > 0) {/* sector-aligned, min/0 is 0x800 */
/* cavia games: loop_start is offset [Drakengard 1/2, GITS: Stand Alone Complex] */
/* offset is absolute from the "cavia stream format" container that adjusts ADS start */
loop_flag = 1;
loop_start_offset = loop_start;
loop_start_offset = loop_start - 0x800;
ignore_silent_frame_cavia = 1;
}
else if (loop_start % 0x800 != 0 || loop_start == 0) { /* not sector aligned */
@ -312,8 +313,6 @@ fail:
/* ****************************************************************************** */
static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
/* ADS in containers */
VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
@ -355,28 +354,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
if (fake_ext) {
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
}
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View file

@ -0,0 +1,80 @@
#include "meta.h"
#include "../coding/coding.h"
/* VA3 - Konami / Sony Atrac3 Container [PS2 DDR Supernova 2 Arcade] */
VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
uint32_t data_size, loop_start, loop_end;
/* check extension, case insensitive */
if (!check_extensions(streamFile, "va3"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x21334156) /* "!3AV" */
goto fail;
/* va3 header */
start_offset = 0x800;
data_size = read_32bitLE(0x04, streamFile);// get_streamfile_size(streamFile) - start_offset;
// pretty sure 0x4 LE 32 bit is some sort of filesize...
loop_flag = 0;
//0x18 is 1... what is this?
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x14, streamFile);
vgmstream->num_samples = read_32bitLE(0x08, streamFile);
vgmstream->channels = channel_count;
vgmstream->meta_type = meta_VA3;
loop_start = 0;
loop_end = 0;
#ifdef VGM_USE_FFMPEG
{
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[200];
int32_t bytes, samples_size = 1024, block_size, encoder_delay, joint_stereo;
block_size = 0xC0 * vgmstream->channels;
//max_samples = (data_size / block_size) * samples_size;
encoder_delay = 0x0;
joint_stereo = 0;
/* make a fake riff so FFmpeg can parse the ATRAC3 */
bytes = ffmpeg_make_riff_atrac3(buf, 200, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, 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 = max_samples;
if (loop_flag) {
vgmstream->loop_start_sample = (loop_start / block_size) * samples_size;
vgmstream->loop_end_sample = (loop_end / block_size) * samples_size;
}
}
#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;
}

View file

@ -4,32 +4,30 @@
/* MSF - Sony's PS3 SDK format (MultiStream File) */
VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, header_offset = 0;
off_t start_offset;
uint32_t data_size, loop_start = 0, loop_end = 0;
uint32_t id, codec_id, flags;
int loop_flag = 0, channel_count;
/* checks */
/* .msf: standard, .at3: Silent Hill HD Collection, .mp3: Darkstalkers Resurrection */
/* .msf: standard
* .at3: Silent Hill HD Collection (PS3)
* .mp3: Darkstalkers Resurrection (PS3) */
if (!check_extensions(streamFile,"msf,at3,mp3"))
goto fail;
/* "WMSF" variation with a mini header over the MSFC header [Dai-2-Ji Super Robot Generations (PS3)] */
if (read_32bitBE(0x00,streamFile) == 0x574D5346) {
header_offset = 0x10;
}
start_offset = header_offset+0x40; /* MSF header is always 0x40 */
start_offset = 0x40;
/* check header "MSF" + version-char
* usually "MSF\0\1", "MSF\0\2", "MSF0"(\3\0), "MSF5"(\3\5), "MSFC"(\4\3) (last/common version) */
id = read_32bitBE(header_offset+0x00,streamFile);
/* check header "MSF" + version-char, usually:
* 0x01, 0x02, 0x30 ("0"), 0x35 ("5"), 0x43 ("C") (last/most common version) */
id = read_32bitBE(0x00,streamFile);
if ((id & 0xffffff00) != 0x4D534600) goto fail;
codec_id = read_32bitBE(header_offset+0x04,streamFile);
channel_count = read_32bitBE(header_offset+0x08,streamFile);
data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header */
codec_id = read_32bitBE(0x04,streamFile);
channel_count = read_32bitBE(0x08,streamFile);
data_size = read_32bitBE(0x0C,streamFile); /* without header */
if (data_size == 0xFFFFFFFF) /* unneeded? */
data_size = get_streamfile_size(streamFile) - start_offset;
@ -39,15 +37,15 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
* 0x20: VBR MP3 source (changed into simplified 0x1a1 CBR)
* 0x40: joint stereo MP3 (apparently interleaved stereo for other formats)
* 0x80+: (none/reserved) */
flags = read_32bitBE(header_offset+0x14,streamFile);
flags = read_32bitBE(0x14,streamFile);
/* 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 markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */
if (loop_flag) {
loop_start = read_32bitBE(header_offset+0x18,streamFile);
loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */
loop_start = read_32bitBE(0x18,streamFile);
loop_end = read_32bitBE(0x1C,streamFile); /* loop duration */
loop_end = loop_start + loop_end; /* usually equals data_size but not always */
if (loop_end > data_size)/* not seen */
loop_end = data_size;
@ -58,9 +56,9 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* sample rate hack for strange MSFv1 files that don't have it */
vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile);
if (vgmstream->sample_rate == 0x00000000) /* PS ADPCM only? */
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
/* sample rate hack for strange MSFv1 files (PS ADPCM only?) */
if (vgmstream->sample_rate == 0x00000000)
vgmstream->sample_rate = 48000;
vgmstream->meta_type = meta_PS3_MSF;
@ -116,11 +114,9 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
encoder_delay = 1162;
vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_size) - encoder_delay;
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->sample_rate = 44100; /* voice tracks seems to use 44khz, not sure about other tracks */
bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
@ -156,7 +152,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
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 CBR frame beginnings */
/* loops are always aligned to CBR frame beginnings */
}
/* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */
@ -164,7 +160,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
}
#endif
#ifdef VGM_USE_MPEG
case 0x07: { /* MPEG (CBR LAME MP3) []Dengeki Bunko Fighting Climax (PS3) */
case 0x07: { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */
mpeg_codec_data *mpeg_data = NULL;
mpeg_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels);
@ -176,7 +172,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
if (loop_flag) {
vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data);
vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data);
/* loops are always CBR frame beginnings */
/* loops are always aligned to CBR frame beginnings */
}
/* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */

View file

@ -1,66 +0,0 @@
#include "meta.h"
#include "../util.h"
/* FAG (Jackie Chan - Stuntmaster) */
VGMSTREAM * init_vgmstream_psx_fag(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("fag",filename_extension(filename))) goto fail;
/* check header */
/* Look if there's more than 1 one file... */
if (read_32bitBE(0x00,streamFile) != 0x01000000)
goto fail;
loop_flag = 0;
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = read_32bitLE(0x04,streamFile);
vgmstream->channels = channel_count;
vgmstream->sample_rate = 24000;
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = (read_32bitLE(0x08,streamFile))/channel_count/32*28;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = (read_32bitLE(0x08,streamFile))/channel_count/32*28;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8000;
vgmstream->meta_type = meta_PSX_FAG;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View file

@ -13,7 +13,7 @@ static uint32_t find_key(uint32_t firstword) {
/* RSD - RedSpark (MadWorld)
RS3D - RedSpark (Mario & Luigi: Dream Team I fi*/
VGMSTREAM * init_vgmstream_RedSpark(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_redspark(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
@ -105,7 +105,7 @@ VGMSTREAM * init_vgmstream_RedSpark(STREAMFILE *streamFile) {
} else {
vgmstream->layout_type = layout_none;
}
vgmstream->meta_type = meta_RedSpark;
vgmstream->meta_type = meta_REDSPARK;
{

View file

@ -272,8 +272,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
* .adx: Remember11 (PC) sfx
* .adp: Headhunter (DC)
* .xss: Spider-Man The Movie (Xbox)
* .xsew: Mega Man X Legacy Collections (PC) */
if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew") ) {
* .xsew: Mega Man X Legacy Collections (PC)
* .adpcm: Angry Birds Transformers (Android)
* .adw: Dead Rising 2 (PC)
* .wd: Genma Onimusha (Xbox) voices */
if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd") ) {
;
}
else if ( check_extensions(streamFile, "mwv") ) {
@ -310,13 +313,13 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
/* some Dreamcast/Naomi games do this [Headhunter (DC), Bomber hehhe (DC)] */
if (riff_size + 0x04 == file_size && read_16bitLE(0x14,streamFile)==0x0000)
riff_size -= 0x04;
/* some PC games do this [Halo 2 (PC)] */
/* some PC games do this [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */
if (riff_size + 0x04 == file_size && read_16bitLE(0x14,streamFile)==0x0069)
riff_size -= 0x04;
/* check for truncated RIFF */
if (file_size < riff_size+0x08) goto fail;
if (file_size < riff_size+0x08)
goto fail;
/* read through chunks to verify format and find metadata */
{
@ -371,16 +374,16 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
break;
case 0x736D706C: /* "smpl" (RIFFMIDISample + MIDILoop chunk) */
/* check loop count/loop info (most common) *///todo double check values
/* check loop count/loop info (most common) */
/* 0x00: manufacturer id, 0x04: product id, 0x08: sample period, 0x0c: unity node,
* 0x10: pitch fraction, 0x14: SMPTE format, 0x18: SMPTE offset, 0x1c: loop count, 0x20: sampler data */
if (read_32bitLE(current_chunk+0x08+0x1c, streamFile)==1) {
if (read_32bitLE(current_chunk+0x08+0x1c, streamFile) == 1) { /* handle only one loop (could contain N MIDILoop) */
/* 0x24: cue point id, 0x28: type (0=forward, 1=alternating, 2=backward)
* 0x2c: start, 0x30: end, 0x34: fraction, 0x38: play count */
if (read_32bitLE(current_chunk+0x08+0x28, streamFile)==0) {
if (read_32bitLE(current_chunk+0x08+0x28, streamFile) == 0) { /* loop forward */
loop_flag = 1;
loop_start_smpl = read_32bitLE(current_chunk+0x08+0x2c, streamFile);
loop_end_smpl = read_32bitLE(current_chunk+0x08+0x30, streamFile);
loop_end_smpl = read_32bitLE(current_chunk+0x08+0x30, streamFile) + 1; /* must add 1 as per spec (ok for standard WAV/AT3/AT9) */
}
}
break;
@ -393,10 +396,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
&& read_32bitLE(current_chunk+0x08+0x10, streamFile) > 0
&& read_32bitLE(current_chunk+0x08+0x14, streamFile) == 0x10) {
/* 0x14: size, 0x18: loop type (0=forward, 1=release), 0x1c: loop start, 0x20: loop length */
if (read_32bitLE(current_chunk+0x08+0x18, streamFile)==0) {
if (read_32bitLE(current_chunk+0x08+0x18, streamFile) == 0) { /* loop forward */
loop_flag = 1;
loop_start_wsmp = read_32bitLE(current_chunk+0x08+0x1c, streamFile);
loop_end_wsmp = read_32bitLE(current_chunk+0x08+0x20, streamFile);
loop_end_wsmp = read_32bitLE(current_chunk+0x08+0x20, streamFile); /* must not add 1 as per spec */
loop_end_wsmp += loop_start_wsmp;
}
}
@ -655,6 +658,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
else if (loop_start_smpl >= 0) {
vgmstream->loop_start_sample = loop_start_smpl;
vgmstream->loop_end_sample = loop_end_smpl;
/* end must add +1, but check in case of faulty tools */
if (vgmstream->loop_end_sample - 1 == vgmstream->num_samples)
vgmstream->loop_end_sample--;
vgmstream->meta_type = meta_RIFF_WAVE_smpl;
}
else if (loop_start_wsmp >= 0) {
@ -754,7 +761,7 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
if (read_32bitBE(current_chunk+0x2c+4, streamFile)==0) {
loop_flag = 1;
loop_start_offset = read_32bitBE(current_chunk+0x2c+8, streamFile);
loop_end_offset = read_32bitBE(current_chunk+0x2c+0xc,streamFile);
loop_end_offset = read_32bitBE(current_chunk+0x2c+0xc,streamFile) + 1;
}
}
break;
@ -803,6 +810,10 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
if (loop_start_offset >= 0) {
vgmstream->loop_start_sample = loop_start_offset;
vgmstream->loop_end_sample = loop_end_offset;
/* end must add +1, but check in case of faulty tools */
if (vgmstream->loop_end_sample - 1 == vgmstream->num_samples)
vgmstream->loop_end_sample--;
vgmstream->meta_type = meta_RIFX_WAVE_smpl;
}
}

View file

@ -1025,13 +1025,15 @@ VGMSTREAM * init_vgmstream_rsd6xma(STREAMFILE *streamFile) {
datasize = read_32bitBE(0x808, streamFile);
bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
if (bytes <= 0) goto fail;
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;
/* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */
//xma_fix_raw_samples(vgmstream, streamFile, start_offset,datasize, 0, 0,0);
ffmpeg_set_skip_samples(ffmpeg_data, 512+64);
}
#else
goto fail;
@ -1053,13 +1055,15 @@ VGMSTREAM * init_vgmstream_rsd6xma(STREAMFILE *streamFile) {
datasize = read_32bitBE(0x808, streamFile);
bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
if (bytes <= 0) goto fail;
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;
/* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */
//xma_fix_raw_samples(vgmstream, streamFile, start_offset,datasize, 0, 0,0);
ffmpeg_set_skip_samples(ffmpeg_data, 512+64);
}
#else
goto fail;

View file

@ -1,80 +1,58 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* SDT (Baldur's Gate - Dark Alliance) */
/* .sdt - from High Voltage games? [Baldur's Gate - Dark Alliance (GC)] */
VGMSTREAM * init_vgmstream_sdt(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("sdt",filename_extension(filename))) goto fail;
#if 0
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */
/* checks */
if (!check_extensions(streamFile, "sdt"))
goto fail;
#endif
channel_count = read_32bitBE(0x00,streamFile); /* assumed */
loop_flag = (read_32bitBE(0x04,streamFile) != 0);
channel_count = 2;
start_offset = 0xA0;
data_size = get_streamfile_size(streamFile) - start_offset;
if (channel_count != 2)
goto fail; /* only seen this */
if (read_32bitBE(0x08,streamFile) != read_32bitBE(0x08,streamFile))
goto fail; /* sample rate agreement between channels */
if (read_32bitBE(0x98,streamFile) != 0x8000)
goto fail; /* expected interleave */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0xA0;
vgmstream->channels = channel_count; /*read_32bitBE(0x00,streamFile);*/
vgmstream->meta_type = meta_SDT;
vgmstream->sample_rate = read_32bitBE(0x08,streamFile);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = read_32bitBE(0x14,streamFile)/8*14/channel_count;
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); /* maybe at @0x14 - 2? */
if (loop_flag) {
vgmstream->loop_start_sample = 0; /* (read_32bitLE(0x08,streamFile)-1)*28; */
vgmstream->loop_end_sample = read_32bitBE(0x14,streamFile)/8*14/channel_count;
vgmstream->loop_start_sample = 0; /* maybe @0x08? */
vgmstream->loop_end_sample = vgmstream->num_samples; /* maybe @0x10? */
}
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8000;
vgmstream->meta_type = meta_SDT;
if (vgmstream->interleave_block_size)
vgmstream->interleave_last_block_size =
(data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels;
dsp_read_coefs_be(vgmstream,streamFile, 0x3c,0x2E);
dsp_read_hist_be(vgmstream, streamFile, 0x60,0x2E);
if (vgmstream->coding_type == coding_NGC_DSP) {
int i;
for (i=0;i<16;i++) {
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x3C+i*2,streamFile);
}
if (vgmstream->channels) {
for (i=0;i<16;i++) {
vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x6A+i*2,streamFile);
}
}
}
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -1,94 +1,112 @@
#include "meta.h"
#include "../coding/coding.h"
/* SEG - found in Eragon */
/* SEG - from Stormfront games [Eragon (multi), Forgotten Realms: Demon Stone (multi) */
VGMSTREAM * init_vgmstream_seg(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
coding_t coding;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("seg",filename_extension(filename))) goto fail;
int loop_flag, channel_count;
size_t data_size;
uint32_t codec;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
/* check header */
/* checks */
if (!check_extensions(streamFile, "seg"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x73656700) /* "seg\0" */
goto fail;
if (read_32bitBE(0x04,streamFile) == 0x70733200) /* "ps2\0" */
{
coding = coding_PSX;
}
else if (read_32bitBE(0x04,streamFile) == 0x78627800) /* "xbx\0" */
{
coding = coding_XBOX_IMA;
}
else goto fail;
loop_flag = 0;
channel_count = read_32bitLE(0x24,streamFile);
codec = read_32bitBE(0x04,streamFile);
/* 0x08: version? (2: Eragon, Spiderwick Chronicles Wii / 3: Spiderwick Chronicles X360 / 4: Spiderwick Chronicles PC) */
if (guess_endianness32bit(0x08,streamFile)) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;
}
/* 0x0c: file size */
data_size = read_32bit(0x10, streamFile); /* including interleave padding */
/* 0x14: null */
loop_flag = read_32bit(0x20,streamFile); /* rare */
channel_count = read_32bit(0x24,streamFile);
/* 0x28: extradata 1 entries (0x08 per entry, unknown) */
/* 0x2c: extradata 1 offset */
/* 0x30: extradata 2 entries (0x10 or 0x14 per entry, seek/hist table?) */
/* 0x34: extradata 2 offset */
start_offset = 0x4000;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x4000;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
vgmstream->coding_type = coding;
vgmstream->meta_type = meta_SEG;
vgmstream->sample_rate = read_32bit(0x18,streamFile);
vgmstream->num_samples = read_32bit(0x1c,streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = read_32bitLE(0x1C,streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples;
}
read_string(vgmstream->stream_name,0x20+1, 0x38,streamFile);
vgmstream->interleave_block_size = 0;
if (coding_PSX == coding)
{
vgmstream->num_samples = (read_32bitLE(0x0C,streamFile)-start_offset)*28/16/channel_count;
vgmstream->meta_type = meta_PS2_SEG;
if (channel_count == 1) {
vgmstream->layout_type = layout_none;
} else if (channel_count == 2) {
switch(codec) {
case 0x70733200: /* "ps2\0" */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2000;
}
}
else if (coding_XBOX_IMA == coding)
{
vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitLE(0x0C,streamFile)-start_offset, channel_count);
vgmstream->meta_type = meta_XBOX_SEG;
break;
case 0x78627800: /* "xbx\0" */
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
break;
case 0x77696900: /* "wii\0" */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2000;
/* standard dsp header at start_offset */
dsp_read_coefs_be(vgmstream, streamFile, start_offset+0x1c, vgmstream->interleave_block_size);
dsp_read_hist_be(vgmstream, streamFile, start_offset+0x40, vgmstream->interleave_block_size);
//todo first_interleave: 0x2000 - 60
break;
case 0x70635F00: /* "pc_\0" */
vgmstream->coding_type = coding_IMA;
vgmstream->layout_type = layout_none;
break;
#ifdef VGM_USE_FFMPEG
case 0x78623300: { /* "xb3\0" */
uint8_t buf[0x100];
int bytes, block_size, block_count;
block_size = 0x4000;
block_count = data_size / block_size + (data_size % block_size ? 1 : 0);
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(streamFile, buf,bytes, start_offset,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* samples are ok */
break;
}
else goto fail;
#endif
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -111,11 +111,6 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
/* needs -1 to match RIFF AT3's loop chunk
* (maybe SGXD = "loop before this sample" rather than "loop after this sample" as expected by vgmstream) */
if (vgmstream->loop_end_sample > 0)
vgmstream->loop_end_sample -= 1;
switch (type) {
case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/
vgmstream->coding_type = coding_PSX;

View file

@ -2,7 +2,7 @@
#include <ctype.h>
/* .sli - loop points associated with a similarly named .ogg [Fate/Stay Night (PC), World End Economica (PC)]*/
/* .sli+ogg/opus - KiriKiri engine / WaveLoopManager loop points loader [Fate/Stay Night (PC), World End Economica (PC)] */
VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamData = NULL;
@ -14,23 +14,42 @@ VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) {
goto fail;
{
/* try with file.ogg.sli=header and file.ogg=data */
/* try with file.ogg/opus.sli=header and file.ogg/opus=data */
char basename[PATH_LIMIT];
get_streamfile_basename(streamFile,basename,PATH_LIMIT);
streamData = open_streamfile_by_filename(streamFile, basename);
if (!streamData) goto fail;
if (!check_extensions(streamData, "ogg"))
goto fail;
}
#ifdef VGM_USE_VORBIS
/* let the real initer do the parsing */
if (check_extensions(streamData, "ogg")) { /* Fate/Stay Night (PC) */
#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(streamData);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_OGG_SLI;
#else
goto fail;
#endif
}
else if (check_extensions(streamData, "opus")) { /* Sabbat of the Witch (PC) */
#ifdef VGM_USE_FFMPEG
vgmstream = init_vgmstream_ffmpeg(streamData);
if (!vgmstream) goto fail;
/* FFmpeg's Opus encoder delay is borked but no need to fix:
* somehow sli+opus use 0 in the OpusHead (to simplify looping?) */
vgmstream->meta_type = meta_OPUS_SLI;
#else
goto fail;
#endif
}
else {
goto fail;
}
/* find loop text */
{
@ -77,13 +96,11 @@ VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) {
}
}
if (loop_start != -1 && loop_length != -1) {
if (loop_start != -1 && loop_length != -1) { /* v1 */
vgmstream_force_loop(vgmstream,1,loop_start, loop_start+loop_length);
vgmstream->meta_type = meta_OGG_SLI;
}
else if (loop_from != -1 && loop_to != -1) {
else if (loop_from != -1 && loop_to != -1) { /* v2 */
vgmstream_force_loop(vgmstream,1,loop_to, loop_from);
vgmstream->meta_type = meta_OGG_SLI2;
}
else {
goto fail; /* if there's no loop points the .sli wasn't valid */

View file

@ -1,9 +1,7 @@
#include "meta.h"
#include "../coding/coding.h"
static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size, char* extension);
/* .SPS - Nippon Ichi's RIFF AT3 wrapper [ClaDun (PSP)] */
/* .SPS - Nippon Ichi wrapper [ClaDun (PSP)] */
VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
@ -16,17 +14,17 @@ VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) {
goto fail;
/* mini header */
type = read_32bitLE(0x00,streamFile); //todo channels? all known VAG are mono and AT3 stereo
type = read_32bitLE(0x00,streamFile);
subfile_size = read_32bitLE(0x04,streamFile);
sample_rate = (uint16_t)read_16bitLE(0x08,streamFile);
/* 0x0a/0b: stereo+loop flags? */
/* 0x0a: flag? */
//num_samples = read_32bitLE(0x0c,streamFile);
subfile_offset = 0x10;
/* init the VGMSTREAM */
switch(type) {
case 1: /* .vag */
temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "vag");
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "vag");
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_vag(temp_streamFile);
@ -34,7 +32,7 @@ VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) {
break;
case 2: /* .at3 */
temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "at3");
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "at3");
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_riff(temp_streamFile);
@ -54,26 +52,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, char* extension) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View file

@ -340,8 +340,6 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
/* extradata_offset+0x00: fmt0x166 header (BE), extradata_offset+0x34: seek table */
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, extradata_offset,0x34, stream_size, streamFile, 1);
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
@ -351,6 +349,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
vgmstream->num_samples = ffmpeg_data->totalSamples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
xma_fix_raw_samples(vgmstream, streamFile, start_offset,stream_size, 0, 0,0); /* samples are ok, loops? */
break;
}

View file

@ -1,69 +1,54 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* SVS (from Unlimited Saga) */
/* probably Square Vag Stream */
/* SVS - SeqVagStream from Square games [Unlimited Saga (PS2) music] */
VGMSTREAM * init_vgmstream_svs(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int channel_count, loop_flag, pitch;
int loop_flag = 0;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("svs",filename_extension(filename))) goto fail;
/* check header */
/* checks */
/* .svs: header id (probably ok like The Bouncer's .vs) */
if (!check_extensions(streamFile, "svs"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */
goto fail;
loop_flag = (read_32bitLE(0x08,streamFile)!=0);
/* 63.SVS has start and end on the same sample, which crashes stuff */
if (read_32bitLE(0x08,streamFile)==read_32bitLE(0x0c,streamFile))
loop_flag = 0;
/* 0x04: flags (1=stereo?, 2=loop) */
pitch = read_32bitLE(0x10,streamFile); /* usually 0x1000 = 48000 */
/* 0x14: volume? */
/* 0x18: file id (may be null) */
/* 0x1c: null */
loop_flag = (read_32bitLE(0x08,streamFile) > 0); /* loop start frame, min is 1 */
channel_count = 2;
start_offset = 0x20;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x40;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = (get_streamfile_size(streamFile)-0x40)*28/16/channel_count;
vgmstream->meta_type = meta_SVS;
vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* music = ~44100, ambience = 48000 (rounding makes more sense but not sure) */
vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count);
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitLE(0x08,streamFile)-1)*28;
vgmstream->loop_end_sample = (read_32bitLE(0x0c,streamFile)-1)*28;
vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile) * 28; /* frame count (0x10*ch) */
vgmstream->loop_end_sample = read_32bitLE(0x0c,streamFile) * 28; /* frame count, (not exact num_samples when no loop) */
/* start/end on the same frame rarely happens too (ex. file_id 63 SVS), perhaps loop should be +1 */
}
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->meta_type = meta_PS2_SVS;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -10,7 +10,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
size_t chunk_size, stream_size = 0;
int is_dual = 0, is_external = 0;
int loop_flag, channels, codec, location;
int loop_flag, channels, codec, flags;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
uint32_t at9_config_data = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
@ -35,12 +35,15 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
/* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */
if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */
if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) /* "WAVE" */
goto fail;
/* check multi-streams (usually only in SFX containers) */
total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
// todo rarely a WAVE subsong may point to a repeated data offset, with different tags only
/* read stream header */
{
@ -50,9 +53,10 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
table_offset = chunk_offset + 0x08 + 4*(target_subsong-1);
header_offset = table_offset + read_32bitLE(table_offset,streamHeader);
location = read_32bitLE(header_offset+0x00,streamHeader);
flags = read_32bitLE(header_offset+0x00,streamHeader);
codec = read_8bit (header_offset+0x04,streamHeader);
channels = read_8bit (header_offset+0x05,streamHeader);
/* 0x06(2): unknown, rarely 0xFF */
sample_rate = read_32bitLE(header_offset+0x08,streamHeader);
/* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */
/* 0x10(4): ? + volume? + pan? (can be 0 for music) */
@ -62,14 +66,32 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
stream_size = read_32bitLE(header_offset+0x20,streamHeader);
stream_offset = read_32bitLE(header_offset+0x24,streamHeader);
/* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller).
* One tag seems to add a small part of the ATRAC9 data, for RAM preloding I guess. */
loop_flag = loop_start_sample != -1 && loop_end_sample != -1;
/* known flag combos:
* 0x00: Chaos Rings 2 sfx (RAM + no tags)
* 0x01: common (RAM + tags)
* 0x02: Chaos Rings 3 sfx (stream + no tags)
* 0x03: common (stream + tags)
* 0x05: Gravity Rush 2 sfx (RAM + tags) */
//has_tags = flags & 1;
is_external = flags & 2;
//unknown = flags & 4; /* no apparent differences with/without it? */
/* flag 1 signals TLV-like extra data. Format appears to be 0x00(1)=tag?, 0x01(1)=extra size*32b?, 0x02(2)=config?
* but not always (ex. size=0x07 but actually=0), perhaps only some bits are used or varies with tag, or are subflags.
* A tag may appear with or without extra data (even 0x0a), 0x01/03/04/06 are common at the beginnig (imply number of tags?),
* 0x64/7F are common at the end (but not always), 0x0A=ATRAC9 config, 0x0B/0C appear with RAM preloading data
* (most variable in Soul Sacrifice; total TLVs size isn't plainly found in the SXD header AFAIK). */
/* manually try to find ATRAC9 tag */
if (codec == 0x42) {
off_t extra_offset = header_offset+0x28;
off_t max_offset = chunk_offset + chunk_size;
/* manually try to find certain tag, no idea about the actual format
* (most variable in Soul Sacrifice; extra data size isn't found in the header AFAIK) */
if (!(flags & 1))
goto fail;
while (extra_offset < max_offset) {
uint32_t tag = read_32bitBE(extra_offset, streamHeader);
if (tag == 0x0A010000 || tag == 0x0A010600) {
@ -83,26 +105,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
goto fail;
}
loop_flag = loop_start_sample != -1 && loop_end_sample != -1;
/* usually sxd=header+data and sxd1=header + sxd2=data, but rarely sxd1 contain data [The Last Guardian (PS4)] */
switch(location) { /* might not be exact but seems the only difference in TLG */
case 0x00: /* some Chaos Rings 2 sfx */
case 0x01: /* most common */
case 0x05: /* some Gradity Rush 2 sfx */
is_external = 0; /* RAM asset? */
break;
case 0x02: /* some Chaos Rings 3 sfx */
case 0x03: /* most common */
is_external = 1; /* stream asset? */
break;
default:
VGM_LOG("SXD: unknown location 0x%x\n", location);
goto fail;
}
/* usually .sxd=header+data and .sxd1=header + .sxd2=data, but rarely sxd1 may contain data [The Last Guardian (PS4)] */
if (is_external) {
start_offset = stream_offset; /* absolute if external */
} else {

View file

@ -108,13 +108,17 @@ VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile) {
datasize = dataSize;
bytes = ffmpeg_make_riff_xma2(buf,100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
if (bytes <= 0) goto fail;
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;

View file

@ -30,16 +30,22 @@ typedef enum {
XMA2 = 21, /* raw XMA2 */
FFMPEG = 22, /* any headered FFmpeg format */
AC3 = 23, /* AC3/SPDIF */
PCFX = 24, /* PC-FX ADPCM */
} txth_type;
typedef struct {
txth_type codec;
uint32_t codec_mode;
uint32_t interleave;
uint32_t value_mul;
uint32_t value_div;
uint32_t value_add;
uint32_t value_sub;
uint32_t id_value;
uint32_t id_offset;
uint32_t interleave;
uint32_t channels;
uint32_t sample_rate;
@ -66,12 +72,33 @@ typedef struct {
int num_samples_data_size;
int target_subsong;
uint32_t subsong_count;
uint32_t subsong_offset;
uint32_t name_offset_set;
uint32_t name_offset;
uint32_t name_size;
/* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */
STREAMFILE *streamFile;
int streamfile_is_txth;
/* configurable STREAMFILEs and if we opened it (thus must close it later) */
STREAMFILE *streamText;
STREAMFILE *streamHead;
STREAMFILE *streamBody;
int streamtext_opened;
int streamhead_opened;
int streambody_opened;
} txth_header;
static STREAMFILE * open_txth(STREAMFILE * streamFile);
static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth);
static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth, const char * key, const char * val);
static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_value);
static int parse_txth(txth_header * txth);
static int parse_keyval(STREAMFILE * streamFile, txth_header * txth, const char * key, char * val);
static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value);
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes);
@ -79,23 +106,42 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes);
* Similar to GENH, but with a single separate .txth file in the dir and text-based. */
VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamText = NULL;
txth_header txth = {0};
coding_t coding;
int i, j;
/* reject .txth as the CLI can open and decode with itself */
if (check_extensions(streamFile, "txth"))
goto fail;
/* accept .txth (should set body_file or will fail later) */
if (check_extensions(streamFile, "txth")) {
txth.streamFile = streamFile;
txth.streamfile_is_txth = 1;
/* no need for ID or ext checks -- if a .TXTH exists all is good
txth.streamText = streamFile;
txth.streamHead = NULL;
txth.streamBody = NULL;
txth.streamtext_opened = 0;
txth.streamhead_opened = 0;
txth.streambody_opened = 0;
}
else {
/* accept base file (no need for ID or ext checks --if a companion .TXTH exists all is good)
* (player still needs to accept the streamfile's ext, so at worst rename to .vgmstream) */
streamText = open_txth(streamFile);
STREAMFILE * streamText = open_txth(streamFile);
if (!streamText) goto fail;
txth.streamFile = streamFile;
txth.streamfile_is_txth = 0;
txth.streamText = streamText;
txth.streamHead = streamFile;
txth.streamBody = streamFile;
txth.streamtext_opened = 1;
txth.streamhead_opened = 0;
txth.streambody_opened = 0;
}
/* process the text file */
if (!parse_txth(streamFile, streamText, &txth))
if (!parse_txth(&txth))
goto fail;
@ -129,14 +175,15 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case AC3:
case FFMPEG: coding = coding_FFmpeg; break;
#endif
case PCFX: coding = coding_PCFX; break;
default:
goto fail;
}
/* try to autodetect PS-ADPCM loop data */
if (txth.loop_flag_auto && coding == coding_PSX) {
size_t data_size = get_streamfile_size(streamFile) - txth.start_offset;
txth.loop_flag = ps_find_loop_offsets(streamFile, txth.start_offset, data_size, txth.channels, txth.interleave,
txth.loop_flag = ps_find_loop_offsets(txth.streamBody, txth.start_offset, txth.data_size, txth.channels, txth.interleave,
(int32_t*)&txth.loop_start_sample, (int32_t*)&txth.loop_end_sample);
}
@ -149,6 +196,12 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
vgmstream->num_samples = txth.num_samples;
vgmstream->loop_start_sample = txth.loop_start_sample;
vgmstream->loop_end_sample = txth.loop_end_sample;
vgmstream->num_streams = txth.subsong_count;
vgmstream->stream_size = txth.data_size;
if (txth.name_offset_set) {
size_t name_size = txth.name_size ? txth.name_size + 1 : STREAM_NAME_SIZE;
read_string(vgmstream->stream_name,name_size, txth.name_offset,txth.streamHead);
}
/* codec specific (taken from GENH with minimal changes) */
switch (coding) {
@ -207,8 +260,15 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
vgmstream->ch[i].adpcm_step_index = 0x7f;
}
}
break;
case coding_PCFX:
vgmstream->interleave_block_size = txth.interleave;
vgmstream->layout_type = layout_interleave;
if (txth.codec_mode >= 0 && txth.codec_mode <= 3)
vgmstream->codec_config = txth.codec_mode;
break;
case coding_MS_IMA:
if (!txth.interleave) goto fail; /* creates garbage */
@ -260,17 +320,17 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE;
/* normal/split coefs */
if (txth.coef_mode == 0) {
if (txth.coef_mode == 0) { /* normal mode */
for (j = 0; j < 16; j++) {
vgmstream->ch[i].adpcm_coef[j] = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2,streamFile);
vgmstream->ch[i].adpcm_coef[j] = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2, txth.streamHead);
}
}
else {
else { /* split coefs */
goto fail; //IDK what is this
/*
for (j = 0; j < 8; j++) {
vgmstream->ch[i].adpcm_coef[j*2]=read_16bit(coef[i]+j*2,streamFile);
vgmstream->ch[i].adpcm_coef[j*2+1]=read_16bit(coef_splitted[i]+j*2,streamFile);
vgmstream->ch[i].adpcm_coef[j*2] = read_16bit(genh.coef_offset + i*genh.coef_spacing + j*2, txth.streamHead);
vgmstream->ch[i].adpcm_coef[j*2+1] = read_16bit(genh.coef_split_offset + i*genh.coef_split_spacing + j*2, txth.streamHead);
}
*/
}
@ -280,7 +340,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
#ifdef VGM_USE_MPEG
case coding_MPEG_layer3:
vgmstream->layout_type = layout_none;
vgmstream->codec_data = init_mpeg(streamFile, txth.start_offset, &coding, vgmstream->channels);
vgmstream->codec_data = init_mpeg(txth.streamBody, txth.start_offset, &coding, vgmstream->channels);
if (!vgmstream->codec_data) goto fail;
break;
@ -291,7 +351,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
if (txth.codec == FFMPEG || txth.codec == AC3) {
/* default FFmpeg */
ffmpeg_data = init_ffmpeg_offset(streamFile, txth.start_offset,txth.data_size);
ffmpeg_data = init_ffmpeg_offset(txth.streamBody, txth.start_offset,txth.data_size);
if ( !ffmpeg_data ) goto fail;
if (vgmstream->num_samples == 0)
@ -335,17 +395,17 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
else {
goto fail;
}
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, txth.start_offset,txth.data_size);
ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size);
if ( !ffmpeg_data ) goto fail;
}
vgmstream->codec_data = ffmpeg_data;
vgmstream->layout_type = layout_none;
/* force encoder delay */
if (txth.skip_samples_set) {
if (txth.codec == XMA1 || txth.codec == XMA2) {
xma_fix_raw_samples(vgmstream, txth.streamBody, txth.start_offset,txth.data_size, 0, 0,0);
} else if (txth.skip_samples_set) { /* force encoder delay */
ffmpeg_set_skip_samples(ffmpeg_data, txth.skip_samples);
}
@ -373,14 +433,13 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
}
xma_get_samples(&msd, streamFile);
xma_get_samples(&msd, txth.streamBody);
vgmstream->num_samples = msd.num_samples;
if (txth.sample_type==1) {
vgmstream->loop_start_sample = msd.loop_start_sample;
vgmstream->loop_end_sample = msd.loop_end_sample;
}
//skip_samples = msd.skip_samples; //todo add skip samples
}
#endif
@ -389,14 +448,18 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
vgmstream->allow_dual_stereo = 1;
if ( !vgmstream_open_stream(vgmstream,streamFile,txth.start_offset) )
if ( !vgmstream_open_stream(vgmstream,txth.streamBody,txth.start_offset) )
goto fail;
if (streamText) close_streamfile(streamText);
if (txth.streamtext_opened) close_streamfile(txth.streamText);
if (txth.streamhead_opened) close_streamfile(txth.streamHead);
if (txth.streambody_opened) close_streamfile(txth.streamBody);
return vgmstream;
fail:
if (streamText) close_streamfile(streamText);
if (txth.streamtext_opened) close_streamfile(txth.streamText);
if (txth.streamhead_opened) close_streamfile(txth.streamHead);
if (txth.streambody_opened) close_streamfile(txth.streamBody);
close_vgmstream(vgmstream);
return NULL;
}
@ -434,15 +497,25 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) {
/* Simple text parser of "key = value" lines.
* The code is meh and error handling not exactly the best. */
static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth) {
static int parse_txth(txth_header * txth) {
off_t txt_offset = 0x00;
off_t file_size = get_streamfile_size(streamText);
off_t file_size = get_streamfile_size(txth->streamText);
/* setup txth defaults */
if (txth->streamBody)
txth->data_size = get_streamfile_size(txth->streamBody);
txth->target_subsong = txth->streamFile->stream_index;
if (txth->target_subsong == 0) txth->target_subsong = 1;
txth->data_size = get_streamfile_size(streamFile); /* for later use */
/* skip BOM if needed */
if ((uint16_t)read_16bitLE(0x00, streamText) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamText) == 0xFEFF)
if ((uint16_t)read_16bitLE(0x00, txth->streamText) == 0xFFFE ||
(uint16_t)read_16bitLE(0x00, txth->streamText) == 0xFEFF) {
txt_offset = 0x02;
}
else if (((uint32_t)read_32bitBE(0x00, txth->streamText) & 0xFFFFFF00) == 0xEFBBBF00) {
txt_offset = 0x03;
}
/* read lines */
while (txt_offset < file_size) {
@ -450,8 +523,9 @@ static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_hea
char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */
int ok, bytes_read, line_done;
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,streamText, &line_done);
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,txth->streamText, &line_done);
if (!line_done) goto fail;
//;VGM_LOG("TXTH: line=%s\n",line);
txt_offset += bytes_read;
@ -460,24 +534,32 @@ static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_hea
if (ok != 2) /* ignore line if no key=val (comment or garbage) */
continue;
if (!parse_keyval(streamFile, streamText, txth, key, val)) /* read key/val */
if (!parse_keyval(txth->streamFile, txth, key, val)) /* read key/val */
goto fail;
}
if (!txth->loop_flag_set)
txth->loop_flag = txth->loop_end_sample && txth->loop_end_sample != 0xFFFFFFFF;
if (!txth->streamBody)
goto fail;
if (txth->data_size > get_streamfile_size(txth->streamBody) - txth->start_offset || txth->data_size == 0)
txth->data_size = get_streamfile_size(txth->streamBody) - txth->start_offset;
return 1;
fail:
return 0;
}
static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth, const char * key, const char * val) {
static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char * key, char * val) {
//;VGM_LOG("TXTH: key=%s, val=%s\n", key, val);
if (0==strcmp(key,"codec")) {
if (0==strcmp(val,"PSX")) txth->codec = PSX;
else if (0==strcmp(val,"XBOX")) txth->codec = XBOX;
else if (0==strcmp(val,"NGC_DTK")) txth->codec = NGC_DTK;
else if (0==strcmp(val,"DTK")) txth->codec = NGC_DTK;
else if (0==strcmp(val,"PCM16BE")) txth->codec = PCM16BE;
else if (0==strcmp(val,"PCM16LE")) txth->codec = PCM16LE;
else if (0==strcmp(val,"PCM8")) txth->codec = PCM8;
@ -488,6 +570,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
else if (0==strcmp(val,"AICA")) txth->codec = AICA;
else if (0==strcmp(val,"MSADPCM")) txth->codec = MSADPCM;
else if (0==strcmp(val,"NGC_DSP")) txth->codec = NGC_DSP;
else if (0==strcmp(val,"DSP")) txth->codec = NGC_DSP;
else if (0==strcmp(val,"PCM8_U_int")) txth->codec = PCM8_U_int;
else if (0==strcmp(val,"PSX_bf")) txth->codec = PSX_bf;
else if (0==strcmp(val,"MS_IMA")) txth->codec = MS_IMA;
@ -499,10 +582,31 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
else if (0==strcmp(val,"XMA2")) txth->codec = XMA2;
else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG;
else if (0==strcmp(val,"AC3")) txth->codec = AC3;
else if (0==strcmp(val,"PCFX")) txth->codec = PCFX;
else goto fail;
}
else if (0==strcmp(key,"codec_mode")) {
if (!parse_num(streamFile,val, &txth->codec_mode)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->codec_mode)) goto fail;
}
else if (0==strcmp(key,"value_mul") || 0==strcmp(key,"value_*")) {
if (!parse_num(txth->streamHead,txth,val, &txth->value_mul)) goto fail;
}
else if (0==strcmp(key,"value_div") || 0==strcmp(key,"value_/")) {
if (!parse_num(txth->streamHead,txth,val, &txth->value_div)) goto fail;
}
else if (0==strcmp(key,"value_add") || 0==strcmp(key,"value_+")) {
if (!parse_num(txth->streamHead,txth,val, &txth->value_add)) goto fail;
}
else if (0==strcmp(key,"value_sub") || 0==strcmp(key,"value_-")) {
if (!parse_num(txth->streamHead,txth,val, &txth->value_sub)) goto fail;
}
else if (0==strcmp(key,"id_value")) {
if (!parse_num(txth->streamHead,txth,val, &txth->id_value)) goto fail;
}
else if (0==strcmp(key,"id_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->id_offset)) goto fail;
if (txth->id_value != txth->id_offset) /* evaluate current ID */
goto fail;
}
else if (0==strcmp(key,"interleave")) {
if (0==strcmp(val,"half_size")) {
@ -510,30 +614,24 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
txth->interleave = txth->data_size / txth->channels;
}
else {
if (!parse_num(streamFile,val, &txth->interleave)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail;
}
}
else if (0==strcmp(key,"id_value")) {
if (!parse_num(streamFile,val, &txth->id_value)) goto fail;
}
else if (0==strcmp(key,"id_offset")) {
if (!parse_num(streamFile,val, &txth->id_offset)) goto fail;
if (txth->id_value != txth->id_offset) /* evaluate current ID */
goto fail;
}
else if (0==strcmp(key,"channels")) {
if (!parse_num(streamFile,val, &txth->channels)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail;
}
else if (0==strcmp(key,"sample_rate")) {
if (!parse_num(streamFile,val, &txth->sample_rate)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->sample_rate)) goto fail;
}
else if (0==strcmp(key,"start_offset")) {
if (!parse_num(streamFile,val, &txth->start_offset)) goto fail;
if (!txth->data_size_set)
txth->data_size = get_streamfile_size(streamFile) - txth->start_offset; /* re-evaluate */
if (!parse_num(txth->streamHead,txth,val, &txth->start_offset)) goto fail;
if (!txth->data_size_set) {
txth->data_size = !txth->streamBody ? 0 :
get_streamfile_size(txth->streamBody) - txth->start_offset; /* re-evaluate */
}
}
else if (0==strcmp(key,"data_size")) {
if (!parse_num(streamFile,val, &txth->data_size)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->data_size)) goto fail;
txth->data_size_set = 1;
}
else if (0==strcmp(key,"sample_type")) {
@ -548,7 +646,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
txth->num_samples_data_size = 1;
}
else {
if (!parse_num(streamFile,val, &txth->num_samples)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->num_samples)) goto fail;
if (txth->sample_type==1)
txth->num_samples = get_bytes_to_samples(txth, txth->num_samples);
if (txth->sample_type==2)
@ -556,7 +654,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
}
}
else if (0==strcmp(key,"loop_start_sample")) {
if (!parse_num(streamFile,val, &txth->loop_start_sample)) goto fail;
if (!parse_num(txth->streamHead,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);
if (txth->sample_type==2)
@ -569,7 +667,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
txth->loop_end_sample = get_bytes_to_samples(txth, txth->data_size);
}
else {
if (!parse_num(streamFile,val, &txth->loop_end_sample)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->loop_end_sample)) goto fail;
if (txth->sample_type==1)
txth->loop_end_sample = get_bytes_to_samples(txth, txth->loop_end_sample);
if (txth->sample_type==2)
@ -579,7 +677,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
txth->loop_end_sample += txth->loop_adjust;
}
else if (0==strcmp(key,"skip_samples")) {
if (!parse_num(streamFile,val, &txth->skip_samples)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->skip_samples)) goto fail;
txth->skip_samples_set = 1;
if (txth->sample_type==1)
txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples);
@ -587,7 +685,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples * (txth->interleave*txth->channels));
}
else if (0==strcmp(key,"loop_adjust")) {
if (!parse_num(streamFile,val, &txth->loop_adjust)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->loop_adjust)) goto fail;
if (txth->sample_type==1)
txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust);
if (txth->sample_type==2)
@ -598,28 +696,105 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
txth->loop_flag_auto = 1;
}
else {
if (!parse_num(streamFile,val, &txth->loop_flag)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->loop_flag)) goto fail;
txth->loop_flag_set = 1;
if (txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF) { /* normally -1 = no loop */
txth->loop_flag = 0;
}
}
}
else if (0==strcmp(key,"coef_offset")) {
if (!parse_num(streamFile,val, &txth->coef_offset)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->coef_offset)) goto fail;
}
else if (0==strcmp(key,"coef_spacing")) {
if (!parse_num(streamFile,val, &txth->coef_spacing)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->coef_spacing)) goto fail;
}
else if (0==strcmp(key,"coef_endianness")) {
if (val[0]=='B' && val[1]=='E')
txth->coef_big_endian = 1;
else if (val[0]=='L' && val[1]=='E')
txth->coef_big_endian = 0;
else if (!parse_num(streamFile,val, &txth->coef_big_endian)) goto fail;
else if (!parse_num(txth->streamHead,txth,val, &txth->coef_big_endian)) goto fail;
}
else if (0==strcmp(key,"coef_mode")) {
if (!parse_num(streamFile,val, &txth->coef_mode)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->coef_mode)) goto fail;
}
else if (0==strcmp(key,"psx_loops")) {
if (!parse_num(streamFile,val, &txth->coef_mode)) goto fail;
if (!parse_num(txth->streamHead,txth,val, &txth->coef_mode)) goto fail;
}
else if (0==strcmp(key,"subsong_count")) {
if (!parse_num(txth->streamHead,txth,val, &txth->subsong_count)) goto fail;
}
else if (0==strcmp(key,"subsong_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->subsong_offset)) goto fail;
}
else if (0==strcmp(key,"name_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail;
txth->name_offset_set = 1;
/* special subsong adjustment */
if (txth->subsong_offset)
txth->name_offset = txth->name_offset + txth->subsong_offset * (txth->target_subsong - 1);
}
else if (0==strcmp(key,"name_size")) {
if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail;
}
else if (0==strcmp(key,"header_file")) {
if (txth->streamhead_opened) {
close_streamfile(txth->streamHead);
txth->streamHead = NULL;
txth->streamhead_opened = 0;
}
if (0==strcmp(val,"null")) { /* reset */
if (!txth->streamfile_is_txth) {
txth->streamHead = txth->streamFile;
}
}
else if (val[0]=='*' && val[1]=='.') { /* basename + extension */
txth->streamHead = open_streamfile_by_ext(txth->streamFile, (val+2));
if (!txth->streamHead) goto fail;
txth->streamhead_opened = 1;
}
else { /* open file */
fix_dir_separators(val); /* clean paths */
txth->streamHead = open_streamfile_by_filename(txth->streamFile, val);
if (!txth->streamHead) goto fail;
txth->streamhead_opened = 1;
}
}
else if (0==strcmp(key,"body_file")) {
if (txth->streambody_opened) {
close_streamfile(txth->streamBody);
txth->streamBody = NULL;
txth->streambody_opened = 0;
}
if (0==strcmp(val,"null")) { /* reset */
if (!txth->streamfile_is_txth) {
txth->streamBody = txth->streamFile;
}
}
else if (val[0]=='*' && val[1]=='.') { /* basename + extension */
txth->streamBody = open_streamfile_by_ext(txth->streamFile, (val+2));
if (!txth->streamBody) goto fail;
txth->streambody_opened = 1;
}
else { /* open file */
fix_dir_separators(val); /* clean paths */
txth->streamBody = open_streamfile_by_filename(txth->streamFile, val);
if (!txth->streamBody) goto fail;
txth->streambody_opened = 1;
}
/* use body as header when opening a .txth directly to simplify things */
if (txth->streamfile_is_txth && !txth->streamhead_opened) {
txth->streamHead = txth->streamBody;
}
txth->data_size = !txth->streamBody ? 0 :
get_streamfile_size(txth->streamBody) - txth->start_offset; /* re-evaluate */
}
else {
VGM_LOG("TXTH: unknown key=%s, val=%s\n", key,val);
@ -631,27 +806,37 @@ fail:
return 0;
}
static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_value) {
static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value) {
/* out_value can be these, save before modifying */
uint32_t value_mul = txth->value_mul;
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;
if (val[0] == '@') { /* offset */
uint32_t off = 0;
uint32_t offset = 0;
char ed1 = 'L', ed2 = 'E';
int size = 4;
int big_endian = 0;
int hex = (val[1]=='0' && val[2]=='x');
/* can happen when loading .txth and not setting body/head */
if (!streamFile)
goto fail;
/* read exactly N fields in the expected format */
if (strchr(val,':') && strchr(val,'$')) {
if (sscanf(val, hex ? "@%x:%c%c$%i" : "@%u:%c%c$%i", &off, &ed1,&ed2, &size) != 4) goto fail;
if (sscanf(val, hex ? "@%x:%c%c$%i" : "@%u:%c%c$%i", &offset, &ed1,&ed2, &size) != 4) goto fail;
} else if (strchr(val,':')) {
if (sscanf(val, hex ? "@%x:%c%c" : "@%u:%c%c", &off, &ed1,&ed2) != 3) goto fail;
if (sscanf(val, hex ? "@%x:%c%c" : "@%u:%c%c", &offset, &ed1,&ed2) != 3) goto fail;
} else if (strchr(val,'$')) {
if (sscanf(val, hex ? "@%x$%i" : "@%u$%i", &off, &size) != 2) goto fail;
if (sscanf(val, hex ? "@%x$%i" : "@%u$%i", &offset, &size) != 2) goto fail;
} else {
if (sscanf(val, hex ? "@%x" : "@%u", &off) != 1) goto fail;
if (sscanf(val, hex ? "@%x" : "@%u", &offset) != 1) goto fail;
}
if (off < 0 || off > get_streamfile_size(streamFile))
if (/*offset < 0 ||*/ offset > get_streamfile_size(streamFile))
goto fail;
if (ed1 == 'B' && ed2 == 'E')
@ -659,21 +844,48 @@ static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_v
else if (!(ed1 == 'L' && ed2 == 'E'))
goto fail;
if (subsong_offset)
offset = offset + subsong_offset * (txth->target_subsong - 1);
switch(size) {
case 1: *out_value = read_8bit(off,streamFile); break;
case 2: *out_value = big_endian ? (uint16_t)read_16bitBE(off,streamFile) : (uint16_t)read_16bitLE(off,streamFile); break;
case 3: *out_value = (big_endian ? (uint32_t)read_32bitBE(off,streamFile) : (uint32_t)read_32bitLE(off,streamFile)) & 0x00FFFFFF; break;
case 4: *out_value = big_endian ? (uint32_t)read_32bitBE(off,streamFile) : (uint32_t)read_32bitLE(off,streamFile); break;
case 1: *out_value = read_8bit(offset,streamFile); break;
case 2: *out_value = big_endian ? (uint16_t)read_16bitBE(offset,streamFile) : (uint16_t)read_16bitLE(offset,streamFile); break;
case 3: *out_value = (big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile)) & 0x00FFFFFF; break;
case 4: *out_value = big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile); break;
default: goto fail;
}
}
else { /* constant */
else if (val[0] >= '0' && val[0] <= '9') { /* unsigned constant */
int hex = (val[0]=='0' && val[1]=='x');
if (sscanf(val, hex ? "%x" : "%u", out_value)!=1) goto fail;
if (sscanf(val, hex ? "%x" : "%u", out_value)!=1)
goto fail;
}
else { /* known field */
if (0==strcmp(val,"interleave")) *out_value = txth->interleave;
else if (0==strcmp(val,"channels")) *out_value = txth->channels;
else if (0==strcmp(val,"sample_rate")) *out_value = txth->sample_rate;
else if (0==strcmp(val,"start_offset")) *out_value = txth->start_offset;
else if (0==strcmp(val,"data_size")) *out_value = txth->data_size;
else if (0==strcmp(val,"num_samples")) *out_value = txth->num_samples;
else if (0==strcmp(val,"loop_start_sample")) *out_value = txth->loop_start_sample;
else if (0==strcmp(val,"loop_end_sample")) *out_value = txth->loop_end_sample;
else if (0==strcmp(val,"subsong_count")) *out_value = txth->subsong_count;
else if (0==strcmp(val,"subsong_offset")) *out_value = txth->subsong_offset;
else goto fail;
}
//VGM_LOG("TXTH: value=%s, read %u\n", val, *out_value);
/* operators, but only if current value wasn't set to 0 right before */
if (value_mul && txth->value_mul)
*out_value = (*out_value) * value_mul;
if (value_div && txth->value_div)
*out_value = (*out_value) / value_div;
if (value_add && txth->value_add)
*out_value = (*out_value) + value_add;
if (value_sub && txth->value_sub)
*out_value = (*out_value) - value_sub;
//;VGM_LOG("TXTH: val=%s, read %u (0x%x)\n", val, *out_value, *out_value);
return 1;
fail:
return 0;
@ -725,6 +937,8 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
return ima_bytes_to_samples(bytes, txth->channels);
case AICA:
return aica_bytes_to_samples(bytes, txth->channels);
case PCFX:
return pcfx_bytes_to_samples(bytes, txth->channels);
/* untested */
case SDX2:

Some files were not shown because too many files have changed in this diff Show more