diff --git a/Cog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Cog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Cog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index e3b3ad16e..c7bbcf537 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -11,7 +11,63 @@ 8301659A1F256BD000CA0941 /* txth.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165971F256BD000CA0941 /* txth.c */; }; 8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165981F256BD000CA0941 /* ea_schl_fixed.c */; }; 8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165991F256BD000CA0941 /* nds_strm_ffta2.c */; }; - 830165A21F256BF400CA0941 /* hwas_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165A11F256BF400CA0941 /* hwas_blocked.c */; }; + 8306B08420984518000302D4 /* at3plus_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08120984517000302D4 /* at3plus_decoder.c */; }; + 8306B08520984518000302D4 /* yamaha_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08220984517000302D4 /* yamaha_decoder.c */; }; + 8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08320984517000302D4 /* fadpcm_decoder.c */; }; + 8306B0A220984552000302D4 /* blocked_bdsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0872098454C000302D4 /* blocked_bdsp.c */; }; + 8306B0A320984552000302D4 /* blocked_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0882098454C000302D4 /* blocked_ast.c */; }; + 8306B0A420984552000302D4 /* segmented.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0892098454D000302D4 /* segmented.c */; }; + 8306B0A520984552000302D4 /* blocked_ea_wve_au00.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08A2098454D000302D4 /* blocked_ea_wve_au00.c */; }; + 8306B0A620984552000302D4 /* blocked_ea_wve_ad10.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08B2098454D000302D4 /* blocked_ea_wve_ad10.c */; }; + 8306B0A720984552000302D4 /* blocked_xvas.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08C2098454D000302D4 /* blocked_xvas.c */; }; + 8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08D2098454D000302D4 /* blocked_ps2_iab.c */; }; + 8306B0A920984552000302D4 /* blocked_adm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08E2098454E000302D4 /* blocked_adm.c */; }; + 8306B0AA20984552000302D4 /* blocked_sthd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08F2098454E000302D4 /* blocked_sthd.c */; }; + 8306B0AB20984552000302D4 /* layered.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0902098454E000302D4 /* layered.c */; }; + 8306B0AC20984552000302D4 /* blocked_xa.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0912098454E000302D4 /* blocked_xa.c */; }; + 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 */; }; + 8306B0B920984552000302D4 /* blocked_matx.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B09E20984551000302D4 /* blocked_matx.c */; }; + 8306B0BA20984552000302D4 /* blocked_wsi.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B09F20984551000302D4 /* blocked_wsi.c */; }; + 8306B0BB20984552000302D4 /* blocked_gsb.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0A020984551000302D4 /* blocked_gsb.c */; }; + 8306B0BC20984552000302D4 /* blocked_vs.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0A120984551000302D4 /* blocked_vs.c */; }; + 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */; }; + 8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0BE2098458C000302D4 /* ngc_str_cauldron.c */; }; + 8306B0DA20984590000302D4 /* ea_wve_au00.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0BF2098458C000302D4 /* ea_wve_au00.c */; }; + 8306B0DB20984590000302D4 /* nxap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C02098458C000302D4 /* nxap.c */; }; + 8306B0DC20984590000302D4 /* sthd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C12098458C000302D4 /* sthd.c */; }; + 8306B0DD20984590000302D4 /* waf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C22098458C000302D4 /* waf.c */; }; + 8306B0DE20984590000302D4 /* awc_xma_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C32098458C000302D4 /* awc_xma_streamfile.h */; }; + 8306B0DF20984590000302D4 /* ea_wve_ad10.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C42098458D000302D4 /* ea_wve_ad10.c */; }; + 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C52098458D000302D4 /* ppst_streamfile.h */; }; + 8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */; }; + 8306B0E220984590000302D4 /* smv.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C72098458D000302D4 /* smv.c */; }; + 8306B0E320984590000302D4 /* aax_utf.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C82098458D000302D4 /* aax_utf.h */; }; + 8306B0E420984590000302D4 /* wave.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C92098458E000302D4 /* wave.c */; }; + 8306B0E520984590000302D4 /* ubi_lyn.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CA2098458E000302D4 /* ubi_lyn.c */; }; + 8306B0E620984590000302D4 /* msb_msh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CB2098458E000302D4 /* msb_msh.c */; }; + 8306B0E720984590000302D4 /* opus_ppp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CC2098458E000302D4 /* opus_ppp.c */; }; + 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */; }; + 8306B0E920984590000302D4 /* opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CE2098458E000302D4 /* opus.c */; }; + 8306B0EA20984590000302D4 /* caf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CF2098458F000302D4 /* caf.c */; }; + 8306B0EB20984590000302D4 /* wave_segmented.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0D02098458F000302D4 /* wave_segmented.c */; }; + 8306B0EC20984590000302D4 /* pcm_sre.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0D12098458F000302D4 /* pcm_sre.c */; }; + 8306B0ED20984590000302D4 /* txtp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0D22098458F000302D4 /* txtp.c */; }; + 8306B0EE20984590000302D4 /* smc_smh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0D32098458F000302D4 /* smc_smh.c */; }; + 8306B0EF20984590000302D4 /* ubi_bao.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0D420984590000302D4 /* ubi_bao.c */; }; + 8306B0F020984590000302D4 /* atsl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0D520984590000302D4 /* atsl.c */; }; + 8306B0F120984590000302D4 /* ppst.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0D620984590000302D4 /* ppst.c */; }; + 8306B0F220984590000302D4 /* ubi_jade.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0D720984590000302D4 /* ubi_jade.c */; }; 830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */; }; 830EBE132004656E0023AA10 /* xnb.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE112004656E0023AA10 /* xnb.c */; }; 830EBE142004656E0023AA10 /* ktss.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE122004656E0023AA10 /* ktss.c */; }; @@ -39,7 +95,6 @@ 83345A4A1F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */; }; 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 */; }; - 83345A511F8AEB2800B2EAA4 /* nsw_opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4D1F8AEB2800B2EAA4 /* nsw_opus.c */; }; 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */; }; @@ -72,7 +127,6 @@ 8349A91A1FE6258200E26435 /* vxn.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9011FE6258000E26435 /* vxn.c */; }; 8349A91B1FE6258200E26435 /* adx_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 8349A9021FE6258100E26435 /* adx_keys.h */; }; 8349A91C1FE6258200E26435 /* mogg.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9031FE6258100E26435 /* mogg.c */; }; - 8349A91D1FE6258200E26435 /* aax_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8349A9041FE6258100E26435 /* aax_streamfile.h */; }; 8349A91E1FE6258200E26435 /* bar.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9051FE6258100E26435 /* bar.c */; }; 8349A91F1FE6258200E26435 /* naac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9061FE6258100E26435 /* naac.c */; }; 834D3A6E19F47C98001C54F6 /* g1l.c in Sources */ = {isa = PBXBuildFile; fileRef = 834D3A6D19F47C98001C54F6 /* g1l.c */; }; @@ -84,8 +138,6 @@ 836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE018BDC2180095E648 /* acm_decoder.c */; }; 836F6F1F18BDC2190095E648 /* acm_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6DE118BDC2180095E648 /* acm_decoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE218BDC2180095E648 /* adx_decoder.c */; }; - 836F6F2118BDC2190095E648 /* aica_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE318BDC2180095E648 /* aica_decoder.c */; }; - 836F6F2218BDC2190095E648 /* at3_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE418BDC2180095E648 /* at3_decoder.c */; }; 836F6F2318BDC2190095E648 /* coding.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6DE518BDC2180095E648 /* coding.h */; }; 836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE718BDC2180095E648 /* g721_decoder.c */; }; 836F6F2618BDC2190095E648 /* g7221_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE818BDC2180095E648 /* g7221_decoder.c */; }; @@ -110,34 +162,11 @@ 836F6F3A18BDC2190095E648 /* sdx2_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DFC18BDC2180095E648 /* sdx2_decoder.c */; }; 836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DFD18BDC2180095E648 /* ws_decoder.c */; }; 836F6F3C18BDC2190095E648 /* xa_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DFE18BDC2180095E648 /* xa_decoder.c */; }; - 836F6F3D18BDC2190095E648 /* aax_layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0018BDC2180095E648 /* aax_layout.c */; }; 836F6F3E18BDC2190095E648 /* aix_layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0118BDC2180095E648 /* aix_layout.c */; }; - 836F6F3F18BDC2190095E648 /* ast_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0218BDC2180095E648 /* ast_blocked.c */; }; - 836F6F4018BDC2190095E648 /* bdsp_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0318BDC2180095E648 /* bdsp_blocked.c */; }; 836F6F4118BDC2190095E648 /* blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0418BDC2180095E648 /* blocked.c */; }; - 836F6F4218BDC2190095E648 /* caf_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0518BDC2180095E648 /* caf_blocked.c */; }; - 836F6F4518BDC2190095E648 /* emff_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0818BDC2180095E648 /* emff_blocked.c */; }; - 836F6F4618BDC2190095E648 /* filp_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0918BDC2180095E648 /* filp_blocked.c */; }; - 836F6F4718BDC2190095E648 /* gsb_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0A18BDC2180095E648 /* gsb_blocked.c */; }; - 836F6F4818BDC2190095E648 /* halpst_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0B18BDC2180095E648 /* halpst_blocked.c */; }; - 836F6F4918BDC2190095E648 /* ims_block.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0C18BDC2180095E648 /* ims_block.c */; }; 836F6F4A18BDC2190095E648 /* interleave.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0D18BDC2180095E648 /* interleave.c */; }; 836F6F4D18BDC2190095E648 /* layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6E1018BDC2180095E648 /* layout.h */; }; - 836F6F4F18BDC2190095E648 /* mus_acm_layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1218BDC2180095E648 /* mus_acm_layout.c */; }; - 836F6F5018BDC2190095E648 /* mxch_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1318BDC2180095E648 /* mxch_blocked.c */; }; 836F6F5118BDC2190095E648 /* nolayout.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1418BDC2180095E648 /* nolayout.c */; }; - 836F6F5218BDC2190095E648 /* ps2_adm_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1518BDC2180095E648 /* ps2_adm_blocked.c */; }; - 836F6F5318BDC2190095E648 /* ps2_iab_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1618BDC2180095E648 /* ps2_iab_blocked.c */; }; - 836F6F5418BDC2190095E648 /* ps2_strlr_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1718BDC2180095E648 /* ps2_strlr_blocked.c */; }; - 836F6F5618BDC2190095E648 /* scd_int_layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1918BDC2180095E648 /* scd_int_layout.c */; }; - 836F6F5718BDC2190095E648 /* str_snds_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1A18BDC2180095E648 /* str_snds_blocked.c */; }; - 836F6F5818BDC2190095E648 /* thp_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1B18BDC2180095E648 /* thp_blocked.c */; }; - 836F6F5918BDC2190095E648 /* tra_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1C18BDC2180095E648 /* tra_blocked.c */; }; - 836F6F5A18BDC2190095E648 /* vs_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1D18BDC2180095E648 /* vs_blocked.c */; }; - 836F6F5B18BDC2190095E648 /* ws_aud_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1E18BDC2180095E648 /* ws_aud_blocked.c */; }; - 836F6F5C18BDC2190095E648 /* wsi_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1F18BDC2180095E648 /* wsi_blocked.c */; }; - 836F6F5D18BDC2190095E648 /* xa_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2018BDC2180095E648 /* xa_blocked.c */; }; - 836F6F5E18BDC2190095E648 /* xvas_block.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2118BDC2180095E648 /* xvas_block.c */; }; 836F6F6518BDC2190095E648 /* 2dx9.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2918BDC2180095E648 /* 2dx9.c */; }; 836F6F6618BDC2190095E648 /* aax.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2A18BDC2180095E648 /* aax.c */; }; 836F6F6718BDC2190095E648 /* acm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2B18BDC2180095E648 /* acm.c */; }; @@ -202,8 +231,6 @@ 836F6FA818BDC2190095E648 /* nds_swav.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6C18BDC2180095E648 /* nds_swav.c */; }; 836F6FA918BDC2190095E648 /* ngc_adpdtk.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6D18BDC2180095E648 /* ngc_adpdtk.c */; }; 836F6FAA18BDC2190095E648 /* ngc_bh2pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6E18BDC2180095E648 /* ngc_bh2pcm.c */; }; - 836F6FAB18BDC2190095E648 /* ngc_bo2.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6F18BDC2180095E648 /* ngc_bo2.c */; }; - 836F6FAC18BDC2190095E648 /* ngc_caf.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7018BDC2180095E648 /* ngc_caf.c */; }; 836F6FAD18BDC2190095E648 /* ngc_dsp_konami.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7118BDC2180095E648 /* ngc_dsp_konami.c */; }; 836F6FAE18BDC2190095E648 /* ngc_dsp_mpds.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7218BDC2180095E648 /* ngc_dsp_mpds.c */; }; 836F6FAF18BDC2190095E648 /* ngc_dsp_std.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7318BDC2180095E648 /* ngc_dsp_std.c */; }; @@ -265,7 +292,6 @@ 836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB218BDC2180095E648 /* ps2_p2bt.c */; }; 836F6FEF18BDC2190095E648 /* ps2_pnb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB318BDC2180095E648 /* ps2_pnb.c */; }; 836F6FF018BDC2190095E648 /* ps2_psh.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB418BDC2180095E648 /* ps2_psh.c */; }; - 836F6FF118BDC2190095E648 /* ps2_psw.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB518BDC2180095E648 /* ps2_psw.c */; }; 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB618BDC2180095E648 /* ps2_rnd.c */; }; 836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB718BDC2180095E648 /* ps2_rstm.c */; }; 836F6FF418BDC2190095E648 /* rws.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB818BDC2180095E648 /* rws.c */; }; @@ -348,7 +374,6 @@ 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0E18BDC2190095E648 /* xbox_wavm.c */; }; 836F704B18BDC2190095E648 /* xbox_xmu.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0F18BDC2190095E648 /* xbox_xmu.c */; }; 836F704C18BDC2190095E648 /* xbox_xvas.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1018BDC2190095E648 /* xbox_xvas.c */; }; - 836F704D18BDC2190095E648 /* xbox_xwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1118BDC2190095E648 /* xbox_xwav.c */; }; 836F704E18BDC2190095E648 /* xss.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1218BDC2190095E648 /* xss.c */; }; 836F704F18BDC2190095E648 /* xwb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1318BDC2190095E648 /* xwb.c */; }; 836F705018BDC2190095E648 /* ydsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1418BDC2190095E648 /* ydsp.c */; }; @@ -366,7 +391,6 @@ 83709E071ECBC1A4005C03D3 /* mss.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E011ECBC1A4005C03D3 /* mss.c */; }; 83709E081ECBC1A4005C03D3 /* ps2_rxws.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E021ECBC1A4005C03D3 /* ps2_rxws.c */; }; 83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E031ECBC1A4005C03D3 /* ta_aac.c */; }; - 83709E0A1ECBC1A4005C03D3 /* waa_wac_wad_wam.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E041ECBC1A4005C03D3 /* waa_wac_wad_wam.c */; }; 83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */; }; 83709E0E1ECBC1C3005C03D3 /* psv_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */; }; 8374EE3E1F787AB600033E90 /* ffmpeg_decoder_utils_ea_xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */; }; @@ -388,7 +412,6 @@ 838BDB7D1D3B1FCC0022CA6F /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB7C1D3B1FCC0022CA6F /* CoreVideo.framework */; }; 838BDB7F1D3B1FD10022CA6F /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB7E1D3B1FD10022CA6F /* Cocoa.framework */; }; 839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BD11F1EEE1CF200198540 /* ngc_ulw.c */; }; - 839B54571EEE1DA000048A2D /* rws_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BD1201EEE1D2A00198540 /* rws_blocked.c */; }; 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */; }; 839E21E11F2EDAF100EE54D7 /* vorbis_custom_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */; }; 839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */; }; @@ -405,7 +428,6 @@ 83A21F86201D8981000F04B9 /* xwc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7D201D8980000F04B9 /* xwc.c */; }; 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A21F7E201D8980000F04B9 /* fsb_keys.h */; }; 83A21F88201D8981000F04B9 /* ogg_vorbis.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */; }; - 83A21F89201D8982000F04B9 /* atsl3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F80201D8980000F04B9 /* atsl3.c */; }; 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F81201D8981000F04B9 /* fsb_encrypted.c */; }; 83A21F8B201D8982000F04B9 /* sps_n1.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F82201D8981000F04B9 /* sps_n1.c */; }; 83A21F8C201D8982000F04B9 /* kma9.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F83201D8981000F04B9 /* kma9.c */; }; @@ -572,7 +594,63 @@ 830165971F256BD000CA0941 /* txth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = txth.c; sourceTree = ""; }; 830165981F256BD000CA0941 /* ea_schl_fixed.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl_fixed.c; sourceTree = ""; }; 830165991F256BD000CA0941 /* nds_strm_ffta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_strm_ffta2.c; sourceTree = ""; }; - 830165A11F256BF400CA0941 /* hwas_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hwas_blocked.c; sourceTree = ""; }; + 8306B08120984517000302D4 /* at3plus_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = at3plus_decoder.c; sourceTree = ""; }; + 8306B08220984517000302D4 /* yamaha_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yamaha_decoder.c; sourceTree = ""; }; + 8306B08320984517000302D4 /* fadpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fadpcm_decoder.c; sourceTree = ""; }; + 8306B0872098454C000302D4 /* blocked_bdsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_bdsp.c; sourceTree = ""; }; + 8306B0882098454C000302D4 /* blocked_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ast.c; sourceTree = ""; }; + 8306B0892098454D000302D4 /* segmented.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = segmented.c; sourceTree = ""; }; + 8306B08A2098454D000302D4 /* blocked_ea_wve_au00.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_wve_au00.c; sourceTree = ""; }; + 8306B08B2098454D000302D4 /* blocked_ea_wve_ad10.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_wve_ad10.c; sourceTree = ""; }; + 8306B08C2098454D000302D4 /* blocked_xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xvas.c; sourceTree = ""; }; + 8306B08D2098454D000302D4 /* blocked_ps2_iab.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ps2_iab.c; sourceTree = ""; }; + 8306B08E2098454E000302D4 /* blocked_adm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_adm.c; sourceTree = ""; }; + 8306B08F2098454E000302D4 /* blocked_sthd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_sthd.c; sourceTree = ""; }; + 8306B0902098454E000302D4 /* layered.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = layered.c; sourceTree = ""; }; + 8306B0912098454E000302D4 /* blocked_xa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xa.c; sourceTree = ""; }; + 8306B0922098454E000302D4 /* blocked_caf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_caf.c; sourceTree = ""; }; + 8306B0932098454F000302D4 /* blocked_filp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_filp.c; sourceTree = ""; }; + 8306B0942098454F000302D4 /* blocked_rws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_rws.c; sourceTree = ""; }; + 8306B0952098454F000302D4 /* blocked_ps2_strlr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ps2_strlr.c; sourceTree = ""; }; + 8306B0962098454F000302D4 /* blocked_halpst.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_halpst.c; sourceTree = ""; }; + 8306B0972098454F000302D4 /* blocked_mxch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_mxch.c; sourceTree = ""; }; + 8306B09820984550000302D4 /* blocked_thp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_thp.c; sourceTree = ""; }; + 8306B09920984550000302D4 /* blocked_tra.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_tra.c; sourceTree = ""; }; + 8306B09A20984550000302D4 /* blocked_emff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_emff.c; sourceTree = ""; }; + 8306B09B20984550000302D4 /* blocked_hwas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_hwas.c; sourceTree = ""; }; + 8306B09C20984550000302D4 /* blocked_str_snds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_str_snds.c; sourceTree = ""; }; + 8306B09D20984551000302D4 /* blocked_ws_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ws_aud.c; sourceTree = ""; }; + 8306B09E20984551000302D4 /* blocked_matx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_matx.c; sourceTree = ""; }; + 8306B09F20984551000302D4 /* blocked_wsi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_wsi.c; sourceTree = ""; }; + 8306B0A020984551000302D4 /* blocked_gsb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_gsb.c; sourceTree = ""; }; + 8306B0A120984551000302D4 /* blocked_vs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vs.c; sourceTree = ""; }; + 8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_eaac_streamfile.h; sourceTree = ""; }; + 8306B0BE2098458C000302D4 /* ngc_str_cauldron.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_str_cauldron.c; sourceTree = ""; }; + 8306B0BF2098458C000302D4 /* ea_wve_au00.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_wve_au00.c; sourceTree = ""; }; + 8306B0C02098458C000302D4 /* nxap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nxap.c; sourceTree = ""; }; + 8306B0C12098458C000302D4 /* sthd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sthd.c; sourceTree = ""; }; + 8306B0C22098458C000302D4 /* waf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = waf.c; sourceTree = ""; }; + 8306B0C32098458C000302D4 /* awc_xma_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = awc_xma_streamfile.h; sourceTree = ""; }; + 8306B0C42098458D000302D4 /* ea_wve_ad10.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_wve_ad10.c; sourceTree = ""; }; + 8306B0C52098458D000302D4 /* ppst_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ppst_streamfile.h; sourceTree = ""; }; + 8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_lyn_ogg_streamfile.h; sourceTree = ""; }; + 8306B0C72098458D000302D4 /* smv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smv.c; sourceTree = ""; }; + 8306B0C82098458D000302D4 /* aax_utf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aax_utf.h; sourceTree = ""; }; + 8306B0C92098458E000302D4 /* wave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wave.c; sourceTree = ""; }; + 8306B0CA2098458E000302D4 /* ubi_lyn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_lyn.c; sourceTree = ""; }; + 8306B0CB2098458E000302D4 /* msb_msh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msb_msh.c; sourceTree = ""; }; + 8306B0CC2098458E000302D4 /* opus_ppp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opus_ppp.c; sourceTree = ""; }; + 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opus_interleave_streamfile.h; sourceTree = ""; }; + 8306B0CE2098458E000302D4 /* opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opus.c; sourceTree = ""; }; + 8306B0CF2098458F000302D4 /* caf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = caf.c; sourceTree = ""; }; + 8306B0D02098458F000302D4 /* wave_segmented.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wave_segmented.c; sourceTree = ""; }; + 8306B0D12098458F000302D4 /* pcm_sre.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcm_sre.c; sourceTree = ""; }; + 8306B0D22098458F000302D4 /* txtp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = txtp.c; sourceTree = ""; }; + 8306B0D32098458F000302D4 /* smc_smh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smc_smh.c; sourceTree = ""; }; + 8306B0D420984590000302D4 /* ubi_bao.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_bao.c; sourceTree = ""; }; + 8306B0D520984590000302D4 /* atsl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atsl.c; sourceTree = ""; }; + 8306B0D620984590000302D4 /* ppst.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ppst.c; sourceTree = ""; }; + 8306B0D720984590000302D4 /* ubi_jade.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_jade.c; sourceTree = ""; }; 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libatrac9.xcodeproj; path = ../libatrac9/libatrac9.xcodeproj; sourceTree = ""; }; 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atrac9_decoder.c; sourceTree = ""; }; 830EBE112004656E0023AA10 /* xnb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xnb.c; sourceTree = ""; }; @@ -589,7 +667,6 @@ 831BA6171EAC61A500CF89B0 /* x360_pasx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_pasx.c; sourceTree = ""; }; 831BA6221EAC61CB00CF89B0 /* coding_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coding_utils.c; sourceTree = ""; }; 831BD11F1EEE1CF200198540 /* ngc_ulw.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ngc_ulw.c; sourceTree = ""; }; - 831BD1201EEE1D2A00198540 /* rws_blocked.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rws_blocked.c; sourceTree = ""; }; 832389481D22419B00482226 /* clHCA.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = clHCA.c; sourceTree = ""; }; 832389491D22419B00482226 /* clHCA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clHCA.h; sourceTree = ""; }; 8323894F1D2246C300482226 /* hca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca.c; sourceTree = ""; }; @@ -599,7 +676,6 @@ 83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils_switch_opus.c; sourceTree = ""; }; 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_xma.c; sourceTree = ""; }; 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = ""; }; - 83345A4D1F8AEB2800B2EAA4 /* nsw_opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nsw_opus.c; sourceTree = ""; }; 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = ""; }; 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = ""; }; @@ -632,7 +708,6 @@ 8349A9011FE6258000E26435 /* vxn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vxn.c; sourceTree = ""; }; 8349A9021FE6258100E26435 /* adx_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adx_keys.h; sourceTree = ""; }; 8349A9031FE6258100E26435 /* mogg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mogg.c; sourceTree = ""; }; - 8349A9041FE6258100E26435 /* aax_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aax_streamfile.h; sourceTree = ""; }; 8349A9051FE6258100E26435 /* bar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bar.c; sourceTree = ""; }; 8349A9061FE6258100E26435 /* naac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = naac.c; sourceTree = ""; }; 834D3A6D19F47C98001C54F6 /* g1l.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g1l.c; sourceTree = ""; }; @@ -646,8 +721,6 @@ 836F6DE018BDC2180095E648 /* acm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm_decoder.c; sourceTree = ""; }; 836F6DE118BDC2180095E648 /* acm_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acm_decoder.h; sourceTree = ""; }; 836F6DE218BDC2180095E648 /* adx_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adx_decoder.c; sourceTree = ""; }; - 836F6DE318BDC2180095E648 /* aica_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aica_decoder.c; sourceTree = ""; }; - 836F6DE418BDC2180095E648 /* at3_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = at3_decoder.c; sourceTree = ""; }; 836F6DE518BDC2180095E648 /* coding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding.h; sourceTree = ""; }; 836F6DE718BDC2180095E648 /* g721_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g721_decoder.c; sourceTree = ""; }; 836F6DE818BDC2180095E648 /* g7221_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g7221_decoder.c; sourceTree = ""; }; @@ -672,34 +745,11 @@ 836F6DFC18BDC2180095E648 /* sdx2_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sdx2_decoder.c; sourceTree = ""; }; 836F6DFD18BDC2180095E648 /* ws_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ws_decoder.c; sourceTree = ""; }; 836F6DFE18BDC2180095E648 /* xa_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_decoder.c; sourceTree = ""; }; - 836F6E0018BDC2180095E648 /* aax_layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aax_layout.c; sourceTree = ""; }; 836F6E0118BDC2180095E648 /* aix_layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aix_layout.c; sourceTree = ""; }; - 836F6E0218BDC2180095E648 /* ast_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ast_blocked.c; sourceTree = ""; }; - 836F6E0318BDC2180095E648 /* bdsp_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bdsp_blocked.c; sourceTree = ""; }; 836F6E0418BDC2180095E648 /* blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked.c; sourceTree = ""; }; - 836F6E0518BDC2180095E648 /* caf_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = caf_blocked.c; sourceTree = ""; }; - 836F6E0818BDC2180095E648 /* emff_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emff_blocked.c; sourceTree = ""; }; - 836F6E0918BDC2180095E648 /* filp_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filp_blocked.c; sourceTree = ""; }; - 836F6E0A18BDC2180095E648 /* gsb_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gsb_blocked.c; sourceTree = ""; }; - 836F6E0B18BDC2180095E648 /* halpst_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = halpst_blocked.c; sourceTree = ""; }; - 836F6E0C18BDC2180095E648 /* ims_block.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ims_block.c; sourceTree = ""; }; 836F6E0D18BDC2180095E648 /* interleave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interleave.c; sourceTree = ""; }; 836F6E1018BDC2180095E648 /* layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = layout.h; sourceTree = ""; }; - 836F6E1218BDC2180095E648 /* mus_acm_layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mus_acm_layout.c; sourceTree = ""; }; - 836F6E1318BDC2180095E648 /* mxch_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mxch_blocked.c; sourceTree = ""; }; 836F6E1418BDC2180095E648 /* nolayout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nolayout.c; sourceTree = ""; }; - 836F6E1518BDC2180095E648 /* ps2_adm_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_adm_blocked.c; sourceTree = ""; }; - 836F6E1618BDC2180095E648 /* ps2_iab_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_iab_blocked.c; sourceTree = ""; }; - 836F6E1718BDC2180095E648 /* ps2_strlr_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_strlr_blocked.c; sourceTree = ""; }; - 836F6E1918BDC2180095E648 /* scd_int_layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scd_int_layout.c; sourceTree = ""; }; - 836F6E1A18BDC2180095E648 /* str_snds_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = str_snds_blocked.c; sourceTree = ""; }; - 836F6E1B18BDC2180095E648 /* thp_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = thp_blocked.c; sourceTree = ""; }; - 836F6E1C18BDC2180095E648 /* tra_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tra_blocked.c; sourceTree = ""; }; - 836F6E1D18BDC2180095E648 /* vs_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vs_blocked.c; sourceTree = ""; }; - 836F6E1E18BDC2180095E648 /* ws_aud_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ws_aud_blocked.c; sourceTree = ""; }; - 836F6E1F18BDC2180095E648 /* wsi_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wsi_blocked.c; sourceTree = ""; }; - 836F6E2018BDC2180095E648 /* xa_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_blocked.c; sourceTree = ""; }; - 836F6E2118BDC2180095E648 /* xvas_block.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvas_block.c; sourceTree = ""; }; 836F6E2918BDC2180095E648 /* 2dx9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2dx9.c; sourceTree = ""; }; 836F6E2A18BDC2180095E648 /* aax.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aax.c; sourceTree = ""; }; 836F6E2B18BDC2180095E648 /* acm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm.c; sourceTree = ""; }; @@ -764,8 +814,6 @@ 836F6E6C18BDC2180095E648 /* nds_swav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_swav.c; sourceTree = ""; }; 836F6E6D18BDC2180095E648 /* ngc_adpdtk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_adpdtk.c; sourceTree = ""; }; 836F6E6E18BDC2180095E648 /* ngc_bh2pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_bh2pcm.c; sourceTree = ""; }; - 836F6E6F18BDC2180095E648 /* ngc_bo2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_bo2.c; sourceTree = ""; }; - 836F6E7018BDC2180095E648 /* ngc_caf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_caf.c; sourceTree = ""; }; 836F6E7118BDC2180095E648 /* ngc_dsp_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_dsp_konami.c; sourceTree = ""; }; 836F6E7218BDC2180095E648 /* ngc_dsp_mpds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_dsp_mpds.c; sourceTree = ""; }; 836F6E7318BDC2180095E648 /* ngc_dsp_std.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_dsp_std.c; sourceTree = ""; }; @@ -827,7 +875,6 @@ 836F6EB218BDC2180095E648 /* ps2_p2bt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_p2bt.c; sourceTree = ""; }; 836F6EB318BDC2180095E648 /* ps2_pnb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_pnb.c; sourceTree = ""; }; 836F6EB418BDC2180095E648 /* ps2_psh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_psh.c; sourceTree = ""; }; - 836F6EB518BDC2180095E648 /* ps2_psw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_psw.c; sourceTree = ""; }; 836F6EB618BDC2180095E648 /* ps2_rnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_rnd.c; sourceTree = ""; }; 836F6EB718BDC2180095E648 /* ps2_rstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_rstm.c; sourceTree = ""; }; 836F6EB818BDC2180095E648 /* rws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rws.c; sourceTree = ""; }; @@ -910,7 +957,6 @@ 836F6F0E18BDC2190095E648 /* xbox_wavm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_wavm.c; sourceTree = ""; }; 836F6F0F18BDC2190095E648 /* xbox_xmu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xmu.c; sourceTree = ""; }; 836F6F1018BDC2190095E648 /* xbox_xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xvas.c; sourceTree = ""; }; - 836F6F1118BDC2190095E648 /* xbox_xwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox_xwav.c; sourceTree = ""; }; 836F6F1218BDC2190095E648 /* xss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xss.c; sourceTree = ""; }; 836F6F1318BDC2190095E648 /* xwb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwb.c; sourceTree = ""; }; 836F6F1418BDC2190095E648 /* ydsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ydsp.c; sourceTree = ""; }; @@ -928,7 +974,6 @@ 83709E011ECBC1A4005C03D3 /* mss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mss.c; sourceTree = ""; }; 83709E021ECBC1A4005C03D3 /* ps2_rxws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_rxws.c; sourceTree = ""; }; 83709E031ECBC1A4005C03D3 /* ta_aac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ta_aac.c; sourceTree = ""; }; - 83709E041ECBC1A4005C03D3 /* waa_wac_wad_wam.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = waa_wac_wad_wam.c; sourceTree = ""; }; 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mc3_decoder.c; sourceTree = ""; }; 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psv_decoder.c; sourceTree = ""; }; 8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils_ea_xma.c; sourceTree = ""; }; @@ -965,7 +1010,6 @@ 83A21F7D201D8980000F04B9 /* xwc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwc.c; sourceTree = ""; }; 83A21F7E201D8980000F04B9 /* fsb_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_keys.h; sourceTree = ""; }; 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_vorbis.c; sourceTree = ""; }; - 83A21F80201D8980000F04B9 /* atsl3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atsl3.c; sourceTree = ""; }; 83A21F81201D8981000F04B9 /* fsb_encrypted.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb_encrypted.c; sourceTree = ""; }; 83A21F82201D8981000F04B9 /* sps_n1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sps_n1.c; sourceTree = ""; }; 83A21F83201D8981000F04B9 /* kma9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kma9.c; sourceTree = ""; }; @@ -1153,14 +1197,14 @@ 836F6DE018BDC2180095E648 /* acm_decoder.c */, 836F6DE118BDC2180095E648 /* acm_decoder.h */, 836F6DE218BDC2180095E648 /* adx_decoder.c */, - 836F6DE318BDC2180095E648 /* aica_decoder.c */, - 836F6DE418BDC2180095E648 /* at3_decoder.c */, + 8306B08120984517000302D4 /* at3plus_decoder.c */, 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, 831BA6221EAC61CB00CF89B0 /* coding_utils.c */, 836F6DE518BDC2180095E648 /* coding.h */, 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */, 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */, 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */, + 8306B08320984517000302D4 /* fadpcm_decoder.c */, 8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */, 83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */, 8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */, @@ -1208,6 +1252,7 @@ 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, 836F6DFD18BDC2180095E648 /* ws_decoder.c */, 836F6DFE18BDC2180095E648 /* xa_decoder.c */, + 8306B08220984517000302D4 /* yamaha_decoder.c */, ); path = coding; sourceTree = ""; @@ -1215,46 +1260,48 @@ 836F6DFF18BDC2180095E648 /* layout */ = { isa = PBXGroup; children = ( - 836F6E0018BDC2180095E648 /* aax_layout.c */, 836F6E0118BDC2180095E648 /* aix_layout.c */, - 836F6E0218BDC2180095E648 /* ast_blocked.c */, - 836F6E0318BDC2180095E648 /* bdsp_blocked.c */, + 8306B08E2098454E000302D4 /* blocked_adm.c */, + 8306B0882098454C000302D4 /* blocked_ast.c */, 83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */, + 8306B0872098454C000302D4 /* blocked_bdsp.c */, + 8306B0922098454E000302D4 /* blocked_caf.c */, 8349A8E21FE6253800E26435 /* blocked_dec.c */, 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */, 8349A8E41FE6253800E26435 /* blocked_ea_schl.c */, 8349A8E71FE6253900E26435 /* blocked_ea_sns.c */, 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 */, + 8306B0962098454F000302D4 /* blocked_halpst.c */, + 8306B09B20984550000302D4 /* blocked_hwas.c */, 8349A8E51FE6253800E26435 /* blocked_ivaud.c */, + 8306B09E20984551000302D4 /* blocked_matx.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 */, + 8306B09820984550000302D4 /* blocked_thp.c */, + 8306B09920984550000302D4 /* blocked_tra.c */, 8349A8E61FE6253900E26435 /* blocked_vawx.c */, 83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */, + 8306B0A120984551000302D4 /* blocked_vs.c */, + 8306B09D20984551000302D4 /* blocked_ws_aud.c */, + 8306B09F20984551000302D4 /* blocked_wsi.c */, + 8306B0912098454E000302D4 /* blocked_xa.c */, 83A21F7A201D895B000F04B9 /* blocked_xvag.c */, + 8306B08C2098454D000302D4 /* blocked_xvas.c */, 836F6E0418BDC2180095E648 /* blocked.c */, - 836F6E0518BDC2180095E648 /* caf_blocked.c */, - 836F6E0818BDC2180095E648 /* emff_blocked.c */, - 836F6E0918BDC2180095E648 /* filp_blocked.c */, - 836F6E0A18BDC2180095E648 /* gsb_blocked.c */, - 836F6E0B18BDC2180095E648 /* halpst_blocked.c */, - 830165A11F256BF400CA0941 /* hwas_blocked.c */, - 836F6E0C18BDC2180095E648 /* ims_block.c */, 836F6E0D18BDC2180095E648 /* interleave.c */, + 8306B0902098454E000302D4 /* layered.c */, 836F6E1018BDC2180095E648 /* layout.h */, - 836F6E1218BDC2180095E648 /* mus_acm_layout.c */, - 836F6E1318BDC2180095E648 /* mxch_blocked.c */, 836F6E1418BDC2180095E648 /* nolayout.c */, - 836F6E1518BDC2180095E648 /* ps2_adm_blocked.c */, - 836F6E1618BDC2180095E648 /* ps2_iab_blocked.c */, - 836F6E1718BDC2180095E648 /* ps2_strlr_blocked.c */, - 831BD1201EEE1D2A00198540 /* rws_blocked.c */, - 836F6E1918BDC2180095E648 /* scd_int_layout.c */, - 836F6E1A18BDC2180095E648 /* str_snds_blocked.c */, - 836F6E1B18BDC2180095E648 /* thp_blocked.c */, - 836F6E1C18BDC2180095E648 /* tra_blocked.c */, - 836F6E1D18BDC2180095E648 /* vs_blocked.c */, - 836F6E1E18BDC2180095E648 /* ws_aud_blocked.c */, - 836F6E1F18BDC2180095E648 /* wsi_blocked.c */, - 836F6E2018BDC2180095E648 /* xa_blocked.c */, - 836F6E2118BDC2180095E648 /* xvas_block.c */, + 8306B0892098454D000302D4 /* segmented.c */, ); path = layout; sourceTree = ""; @@ -1263,7 +1310,7 @@ isa = PBXGroup; children = ( 836F6E2918BDC2180095E648 /* 2dx9.c */, - 8349A9041FE6258100E26435 /* aax_streamfile.h */, + 8306B0C82098458D000302D4 /* aax_utf.h */, 836F6E2A18BDC2180095E648 /* aax.c */, 836F6E2B18BDC2180095E648 /* acm.c */, 836F6E2C18BDC2180095E648 /* ads.c */, @@ -1278,9 +1325,10 @@ 836F6E3318BDC2180095E648 /* akb.c */, 836F6E3418BDC2180095E648 /* apple_caff.c */, 836F6E3518BDC2180095E648 /* ast.c */, - 83A21F80201D8980000F04B9 /* atsl3.c */, + 8306B0D520984590000302D4 /* atsl.c */, 83A21F7C201D897F000F04B9 /* atx.c */, 83EED5D2203A8BC7008BEB45 /* aus.c */, + 8306B0C32098458C000302D4 /* awc_xma_streamfile.h */, 83AA5D201F6E2F9B0020821C /* awc.c */, 836F6E3618BDC2180095E648 /* baf.c */, 8349A8F81FE6257E00E26435 /* bar_streamfile.h */, @@ -1293,6 +1341,7 @@ 836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3A18BDC2180095E648 /* brstm.c */, 83EDE5D71A70951A005F5D84 /* btsnd.c */, + 8306B0CF2098458F000302D4 /* caf.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */, 836F6E3C18BDC2180095E648 /* Cstr.c */, 836F6E3D18BDC2180095E648 /* dc_asd.c */, @@ -1306,10 +1355,13 @@ 836F6E4418BDC2180095E648 /* dsp_bdsp.c */, 836F6E4518BDC2180095E648 /* dsp_sth_str.c */, 8349A8FF1FE6258000E26435 /* ea_1snh.c */, + 8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */, 8349A8F71FE6257E00E26435 /* ea_eaac.c */, 830165981F256BD000CA0941 /* ea_schl_fixed.c */, 836F6E4618BDC2180095E648 /* ea_schl.c */, 83EED5D1203A8BC7008BEB45 /* ea_swvr.c */, + 8306B0C42098458D000302D4 /* ea_wve_ad10.c */, + 8306B0BF2098458C000302D4 /* ea_wve_au00.c */, 836F6E4818BDC2180095E648 /* emff.c */, 836F6E4918BDC2180095E648 /* exakt_sc.c */, 836F6E4A18BDC2180095E648 /* excitebots.c */, @@ -1348,6 +1400,7 @@ 836F6E5F18BDC2180095E648 /* mn_str.c */, 8349A9031FE6258100E26435 /* mogg.c */, 836F6E6018BDC2180095E648 /* mp4.c */, + 8306B0CB2098458E000302D4 /* msb_msh.c */, 83709E011ECBC1A4005C03D3 /* mss.c */, 836F6E6118BDC2180095E648 /* msvp.c */, 836F6E6218BDC2180095E648 /* mus_acm.c */, @@ -1365,8 +1418,6 @@ 836F6E6C18BDC2180095E648 /* nds_swav.c */, 836F6E6D18BDC2180095E648 /* ngc_adpdtk.c */, 836F6E6E18BDC2180095E648 /* ngc_bh2pcm.c */, - 836F6E6F18BDC2180095E648 /* ngc_bo2.c */, - 836F6E7018BDC2180095E648 /* ngc_caf.c */, 836F6E7118BDC2180095E648 /* ngc_dsp_konami.c */, 836F6E7218BDC2180095E648 /* ngc_dsp_mpds.c */, 836F6E7318BDC2180095E648 /* ngc_dsp_std.c */, @@ -1378,18 +1429,22 @@ 836F6E7918BDC2180095E648 /* ngc_pdt.c */, 836F6E7A18BDC2180095E648 /* ngc_sck_dsp.c */, 836F6E7B18BDC2180095E648 /* ngc_ssm.c */, + 8306B0BE2098458C000302D4 /* ngc_str_cauldron.c */, 836F6E7C18BDC2180095E648 /* ngc_tydsp.c */, 831BD11F1EEE1CF200198540 /* ngc_ulw.c */, 8349A8FA1FE6257E00E26435 /* ngc_vid1.c */, 836F6E7E18BDC2180095E648 /* ngc_ymf.c */, 836F6E7F18BDC2180095E648 /* ngca.c */, - 83345A4D1F8AEB2800B2EAA4 /* nsw_opus.c */, 83AB8C731E8072A100086084 /* nub_vag.c */, 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */, 836F6E8118BDC2180095E648 /* nwa.c */, + 8306B0C02098458C000302D4 /* nxap.c */, 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */, 831BA60F1EAC61A500CF89B0 /* ogl.c */, 8349A8FB1FE6257F00E26435 /* omu.c */, + 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */, + 8306B0CC2098458E000302D4 /* opus_ppp.c */, + 8306B0CE2098458E000302D4 /* opus.c */, 836F6E8318BDC2180095E648 /* otm.c */, 836F6E8418BDC2180095E648 /* p3d.c */, 8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */, @@ -1400,8 +1455,11 @@ 836F6E8718BDC2180095E648 /* pc_smp.c */, 836F6E8818BDC2180095E648 /* pc_snds.c */, 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */, + 8306B0D12098458F000302D4 /* pcm_sre.c */, 836F6E8B18BDC2180095E648 /* pona.c */, 836F6E8C18BDC2180095E648 /* pos.c */, + 8306B0C52098458D000302D4 /* ppst_streamfile.h */, + 8306B0D620984590000302D4 /* ppst.c */, 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */, 836F6E8E18BDC2180095E648 /* ps2_adm.c */, 836F6E8F18BDC2180095E648 /* ps2_ads.c */, @@ -1441,7 +1499,6 @@ 8349A8F21FE6257D00E26435 /* ps2_pcm.c */, 836F6EB318BDC2180095E648 /* ps2_pnb.c */, 836F6EB418BDC2180095E648 /* ps2_psh.c */, - 836F6EB518BDC2180095E648 /* ps2_psw.c */, 836F6EB618BDC2180095E648 /* ps2_rnd.c */, 836F6EB718BDC2180095E648 /* ps2_rstm.c */, 83709E021ECBC1A4005C03D3 /* ps2_rxws.c */, @@ -1505,11 +1562,14 @@ 831BA6111EAC61A500CF89B0 /* sgxd.c */, 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, 836F6EF218BDC2190095E648 /* sli.c */, + 8306B0D32098458F000302D4 /* smc_smh.c */, + 8306B0C72098458D000302D4 /* smv.c */, 83A21F82201D8981000F04B9 /* sps_n1.c */, 836F6EF318BDC2190095E648 /* spt_spd.c */, 8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */, 836F6EF418BDC2190095E648 /* sqex_scd.c */, 83A21F84201D8981000F04B9 /* sqex_sead.c */, + 8306B0C12098458C000302D4 /* sthd.c */, 83AA5D231F6E2F9C0020821C /* stm.c */, 836F6EF618BDC2190095E648 /* str_asr.c */, 836F6EF718BDC2190095E648 /* str_snds.c */, @@ -1520,7 +1580,12 @@ 836F6EFA18BDC2190095E648 /* thp.c */, 836F6EFB18BDC2190095E648 /* tun.c */, 830165971F256BD000CA0941 /* txth.c */, + 8306B0D22098458F000302D4 /* txtp.c */, + 8306B0D420984590000302D4 /* ubi_bao.c */, 836F6EFC18BDC2190095E648 /* ubi_ckd.c */, + 8306B0D720984590000302D4 /* ubi_jade.c */, + 8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */, + 8306B0CA2098458E000302D4 /* ubi_lyn.c */, 831BA6131EAC61A500CF89B0 /* ubi_raki.c */, 8349A8F41FE6257D00E26435 /* ubi_sb.c */, 831BA6141EAC61A500CF89B0 /* vawx.c */, @@ -1529,7 +1594,9 @@ 8349A8F91FE6257E00E26435 /* vsf_tta.c */, 836F6EFF18BDC2190095E648 /* vsf.c */, 8349A9011FE6258000E26435 /* vxn.c */, - 83709E041ECBC1A4005C03D3 /* waa_wac_wad_wam.c */, + 8306B0C22098458C000302D4 /* waf.c */, + 8306B0D02098458F000302D4 /* wave_segmented.c */, + 8306B0C92098458E000302D4 /* wave.c */, 83CAB8E11F0B0745001BC993 /* wii_04sw.c */, 836F6F0018BDC2190095E648 /* wii_bns.c */, 836F6F0118BDC2190095E648 /* wii_mus.c */, @@ -1552,7 +1619,6 @@ 836F6F0E18BDC2190095E648 /* xbox_wavm.c */, 836F6F0F18BDC2190095E648 /* xbox_xmu.c */, 836F6F1018BDC2190095E648 /* xbox_xvas.c */, - 836F6F1118BDC2190095E648 /* xbox_xwav.c */, 8350C0541E071881009E0A93 /* xma.c */, 830EBE112004656E0023AA10 /* xnb.c */, 836F6F1218BDC2190095E648 /* xss.c */, @@ -1603,11 +1669,11 @@ 836F705518BDC2190095E648 /* streamtypes.h in Headers */, 836F6F1F18BDC2190095E648 /* acm_decoder.h in Headers */, 836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */, - 8349A91D1FE6258200E26435 /* aax_streamfile.h in Headers */, 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, 8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */, 836F6F2718BDC2190095E648 /* g72x_state.h in Headers */, + 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */, 836F705418BDC2190095E648 /* streamfile.h in Headers */, 8374EE401F787AB600033E90 /* ffmpeg_decoder_utils.h in Headers */, @@ -1615,11 +1681,16 @@ 8323894B1D22419B00482226 /* clHCA.h in Headers */, 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */, 839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */, + 8306B0E320984590000302D4 /* aax_utf.h in Headers */, + 8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */, 836F705918BDC2190095E648 /* vgmstream.h in Headers */, + 8306B0DE20984590000302D4 /* awc_xma_streamfile.h in Headers */, 839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */, 48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */, 836F705718BDC2190095E648 /* util.h in Headers */, 836F6F9A18BDC2190095E648 /* meta.h in Headers */, + 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, + 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */, 8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */, 8349A91B1FE6258200E26435 /* adx_keys.h in Headers */, 836F6F4D18BDC2190095E648 /* layout.h in Headers */, @@ -1787,12 +1858,14 @@ files = ( 839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */, 83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */, + 8306B0B020984552000302D4 /* blocked_ps2_strlr.c in Sources */, 8349A9071FE6258200E26435 /* dec.c in Sources */, 839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.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 */, 839E21E31F2EDAF100EE54D7 /* mpeg_custom_utils_ahx.c in Sources */, + 8306B0AF20984552000302D4 /* blocked_rws.c in Sources */, 839E21EB1F2EDB0600EE54D7 /* sk_aud.c in Sources */, 8301659A1F256BD000CA0941 /* txth.c in Sources */, 8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */, @@ -1801,16 +1874,14 @@ 83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */, 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */, 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */, - 839B54571EEE1DA000048A2D /* rws_blocked.c in Sources */, 83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */, 839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */, + 8306B0EE20984590000302D4 /* smc_smh.c in Sources */, 836F6FAD18BDC2190095E648 /* ngc_dsp_konami.c in Sources */, 836F6FF818BDC2190095E648 /* ps2_smpl.c in Sources */, 836F6F8118BDC2190095E648 /* dsp_sth_str.c in Sources */, - 836F6F5E18BDC2190095E648 /* xvas_block.c in Sources */, 836F703818BDC2190095E648 /* ubi_ckd.c in Sources */, 836F705318BDC2190095E648 /* streamfile.c in Sources */, - 836F6F5C18BDC2190095E648 /* wsi_blocked.c in Sources */, 836F6F7418BDC2190095E648 /* bgw.c in Sources */, 836F6F7218BDC2190095E648 /* baf.c in Sources */, 83F5F8831908D0A400C8E65F /* fsb5.c in Sources */, @@ -1825,7 +1896,7 @@ 836F6F2F18BDC2190095E648 /* mtaf_decoder.c in Sources */, 83AA5D161F6E2F600020821C /* ea_xa_decoder.c in Sources */, 836F6F9B18BDC2190095E648 /* mn_str.c in Sources */, - 836F6F5918BDC2190095E648 /* tra_blocked.c in Sources */, + 8306B0EB20984590000302D4 /* wave_segmented.c in Sources */, 836F6F9F18BDC2190095E648 /* musc.c in Sources */, 8349A9121FE6258200E26435 /* vsf_tta.c in Sources */, 836F6FCA18BDC2190095E648 /* ps2_adm.c in Sources */, @@ -1837,26 +1908,30 @@ 836F700018BDC2190095E648 /* ps2_svag.c in Sources */, 836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */, 836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */, + 8306B0DD20984590000302D4 /* waf.c in Sources */, + 8306B0B320984552000302D4 /* blocked_thp.c in Sources */, 836F702318BDC2190095E648 /* rsf.c in Sources */, 83299FD01E7660C7003A3242 /* bik.c in Sources */, 836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */, + 8306B0EF20984590000302D4 /* ubi_bao.c in Sources */, 836F6FBB18BDC2190095E648 /* ngca.c in Sources */, + 8306B0E220984590000302D4 /* smv.c in Sources */, 8349A91E1FE6258200E26435 /* bar.c in Sources */, - 836F6F5218BDC2190095E648 /* ps2_adm_blocked.c in Sources */, 832389521D224C0800482226 /* hca_decoder.c in Sources */, 836F6F9418BDC2190095E648 /* ivb.c in Sources */, - 836F6FF118BDC2190095E648 /* ps2_psw.c in Sources */, 836F6F8D18BDC2190095E648 /* gsp_gsb.c in Sources */, 836F704518BDC2190095E648 /* wvs.c in Sources */, - 836F6F2218BDC2190095E648 /* at3_decoder.c in Sources */, 83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */, + 8306B0E420984590000302D4 /* wave.c in Sources */, 836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */, 8349A9171FE6258200E26435 /* pc_adp_otns.c in Sources */, 836F701E18BDC2190095E648 /* redspark.c in Sources */, + 8306B0ED20984590000302D4 /* txtp.c in Sources */, 836F6FA018BDC2190095E648 /* musx.c in Sources */, 836F705818BDC2190095E648 /* vgmstream.c in Sources */, 8349A90A1FE6258200E26435 /* sab.c in Sources */, 836F6F6818BDC2190095E648 /* ads.c in Sources */, + 8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */, 83AB8C761E8072A100086084 /* x360_ast.c in Sources */, 836F6F8B18BDC2190095E648 /* genh.c in Sources */, 8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */, @@ -1866,6 +1941,7 @@ 836F703C18BDC2190095E648 /* wii_bns.c in Sources */, 830EBE132004656E0023AA10 /* xnb.c in Sources */, 835027131ED119E000C25929 /* mta2_decoder.c in Sources */, + 8306B0DB20984590000302D4 /* nxap.c in Sources */, 836F6FA718BDC2190095E648 /* nds_strm.c in Sources */, 8349A91A1FE6258200E26435 /* vxn.c in Sources */, 8349A8EB1FE6253900E26435 /* blocked_ivaud.c in Sources */, @@ -1875,43 +1951,44 @@ 8349A9141FE6258200E26435 /* omu.c in Sources */, 836F6FE418BDC2190095E648 /* ps2_leg.c in Sources */, 836F705618BDC2190095E648 /* util.c in Sources */, + 8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */, + 8306B0EA20984590000302D4 /* caf.c in Sources */, 836F703618BDC2190095E648 /* thp.c in Sources */, 836F6F7818BDC2190095E648 /* Cstr.c in Sources */, 836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */, 836F6FD818BDC2190095E648 /* ps2_gbts.c in Sources */, - 83709E0A1ECBC1A4005C03D3 /* waa_wac_wad_wam.c in Sources */, 836F6F7A18BDC2190095E648 /* dc_dcsw_dcs.c in Sources */, - 836F6F5318BDC2190095E648 /* ps2_iab_blocked.c in Sources */, 831BA61A1EAC61A500CF89B0 /* ps2_vds_vdm.c in Sources */, 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */, 83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */, 836F6FA818BDC2190095E648 /* nds_swav.c in Sources */, - 836F6F3F18BDC2190095E648 /* ast_blocked.c in Sources */, 8374EE411F787AB600033E90 /* ffmpeg_decoder_utils.c in Sources */, + 8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */, 833A7A2E1ED11961003EC53E /* xau.c in Sources */, - 836F6F5018BDC2190095E648 /* mxch_blocked.c in Sources */, 836F6FB518BDC2190095E648 /* ngc_pdt.c in Sources */, - 836F6F4F18BDC2190095E648 /* mus_acm_layout.c in Sources */, - 836F6F4218BDC2190095E648 /* caf_blocked.c in Sources */, 836F6F6C18BDC2190095E648 /* ahx.c in Sources */, 83AB8C751E8072A100086084 /* nub_vag.c in Sources */, 836F702D18BDC2190095E648 /* sfl.c in Sources */, 836F6FEC18BDC2190095E648 /* ps2_mtaf.c in Sources */, 83D7318C1A749EEE00CA1366 /* g719_decoder.c in Sources */, 836F701118BDC2190095E648 /* ps3_cps.c in Sources */, + 8306B0DA20984590000302D4 /* ea_wve_au00.c in Sources */, 83A21F85201D8981000F04B9 /* atx.c in Sources */, + 8306B0E720984590000302D4 /* opus_ppp.c in Sources */, 836F701418BDC2190095E648 /* ps3_msf.c in Sources */, 836F6F6518BDC2190095E648 /* 2dx9.c in Sources */, 830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */, 836F700818BDC2190095E648 /* ps2_vms.c in Sources */, 836F702418BDC2190095E648 /* rwsd.c in Sources */, 830EBE142004656E0023AA10 /* ktss.c in Sources */, - 836F6F5618BDC2190095E648 /* scd_int_layout.c in Sources */, 836F6F6618BDC2190095E648 /* aax.c in Sources */, 836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */, + 8306B0BC20984552000302D4 /* blocked_vs.c in Sources */, 836F6F6718BDC2190095E648 /* acm.c in Sources */, + 8306B0DF20984590000302D4 /* ea_wve_ad10.c in Sources */, 836F6F8A18BDC2190095E648 /* gcsw.c in Sources */, 836F6F9C18BDC2190095E648 /* mp4.c in Sources */, + 8306B0A720984552000302D4 /* blocked_xvas.c in Sources */, 8349A9101FE6258200E26435 /* ea_eaac.c in Sources */, 836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */, 8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */, @@ -1923,6 +2000,7 @@ 83709E071ECBC1A4005C03D3 /* mss.c in Sources */, 836F6F8F18BDC2190095E648 /* his.c in Sources */, 836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */, + 8306B08520984518000302D4 /* yamaha_decoder.c in Sources */, 836F6FEF18BDC2190095E648 /* ps2_pnb.c in Sources */, 836F6FCB18BDC2190095E648 /* ps2_ads.c in Sources */, 836F6FD318BDC2190095E648 /* ps2_ccc.c in Sources */, @@ -1930,7 +2008,9 @@ 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */, 836F704C18BDC2190095E648 /* xbox_xvas.c in Sources */, 836F6F3918BDC2190095E648 /* SASSC_decoder.c in Sources */, + 8306B0A920984552000302D4 /* blocked_adm.c in Sources */, 836F703A18BDC2190095E648 /* vs.c in Sources */, + 8306B0F220984590000302D4 /* ubi_jade.c in Sources */, 836F6FF918BDC2190095E648 /* ps2_snd.c in Sources */, 836F6F9018BDC2190095E648 /* idsp.c in Sources */, 836F6F2918BDC2190095E648 /* l5_555_decoder.c in Sources */, @@ -1940,7 +2020,6 @@ 836F6FC118BDC2190095E648 /* pc_adp.c in Sources */, 836F701A18BDC2190095E648 /* psx_fag.c in Sources */, 836F703B18BDC2190095E648 /* vsf.c in Sources */, - 836F6F3D18BDC2190095E648 /* aax_layout.c in Sources */, 836F6F8218BDC2190095E648 /* ea_schl.c in Sources */, 836F700A18BDC2190095E648 /* ps2_vpk.c in Sources */, 836F6F7318BDC2190095E648 /* bcstm.c in Sources */, @@ -1951,32 +2030,34 @@ 836F6F3218BDC2190095E648 /* ngc_dsp_decoder.c in Sources */, 836F704218BDC2190095E648 /* wii_sts.c in Sources */, 836F703918BDC2190095E648 /* vgs.c in Sources */, - 830165A21F256BF400CA0941 /* hwas_blocked.c in Sources */, 836F6F2C18BDC2190095E648 /* mp4_aac_decoder.c in Sources */, 836F701F18BDC2190095E648 /* riff.c in Sources */, 83AA5D191F6E2F600020821C /* ea_xas_decoder.c in Sources */, 836F6F9318BDC2190095E648 /* ivaud.c in Sources */, 836F6F8518BDC2190095E648 /* exakt_sc.c in Sources */, 836F6FA618BDC2190095E648 /* nds_sad.c in Sources */, + 8306B0F120984590000302D4 /* ppst.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 */, + 8306B0A220984552000302D4 /* blocked_bdsp.c in Sources */, 836F700118BDC2190095E648 /* ps2_tec.c in Sources */, - 836F6F4718BDC2190095E648 /* gsb_blocked.c in Sources */, 836F703018BDC2190095E648 /* sqex_scd.c in Sources */, + 8306B0A320984552000302D4 /* blocked_ast.c in Sources */, 836F6FD518BDC2190095E648 /* ps2_enth.c in Sources */, + 8306B0DC20984590000302D4 /* sthd.c in Sources */, 836F6FAE18BDC2190095E648 /* ngc_dsp_mpds.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 */, - 836F6F4018BDC2190095E648 /* bdsp_blocked.c in Sources */, 836F700418BDC2190095E648 /* ps2_vas.c in Sources */, 836F6F9818BDC2190095E648 /* mattel_hyperscan.c in Sources */, + 8306B0BA20984552000302D4 /* blocked_wsi.c in Sources */, 83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */, 836F704418BDC2190095E648 /* ws_aud.c in Sources */, 83709E081ECBC1A4005C03D3 /* ps2_rxws.c in Sources */, @@ -1990,36 +2071,35 @@ 836F6F7118BDC2190095E648 /* ast.c in Sources */, 836F6FE318BDC2190095E648 /* ps2_khv.c in Sources */, 836F701318BDC2190095E648 /* ps3_klbs.c in Sources */, - 836F6F5818BDC2190095E648 /* thp_blocked.c in Sources */, 836F6F6B18BDC2190095E648 /* agsc.c in Sources */, 836F6FF018BDC2190095E648 /* ps2_psh.c in Sources */, 836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */, 836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */, 836F6F3118BDC2190095E648 /* ngc_afc_decoder.c in Sources */, 836F6FE718BDC2190095E648 /* ps2_mib.c in Sources */, - 836F6FAB18BDC2190095E648 /* ngc_bo2.c in Sources */, 836F6F9918BDC2190095E648 /* maxis_xa.c in Sources */, 836F702118BDC2190095E648 /* rs03.c in Sources */, 836F6F8418BDC2190095E648 /* emff.c in Sources */, 836F704118BDC2190095E648 /* wii_str.c in Sources */, 836F6F8818BDC2190095E648 /* fsb.c in Sources */, - 836F6F4618BDC2190095E648 /* filp_blocked.c in Sources */, 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */, 836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */, 836F6FC018BDC2190095E648 /* p3d.c in Sources */, 836F6FC718BDC2190095E648 /* pona.c in Sources */, + 8306B0B820984552000302D4 /* blocked_ws_aud.c in Sources */, 836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */, 836F6F5118BDC2190095E648 /* nolayout.c in Sources */, 83AA5D241F6E2F9C0020821C /* awc.c in Sources */, 8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */, + 8306B0E620984590000302D4 /* msb_msh.c in Sources */, 836F702918BDC2190095E648 /* sat_sap.c in Sources */, 836F6F3718BDC2190095E648 /* pcm_decoder.c in Sources */, 831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */, - 836F6F5B18BDC2190095E648 /* ws_aud_blocked.c in Sources */, 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */, 83AA5D271F6E2F9C0020821C /* stm.c in Sources */, 831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */, 836F703F18BDC2190095E648 /* wii_smp.c in Sources */, + 8306B0A520984552000302D4 /* blocked_ea_wve_au00.c in Sources */, 836F6FB818BDC2190095E648 /* ngc_tydsp.c in Sources */, 836F701518BDC2190095E648 /* ps3_past.c in Sources */, 836F6F7C18BDC2190095E648 /* dc_kcey.c in Sources */, @@ -2035,6 +2115,7 @@ 836F6FFE18BDC2190095E648 /* ps2_str.c in Sources */, 836F6F4A18BDC2190095E648 /* interleave.c in Sources */, 83EDE5D81A70951A005F5D84 /* mca.c in Sources */, + 8306B0AE20984552000302D4 /* blocked_filp.c in Sources */, 831BA61B1EAC61A500CF89B0 /* sgxd.c in Sources */, 838BDB6C1D3AFAB10022CA6F /* ffmpeg_decoder.c in Sources */, 8349A8E11FE6251F00E26435 /* ea_mt_decoder.c in Sources */, @@ -2047,24 +2128,25 @@ 836F6FC318BDC2190095E648 /* pc_smp.c in Sources */, 836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */, 8349A9091FE6258200E26435 /* pc_ast.c in Sources */, - 836F6F5418BDC2190095E648 /* ps2_strlr_blocked.c in Sources */, 8349A91C1FE6258200E26435 /* mogg.c in Sources */, 836F703D18BDC2190095E648 /* wii_mus.c in Sources */, 836F700D18BDC2190095E648 /* ps2_wmus.c in Sources */, 831BA61C1EAC61A500CF89B0 /* sxd.c in Sources */, 836F6F8018BDC2190095E648 /* dsp_bdsp.c in Sources */, 836F6F9618BDC2190095E648 /* lsf.c in Sources */, + 8306B0AB20984552000302D4 /* layered.c in Sources */, 8374EE3E1F787AB600033E90 /* ffmpeg_decoder_utils_ea_xma.c in Sources */, - 83345A511F8AEB2800B2EAA4 /* nsw_opus.c in Sources */, + 8306B0EC20984590000302D4 /* pcm_sre.c in Sources */, 836F6FC818BDC2190095E648 /* pos.c in Sources */, 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */, 836F6F8918BDC2190095E648 /* gca.c in Sources */, - 836F6F5718BDC2190095E648 /* str_snds_blocked.c in Sources */, 836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */, 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */, + 8306B0B120984552000302D4 /* blocked_halpst.c in Sources */, 836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */, 836F701218BDC2190095E648 /* ps3_ivag.c in Sources */, 83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */, + 8306B0BB20984552000302D4 /* blocked_gsb.c in Sources */, 836F6F7718BDC2190095E648 /* capdsp.c in Sources */, 836F6FB018BDC2190095E648 /* ngc_dsp_ygo.c in Sources */, 836F703318BDC2190095E648 /* str_snds.c in Sources */, @@ -2077,13 +2159,18 @@ 836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */, 836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */, 836F6FF618BDC2190095E648 /* ps2_sfs.c in Sources */, + 8306B08420984518000302D4 /* at3plus_decoder.c in Sources */, 8349A90E1FE6258200E26435 /* scd_pcm.c in Sources */, 836F6F9518BDC2190095E648 /* kraw.c in Sources */, 836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */, + 8306B0E920984590000302D4 /* opus.c in Sources */, 83709E051ECBC1A4005C03D3 /* gtd.c in Sources */, + 8306B0A420984552000302D4 /* segmented.c in Sources */, 83A21F86201D8981000F04B9 /* xwc.c in Sources */, + 8306B0A620984552000302D4 /* blocked_ea_wve_ad10.c in Sources */, 83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */, 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */, + 8306B0B220984552000302D4 /* blocked_mxch.c in Sources */, 836F6F8618BDC2190095E648 /* excitebots.c in Sources */, 836F6FF418BDC2190095E648 /* rws.c in Sources */, 836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */, @@ -2092,19 +2179,17 @@ 83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */, 836F6F9118BDC2190095E648 /* ios_psnd.c in Sources */, 836F700618BDC2190095E648 /* ps2_vgs.c in Sources */, - 836F6F2118BDC2190095E648 /* aica_decoder.c in Sources */, 836F700318BDC2190095E648 /* ps2_vag.c in Sources */, 836F6FAA18BDC2190095E648 /* ngc_bh2pcm.c in Sources */, 831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */, 836F6F3018BDC2190095E648 /* nds_procyon_decoder.c in Sources */, 8349A8E81FE6253900E26435 /* blocked_dec.c in Sources */, 831BA6181EAC61A500CF89B0 /* adx.c in Sources */, - 836F6F5A18BDC2190095E648 /* vs_blocked.c in Sources */, 836F6FB118BDC2190095E648 /* ngc_ffcc_str.c in Sources */, + 8306B0B620984552000302D4 /* blocked_hwas.c in Sources */, 836F6FC218BDC2190095E648 /* pc_mxst.c in Sources */, 836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */, 836F704818BDC2190095E648 /* xbox_ims.c in Sources */, - 836F6F4918BDC2190095E648 /* ims_block.c in Sources */, 836F6F7518BDC2190095E648 /* bnsf.c in Sources */, 836F704318BDC2190095E648 /* wpd.c in Sources */, 8349A9081FE6258200E26435 /* ezw.c in Sources */, @@ -2114,7 +2199,6 @@ 836F6F8E18BDC2190095E648 /* halpst.c in Sources */, 836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */, 836F702618BDC2190095E648 /* s14_sss.c in Sources */, - 836F6F4818BDC2190095E648 /* halpst_blocked.c in Sources */, 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, 8323894A1D22419B00482226 /* clHCA.c in Sources */, 836F702E18BDC2190095E648 /* sli.c in Sources */, @@ -2125,20 +2209,24 @@ 836F6F3618BDC2190095E648 /* ogg_vorbis_decoder.c in Sources */, 836F704718BDC2190095E648 /* xbox_hlwav.c in Sources */, 83345A4F1F8AEB2800B2EAA4 /* nub_xma.c in Sources */, + 8306B0E520984590000302D4 /* ubi_lyn.c in Sources */, 836F6F7618BDC2190095E648 /* brstm.c in Sources */, 836F700718BDC2190095E648 /* ps2_vgv.c in Sources */, 836F704F18BDC2190095E648 /* xwb.c in Sources */, + 8306B0AD20984552000302D4 /* blocked_caf.c in Sources */, + 8306B0AA20984552000302D4 /* blocked_sthd.c in Sources */, 8350270D1ED119D200C25929 /* ps3_mta2.c in Sources */, 836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */, 836F6FA918BDC2190095E648 /* ngc_adpdtk.c in Sources */, 836F6FDC18BDC2190095E648 /* ps2_iab.c in Sources */, - 836F6FAC18BDC2190095E648 /* ngc_caf.c in Sources */, 836F6F3818BDC2190095E648 /* psx_decoder.c in Sources */, 836F6F2D18BDC2190095E648 /* mpeg_decoder.c in Sources */, 836F6F2618BDC2190095E648 /* g7221_decoder.c in Sources */, 836F6FA218BDC2190095E648 /* naomi_adpcm.c in Sources */, 836F6FBF18BDC2190095E648 /* otm.c in Sources */, + 8306B0B420984552000302D4 /* blocked_tra.c in Sources */, 836F6FDD18BDC2190095E648 /* ps2_ikm.c in Sources */, + 8306B0B520984552000302D4 /* blocked_emff.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */, 83A21F8C201D8982000F04B9 /* kma9.c in Sources */, 836F6FC418BDC2190095E648 /* pc_snds.c in Sources */, @@ -2146,9 +2234,7 @@ 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, 836F6F4118BDC2190095E648 /* blocked.c in Sources */, 836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */, - 836F6F4518BDC2190095E648 /* emff_blocked.c in Sources */, 838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */, - 836F6F5D18BDC2190095E648 /* xa_blocked.c in Sources */, 836F6F3418BDC2190095E648 /* nwa_decoder.c in Sources */, 836F6F2E18BDC2190095E648 /* msadpcm_decoder.c in Sources */, 836F6F9218BDC2190095E648 /* ish_isd.c in Sources */, @@ -2157,18 +2243,20 @@ 836F703518BDC2190095E648 /* svs.c in Sources */, 836F6FA318BDC2190095E648 /* naomi_spsd.c in Sources */, 836F6F9D18BDC2190095E648 /* msvp.c in Sources */, + 8306B0B920984552000302D4 /* blocked_matx.c in Sources */, 83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */, 836F6FBA18BDC2190095E648 /* ngc_ymf.c in Sources */, - 83A21F89201D8982000F04B9 /* atsl3.c in Sources */, 836F705018BDC2190095E648 /* ydsp.c in Sources */, + 8306B0B720984552000302D4 /* blocked_str_snds.c in Sources */, 836F702718BDC2190095E648 /* sat_baka.c in Sources */, 83345A4A1F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c in Sources */, 836F704B18BDC2190095E648 /* xbox_xmu.c in Sources */, 832389501D2246C300482226 /* hca.c in Sources */, 836F701B18BDC2190095E648 /* psx_gms.c in Sources */, + 8306B0F020984590000302D4 /* atsl.c in Sources */, 836F700518BDC2190095E648 /* ps2_vbk.c in Sources */, 836F6FDF18BDC2190095E648 /* ps2_int.c in Sources */, - 836F704D18BDC2190095E648 /* xbox_xwav.c in Sources */, + 8306B0AC20984552000302D4 /* blocked_xa.c in Sources */, 836F6F7B18BDC2190095E648 /* dc_idvi.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c index edc53d742..2b12c3836 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c @@ -817,23 +817,65 @@ void acm_reset(ACMStream *acm) memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int)); } -/* interface to vgmstream */ -void decode_acm(ACMStream * acm, sample * outbuf, - int32_t samples_to_do, int channelspacing) { + +/*********************************************** + * interface to vgmstream + ***********************************************/ + +acm_codec_data *init_acm(STREAMFILE *streamFile) { + acm_codec_data* data = NULL; + ACMStream *acm_stream = NULL; + char filename[PATH_LIMIT]; + + data = calloc(1,sizeof(acm_codec_data)); + if (!data) goto fail; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (acm_open_decoder(&acm_stream,streamFile,filename) != ACM_OK) + goto fail; + + data->file = acm_stream; + + return data; + +fail: + free_acm(data); + return NULL; +} + +void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing) { + ACMStream * acm = data->file; int32_t samples_read = 0; + while (samples_read < samples_to_do) { - int32_t bytes_read_just_now; - bytes_read_just_now = - acm_read(acm,(char*)( - outbuf+samples_read*channelspacing), - (samples_to_do-samples_read)*sizeof(sample)* - channelspacing,0,2,1); + int32_t bytes_read_just_now = acm_read( + acm, + (char*)(outbuf+samples_read*channelspacing), + (samples_to_do-samples_read)*sizeof(sample)*channelspacing, + 0,2,1); if (bytes_read_just_now > 0) { - samples_read += - bytes_read_just_now/sizeof(sample)/channelspacing; + samples_read += bytes_read_just_now/sizeof(sample)/channelspacing; } else { return; } } } + +void reset_acm(VGMSTREAM *vgmstream) { + acm_codec_data *data = vgmstream->codec_data; + + if (data && data->file) { + acm_reset(data->file); + } +} + +void free_acm(acm_codec_data *data) { + if (data) { + if (data->file) { + acm_close(data->file); + } + free(data); + } +} + diff --git a/Frameworks/vgmstream/vgmstream/src/coding/aica_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/aica_decoder.c deleted file mode 100644 index 556f1986b..000000000 --- a/Frameworks/vgmstream/vgmstream/src/coding/aica_decoder.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "../util.h" -#include "coding.h" - -/* fixed point (.8) amount to scale the current step size by */ -/* part of the same series as used in MS ADPCM "ADPCMTable" */ -static const unsigned int scale_step[16] = -{ - 230, 230, 230, 230, 307, 409, 512, 614, - 230, 230, 230, 230, 307, 409, 512, 614 -}; - -/* expand an unsigned four bit delta to a wider signed range */ -static const int scale_delta[16] = -{ - 1, 3, 5, 7, 9, 11, 13, 15, - -1, -3, -5, -7, -9,-11,-13,-15 -}; - -/* Yamaha AICA ADPCM (as seen in Dreamcast) */ - -void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; - int32_t sample_count; - int32_t hist1 = stream->adpcm_history1_16; - uint32_t step_size = stream->adpcm_step_index; - - for (i=first_sample,sample_count=0; ioffset+i/2,stream->streamfile) >> - (i&1?4:0) - )&0xf; - - int32_t sample_delta = (int32_t)step_size * scale_delta[sample_nibble]; - int32_t new_sample; - - new_sample = hist1 + sample_delta/8; - - outbuf[sample_count] = clamp16(new_sample); - - hist1 = outbuf[sample_count]; - - step_size = (step_size * scale_step[sample_nibble])/0x100; - if (step_size < 0x7f) step_size = 0x7f; - if (step_size > 0x6000) step_size = 0x6000; - } - - stream->adpcm_history1_16 = hist1; - stream->adpcm_step_index = step_size; -} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/at3_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c similarity index 98% rename from Frameworks/vgmstream/vgmstream/src/coding/at3_decoder.c rename to Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c index 57f56ef9b..ff00f8bbf 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/at3_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c @@ -51,6 +51,7 @@ void decode_at3plus(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, void reset_at3plus(VGMSTREAM *vgmstream) { maiatrac3plus_codec_data *data = vgmstream->codec_data; + if (!data) return; if (data->handle) Atrac3plusDecoder_closeContext(data->handle); @@ -62,6 +63,8 @@ void seek_at3plus(VGMSTREAM *vgmstream, int32_t num_sample) { int blocks_to_skip = num_sample / 2048; int samples_to_discard = num_sample % 2048; maiatrac3plus_codec_data *data = (maiatrac3plus_codec_data *)(vgmstream->codec_data); + if (!data) return; + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + vgmstream->interleave_block_size * blocks_to_skip; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index 95c8c0020..fad4c0532 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -152,6 +152,7 @@ decode_fail: void reset_atrac9(VGMSTREAM *vgmstream) { atrac9_codec_data *data = vgmstream->codec_data; + if (!data) return; if (!data->handle) goto fail; @@ -184,6 +185,7 @@ fail: void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { atrac9_codec_data *data = vgmstream->codec_data; + if (!data) return; reset_atrac9(vgmstream); @@ -196,8 +198,7 @@ void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { } void free_atrac9(atrac9_codec_data *data) { - if (!data) - return; + if (!data) return; if (data->handle) Atrac9ReleaseHandle(data->handle); free(data->data_buffer); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index d3a350f8d..b9de6322a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -17,8 +17,9 @@ void g72x_init_state(struct g72x_state *state_ptr); /* ima_decoder */ void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_xbox_ima_mch(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first); void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -36,6 +37,7 @@ size_t ima_bytes_to_samples(size_t bytes, int channels); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); size_t xbox_ima_bytes_to_samples(size_t bytes, int channels); size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset); +size_t apple_ima4_bytes_to_samples(size_t bytes, int channels); /* ngc_dsp_decoder */ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -57,7 +59,6 @@ void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci /* pcm_decoder */ void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm16BE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -74,10 +75,10 @@ size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); /* psx_decoder */ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_psx_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_psx_bmdx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t ps_bytes_to_samples(size_t bytes, int channels); +size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels); /* xa_decoder */ void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); @@ -102,7 +103,10 @@ void decode_cbd2_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* acm_decoder */ -void decode_acm(ACMStream * acm, sample * outbuf, int32_t samples_to_do, int channelspacing); +acm_codec_data *init_acm(); +void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing); +void reset_acm(VGMSTREAM *vgmstream); +void free_acm(acm_codec_data *data); /* nwa_decoder */ void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do); @@ -112,8 +116,12 @@ void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do); long msadpcm_bytes_to_samples(long bytes, int block_size, int channels); -/* aica_decoder */ -void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +/* yamaha_decoder */ +void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_yamaha(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_yamaha_nxap(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +size_t aica_bytes_to_samples(size_t bytes, int channels); +size_t yamaha_bytes_to_samples(size_t bytes, int channels); /* nds_procyon_decoder */ void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -136,6 +144,9 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, /* mc3_decoder */ void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +/* fadpcm_decoder */ +void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* ea_mt_decoder*/ ea_mt_codec_data *init_ea_mt(int channel_count, int type); void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c index d0cec7fc7..466054b41 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c @@ -297,15 +297,9 @@ fail: int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t * buf, size_t buf_size, off_t xma2_offset, size_t xma2_size, size_t data_size, STREAMFILE *streamFile) { uint8_t chunk[0x100]; size_t riff_size; - size_t xma2_final_size = xma2_size; - int xma2_chunk_version = read_8bit(xma2_offset,streamFile); - /* FFmpeg can't parse v3 "XMA2" chunks so we'll have to extend (8 bytes in the middle) */ - if (xma2_chunk_version == 3) - xma2_final_size += 0x8; - riff_size = 4+4+ 4 + 4+4+xma2_final_size + 4+4; - - if (buf_size < riff_size || xma2_final_size > 0x100) + riff_size = 4+4+ 4 + 4+4+xma2_size + 4+4; + if (buf_size < riff_size || xma2_size > 0x100) goto fail; if (read_streamfile(chunk,xma2_offset,xma2_size, streamFile) != xma2_size) goto fail; @@ -316,20 +310,11 @@ int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t * buf, size_t buf_size, off_t memcpy(buf+0x08, "WAVE", 4); memcpy(buf+0x0c, "XMA2", 4); - put_32bitLE(buf+0x10, xma2_final_size); - if (xma2_chunk_version == 3) { - /* old XMA2 v3: change to v4 (extra 8 bytes in the middle); always BE */ - put_8bit (buf+0x14 + 0x00, 4); /* v4 */ - memcpy (buf+0x14 + 0x01, chunk+1, 0xF); /* first v3 part (fixed) */ - put_32bitBE(buf+0x14 + 0x10, 0x000010D6); /* extra v4 BE: "EncodeOptions" (not used by FFmpeg) */ - put_32bitBE(buf+0x14 + 0x14, 0); /* extra v4 BE: "PsuedoBytesPerSec" (not used by FFmpeg) */ - memcpy (buf+0x14 + 0x18, chunk+0x10, xma2_size - 0x10); /* second v3 part (variable size) */ - } else { - memcpy(buf+0x14, chunk, xma2_size); - } + put_32bitLE(buf+0x10, xma2_size); + memcpy(buf+0x14, chunk, xma2_size); - memcpy(buf+0x14+xma2_final_size, "data", 4); - put_32bitLE(buf+0x14+xma2_final_size+4, data_size); /* data size */ + memcpy(buf+0x14+xma2_size, "data", 4); + put_32bitLE(buf+0x14+xma2_size+4, data_size); /* data size */ return riff_size; @@ -436,20 +421,19 @@ fail: 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; - uint32_t first_frame_b, packet_skip_count = 0, frame_size_b, packet_size_b, header_size_b; - uint64_t offset_b, packet_offset_b, frame_offset_b; - size_t size; + size_t first_frame_b, packet_skip_count = 0, frame_size_b, packet_size_b, header_size_b; + off_t offset_b, packet_offset_b, frame_offset_b; - uint32_t packet_size = bytes_per_packet; + size_t packet_size = bytes_per_packet; off_t offset = msd->data_offset; - uint32_t stream_offset_b = msd->data_offset * 8; + off_t max_offset = msd->data_offset + msd->data_size; + off_t stream_offset_b = msd->data_offset * 8; offset += start_packet * packet_size; - size = offset + msd->data_size; packet_size_b = packet_size * 8; /* read packets */ - while (offset < size) { + while (offset < max_offset) { offset_b = offset * 8; /* global offset in bits */ offset += packet_size; /* global offset in bytes */ @@ -678,7 +662,7 @@ void wmapro_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_ return; } samples_per_frame = wma_get_samples_per_frame(version, sample_rate, decode_flags); - bits_frame_size = floor(log(block_align) / log(2)) + 4; /* max bits needed to represent this block_align */ + 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) */ msd->xma_version = 0; /* signal it's not XMA */ @@ -705,7 +689,8 @@ void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_ali else { /* variable frames per packet (mini-header values) */ off_t offset = msd->data_offset; - while (offset < msd->data_size) { /* read packets (superframes) */ + off_t max_offset = msd->data_offset + msd->data_size; + while (offset < max_offset) { /* read packets (superframes) */ int packet_frames; uint8_t header = read_8bit(offset, streamFile); /* upper nibble: index; lower nibble: frames */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c index fa02d8c5c..1ecc4106d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c @@ -534,6 +534,8 @@ static void flush_ea_mt_internal(VGMSTREAM *vgmstream, int is_start) { int i; size_t bytes; + if (!data) return; + /* the decoder needs to be notified when offsets change */ for (i = 0; i < vgmstream->channels; i++) { UTKContext *ctx = data->utk_context[i]; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/fadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/fadpcm_decoder.c new file mode 100644 index 000000000..331e94c47 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/fadpcm_decoder.c @@ -0,0 +1,81 @@ +#include "coding.h" + + +/* FADPCM table */ +static const int8_t fadpcm_coefs[8][2] = { + { 0 , 0 }, + { 60 , 0 }, + { 122 , 60 }, + { 115 , 52 }, + { 98 , 55 }, + { 0 , 0 }, + { 0 , 0 }, + { 0 , 0 }, +}; + +/* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout. + * Code/layout could be simplified but tries to emulate FMOD's code. + * Algorithm and tables debugged from their PC DLLs. */ +void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + off_t frame_offset; + int i, j, k; + int block_samples, num_frame, samples_done = 0, sample_count = 0; + uint32_t coefs, shifts; + int32_t hist1; //= stream->adpcm_history1_32; + int32_t hist2; //= stream->adpcm_history2_32; + + /* external interleave (fixed size), mono */ + block_samples = (0x8c - 0xc) * 2; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; + + frame_offset = stream->offset + 0x8c*num_frame; + + + /* parse 0xc header (header samples are not written to outbuf) */ + coefs = read_32bitLE(frame_offset + 0x00, stream->streamfile); + shifts = read_32bitLE(frame_offset + 0x04, stream->streamfile); + hist1 = read_16bitLE(frame_offset + 0x08, stream->streamfile); + hist2 = read_16bitLE(frame_offset + 0x0a, stream->streamfile); + + + /* decode nibbles, grouped in 8 sets of 0x10 * 0x04 * 2 */ + for (i = 0; i < 8; i++) { + int32_t coef1, coef2, shift, coef_index, shift_factor; + off_t group_offset = frame_offset + 0x0c + 0x10*i; + + /* each set has its own coefs/shifts (indexes > 7 are repeat, ex. 0x9 is 0x2) */ + coef_index = ((coefs >> i*4) & 0x0f) % 0x07; + shift_factor = (shifts >> i*4) & 0x0f; + + coef1 = fadpcm_coefs[coef_index][0]; + coef2 = fadpcm_coefs[coef_index][1]; + shift = 0x16 - shift_factor; + + for (j = 0; j < 4; j++) { + uint32_t nibbles = read_32bitLE(group_offset + 0x04*j, stream->streamfile); + + for (k = 0; k < 8; k++) { + int32_t new_sample; + + new_sample = (nibbles >> k*4) & 0x0f; + new_sample = (new_sample << 28) >> shift; /* sign extend + scale */ + new_sample = (new_sample - hist2*coef2 + hist1*coef1); + new_sample = new_sample >> 6; /* (new_sample / 64) has minor rounding differences */ + new_sample = clamp16(new_sample); + + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = new_sample; + samples_done++; + } + sample_count++; + + hist2 = hist1; + hist1 = new_sample; + } + } + } + + //stream->adpcm_history1_32 = hist1; + //stream->adpcm_history2_32 = hist2; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index df24db802..f9ef42f0c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -364,6 +364,10 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, memcpy(&data->config, config, sizeof(ffmpeg_custom_config)); } + /* ignore bad combos */ + if ((header && !header_size) || (!header && header_size)) + goto fail; + /* fake header to trick FFmpeg into demuxing/decoding the stream */ if (header_size > 0) { data->header_size = header_size; @@ -706,6 +710,7 @@ end: void reset_ffmpeg(VGMSTREAM *vgmstream) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; + if (!data) return; if (data->formatCtx) { avformat_seek_file(data->formatCtx, data->streamIndex, 0, 0, 0, AVSEEK_FLAG_ANY); @@ -733,6 +738,7 @@ 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; /* 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). */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c index 9c01f8e0d..ab3cb8293 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c @@ -53,6 +53,7 @@ void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int void reset_g719(VGMSTREAM *vgmstream) { g719_codec_data *data = vgmstream->codec_data; int i; + if (!data) return; for (i = 0; i < vgmstream->channels; i++) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c index 5f5aa761e..648a8b575 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c @@ -52,6 +52,7 @@ void decode_g7221(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, in void reset_g7221(VGMSTREAM *vgmstream) { g7221_codec_data *data = vgmstream->codec_data; int i; + if (!data) return; for (i = 0; i < vgmstream->channels; i++) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c index c8476b7f3..8288229f3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c @@ -2,7 +2,6 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { int samples_done = 0; - int32_t samples_remain = clHCA_samplesPerBlock - data->sample_ptr; void *hca_data = NULL; @@ -84,7 +83,8 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, i void reset_hca(VGMSTREAM *vgmstream) { hca_codec_data *data = vgmstream->codec_data; - /*clHCA *hca = (clHCA *)(data + 1);*/ + if (!data) return; + data->curblock = 0; data->sample_ptr = clHCA_samplesPerBlock; data->samples_discard = 0; @@ -92,6 +92,8 @@ void reset_hca(VGMSTREAM *vgmstream) { void loop_hca(VGMSTREAM *vgmstream) { hca_codec_data *data = (hca_codec_data *)(vgmstream->codec_data); + if (!data) return; + data->curblock = data->info.loopStart; data->sample_ptr = clHCA_samplesPerBlock; data->samples_discard = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index bdb09b4e8..9fc8902d7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -8,14 +8,7 @@ * All IMAs are mostly the same with these variations: * - interleave: blocks and channels are handled externally (layouts) or internally (mixed channels) * - block header: none (external), normal (4 bytes of history 16b + step 8b + reserved 8b) or others; per channel/global - * - expand type: ms-ima style or others; low or high nibble first - * - * todo: - * MS IMAs have the last sample of the prev block in the block header. In Microsoft's implementation, the header sample - * is written first and last sample is skipped (since they match). vgmstream ignores the header sample and - * writes the last one instead. This means the very first sample in the first header in a stream is incorrectly skipped. - * Header step should be 8 bit. - * Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). + * - expand type: IMA style or variations; low or high nibble first */ static const int ADPCMTable[89] = { @@ -39,15 +32,15 @@ static const int IMA_IndexTable[16] = { }; -/* Standard IMA (most common) */ +/* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */ static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { int sample_nibble, sample_decoded, step, delta; - /* calculate diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] - * simplified through math, using bitwise ops to avoid rounding: - * diff = (code + 1/2) * (step / 4) - * > diff = (step * nibble / 4) + (step / 8) - * > diff = (((step * nibble) + (step / 2)) / 4) */ + /* simplified through math from: + * - diff = (code + 1/2) * (step / 4) + * > diff = ((step * nibble) + (step / 2)) / 4 + * > diff = (step * nibble / 4) + (step / 8) + * final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */ sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */ sample_decoded = *hist1; /* predictor value */ @@ -81,7 +74,33 @@ static void std_ima_expand_nibble_16(VGMSTREAMCHANNEL * stream, off_t byte_offse if (sample_nibble & 8) delta = -delta; sample_decoded += delta; - *hist1 = clamp16(sample_decoded); //no need for this actually + *hist1 = clamp16(sample_decoded); /* no need for this, actually */ + *step_index += IMA_IndexTable[sample_nibble]; + if (*step_index < 0) *step_index=0; + if (*step_index > 88) *step_index=88; +} + +/* Original IMA expansion, but using MULs rather than shift+ADDs (faster for newer processors). + * There is minor rounding difference between ADD and MUL expansions, noticeable/propagated in non-headered IMAs. */ +static void std_ima_expand_nibble_mul(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + int sample_nibble, sample_decoded, step, delta; + + /* simplified through math from: + * - diff = (code + 1/2) * (step / 4) + * > diff = (code + 1/2) * step) / 4) * (2 / 2) + * > diff = (code + 1/2) * 2 * step / 8 + * final diff = [signed] ((code * 2 + 1) * step) / 8 */ + + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_decoded = *hist1; + step = ADPCMTable[*step_index]; + + delta = (sample_nibble & 0x7); + delta = ((delta * 2 + 1) * step) >> 3; + if (sample_nibble & 8) delta = -delta; + sample_decoded += delta; + + *hist1 = clamp16(sample_decoded); *step_index += IMA_IndexTable[sample_nibble]; if (*step_index < 0) *step_index=0; if (*step_index > 88) *step_index=88; @@ -96,7 +115,8 @@ static void n3ds_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, step = ADPCMTable[*step_index]; sample_decoded = sample_decoded << 3; - delta = step * (sample_nibble & 7) * 2 + step; /* custom */ + delta = (sample_nibble & 0x07); + delta = step * delta * 2 + step; /* custom */ if (sample_nibble & 8) delta = -delta; sample_decoded += delta; sample_decoded = sample_decoded >> 3; @@ -149,24 +169,6 @@ static void otns_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, if (*step_index > 88) *step_index=88; } -/* Ubisoft games, algorithm by Zench (https://bitbucket.org/Zenchreal/decubisnd) */ -static void ubi_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { - int sample_nibble, sample_decoded, step, delta; - - sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; - sample_decoded = *hist1; - step = ADPCMTable[*step_index]; - - delta = (((sample_nibble & 7) * 2 + 1) * step) >> 3; /* custom */ - if (sample_nibble & 8) delta = -delta; - sample_decoded += delta; - - *hist1 = clamp16(sample_decoded); - *step_index += IMA_IndexTable[sample_nibble]; - if (*step_index < 0) *step_index=0; - if (*step_index > 88) *step_index=88; -} - /* ************************************ */ /* DVI/IMA */ /* ************************************ */ @@ -176,7 +178,6 @@ static void ubi_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, * For vgmstream, low nibble is called "IMA ADPCM" and high nibble is "DVI IMA ADPCM" (same thing though). */ void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first) { int i, sample_count = 0; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -186,7 +187,7 @@ void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel if (step_index < 0) step_index=0; if (step_index > 88) step_index=88; - /* decode nibbles */ + /* decode nibbles (layout: varies) */ for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { off_t byte_offset = is_stereo ? stream->offset + i : /* stereo: one nibble per channel */ @@ -205,7 +206,6 @@ void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel void decode_3ds_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; @@ -227,7 +227,6 @@ void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -249,7 +248,6 @@ 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) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -275,20 +273,20 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * /* MS-IMA */ /* ************************************ */ -/* IMA with variable-sized frames, header and custom nibble layout (outputs non-aligned number of samples). +/* IMA with custom frame sizes, header and nibble layout. Outputs an odd number of samples per frame, + * so to simplify calcs this decodes full frames, thus hist doesn't need to be mantained. * Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). */ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, samples_read = 0, samples_done = 0, max_samples; - int32_t hist1;// = stream->adpcm_history1_32; int step_index;// = stream->adpcm_step_index; - /* internal interleave (configurable size), mixed channels (4 byte per ch) */ + /* internal interleave (configurable size), mixed channels */ int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; first_sample = first_sample % block_samples; - /* normal header (hist+step+reserved per channel) */ - { + /* normal header (hist+step+reserved), per channel */ + { //if (first_sample == 0) { off_t header_offset = stream->offset + 0x04*channel; hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); @@ -296,7 +294,7 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou if (step_index < 0) step_index = 0; if (step_index > 88) step_index = 88; - /* write header sample */ + /* write header sample (odd samples per block) */ if (samples_read >= first_sample && samples_done < samples_to_do) { outbuf[samples_done * channelspacing] = (short)hist1; samples_done++; @@ -308,12 +306,12 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou if (max_samples > samples_to_do + first_sample - samples_done) max_samples = samples_to_do + first_sample - samples_done; /* for smaller last block */ - /* decode nibbles (layout: alternates 4*2 nibbles per channel) */ + /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel) */ for (i = 0; i < max_samples; i++) { off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x04*channel + 0x04*vgmstream->channels*(i/8) + (i%8)/2; int nibble_shift = (i&1?4:0); /* low nibble first */ - std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); /* original expand */ if (samples_read >= first_sample && samples_done < samples_to_do) { outbuf[samples_done * channelspacing] = (short)(hist1); @@ -331,24 +329,23 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou //stream->adpcm_step_index = step_index; } -/* Reflection's MS-IMA (some layout info from XA2WAV by Deniz Oezmen) */ +/* Reflection's MS-IMA with custom nibble layout (some info from XA2WAV by Deniz Oezmen) */ void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, samples_read = 0, samples_done = 0, max_samples; - int32_t hist1;// = stream->adpcm_history1_32; int step_index;// = stream->adpcm_step_index; - /* internal interleave (configurable size), mixed channels (4 byte per ch) */ + /* internal interleave (configurable size), mixed channels */ int block_channel_size = (vgmstream->interleave_block_size - 0x04*vgmstream->channels) / vgmstream->channels; int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; first_sample = first_sample % block_samples; - /* normal header (hist+step+reserved per channel) */ - { + /* normal header (hist+step+reserved), per channel */ + { //if (first_sample == 0) { off_t header_offset = stream->offset + 0x04*channel; hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); - step_index = read_8bit(header_offset+0x02,stream->streamfile); /* 0x03: reserved */ + step_index = read_8bit(header_offset+0x02,stream->streamfile); if (step_index < 0) step_index = 0; if (step_index > 88) step_index = 88; @@ -391,90 +388,93 @@ void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o /* XBOX-IMA */ /* ************************************ */ -/* MS-IMA with fixed frame size, skips last sample per channel (for aligment) and custom multichannel nibble layout. - * For multichannel the layout is (I think) mixed stereo channels (ex. 6ch: 2ch + 2ch + 2ch) */ -void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { - int i, sample_count; - +/* MS-IMA with fixed frame size, and outputs an even number of samples per frame (skips last nibble). + * Defined in Xbox's SDK. Multichannel interleaves 2ch*N/2, or 1ch*N with odd num_channels + * (seen in some Koei .wav, could be simplified as interleaved stereo) --unsure if official. */ +void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - off_t offset = stream->offset; - - //internal interleave (0x20+4 size), mixed channels (4 byte per ch, mixed stereo) - int block_samples = (vgmstream->channels==1) ? - 32 : - 32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using % below + /* internal interleave (fixed size), mixed channels */ + int block_samples = (0x24-0x4) * 2; first_sample = first_sample % block_samples; - //normal header (per channel) + /* normal header (hist+step+reserved), per stereo/mono channel in blocks */ if (first_sample == 0) { - off_t header_offset; - header_offset = stream->offset + 4*(channel%2); + off_t header_offset = (channelspacing & 1) ? + stream->offset + 0x24*(channel) + 0x00: + stream->offset + 0x48*(channel/2) + 0x04*(channel%2); - hist1 = read_16bitLE(header_offset,stream->streamfile); - step_index = read_16bitLE(header_offset+2,stream->streamfile); - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - } - - for (i=first_sample,sample_count=0; ioffset + 4*(channel%2) + 4 + i/8*4 + (i%8)/2 : - stream->offset + 4*(channel%2) + 4*2 + i/8*4*2 + (i%8)/2; - nibble_shift = (i&1?4:0); //low nibble first - - std_ima_expand_nibble(stream, offset,nibble_shift, &hist1, &step_index); - outbuf[sample_count] = (short)(hist1); - } - - //internal interleave: increment offset on complete frame - if (channelspacing==1) { /* mono */ - if (offset-stream->offset == 32+3) // ?? - stream->offset += 0x24; - } else { - if (offset-stream->offset == 64+(4*(channel%2))+3) // ?? - stream->offset += 0x24*channelspacing; - } - - stream->adpcm_history1_32 = hist1; - stream->adpcm_step_index = step_index; -} - -/* mono XBOX-IMA ADPCM for interleave */ -void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0, num_frame; - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; - - //external interleave - int block_samples = (0x24 - 0x4) * 2; /* block size - header, 2 samples per byte */ - num_frame = first_sample / block_samples; - first_sample = first_sample % block_samples; - - //normal header - if (first_sample == 0) { - off_t header_offset = stream->offset + 0x24*num_frame; - - hist1 = read_16bitLE(header_offset,stream->streamfile); - step_index = read_8bit(header_offset+2,stream->streamfile); + hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); + step_index = read_8bit(header_offset+0x02,stream->streamfile); if (step_index < 0) step_index=0; if (step_index > 88) step_index=88; - //must write history from header as last nibble/sample in block is almost always 0 / not encoded + /* write header sample (even samples per block, skips last nibble) */ outbuf[sample_count] = (short)(hist1); sample_count += channelspacing; first_sample += 1; samples_to_do -= 1; } - for (i=first_sample; i < first_sample + samples_to_do; i++) { /* first_sample + samples_to_do should be block_samples at most */ - off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2; - int nibble_shift = ((i-1)&1?4:0); //low nibble first + /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel, in stereo blocks) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = (channelspacing & 1) ? + (stream->offset + 0x24*(channel) + 0x04) + (i-1)/2: + (stream->offset + 0x48*(channel/2) + 0x04*2) + 0x04*(channel%2) + 0x04*2*((i-1)/8) + ((i-1)%8)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ - //last nibble/sample in block is ignored (next header sample contains it) + /* must skip last nibble per official decoder, probably not needed though */ + if (i < block_samples) { + std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + } + } + + /* internal interleave: increment offset on complete frame */ + if (i == block_samples) { + stream->offset += 0x24*channelspacing; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* Multichannel XBOX-IMA ADPCM, with all channels mixed in the same block (equivalent to multichannel MS-IMA; seen in .rsd XADP). */ +void decode_xbox_ima_mch(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0, num_frame; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + /* external interleave (fixed size), multichannel */ + int block_samples = (0x24 - 0x4) * 2; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; + + /* normal header (hist+step+reserved), multichannel */ + if (first_sample == 0) { + off_t header_offset = stream->offset + 0x24*channelspacing*num_frame + 0x04*channel; + + hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); + step_index = read_8bit(header_offset+0x02,stream->streamfile); + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + + /* write header sample (even samples per block, skips last nibble) */ + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + first_sample += 1; + samples_to_do -= 1; + } + + /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = (stream->offset + 0x24*channelspacing*num_frame + 0x04*channelspacing) + 0x04*channel + 0x04*channelspacing*((i-1)/8) + ((i-1)%8)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ + + /* must skip last nibble per spec, rarely needed though */ if (i < block_samples) { std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); @@ -486,39 +486,86 @@ void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel stream->adpcm_step_index = step_index; } -void decode_nds_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_16;//todo unneeded 16? +/* Mono XBOX-IMA ADPCM, used for interleave. Also defined in Xbox's SDK. */ +void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0, num_frame; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - //external interleave + /* external interleave (fixed size), mono */ + int block_samples = (0x24 - 0x4) * 2; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; - //normal header + /* normal header (hist+step+reserved), single channel */ + if (first_sample == 0) { + off_t header_offset = stream->offset + 0x24*num_frame; + + hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); + step_index = read_8bit(header_offset+0x02,stream->streamfile); + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + + /* write header sample (even samples per block, skips last nibble) */ + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + first_sample += 1; + samples_to_do -= 1; + } + + /* decode nibbles (layout: all nibbles from one channel) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ + + /* must skip last nibble per spec, rarely needed though (ex. Gauntlet Dark Legacy) */ + if (i < block_samples) { + std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + } + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* Similar to MS-IMA with even number of samples, header sample is not written (setup only). + * Apparently clamps to -32767 unlike standard's -32768 (probably not noticeable). + * Info here: http://problemkaputt.de/gbatek.htm#dssoundnotes */ +void decode_nds_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; + + /* external interleave (configurable size), mono */ + + /* normal header (hist+step+reserved), single channel */ if (first_sample == 0) { off_t header_offset = stream->offset; hist1 = read_16bitLE(header_offset,stream->streamfile); step_index = read_16bitLE(header_offset+2,stream->streamfile); - - //todo clip step_index? + if (step_index < 0) step_index=0; /* probably pre-adjusted */ + if (step_index > 88) step_index=88; } + /* decode nibbles (layout: all nibbles from the channel) */ for (i=first_sample,sample_count=0; ioffset + 4 + i/2; - int nibble_shift = (i&1?4:0); //low nibble first + off_t byte_offset = stream->offset + 0x04 + i/2; + int nibble_shift = (i&1?4:0); /* low nibble first */ + //todo waveform has minor deviations using known expands std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); } - stream->adpcm_history1_16 = hist1; + stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } void decode_dat4_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_16;//todo unneeded 16? int step_index = stream->adpcm_step_index; @@ -548,7 +595,6 @@ void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -583,7 +629,6 @@ void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou void decode_rad_ima_mono(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; @@ -613,6 +658,7 @@ void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel stream->adpcm_step_index = step_index; } +/* Apple's IMA4, a.k.a QuickTime IMA. 2 byte header and header sample is not written (setup only). */ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i, sample_count, num_frame; int16_t hist1 = stream->adpcm_history1_16;//todo unneeded 16? @@ -647,91 +693,99 @@ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelsp /* XBOX-IMA with modified data layout */ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { - int i, sample_count; - + int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - //internal interleave - int block_samples = (0x24 - 4) * 2; /* block size - header, 2 samples per byte */ + /* internal interleave (configurable size), mixed channels */ + int block_samples = (0x24 - 0x4) * 2; first_sample = first_sample % block_samples; - //interleaved header (all hist per channel + all step_index per channel) + /* interleaved header (all hist per channel + all step_index+reserved per channel) */ if (first_sample == 0) { - off_t hist_offset = stream->offset + 2*channel; - off_t step_offset = stream->offset + 2*channel + 2*vgmstream->channels; + off_t hist_offset = stream->offset + 0x02*channel + 0x00; + off_t step_offset = stream->offset + 0x02*channel + 0x02*vgmstream->channels; - hist1 = read_16bitLE(hist_offset,stream->streamfile); + hist1 = read_16bitLE(hist_offset,stream->streamfile); step_index = read_8bit(step_offset,stream->streamfile); if (step_index < 0) step_index=0; if (step_index > 88) step_index=88; - } - for (i=first_sample,sample_count=0; ioffset + 4*vgmstream->channels + 2*channel + i/4*2*vgmstream->channels + (i%4)/2;//2-byte per channel - int nibble_shift = (i&1?4:0); //low nibble first - - std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); - outbuf[sample_count] = (short)(hist1); - } - - //internal interleave: increment offset on complete frame - if (i == block_samples) stream->offset += 36*vgmstream->channels; - - stream->adpcm_history1_32 = hist1; - stream->adpcm_step_index = step_index; -} - -/* XBOX-IMA with modified data layout */ -void decode_wwise_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0; - - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; - - //internal interleave (configurable size), block-interleave multichannel (ex. if block is 0xD8 in 6ch: 6 blocks of 4+0x20) - int block_samples = (vgmstream->interleave_block_size - 4*vgmstream->channels) * 2 / vgmstream->channels; - first_sample = first_sample % block_samples; - - //block-interleaved header (1 header per channel block); can be LE or BE - if (first_sample == 0) { - int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE; - off_t header_offset = stream->offset + (vgmstream->interleave_block_size / vgmstream->channels)*channel; - - hist1 = read_16bit(header_offset,stream->streamfile); - step_index = read_8bit(header_offset+2,stream->streamfile); - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - //must write history from header as last nibble/sample in block is almost always 0 / not encoded + /* write header sample (even samples per block, skips last nibble) */ outbuf[sample_count] = (short)(hist1); sample_count += channelspacing; first_sample += 1; samples_to_do -= 1; } - for (i=first_sample; i < first_sample + samples_to_do; i++) { /* first_sample + samples_to_do should be block_samples at most */ - off_t byte_offset = stream->offset + (vgmstream->interleave_block_size / vgmstream->channels)*channel + 4 + (i-1)/2; - int nibble_shift = ((i-1)&1?4:0); //low nibble first + /* decode nibbles (layout: 2 bytes/2*2 nibbles per channel) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x02*channel + (i-1)/4*2*vgmstream->channels + ((i-1)%4)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ - //last nibble/sample in block is ignored (next header sample contains it) + /* must skip last nibble per official decoder, probably not needed though */ if (i < block_samples) { std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); - sample_count+=channelspacing; + sample_count += channelspacing; } } - //internal interleave: increment offset on complete frame - if (i == block_samples) stream->offset += vgmstream->interleave_block_size; + /* internal interleave: increment offset on complete frame */ + if (i == block_samples) { + stream->offset += 0x24*vgmstream->channels; + } stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } -//todo atenuation: apparently from hcs's analysis Wwise IMA expands nibbles slightly different, reducing clipping/dbs -/* -From Wwise_v2015.1.6_Build5553_SDK.Linux -<_ZN13CAkADPCMCodec12DecodeSampleEiii>: + +/* mono XBOX-IMA with header endianness and alt nibble expand (per hcs's decompilation) */ +void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0, num_frame; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + /* external interleave (fixed size), mono */ + int block_samples = (0x24 - 0x4) * 2; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; + + /* normal header (hist+step+reserved), single channel */ + if (first_sample == 0) { + int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE; + off_t header_offset = stream->offset + 0x24*num_frame; + + hist1 = read_16bit(header_offset,stream->streamfile); + step_index = read_8bit(header_offset+2,stream->streamfile); + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + + /* write header sample (even samples per block, skips last nibble) */ + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + first_sample += 1; + samples_to_do -= 1; + } + + /* decode nibbles (layout: all nibbles from one channel) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ + + /* must skip last nibble like other XBOX-IMAs, often needed (ex. Bayonetta 2 sfx) */ + if (i < block_samples) { + std_ima_expand_nibble_mul(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + } + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} +/* from hcs's analysis Wwise IMA expands nibbles slightly different, reducing dbs. Just "MUL" expand? +<_ZN13CAkADPCMCodec12DecodeSampleEiii>: //From Wwise_v2015.1.6_Build5553_SDK.Linux 10: 83 e0 07 and $0x7,%eax ; sample 13: 01 c0 add %eax,%eax ; sample*2 15: 83 c0 01 add $0x1,%eax ; sample*2+1 @@ -740,8 +794,6 @@ From Wwise_v2015.1.6_Build5553_SDK.Linux 1f: 85 c0 test %eax,%eax ; result negative? 21: 0f 48 c2 cmovs %edx,%eax ; adjust if negative to fix rounding for below division 24: c1 f8 03 sar $0x3,%eax ; (sample*2+1)*scale/8 - -Different rounding model vs IMA's shift-and-add (also "adjust" step may be unnecessary). */ /* MS-IMA with possibly the XBOX-IMA model of even number of samples per block (more tests are needed) */ @@ -832,7 +884,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci (!(i%2) ? 4:0) : /* mono mode (high first) */ (channel==0 ? 4:0); /* stereo mode (high=L,low=R) */ - ubi_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + std_ima_expand_nibble_mul(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); /* all samples are written */ } @@ -849,7 +901,7 @@ size_t ima_bytes_to_samples(size_t bytes, int channels) { } size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) { - /* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ + /* MS-IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ return (bytes / block_align) * ((block_align - 0x04*channels) * 2 / channels + 1) + ((bytes % block_align) ? (((bytes % block_align) - 0x04*channels) * 2 / channels + 1) : 0); } @@ -858,7 +910,13 @@ size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) { int block_align = 0x24 * channels; /* XBOX IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels - + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); //todo probably not possible (aligned) + + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */ +} + +size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) { + int block_align = 0x22 * channels; + return (bytes / block_align) * (block_align - 0x02*channels) * 2 / channels + + ((bytes % block_align) ? ((bytes % block_align) - 0x02*channels) * 2 / channels : 0); } size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mp4_aac_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mp4_aac_decoder.c index 4675b78fc..3712362f1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mp4_aac_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mp4_aac_decoder.c @@ -86,6 +86,8 @@ void decode_mp4_aac(mp4_aac_codec_data * data, sample * outbuf, int32_t samples_ void reset_mp4_aac(VGMSTREAM *vgmstream) { mp4_aac_codec_data *data = vgmstream->codec_data; + if (!data) return; + data->sampleId = 0; data->sample_ptr = data->samples_per_frame; data->samples_discard = 0; @@ -93,6 +95,8 @@ void reset_mp4_aac(VGMSTREAM *vgmstream) { void seek_mp4_aac(VGMSTREAM *vgmstream, int32_t num_sample) { mp4_aac_codec_data *data = (mp4_aac_codec_data *)(vgmstream->codec_data); + if (!data) return; + data->sampleId = 0; data->sample_ptr = data->samples_per_frame; data->samples_discard = num_sample; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index f225f931d..a7737ddf9 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -58,7 +58,11 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m break; case MPEG_LYN: - goto fail; /* not fully implemented */ + if (data->config.interleave <= 0) + goto fail; /* needs external fixed size */ + data->default_buffer_size = data->config.interleave; + //todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder + break; case MPEG_STANDARD: case MPEG_AHX: @@ -137,9 +141,9 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */ case MPEG_SCD: + case MPEG_LYN: current_interleave = data->config.interleave; -#if 1 /* check if current interleave block is short */ { off_t block_offset = stream->offset - stream->channel_start_offset; @@ -148,7 +152,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d if (data->config.data_size && block_offset + next_block >= data->config.data_size) current_interleave = (data->config.data_size % next_block) / data->streams_size; /* short_interleave*/ } -#endif + current_interleave_pre = current_interleave*num_stream; current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre; @@ -162,7 +166,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d break; } if (!current_data_size || current_data_size > ms->buffer_size) { - VGM_LOG("MPEG: incorrect data_size 0x%x\n", current_data_size); + VGM_LOG("MPEG: incorrect data_size 0x%x vs buffer 0x%x\n", current_data_size, ms->buffer_size); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c index c8d827f18..6c1c6d76a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -147,10 +147,12 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data * if (!ealayer3_skip_data(stream, data, num_stream, 0)) goto fail; - } - // todo rarely there is a block between granules (ex. EAL3 v2P in FIFA 2016) + /* In EAL3 V2P sometimes there is a new SNS/SPS block between granules. Instead of trying to fix it here + * or in blocked layout (too complex/patchy), SNS/SPS uses a custom streamfile that simply removes all + * block headers, so this parser only sees raw EALayer3 data. It also discards samples, which confuses + * blocked layout calculations */ /* get second frame/granule (MPEG1 only) if first granule was found */ granule_found = 0; @@ -298,7 +300,7 @@ static int ealayer3_parse_frame_v2(vgm_bitstream *is, ealayer3_frame_info * eaf) if (!ok) goto fail; } VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_common_size == 0, "EA EAL3: v2 empty frame\n"); /* seen in V2S */ - VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_mode_value > 0, "EA EAL3: v2_mode=%x with 0x%x\n", eaf->v2_mode, eaf->v2_mode_value); + VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_mode_value > 0, "EA EAL3: v2_mode=%x with value=0x%x\n", eaf->v2_mode, eaf->v2_mode_value); //VGM_ASSERT(eaf->v2_pcm_number > 0, "EA EAL3: v2_pcm_number 0x%x\n", eaf->v2_pcm_number); eaf->pcm_size = (2*eaf->v2_pcm_number * eaf->channels); @@ -550,8 +552,6 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d size_t bytes_filled; int i; - if (!eaf->pcm_size) - return 1; bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame; if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) { @@ -561,6 +561,9 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d if (eaf->v1_pcm_number) { + if (!eaf->pcm_size) + return 1; + VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%lx\n", eaf->v1_pcm_decode_discard, stream->offset); VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%lx\n", eaf->v1_pcm_number, stream->offset); @@ -583,6 +586,7 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number; } else { + // todo also discard if (decode_to_discard == 0) /* seems ok? */ decode_to_discard += data->samples_per_frame;//+ eaf->v1_pcm_number; else if (decode_to_discard == 576) /* untested */ @@ -594,17 +598,6 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d if (eaf->v2_extended_flag) { - if (eaf->v2_pcm_number) { - /* read + write PCM block samples (always BE) */ - for (i = 0; i < eaf->v2_pcm_number * data->channels_per_frame; i++) { - off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size + sizeof(sample)*i; - int16_t pcm_sample = read_16bitBE(pcm_offset,stream->streamfile); - put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample); - } - ms->samples_filled += eaf->v2_pcm_number; - } - -#if 0 /* todo supposed skip modes (only seen 0x00): * * AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG @@ -620,16 +613,27 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d * if 3: 576 */ + //;VGM_LOG("EA EAL3 v2: off=%lx, mode=%x, value=%x, pcm=%x, size=%x\n", stream->offset, eaf->v2_mode, eaf->v2_mode_value, eaf->v2_pcm_number, eaf->v2_common_size); + + if (eaf->v2_pcm_number) { + /* read + write PCM block samples (always BE) */ + for (i = 0; i < eaf->v2_pcm_number * data->channels_per_frame; i++) { + off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size + sizeof(sample)*i; + int16_t pcm_sample = read_16bitBE(pcm_offset,stream->streamfile); + put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample); + } + ms->samples_filled += eaf->v2_pcm_number; + } + /* modify decoded samples depending on flag */ if (eaf->v2_mode == 0x00) { - size_t decode_to_discard = eaf->v2_mode_value; - - if (decode_to_discard == 576) - decode_to_discard = data->samples_per_frame;//+ eaf->v2_pcm_number; + size_t decode_to_discard = eaf->v2_mode_value; /* (usually 0 in V2P, varies in V2S) */ + if (decode_to_discard == 0) + decode_to_discard = 576; + //todo output seems correct-ish but reaches file end and tries to parse more frames ms->decode_to_discard += decode_to_discard; } -#endif } return 1; @@ -638,7 +642,7 @@ fail: } -/* Skip EA-frames from other streams for multichannel (interleaved 1 EA-frame per stream). +/* Skip EA-frames from other streams for .sns/sps multichannel (interleaved 1 EA-frame per stream). * Due to EALayer3 being in blocks and other complexities (we can't go past a block) all * streams's offsets should start in the first stream's EA-frame. * @@ -649,6 +653,8 @@ fail: * - skip one EA-frame per following streams until offset is in first stream's EA-frame * (ie. 1st stream skips 2, 2nd stream skips 1, 3rd stream skips 0) * - repeat again for granule1 + * + * EALayer3 v1 in SCHl uses external offsets and 1ch multichannel instead. */ static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) { int ok, i; @@ -657,6 +663,9 @@ static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, i uint8_t ibuf[EALAYER3_EA_FRAME_BUFFER_SIZE]; int skips = at_start ? num_stream : data->streams_size - 1 - num_stream; + /* v1 does multichannel with set offsets */ + if (data->type == MPEG_EAL31) + return 1; for (i = 0; i < skips; i++) { is.buf = ibuf; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index 1c1385acc..ba7f77d96 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -130,6 +130,8 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co memcpy(&data->config, config, sizeof(mpeg_custom_config)); data->config.channels = channels; + data->default_buffer_size = MPEG_DATA_BUFFER_SIZE; + /* init per subtype */ switch(data->type) { case MPEG_EAL31: @@ -144,6 +146,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */ if (channels < data->channels_per_frame) goto fail; + if (data->default_buffer_size > 0x8000) goto fail; /* init streams */ @@ -160,7 +163,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co if (!data->streams[i]->output_buffer) goto fail; /* one per stream as sometimes mpg123 can't read the whole buffer in one pass */ - data->streams[i]->buffer_size = MPEG_DATA_BUFFER_SIZE; + data->streams[i]->buffer_size = data->default_buffer_size; data->streams[i]->buffer = calloc(sizeof(uint8_t), data->streams[i]->buffer_size); if (!data->streams[i]->buffer) goto fail; } @@ -499,6 +502,7 @@ void free_mpeg(mpeg_codec_data *data) { 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 @@ -523,6 +527,7 @@ void reset_mpeg(VGMSTREAM *vgmstream) { 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) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c index 17fba4b8f..6e25a9286 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c @@ -22,15 +22,21 @@ void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample * outbuf, int32_t sa void reset_ogg_vorbis(VGMSTREAM *vgmstream) { + OggVorbis_File *ogg_vorbis_file; ogg_vorbis_codec_data *data = vgmstream->codec_data; - OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); + if (!data) return; + + ogg_vorbis_file = &(data->ogg_vorbis_file); ov_pcm_seek(ogg_vorbis_file, 0); } void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { + OggVorbis_File *ogg_vorbis_file; ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *)(vgmstream->codec_data); - OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); + if (!data) return; + + ogg_vorbis_file = &(data->ogg_vorbis_file); ov_pcm_seek_lap(ogg_vorbis_file, num_sample); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c index 3e016f949..406f0dff8 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c @@ -78,15 +78,6 @@ void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa } } -void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; - int32_t sample_count; - - for (i=first_sample,sample_count=0; ioffset+i*2*channelspacing,stream->streamfile)^stream->key_xor; - } -} - static int expand_ulaw(uint8_t ulawbyte) { int sign, segment, quantization, new_sample; const int bias = 0x84; @@ -177,7 +168,7 @@ void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac int sample_pcm; sample_float = (float*)&sample_int; - sample_pcm = floor((*sample_float) * 32767.f + .5f); + sample_pcm = (int)floor((*sample_float) * 32767.f + .5f); outbuf[sample_count] = clamp16(sample_pcm); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c index a85250ad5..0fcc3751b 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c @@ -110,52 +110,6 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, stream->adpcm_history2_32=hist2; } -/* encrypted */ -void decode_psx_bmdx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - - int predict_nr, shift_factor, sample; - int32_t hist1=stream->adpcm_history1_32; - int32_t hist2=stream->adpcm_history2_32; - - short scale; - int i; - int32_t sample_count; - uint8_t flag; - - int framesin = first_sample/28; - int head = read_8bit(stream->offset+framesin*16,stream->streamfile) ^ stream->bmdx_xor; - - predict_nr = ((head >> 4) & 0xf); - shift_factor = (head & 0xf); - flag = read_8bit(stream->offset+framesin*16+1,stream->streamfile); - - first_sample = first_sample % 28; - - for (i=first_sample,sample_count=0; ioffset+(framesin*16)+2+i/2,stream->streamfile); - if (i/2 == 0) - sample_byte = (short)(int8_t)(sample_byte+stream->bmdx_add); - - scale = ((i&1 ? - sample_byte >> 4 : - sample_byte & 0x0f)<<12); - - sample=(int)((scale >> shift_factor)+hist1*VAG_f[predict_nr][0]+hist2*VAG_f[predict_nr][1]); - } - - outbuf[sample_count] = clamp16(sample); - hist2=hist1; - hist1=sample; - } - stream->adpcm_history1_32=hist1; - stream->adpcm_history2_32=hist2; -} - /* some games have garbage (?) in their flags, this decoder just ignores that byte */ void decode_psx_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { @@ -257,5 +211,9 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int cha size_t ps_bytes_to_samples(size_t bytes, int channels) { - return bytes / channels / 16 * 28; + return bytes / channels / 0x10 * 28; +} + +size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels) { + return bytes / channels / frame_size * 28; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c index 29d20a321..b41dbd226 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c @@ -180,7 +180,7 @@ static void pcm_convert_float_to_16(vorbis_custom_codec_data * data, sample * ou sample *ptr = outbuf + i; float *mono = pcm[i]; for (j = 0; j < samples_to_do; j++) { - int val = floor(mono[j] * 32767.f + .5f); + int val = (int)floor(mono[j] * 32767.f + .5f); if (val > 32767) val = 32767; if (val < -32768) val = -32768; @@ -207,6 +207,7 @@ void free_vorbis_custom(vorbis_custom_codec_data * data) { void reset_vorbis_custom(VGMSTREAM *vgmstream) { vorbis_custom_codec_data *data = vgmstream->codec_data; + if (!data) return; /* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead. * To avoid having to parse different formats we'll just discard until the expected sample */ @@ -216,6 +217,7 @@ void reset_vorbis_custom(VGMSTREAM *vgmstream) { void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample) { vorbis_custom_codec_data *data = vgmstream->codec_data; + if (!data) return; /* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead. * To avoid having to parse different formats we'll just discard until the expected sample */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c index 3198da0cc..a8fd60f63 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c @@ -146,7 +146,12 @@ static int build_header_comment(uint8_t * buf, size_t bufsize) { static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { int bytes; - /* try to load from external files first */ + /* try to locate from the precompiled list */ + bytes = load_fvs_array(buf, bufsize, setup_id, streamFile); + if (bytes) + return bytes; + + /* try to load from external files */ bytes = load_fvs_file_single(buf, bufsize, setup_id, streamFile); if (bytes) return bytes; @@ -155,11 +160,6 @@ static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, if (bytes) return bytes; - /* try to locate from the precompiled list */ - bytes = load_fvs_array(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - /* not found */ VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id); return 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c index 73ee1508c..8ddafb51c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c @@ -13,11 +13,11 @@ /* DEFS */ /* **************************************************************************** */ -static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); -static int build_header_comment(uint8_t * buf, size_t bufsize); -static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian); -static int rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); -static int rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels); +static size_t build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); +static size_t build_header_comment(uint8_t * buf, size_t bufsize); +static size_t get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian); +static size_t rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); +static size_t rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels); static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile); @@ -126,7 +126,7 @@ fail: /* **************************************************************************** */ /* loads info from a wwise packet header */ -static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian) { +static size_t get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian) { int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; @@ -153,7 +153,7 @@ static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_ } /* Transforms a Wwise data packet into a real Vorbis one (depending on config) */ -static int rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) { +static size_t rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) { vgm_bitstream ow, iw; int rc, granulepos; size_t header_size, packet_size; @@ -196,7 +196,7 @@ fail: /* Transforms a Wwise setup packet into a real Vorbis one (depending on config). */ -static int rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels) { +static size_t rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels) { vgm_bitstream ow, iw; int rc, granulepos; size_t header_size, packet_size; @@ -238,8 +238,8 @@ fail: return 0; } -static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp) { - int bytes = 0x1e; +static size_t build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp) { + size_t bytes = 0x1e; uint8_t blocksizes; if (bytes > bufsize) return 0; @@ -260,8 +260,8 @@ static int build_header_identification(uint8_t * buf, size_t bufsize, int channe return bytes; } -static int build_header_comment(uint8_t * buf, size_t bufsize) { - int bytes = 0x19; +static size_t build_header_comment(uint8_t * buf, size_t bufsize) { + size_t bytes = 0x19; if (bytes > bufsize) return 0; @@ -1098,13 +1098,13 @@ static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) { size_t bytes; - /* try to load from external file (ignoring type, just use file if found) */ - bytes = load_wvc_file(ibuf, ibufsize, codebook_id, streamFile); + /* try to locate from the precompiled list */ + bytes = load_wvc_array(ibuf, ibufsize, codebook_id, setup_type); if (bytes) return bytes; - /* try to locate from the precompiled list */ - bytes = load_wvc_array(ibuf, ibufsize, codebook_id, setup_type); + /* try to load from external file (ignoring type, just use file if found) */ + bytes = load_wvc_file(ibuf, ibufsize, codebook_id, streamFile); if (bytes) return bytes; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c new file mode 100644 index 000000000..15b1e9af3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c @@ -0,0 +1,158 @@ +#include "../util.h" +#include "coding.h" + +/* fixed point (.8) amount to scale the current step size by */ +/* part of the same series as used in MS ADPCM "ADPCMTable" */ +static const unsigned int scale_step[16] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 230, 230, 230, 230, 307, 409, 512, 614 +}; + +/* expand an unsigned four bit delta to a wider signed range */ +static const int scale_delta[16] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9,-11,-13,-15 +}; + + +/* raw Yamaha ADPCM a.k.a AICA as it's mainly used in Naomi/Dreamcast (also in RIFF and older arcade sound chips). */ +void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { + int i, sample_count; + + int32_t hist1 = stream->adpcm_history1_16; + int step_size = stream->adpcm_step_index; + + /* no header (external setup), pre-clamp for wrong values */ + if (step_size < 0x7f) step_size = 0x7f; + if (step_size > 0x6000) step_size = 0x6000; + + for (i=first_sample,sample_count=0; ioffset + i : /* stereo: one nibble per channel */ + stream->offset + i/2; /* mono: consecutive nibbles */ + int nibble_shift = is_stereo ? + (!(channel&1) ? 0:4) : /* even = low/L, odd = high/R */ + (!(i&1) ? 0:4); /* low nibble first */ + + /* Yamaha/AICA expand, but same result as IMA's (((delta * 2 + 1) * step) >> 3) */ + sample_nibble = ((read_8bit(byte_offset,stream->streamfile) >> nibble_shift))&0xf; + sample_delta = (step_size * scale_delta[sample_nibble]) / 8; + sample_decoded = hist1 + sample_delta; + + outbuf[sample_count] = clamp16(sample_decoded); + hist1 = outbuf[sample_count]; + + step_size = (step_size * scale_step[sample_nibble]) >> 8; + if (step_size < 0x7f) step_size = 0x7f; + if (step_size > 0x6000) step_size = 0x6000; + } + + stream->adpcm_history1_16 = hist1; + stream->adpcm_step_index = step_size; +} + +/* Yamaha ADPCM, in headered frames like MS-IMA. Possibly originated from Yamaha's SMAF tools + * (Windows ACM encoder/decoder was given in their site). Some info from Rockbox's yamaha_adpcm.c */ +void decode_yamaha(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count, num_frame; + int32_t hist1 = stream->adpcm_history1_32; + int step_size = stream->adpcm_step_index; + + /* external interleave */ + int block_samples = (0x40 - 0x04*channelspacing) * 2 / channelspacing; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; + + /* header (hist+step) */ + if (first_sample == 0) { + off_t header_offset = stream->offset + 0x40*num_frame + 0x04*channel; + + hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); + step_size = read_16bitLE(header_offset+0x02,stream->streamfile); + if (step_size < 0x7f) step_size = 0x7f; + if (step_size > 0x6000) step_size = 0x6000; + } + + /* decode nibbles (layout: varies) */ + for (i=first_sample,sample_count=0; ioffset + 0x40*num_frame + 0x04*channelspacing) + i : /* stereo: one nibble per channel */ + (stream->offset + 0x40*num_frame + 0x04*channelspacing) + i/2; /* mono: consecutive nibbles */ + int nibble_shift = (channelspacing == 2) ? + (!(channel&1) ? 0:4) : + (!(i&1) ? 0:4); /* even = low, odd = high */ + + /* Yamaha/AICA expand, but same result as IMA's (((delta * 2 + 1) * step) >> 3) */ + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_delta = (step_size * scale_delta[sample_nibble]) / 8; + sample_decoded = hist1 + sample_delta; + + outbuf[sample_count] = clamp16(sample_decoded); + hist1 = outbuf[sample_count]; + + step_size = (step_size * scale_step[sample_nibble]) >> 8; + if (step_size < 0x7f) step_size = 0x7f; + if (step_size > 0x6000) step_size = 0x6000; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_size; +} + +/* Yamaha ADPCM with unknown expand variation (noisy), step size is double of normal Yamaha? */ +void decode_yamaha_nxap(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i, sample_count, num_frame; + int32_t hist1 = stream->adpcm_history1_32; + int step_size = stream->adpcm_step_index; + + /* external interleave */ + int block_samples = (0x40 - 0x4) * 2; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; + + /* header (hist+step) */ + if (first_sample == 0) { + off_t header_offset = stream->offset + 0x40*num_frame; + + hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); + step_size = read_16bitLE(header_offset+0x02,stream->streamfile); + if (step_size < 0x7f) step_size = 0x7f; + if (step_size > 0x6000) step_size = 0x6000; + } + + /* decode nibbles (layout: all nibbles from one channel) */ + for (i=first_sample,sample_count=0; ioffset + 0x40*num_frame + 0x04) + i/2; + int nibble_shift = (i&1?4:0); /* low nibble first */ + + /* Yamaha expand? */ + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_delta = (step_size * scale_delta[sample_nibble] / 4) / 8; //todo not ok + sample_decoded = hist1 + sample_delta; + + outbuf[sample_count] = clamp16(sample_decoded); + hist1 = outbuf[sample_count]; + + step_size = (step_size * scale_step[sample_nibble]) >> 8; + if (step_size < 0x7f) step_size = 0x7f; + if (step_size > 0x6000) step_size = 0x6000; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_size; +} + +size_t aica_bytes_to_samples(size_t bytes, int channels) { + /* 2 samples per byte (2 nibbles) in stereo or mono config */ + return bytes * 2 / channels; +} + +size_t yamaha_bytes_to_samples(size_t bytes, int channels) { + int block_align = 0x40; + + return (bytes / block_align) * (block_align - 0x04*channels) * 2 / channels + + ((bytes % block_align) ? ((bytes % block_align) - 0x04*channels) * 2 / channels : 0); +} diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 445b64e3c..8ba3bb0ff 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -19,6 +19,7 @@ static const char* extension_list[] = { //"ac3", //FFmpeg, not parsed //common? "ace", //fake, for tri-Ace's formats (to be removed) "acm", + "ad", //txth/reserved [Xenosaga Freaks (PS2)] "adm", "adp", "adpcm", @@ -43,7 +44,9 @@ static const char* extension_list[] = { "ast", "at3", "at9", + "atsl", "atsl3", + "atsl4", "atx", "aud", "aus", @@ -86,10 +89,11 @@ static const char* extension_list[] = { "cbd2", "ccc", "cd", - "cfn", + "cfn", //fake extension/header id for .caf (to be removed) "ckd", "cnk", "cps", + "cvs", "cxs", "da", @@ -105,8 +109,10 @@ static const char* extension_list[] = { "dvi", "dxh", + "e4x", "eam", "emff", + "eno", "enth", "exa", "ezw", @@ -116,6 +122,7 @@ static const char* extension_list[] = { "filp", "flx", "fsb", + "fsv", "fwav", "g1l", @@ -159,10 +166,12 @@ static const char* extension_list[] = { "khv", "km9", "kovs", //.kvs header id + "kns", "kraw", - "ktss", + "ktss", //.kns header id "kvs", + "l", "laac", //fake extension, for AAC (tri-Ace/FFmpeg) "lac3", //fake extension, for AC3 "leg", @@ -170,6 +179,7 @@ static const char* extension_list[] = { "logg", //fake extension, for OGGs "lopus", //fake extension, for OPUS "lpcm", + "lpk", "lps", "lsf", "lstm", //fake extension, for STMs @@ -179,6 +189,7 @@ static const char* extension_list[] = { "matx", "mc3", "mca", + "mcadpcm", "mcg", "mds", "mdsp", @@ -193,9 +204,13 @@ static const char* extension_list[] = { //"mpc", //FFmpeg, not parsed (musepack) //common "mpdsp", "mpds", + "ms", "msa", + "msb", + "msd", "msf", "mss", + "msv", //txh/reserved [Fight Club (PS2)] "msvp", "mta2", "mtaf", @@ -221,6 +236,7 @@ static const char* extension_list[] = { "omu", //"opus", //common "otm", + "ovb", "p1d", //txth/reserved [Farming Simulator 18 (3DS)] "p2bt", @@ -228,19 +244,24 @@ static const char* extension_list[] = { "past", "pcm", "pdt", + "pk", "pnb", "pona", "pos", "ps2stm", //fake extension for .stm (to be removed) - "psh", + "psh", // fake extension for VSV(?) Dawn of Mana needs to be checked again "psnd", - "psw", + "psw", //fake extension for .wam + "r", + "rac", //txth/reserved [Manhunt (Xbox)] "rak", "ras", "raw", "rkv", "rnd", + "rof", + "rpgmvo", "rrds", "rsd", "rsf", @@ -282,9 +303,12 @@ static const char* extension_list[] = { "sgd", "sgx", "sl3", + "slb", //txth/reserved [THE Nekomura no Hitobito (PS2)] "sli", + "smc", "smp", "smpl", //fake extension (to be removed) + "smv", "snd", "snds", "sng", @@ -292,6 +316,7 @@ static const char* extension_list[] = { "snr", "sns", "snu", + "son", "spd", "spm", "sps", @@ -305,6 +330,7 @@ static const char* extension_list[] = { //"stm", //common "stma", //fake extension (to be removed) "str", + "stream", "strm", "sts", "stx", @@ -313,16 +339,19 @@ static const char* extension_list[] = { "swag", "swav", "swd", + "switch_audio" "sx", "sxd", "sxd2", "tec", "thp", - "tk1", "tk5", "tra", + "trj", + "trm", "tun", + "txtp", "tydsp", "ulw", @@ -335,24 +364,29 @@ static const char* extension_list[] = { "vawx", "vb", "vbk", + "vbx", //txth/reserved [THE Taxi 2 (PS2)] "vds", "vdm", "vgs", "vgv", "vig", + "vis", //txth/reserved [AirForce Delta (PS2)] "vms", "voi", "vpk", "vs", "vsf", + "vsv", // official extension for PSH? TODO: recheck Dawn of Mana "vxn", "waa", "wac", "wad", + "waf", "wam", "was", //"wav", //common + "wave", "wavm", "wb", "wem", @@ -364,6 +398,7 @@ static const char* extension_list[] = { "wsd", "wsi", "wv2", //txth/reserved [Slave Zero (PC)] + "wve", "wvs", "xa", @@ -379,7 +414,7 @@ static const char* extension_list[] = { "xss", "xvag", "xvas", - "xwav", + "xwav",//fake, to be removed "xwb", "xwc", "xwm", //FFmpeg, not parsed (XWMA) @@ -425,7 +460,6 @@ typedef struct { static const coding_info coding_info_list[] = { {coding_PCM16LE, "Little Endian 16-bit PCM"}, - {coding_PCM16LE_XOR_int, "Little Endian 16-bit PCM with 2 byte interleave and XOR obfuscation"}, {coding_PCM16BE, "Big Endian 16-bit PCM"}, {coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"}, {coding_PCM8, "8-bit PCM"}, @@ -454,9 +488,8 @@ static const coding_info coding_info_list[] = { {coding_XA, "CD-ROM XA 4-bit ADPCM"}, {coding_PSX, "Playstation 4-bit ADPCM"}, {coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"}, - {coding_PSX_bmdx, "Playstation 4-bit ADPCM (BMDX encryption)"}, {coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"}, - {coding_HEVAG, "Playstation Vita HEVAG 4-bit ADPCM"}, + {coding_HEVAG, "Sony HEVAG 4-bit ADPCM"}, {coding_EA_XA, "Electronic Arts EA-XA 4-bit ADPCM v1"}, {coding_EA_XA_int, "Electronic Arts EA-XA 4-bit ADPCM v1 (mono/interleave)"}, @@ -471,6 +504,7 @@ static const coding_info coding_info_list[] = { {coding_3DS_IMA, "3DS IMA 4-bit ADPCM"}, {coding_MS_IMA, "Microsoft 4-bit IMA ADPCM"}, {coding_XBOX_IMA, "XBOX 4-bit IMA ADPCM"}, + {coding_XBOX_IMA_mch, "XBOX 4-bit IMA ADPCM (multichannel)"}, {coding_XBOX_IMA_int, "XBOX 4-bit IMA ADPCM (mono/interleave)"}, {coding_NDS_IMA, "NDS-style 4-bit IMA ADPCM"}, {coding_DAT4_IMA, "Eurocom DAT4 4-bit IMA ADPCM"}, @@ -488,6 +522,9 @@ static const coding_info coding_info_list[] = { {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, + {coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"}, + {coding_YAMAHA, "Yamaha 4-bit ADPCM"}, + {coding_YAMAHA_NXAP, "Yamaha NXAP 4-bit ADPCM"}, {coding_NDS_PROCYON, "Procyon Studio Digital Sound Elements NDS 4-bit APDCM"}, {coding_L5_555, "Level-5 0x555 4-bit ADPCM"}, {coding_SASSC, "Activision / EXAKT SASSC 8-bit DPCM"}, @@ -495,25 +532,21 @@ static const coding_info coding_info_list[] = { {coding_MTAF, "Konami MTAF 4-bit ADPCM"}, {coding_MTA2, "Konami MTA2 4-bit ADPCM"}, {coding_MC3, "Paradigm MC3 3-bit ADPCM"}, + {coding_FADPCM, "FMOD FADPCM 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"}, {coding_CBD2, "Cuberoot-delta-exact (CBD2) 8-bit DPCM"}, {coding_CBD2_int, "Cuberoot-delta-exact (CBD2) 8-bit DPCM with 1 byte interleave"}, {coding_ACM, "InterPlay ACM"}, - {coding_NWA0, "NWA DPCM Level 0"}, - {coding_NWA1, "NWA DPCM Level 1"}, - {coding_NWA2, "NWA DPCM Level 2"}, - {coding_NWA3, "NWA DPCM Level 3"}, - {coding_NWA4, "NWA DPCM Level 4"}, - {coding_NWA5, "NWA DPCM Level 5"}, + {coding_NWA, "VisualArt's NWA DPCM"}, {coding_EA_MT, "Electronic Arts MicroTalk"}, {coding_CRI_HCA, "CRI HCA"}, #ifdef VGM_USE_VORBIS - {coding_ogg_vorbis, "Ogg Vorbis"}, + {coding_OGG_VORBIS, "Ogg Vorbis"}, {coding_VORBIS_custom, "Custom Vorbis"}, #endif #ifdef VGM_USE_MPEG @@ -544,48 +577,47 @@ static const coding_info coding_info_list[] = { static const layout_info layout_info_list[] = { {layout_none, "flat (no layout)"}, {layout_interleave, "interleave"}, - {layout_interleave_shortblock, "interleave with short last block"}, - {layout_mxch_blocked, "MxCh blocked"}, - {layout_ast_blocked, "AST blocked"}, - {layout_halpst_blocked, "HALPST blocked"}, - {layout_xa_blocked, "CD-ROM XA"}, + + {layout_segmented, "segmented"}, + {layout_layered, "layered"}, + {layout_aix, "AIX"}, + + {layout_blocked_mxch, "blocked (MxCh)"}, + {layout_blocked_ast, "blocked (AST)"}, + {layout_blocked_halpst, "blocked (HALPST)"}, + {layout_blocked_xa, "blocked (XA)"}, {layout_blocked_ea_schl, "blocked (EA SCHl)"}, {layout_blocked_ea_1snh, "blocked (EA 1SNh)"}, - {layout_caf_blocked, "CAF blocked"}, - {layout_wsi_blocked, ".wsi blocked"}, - {layout_xvas_blocked, ".xvas blocked"}, - {layout_str_snds_blocked, ".str SNDS blocked"}, - {layout_ws_aud_blocked, "Westwood Studios .aud blocked"}, - {layout_matx_blocked, "Matrix .matx blocked"}, + {layout_blocked_caf, "blocked (CAF)"}, + {layout_blocked_wsi, "blocked (WSI)"}, + {layout_blocked_xvas, "blocked (.xvas)"}, + {layout_blocked_str_snds, "blocked (.str SNDS)"}, + {layout_blocked_ws_aud, "blocked (Westwood Studios .aud)"}, + {layout_blocked_matx, "blocked (Matrix .matx)"}, {layout_blocked_dec, "blocked (DEC)"}, - {layout_vs_blocked, "vs blocked"}, - {layout_emff_ps2_blocked, "EMFF (PS2) blocked"}, - {layout_emff_ngc_blocked, "EMFF (NGC/WII) blocked"}, - {layout_gsb_blocked, "GSB blocked"}, - {layout_thp_blocked, "THP Movie Audio blocked"}, - {layout_filp_blocked, "FILp blocked"}, + {layout_blocked_vs, "blocked (vs)"}, + {layout_blocked_emff_ps2, "blocked (EMFF PS2)"}, + {layout_blocked_emff_ngc, "blocked (EMFF NGC)"}, + {layout_blocked_gsb, "blocked (GSB)"}, + {layout_blocked_thp, "blocked (THP Movie Audio)"}, + {layout_blocked_filp, "blocked (FILP)"}, {layout_blocked_ea_swvr, "blocked (EA SWVR)"}, - {layout_ps2_adm_blocked, "ADM blocked"}, - {layout_dsp_bdsp_blocked, "DSP blocked"}, + {layout_blocked_adm, "blocked (ADM)"}, + {layout_blocked_bdsp, "blocked (BDSP)"}, {layout_blocked_ivaud, "blocked (IVAUD)"}, - {layout_ps2_iab_blocked, "IAB blocked"}, - {layout_ps2_strlr_blocked, "The Bouncer STR blocked"}, - {layout_rws_blocked, "RWS blocked"}, - {layout_hwas_blocked, "HWAS blocked"}, - {layout_tra_blocked, "TRA blocked"}, - {layout_acm, "ACM blocked"}, - {layout_mus_acm, "multiple ACM files, ACM blocked"}, - {layout_aix, "AIX interleave, internally 18-byte interleaved"}, - {layout_aax, "AAX blocked, 18-byte interleaved"}, - {layout_scd_int, "SCD multistream interleave"}, + {layout_blocked_ps2_iab, "blocked (IAB)"}, + {layout_blocked_ps2_strlr, "blocked (The Bouncer STR)"}, + {layout_blocked_rws, "blocked (RWS)"}, + {layout_blocked_hwas, "blocked (HWAS)"}, + {layout_blocked_tra, "blocked (TRA)"}, {layout_blocked_ea_sns, "blocked (EA SNS)"}, {layout_blocked_awc, "blocked (AWC)"}, {layout_blocked_vgs, "blocked (VGS)"}, {layout_blocked_vawx, "blocked (VAWX)"}, {layout_blocked_xvag_subsong, "blocked (XVAG subsong)"}, -#ifdef VGM_USE_VORBIS - {layout_ogg_vorbis, "Ogg"}, -#endif + {layout_blocked_ea_wve_au00, "blocked (EA WVE au00)"}, + {layout_blocked_ea_wve_ad10, "blocked (EA WVE Ad10)"}, + {layout_blocked_sthd, "blocked (STHD)"}, }; static const meta_info meta_info_list[] = { @@ -642,17 +674,16 @@ static const meta_info meta_info_list[] = { {meta_PS2_ILD, "ILD header"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, {meta_XBOX_WAVM, "Xbox WAVM raw header"}, - {meta_XBOX_RIFF, "Microsoft XWAV RIFF header"}, {meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"}, {meta_EA_SCHL, "Electronic Arts SCHl header (variable)"}, {meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"}, - {meta_CFN, "tri-Crescendo CAF Header"}, + {meta_CAF, "tri-Crescendo CAF Header"}, {meta_PS2_VPK, "VPK Header"}, - {meta_GENH, "GENH Generic Header"}, - {meta_DSP_SADB, "sadb header"}, - {meta_SADL, "sadl header"}, + {meta_GENH, "GENH generic header"}, + {meta_DSP_SADB, "Procyon Studio SADB header"}, + {meta_SADL, "Procyon Studio SADL header"}, {meta_PS2_BMDX, "Beatmania .bmdx header"}, - {meta_DSP_WSI, ".wsi header"}, + {meta_DSP_WSI, "Alone in the Dark .WSI header"}, {meta_AIFC, "Audio Interchange File Format AIFF-C"}, {meta_AIFF, "Audio Interchange File Format"}, {meta_STR_SNDS, ".str SNDS SHDR chunk"}, @@ -662,15 +693,15 @@ static const meta_info meta_info_list[] = { {meta_PS2_SVS, "Square SVS header"}, {meta_RIFF_WAVE, "RIFF WAVE header"}, {meta_RIFF_WAVE_POS, "RIFF WAVE header and .pos for looping"}, - {meta_NWA, "Visual Art's NWA header"}, - {meta_NWA_NWAINFOINI, "Visual Art's NWA header and NWAINFO.INI for looping"}, - {meta_NWA_GAMEEXEINI, "Visual Art's NWA header and Gameexe.ini for looping"}, + {meta_NWA, "VisualArt's NWA header"}, + {meta_NWA_NWAINFOINI, "VisualArt's NWA header (NWAINFO.INI looping)"}, + {meta_NWA_GAMEEXEINI, "VisualArt's NWA header (Gameexe.ini looping)"}, {meta_XSS, "Dino Crisis 3 XSS File"}, {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_SL3, "SL3 Header"}, + {meta_SL3, "Atari Melbourne House SL3 header"}, {meta_FSB1, "FMOD Sample Bank (FSB1) Header"}, {meta_FSB2, "FMOD Sample Bank (FSB2) Header"}, {meta_FSB3, "FMOD Sample Bank (FSB3) Header"}, @@ -694,19 +725,19 @@ static const meta_info meta_info_list[] = { {meta_BG00, "Falcom BG00 Header"}, {meta_PS2_RSTM, "Rockstar Games RSTM Header"}, {meta_ACM, "InterPlay ACM Header"}, - {meta_MUS_ACM, "MUS playlist and multiple InterPlay ACM Headered files"}, + {meta_MUS_ACM, "InterPlay MUS ACM header"}, {meta_PS2_KCES, "Konami KCES Header"}, {meta_PS2_DXH, "Tokobot Plus DXH Header"}, - {meta_PS2_PSH, "Dawn of Mana - Seiken Densetsu 4 PSH Header"}, + {meta_PS2_PSH, "Square Enix PSH/VSV Header"}, {meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"}, {meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"}, + {meta_RIFF_WAVE_wsmp, "RIFF WAVE header with wsmp looping info"}, {meta_RIFX_WAVE, "RIFX WAVE header"}, {meta_RIFX_WAVE_smpl, "RIFX WAVE header with sample looping info"}, {meta_XNB, "Microsoft XNA Game Studio 4.0 header"}, {meta_SCD_PCM, "Lunar: Eternal Blue .PCM header"}, {meta_PS2_PCM, "Konami KCEJ East .PCM header"}, - {meta_PS2_RKV, "Legacy of Kain - Blood Omen 2 RKV Header"}, - {meta_PS2_PSW, "Rayman Raving Rabbids Riff Container File"}, + {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_XBOX_WVS, "Metal Arms WVS Header (XBOX)"}, @@ -724,32 +755,34 @@ static const meta_info meta_info_list[] = { {meta_NGC_YMF, "YMF DSP Header"}, {meta_PS2_CCC, "CCC Header"}, {meta_PSX_FAG, "FAG Header"}, - {meta_PS2_MIHB, "Merged MIH+MIB"}, + {meta_PS2_MIHB, "MIH+MIB header"}, {meta_DSP_WII_MUS, "mus header"}, {meta_WII_SNG, "SNG DSP Header"}, - {meta_RSD2VAG, "RSD2/VAG Header"}, - {meta_RSD2PCMB, "RSD2/PCMB Header"}, - {meta_RSD2XADP, "RSD2/XADP Header"}, - {meta_RSD3VAG, "RSD3/VAG Header"}, - {meta_RSD3GADP, "RSD3/GADP Header"}, - {meta_RSD3PCM, "RSD3/PCM Header"}, - {meta_RSD3PCMB, "RSD3/PCMB Header"}, - {meta_RSD4PCMB, "RSD4/PCMB Header"}, - {meta_RSD4PCM, "RSD4/PCM Header"}, - {meta_RSD4RADP, "RSD4/RADP Header"}, - {meta_RSD4VAG, "RSD4/VAG Header"}, - {meta_RSD6XADP, "RSD6/XADP Header"}, - {meta_RSD6VAG, "RSD6/VAG Header"}, - {meta_RSD6WADP, "RSD6/WADP Header"}, - {meta_RSD6RADP, "RSD6/RADP Header"}, - {meta_RSD6XMA, "RSD6/XMA Header"}, + {meta_RSD2VAG, "Radical RSD2/VAG header"}, + {meta_RSD2PCMB, "Radical RSD2/PCMB header"}, + {meta_RSD2XADP, "Radical RSD2/XADP header"}, + {meta_RSD3VAG, "Radical RSD3/VAG header"}, + {meta_RSD3GADP, "Radical RSD3/GADP header"}, + {meta_RSD3PCM, "Radical RSD3/PCM header"}, + {meta_RSD3PCMB, "Radical RSD3/PCMB header"}, + {meta_RSD4PCMB, "Radical RSD4/PCMB header"}, + {meta_RSD4PCM, "Radical RSD4/PCM header"}, + {meta_RSD4RADP, "Radical RSD4/RADP header"}, + {meta_RSD4VAG, "Radical RSD4/VAG header"}, + {meta_RSD6XADP, "Radical RSD6/XADP header"}, + {meta_RSD6VAG, "Radical RSD6/VAG header"}, + {meta_RSD6WADP, "Radical RSD6/WADP header"}, + {meta_RSD6RADP, "Radical RSD6/RADP header"}, + {meta_RSD6XMA, "Radical RSD6/XMA header"}, + {meta_RSD6AT3P, "Radical RSD6/AT3+ header"}, + {meta_RSD6WMA, "Radical RSD6/WMA header"}, {meta_DC_ASD, "ASD Header"}, - {meta_NAOMI_SPSD, "SPSD Header"}, + {meta_NAOMI_SPSD, "Naomi SPSD header"}, {meta_FFXI_BGW, "BGW BGMStream header"}, {meta_FFXI_SPW, "SPW SeWave header"}, {meta_PS2_ASS, "ASS Header"}, {meta_IDSP, "IDSP Header"}, - {meta_WAA_WAC_WAD_WAM, "WAA/WAC/WAD/WAM RIFF Header"}, + {meta_UBI_JADE, "Ubisoft Jade RIFF header"}, {meta_PS2_SEG, "SEG (PS2) Header"}, {meta_XBOX_SEG, "SEG (XBOX) Header"}, {meta_NDS_STRM_FFTA2, "Final Fantasy Tactics A2 RIFF Header"}, @@ -775,7 +808,6 @@ static const meta_info meta_info_list[] = { {meta_NGC_DSP_IADP, "IADP Header"}, {meta_RSTM_shrunken, "Nintendo RSTM header, corrupted by Atlus"}, {meta_RIFF_WAVE_MWV, "RIFF WAVE header with .mwv flavoring"}, - {meta_RIFF_WAVE_SNS, "RIFF WAVE header with .sns flavoring"}, {meta_FFCC_STR, "Final Fantasy: Crystal Chronicles STR header"}, {meta_SAT_BAKA, "BAKA header from Crypt Killer"}, {meta_NDS_SWAV, "SWAV Header"}, @@ -790,8 +822,8 @@ static const meta_info meta_info_list[] = { {meta_ZSD, "ZSD Header"}, {meta_RedSpark, "RedSpark Header"}, {meta_PC_IVAUD, "assumed GTA IV Audio file by .ivaud extension"}, - {meta_DSP_WII_WSD, "Standard Nintendo DSP headers in .wsd"}, - {meta_WII_NDP, "Vertigo NDP Header"}, + {meta_DSP_WII_WSD, ".WSD header"}, + {meta_WII_NDP, "Icon Games NDP header"}, {meta_PS2_SPS, "Ape Escape 2 SPS Header"}, {meta_PS2_XA2_RRP, "Acclaim XA2 Header"}, {meta_NDS_HWAS, "Vicarious Visions HWAS header"}, @@ -804,13 +836,13 @@ static const meta_info meta_info_list[] = { {meta_NGC_GCUB, "GCub Header"}, {meta_NGC_SCK_DSP, "The Scorpion King SCK Header"}, {meta_NGC_SWD, "PSF + Standard DSP Headers"}, - {meta_CAFF, "Apple Core Audio Format Header"}, + {meta_CAFF, "Apple Core Audio Format File header"}, {meta_PC_MXST, "Lego Island MxSt Header"}, {meta_SAB, "Team17 SAB header"}, {meta_MAXIS_XA, "Maxis XAI/XAJ Header"}, {meta_EXAKT_SC, "assumed Activision / EXAKT SC by extension"}, {meta_WII_BNS, "Nintendo BNS header"}, - {meta_WII_WAS, "WAS (iSWS) DSP header"}, + {meta_WII_WAS, "Sumo Digital iSWS header"}, {meta_XBOX_HLWAV, "Half Life 2 bgm header"}, {meta_STX, "Nintendo .stx header"}, {meta_MYSPD, "U-Sing .MYSPD header"}, @@ -820,7 +852,7 @@ static const meta_info meta_info_list[] = { {meta_DMSG, "RIFF/DMSGsegh header"}, {meta_PONA_3DO, "Policenauts BGM header"}, {meta_PONA_PSX, "Policenauts BGM header"}, - {meta_NGC_DSP_AAAP, "Double standard DSP header in 'AAAp'"}, + {meta_NGC_DSP_AAAP, "Acclaim Austin AAAp header"}, {meta_NGC_DSP_KONAMI, "Konami DSP header"}, {meta_PS2_STER, "STER Header"}, {meta_BNSF, "Namco Bandai BNSF header"}, @@ -831,25 +863,27 @@ static const meta_info meta_info_list[] = { {meta_PS2_SMPL, "Homura SMPL header"}, {meta_PS2_MSA, "Psyvariar -Complete Edition- MSA header"}, {meta_PC_SMP, "Ghostbusters .smp Header"}, - {meta_NGC_PDT, "PDT DSP header"}, - {meta_NGC_BO2, "Blood Omen 2 DSP header"}, + {meta_NGC_PDT, "Hudson .PDT header"}, + {meta_NGC_RKV, "Legacy of Kain - Blood Omen 2 RKV GC header"}, + {meta_DSP_DDSP, ".DDSP header"}, {meta_P3D, "Radical P3D header"}, {meta_PS2_TK1, "Tekken TK5STRM1 Header"}, {meta_PS2_ADSC, "ADSC Header"}, {meta_NGC_DSP_MPDS, "MPDS DSP header"}, - {meta_DSP_STR_IG, "Infogrames dual dsp header"}, - {meta_EA_SWVR, "Electronic Arts SWVR header"}, + {meta_DSP_STR_IG, "Infogrames .DSP header"}, + {meta_EA_SWVR, "Electronic Arts SWVR header"}, {meta_PS2_B1S, "B1S header"}, {meta_PS2_WAD, "WAD header"}, {meta_DSP_XIII, "XIII dsp header"}, {meta_NGC_DSP_STH_STR, "STH dsp header"}, {meta_DSP_CABELAS, "Cabelas games dsp header"}, + {meta_PS2_ADM, "Dragon Quest V .ADM raw header"}, {meta_PS2_LPCM, "LPCM header"}, {meta_PS2_VMS, "VMS Header"}, {meta_XAU, "XPEC XAU header"}, {meta_GH3_BAR, "Guitar Hero III Mobile .bar"}, {meta_FFW, "Freedom Fighters BGM header"}, - {meta_DSP_DSPW, "DSPW dsp header"}, + {meta_DSP_DSPW, "Capcom DSPW header"}, {meta_PS2_JSTM, "JSTM Header"}, {meta_XVAG, "Sony XVAG header"}, {meta_PS3_CPS, "tri-Crescendo CPS Header"}, @@ -865,7 +899,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_SPM, "SPM header"}, {meta_X360_TRA, "Terminal Reality .TRA raw header"}, {meta_PS2_VGS, "Princess Soft VGS header"}, - {meta_PS2_IAB, "IAB header"}, + {meta_PS2_IAB, "Runtime .IAB header"}, {meta_PS2_STRLR, "STR L/R header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, {meta_VAWX, "feelplus VAWX header"}, @@ -895,14 +929,14 @@ static const meta_info meta_info_list[] = { {meta_CSTM, "Nintendo 3DS CSTM Header"}, {meta_FSTM, "Nintendo Wii U FSTM Header"}, {meta_KT_WIIBGM, "Koei Tecmo WiiBGM Header"}, - {meta_KTSS, "Koei Tecmo Switch Sound Header"}, + {meta_KTSS, "Koei Tecmo Nintendo Stream KTSS Header"}, {meta_3DS_IDSP, "Nintendo IDSP Header"}, {meta_WIIU_BTSND, "Nintendo Wii U Menu Boot Sound"}, {meta_MCA, "Capcom MCA header"}, {meta_XB3D_ADX, "Xenoblade 3D ADX header"}, {meta_HCA, "CRI MiddleWare HCA Header"}, {meta_PS2_SVAG_SNK, "SNK SVAG header"}, - {meta_PS2_VDS_VDM, "Graffiti Kingdom VDS/VDM header"}, + {meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"}, {meta_X360_CXS, "tri-Crescendo CXS header"}, {meta_AKB, "Square-Enix AKB header"}, {meta_NUB_XMA, "Namco NUB XMA header"}, @@ -917,12 +951,12 @@ static const meta_info meta_info_list[] = { {meta_GTD, "GTD/GHS header"}, {meta_TA_AAC_X360, "tri-Ace AAC (X360) header"}, {meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"}, - {meta_TA_AAC_VORBIS, "tri-Ace AAC (Mobile Vorbis) header"}, + {meta_TA_AAC_MOBILE, "tri-Ace AAC (Mobile) header"}, {meta_PS3_MTA2, "Konami MTA2 header"}, {meta_NGC_ULW, "Criterion ULW raw header"}, {meta_PC_XA30, "Reflections XA30 PC header"}, {meta_WII_04SW, "Reflections 04SW header"}, - {meta_TXTH, "TXTH Generic Header"}, + {meta_TXTH, "TXTH generic header"}, {meta_EA_BNK, "Electronic Arts BNK header"}, {meta_SK_AUD, "Silicon Knights AUD header"}, {meta_AHX, "CRI AHX header"}, @@ -930,7 +964,7 @@ static const meta_info meta_info_list[] = { {meta_BINK, "RAD Game Tools Bink header"}, {meta_EA_SNU, "Electronic Arts SNU header"}, {meta_AWC, "Rockstar AWC header"}, - {meta_NSW_OPUS, "Nintendo Switch OPUS header"}, + {meta_OPUS, "Nintendo Switch OPUS header"}, {meta_PC_AL2, "Illwinter Game Design AL2 raw header"}, {meta_PC_AST, "Capcom AST (PC) header"}, {meta_UBI_SB, "Ubisoft SBx header"}, @@ -956,10 +990,29 @@ static const meta_info meta_info_list[] = { {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)"}, + {meta_SMV, "Cho Aniki Zero .SMV header"}, + {meta_NXAP, "Nex NXAP header"}, + {meta_EA_WVE_AU00, "Electronic Arts WVE (au00) header"}, + {meta_EA_WVE_AD10, "Electronic Arts WVE (Ad10) header"}, + {meta_STHD, "Dream Factory STHD header"}, + {meta_MP4, "MP4/AAC header"}, + {meta_PCM_SRE, "Capcom .PCM+SRE header"}, + {meta_DSP_MCADPCM, "Bethesda .mcadpcm header"}, + {meta_UBI_LYN, "Ubisoft LyN RIFF header"}, + {meta_MSB_MSH, "Sony MSB+MSH 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"}, -#ifdef VGM_USE_MP4V2 - {meta_MP4, "AAC header"}, -#endif #ifdef VGM_USE_FFMPEG {meta_FFmpeg, "FFmpeg supported file format"}, #endif diff --git a/Frameworks/vgmstream/vgmstream/src/layout/aax_layout.c b/Frameworks/vgmstream/vgmstream/src/layout/aax_layout.c deleted file mode 100644 index 2e0834ae9..000000000 --- a/Frameworks/vgmstream/vgmstream/src/layout/aax_layout.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" -#include "../coding/coding.h" - -void render_vgmstream_aax(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; - aax_codec_data *data = vgmstream->codec_data; - - while (samples_writtensample_counts[data->current_segment]; - - if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - int i; - data->current_segment = data->loop_segment; - - reset_vgmstream(data->adxs[data->current_segment]); - - /* carry over the history from the loop point */ - if (data->loop_segment > 0) - { - for (i=0;iadxs[0]->channels;i++) - { - data->adxs[data->loop_segment]->ch[i].adpcm_history1_32 = - data->adxs[data->loop_segment-1]->ch[i].adpcm_history1_32; - data->adxs[data->loop_segment]->ch[i].adpcm_history2_32 = - data->adxs[data->loop_segment-1]->ch[i].adpcm_history2_32; - } - } - vgmstream->samples_into_block = 0; - continue; - } - - samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); - - /*printf("samples_to_do=%d,samples_this_block=%d,samples_written=%d,sample_count=%d\n",samples_to_do,samples_this_block,samples_written,sample_count);*/ - - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; - - if (samples_to_do == 0) - { - int i; - data->current_segment++; - /*printf("advance to %d at %d samples\n",data->current_segment,vgmstream->current_sample);*/ - reset_vgmstream(data->adxs[data->current_segment]); - - /* carry over the history from the previous segment */ - for (i=0;iadxs[0]->channels;i++) - { - data->adxs[data->current_segment]->ch[i].adpcm_history1_32 = - data->adxs[data->current_segment-1]->ch[i].adpcm_history1_32; - data->adxs[data->current_segment]->ch[i].adpcm_history2_32 = - data->adxs[data->current_segment-1]->ch[i].adpcm_history2_32; - } - vgmstream->samples_into_block = 0; - continue; - } - - render_vgmstream(&buffer[samples_written*data->adxs[data->current_segment]->channels], - samples_to_do,data->adxs[data->current_segment]); - - samples_written += samples_to_do; - vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; - } -} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index b6b1530f4..d59417d84 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -1,6 +1,8 @@ #include "layout.h" #include "../vgmstream.h" +static void block_update(VGMSTREAM * vgmstream); + void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { int samples_written=0; @@ -34,12 +36,13 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * } /* probably block bug or EOF, next calcs would give wrong values and buffer segfaults */ - if (samples_this_block <= 0) { - VGM_LOG("layout_blocked: empty/wrong block\n"); + if (samples_this_block < 0) { + VGM_LOG("layout_blocked: wrong block at 0x%lx\n", vgmstream->current_block_offset); memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); - break; + break; /* probable infinite loop otherwise */ } + /* samples_this_block = 0 is allowed (empty block), will do nothing then move to next block */ samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); if (samples_written + samples_to_do > sample_count) @@ -49,7 +52,7 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); } else { - /* block end signal (used below): partially 0-set buffer */ + /* block end signal (used in halpst): partially 0-set buffer */ int i; for (i = samples_written*vgmstream->channels; i < (samples_written+samples_to_do)*vgmstream->channels; i++) { buffer[i]=0; @@ -64,115 +67,10 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * /* move to next block when all samples are consumed */ if (vgmstream->samples_into_block==samples_this_block /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ - switch (vgmstream->layout_type) { - case layout_ast_blocked: - ast_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_mxch_blocked: - mxch_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_halpst_blocked: - if (vgmstream->next_block_offset>=0) - halpst_block_update(vgmstream->next_block_offset,vgmstream); - else - vgmstream->current_block_offset = -1; - break; - case layout_xa_blocked: - xa_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_ea_schl: - block_update_ea_schl(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_ea_1snh: - block_update_ea_1snh(vgmstream->next_block_offset,vgmstream); - break; - case layout_caf_blocked: - caf_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_wsi_blocked: - wsi_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_str_snds_blocked: - str_snds_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_ws_aud_blocked: - ws_aud_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_matx_blocked: - matx_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_dec: - block_update_dec(vgmstream->next_block_offset,vgmstream); - break; - case layout_emff_ps2_blocked: - emff_ps2_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_emff_ngc_blocked: - emff_ngc_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_gsb_blocked: - gsb_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_vs_blocked: - vs_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_xvas_blocked: - xvas_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_thp_blocked: - thp_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_filp_blocked: - filp_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_ivaud: - block_update_ivaud(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_ea_swvr: - block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); - break; - case layout_ps2_adm_blocked: - ps2_adm_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_dsp_bdsp_blocked: - dsp_bdsp_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_tra_blocked: - tra_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_ps2_iab_blocked: - ps2_iab_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_ps2_strlr_blocked: - ps2_strlr_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_rws_blocked: - rws_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_hwas_blocked: - hwas_block_update(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_ea_sns: - block_update_ea_sns(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_awc: - block_update_awc(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_vgs: - block_update_vgs(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_vawx: - block_update_vawx(vgmstream->next_block_offset,vgmstream); - break; - case layout_blocked_xvag_subsong: - block_update_xvag_subsong(vgmstream->next_block_offset,vgmstream); - break; - default: - break; - } + block_update(vgmstream); /* for VBR these may change */ - frame_size = get_vgmstream_frame_size(vgmstream); /* for VBR these may change */ + frame_size = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); /* get samples in the current block */ @@ -189,3 +87,122 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * } } + + +static void block_update(VGMSTREAM * vgmstream) { + switch (vgmstream->layout_type) { + case layout_blocked_ast: + block_update_ast(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_mxch: + block_update_mxch(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_halpst: + if (vgmstream->next_block_offset>=0) + block_update_halpst(vgmstream->next_block_offset,vgmstream); + else + vgmstream->current_block_offset = -1; + break; + case layout_blocked_xa: + block_update_xa(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ea_schl: + block_update_ea_schl(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ea_1snh: + block_update_ea_1snh(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_caf: + block_update_caf(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_wsi: + block_update_wsi(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_str_snds: + block_update_str_snds(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ws_aud: + block_update_ws_aud(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_matx: + block_update_matx(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_dec: + block_update_dec(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_emff_ps2: + block_update_emff_ps2(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_emff_ngc: + block_update_emff_ngc(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_gsb: + block_update_gsb(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_vs: + block_update_vs(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_xvas: + block_update_xvas(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_thp: + block_update_thp(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_filp: + block_update_filp(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ivaud: + block_update_ivaud(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ea_swvr: + block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_adm: + block_update_adm(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_bdsp: + block_update_bdsp(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_tra: + block_update_tra(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ps2_iab: + block_update_ps2_iab(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ps2_strlr: + block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_rws: + block_update_rws(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_hwas: + block_update_hwas(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ea_sns: + block_update_ea_sns(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_awc: + block_update_awc(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_vgs: + block_update_vgs(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_vawx: + block_update_vawx(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_xvag_subsong: + block_update_xvag_subsong(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ea_wve_au00: + block_update_ea_wve_au00(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ea_wve_ad10: + block_update_ea_wve_ad10(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_sthd: + block_update_sthd(vgmstream->next_block_offset,vgmstream); + break; + default: + break; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_adm.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_adm.c new file mode 100644 index 000000000..31d0fcc45 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_adm.c @@ -0,0 +1,47 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* blocks of 0x1000 with interleave 0x400 but also smaller last interleave */ +void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i, new_full_block; + size_t block_size, interleave_size, interleave_data; + + /* no header */ + interleave_size = 0x400; + interleave_data = 0x400; + block_size = interleave_size * vgmstream->channels; + + /* every 0x1000 is a full block, signaled by PS-ADPCM flags */ + new_full_block = (read_8bit(block_offset+0x01, streamFile) == 0x06); + + /* try to autodetect usable interleave data size as can be smaller when a discrete block ends (ex. 0x10~0x50, varies with file) */ + if (!new_full_block) { + off_t next_block_offset = block_offset + block_size; + + while (next_block_offset > block_offset) { + next_block_offset -= 0x10; + + /* check if unused line (all blocks should only use flags 0x06/0x03/0x02) */ + if (read_32bitLE(next_block_offset, streamFile) == 0x00000000) { + interleave_data -= 0x10; + } + else { + break; + } + } + } + + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; + vgmstream->current_block_size = interleave_data; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + interleave_size*i; + + //if (new_full_block) { /* blocks are not discrete */ + // vgmstream->ch[i].adpcm_history1_32 = 0; + // vgmstream->ch[i].adpcm_step_index = 0; + //} + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ast_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c similarity index 90% rename from Frameworks/vgmstream/vgmstream/src/layout/ast_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c index d882a0de1..8ff32ba08 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/ast_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void ast_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_ast(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; vgmstream->current_block_size = read_32bitBE( diff --git a/Frameworks/vgmstream/vgmstream/src/layout/bdsp_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_bdsp.c similarity index 88% rename from Frameworks/vgmstream/vgmstream/src/layout/bdsp_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_bdsp.c index 88f640b66..f5e763abd 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/bdsp_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_bdsp.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void dsp_bdsp_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_bdsp(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_caf.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_caf.c new file mode 100644 index 000000000..988e77147 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_caf.c @@ -0,0 +1,21 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* each block is a new CAF header */ +void block_update_caf(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i,ch; + + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + read_32bitBE(block_offset+0x04, streamFile); + vgmstream->current_block_size = read_32bitBE(block_offset+0x14, streamFile); + + for (ch = 0; ch < vgmstream->channels; ch++) { + vgmstream->ch[ch].offset = block_offset + read_32bitBE(block_offset+0x10+(0x08*ch), streamFile); + + /* re-read coeffs (though blocks seem to repeat them) */ + for (i = 0; i < 16; i++) { + vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(block_offset+0x34 + 0x2c*ch + 0x02*i, streamFile); + } + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index eea96ffd5..169bbaec1 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -21,7 +21,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { block_samples = 0; - if (id == 0x5343446C || id == 0x5344454E) { /* "SCDl" "SDEN" audio data */ + if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl" "SDEN" "SDFR" audio data */ switch(vgmstream->coding_type) { case coding_PSX: block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels); @@ -37,7 +37,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { block_size = 0x04; } - if (id == 0x5343486C || id == 0x5348454E) { /* "SCHl" "SHEN" end block */ + if (id == 0x5343486C || id == 0x5348454E || id == 0x53484652) { /* "SCHl" "SHEN" "SHFR" end block */ new_schl = 1; } } @@ -53,8 +53,8 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { break; block_offset += block_size; - /* "SCEl" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ - if ((id == 0x5343456C || id == 0x5345454E) && block_offset % 0x04) { + /* "SCEl" "SEEN" "SEFR" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ + if ((id == 0x5343456C || id == 0x5345454E || id == 0x53454652) && block_offset % 0x04) { block_offset += 0x04 - (block_offset % 0x04); } } @@ -93,6 +93,14 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { break; + /* id, size, samples */ + case coding_PCM16_int: + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x02); + } + + break; + /* id, size, samples, hists-per-channel, stereo/interleaved data */ case coding_EA_XA: //case coding_EA_XA_V2: /* handled in default */ @@ -126,14 +134,22 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { break; #ifdef VGM_USE_MPEG - /* id, size, samples, offset?, unknown (null for MP2, some constant for all blocks for EALayer3) */ + /* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */ case coding_MPEG_custom: case coding_MPEG_layer1: case coding_MPEG_layer2: case coding_MPEG_layer3: case coding_MPEG_ealayer3: for (i = 0; i < vgmstream->channels; i++) { - off_t channel_start = read_32bit(block_offset + 0x0C,streamFile); + off_t channel_start; + + /* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */ + if (vgmstream->channels > 2) { + channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile); + } else { + channel_start = read_32bit(block_offset + 0x0C,streamFile); + } + vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_swvr.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_swvr.c index d4206b5c5..080443ad2 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_swvr.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_swvr.c @@ -1,18 +1,97 @@ #include "layout.h" -#include "../vgmstream.h" +#include "../coding/coding.h" -/* set up for the block at the given offset */ +/* EA-style blocks */ void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; + size_t block_size, header_size = 0, channel_size = 0, interleave = 0; + uint32_t block_id; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE; + block_id = read_32bit(block_offset+0x00, streamFile); + block_size = read_32bit(block_offset+0x04, streamFile); + + /* parse blocks (Freekstyle uses multiblocks) */ + switch(block_id) { + case 0x5641474D: /* "VAGM" */ + if (read_16bit(block_offset+0x1a, streamFile) == 0x0024) { + header_size = 0x40; + channel_size = (block_size - header_size) / vgmstream->channels; + + /* ignore blocks of other subsongs */ + { + int target_subsong = vgmstream->stream_index ? vgmstream->stream_index : 1; + if (read_32bit(block_offset+0x0c, streamFile)+1 != target_subsong) { + channel_size = 0; + } + } + } else { + header_size = 0x1c; + channel_size = (block_size - header_size) / vgmstream->channels; + } + break; + case 0x56414742: /* "VAGB" */ + if (read_16bit(block_offset+0x1a, streamFile) == 0x6400) { + header_size = 0x40; + } else { + header_size = 0x18; + } + channel_size = (block_size - header_size) / vgmstream->channels; + break; + + case 0x4453504D: /* "DSPM" */ + header_size = 0x60; + channel_size = (block_size - header_size) / vgmstream->channels; + + /* ignore blocks of other subsongs */ + { + int target_subsong = vgmstream->stream_index ? vgmstream->stream_index : 1; + if (read_32bit(block_offset+0x0c, streamFile)+1 != target_subsong) { + channel_size = 0; + } + } + dsp_read_coefs_be(vgmstream, streamFile, block_offset+0x1a, 0x22); + //todo adpcm history? + break; + case 0x44535042: /* "DSPB" */ + header_size = 0x40; + channel_size = (block_size - header_size) / vgmstream->channels; + dsp_read_coefs_be(vgmstream, streamFile, block_offset+0x18, 0x00); + //todo adpcm history? + break; + + case 0x4D534943: /* "MSIC" */ + header_size = 0x1c; + channel_size = (block_size - header_size) / vgmstream->channels; + break; + case 0x53484F43: /* "SHOC" (a generic block but hopefully has PC sounds) */ + header_size = 0x14; //todo the first block is 0x18 + channel_size = (block_size - header_size) / vgmstream->channels; + break; + + case 0x46494C4C: /* "FILL" (FILLs do that up to 0x6000, but at 0x5FFC don't actually have size) */ + if ((block_offset + 0x04) % 0x6000 == 0) + block_size = 0x04; + header_size = 0x08; + break; + + case 0xFFFFFFFF: + channel_size = -1; /* signal bad block */ + break; + + default: /* ignore, 0 samples */ + VGM_LOG("EA SWVR: ignored 0x%08x at 0x%lx\n", block_id, block_offset); + break; + } + + vgmstream->current_block_size = channel_size; vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = read_32bit(vgmstream->current_block_offset+0x04,streamFile)-0x1C; - vgmstream->next_block_offset = vgmstream->current_block_offset+vgmstream->current_block_size+0x1C; - vgmstream->current_block_size/=vgmstream->channels; + vgmstream->next_block_offset = block_offset + block_size; - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset+0x1C+(vgmstream->current_block_size*i); + interleave = vgmstream->coding_type == coding_PCM8_U_int ? 0x1 : channel_size; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + header_size + interleave*i; } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_wve_ad10.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_wve_ad10.c new file mode 100644 index 000000000..705fd49f3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_wve_ad10.c @@ -0,0 +1,30 @@ +#include "layout.h" +#include "../coding/coding.h" + + +/* EA style blocks, one block per channel when stereo */ +void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i; + size_t block_size, channel_size = 0, interleave = 0; + uint32_t block_id; + + block_id = read_32bitBE(block_offset+0x00, streamFile); + block_size = read_32bitBE(block_offset+0x04, streamFile); + + /* accept "Ad10/Ad11" audio block/footer */ + if (block_id == 0x41643130 || block_id == 0x41643131) { + channel_size = block_size - 0x08; /* one block per channel */ + interleave = block_size; + block_size = block_size*vgmstream->channels; + } + /* rest could be "MDEC" video blocks with 0 size/samples */ + + vgmstream->current_block_size = channel_size; + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = (block_offset + 0x08) + interleave*i; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_wve_au00.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_wve_au00.c new file mode 100644 index 000000000..038cf5cc8 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_wve_au00.c @@ -0,0 +1,29 @@ +#include "layout.h" +#include "../coding/coding.h" + + +/* EA style blocks */ +void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i; + size_t block_size, channel_size = 0; + uint32_t block_id; + + block_id = read_32bitBE(block_offset+0x00, streamFile); + block_size = read_32bitBE(block_offset+0x04, streamFile); + + /* accept "au00/au01" audio block/footer */ + if (block_id == 0x61753030 || block_id == 0x61753031) { + /* adjusted to frame boundaries as blocks have padding */ + channel_size = ((block_size - 0x10) / vgmstream->interleave_block_size * vgmstream->interleave_block_size) / vgmstream->channels; + } + /* rest could be "MDEC" video blocks with 0 size/samples */ + + vgmstream->current_block_size = channel_size; + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = (block_offset + 0x10) + channel_size*i; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/emff_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_emff.c similarity index 89% rename from Frameworks/vgmstream/vgmstream/src/layout/emff_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_emff.c index b3ed7f5f2..41ad197f0 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/emff_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_emff.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void emff_ps2_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; @@ -17,7 +17,7 @@ void emff_ps2_block_update(off_t block_offset, VGMSTREAM * vgmstream) { } } -void emff_ngc_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/filp_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_filp.c similarity index 90% rename from Frameworks/vgmstream/vgmstream/src/layout/filp_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_filp.c index 550c6e738..6baea3627 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/filp_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_filp.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void filp_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_filp(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/gsb_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_gsb.c similarity index 93% rename from Frameworks/vgmstream/vgmstream/src/layout/gsb_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_gsb.c index eccd40d8d..29a417711 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/gsb_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_gsb.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void gsb_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream) { int i; int block_header_size = 0x20; /*from header*/ int block_channel_size = 0x8000; /*from header, per channel*/ diff --git a/Frameworks/vgmstream/vgmstream/src/layout/halpst_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_halpst.c similarity index 92% rename from Frameworks/vgmstream/vgmstream/src/layout/halpst_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_halpst.c index 7c36c1bf8..d9ec3763e 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/halpst_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_halpst.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void halpst_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_halpst(off_t block_offset, VGMSTREAM * vgmstream) { int i, header_length; /* header length must be a multiple of 0x20 */ header_length = (4+8*vgmstream->channels+0x1f)/0x20*0x20; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/hwas_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_hwas.c similarity index 90% rename from Frameworks/vgmstream/vgmstream/src/layout/hwas_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_hwas.c index f55a40fc5..dbd4fd700 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/hwas_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_hwas.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* a simple headerless block with special adpcm history handling */ -void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream) { int i; size_t block_size; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ims_block.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_matx.c similarity index 90% rename from Frameworks/vgmstream/vgmstream/src/layout/ims_block.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_matx.c index c5df59480..e69a05038 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/ims_block.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_matx.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void matx_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/mxch_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_mxch.c similarity index 91% rename from Frameworks/vgmstream/vgmstream/src/layout/mxch_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_mxch.c index 8e49a4a95..f15afbcbb 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/mxch_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_mxch.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" //MxCh blocked layout as used by Lego Island -void mxch_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_mxch(off_t block_offset, VGMSTREAM * vgmstream) { vgmstream->current_block_offset = block_offset; vgmstream->next_block_offset = block_offset + read_32bitLE(vgmstream->current_block_offset+4,vgmstream->ch[0].streamfile)+8; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ps2_iab.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ps2_iab.c new file mode 100644 index 000000000..5ec5a0613 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ps2_iab.c @@ -0,0 +1,22 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* blocks with mini header (0x48124812 + unknown + block data + block size) */ +void block_update_ps2_iab(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i; + size_t block_size, channel_size; + + channel_size = read_32bitLE(block_offset+0x08,streamFile) / vgmstream->channels; + block_size = read_32bitLE(block_offset+0x0c,streamFile); + if (!block_size) + block_size = 0x10; /* happens on last block */ + + vgmstream->current_block_size = channel_size; + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x10 + channel_size*i; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ps2_strlr_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ps2_strlr.c similarity index 89% rename from Frameworks/vgmstream/vgmstream/src/layout/ps2_strlr_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_ps2_strlr.c index 7bf23bda5..d2834db50 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/ps2_strlr_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ps2_strlr.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/rws_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_rws.c similarity index 87% rename from Frameworks/vgmstream/vgmstream/src/layout/rws_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_rws.c index 23b90c535..7d3fee2a5 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/rws_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_rws.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* a simple headerless block with padding; configured in the main header */ -void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream) { int i; size_t block_size; size_t interleave; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_sthd.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_sthd.c new file mode 100644 index 000000000..2d9d9fbcf --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_sthd.c @@ -0,0 +1,22 @@ +#include "layout.h" + +/* Dream Factory STHD blocks */ +void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + size_t block_size, channel_size; + off_t data_offset; + int i; + + block_size = 0x800; + data_offset = read_16bitLE(block_offset + 0x04, streamFile); + channel_size = read_16bitLE(block_offset + 0x16, streamFile); + /* 0x06: num channels, 0x10: total blocks, 0x12: block count, 0x14(2): null, 0x18: block count + 1 */ + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = channel_size; + vgmstream->next_block_offset = block_offset + block_size; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + data_offset + channel_size*i; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/str_snds_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_str_snds.c similarity index 96% rename from Frameworks/vgmstream/vgmstream/src/layout/str_snds_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_str_snds.c index 91b64d60e..7a7fca9ac 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/str_snds_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_str_snds.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void str_snds_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_str_snds(off_t block_offset, VGMSTREAM * vgmstream) { off_t current_chunk; size_t file_size; int i; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/thp_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_thp.c similarity index 94% rename from Frameworks/vgmstream/vgmstream/src/layout/thp_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_thp.c index 828d6c8de..abea3e2c7 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/thp_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_thp.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void thp_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream) { int i,j; STREAMFILE *streamFile=vgmstream->ch[0].streamfile; off_t start_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/tra_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_tra.c similarity index 89% rename from Frameworks/vgmstream/vgmstream/src/layout/tra_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_tra.c index cc235f723..db29fe8ed 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/tra_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_tra.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset (first 32bytes is useless for decoding) */ -void tra_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_tra(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/vs_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_vs.c similarity index 89% rename from Frameworks/vgmstream/vgmstream/src/layout/vs_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_vs.c index 76e4cb277..4284642d5 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/vs_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_vs.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void vs_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream) { int i; for (i=0;ichannels;i++) { diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ws_aud_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ws_aud.c similarity index 92% rename from Frameworks/vgmstream/vgmstream/src/layout/ws_aud_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_ws_aud.c index dfc824a57..6912060e2 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/ws_aud_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ws_aud.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void ws_aud_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_ws_aud(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; vgmstream->current_block_size = read_16bitLE( diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_wsi.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_wsi.c new file mode 100644 index 000000000..572c6cc12 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_wsi.c @@ -0,0 +1,21 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* .wsi - headered blocks with a single channel */ +void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i; + off_t channel_block_size; + + + /* assume that all channels have the same size for this block */ + channel_block_size = read_32bitBE(block_offset, streamFile); + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = channel_block_size - 0x10; /* remove header */ + vgmstream->next_block_offset = block_offset + channel_block_size*vgmstream->channels; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + channel_block_size*i + 0x10; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/xa_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c similarity index 97% rename from Frameworks/vgmstream/vgmstream/src/layout/xa_blocked.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c index bd208fd28..e7829b68a 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/xa_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c @@ -3,7 +3,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) { int i; int8_t currentChannel=0; int8_t subAudio=0; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/xvas_block.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xvas.c similarity index 91% rename from Frameworks/vgmstream/vgmstream/src/layout/xvas_block.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_xvas.c index ee78c3ad1..a26ed0da3 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/xvas_block.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xvas.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void xvas_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/caf_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/caf_blocked.c deleted file mode 100644 index d944baa01..000000000 --- a/Frameworks/vgmstream/vgmstream/src/layout/caf_blocked.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -/* set up for the block at the given offset */ -void caf_block_update(off_t block_offset, VGMSTREAM * vgmstream) { - int i; - - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = read_32bitBE( - vgmstream->current_block_offset+0x14, - vgmstream->ch[0].streamfile); - vgmstream->next_block_offset = vgmstream->current_block_offset + - (off_t)read_32bitBE(vgmstream->current_block_offset+0x04, - vgmstream->ch[0].streamfile); - - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset + - read_32bitBE(block_offset+0x10+(8*i),vgmstream->ch[0].streamfile); - } - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(block_offset+0x34+(2*i),vgmstream->ch[0].streamfile); - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(block_offset+0x60+(2*i),vgmstream->ch[0].streamfile); - } -} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c index f10ee574e..6090d197d 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c @@ -10,12 +10,12 @@ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREA samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; - if (vgmstream->layout_type == layout_interleave_shortblock && + if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block> vgmstream->num_samples) { frame_size = get_vgmstream_shortframe_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_smallblock_size / frame_size * samples_per_frame; + samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; } while (samples_writtenloop_flag && vgmstream_do_loop(vgmstream)) { /* we assume that the loop is not back into a short block */ - if (vgmstream->layout_type == layout_interleave_shortblock) { + if (vgmstream->interleave_last_block_size && vgmstream->channels > 1) { frame_size = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; @@ -45,14 +45,14 @@ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREA if (vgmstream->samples_into_block==samples_this_block) { int chan; - if (vgmstream->layout_type == layout_interleave_shortblock && + if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { frame_size = get_vgmstream_shortframe_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_smallblock_size / frame_size * samples_per_frame; + samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; for (chan=0;chanchannels;chan++) - vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*(vgmstream->channels-chan)+vgmstream->interleave_smallblock_size*chan; + vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*(vgmstream->channels-chan)+vgmstream->interleave_last_block_size*chan; } else { for (chan=0;chanchannels;chan++) diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layered.c b/Frameworks/vgmstream/vgmstream/src/layout/layered.c new file mode 100644 index 000000000..bfaaec4b8 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/layered.c @@ -0,0 +1,129 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* TODO: there must be a reasonable way to respect the loop settings, as + the substreams are in their own little world. + Currently the VGMSTREAMs layers loop internally and the external/base VGMSTREAM + doesn't actually loop, and would ignore any altered values/loop_flag. */ + +#define LAYER_BUF_SIZE 512 +#define LAYER_MAX_CHANNELS 6 /* at least 2, but let's be generous */ + + +void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { + sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS]; + int32_t samples_done = 0; + layered_layout_data *data = vgmstream->layout_data; + + while (samples_done < sample_count) { + int32_t samples_to_do = LAYER_BUF_SIZE; + int layer; + + if (samples_to_do > sample_count - samples_done) + samples_to_do = sample_count - samples_done; + + for (layer = 0; layer < data->layer_count; layer++) { + int s,l_ch; + int layer_channels = data->layers[layer]->channels; + + render_vgmstream(interleave_buf, samples_to_do, data->layers[layer]); + + for (l_ch = 0; l_ch < layer_channels; l_ch++) { + for (s = 0; s < samples_to_do; s++) { + size_t layer_sample = s*layer_channels + l_ch; + size_t buffer_sample = (samples_done+s)*vgmstream->channels + (layer*layer_channels+l_ch); + + buffer[buffer_sample] = interleave_buf[layer_sample]; + } + } + + } + + samples_done += samples_to_do; + } +} + + +layered_layout_data* init_layout_layered(int layer_count) { + layered_layout_data *data = NULL; + + if (layer_count <= 0 || layer_count > 255) + goto fail; + + data = calloc(1, sizeof(layered_layout_data)); + if (!data) goto fail; + + data->layer_count = layer_count; + + data->layers = calloc(layer_count, sizeof(VGMSTREAM*)); + if (!data->layers) goto fail; + + return data; +fail: + free_layout_layered(data); + return NULL; +} + +int setup_layout_layered(layered_layout_data* data) { + int i; + + /* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */ + for (i = 0; i < data->layer_count; i++) { + if (!data->layers[i]) + goto fail; + + if (data->layers[i]->num_samples <= 0) + goto fail; + + if (data->layers[i]->channels > LAYER_MAX_CHANNELS) + goto fail; + + if (i > 0) { + /* a bit weird, but no matter */ + if (data->layers[i]->sample_rate != data->layers[i-1]->sample_rate) { + VGM_LOG("layered layout: layer %i has different sample rate\n", i); + } + + /* also weird */ + if (data->layers[i]->coding_type != data->layers[i-1]->coding_type) { + VGM_LOG("layered layout: layer %i has different coding type\n", i); + } + } + + //todo could check if layers'd loop match vs main, etc + + /* save start things so we can restart for seeking/looping */ + memcpy(data->layers[i]->start_ch,data->layers[i]->ch,sizeof(VGMSTREAMCHANNEL)*data->layers[i]->channels); + memcpy(data->layers[i]->start_vgmstream,data->layers[i],sizeof(VGMSTREAM)); + } + + return 1; +fail: + return 0; /* caller is expected to free */ +} + +void free_layout_layered(layered_layout_data *data) { + int i; + + if (!data) + return; + + if (data->layers) { + for (i = 0; i < data->layer_count; i++) { + close_vgmstream(data->layers[i]); + } + free(data->layers); + } + free(data); +} + +void reset_layout_layered(layered_layout_data *data) { + int i; + + if (!data) + return; + + for (i = 0; i < data->layer_count; i++) { + reset_vgmstream(data->layers[i]); + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 466281eff..a8fc78343 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -7,78 +7,60 @@ /* blocked layouts */ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); -void ast_block_update(off_t block_ofset, VGMSTREAM * vgmstream); - -void mxch_block_update(off_t block_ofset, VGMSTREAM * vgmstream); - -void halpst_block_update(off_t block_ofset, VGMSTREAM * vgmstream); - -void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream); - +void block_update_ast(off_t block_ofset, VGMSTREAM * vgmstream); +void block_update_mxch(off_t block_ofset, VGMSTREAM * vgmstream); +void block_update_halpst(off_t block_ofset, VGMSTREAM * vgmstream); +void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream); - -void caf_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void wsi_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void str_snds_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void ws_aud_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void matx_block_update(off_t block_offset, VGMSTREAM * vgmstream); - +void block_update_caf(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_str_snds(off_t block_offset, VGMSTREAM * vgmstream); +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 vs_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void emff_ps2_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void emff_ngc_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void gsb_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void xvas_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void thp_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void filp_block_update(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_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); +void block_update_filp(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream); - void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream); - -void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void dsp_bdsp_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void tra_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void ps2_iab_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream); - +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_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); void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vawx(off_t block_offset, VGMSTREAM * vgmstream); void block_update_xvag_subsong(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream); /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); -void render_vgmstream_mus_acm(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); - void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); -void render_vgmstream_aax(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +segmented_layout_data* init_layout_segmented(int segment_count); +int setup_layout_segmented(segmented_layout_data* data); +void free_layout_segmented(segmented_layout_data *data); +void reset_layout_segmented(segmented_layout_data *data); -void render_vgmstream_scd_int(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +layered_layout_data* init_layout_layered(int layer_count); +int setup_layout_layered(layered_layout_data* data); +void free_layout_layered(layered_layout_data *data); +void reset_layout_layered(layered_layout_data *data); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/layout/mus_acm_layout.c b/Frameworks/vgmstream/vgmstream/src/layout/mus_acm_layout.c deleted file mode 100644 index bda82b501..000000000 --- a/Frameworks/vgmstream/vgmstream/src/layout/mus_acm_layout.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" -#include "../coding/acm_decoder.h" -#include "../coding/coding.h" - -void render_vgmstream_mus_acm(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; - mus_acm_codec_data *data = vgmstream->codec_data; - - while (samples_writtenfiles[data->current_file]; - int samples_to_do; - int samples_this_block = acm->total_values / acm->info.channels; - - if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - data->current_file = data->loop_start_file; - acm_reset(data->files[data->current_file]); - vgmstream->samples_into_block = 0; - continue; - } - - samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); - - /*printf("samples_to_do=%d,samples_this_block=%d,samples_written=%d,sample_count=%d\n",samples_to_do,samples_this_block,samples_written,sample_count);*/ - - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; - - if (samples_to_do == 0) - { - data->current_file++; - /*printf("next %d, %d samples\n",data->current_file,data->files[data->current_file]->total_values/data->files[data->current_file]->info.channels);*/ - /* force loop back to first file in case we're still playing for some - * reason, prevent out of bounds stuff */ - if (data->current_file >= data->file_count) data->current_file = 0; - acm_reset(data->files[data->current_file]); - vgmstream->samples_into_block = 0; - continue; - } - - /*printf("decode %d samples file %d\n",samples_to_do,data->current_file);*/ - decode_acm(acm, - buffer+samples_written*vgmstream->channels, - samples_to_do, vgmstream->channels); - - samples_written += samples_to_do; - vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; - } -} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ps2_adm_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/ps2_adm_blocked.c deleted file mode 100644 index 87c7c606e..000000000 --- a/Frameworks/vgmstream/vgmstream/src/layout/ps2_adm_blocked.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -/* set up for the block at the given offset */ -void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream) { - int i; - - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = 0x1000; /*read_32bitLE( - vgmstream->current_block_offset+0x10, - vgmstream->ch[0].streamfile); */ - vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size; - //vgmstream->current_block_size/=vgmstream->channels; - - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset+(0x400*i); - } -} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ps2_iab_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/ps2_iab_blocked.c deleted file mode 100644 index 0b375b1ea..000000000 --- a/Frameworks/vgmstream/vgmstream/src/layout/ps2_iab_blocked.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -/* set up for the block at the given offset */ -void ps2_iab_block_update(off_t block_offset, VGMSTREAM * vgmstream) { - int i; - - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = read_32bitLE(vgmstream->current_block_offset+0x08,vgmstream->ch[0].streamfile); - vgmstream->next_block_offset = vgmstream->current_block_offset+vgmstream->current_block_size+0x10; - vgmstream->current_block_size/=vgmstream->channels; - - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset+0x10+(vgmstream->current_block_size*i); - - } -} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/scd_int_layout.c b/Frameworks/vgmstream/vgmstream/src/layout/scd_int_layout.c deleted file mode 100644 index 1a2c5e8a9..000000000 --- a/Frameworks/vgmstream/vgmstream/src/layout/scd_int_layout.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -/* TODO: currently only properly handles mono substreams */ -/* TODO: there must be a reasonable way to respect the loop settings, as is - the substreams are in their own little world */ - -#define INTERLEAVE_BUF_SIZE 512 -void render_vgmstream_scd_int(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - sample interleave_buf[INTERLEAVE_BUF_SIZE]; - int32_t samples_done = 0; - scd_int_codec_data *data = vgmstream->codec_data; - - while (samples_done < sample_count) - { - int32_t samples_to_do = INTERLEAVE_BUF_SIZE; - int c; - if (samples_to_do > sample_count - samples_done) - samples_to_do = sample_count - samples_done; - - for (c=0; c < data->substream_count; c++) - { - int32_t i; - - render_vgmstream(interleave_buf, - samples_to_do, data->substreams[c]); - - for (i=0; i < samples_to_do; i++) - { - buffer[(samples_done+i)*data->substream_count + c] = interleave_buf[i]; - } - } - - samples_done += samples_to_do; - - } -} - diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c new file mode 100644 index 000000000..4f2b70737 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -0,0 +1,136 @@ +#include "layout.h" +#include "../vgmstream.h" + + +void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { + int samples_written=0; + segmented_layout_data *data = vgmstream->layout_data; + //int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + + while (samples_writtensegments[data->current_segment]->num_samples; + + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { + //todo can only loop in a segment start + // (for arbitrary values find loop segment from loop_start_sample, and skip N samples until loop start) + data->current_segment = data->loop_segment; + + reset_vgmstream(data->segments[data->current_segment]); + + vgmstream->samples_into_block = 0; + continue; + } + + samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); + + if (samples_written+samples_to_do > sample_count) + samples_to_do=sample_count-samples_written; + + if (samples_to_do == 0) { + data->current_segment++; + reset_vgmstream(data->segments[data->current_segment]); + + vgmstream->samples_into_block = 0; + continue; + } + + render_vgmstream(&buffer[samples_written*data->segments[data->current_segment]->channels], + samples_to_do,data->segments[data->current_segment]); + + samples_written += samples_to_do; + vgmstream->current_sample += samples_to_do; + vgmstream->samples_into_block+=samples_to_do; + } +} + + +segmented_layout_data* init_layout_segmented(int segment_count) { + segmented_layout_data *data = NULL; + + if (segment_count <= 0 || segment_count > 255) + goto fail; + + data = calloc(1, sizeof(segmented_layout_data)); + if (!data) goto fail; + + data->segment_count = segment_count; + data->current_segment = 0; + + data->segments = calloc(segment_count, sizeof(VGMSTREAM*)); + if (!data->segments) goto fail; + + return data; +fail: + free_layout_segmented(data); + return NULL; +} + +int setup_layout_segmented(segmented_layout_data* data) { + int i; + + /* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */ + for (i = 0; i < data->segment_count; i++) { + if (!data->segments[i]) + goto fail; + + if (data->segments[i]->num_samples <= 0) + goto fail; + + /* shouldn't happen */ + if (data->segments[i]->loop_flag != 0) { + VGM_LOG("segmented layout: segment %i is looped\n", i); + data->segments[i]->loop_flag = 0; + } + + if (i > 0) { + if (data->segments[i]->channels != data->segments[i-1]->channels) + goto fail; + + /* a bit weird, but no matter */ + if (data->segments[i]->sample_rate != data->segments[i-1]->sample_rate) { + VGM_LOG("segmented layout: segment %i has different sample rate\n", i); + } + + //if (data->segments[i]->coding_type != data->segments[i-1]->coding_type) + // goto fail; /* perfectly acceptable */ + } + + + /* save start things so we can restart for seeking/looping */ + memcpy(data->segments[i]->start_ch,data->segments[i]->ch,sizeof(VGMSTREAMCHANNEL)*data->segments[i]->channels); + memcpy(data->segments[i]->start_vgmstream,data->segments[i],sizeof(VGMSTREAM)); + } + + + return 1; +fail: + return 0; /* caller is expected to free */ +} + +void free_layout_segmented(segmented_layout_data *data) { + int i; + + if (!data) + return; + + if (data->segments) { + for (i = 0; i < data->segment_count; i++) { + close_vgmstream(data->segments[i]); + } + free(data->segments); + } + free(data); +} + +void reset_layout_segmented(segmented_layout_data *data) { + int i; + + if (!data) + return; + + data->current_segment = 0; + for (i = 0; i < data->segment_count; i++) { + reset_vgmstream(data->segments[i]); + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/wsi_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/wsi_blocked.c deleted file mode 100644 index 6dcd79a52..000000000 --- a/Frameworks/vgmstream/vgmstream/src/layout/wsi_blocked.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -/* set up for the block at the given offset */ -void wsi_block_update(off_t block_offset, VGMSTREAM * vgmstream) { - int i; - - /* assume that all channels have the same size for this block */ - - vgmstream->current_block_offset = block_offset; - /* current_block_size is the data size in this block, so subtract header */ - vgmstream->current_block_size = read_32bitBE( - vgmstream->current_block_offset, - vgmstream->ch[0].streamfile) - 0x10; - vgmstream->next_block_offset = - vgmstream->current_block_offset + - (vgmstream->current_block_size + 0x10) * vgmstream->channels; - - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset + - 0x10 + (vgmstream->current_block_size+0x10)*i; - } -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aax.c b/Frameworks/vgmstream/vgmstream/src/meta/aax.c index b37c70f84..15df8edec 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aax.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aax.c @@ -1,673 +1,193 @@ #include "meta.h" -#include "aax_streamfile.h" +#include "../layout/layout.h" +#include "../coding/coding.h" +#include "aax_utf.h" -struct utf_query -{ - /* if 0 */ - const char *name; - int index; -}; - -struct offset_size_pair -{ - uint32_t offset; - uint32_t size; -}; - -struct utf_query_result -{ - int valid; /* table is valid */ - int found; - int type; /* one of COLUMN_TYPE_* */ - union - { - uint64_t value_u64; - uint32_t value_u32; - uint16_t value_u16; - uint8_t value_u8; - float value_float; - struct offset_size_pair value_data; - uint32_t value_string; - } value; - - /* info for the queried table */ - uint32_t rows; - uint32_t name_offset; - uint32_t string_table_offset; - uint32_t data_offset; -}; - -static struct utf_query_result analyze_utf(STREAMFILE *infile, long offset, - const struct utf_query *query); - -static struct utf_query_result query_utf(STREAMFILE *infile, long offset, - const struct utf_query *query); - -static struct utf_query_result query_utf_nofail(STREAMFILE *infile, const long offset, - const struct utf_query *query, int *error); - -static struct utf_query_result query_utf_key(STREAMFILE *infile, const long offset, - int index, const char *name, int *error); - -static uint8_t query_utf_1byte(STREAMFILE *infile, const long offset, - int index, const char *name, int *error); - -static struct offset_size_pair query_utf_data(STREAMFILE *infile, const long offset, - int index, const char *name, int *error); - -#define COLUMN_STORAGE_MASK 0xf0 -#define COLUMN_STORAGE_PERROW 0x50 -#define COLUMN_STORAGE_CONSTANT 0x30 -#define COLUMN_STORAGE_ZERO 0x10 - -#define COLUMN_TYPE_MASK 0x0f -#define COLUMN_TYPE_DATA 0x0b -#define COLUMN_TYPE_STRING 0x0a -#define COLUMN_TYPE_FLOAT 0x08 -#define COLUMN_TYPE_8BYTE 0x06 -#define COLUMN_TYPE_4BYTE 0x04 -#define COLUMN_TYPE_2BYTE2 0x03 -#define COLUMN_TYPE_2BYTE 0x02 -#define COLUMN_TYPE_1BYTE2 0x01 -#define COLUMN_TYPE_1BYTE 0x00 - -struct utf_column_info -{ - uint8_t type; - const char *column_name; - long constant_offset; -}; - -struct utf_table_info -{ - long table_offset; - uint32_t table_size; - uint32_t schema_offset; - uint32_t rows_offset; - uint32_t string_table_offset; - uint32_t data_offset; - const char *string_table; - const char *table_name; - uint16_t columns; - uint16_t row_width; - uint32_t rows; - - const struct utf_column_info *schema; -}; +static STREAMFILE* setup_aax_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext); -/* AAX - segmented ADX [Padora's Tower (Wii)] */ +#define MAX_SEGMENTS 2 /* usually segment0=intro, segment1=loop/main */ + +/* AAX - segmented ADX [Bayonetta (PS3), Pandora's Tower (Wii), Catherine (X360), Binary Domain (PS3)] */ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileAAX = NULL; - STREAMFILE * streamFileADX = NULL; - char filename[PATH_LIMIT]; - off_t *segment_offset = NULL; - off_t *segment_size = NULL; - int32_t sample_count; + int is_hca; + + int loop_flag = 0, channel_count = 0; + int32_t sample_count, loop_start_sample = 0, loop_end_sample = 0; + int segment_count, loop_segment = 0; + + segmented_layout_data *data = NULL; int table_error = 0; - - int loop_flag = 0; - int32_t loop_start_sample=0; - int32_t loop_end_sample=0; - int loop_segment = 0; - - aax_codec_data *data = NULL; - - const long AAX_offset = 0; - - int channel_count = 0, segment_count; - int sample_rate = 0; - + const long top_offset = 0x00; + off_t segment_offset[MAX_SEGMENTS]; + size_t segment_size[MAX_SEGMENTS]; int i; - long aax_data_offset; + /* checks */ + if (!check_extensions(streamFile, "aax")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */ + goto fail; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("aax",filename_extension(filename))) goto fail; - - /* get AAX entry count, data offset */ + + /* get segment count, offsets and sizes */ { struct utf_query_result result; long aax_string_table_offset; long aax_string_table_size; - - result = query_utf_nofail(streamFile, AAX_offset, NULL, &table_error); + long aax_data_offset; + + result = query_utf_nofail(streamFile, top_offset, NULL, &table_error); if (table_error) goto fail; + segment_count = result.rows; - aax_string_table_offset = AAX_offset + 8 + result.string_table_offset; - aax_data_offset = AAX_offset + 8 + result.data_offset; + if (segment_count > MAX_SEGMENTS) goto fail; + + aax_string_table_offset = top_offset+0x08 + result.string_table_offset; + aax_data_offset = top_offset+0x08 + result.data_offset; aax_string_table_size = aax_data_offset - aax_string_table_offset; - if (result.name_offset+4 > aax_string_table_size) goto fail; - if (read_32bitBE(aax_string_table_offset + result.name_offset, - streamFile) != 0x41415800) /* "AAX\0" */ + if (result.name_offset+0x04 > aax_string_table_size) goto fail; - } - segment_offset = calloc(segment_count,sizeof(off_t)); - if (!segment_offset) - goto fail; - segment_size = calloc(segment_count,sizeof(off_t)); - if (!segment_size) - goto fail; - - /* get offsets of constituent ADXs */ - for (i = 0; i < segment_count; i++) - { - struct offset_size_pair offset_size; - offset_size = query_utf_data(streamFile, AAX_offset, i, "data", &table_error); - if (table_error) goto fail; - segment_offset[i] = aax_data_offset + offset_size.offset; - segment_size[i] = offset_size.size; - } - - streamFileAAX = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileAAX) goto fail; - - data = malloc(sizeof(aax_codec_data)); - if (!data) goto fail; - data->segment_count = segment_count; - data->adxs = malloc(sizeof(STREAMFILE *)*segment_count); - if (!data->adxs) goto fail; - for (i=0;iadxs[i] = NULL; - } - data->sample_counts = calloc(segment_count,sizeof(int32_t)); - if (!data->sample_counts) goto fail; - - /* for each segment */ - for (i = 0; i < segment_count; i++) - { - VGMSTREAM *adx; - /*printf("try opening segment %d/%d %x\n",i,segment_count,segment_offset[i]);*/ - streamFileADX = open_aax_with_STREAMFILE(streamFileAAX,segment_offset[i],segment_size[i]); - if (!streamFileADX) goto fail; - adx = data->adxs[i] = init_vgmstream_adx(streamFileADX); - if (!adx) - goto fail; - data->sample_counts[i] = adx->num_samples; - close_streamfile(streamFileADX); streamFileADX = NULL; - - if (i == 0) - { - channel_count = adx->channels; - sample_rate = adx->sample_rate; - } + if (read_32bitBE(aax_string_table_offset + result.name_offset, streamFile) == 0x41415800) /* "AAX\0" */ + is_hca = 0; + else if (read_32bitBE(aax_string_table_offset + result.name_offset, streamFile) == 0x48434100) /* "HCA\0" */ + is_hca = 1; else - { - if (channel_count != adx->channels) - goto fail; - if (sample_rate != adx->sample_rate) - goto fail; - } - - if (adx->loop_flag != 0) goto fail; - /* save start things so we can restart for seeking/looping */ - /* copy the channels */ - memcpy(adx->start_ch,adx->ch,sizeof(VGMSTREAMCHANNEL)*adx->channels); - /* copy the whole VGMSTREAM */ - memcpy(adx->start_vgmstream,adx,sizeof(VGMSTREAM)); + /* get offsets of constituent segments */ + for (i = 0; i < segment_count; i++) { + struct offset_size_pair offset_size; + offset_size = query_utf_data(streamFile, top_offset, i, "data", &table_error); + if (table_error) goto fail; + + segment_offset[i] = aax_data_offset + offset_size.offset; + segment_size[i] = offset_size.size; + } } + /* init layout */ + data = init_layout_segmented(segment_count); + if (!data) goto fail; + + /* 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")); + if (!temp_streamFile) goto fail; + + data->segments[i] = is_hca ? + init_vgmstream_hca(temp_streamFile) : + init_vgmstream_adx(temp_streamFile); + + close_streamfile(temp_streamFile); + + if (!data->segments[i]) goto fail; + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + /* get looping and samples */ sample_count = 0; loop_flag = 0; - for (i = 0; i < segment_count; i++) - { - int segment_loop_flag = query_utf_1byte(streamFile, AAX_offset, i, - "lpflg", &table_error); + for (i = 0; i < segment_count; i++) { + int segment_loop_flag = query_utf_1byte(streamFile, top_offset, i, "lpflg", &table_error); if (table_error) segment_loop_flag = 0; - if (!loop_flag && segment_loop_flag) - { + if (!loop_flag && segment_loop_flag) { loop_start_sample = sample_count; loop_segment = i; } - sample_count += data->sample_counts[i]; + sample_count += data->segments[i]->num_samples; - if (!loop_flag && segment_loop_flag) - { + if (!loop_flag && segment_loop_flag) { loop_end_sample = sample_count; loop_flag = 1; } } + + channel_count = data->segments[0]->channels; + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + vgmstream->sample_rate = data->segments[0]->sample_rate; vgmstream->num_samples = sample_count; - vgmstream->sample_rate = sample_rate; - vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; - vgmstream->coding_type = data->adxs[0]->coding_type; - vgmstream->layout_type = layout_aax; vgmstream->meta_type = meta_AAX; + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; - vgmstream->ch[0].streamfile = streamFileAAX; - data->current_segment = 0; + vgmstream->layout_data = data; data->loop_segment = loop_segment; - vgmstream->codec_data = data; - free(segment_offset); - free(segment_size); - return vgmstream; - /* clean up anything we may have opened */ fail: - if (streamFileAAX) close_streamfile(streamFileAAX); - if (streamFileADX) close_streamfile(streamFileADX); - if (vgmstream) close_vgmstream(vgmstream); - if (segment_offset) free(segment_offset); - if (segment_size) free(segment_size); - if (data) { - if (data->adxs) - { - int i; - for (i=0;isegment_count;i++) - if (data->adxs) - close_vgmstream(data->adxs[i]); - free(data->adxs); - } - if (data->sample_counts) - { - free(data->sample_counts); - } - free(data); - } + close_vgmstream(vgmstream); + free_layout_segmented(data); return NULL; } -/* @UTF table reading, abridged */ -static struct utf_query_result analyze_utf(STREAMFILE *infile, const long offset, const struct utf_query *query) -{ - unsigned char buf[4]; - struct utf_table_info table_info; - char *string_table = NULL; - struct utf_column_info * schema = NULL; - struct utf_query_result result; - uint32_t table_name_string; - int string_table_size; - - result.valid = 0; +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; - table_info.table_offset = offset; + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; - /* check header */ - { - static const char UTF_signature[4] = "@UTF"; /* intentionally unterminated */ - if (4 != read_streamfile(buf, offset, 4, infile)) goto cleanup_error; - if (memcmp(buf, UTF_signature, sizeof(UTF_signature))) - { - goto cleanup_error; - } - } + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; - /* get table size */ - table_info.table_size = read_32bitBE(offset+4, infile); + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; - table_info.schema_offset = 0x20; - table_info.rows_offset = read_32bitBE(offset+8, infile); - table_info.string_table_offset = read_32bitBE(offset+0xc,infile); - table_info.data_offset = read_32bitBE(offset+0x10,infile); - table_name_string = read_32bitBE(offset+0x14,infile); - table_info.columns = read_16bitBE(offset+0x18,infile); - table_info.row_width = read_16bitBE(offset+0x1a,infile); - table_info.rows = read_32bitBE(offset+0x1c,infile); + return temp_streamFile; - /* allocate for string table */ - string_table_size = table_info.data_offset-table_info.string_table_offset; - string_table = malloc(string_table_size+1); - if (!string_table) goto cleanup_error; - table_info.string_table = string_table; - memset(string_table, 0, string_table_size+1); - - /* load schema */ - schema = malloc(sizeof(struct utf_column_info) * table_info.columns); - if (!schema) goto cleanup_error; - - { - int i; - long schema_current_offset = table_info.schema_offset; - for (i = 0; i < table_info.columns; i++) - { - schema[i].type = read_8bit(schema_current_offset,infile); - schema_current_offset ++; - schema[i].column_name = string_table + read_32bitBE(schema_current_offset,infile); - schema_current_offset += 4; - - if ((schema[i].type & COLUMN_STORAGE_MASK) == COLUMN_STORAGE_CONSTANT) - { - schema[i].constant_offset = schema_current_offset; - switch (schema[i].type & COLUMN_TYPE_MASK) - { - case COLUMN_TYPE_8BYTE: - case COLUMN_TYPE_DATA: - schema_current_offset+=8; - break; - case COLUMN_TYPE_STRING: - case COLUMN_TYPE_FLOAT: - case COLUMN_TYPE_4BYTE: - schema_current_offset+=4; - break; - case COLUMN_TYPE_2BYTE2: - case COLUMN_TYPE_2BYTE: - schema_current_offset+=2; - break; - case COLUMN_TYPE_1BYTE2: - case COLUMN_TYPE_1BYTE: - schema_current_offset++; - break; - default: - goto cleanup_error; - } - } - } - } - - table_info.schema = schema; - - /* read string table */ - read_streamfile((unsigned char *)string_table, - table_info.string_table_offset+8+offset, - string_table_size, infile); - table_info.table_name = table_info.string_table+table_name_string; - - /* fill in the default stuff */ - result.found = 0; - result.rows = table_info.rows; - result.name_offset = table_name_string; - result.string_table_offset = table_info.string_table_offset; - result.data_offset = table_info.data_offset; - - /* explore the values */ - if (query) { - int i, j; - - for (i = 0; i < table_info.rows; i++) - { - uint32_t row_offset = - table_info.table_offset + 8 + table_info.rows_offset + - i * table_info.row_width; - const uint32_t row_start_offset = row_offset; - - if (query && i != query->index) continue; - - for (j = 0; j < table_info.columns; j++) - { - uint8_t type = table_info.schema[j].type; - long constant_offset = table_info.schema[j].constant_offset; - int constant = 0; - - int qthis = (query && i == query->index && - !strcmp(table_info.schema[j].column_name, query->name)); - - if (qthis) - { - result.found = 1; - result.type = schema[j].type & COLUMN_TYPE_MASK; - } - - switch (schema[j].type & COLUMN_STORAGE_MASK) - { - case COLUMN_STORAGE_PERROW: - break; - case COLUMN_STORAGE_CONSTANT: - constant = 1; - break; - case COLUMN_STORAGE_ZERO: - if (qthis) - { - memset(&result.value, 0, - sizeof(result.value)); - } - continue; - default: - goto cleanup_error; - } - - if (1) - { - long data_offset; - int bytes_read; - - if (constant) - { - data_offset = constant_offset; - } - else - { - data_offset = row_offset; - } - - switch (type & COLUMN_TYPE_MASK) - { - case COLUMN_TYPE_STRING: - { - uint32_t string_offset; - string_offset = read_32bitBE(data_offset, infile); - bytes_read = 4; - if (qthis) - { - result.value.value_string = string_offset; - } - } - break; - case COLUMN_TYPE_DATA: - { - uint32_t vardata_offset, vardata_size; - - vardata_offset = read_32bitBE(data_offset, infile); - vardata_size = read_32bitBE(data_offset+4, infile); - bytes_read = 8; - if (qthis) - { - result.value.value_data.offset = vardata_offset; - result.value.value_data.size = vardata_size; - } - } - break; - - case COLUMN_TYPE_8BYTE: - { - uint64_t value = - read_32bitBE(data_offset, infile); - value <<= 32; - value |= - read_32bitBE(data_offset+4, infile); - if (qthis) - { - result.value.value_u64 = value; - } - bytes_read = 8; - break; - } - case COLUMN_TYPE_4BYTE: - { - uint32_t value = - read_32bitBE(data_offset, infile); - if (qthis) - { - result.value.value_u32 = value; - } - bytes_read = 4; - } - break; - case COLUMN_TYPE_2BYTE2: - case COLUMN_TYPE_2BYTE: - { - uint16_t value = - read_16bitBE(data_offset, infile); - if (qthis) - { - result.value.value_u16 = value; - } - bytes_read = 2; - } - break; - case COLUMN_TYPE_FLOAT: - if (sizeof(float) == 4) - { - union { - float float_value; - uint32_t int_value; - } int_float; - - int_float.int_value = read_32bitBE(data_offset, infile); - if (qthis) - { - result.value.value_float = int_float.float_value; - } - } - else - { - read_32bitBE(data_offset, infile); - if (qthis) - { - goto cleanup_error; - } - } - bytes_read = 4; - break; - case COLUMN_TYPE_1BYTE2: - case COLUMN_TYPE_1BYTE: - { - uint8_t value = - read_8bit(data_offset, infile); - if (qthis) - { - result.value.value_u8 = value; - } - bytes_read = 1; - } - break; - default: - goto cleanup_error; - } - - if (!constant) - { - row_offset += bytes_read; - } - } /* useless if end */ - } /* column for loop end */ - - if (row_offset - row_start_offset != table_info.row_width) - goto cleanup_error; - - if (query && i >= query->index) break; - } /* row for loop end */ - } /* explore values block end */ - -//cleanup: - - result.valid = 1; -cleanup_error: - - if (string_table) - { - free(string_table); - string_table = NULL; - } - - if (schema) - { - free(schema); - schema = NULL; - } - - return result; +fail: + close_streamfile(temp_streamFile); + return NULL; } -static struct utf_query_result query_utf(STREAMFILE *infile, const long offset, const struct utf_query *query) -{ - return analyze_utf(infile, offset, query); -} -static struct utf_query_result query_utf_nofail(STREAMFILE *infile, const long offset, const struct utf_query *query, int *error) -{ - const struct utf_query_result result = query_utf(infile, offset, query); - - if (error) - { - *error = 0; - if (!result.valid) *error = 1; - if (query && !result.found) *error = 1; - } - - return result; -} - -static struct utf_query_result query_utf_key(STREAMFILE *infile, const long offset, int index, const char *name, int *error) -{ - struct utf_query query; - query.index = index; - query.name = name; - - return query_utf_nofail(infile, offset, &query, error); -} - -static uint8_t query_utf_1byte(STREAMFILE *infile, const long offset, int index, const char *name, int *error) -{ - struct utf_query_result result = query_utf_key(infile, offset, index, name, error); - if (error) - { - if (result.type != COLUMN_TYPE_1BYTE) *error = 1; - } - return result.value.value_u8; -} - -static uint32_t query_utf_4byte(STREAMFILE *infile, const long offset, int index, const char *name, int *error) -{ - struct utf_query_result result = query_utf_key(infile, offset, index, name, error); - if (error) - { - if (result.type != COLUMN_TYPE_4BYTE) *error = 1; - } - return result.value.value_u32; -} - -static struct offset_size_pair query_utf_data(STREAMFILE *infile, const long offset, - int index, const char *name, int *error) -{ - struct utf_query_result result = query_utf_key(infile, offset, index, name, error); - if (error) - { - if (result.type != COLUMN_TYPE_DATA) *error = 1; - } - return result.value.value_data; -} - -/* CRI's UTF wrapper around DSP */ +/* CRI's UTF wrapper around DSP [Sonic Colors sfx (Wii), NiGHTS: Journey of Dreams sfx (Wii)] */ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int table_error = 0; + off_t start_offset; + size_t channel_size; - int loop_flag = 0; - - const long top_offset = 0; - - int channel_count; - int sample_rate; + int loop_flag = 0, channel_count, sample_rate; long sample_count; + int table_error = 0; + const long top_offset = 0x00; long top_data_offset, segment_count; long body_offset, body_size; long header_offset, header_size; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - //if (strcasecmp("aax",filename_extension(filename))) goto fail; - /* get entry count, data offset */ + /* checks */ + /* files don't have extension, we accept "" for CLI and .aax for plugins (they aren't exactly .aax though) */ + if (!check_extensions(streamFile, "aax,")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */ + goto fail; + + /* get segment count, offsets and sizes*/ { struct utf_query_result result; long top_string_table_offset; @@ -676,8 +196,10 @@ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) { result = query_utf_nofail(streamFile, top_offset, NULL, &table_error); if (table_error) goto fail; + segment_count = result.rows; - if (segment_count != 1) goto fail; // only simple stuff for now + if (segment_count != 1) goto fail; /* only simple stuff for now (multisegment not known) */ + top_string_table_offset = top_offset + 8 + result.string_table_offset; top_data_offset = top_offset + 8 + result.data_offset; top_string_table_size = top_data_offset - top_string_table_offset; @@ -685,12 +207,13 @@ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) { if (result.name_offset+10 > top_string_table_size) goto fail; name_offset = top_string_table_offset + result.name_offset; - if (read_32bitBE(name_offset, streamFile) != 0x41445043 ||// "ADPC" - read_32bitBE(name_offset+4, streamFile) != 0x4D5F5749 ||// "M_WI" - read_16bitBE(name_offset+8, streamFile) != 0x4900) // "I\0" + if (read_32bitBE(name_offset+0x00, streamFile) != 0x41445043 || /* "ADPC" */ + read_32bitBE(name_offset+0x04, streamFile) != 0x4D5F5749 || /* "M_WI" */ + read_16bitBE(name_offset+0x08, streamFile) != 0x4900) /* "I\0" */ goto fail; } + /* get sizes */ { struct offset_size_pair offset_size; @@ -712,36 +235,29 @@ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) { if (channel_count != 1 && channel_count != 2) goto fail; if (header_size != channel_count * 0x60) goto fail; + start_offset = body_offset; + channel_size = (body_size+7) / 8 * 8 / channel_count; + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; - vgmstream->num_samples = sample_count; vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = sample_count; - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_UTF_DSP; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = channel_size; - { - int i,j; - long channel_size = (body_size+7)/8*8/channel_count; - for (i = 0; i < channel_count; i++) - { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = body_offset + i * channel_size; - for (j=0;j<16;j++) - { - vgmstream->ch[i].adpcm_coef[j] = - read_16bitBE(header_offset + 0x60*i + 0x1c + j*2, streamFile); - } - } - } + dsp_read_coefs_be(vgmstream, streamFile, header_offset+0x1c, 0x60); + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h deleted file mode 100644 index 1490e7c4a..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef _AAX_STREAMFILE_H_ -#define _AAX_STREAMFILE_H_ -#include "../streamfile.h" - -/* a streamfile representing a subfile inside another */ - -typedef struct _AAXSTREAMFILE { - STREAMFILE sf; - STREAMFILE *real_file; - off_t start_physical_offset; - size_t file_size; -} AAXSTREAMFILE; - -static size_t read_aax(AAXSTREAMFILE *streamfile,uint8_t *dest,off_t offset,size_t length) { - /* truncate at end of logical file */ - if (offset+length > streamfile->file_size) { - long signed_length = length; - signed_length = streamfile->file_size - offset; - if (signed_length < 0) signed_length = 0; - length = signed_length; - } - return read_streamfile(dest, streamfile->start_physical_offset+offset, length,streamfile->real_file); -} - -static void close_aax(AAXSTREAMFILE *streamfile) { - free(streamfile); - return; -} - -static size_t get_size_aax(AAXSTREAMFILE *streamfile) { - return 0; -} - -static size_t get_offset_aax(AAXSTREAMFILE *streamfile) { - long offset = streamfile->real_file->get_offset(streamfile->real_file); - offset -= streamfile->start_physical_offset; - if (offset < 0) offset = 0; - if (offset > streamfile->file_size) offset = streamfile->file_size; - - return offset; -} - -static void get_name_aax(AAXSTREAMFILE *streamfile,char *buffer,size_t length) { - strncpy(buffer,"ARBITRARY.ADX",length); - buffer[length-1]='\0'; -} - -static STREAMFILE *open_aax_impl(AAXSTREAMFILE *streamfile,const char * const filename,size_t buffersize) { - AAXSTREAMFILE *newfile; - if (strcmp(filename,"ARBITRARY.ADX")) - return NULL; - - newfile = malloc(sizeof(AAXSTREAMFILE)); - if (!newfile) - return NULL; - memcpy(newfile,streamfile,sizeof(AAXSTREAMFILE)); - return &newfile->sf; -} - -static STREAMFILE *open_aax_with_STREAMFILE(STREAMFILE *file,off_t start_offset,size_t file_size) { - AAXSTREAMFILE *streamfile = malloc(sizeof(AAXSTREAMFILE)); - - if (!streamfile) - return NULL; - - /* success, set our pointers */ - - streamfile->sf.read = (void*)read_aax; - streamfile->sf.get_size = (void*)get_size_aax; - streamfile->sf.get_offset = (void*)get_offset_aax; - streamfile->sf.get_name = (void*)get_name_aax; - streamfile->sf.get_realname = (void*)get_name_aax; - streamfile->sf.open = (void*)open_aax_impl; - streamfile->sf.close = (void*)close_aax; - - streamfile->real_file = file; - streamfile->start_physical_offset = start_offset; - streamfile->file_size = file_size; - - return &streamfile->sf; -} - -#endif /* _AAX_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aax_utf.h b/Frameworks/vgmstream/vgmstream/src/meta/aax_utf.h new file mode 100644 index 000000000..24dc88dc3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/aax_utf.h @@ -0,0 +1,442 @@ +#ifndef _AAX_UTF_H_ +#define _AAX_UTF_H_ + +struct utf_query +{ + /* if 0 */ + const char *name; + int index; +}; + +struct offset_size_pair +{ + uint32_t offset; + uint32_t size; +}; + +struct utf_query_result +{ + int valid; /* table is valid */ + int found; + int type; /* one of COLUMN_TYPE_* */ + union + { + uint64_t value_u64; + uint32_t value_u32; + uint16_t value_u16; + uint8_t value_u8; + float value_float; + struct offset_size_pair value_data; + uint32_t value_string; + } value; + + /* info for the queried table */ + uint32_t rows; + uint32_t name_offset; + uint32_t string_table_offset; + uint32_t data_offset; +}; + + +#define COLUMN_STORAGE_MASK 0xf0 +#define COLUMN_STORAGE_PERROW 0x50 +#define COLUMN_STORAGE_CONSTANT 0x30 +#define COLUMN_STORAGE_ZERO 0x10 + +#define COLUMN_TYPE_MASK 0x0f +#define COLUMN_TYPE_DATA 0x0b +#define COLUMN_TYPE_STRING 0x0a +#define COLUMN_TYPE_FLOAT 0x08 +#define COLUMN_TYPE_8BYTE 0x06 +#define COLUMN_TYPE_4BYTE 0x04 +#define COLUMN_TYPE_2BYTE2 0x03 +#define COLUMN_TYPE_2BYTE 0x02 +#define COLUMN_TYPE_1BYTE2 0x01 +#define COLUMN_TYPE_1BYTE 0x00 + +struct utf_column_info +{ + uint8_t type; + const char *column_name; + long constant_offset; +}; + +struct utf_table_info +{ + long table_offset; + uint32_t table_size; + uint32_t schema_offset; + uint32_t rows_offset; + uint32_t string_table_offset; + uint32_t data_offset; + const char *string_table; + const char *table_name; + uint16_t columns; + uint16_t row_width; + uint32_t rows; + + const struct utf_column_info *schema; +}; + +/* @UTF table reading, abridged */ +static struct utf_query_result analyze_utf(STREAMFILE *infile, const long offset, const struct utf_query *query) +{ + unsigned char buf[4]; + struct utf_table_info table_info; + char *string_table = NULL; + struct utf_column_info * schema = NULL; + struct utf_query_result result; + uint32_t table_name_string; + int string_table_size; + + result.valid = 0; + + table_info.table_offset = offset; + + /* check header */ + { + static const char UTF_signature[4] = "@UTF"; /* intentionally unterminated */ + if (4 != read_streamfile(buf, offset, 4, infile)) goto cleanup_error; + if (memcmp(buf, UTF_signature, sizeof(UTF_signature))) + { + goto cleanup_error; + } + } + + /* get table size */ + table_info.table_size = read_32bitBE(offset+4, infile); + + table_info.schema_offset = 0x20; + table_info.rows_offset = read_32bitBE(offset+8, infile); + table_info.string_table_offset = read_32bitBE(offset+0xc,infile); + table_info.data_offset = read_32bitBE(offset+0x10,infile); + table_name_string = read_32bitBE(offset+0x14,infile); + table_info.columns = read_16bitBE(offset+0x18,infile); + table_info.row_width = read_16bitBE(offset+0x1a,infile); + table_info.rows = read_32bitBE(offset+0x1c,infile); + + /* allocate for string table */ + string_table_size = table_info.data_offset-table_info.string_table_offset; + string_table = malloc(string_table_size+1); + if (!string_table) goto cleanup_error; + table_info.string_table = string_table; + memset(string_table, 0, string_table_size+1); + + /* load schema */ + schema = malloc(sizeof(struct utf_column_info) * table_info.columns); + if (!schema) goto cleanup_error; + + { + int i; + long schema_current_offset = table_info.schema_offset; + for (i = 0; i < table_info.columns; i++) + { + schema[i].type = read_8bit(schema_current_offset,infile); + schema_current_offset ++; + schema[i].column_name = string_table + read_32bitBE(schema_current_offset,infile); + schema_current_offset += 4; + + if ((schema[i].type & COLUMN_STORAGE_MASK) == COLUMN_STORAGE_CONSTANT) + { + schema[i].constant_offset = schema_current_offset; + switch (schema[i].type & COLUMN_TYPE_MASK) + { + case COLUMN_TYPE_8BYTE: + case COLUMN_TYPE_DATA: + schema_current_offset+=8; + break; + case COLUMN_TYPE_STRING: + case COLUMN_TYPE_FLOAT: + case COLUMN_TYPE_4BYTE: + schema_current_offset+=4; + break; + case COLUMN_TYPE_2BYTE2: + case COLUMN_TYPE_2BYTE: + schema_current_offset+=2; + break; + case COLUMN_TYPE_1BYTE2: + case COLUMN_TYPE_1BYTE: + schema_current_offset++; + break; + default: + goto cleanup_error; + } + } + } + } + + table_info.schema = schema; + + /* read string table */ + read_streamfile((unsigned char *)string_table, + table_info.string_table_offset+8+offset, + string_table_size, infile); + table_info.table_name = table_info.string_table+table_name_string; + + /* fill in the default stuff */ + result.found = 0; + result.rows = table_info.rows; + result.name_offset = table_name_string; + result.string_table_offset = table_info.string_table_offset; + result.data_offset = table_info.data_offset; + + /* explore the values */ + if (query) { + int i, j; + + for (i = 0; i < table_info.rows; i++) + { + uint32_t row_offset = + table_info.table_offset + 8 + table_info.rows_offset + + i * table_info.row_width; + const uint32_t row_start_offset = row_offset; + + if (query && i != query->index) continue; + + for (j = 0; j < table_info.columns; j++) + { + uint8_t type = table_info.schema[j].type; + long constant_offset = table_info.schema[j].constant_offset; + int constant = 0; + + int qthis = (query && i == query->index && + !strcmp(table_info.schema[j].column_name, query->name)); + + if (qthis) + { + result.found = 1; + result.type = schema[j].type & COLUMN_TYPE_MASK; + } + + switch (schema[j].type & COLUMN_STORAGE_MASK) + { + case COLUMN_STORAGE_PERROW: + break; + case COLUMN_STORAGE_CONSTANT: + constant = 1; + break; + case COLUMN_STORAGE_ZERO: + if (qthis) + { + memset(&result.value, 0, + sizeof(result.value)); + } + continue; + default: + goto cleanup_error; + } + + if (1) + { + long data_offset; + int bytes_read; + + if (constant) + { + data_offset = constant_offset; + } + else + { + data_offset = row_offset; + } + + switch (type & COLUMN_TYPE_MASK) + { + case COLUMN_TYPE_STRING: + { + uint32_t string_offset; + string_offset = read_32bitBE(data_offset, infile); + bytes_read = 4; + if (qthis) + { + result.value.value_string = string_offset; + } + } + break; + case COLUMN_TYPE_DATA: + { + uint32_t vardata_offset, vardata_size; + + vardata_offset = read_32bitBE(data_offset, infile); + vardata_size = read_32bitBE(data_offset+4, infile); + bytes_read = 8; + if (qthis) + { + result.value.value_data.offset = vardata_offset; + result.value.value_data.size = vardata_size; + } + } + break; + + case COLUMN_TYPE_8BYTE: + { + uint64_t value = + read_32bitBE(data_offset, infile); + value <<= 32; + value |= + read_32bitBE(data_offset+4, infile); + if (qthis) + { + result.value.value_u64 = value; + } + bytes_read = 8; + break; + } + case COLUMN_TYPE_4BYTE: + { + uint32_t value = + read_32bitBE(data_offset, infile); + if (qthis) + { + result.value.value_u32 = value; + } + bytes_read = 4; + } + break; + case COLUMN_TYPE_2BYTE2: + case COLUMN_TYPE_2BYTE: + { + uint16_t value = + read_16bitBE(data_offset, infile); + if (qthis) + { + result.value.value_u16 = value; + } + bytes_read = 2; + } + break; + case COLUMN_TYPE_FLOAT: + if (sizeof(float) == 4) + { + union { + float float_value; + uint32_t int_value; + } int_float; + + int_float.int_value = read_32bitBE(data_offset, infile); + if (qthis) + { + result.value.value_float = int_float.float_value; + } + } + else + { + read_32bitBE(data_offset, infile); + if (qthis) + { + goto cleanup_error; + } + } + bytes_read = 4; + break; + case COLUMN_TYPE_1BYTE2: + case COLUMN_TYPE_1BYTE: + { + uint8_t value = + read_8bit(data_offset, infile); + if (qthis) + { + result.value.value_u8 = value; + } + bytes_read = 1; + } + break; + default: + goto cleanup_error; + } + + if (!constant) + { + row_offset += bytes_read; + } + } /* useless if end */ + } /* column for loop end */ + + if (row_offset - row_start_offset != table_info.row_width) + goto cleanup_error; + + if (query && i >= query->index) break; + } /* row for loop end */ + } /* explore values block end */ + +//cleanup: + + result.valid = 1; +cleanup_error: + + if (string_table) + { + free(string_table); + string_table = NULL; + } + + if (schema) + { + free(schema); + schema = NULL; + } + + return result; +} + +static struct utf_query_result query_utf(STREAMFILE *infile, const long offset, const struct utf_query *query) +{ + return analyze_utf(infile, offset, query); +} + +/*static*/ struct utf_query_result query_utf_nofail(STREAMFILE *infile, const long offset, const struct utf_query *query, int *error) +{ + const struct utf_query_result result = query_utf(infile, offset, query); + + if (error) + { + *error = 0; + if (!result.valid) *error = 1; + if (query && !result.found) *error = 1; + } + + return result; +} + +static struct utf_query_result query_utf_key(STREAMFILE *infile, const long offset, int index, const char *name, int *error) +{ + struct utf_query query; + query.index = index; + query.name = name; + + return query_utf_nofail(infile, offset, &query, error); +} + +/*static*/ uint8_t query_utf_1byte(STREAMFILE *infile, const long offset, int index, const char *name, int *error) +{ + struct utf_query_result result = query_utf_key(infile, offset, index, name, error); + if (error) + { + if (result.type != COLUMN_TYPE_1BYTE) *error = 1; + } + return result.value.value_u8; +} + +/*static*/ uint32_t query_utf_4byte(STREAMFILE *infile, const long offset, int index, const char *name, int *error) +{ + struct utf_query_result result = query_utf_key(infile, offset, index, name, error); + if (error) + { + if (result.type != COLUMN_TYPE_4BYTE) *error = 1; + } + return result.value.value_u32; +} + +/*static*/ struct offset_size_pair query_utf_data(STREAMFILE *infile, const long offset, + int index, const char *name, int *error) +{ + struct utf_query_result result = query_utf_key(infile, offset, index, name, error); + if (error) + { + if (result.type != COLUMN_TYPE_DATA) *error = 1; + } + return result.value.value_data; +} + + +#endif /* SRC_META_AAX_UTF_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acm.c b/Frameworks/vgmstream/vgmstream/src/meta/acm.c index e91a9cb7f..4f1cf6fa7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/acm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/acm.c @@ -1,66 +1,49 @@ -#include "../vgmstream.h" #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" #include "../coding/acm_decoder.h" -/* InterPlay ACM */ -/* The real work is done by libacm */ +/* ACM - InterPlay infinity engine games [Planescape: Torment (PC), Baldur's Gate (PC)] */ VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - ACMStream *acm_stream = NULL; - mus_acm_codec_data *data; + int loop_flag = 0, channel_count, sample_rate, num_samples; + acm_codec_data *data = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("acm",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x97280301) - goto fail; - - data = calloc(1,sizeof(mus_acm_codec_data)); - if (!data) goto fail; - - data->files = calloc(1,sizeof(ACMStream *)); - if (!data->files) { - free(data); data = NULL; + /* checks */ + if (!check_extensions(streamFile, "acm")) goto fail; + if (read_32bitBE(0x0,streamFile) != 0x97280301) /* header id */ + goto fail; + + + /* init decoder */ + { + data = init_acm(streamFile); + if (!data) goto fail; + + channel_count = data->file->info.channels; + sample_rate = data->file->info.rate; + num_samples = data->file->total_values / data->file->info.channels; } - /* gonna do this a little backwards, open and parse the file - before creating the vgmstream */ - if (acm_open_decoder(&acm_stream,streamFile,filename) != ACM_OK) { - goto fail; - } - - channel_count = acm_stream->info.channels; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->channels = channel_count; - vgmstream->sample_rate = acm_stream->info.rate; - vgmstream->coding_type = coding_ACM; - vgmstream->num_samples = acm_stream->total_values / acm_stream->info.channels; - vgmstream->layout_type = layout_acm; - vgmstream->meta_type = meta_ACM; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; - data->file_count = 1; - data->current_file = 0; - data->files[0] = acm_stream; - /*data->end_file = -1;*/ + vgmstream->meta_type = meta_ACM; + vgmstream->coding_type = coding_ACM; + vgmstream->layout_type = layout_none; vgmstream->codec_data = data; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + free_acm(data); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 5e6a875c2..0dc09918c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -28,8 +28,10 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { uint16_t xor_start=0,xor_mult=0,xor_add=0; - /* check extension, case insensitive */ - if (!check_extensions(streamFile,"adx")) goto fail; + /* checks*/ + /* .adx: standard, .adp: Headhunter (DC) */ + if (!check_extensions(streamFile,"adx,adp")) + goto fail; /* check first 2 bytes */ if ((uint16_t)read_16bitBE(0x00,streamFile)!=0x8000) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index 9d5b750e8..d35564864 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -26,7 +26,7 @@ static const adxkey_info adxkey8_list[] = { {0x49e1,0x4a57,0x553d, "karaage",0}, /* Grasshopper Manufacture 0 (Blood+) */ - {0x5f5d,0x58bd,0x55ed, NULL,0}, // estimated + {0x5f5d,0x58bd,0x55ed, NULL,0}, // estimated (keystring not in ELF?) /* Grasshopper Manufacture 1 (Killer7) */ {0x50fb,0x5803,0x5701, "GHM",0}, @@ -132,7 +132,7 @@ static const adxkey_info adxkey8_list[] = { {0x6731,0x645d,0x566b, NULL,0}, // confirmed unique with guessadx /* Shakugan no Shana (2006)(Vridge)(Media Works)[PS2] */ - {0x5fc5,0x63d9,0x599f, NULL,0}, // confirmed unique with guessadx + {0x5fc5,0x63d9,0x599f, "FUZETSU",0}, /* Uragiri wa Boku no Namae o Shitteiru (2010)(Kadokawa Shoten)[PS2] */ {0x4c73,0x4d8d,0x5827, NULL,0}, // confirmed unique with guessadx @@ -207,6 +207,9 @@ static const adxkey_info adxkey9_list[] = { // Super Robot Wars X-Omega (voices) [iOS/Android] {0x5152,0x7979,0x152b, NULL,165521992944278}, // 0000968A97978A96 + // 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) }; static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h index 7fd36fd83..ae2be5984 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h @@ -148,7 +148,6 @@ static STREAMFILE *open_aix_impl(AIXSTREAMFILE *streamfile,const char * const fi streamfile->sf.get_size = (void*)get_size_aix; streamfile->sf.get_offset = (void*)get_offset_aix; streamfile->sf.get_name = (void*)get_name_aix; - streamfile->sf.get_realname = (void*)get_name_aix; streamfile->sf.open = (void*)open_aix_impl; streamfile->sf.close = (void*)close_aix; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/akb.c b/Frameworks/vgmstream/vgmstream/src/meta/akb.c index 8a22de08a..0f65d66a8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/akb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/akb.c @@ -3,7 +3,7 @@ #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) /* AKB (AAC only) - found in SQEX iOS games */ -VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_akb_mp4(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; size_t filesize; @@ -34,74 +34,99 @@ fail: /* AKB - found in SQEX iOS games */ -VGMSTREAM * init_vgmstream_akb_multi(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, extra_data_offset = 0; - size_t file_size, header_size, extra_header_size = 0, extra_data_size = 0; - int loop_flag = 0, channel_count, codec; + off_t start_offset, extradata_offset = 0; + size_t stream_size, header_size, subheader_size = 0, extradata_size = 0; + int loop_flag = 0, channel_count, codec, sample_rate; + int num_samples, loop_start, loop_end; - /* check extensions */ + + /* checks */ /* .akb.bytes is the usual extension in later games */ - if ( !check_extensions(streamFile, "akb") ) + if ( !check_extensions(streamFile, "akb,bytes") ) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x414B4220) /* "AKB " */ goto fail; + if (read_32bitLE(0x08,streamFile) != get_streamfile_size(streamFile)) + goto fail; - channel_count = read_8bit(0x0d,streamFile); - loop_flag = read_32bitLE(0x18,streamFile) > 0; /* loop end */ - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* 0x04 (2): version (iPad/IPhone?) */ + /* 0x04(2): version? (iPad/IPhone?) */ header_size = read_16bitLE(0x06,streamFile); - file_size = read_32bitLE(0x08,streamFile); - codec = read_8bit(0x0c,streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x0e,streamFile); - vgmstream->num_samples = read_32bitLE(0x10,streamFile); - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x18,streamFile); - vgmstream->meta_type = meta_AKB; - /* should be ok but not exact (probably more complex, see AKB2) */ - if ( header_size >= 0x44) { /* v2 */ - /* 0x10+: config? (pan, volume), 0x24: file_id? */ - extra_data_size = read_16bitLE(0x1c,streamFile); - extra_header_size = read_16bitLE(0x28,streamFile); - extra_data_offset = header_size + extra_header_size; - start_offset = extra_data_offset + extra_data_size; - /* ~num_samples at extra_data_offset + 0x04/0x0c? */ + codec = read_8bit(0x0c,streamFile); + channel_count = read_8bit(0x0d,streamFile); + sample_rate = (uint16_t)read_16bitLE(0x0e,streamFile); + num_samples = read_32bitLE(0x10,streamFile); + loop_start = read_32bitLE(0x14,streamFile); + loop_end = read_32bitLE(0x18,streamFile); + + /* possibly more complex, see AKB2 */ + if (header_size >= 0x44) { /* v2 */ + extradata_size = read_16bitLE(0x1c,streamFile); + subheader_size = read_16bitLE(0x28,streamFile); + /* 0x20+: config? (pan, volume), 0x24: file_id? */ + extradata_offset = header_size + subheader_size; + start_offset = extradata_offset + extradata_size; } else { /* v0 */ start_offset = header_size; } + stream_size = get_streamfile_size(streamFile) - start_offset; + loop_flag = read_32bitLE(0x18,streamFile) > 0; /* loop end */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_AKB; + switch (codec) { - case 0x02: { /* MSAPDCM [various SFX] */ + case 0x02: { /* MSAPDCM [Dragon Quest II (iOS) sfx] */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = read_16bitLE(extra_data_offset + 0x02,streamFile); + vgmstream->interleave_block_size = read_16bitLE(extradata_offset + 0x02,streamFile); /* adjusted samples; bigger or smaller than base samples, but seems more accurate * (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(extra_data_offset + 0x04, streamFile); - vgmstream->loop_start_sample = read_32bitLE(extra_data_offset + 0x08, streamFile); - vgmstream->loop_end_sample = read_32bitLE(extra_data_offset + 0x0c, streamFile); + vgmstream->num_samples = read_32bitLE(extradata_offset + 0x04, streamFile); + vgmstream->loop_start_sample = read_32bitLE(extradata_offset + 0x08, streamFile); + vgmstream->loop_end_sample = read_32bitLE(extradata_offset + 0x0c, streamFile); break; } -#ifdef VGM_USE_FFMPEG - case 0x05: { /* Ogg Vorbis [Final Fantasy VI, Dragon Quest II-VI] */ - /* Starting from an offset in the current libvorbis code is a bit hard so just use FFmpeg. - * Decoding seems to produce the same output with (inaudible) +-1 lower byte differences due to rounding. */ +#ifdef VGM_USE_VORBIS + case 0x05: { /* Ogg Vorbis [Final Fantasy VI (iOS), Dragon Quest II-VI (iOS)] */ + VGMSTREAM *ogg_vgmstream = NULL; + ogg_vorbis_meta_info_t ovmi = {0}; + + ovmi.meta_type = vgmstream->meta_type; + ovmi.stream_size = stream_size; + + ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); + if (ogg_vgmstream) { + close_vgmstream(vgmstream); + return ogg_vgmstream; + } + else { + goto fail; + } + + break; + } + +#elif defined(VGM_USE_FFMPEG) + /* Alt decoding without libvorbis (minor number of beginning samples difference). + * Otherwise same output with (inaudible) +-1 lower byte differences due to rounding. */ + case 0x05: { /* Ogg Vorbis [Final Fantasy VI (iOS), Dragon Quest II-VI (iOS)] */ ffmpeg_codec_data *ffmpeg_data; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,file_size-start_offset); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; @@ -109,20 +134,30 @@ VGMSTREAM * init_vgmstream_akb_multi(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; /* These oggs have loop info in the comments, too */ + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; } +#endif +#ifdef VGM_USE_FFMPEG case 0x06: { /* aac [The World Ends with You (iPad)] */ - /* init_vgmstream_akb above has priority, but this works fine too */ + /* init_vgmstream_akb_mp4 above has priority, but this works fine too */ ffmpeg_codec_data *ffmpeg_data; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,file_size-start_offset); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size-start_offset); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + /* remove encoder delay from the "global" sample values */ vgmstream->num_samples -= ffmpeg_data->skipSamples; vgmstream->loop_start_sample -= ffmpeg_data->skipSamples; @@ -149,80 +184,134 @@ fail: /* AKB2 - found in later SQEX iOS games */ -VGMSTREAM * init_vgmstream_akb2_multi(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, header_offset; - size_t datasize; - int loop_flag = 0, channel_count, codec; - int akb_header_size, sound_index = 0, sound_offset_data, sound, sound_header_size, material_offset_data, material_index = 0, material, extradata, encryption_flag; + 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 total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ /* .akb.bytes is the usual extension in later games */ - if ( !check_extensions(streamFile, "akb") ) + if ( !check_extensions(streamFile, "akb,bytes") ) goto fail; - /* check header */ + /* checks */ if (read_32bitBE(0x00,streamFile) != 0x414B4232) /* "AKB2" */ goto fail; + if (read_32bitLE(0x08,streamFile) != get_streamfile_size(streamFile)) + goto fail; - akb_header_size = read_16bitLE(0x06, streamFile); - sound_offset_data = akb_header_size + sound_index * 0x10; - sound = read_32bitLE(sound_offset_data + 0x04, streamFile); - sound_header_size = read_16bitLE(sound + 0x02, streamFile); - material_offset_data = sound + sound_header_size + material_index * 0x10; - material = sound + read_32bitLE(material_offset_data + 0x04, streamFile); - encryption_flag = read_8bit(material + 0x03, streamFile) & 0x08; - extradata = material + read_16bitLE(material + 0x04, streamFile); + /* parse tables */ + { + off_t table_offset; + size_t table_size, entry_size; + off_t akb_header_size = read_16bitLE(0x06, streamFile); + int table_count = read_8bit(0x0c, streamFile); - start_offset = material + read_16bitLE(material + 0x04, streamFile) + read_32bitLE(material + 0x18, streamFile); - header_offset = material; + /* probably each table has its type somewhere, but only seen last table = sound table */ + if (table_count > 2) /* 2 only seen in some Mobius FF sound banks */ + goto fail; + entry_size = 0x10; /* technically every entry/table has its own size but to simplify... */ + + table_offset = read_32bitLE(akb_header_size + (table_count-1)*entry_size + 0x04, streamFile); + table_size = read_16bitLE(table_offset + 0x02, streamFile); + + total_subsongs = read_8bit(table_offset + 0x0f, streamFile); /* can contain 0 entries too */ + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + material_offset = table_offset + read_32bitLE(table_offset + table_size + (target_subsong-1)*entry_size + 0x04, streamFile); + } + + /** stream header **/ + /* 0x00: 0? */ + codec = read_8bit(material_offset+0x01,streamFile); + channel_count = read_8bit(material_offset+0x02,streamFile); + encryption_flag = read_8bit(material_offset+0x03,streamFile); + material_size = read_16bitLE(material_offset+0x04,streamFile); + sample_rate = (uint16_t)read_16bitLE(material_offset+0x06,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_end = read_32bitLE(material_offset+0x14,streamFile); + extradata_size = read_32bitLE(material_offset+0x18,streamFile); + /* rest: ? (empty or 0x3f80) */ + + loop_flag = (loop_end > 0); + extradata_offset = material_offset + material_size; + start_offset = material_offset + material_size + extradata_size; - channel_count = read_8bit(header_offset+0x02,streamFile); - loop_flag = read_32bitLE(header_offset+0x14,streamFile) > 0; /* loop end */ /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* 0x04: version? 0x08: filesize, 0x28: file_id?, others: no idea */ - codec = read_8bit(header_offset+0x01,streamFile); - datasize = read_32bitLE(header_offset+0x08,streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitLE(header_offset+0x06,streamFile); - /* When loop_flag num_samples may be much larger than real num_samples (it's fine when looping is off) - * Actual num_samples would be loop_end_sample+1, but more testing is needed */ - vgmstream->num_samples = read_32bitLE(header_offset+0x0c,streamFile); - vgmstream->loop_start_sample = read_32bitLE(header_offset+0x10,streamFile); - vgmstream->loop_end_sample = read_32bitLE(header_offset+0x14,streamFile); + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_AKB; switch (codec) { - case 0x02: { /* MSAPDCM [The Irregular at Magic High School Lost Zero (Android)] */ - if (encryption_flag) goto fail; + 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 + 0x02, streamFile); + vgmstream->interleave_block_size = read_16bitLE(extradata_offset + 0x02, streamFile); /* adjusted samples; bigger or smaller than base samples, but seems more accurate * (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 + 0x04, streamFile); - vgmstream->loop_start_sample = read_32bitLE(extradata + 0x08, streamFile); - vgmstream->loop_end_sample = read_32bitLE(extradata + 0x0c, streamFile); + vgmstream->num_samples = read_32bitLE(extradata_offset + 0x04, streamFile); + vgmstream->loop_start_sample = read_32bitLE(extradata_offset + 0x08, streamFile); + vgmstream->loop_end_sample = read_32bitLE(extradata_offset + 0x0c, streamFile); break; } -#ifdef VGM_USE_FFMPEG - case 0x05: { /* ogg vorbis [The World Ends with You (iPhone / latest update)] */ +#ifdef VGM_USE_VORBIS + case 0x05: { /* Ogg Vorbis [The World Ends with You (iOS / latest update)] */ + VGMSTREAM *ogg_vgmstream = NULL; + ogg_vorbis_meta_info_t ovmi = {0}; + + ovmi.meta_type = vgmstream->meta_type; + ovmi.total_subsongs = total_subsongs; + ovmi.stream_size = stream_size; + + ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); + if (ogg_vgmstream) { + ogg_vgmstream->num_streams = vgmstream->num_streams; + ogg_vgmstream->stream_size = vgmstream->stream_size; + + close_vgmstream(vgmstream); + return ogg_vgmstream; + } + else { + goto fail; + } + + break; + } + +#elif defined(VGM_USE_FFMPEG) + /* Alt decoding without libvorbis (minor number of beginning samples difference). + * Otherwise same output with (inaudible) +-1 lower byte differences due to rounding. */ + case 0x05: { /* Ogg Vorbis [The World Ends with You (iOS / latest update)] */ ffmpeg_codec_data *ffmpeg_data; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,datasize); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + /* When loop_flag num_samples may be much larger than real num_samples (it's fine when looping is off) + * Actual num_samples would be loop_end_sample+1, but more testing is needed */ + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/apple_caff.c b/Frameworks/vgmstream/vgmstream/src/meta/apple_caff.c index f2ab60d53..8e7750886 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/apple_caff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/apple_caff.c @@ -1,170 +1,160 @@ #include "meta.h" -#include "../util.h" - -/* Apple Core Audio Format */ +#include "../coding/coding.h" +/* Apple Core Audio Format File - from iOS games [Vectros (iOS), Ridge Racer Accelerated (iOS)] */ VGMSTREAM * init_vgmstream_apple_caff(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + off_t start_offset = 0, chunk_offset; + size_t file_size, data_size = 0; + int loop_flag, channel_count = 0, sample_rate = 0; - off_t start_offset = 0; - off_t data_size = 0; - off_t sample_count = 0; - off_t interleave = 0; - int sample_rate = -1, unused_frames = 0; - int channel_count = 0; - - off_t file_length; - off_t chunk_offset = 8; - int found_desc = 0, found_pakt = 0, found_data = 0; + int found_desc = 0 /*, found_pakt = 0*/, found_data = 0; + uint32_t codec = 0 /*, codec_flags = 0*/; + uint32_t bytes_per_packet = 0, samples_per_packet = 0, channels_per_packet = 0, bits_per_sample = 0; + int valid_samples = 0 /*, priming_samples = 0, unused_samples = 0*/; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("caf",filename_extension(filename))) goto fail; - /* check "caff" id */ - if (read_32bitBE(0,streamFile)!=0x63616666) goto fail; - /* check version, flags */ - if (read_32bitBE(4,streamFile)!=0x00010000) goto fail; + /* checks */ + if (!check_extensions(streamFile, "caf")) + goto fail; - file_length = (off_t)get_streamfile_size(streamFile); + if (read_32bitBE(0x00,streamFile) != 0x63616666) /* "caff" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x00010000) /* version/flags */ + goto fail; - while (chunk_offset < file_length) - { - /* high half of size (expect 0s) */ - if (read_32bitBE(chunk_offset+4,streamFile) != 0) goto fail; + file_size = get_streamfile_size(streamFile); + chunk_offset = 0x08; - /* handle chunk type */ - switch (read_32bitBE(chunk_offset,streamFile)) - { - case 0x64657363: /* desc */ + while (chunk_offset < file_size) { + uint32_t chunk_type = read_32bitBE(chunk_offset+0x00,streamFile); + uint32_t chunk_size = (uint32_t)read_64bitBE(chunk_offset+0x04,streamFile); + chunk_offset += 0x0c; + + switch (chunk_type) { + + case 0x64657363: /* "desc" */ found_desc = 1; - { - /* rather than put a convoluted conversion here for - portability, just look it up */ - uint32_t sratefloat = read_32bitBE(chunk_offset+0x0c, streamFile); - if (read_32bitBE(chunk_offset+0x10, streamFile) != 0) goto fail; - switch (sratefloat) - { - case 0x40D19400: - sample_rate = 18000; - break; - case 0x40D58880: - sample_rate = 22050; - break; - case 0x40DF4000: - sample_rate = 32000; - break; - case 0x40E58880: - sample_rate = 44100; - break; - case 0x40E77000: - sample_rate = 48000; - break; - default: - goto fail; - } - } { - uint32_t bytes_per_packet, frames_per_packet, channels_per_frame, bits_per_channel; - uint32_t codec_4cc = read_32bitBE(chunk_offset+0x14, streamFile); - /* only supporting ima4 for now */ - if (codec_4cc != 0x696d6134) goto fail; + uint64_t sample_long = (uint64_t)read_64bitBE(chunk_offset+0x00, streamFile); + double* sample_double; /* double sample rate, double the fun */ - /* format flags */ - if (read_32bitBE(chunk_offset+0x18, streamFile) != 0) goto fail; - bytes_per_packet = read_32bitBE(chunk_offset+0x1c, streamFile); - frames_per_packet = read_32bitBE(chunk_offset+0x20, streamFile); - channels_per_frame = read_32bitBE(chunk_offset+0x24, streamFile); - bits_per_channel = read_32bitBE(chunk_offset+0x28, streamFile); - - interleave = bytes_per_packet / channels_per_frame; - channel_count = channels_per_frame; - if (channels_per_frame != 1 && channels_per_frame != 2) - goto fail; - /* ima4-specific */ - if (frames_per_packet != 64) goto fail; - if ((frames_per_packet / 2 + 2) * channels_per_frame != - bytes_per_packet) goto fail; - if (bits_per_channel != 0) goto fail; + sample_double = (double*)&sample_long; + sample_rate = (int)(*sample_double); } + + codec = read_32bitBE(chunk_offset+0x08, streamFile); + //codec_flags = read_32bitBE(chunk_offset+0x0c, streamFile); + bytes_per_packet = read_32bitBE(chunk_offset+0x10, streamFile); + samples_per_packet = read_32bitBE(chunk_offset+0x14, streamFile); + channels_per_packet = read_32bitBE(chunk_offset+0x18, streamFile); + bits_per_sample = read_32bitBE(chunk_offset+0x1C, streamFile); break; - case 0x70616b74: /* pakt */ - found_pakt = 1; - /* 64-bit packet table size, 0 for constant bitrate */ - if ( - read_32bitBE(chunk_offset+0x0c,streamFile) != 0 || - read_32bitBE(chunk_offset+0x10,streamFile) != 0) goto fail; - /* high half of valid frames count */ - if (read_32bitBE(chunk_offset+0x14,streamFile) != 0) goto fail; - /* frame count */ - sample_count = read_32bitBE(chunk_offset+0x18,streamFile); - /* priming frames */ - if (read_32bitBE(chunk_offset+0x1c,streamFile) != 0) goto fail; - /* remainder (unused) frames */ - unused_frames = read_32bitBE(chunk_offset+0x20,streamFile); + + case 0x70616b74: /* "pakt" */ + //found_pakt = 1; + + //packets_table_size = (uint32_t)read_64bitBE(chunk_offset+0x00,streamFile); /* 0 for constant bitrate */ + valid_samples = (uint32_t)read_64bitBE(chunk_offset+0x08,streamFile); + //priming_samples = read_32bitBE(chunk_offset+0x10,streamFile); /* encoder delay samples */ + //unused_samples = read_32bitBE(chunk_offset+0x14,streamFile); /* footer samples */ break; - case 0x66726565: /* free */ - /* padding, ignore */ - break; - case 0x64617461: /* data */ - if (read_32bitBE(chunk_offset+12,streamFile) != 1) goto fail; + + case 0x64617461: /* "data" */ found_data = 1; - start_offset = chunk_offset + 16; - data_size = read_32bitBE(chunk_offset+8,streamFile) - 4; + + /* 0x00: version? 0x00/0x01 */ + start_offset = chunk_offset + 0x04; + data_size = chunk_size - 0x4; + break; + + default: /* "free" "kuki" "info" "chan" etc: ignore */ break; - default: - goto fail; } /* done with chunk */ - chunk_offset += 12 + read_32bitBE(chunk_offset+8,streamFile); + chunk_offset += chunk_size; } - if (!found_pakt || !found_desc || !found_data) goto fail; - if (start_offset == 0 || data_size == 0 || sample_count == 0 || - sample_rate == -1 || channel_count == 0) goto fail; + if (!found_desc || !found_data) + goto fail; + if (start_offset == 0 || data_size == 0) + goto fail; - /* ima4-specific */ - /* check for full packets */ - if (data_size % (interleave*channel_count) != 0) goto fail; - if ((sample_count+unused_frames)%((interleave-2)*2) != 0) goto fail; - /* check that all packets are accounted for */ - if (data_size/interleave/channel_count != - (sample_count+unused_frames)/((interleave-2)*2)) goto fail; - vgmstream = allocate_vgmstream(channel_count,0); + loop_flag = 0; + channel_count = channels_per_packet; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->channels = channel_count; vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = sample_count; - /* ima4-specific */ - vgmstream->coding_type = coding_APPLE_IMA4; - if (channel_count == 2) - vgmstream->layout_type = layout_interleave; - else - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = interleave; vgmstream->meta_type = meta_CAFF; - /* open the file for reading by each channel */ - { - int i; - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[i].streamfile) goto fail; + switch(codec) { + case 0x6C70636D: /* "lpcm" */ + vgmstream->num_samples = valid_samples; + if (!vgmstream->num_samples) + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample); - vgmstream->ch[i].offset = - vgmstream->ch[i].channel_start_offset = - start_offset + interleave * i; - } + //todo check codec_flags for BE/LE, signed/etc + if (bits_per_sample == 8) { + vgmstream->coding_type = coding_PCM8; + } + else { + goto fail; + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = bytes_per_packet / channels_per_packet; + + break; + + case 0x696D6134: /* "ima4" [Vectros (iOS), Dragon Quest (iOS)] */ + vgmstream->num_samples = valid_samples; + if (!vgmstream->num_samples) /* rare [Endless Fables 2 (iOS) */ + vgmstream->num_samples = apple_ima4_bytes_to_samples(data_size, channel_count); + + vgmstream->coding_type = coding_APPLE_IMA4; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = bytes_per_packet / channels_per_packet; + + /* ima4 defaults */ + //if (channels_per_packet != 1 && channels_per_packet != 2) + // goto fail; + if (samples_per_packet != 64) + goto fail; + if ((samples_per_packet / 2 + 2) * channels_per_packet != bytes_per_packet) + goto fail; + if (bits_per_sample != 0 && bits_per_sample != 4) /* 4 is rare too [Endless Fables 2 (iOS) */ + goto fail; + + /* check for full packets and that all packets are accounted for */ + //if (found_pakt) { + // if (data_size % (vgmstream->interleave_block_size*channel_count) != 0) + // goto fail; + // if ((valid_samples+unused_samples)%((vgmstream->interleave_block_size-2)*2) != 0) + // goto fail; + // if (data_size/vgmstream->interleave_block_size/channel_count != + // (valid_samples+unused_samples)/((vgmstream->interleave_block_size-2)*2)) + // goto fail; + //} + + break; + + case 0x61616320: /* "aac " [Ridge Racer Accelerated (iOS)] */ + case 0x616C6163: /* "alac" [Chrono Trigger v1 (iOS)] */ + default: /* should be parsed by FFMpeg in its meta (involves parsing complex chunks) */ + goto fail; } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; + fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ast.c b/Frameworks/vgmstream/vgmstream/src/meta/ast.c index fde881724..4ed5162c2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ast.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ast.c @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile); vgmstream->coding_type = coding_type; - vgmstream->layout_type = layout_ast_blocked; + vgmstream->layout_type = layout_blocked_ast; vgmstream->meta_type = meta_AST; /* open the file for reading by each channel */ @@ -80,7 +80,7 @@ VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { } /* start me up */ - ast_block_update(0x40,vgmstream); + block_update_ast(0x40,vgmstream); return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/atsl.c b/Frameworks/vgmstream/vgmstream/src/meta/atsl.c new file mode 100644 index 000000000..bb19f62ff --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/atsl.c @@ -0,0 +1,168 @@ +#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; + +/* .ATSL - Koei Tecmo audio container [One Piece Pirate Warriors (PS3), Warriors All-Stars (PC)] */ +VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + int total_subsongs, target_subsong = streamFile->stream_index; + int type, big_endian = 0, entries; + atsl_codec codec; + const char* fake_ext; + off_t subfile_offset = 0; + size_t subfile_size = 0, header_size; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + + + /* checks */ + /* .atsl: header id (for G1L extractions), .atsl3: PS3 games, .atsl4: PS4 games */ + if ( !check_extensions(streamFile,"atsl,atsl3,atsl4")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */ + goto fail; + + /* main header (LE) */ + header_size = read_32bitLE(0x04,streamFile); + /* 0x08/0c: flags?, 0x10: fixed? (0x03E8) */ + entries = read_32bitLE(0x14,streamFile); + /* 0x18: 0x28, or 0x30 (rarer) */ + /* 0x1c: null, 0x20: subheader size, 0x24/28: null */ + + /* Type byte may be wrong (could need header id tests instead). Example flags at 0x08/0x0c: + * - 00010101 00020001 .atsl3 from One Piece Pirate Warriors (PS3)[ATRAC3] + * - 00000201 00020001 .atsl3 from Fist of North Star: Ken's Rage 2 (PS3)[ATRAC3] + * 00000301 00020101 (same) + * - 01040301 00060301 .atsl4 from Nobunaga's Ambition: Sphere of Influence (PS4)[ATRAC9] + * - 00060301 00040301 atsl in G1L from One Piece Pirate Warriors 3 (Vita)[ATRAC9] + * - 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] + */ + type = read_8bit(0x0d, streamFile); + switch(type) { + case 0x01: + codec = KOVS; + fake_ext = "kvs"; + break; + case 0x02: + codec = ATRAC3; + fake_ext = "at3"; + big_endian = 1; + break; + case 0x04: + case 0x06: + codec = ATRAC9; + fake_ext = "at9"; + break; + case 0x08: + codec = KTSS; + fake_ext = "ktss"; + break; + default: + VGM_LOG("ATSL: unknown type %x\n", type); + goto fail; + } + read_32bit = big_endian ? read_32bitBE : read_32bitLE; + + + /* entries can point to the same file, count unique only */ + { + int i,j; + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + /* parse entry header (in machine endianness) */ + 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 */ + + /* 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) { + is_unique = 0; + break; + } + } + if (!is_unique) + continue; + + total_subsongs++; + + /* target GET, but keep going to count subsongs */ + if (!subfile_offset && target_subsong == total_subsongs) { + subfile_offset = entry_offset; + subfile_size = entry_size; + } + } + } + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + if (!subfile_offset || !subfile_size) goto fail; + + + /* some kind of seek/switch table may follow (optional, found in .atsl3) */ + + + temp_streamFile = setup_atsl_streamfile(streamFile, subfile_offset,subfile_size, fake_ext); + if (!temp_streamFile) goto fail; + + /* init the VGMSTREAM */ + switch(codec) { + case ATRAC3: + case ATRAC9: + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + break; + case KOVS: + vgmstream = init_vgmstream_ogg_vorbis(temp_streamFile); + if (!vgmstream) goto fail; + break; + case KTSS: + vgmstream = init_vgmstream_ktss(temp_streamFile); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + 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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c b/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c deleted file mode 100644 index 722c3f77f..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - -static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); - -/* .ATSL3 - Koei Tecmo container of multiple .AT3 [One Piece Pirate Warriors (PS3)] */ -VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; - int total_subsongs, target_subsong = streamFile->stream_index; - off_t subfile_offset; - size_t subfile_size, header_size, entry_size; - - /* check extensions */ - if ( !check_extensions(streamFile,"atsl3")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */ - goto fail; - - /* main header (LE) */ - header_size = read_32bitLE(0x04,streamFile); - /* 0x08/0c: flags?, 0x10: some size? */ - total_subsongs = read_32bitLE(0x14,streamFile); - entry_size = read_32bitLE(0x18,streamFile); - /* 0x1c: null, 0x20: subheader size, 0x24/28: null */ - - if (target_subsong == 0) target_subsong = 1; - if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; - - /* entry header (BE) */ - /* 0x00: id */ - subfile_offset = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x04,streamFile); - subfile_size = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x08,streamFile); - /* 0x08+: sample rate/num_samples/loop_start/etc, matching subfile header */ - /* some kind of seek/switch table follows */ - - temp_streamFile = setup_atsl3_streamfile(streamFile, subfile_offset,subfile_size); - if (!temp_streamFile) goto fail; - - /* init the VGMSTREAM */ - vgmstream = init_vgmstream_riff(temp_streamFile); - if (!vgmstream) goto fail; - vgmstream->num_streams = total_subsongs; - - close_streamfile(temp_streamFile); - return vgmstream; - -fail: - close_streamfile(temp_streamFile); - close_vgmstream(vgmstream); - return NULL; -} - - -static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { - 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,"at3"); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - return temp_streamFile; - -fail: - close_streamfile(temp_streamFile); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/atx.c b/Frameworks/vgmstream/vgmstream/src/meta/atx.c index e540baa36..bace1a298 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/atx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/atx.c @@ -49,7 +49,7 @@ static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { num_segments = read_16bitLE(0x1e,streamFile); /* expected segment name: X_XXX_XXX_0n.ATX, starting from n=1 */ - get_streamfile_name(streamFile, filename,PATH_LIMIT); + get_streamfile_filename(streamFile, filename,PATH_LIMIT); filename_len = strlen(filename); if (filename_len < 7 || filename[filename_len - 5] != '1') goto fail; @@ -59,7 +59,7 @@ static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { size_t subfile_size; filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */ - new_streamFile = open_stream_name(streamFile, filename); + new_streamFile = open_streamfile_by_filename(streamFile, filename); if (!new_streamFile) goto fail; segment_streamFiles[i] = new_streamFile; @@ -96,12 +96,16 @@ static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; + /* if all this worked we'll have this frankenstein streamfile: + * fakename( clamp( multifile( segment0=clamp(standard(FILE)), segment1=clamp(standard(FILE)) ) ) ) */ + return temp_streamFile; fail: if (!temp_streamFile) { - for (i = 0; i < num_segments; i++) + for (i = 0; i < num_segments; i++) { close_streamfile(segment_streamFiles[i]); + } } else { close_streamfile(temp_streamFile); /* closes all segments */ } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index ab73b6cdd..1994613a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" +#include "awc_xma_streamfile.h" typedef struct { int big_endian; @@ -17,7 +18,7 @@ typedef struct { int block_chunk; off_t stream_offset; - off_t stream_size; + size_t stream_size; } awc_header; @@ -64,6 +65,76 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { vgmstream->codec_endian = awc.big_endian; break; +#ifdef VGM_USE_FFMPEG + case 0x05: { /* XMA2 (X360) */ + uint8_t buf[0x100]; + size_t bytes, block_size, block_count, substream_size; + off_t substream_offset; + + if (awc.is_music) { + /* 1ch XMAs in blocks, we'll use layered layout + custom IO to get multi-FFmpegs working */ + int i; + layered_layout_data * data = NULL; + + /* init layout */ + data = init_layout_layered(awc.channel_count); + if (!data) goto fail; + vgmstream->layout_data = data; + vgmstream->layout_type = layout_layered; + vgmstream->coding_type = coding_FFmpeg; + + /* open each layer subfile */ + for (i = 0; i < awc.channel_count; i++) { + STREAMFILE* temp_streamFile; + int layer_channels = 1; + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, 0); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = awc.sample_rate; + data->layers[i]->meta_type = meta_AWC; + data->layers[i]->coding_type = coding_FFmpeg; + data->layers[i]->layout_type = layout_none; + data->layers[i]->num_samples = awc.num_samples; + + /* setup custom IO streamfile, pass to FFmpeg and hope it's fooled */ + temp_streamFile = setup_awc_xma_streamfile(streamFile, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channel_count, i); + if (!temp_streamFile) goto fail; + + substream_offset = 0; /* where FFmpeg thinks data starts, which our custom streamFile will clamp */ + substream_size = get_streamfile_size(temp_streamFile); /* data of one XMA substream without blocks */ + block_size = 0x8000; /* no idea */ + block_count = substream_size / block_size; /* not accurate but not needed */ + + 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); + + close_streamfile(temp_streamFile); + if (!data->layers[i]->codec_data) goto fail; + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + } + else { + /* regular XMA for sfx */ + block_size = 0x8000; /* no idea */ + block_count = awc.stream_size / block_size; /* not accurate but not needed */ + + bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, awc.stream_size, awc.channel_count, awc.sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, awc.stream_offset,awc.stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + } + + break; + } + + +#endif #ifdef VGM_USE_MPEG case 0x07: { /* MPEG (PS3) */ mpeg_custom_config cfg; @@ -80,7 +151,6 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { } #endif - case 0x05: /* XMA2 (X360) */ default: VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec); goto fail; @@ -187,12 +257,15 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { /* get stream tags */ for (i = 0; i < tag_count; i++) { - uint64_t tag_header, tag, size, offset; + uint64_t tag_header; + uint8_t tag; + size_t size; + off_t offset; tag_header = (uint64_t)read_64bit(off + 0x08*i,streamFile); - tag = (tag_header >> 56) & 0xFF; /* 8b */ - size = (tag_header >> 28) & 0x0FFFFFFF; /* 28b */ - offset = (tag_header >> 0) & 0x0FFFFFFF; /* 28b */ + tag = (uint8_t)((tag_header >> 56) & 0xFF); /* 8b */ + size = (size_t)((tag_header >> 28) & 0x0FFFFFFF); /* 28b */ + offset = (off_t)((tag_header >> 0) & 0x0FFFFFFF); /* 28b */ /* Tags are apparently part of a hash derived from a word ("data", "format", etc). * If music + 1ch, the header and data chunks can repeat for no reason (sometimes not even pointed). */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h new file mode 100644 index 000000000..f4c231c7a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h @@ -0,0 +1,257 @@ +#ifndef _AWC_XMA_STREAMFILE_H_ +#define _AWC_XMA_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + off_t stream_offset; + size_t stream_size; + int channel_count; + int channel; + size_t chunk_size; + + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + off_t next_block_offset; /* physical offset of the next block start */ + off_t last_offset; /* physical offset of where the last block ended */ + size_t current_data_size; + size_t current_consumed_size; + + size_t total_size; /* size of the resulting XMA data */ +} awc_xma_io_data; + + +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xma_io_data *data); +static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset); +static size_t get_block_skip_count(STREAMFILE *streamFile, off_t offset, int channel); + +/* Reads plain XMA data of a single stream. Each block has a header and channels have different num_samples/frames. + * Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes + * the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). */ +static size_t awc_xma_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, awc_xma_io_data* data) { + size_t total_read = 0; + size_t frame_size = 0x800; + + /* ignore bad reads */ + if (offset < 0 || offset > data->total_size) { + return 0; + } + + /* previous offset: re-start as we can't map logical<>physical offsets + * (kinda slow as it trashes buffers, but shouldn't happen often) */ + if (offset < data->logical_offset) { + data->logical_offset = 0x00; + data->physical_offset = data->stream_offset; + data->next_block_offset = 0; + data->last_offset = 0; + data->current_data_size = 0; + data->current_consumed_size = 0; + } + + /* read from block, moving to next when all data is consumed */ + while (length > 0) { + size_t to_read, bytes_read; + + /* new block */ + if (data->current_data_size == 0) { + size_t header_size = get_block_header_size(streamfile, data->physical_offset, data); + /* header table entries = frames... I hope */ + size_t skip_size = get_block_skip_count(streamfile, data->physical_offset, data->channel) * frame_size; + //size_t skip_size = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x00, streamfile) * frame_size; + size_t data_size = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x04, streamfile) * frame_size; + size_t repeat_samples = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x08, streamfile); + size_t repeat_size = 0; + + /* if there are repeat samples current block repeats some frames from last block, find out size */ + if (repeat_samples && data->last_offset) { + off_t data_offset = data->physical_offset + header_size + skip_size; + repeat_size = get_repeated_data_size(streamfile, data_offset, data->last_offset); + } + + data->next_block_offset = data->physical_offset + data->chunk_size; + data->physical_offset += header_size + skip_size + repeat_size; /* data start */ + data->current_data_size = data_size - repeat_size; /* readable max in this block */ + data->current_consumed_size = 0; + continue; + } + + /* block end, go next */ + if (data->current_consumed_size == data->current_data_size) { + data->last_offset = data->physical_offset; /* where last block ended */ + data->physical_offset = data->next_block_offset; + data->current_data_size = 0; + continue; + } + + /* requested offset is further along, pretend we consumed data and try again */ + if (offset > data->logical_offset) { + size_t to_consume = offset - data->logical_offset; + if (to_consume > data->current_data_size - data->current_consumed_size) + to_consume = data->current_data_size - data->current_consumed_size; + + data->physical_offset += to_consume; + data->logical_offset += to_consume; + data->current_consumed_size += to_consume; + continue; + } + + /* clamp reads up to this block's end */ + to_read = (data->current_data_size - data->current_consumed_size); + if (to_read > length) + to_read = length; + if (to_read == 0) + return total_read; /* should never happen... */ + + /* finally read and move buffer/offsets */ + bytes_read = read_streamfile(dest, data->physical_offset, to_read, streamfile); + total_read += bytes_read; + if (bytes_read != to_read) + return total_read; /* couldn't read fully */ + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + + data->physical_offset += bytes_read; + data->logical_offset += bytes_read; + data->current_consumed_size += bytes_read; + } + + return total_read; +} + +static size_t awc_xma_io_size(STREAMFILE *streamfile, awc_xma_io_data* data) { + off_t physical_offset, max_physical_offset, last_offset; + size_t frame_size = 0x800; + size_t total_size = 0; + + if (data->total_size) + return data->total_size; + + physical_offset = data->stream_offset; + max_physical_offset = data->stream_offset + data->stream_size; + last_offset = 0; + + /* read blocks and sum final size */ + while (physical_offset < max_physical_offset) { + size_t header_size = get_block_header_size(streamfile, physical_offset, data); + /* header table entries = frames... I hope */ + size_t skip_size = get_block_skip_count(streamfile, physical_offset, data->channel) * frame_size; + //size_t skip_size = read_32bitBE(physical_offset + 0x10*data->channel + 0x00, streamfile) * frame_size; + size_t data_size = read_32bitBE(physical_offset + 0x10*data->channel + 0x04, streamfile) * frame_size; + size_t repeat_samples = read_32bitBE(physical_offset + 0x10*data->channel + 0x08, streamfile); + size_t repeat_size = 0; + + /* if there are repeat samples current block repeats some frames from last block, find out size */ + if (repeat_samples && last_offset) { + off_t data_offset = physical_offset + header_size + skip_size; + repeat_size = get_repeated_data_size(streamfile, data_offset, last_offset); + } + + last_offset = physical_offset + header_size + skip_size + data_size; + total_size += data_size - repeat_size; + physical_offset += data->chunk_size; + } + + data->total_size = total_size; + return data->total_size; +} + + +/* Prepares custom IO for AWC XMA, which is interleaved XMA in AWC blocks */ +static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, size_t chunk_size, int channel_count, int channel) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + awc_xma_io_data io_data = {0}; + size_t io_data_size = sizeof(awc_xma_io_data); + + io_data.stream_offset = stream_offset; + io_data.stream_size = stream_size; + io_data.chunk_size = chunk_size; + io_data.channel_count = channel_count; + io_data.channel = channel; + io_data.physical_offset = stream_offset; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, awc_xma_io_read,awc_xma_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + //todo maybe should force to read filesize once + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +/* block header size, aligned/padded to 0x800 */ +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xma_io_data *data) { + size_t header_size = 0; + int i; + int entries = data->channel_count; + + for (i = 0; i < entries; i++) { + header_size += 0x10; + header_size += read_32bitBE(offset + 0x10*i + 0x04, streamFile) * 0x04; /* entries in the table */ + } + + if (header_size % 0x800) /* padded */ + header_size += 0x800 - (header_size % 0x800); + + return header_size; +} + + +/* find data that repeats in the beginning of a new block at the end of last block */ +static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset) { + uint8_t new_frame[0x800];/* buffer to avoid fseek back and forth */ + size_t frame_size = 0x800; + off_t offset; + int i; + + /* read block first frame */ + if (read_streamfile(new_frame,new_offset, frame_size,streamFile) != frame_size) + goto fail; + + /* find the frame in last bytes of prev block */ + offset = last_offset - 0x4000; /* typical max is 1 frame of ~0x800, no way to know exact size */ + while (offset < last_offset) { + /* compare frame vs prev block data */ + for (i = 0; i < frame_size; i++) { + if ((uint8_t)read_8bit(offset+i,streamFile) != new_frame[i]) + break; + } + + /* frame fully compared? */ + if (i == frame_size) + return last_offset - offset; + else + offset += i+1; + } + +fail: + VGM_LOG("AWC: can't find repeat size, new=0x%08lx, last=0x%08lx\n", new_offset, last_offset); + return 0; /* keep on truckin' */ +} + +/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */ +static size_t get_block_skip_count(STREAMFILE *streamFile, off_t offset, int channel) { + size_t skip_count = 0; + int i; + + //skip_size = read_32bitBE(offset + 0x10*channel + 0x00, streamFile); /* wrong! */ + for (i = 0; i < channel; i++) { + skip_count += read_32bitBE(offset + 0x10*i + 0x04, streamFile); /* number of frames of this channel */ + } + + return skip_count; +} + + +#endif /* _AWC_XMA_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h index 81d43befc..fbe455952 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h @@ -41,11 +41,7 @@ static size_t get_offset_bar(BARSTREAMFILE *streamFile) { } static void get_name_bar(BARSTREAMFILE *streamFile, char *name, size_t length) { - return streamFile->real_file->get_name(streamFile->real_file, name, length); -} - -static void get_realname_bar(BARSTREAMFILE *streamFile, char *name, size_t length) { - return streamFile->real_file->get_realname(streamFile->real_file, name, length); + streamFile->real_file->get_name(streamFile->real_file, name, length); } STREAMFILE *open_bar(BARSTREAMFILE *streamFile, const char * const filename, size_t buffersize) { @@ -75,7 +71,6 @@ static void close_bar(BARSTREAMFILE *streamFile) { streamfile->sf.get_size = (void*)get_size_bar; streamfile->sf.get_offset = (void*)get_offset_bar; streamfile->sf.get_name = (void*)get_name_bar; - streamfile->sf.get_realname = (void*)get_realname_bar; streamfile->sf.open = (void*)open_bar; streamfile->sf.close = (void*)close_bar; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c b/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c index 2d8c83659..0b5d89ef4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c @@ -112,22 +112,14 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { } vgmstream->coding_type = coding_type; - if (channel_count == 1) - vgmstream->layout_type = layout_none; - else - { - if (ima) - vgmstream->layout_type = layout_interleave; - else - vgmstream->layout_type = layout_interleave_shortblock; - } + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; vgmstream->meta_type = meta_CSTM; - if (ima) + if (ima) { vgmstream->interleave_block_size = 0x200; - else { + } else { vgmstream->interleave_block_size = read_32bitLE(info_offset + 0x34, streamFile); - vgmstream->interleave_smallblock_size = read_32bitLE(info_offset + 0x44, streamFile); + vgmstream->interleave_last_block_size = read_32bitLE(info_offset + 0x44, streamFile); } if (vgmstream->coding_type == coding_NGC_DSP) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c b/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c index 6e86c22a6..19a2bb902 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c @@ -121,22 +121,14 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { } vgmstream->coding_type = coding_type; - if (channel_count == 1) - vgmstream->layout_type = layout_none; - else - { - if (ima) - vgmstream->layout_type = layout_interleave; - else - vgmstream->layout_type = layout_interleave_shortblock; - } + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; vgmstream->meta_type = meta_FSTM; if (ima) vgmstream->interleave_block_size = 0x200; else { vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile); - vgmstream->interleave_smallblock_size = read_32bit(info_offset + 0x44, streamFile); + vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, streamFile); } if (vgmstream->coding_type == coding_NGC_DSP) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c index acf938599..734fdc0c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c @@ -1,150 +1,133 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +/* FWAV - Nintendo streams */ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - coding_t coding_type; - coding_t coding_PCM16; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - - /*int ima = 0;*/ - int nsmbu_flag = 0; - - off_t data_offset; - off_t head_offset; - int codec_number; - int channel_count; - int loop_flag; - - off_t start_offset; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile, filename, sizeof(filename)); - if (strcasecmp("bfwav", filename_extension(filename)) && strcasecmp("fwav", filename_extension(filename))) { - if (strcasecmp("bfwavnsmbu",filename_extension(filename))) goto fail; - else nsmbu_flag = 1; - } - /* check header */ - if ((uint32_t)read_32bitBE(0, streamFile) != 0x46574156) /* "FWAV" */ - goto fail; - - if ((uint16_t)read_16bitBE(4, streamFile) == 0xFEFF) { /* endian marker (BE most common) */ - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - coding_PCM16 = coding_PCM16BE; - } else if ((uint16_t)read_16bitBE(4, streamFile) == 0xFFFE) { /* LE endian marker */ - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - coding_PCM16 = coding_PCM16LE; - } else { - goto fail; - } - - /* get head offset, check */ - head_offset = read_32bit(0x18, streamFile); - data_offset = read_32bit(0x24, streamFile); - - if ((uint32_t)read_32bitBE(head_offset, streamFile) != 0x494E464F) /* "INFO" (FWAV)*/ - goto fail; - - - /* check type details */ - codec_number = read_8bit(head_offset + 0x8, streamFile); - loop_flag = read_8bit(head_offset + 0x9, streamFile); - channel_count = read_32bit(head_offset + 0x1C, streamFile); - - switch (codec_number) { - case 0: - coding_type = coding_PCM8; - break; - case 1: - coding_type = coding_PCM16; - break; - case 2: - coding_type = coding_NGC_DSP; - break; - default: - goto fail; - } - - if (channel_count < 1) goto fail; - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bit(head_offset + 0x14, streamFile); - if (nsmbu_flag) - vgmstream->sample_rate = 16000; - else - vgmstream->sample_rate = (uint16_t)read_32bit(head_offset + 0xC, streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - - vgmstream->loop_start_sample = read_32bit(head_offset + 0x10, streamFile); - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->coding_type = coding_type; - if (channel_count == 1) - vgmstream->layout_type = layout_none; - else - vgmstream->layout_type = layout_interleave; + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + off_t info_offset, data_offset; + int channel_count, loop_flag, codec; + int big_endian; + size_t interleave = 0; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + int nsmbu_flag = 0; - vgmstream->meta_type = meta_FWAV; + /* checks */ + /* .bfwavnsmbu: fake extension to detect New Super Mario Bros U files with weird sample rate */ + if (!check_extensions(streamFile, "bfwav,fwav,bfwavnsmbu")) + goto fail; + nsmbu_flag = check_extensions(streamFile, "bfwavnsmbu"); - vgmstream->interleave_block_size = read_32bit(read_32bit(0x6c, streamFile) + 0x60, streamFile) - 0x18; - + /* FWAV header */ + if (read_32bitBE(0x00, streamFile) != 0x46574156) /* "FWAV" */ + goto fail; + /* 0x06(2): header size (0x40), 0x08: version (0x00010200), 0x0c: file size 0x10(2): sections (2) */ - start_offset = data_offset + 0x20; + if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFEFF) { /* BE BOM check */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + big_endian = 1; + } else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* LE BOM check */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + big_endian = 0; + } else { + goto fail; + } - if (vgmstream->coding_type == coding_NGC_DSP) { - int i, j; + info_offset = read_32bit(0x18, streamFile); /* 0x14(2): info mark (0x7000), 0x1c: info size */ + data_offset = read_32bit(0x24, streamFile); /* 0x20(2): data mark (0x7001), 0x28: data size */ - for (j = 0; jchannels; j++) { - for (i = 0; i<16; i++) { - off_t coeffheader = head_offset + 0x1C + read_32bit(head_offset + 0x24 + (j*8), streamFile); - off_t coef_offset; - if ((uint32_t)read_16bit(coeffheader, streamFile) != 0x1F00) goto fail; + /* INFO section */ + if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ + goto fail; + codec = read_8bit(info_offset + 0x08, streamFile); + loop_flag = read_8bit(info_offset + 0x09, streamFile); + channel_count = read_32bit(info_offset + 0x1C, streamFile); - coef_offset = read_32bit(coeffheader + 0xC, streamFile) + coeffheader; - vgmstream->ch[j].adpcm_coef[i] = read_16bit(coef_offset + i * 2, streamFile); - } - } - } + /* parse channel table */ + { + off_t channel1_info, data_start; + int i; + + channel1_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*0+0x04, streamFile); + data_start = read_32bit(channel1_info+0x04, streamFile); /* within "DATA" after 0x08 */ + + /* channels use absolute offsets but should be ok as interleave */ + interleave = 0; + if (channel_count > 1) { + off_t channel2_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*1+0x04, streamFile); + interleave = read_32bit(channel2_info+0x04, streamFile) - data_start; + } + + start_offset = data_offset + 0x08 + data_start; + + /* validate all channels just in case of multichannel with non-constant interleave */ + for (i = 0; i < channel_count; i++) { + /* channel table, 0x00: flag (0x7100), 0x04: channel info offset */ + off_t channel_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*i+0x04, streamFile); + /* channel info, 0x00(2): flag (0x1f00), 0x04: offset, 0x08(2): ADPCM flag (0x0300), 0x0c: ADPCM offset */ + if ((uint16_t)read_16bit(channel_info+0x00, streamFile) != 0x1F00) + goto fail; + if (read_32bit(channel_info+0x04, streamFile) != data_start + interleave*i) + goto fail; + } + } + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; - /* open the file for reading by each channel */ - { - int i; - for (i = 0; ilayout_type == layout_interleave_shortblock) - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - vgmstream->interleave_block_size); - else if (vgmstream->layout_type == layout_interleave) - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - else - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - 0x1000); + vgmstream->sample_rate = read_32bit(info_offset + 0x0C, streamFile); + if (nsmbu_flag) + vgmstream->sample_rate = 16000; - if (!vgmstream->ch[i].streamfile) goto fail; + vgmstream->num_samples = read_32bit(info_offset + 0x14, streamFile); + vgmstream->loop_start_sample = read_32bit(info_offset + 0x10, streamFile); + vgmstream->loop_end_sample = vgmstream->num_samples; - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = - start_offset + i*vgmstream->interleave_block_size; - } - } + vgmstream->meta_type = meta_FWAV; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = interleave; - return vgmstream; + switch (codec) { + case 0x00: + vgmstream->coding_type = coding_PCM8; + break; + + case 0x01: + vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; + break; + + case 0x02: + vgmstream->coding_type = coding_NGC_DSP; + { + int i, c; + off_t coef_header, coef_offset; + + for (i = 0; i < vgmstream->channels; i++) { + for (c = 0; c < 16; c++) { + coef_header = info_offset + 0x1C + read_32bit(info_offset + 0x24 + (i*0x08), streamFile); + coef_offset = read_32bit(coef_header + 0x0c, streamFile) + coef_header; + vgmstream->ch[i].adpcm_coef[c] = read_16bit(coef_offset + c*2, streamFile); + } + } + } + break; + + default: /* 0x03: IMA? */ + 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); - return NULL; + close_vgmstream(vgmstream); + return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c index 0be6ebabc..601d7cbdd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c @@ -244,7 +244,7 @@ static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t sub if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read); + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read,NULL); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/brstm.c b/Frameworks/vgmstream/vgmstream/src/meta/brstm.c index 249999977..960c21b37 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/brstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/brstm.c @@ -91,10 +91,7 @@ VGMSTREAM * init_vgmstream_brstm(STREAMFILE *streamFile) { vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->coding_type = coding_type; - if (channel_count==1) - vgmstream->layout_type = layout_none; - else - vgmstream->layout_type = layout_interleave_shortblock; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; vgmstream->meta_type = meta_RSTM; if (atlus_shrunken_head) vgmstream->meta_type = meta_RSTM_shrunken; @@ -105,7 +102,7 @@ VGMSTREAM * init_vgmstream_brstm(STREAMFILE *streamFile) { } vgmstream->interleave_block_size = read_32bitBE(head_offset+0x38,streamFile); - vgmstream->interleave_smallblock_size = read_32bitBE(head_offset+0x48,streamFile); + vgmstream->interleave_last_block_size = read_32bitBE(head_offset+0x48,streamFile); if (vgmstream->coding_type == coding_NGC_DSP) { off_t coef_offset; @@ -139,12 +136,7 @@ VGMSTREAM * init_vgmstream_brstm(STREAMFILE *streamFile) { { int i; for (i=0;ilayout_type==layout_interleave_shortblock) - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename, - vgmstream->interleave_block_size); - else - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename, - 0x1000); + vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[i].streamfile) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/caf.c b/Frameworks/vgmstream/vgmstream/src/meta/caf.c new file mode 100644 index 000000000..8180fe8ea --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/caf.c @@ -0,0 +1,65 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../util.h" + +/* CAF - from tri-Crescendo games [Baten Kaitos 1/2 (GC), Fragile (Wii)] */ +VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, offset; + size_t file_size; + int channel_count, loop_flag; + int32_t num_samples = 0; + uint32_t loop_start = -1; + + + /* checks */ + /* .caf: header id, .cfn: fake extension? , "" is accepted as files don't have extensions in the disc */ + if (!check_extensions(streamFile,"caf,cfn,")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x43414620) /* "CAF " */ + goto fail; + + /* get total samples */ + offset = 0; + file_size = get_streamfile_size(streamFile); + while (offset < file_size) { + off_t next_block = read_32bitBE(offset+0x04,streamFile); + num_samples += read_32bitBE(offset+0x14,streamFile)/8*14; + + if(read_32bitBE(offset+0x20,streamFile)==read_32bitBE(offset+0x08,streamFile)) { + loop_start = num_samples - read_32bitBE(offset+0x14,streamFile)/8*14; + } + offset += next_block; + } + + start_offset = 0x00; + channel_count = 2; /* always stereo */ + loop_flag = (loop_start!=-1); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = 32000; + vgmstream->num_samples = num_samples; + if (loop_flag) { + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = num_samples; + } + + vgmstream->meta_type = meta_CAF; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_blocked_caf; + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + goto fail; + + block_update_caf(start_offset,vgmstream); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dc_dcsw_dcs.c b/Frameworks/vgmstream/vgmstream/src/meta/dc_dcsw_dcs.c index 23105fd07..d1710560a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dc_dcsw_dcs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dc_dcsw_dcs.c @@ -92,7 +92,7 @@ VGMSTREAM * init_vgmstream_dc_dcsw_dcs(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x4000; } - vgmstream->coding_type = coding_AICA; + vgmstream->coding_type = coding_AICA_int; vgmstream->meta_type = meta_DC_DCSW_DCS; /* open the file for reading by each channel */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dc_idvi.c b/Frameworks/vgmstream/vgmstream/src/meta/dc_idvi.c index 0200f8cd3..4498204a8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dc_idvi.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dc_idvi.c @@ -5,9 +5,12 @@ VGMSTREAM * init_vgmstream_dc_idvi(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; + size_t data_size; int loop_flag, channel_count; - /* check extension (.dvi: original, .idvi: renamed to header id) */ + + /* checks*/ + /* .dvi: original, .idvi: renamed to header id */ if ( !check_extensions(streamFile,"dvi,idvi") ) goto fail; @@ -18,28 +21,24 @@ VGMSTREAM * init_vgmstream_dc_idvi(STREAMFILE *streamFile) { loop_flag = (read_32bitLE(0x0C,streamFile) != 0); channel_count = read_32bitLE(0x04,streamFile); /* always 2? */ start_offset = 0x800; + data_size = get_streamfile_size(streamFile) - start_offset; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->channels = channel_count; + vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->num_samples = ima_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count); + vgmstream->num_samples = ima_bytes_to_samples(data_size, channel_count); vgmstream->loop_start_sample = read_32bitLE(0x0C,streamFile); - vgmstream->loop_end_sample = ima_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; - vgmstream->coding_type = coding_DVI_IMA_int; vgmstream->meta_type = meta_DC_IDVI; - - /* Calculating the short block... */ - if (channel_count > 1) { - vgmstream->interleave_block_size = 0x400; - vgmstream->interleave_smallblock_size = ((get_streamfile_size(streamFile)-start_offset)%(vgmstream->channels*vgmstream->interleave_block_size))/vgmstream->channels; - vgmstream->layout_type = layout_interleave_shortblock; - } else { - vgmstream->layout_type = layout_none; - } + vgmstream->coding_type = coding_DVI_IMA_int; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x400; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dc_str.c b/Frameworks/vgmstream/vgmstream/src/meta/dc_str.c index 9bac3c362..d9be078db 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dc_str.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dc_str.c @@ -39,7 +39,7 @@ VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile) { /* fill in the vital statistics */ switch (samples) { case 4: - vgmstream->coding_type = coding_AICA; + vgmstream->coding_type = coding_AICA_int; vgmstream->num_samples = read_32bitLE(0x14,streamFile); if (loop_flag) { vgmstream->loop_start_sample = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dec.c b/Frameworks/vgmstream/vgmstream/src/meta/dec.c index 0d562b7a4..43a3fe18e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dec.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dec.c @@ -103,23 +103,23 @@ static int get_falcom_looping(STREAMFILE *streamFile, int *out_loop_start, int * /* try one of the many loop files */ - if ((streamText = open_stream_name(streamFile,"bgm.tbl")) != NULL) { + if ((streamText = open_streamfile_by_filename(streamFile,"bgm.tbl")) != NULL) { type = XANADU_NEXT; } - else if ((streamText = open_stream_name(streamFile,"bgm.scr")) != NULL) { + else if ((streamText = open_streamfile_by_filename(streamFile,"bgm.scr")) != NULL) { type = ZWEI; } - else if ((streamText = open_stream_name(streamFile,"loop.txt")) != NULL) { /* actual name in Shift JIS, 0x838B815B8376 */ + else if ((streamText = open_streamfile_by_filename(streamFile,"loop.txt")) != NULL) { /* actual name in Shift JIS, 0x838B815B8376 */ type = DINOSAUR_RESURRECTION; } - else if ((streamText = open_stream_name(streamFile,"map.itm")) != NULL) { + else if ((streamText = open_streamfile_by_filename(streamFile,"map.itm")) != NULL) { type = GURUMIN; } else { goto end; } - get_streamfile_name(streamFile,filename,TXT_LINE_MAX); + get_streamfile_filename(streamFile,filename,TXT_LINE_MAX); /* read line by line */ while (txt_offset < get_streamfile_size(streamText)) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dsp_bdsp.c b/Frameworks/vgmstream/vgmstream/src/meta/dsp_bdsp.c index d71b7141b..cd39bf622 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dsp_bdsp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dsp_bdsp.c @@ -35,7 +35,7 @@ VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE *streamFile) { #endif - vgmstream->layout_type = layout_dsp_bdsp_blocked; + vgmstream->layout_type = layout_blocked_bdsp; vgmstream->interleave_block_size = 0x8; vgmstream->meta_type = meta_DSP_BDSP; @@ -64,17 +64,17 @@ VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE *streamFile) { /* Calc num_samples */ start_offset = 0x0; - dsp_bdsp_block_update(start_offset,vgmstream); + block_update_bdsp(start_offset,vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size*14/8; - dsp_bdsp_block_update(vgmstream->next_block_offset,vgmstream); + block_update_bdsp(vgmstream->next_block_offset,vgmstream); } while (vgmstream->next_block_offset get_streamfile_size(streamFile)) + max_offset = get_streamfile_size(streamFile); + + /* find .sps start block */ + while (sps_offset < max_offset) { + if ((read_32bitBE(sps_offset, streamFile) & 0xFFFFFF00) == 0x48000000) { + header_offset = sps_offset + 0x04; + start_offset = sps_offset + (read_32bitBE(sps_offset, streamFile) & 0x00FFFFFF); + break; + } + sps_offset += 0x04; + } + + if (!start_offset) + goto fail; /* not found */ + + vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, header_offset, start_offset, meta_EA_SPS); + if (!vgmstream) goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} /* EA newest header from RwAudioCore (RenderWare?) / EAAudioCore library (still generated by sx.exe). * Audio "assets" come in separate RAM headers (.SNR/SPH) and raw blocked streams (.SNS/SPS), @@ -114,28 +157,30 @@ fail: * Some .SNR include stream data, while .SPS have headers so .SPH is optional. */ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type) { VGMSTREAM * vgmstream = NULL; + STREAMFILE* temp_streamFile = NULL; int channel_count, loop_flag = 0, version, codec, channel_config, sample_rate, flags; uint32_t num_samples, loop_start = 0, loop_end = 0; /* EA SNR/SPH header */ - version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0xf; - codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0xf; - channel_config = read_8bit(header_offset + 0x01,streamHead); - sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamHead); - flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead); /* upper nibble only? */ - num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x00FFFFFF; + version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0x0F; + codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0x0F; + channel_config = read_8bit(header_offset + 0x01,streamHead) & 0xFE; + sample_rate = read_32bitBE(header_offset + 0x00,streamHead) & 0x1FFFF; /* some Dead Space 2 (PC) uses 96000 */ + flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead) & 0xFE; //todo upper nibble only? (the first bit is part of size) + num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x01FFFFFF; /* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers): * &0x20: 1 int (usually 0x00), &0x00/40: nothing, &0x60: 2 ints (usually 0x00 and 0x14) */ - /* V0: SNR+SNS, V1: SPR+SPS (not apparent differences) */ + /* V0: SNR+SNS, V1: SPR+SPS (not apparent differences, other than the block flags used) */ if (version != 0 && version != 1) { VGM_LOG("EA SNS/SPS: unknown version\n"); goto fail; } - /* 0x40: stream asset, 0x20: full loop, 0x00: default/RAM asset, 0x01: loop? */ + /* 0x40: stream asset, 0x20: full loop, 0x00: default/RAM asset */ if (flags != 0x60 && flags != 0x40 && flags != 0x20 && flags != 0x00) { VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags); + goto fail; } /* seen in sfx and Dead Space ambient tracks */ @@ -216,14 +261,20 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST case 0x06: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */ case 0x07: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */ mpeg_custom_config cfg = {0}; - off_t mpeg_start_offset = start_offset + 0x08; mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S); - /* layout is still blocks, but should work fine with the custom mpeg decoder */ - vgmstream->codec_data = init_mpeg_custom(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg); - if (!vgmstream->codec_data) goto fail; + /* remove blocks on reads for some edge cases in L32P and to properly apply discard modes + * (otherwise, and removing discards, it'd work with layout_blocked_ea_sns) */ + temp_streamFile = setup_eaac_streamfile(streamData, version, codec, start_offset, 0); + if (!temp_streamFile) goto fail; + + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + + /* layout is still blocks, but should work fine with the custom mpeg decoder */ + vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; - vgmstream->layout_type = layout_blocked_ea_sns; break; } #endif @@ -234,22 +285,28 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST /* DSP coefs are read in the blocks */ break; -#if 0 //todo buffered ATRAC9 #ifdef VGM_USE_ATRAC9 case 0x0a: { /* EATrax */ atrac9_config cfg = {0}; + size_t total_size; cfg.channels = vgmstream->channels; cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead); - /* 0x0c: data size without blocks?, 0x10: frame size? (same as config data?) */ + /* 0x10: frame size? (same as config data?) */ + total_size = read_32bitLE(header_offset + 0x0c,streamHead); /* actual data size without blocks, LE b/c why make sense */ vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_blocked_ea_sns; + vgmstream->layout_type = layout_none; + + /* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */ + temp_streamFile = setup_eaac_streamfile(streamData, version, codec, start_offset, total_size); + if (!temp_streamFile) goto fail; + + start_offset = 0x00; /* must point to the custom streamfile's beginning */ break; } -#endif #endif case 0x00: /* "NONE" (internal 'codec not set' flag) */ @@ -266,16 +323,17 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } - /* open the file for reading by each channel */ - if (!vgmstream_open_stream(vgmstream,streamData,start_offset)) + if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamData,start_offset)) goto fail; if (vgmstream->layout_type == layout_blocked_ea_sns) block_update_ea_sns(start_offset, vgmstream); + close_streamfile(temp_streamFile); return vgmstream; fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h new file mode 100644 index 000000000..4ecb24bfb --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h @@ -0,0 +1,218 @@ +#ifndef _EA_EAAC_STREAMFILE_H_ +#define _EA_EAAC_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + + /* config */ + int version; + int codec; + off_t start_offset; + size_t total_size; /* size of the resulting substream */ +} eaac_io_data; + + +/* Reads skipping EA's block headers, so the resulting data is smaller or larger than physical data. + * physical/logical_offset should always be at the start of a block and only advance when a block is fully done */ +static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, eaac_io_data* data) { + size_t total_read = 0; + + /* ignore bad reads */ + if (offset < 0 || offset > data->total_size) { + return total_read; + } + + /* previous offset: re-start as we can't map logical<>physical offsets + * (kinda slow as it trashes buffers, but shouldn't happen often) */ + if (offset < data->logical_offset) { + data->physical_offset = data->start_offset; + data->logical_offset = 0x00; + } + + /* read doing one EA block at a time */ + while (length > 0) { + size_t to_read, bytes_read; + off_t intrablock_offset, intradata_offset; + uint32_t block_flag, block_size, data_size, skip_size; + + block_flag = read_8bit(data->physical_offset+0x00,streamfile); + block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF; + + if (data->version == 1 && block_flag == 0x48) { + data->physical_offset += block_size; + continue; /* skip header block */ + } + if (data->version == 1 && block_flag == 0x45) + return total_read; /* stop on last block (always empty) */ + + switch(data->codec) { +#if 0 + case 0x03: + data_size = block_size - ???; + extra_size = (data_size % 0x800); /* deflated padding */ + + + skip_size = 0x08 + 0x04*data->stream_count; + break; +#endif + + case 0x05: /* EALayer3 v1 */ + case 0x06: /* EALayer3 v2 "PCM" */ + case 0x07: /* EALayer3 v2 "Spike" */ + data_size = block_size - 0x08; + skip_size = 0x08; + break; + + case 0x0a: /* EATrax */ + data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* should be block_size - 0x08 */ + skip_size = 0x08; + break; + + default: + return total_read; + } + + /* requested offset is outside current block, try next */ + if (offset >= data->logical_offset + data_size) { + data->physical_offset += block_size; + data->logical_offset += data_size; + continue; + } + + /* reads could fall in the middle of the block */ + intradata_offset = offset - data->logical_offset; + intrablock_offset = skip_size + intradata_offset; + + /* clamp reads up to this block's end */ + to_read = (data_size - intradata_offset); + if (to_read > length) + to_read = length; + if (to_read == 0) + return total_read; /* should never happen... */ + + /* finally read and move buffer/offsets */ + bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile); + total_read += bytes_read; + if (bytes_read != to_read) + return total_read; /* couldn't read fully */ + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + + /* block fully read, go next */ + if (intradata_offset + bytes_read == data_size) { + data->physical_offset += block_size; + data->logical_offset += data_size; + } + + if (data->version == 0 && block_flag == 0x80) + break; /* stop on last block */ + } + + return total_read; +} + +static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { + off_t physical_offset, max_physical_offset; + size_t total_size = 0; + + if (data->total_size) + return data->total_size; + + physical_offset = data->start_offset; + max_physical_offset = get_streamfile_size(streamfile) - data->start_offset; + + /* get size of the underlying, non-blocked data */ + while (physical_offset < max_physical_offset) { + uint32_t block_flag, block_size, data_size; + + block_flag = read_8bit(physical_offset+0x00,streamfile); + block_size = read_32bitBE(physical_offset+0x00,streamfile) & 0x00FFFFFF; + + if (data->version == 0 && block_flag != 0x00 && block_flag != 0x80) + break; /* data/end block expected */ + + if (data->version == 1 && block_flag == 0x48) { + physical_offset += block_size; + continue; /* skip header block */ + } + if (data->version == 1 && block_flag == 0x45) + break; /* stop on last block (always empty) */ + if (data->version == 1 && block_flag != 0x44) + break; /* data block expected */ + + switch(data->codec) { +#if 0 + case 0x03: + data_size = block_size - ???; + data_size += (data_size % 0x800); /* deflated padding */ + break; +#endif + case 0x05: /* EALayer3 v1 */ + case 0x06: /* EALayer3 v2 "PCM" */ + case 0x07: /* EALayer3 v2 "Spike" */ + data_size = block_size - 0x08; + break; + + case 0x0a: /* EATrax */ + data_size = read_32bitBE(physical_offset+0x04,streamfile); /* should be block_size - 0x08 */ + break; + + default: + return 0; + } + + physical_offset += block_size; + total_size += data_size; + + if (data->version == 0 && block_flag == 0x80) + break; /* stop on last block */ + } + + data->total_size = total_size; + return data->total_size; +} + + +/* Prepares custom IO for some blocked EAAudioCore formats, that need clean reads without block headers: + * - EA-XMA: deflated XMA in multistreams (separate 2ch frames) + * - 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 + */ +static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, off_t start_offset, size_t total_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + eaac_io_data io_data = {0}; + size_t io_data_size = sizeof(eaac_io_data); + + io_data.version = version; + io_data.codec = codec; + io_data.start_offset = start_offset; + io_data.total_size = total_size; /* optional */ + io_data.physical_offset = start_offset; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, eaac_io_read,eaac_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _EA_EAAC_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index c6986f66d..a2b784082 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -82,21 +82,22 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { off_t start_offset, header_offset; size_t header_size; - ea_header ea; + ea_header ea = {0}; /* 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,ast")) + if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm,ast,trj,trm")) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x5343486C && /* "SCHl" */ - read_32bitBE(0x00,streamFile) != 0x5348454E) /* "SHEN" */ + read_32bitBE(0x00,streamFile) != 0x5348454E && /* "SHEN" */ + read_32bitBE(0x00,streamFile) != 0x53484652) /* "SHFR" */ 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 various blocks (MVhd/MV0K/etc) and sometimes alt audio blocks (SHEN/SDEN/SEEN). + * Video uses various blocks (MVhd/MV0K/etc) and sometimes alt audio blocks (SHxx/SCxx/SDxx/SExx where XX=language, EN/FR). * The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */ header_size = read_32bitLE(0x04,streamFile); @@ -120,7 +121,7 @@ fail: VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { off_t start_offset, header_offset, offset, table_offset; size_t header_size; - ea_header ea; + ea_header ea = {0}; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; int i, bnk_version; @@ -284,7 +285,11 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ break; case EA_CODEC2_S16LE: /* PCM16LE */ - vgmstream->coding_type = coding_PCM16LE; + if (ea->version > 0) { + vgmstream->coding_type = coding_PCM16LE; + } else { /* Need for Speed III: Hot Pursuit (PC) */ + vgmstream->coding_type = coding_PCM16_int; + } break; case EA_CODEC2_VAG: /* PS-ADPCM */ @@ -449,7 +454,6 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be uint32_t platform_id; int is_header_end = 0; - memset(ea,0,sizeof(ea_header)); /* null defaults as 0 can be valid */ ea->version = EA_VERSION_NONE; @@ -467,7 +471,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be offset += 4 + 4; /* GSTRs have an extra field (config?): ex. 0x01000000, 0x010000D8 BE */ } else if ((platform_id & 0xFFFF0000) == 0x50540000) { /* "PT" = PlaTform */ - ea->platform = (uint8_t)read_16bitLE(offset + 2,streamFile); + ea->platform = (uint16_t)read_16bitLE(offset + 2,streamFile); offset += 4; } else { @@ -769,7 +773,7 @@ static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offse block_samples = 0; - if (id == 0x5343446C || id == 0x5344454E) { /* "SCDl" "SDEN" audio data */ + if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl" "SDEN" "SDFR" audio data */ switch (ea->codec2) { case EA_CODEC2_VAG: block_samples = ps_bytes_to_samples(block_size-0x10, ea->channels); @@ -785,7 +789,7 @@ static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offse block_size = 0x04; } - if (id == 0x5343486C || id == 0x5348454E) { /* "SCHl" "SHEN" end block */ + if (id == 0x5343486C || id == 0x5348454E || id == 0x53484652) { /* "SCHl" "SHEN" "SHFR" end block */ new_schl = 1; } } @@ -800,8 +804,8 @@ static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offse num_samples += block_samples; block_offset += block_size; - /* "SCEl" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ - if ((id == 0x5343456C || id == 0x5345454E) && block_offset % 0x04) { + /* "SCEl" "SEEN" "SEFR" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ + if ((id == 0x5343456C || id == 0x5345454E || id == 0x53454652) && block_offset % 0x04) { VGM_LOG_ONCE("EA SCHl: mis-aligned end offset found\n"); block_offset += 0x04 - (block_offset % 0x04); } @@ -832,10 +836,10 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); - if (id == 0x5343446C) { /* "SCDl" data block found */ + if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl/SDEN/SDFR" data block found */ off_t 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; - } else if (id == 0x5343436C) { /* "SCCl" data count found */ + } else if (id == 0x5343436C || id == 0x5343454E || id == 0x53434652) { /* "SCCl/SCEN/SCFR" data count found */ block_offset += block_size; /* size includes header */ continue; } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c index 8c9969470..fcdd7aa7a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c @@ -3,68 +3,151 @@ #include "../coding/coding.h" -/* SWVR - from EA games [Future Cop L.A.P.D. (PS/PC), Freekstyle (PS2/GC), EA Sports Supercross (PS)] */ +/* SWVR - from EA games, demuxed from .av/trk/mis/etc [Future Cop L.A.P.D. (PS/PC), Freekstyle (PS2/GC), EA Sports Supercross (PS)] */ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int loop_flag = 0, channel_count; - int big_endian; + int loop_flag = 0, channel_count, sample_rate, big_endian; + coding_t coding; + uint32_t block_id; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + int total_subsongs, target_subsong = streamFile->stream_index; - /* check extension */ - if (!check_extensions(streamFile,"str")) + /* checks */ + /* .stream: common (found inside files), .str: shortened, probably unnecessary */ + if (!check_extensions(streamFile,"stream,str")) goto fail; - /* check header */ - if (read_32bitBE(0x00,streamFile) == 0x53575652) { /* "SWVR" (GC) */ - big_endian = 1; - read_32bit = read_32bitBE; - } - else if (read_32bitBE(0x00,streamFile) == 0x52565753) { /* "RVWS" (PS/PS2) */ + /* blocks ids are in machine endianness */ + if (read_32bitBE(0x00,streamFile) == 0x52565753) { /* "RVWS" (PS1/PS2/PC) */ big_endian = 0; read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + start_offset = read_32bit(0x04, streamFile); + } + else if (read_32bitBE(0x00,streamFile) == 0x53575652) { /* "SWVR" (GC) */ + big_endian = 1; + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + start_offset = read_32bit(0x04, streamFile); + } + else if (read_32bitBE(0x00,streamFile) == 0x4D474156) { /* "MGAV", Freekstyle (PS2) raw movies */ + big_endian = 0; + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + start_offset = 0x00; + } + else if (read_32bitBE(0x00,streamFile) == 0x4453504D) { /* "DSPM", Freekstyle (GC) raw movies */ + big_endian = 1; + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + start_offset = 0x00; } else { goto fail; } + start_offset = read_32bit(0x04, streamFile); + if (read_32bit(start_offset+0x00, streamFile) == 0x50414444) /* "PADD" (Freekstyle) */ + start_offset += read_32bit(start_offset+0x04, streamFile); + else if (read_32bit(start_offset+0x10, streamFile) == 0x53484452) /* "SHDR" (Future Cop PC) */ + start_offset += read_32bit(start_offset+0x04, streamFile); - start_offset = read_32bit(0x04,streamFile); - loop_flag = 1; - channel_count = 2; + total_subsongs = 1; + block_id = read_32bit(start_offset, streamFile); + + /* files are basically headerless so we inspect blocks the first block + * Freekstyle uses multiblocks/subsongs (though some subsongs may be clones?) */ + switch(block_id) { + case 0x5641474D: /* "VAGM" */ + coding = coding_PSX; + if (read_16bit(start_offset+0x1a, streamFile) == 0x0024) { + total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1; + sample_rate = 24000; + } + else { + sample_rate = 14008; + } + channel_count = 2; + break; + case 0x56414742: /* "VAGB" */ + coding = coding_PSX; + if (read_16bit(start_offset+0x1a, streamFile) == 0x6400) { + sample_rate = 24000; + } + else { + sample_rate = 14008; + } + channel_count = 1; + break; + case 0x4453504D: /* "DSPM" */ + coding = coding_NGC_DSP; + total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1; + sample_rate = 24000; + channel_count = 2; + break; + case 0x44535042: /* "DSPB" */ + coding = coding_NGC_DSP; + channel_count = 1; + sample_rate = 24000; + break; + case 0x4D534943: /* "MSIC" */ + coding = coding_PCM8_U_int; + channel_count = 2; + sample_rate = 14008; + break; + case 0x53484F43: /* "SHOC" (a generic block but hopefully has PC sounds) */ + coding = coding_PCM8_U_int; + channel_count = 1; + sample_rate = 14008; + break; + default: + VGM_LOG("EA SWVR: unknown block id\n"); + goto fail; + } + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + loop_flag = 0;//(channel_count > 1); /* some Future Cop LAPD tracks repeat but other games have fadeouts */ /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = 16000; + vgmstream->sample_rate = sample_rate; vgmstream->codec_endian = big_endian; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = get_streamfile_size(streamFile) / total_subsongs; /* approx... */ vgmstream->meta_type = meta_EA_SWVR; + vgmstream->coding_type = coding; vgmstream->layout_type = layout_blocked_ea_swvr; - - vgmstream->coding_type = coding_PSX; + /* DSP coefs are loaded per block */ + /* some files (voices etc) decode with pops but seems a mastering problem */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - /* calculate samples */ + /* calc num_samples manually */ { - off_t current_chunk = start_offset; - - vgmstream->num_samples = 0; - while ((current_chunk + start_offset) < (get_streamfile_size(streamFile))) { - uint32_t block_id = (read_32bit(current_chunk,streamFile)); - if (block_id == 0x5641474D) { /* "VAGM" */ - block_update_ea_swvr(start_offset,vgmstream); - vgmstream->num_samples += vgmstream->current_block_size/16*28; - current_chunk += vgmstream->current_block_size + 0x1C; + int num_samples; + vgmstream->stream_index = target_subsong; /* needed to skip other subsong-blocks */ + vgmstream->next_block_offset = start_offset; + do { + block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); + switch(vgmstream->coding_type) { + case coding_PSX: num_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break; + case coding_NGC_DSP: num_samples = dsp_bytes_to_samples(vgmstream->current_block_size,1); break; + case coding_PCM8_U_int: num_samples = pcm_bytes_to_samples(vgmstream->current_block_size,1,8); break; + default: num_samples = 0; break; } - current_chunk += 0x10; + vgmstream->num_samples += num_samples; } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); } if (loop_flag) { @@ -72,6 +155,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { vgmstream->loop_end_sample = vgmstream->num_samples; } + block_update_ea_swvr(start_offset, vgmstream); return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_ad10.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_ad10.c new file mode 100644 index 000000000..494a6b461 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_ad10.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + +/* EA WVE (Ad10) - from Electronic Arts PS movies [Wing Commander 3/4 (PS)] */ +VGMSTREAM * init_vgmstream_ea_wve_ad10(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "wve")) + goto fail; + + start_offset = 0x00; + if (read_32bitBE(start_offset, streamFile) != 0x41643130 && /* "Ad10" */ + read_32bitBE(start_offset, streamFile) != 0x41643131) /* "Ad11" (last block, but could be first) */ + goto fail; + loop_flag = 0; + /* no header = no channels, but seems if the first PS-ADPCM header is 00 then it's mono, somehow + * (ex. Wing Commander 3 intro / Wing Commander 4 = stereo, rest of Wing Commander 3 = mono) */ + channel_count = read_8bit(start_offset+0x08,streamFile) != 0 ? 2 : 1; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = 22050; + vgmstream->meta_type = meta_EA_WVE_AD10; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_ea_wve_ad10; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples manually */ + { + vgmstream->next_block_offset = start_offset; + do { + block_update_ea_wve_ad10(vgmstream->next_block_offset,vgmstream); + vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + } + + block_update_ea_wve_ad10(start_offset, vgmstream); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_au00.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_au00.c new file mode 100644 index 000000000..a4aea7a4d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_au00.c @@ -0,0 +1,59 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + +/* EA WVE (VLC0/au00) - from Electronic Arts PS movies [Future Cop - L.A.P.D. (PS), Supercross 2000 (PS)] */ +VGMSTREAM * init_vgmstream_ea_wve_au00(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + /* .wve: common, .fsv: Future Cop LAPD (PS1) */ + if (!check_extensions(streamFile, "wve,fsv")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x564C4330) /* "VLC0" */ + goto fail; + + start_offset = read_32bitBE(0x04,streamFile); + if (read_32bitBE(start_offset, streamFile) != 0x61753030 && /* "au00" */ + read_32bitBE(start_offset, streamFile) != 0x61753031) /* "au01" (last block, but could be first) */ + goto fail; + loop_flag = 0; + channel_count = 2; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = 22050; + vgmstream->meta_type = meta_EA_WVE_AU00; + + /* You'd think they'd use coding_EA_XA_int but instead it's PS-ADPCM without flags and 0x0f frame size + * (equivalent to configurable PS-ADPCM), surely to shoehorn EA-XA sizes into the PS1 hardware decoder */ + vgmstream->coding_type = coding_PSX_cfg; + vgmstream->interleave_block_size = 0x0f; + vgmstream->layout_type = layout_blocked_ea_wve_au00; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples manually */ + { + vgmstream->next_block_offset = start_offset; + do { + block_update_ea_wve_au00(vgmstream->next_block_offset,vgmstream); + vgmstream->num_samples += ps_cfg_bytes_to_samples(vgmstream->current_block_size, vgmstream->interleave_block_size, 1); + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + } + + block_update_ea_wve_au00(start_offset, vgmstream); + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/emff.c b/Frameworks/vgmstream/vgmstream/src/meta/emff.c index 10cb7645e..79928e66b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/emff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/emff.c @@ -44,7 +44,7 @@ VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE *streamFile) { vgmstream->channels = channel_count; vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_emff_ps2_blocked; + vgmstream->layout_type = layout_blocked_emff_ps2; vgmstream->interleave_block_size = 0x10; vgmstream->meta_type = meta_EMFF_PS2; @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE *streamFile) { } /* Calc num_samples */ - emff_ps2_block_update(start_offset,vgmstream); + 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; @@ -148,7 +148,7 @@ VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE *streamFile) { goto fail; } - vgmstream->layout_type = layout_emff_ngc_blocked; + vgmstream->layout_type = layout_blocked_emff_ngc; vgmstream->interleave_block_size = 0x10; vgmstream->meta_type = meta_EMFF_NGC; @@ -163,7 +163,7 @@ VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE *streamFile) { } /* Calc num_samples */ - emff_ngc_block_update(start_offset,vgmstream); + 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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index 87c8662e6..dc0639749 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -3,6 +3,8 @@ #ifdef VGM_USE_FFMPEG +static int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); + /** * Generic init FFmpeg and vgmstream for any file supported by FFmpeg. * Called by vgmstream when trying to identify the file type (if the player allows it). @@ -75,4 +77,47 @@ fail: return NULL; } + +/** + * open file containing looping data and copy to buffer + * + * returns true if found and copied + */ +int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { + char posname[PATH_LIMIT]; + char filename[PATH_LIMIT]; + /*size_t bytes_read;*/ + STREAMFILE * streamFilePos= NULL; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + + if (strlen(filename)+4 > sizeof(posname)) goto fail; + + /* try to open a posfile using variations: "(name.ext).pos" */ + { + strcpy(posname, filename); + strcat(posname, ".pos"); + streamFilePos = streamFile->open(streamFile,posname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamFilePos) goto found; + + goto fail; + } + +found: + //if (get_streamfile_size(streamFilePos) != bufsize) goto fail; + + /* allow pos files to be of different sizes in case of new features, just fill all we can */ + memset(buf, 0, bufsize); + read_streamfile(buf, 0, bufsize, streamFilePos); + + close_streamfile(streamFilePos); + + return 1; + +fail: + if (streamFilePos) close_streamfile(streamFilePos); + + return 0; +} + #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 99bf7d516..da2c2f414 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -110,7 +110,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; case 0x03: /* Loop Info */ LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile); - if (ExtraFlagSize > 0x04) /* probably no needed */ + if (ExtraFlagSize > 0x04) /* probably not needed */ LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile); /* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ @@ -118,9 +118,9 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; case 0x04: /* free comment, or maybe SFX info */ break; - //case 0x05: /* Unknown (32b) */ - // /* found in Tearaway Vita, value 0, first stream only */ - // break; + //case 0x05: /* Unknown (32b) */ + // /* found in Tearaway Vita, value 0, first stream only */ + // break; case 0x06: /* XMA seek table */ /* no need for it */ break; @@ -142,11 +142,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { * (xN entries) */ break; - //case 0x0d: /* Unknown (32b) */ - // /* found in some XMA2 and Vorbis */ - // break; + //case 0x0d: /* Unknown (32b) */ + // /* found in some XMA2/Vorbis/FADPCM */ + // break; default: - VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); + VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x + 0x04 (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); break; } @@ -336,7 +336,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { #endif case 0x10: /* FMOD_SOUND_FORMAT_FADPCM */ - goto fail; + vgmstream->coding_type = coding_FADPCM; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x8c; + break; default: goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c index 98af7df96..a6af18da7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c @@ -56,7 +56,7 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) { for (i = 0; i < fsbkey_list_count; i++) { fsbkey_info entry = fsbkey_list[i]; - ;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt); + //;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt); temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt); if (!temp_streamFile) goto fail; @@ -146,7 +146,7 @@ static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read); + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read,NULL); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index 1b3a6081d..b65bea954 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -63,6 +63,12 @@ static const uint8_t key_inv[] = { 0x6D,0x69,0x6E,0x74,0x37,0x38,0x72,0x75,0x6E, /* Guitar Hero 3 */ //"5atu6w4zaw" static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,0x77 }; +/* Supreme Commander 2 */ //"B2A7BB00" +static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 }; + +/* Cookie Run: Ovenbreak */ //"ghfxhslrghfxhslr" +static const uint8_t key_cro[] = { 0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72,0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72 }; + // Unknown: // - Battle: Los Angeles // - Guitar Hero: Warriors of Rock, DJ hero FSB @@ -118,6 +124,11 @@ static const fsbkey_info fsbkey_list[] = { { 0,1, sizeof(key_gh3),key_gh3 },//untested { 1,0, sizeof(key_gh3),key_gh3 },//untested { 1,1, sizeof(key_gh3),key_gh3 },//untested + { 0,0, sizeof(key_sc2),key_sc2 },//untested + { 0,1, sizeof(key_sc2),key_sc2 },//untested + { 1,0, sizeof(key_sc2),key_sc2 },//untested + { 1,1, sizeof(key_sc2),key_sc2 },//untested + { 1,0, sizeof(key_cro),key_cro }, }; static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/g1l.c b/Frameworks/vgmstream/vgmstream/src/meta/g1l.c index 81b767d35..7c6e43d81 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/g1l.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/g1l.c @@ -4,12 +4,12 @@ static VGMSTREAM * init_vgmstream_kt_wiibgm_offset(STREAMFILE *streamFile, off_t offset); -/* Koei Tecmo G1L - pack format, sometimes containing a single stream - * +/* Koei Tecmo G1L - container format, sometimes containing a single stream. * It probably makes more sense to extract it externally, it's here mainly for Hyrule Warriors */ VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - int type, num_streams, target_stream = streamFile->stream_index; + int type; + int total_streams, target_stream = streamFile->stream_index; off_t stream_offset; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; @@ -22,30 +22,30 @@ VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE *streamFile) { && read_32bitBE(0x4, streamFile) != 0x30303030) /* "0000" (version?) */ goto fail; - if (read_32bitBE(0x0, streamFile) == 0x47314C5F ) { + if (read_32bitBE(0x0, streamFile) == 0x47314C5F) { read_32bit = read_32bitBE; } else { read_32bit = read_32bitLE; } - /* 0x08 filesize */ - /* 0x0c first file offset (same as 0x18) */ + /* 0x08: filesize, 0x0c: header size */ type = read_32bit(0x10,streamFile); - num_streams = read_32bit(0x14,streamFile); + total_streams = read_32bit(0x14,streamFile); if (target_stream==0) target_stream = 1; - if (target_stream < 0 || target_stream > num_streams || num_streams < 1) goto fail; + if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; stream_offset = read_32bit(0x18 + 0x4*(target_stream-1),streamFile); - /* filesize = stream_offset - stream_next_offset*/ + //stream_size = stream_offset - stream_next_offset;//not ok, sometimes entries are unordered/repeats */ switch(type) { /* type may not be correct */ case 0x09: /* DSP (WiiBGM) from Hyrule Warriors (Wii U) */ vgmstream = init_vgmstream_kt_wiibgm_offset(streamFile, stream_offset); break; + case 0x06: /* ATRAC9 (RIFF) from One Piece Pirate Warriors 3 (Vita) */ case 0x01: /* ATRAC3plus (RIFF) from One Piece Pirate Warriors 2 (PS3) */ case 0x00: /* OGG (KOVS) from Romance Three Kindgoms 13 (PC)*/ - case 0x0A: /* OGG (KOVS) from Dragon Quest Heroes (PC)*/ + case 0x0A: /* OGG (KOVS) from Dragon Quest Heroes (PC), Bladestorm (PC) w/ single files */ default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gca.c b/Frameworks/vgmstream/vgmstream/src/meta/gca.c index f48f9014d..246068773 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gca.c @@ -1,69 +1,42 @@ #include "meta.h" +#include "../coding/coding.h" #include "../util.h" -/* GCA (from Metal Slug Anthology [Wii]) */ +/* GCA - Terminal Reality games [Metal Slug Anthology (Wii), BlowOut (GC)] */ VGMSTREAM * init_vgmstream_gca(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int loop_flag = 0; - int channel_count = 1; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("gca",filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "gca")) + goto fail; - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x47434131) /* "GCA1" */ goto fail; - + + start_offset = 0x40; + loop_flag = 0; + channel_count = 1; + /* 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 = read_32bitBE(0x2A,streamFile); vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitBE(0x26,streamFile)*7/8; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitBE(0x26,streamFile)*7/8; - } + vgmstream->num_samples = dsp_nibbles_to_samples(read_32bitBE(0x26,streamFile));//read_32bitBE(0x26,streamFile)*7/8; - /* We have no interleave, so we have no layout */ - vgmstream->layout_type = layout_none; + vgmstream->layout_type = layout_none; /* we have no interleave, so we have no layout */ vgmstream->meta_type = meta_GCA; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - /*Retrieving the coef table */ - { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x04+i*2,streamFile); - } - } + dsp_read_coefs_be(vgmstream,streamFile,0x04,0x00); + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/genh.c b/Frameworks/vgmstream/vgmstream/src/meta/genh.c index ea67480af..70cac1066 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/genh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/genh.c @@ -157,6 +157,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { coding = coding_DVI_IMA_int; if (coding == coding_IMA) coding = coding_IMA_int; + if (coding == coding_AICA) + coding = coding_AICA_int; } /* to avoid endless loops */ @@ -173,7 +175,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { } /* setup adpcm */ - if (coding == coding_AICA) { + if (coding == coding_AICA || coding == coding_AICA_int) { int i; for (i=0;ichannels;i++) { vgmstream->ch[i].adpcm_step_index = 0x7f; @@ -195,7 +197,15 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; break; case coding_XBOX_IMA: - vgmstream->layout_type = layout_none; + if (genh.codec_mode == 1) { + if (!genh.interleave) goto fail; /* creates garbage */ + coding = coding_XBOX_IMA_int; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = genh.interleave; + } + else { + vgmstream->layout_type = layout_none; + } break; case coding_NGC_DTK: if (vgmstream->channels != 2) goto fail; @@ -367,14 +377,16 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) { 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 */ - if (genh->codec == ATRAC3 || genh->codec == ATRAC3PLUS) - genh->codec_mode = read_8bit(0x49,streamFile); /* 0=autodetect, 1=force joint stereo, 2=force full stereo */ - if (genh->codec == XMA1 || genh->codec == XMA2) - genh->codec_mode = read_8bit(0x4a,streamFile); /* 0=default (4ch = 2ch + 2ch), 1=single (4ch = 1ch + 1ch + 1ch + 1ch) */ + genh->codec_mode = read_8bit(0x4b,streamFile); + if ((genh->codec == ATRAC3 || genh->codec == ATRAC3PLUS) && genh->codec_mode==0) + genh->codec_mode = read_8bit(0x49,streamFile); + 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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c index 4e1c3021f..d51ec2407 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c @@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { if (!check_extensions(streamFile,"gsb")) goto fail; - streamFileGSP = open_stream_ext(streamFile, "gsp"); + streamFileGSP = open_streamfile_by_ext(streamFile, "gsp"); if (!streamFileGSP) goto fail; /* check header */ @@ -63,7 +63,7 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { size_t num_blocks; vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_gsb_blocked; + vgmstream->layout_type = layout_blocked_gsb; if (!find_chunk_be(streamFileGSP, 0x47434558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"GCEX"*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/halpst.c b/Frameworks/vgmstream/vgmstream/src/meta/halpst.c index caec16ade..24e6e23cb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/halpst.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/halpst.c @@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_halpst(STREAMFILE *streamFile) { } vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_halpst_blocked; + vgmstream->layout_type = layout_blocked_halpst; vgmstream->meta_type = meta_HALPST; /* load decode coefs */ @@ -120,7 +120,7 @@ VGMSTREAM * init_vgmstream_halpst(STREAMFILE *streamFile) { } /* start me up */ - halpst_block_update(header_length,vgmstream); + block_update_halpst(header_length,vgmstream); return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 231307cda..fce656081 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -228,6 +228,9 @@ static const hcakey_info hcakey_list[] = { // Dx2 Shin Megami Tensei Liberation (iOS/Android) {118714477}, // 000000000713706D + // Oira (Cygames) [iOS/Android] + {46460622}, // 0000000002C4EECE + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/idsp.c b/Frameworks/vgmstream/vgmstream/src/meta/idsp.c index a5f2ad9a6..82f79a5d3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/idsp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/idsp.c @@ -1,16 +1,16 @@ #include "meta.h" #include "../util.h" -/* "idsp/IDSP" - Soul Calibur Legends (Wii) - Sky Crawlers: Innocent Aces (Wii) +/* "idsp/IDSP" + Soul Calibur Legends (Wii) + Sky Crawlers: Innocent Aces (Wii) */ VGMSTREAM * init_vgmstream_idsp2(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; int loop_flag; - int channel_count; - int i, j; + int channel_count; + int i, j; off_t start_offset; /* check extension, case insensitive */ @@ -20,60 +20,53 @@ VGMSTREAM * init_vgmstream_idsp2(STREAMFILE *streamFile) { /* check header */ if (read_32bitBE(0x00,streamFile) != 0x69647370 || /* "idsp" */ read_32bitBE(0xBC,streamFile) != 0x49445350) /* IDSP */ - goto fail; + goto fail; loop_flag = read_32bitBE(0x20,streamFile); channel_count = read_32bitBE(0xC4,streamFile); if (channel_count > 8) - { - goto fail; - } + goto fail; - /* build the VGMSTREAM */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = (channel_count * 0x60) + 0x100; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0xC8,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = (read_32bitBE(0x14,streamFile))*14/8/channel_count; + start_offset = (channel_count * 0x60) + 0x100; + vgmstream->sample_rate = read_32bitBE(0xC8,streamFile); + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->num_samples = (read_32bitBE(0x14,streamFile))*14/8/channel_count; if (loop_flag) { vgmstream->loop_start_sample = (read_32bitBE(0xD0,streamFile)); vgmstream->loop_end_sample = (read_32bitBE(0xD4,streamFile)); } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; + + if (channel_count == 1) { + vgmstream->layout_type = layout_none; } - else if (channel_count > 1) - { - if (read_32bitBE(0xD8,streamFile) == 0) - { - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = (get_streamfile_size(streamFile)-start_offset)/2; + else if (channel_count > 1) { + if (read_32bitBE(0xD8,streamFile) == 0) { + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = (get_streamfile_size(streamFile)-start_offset)/2; } - else if (read_32bitBE(0xD8,streamFile) > 0) - { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0xD8,streamFile); + else if (read_32bitBE(0xD8,streamFile) > 0) { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0xD8,streamFile); } } - vgmstream->meta_type = meta_IDSP; + vgmstream->meta_type = meta_IDSP; - { - if (vgmstream->coding_type == coding_NGC_DSP) { - off_t coef_table[8] = {0x118,0x178,0x1D8,0x238,0x298,0x2F8,0x358,0x3B8}; - for (j=0;jchannels;j++) { - for (i=0;i<16;i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); - } + { + if (vgmstream->coding_type == coding_NGC_DSP) { + off_t coef_table[8] = {0x118,0x178,0x1D8,0x238,0x298,0x2F8,0x358,0x3B8}; + for (j=0;jchannels;j++) { + for (i=0;i<16;i++) { + vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); + } + } } - } } /* open the file for reading */ @@ -121,17 +114,13 @@ VGMSTREAM * init_vgmstream_idsp3(STREAMFILE *streamFile) { channel_count = read_32bitBE(0x24,streamFile); if (channel_count > 8) - { - goto fail; - } + goto fail; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ start_offset = (channel_count*0x60)+0xC; - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x14,streamFile); vgmstream->coding_type = coding_NGC_DSP; vgmstream->num_samples = read_32bitBE(0x0C,streamFile); @@ -141,9 +130,9 @@ VGMSTREAM * init_vgmstream_idsp3(STREAMFILE *streamFile) { } vgmstream->interleave_block_size = read_32bitBE(0x04,streamFile); - vgmstream->interleave_smallblock_size = ((vgmstream->num_samples/7*8)%(vgmstream->interleave_block_size)/vgmstream->channels); - vgmstream->layout_type = layout_interleave_shortblock; - + vgmstream->interleave_last_block_size = ((vgmstream->num_samples/7*8)%(vgmstream->interleave_block_size)/vgmstream->channels); + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = meta_IDSP; if (vgmstream->coding_type == coding_NGC_DSP) { @@ -170,7 +159,6 @@ VGMSTREAM * init_vgmstream_idsp3(STREAMFILE *streamFile) { vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset=start_offset+ vgmstream->interleave_block_size*i; - } } @@ -187,7 +175,7 @@ VGMSTREAM * init_vgmstream_idsp4(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; int loop_flag = 0; - int channel_count; + int channel_count; off_t start_offset; /* check extension, case insensitive */ @@ -201,17 +189,13 @@ VGMSTREAM * init_vgmstream_idsp4(STREAMFILE *streamFile) { channel_count = read_32bitBE(0x0C,streamFile); if (channel_count > 2) // Refuse everything else for now - { - goto fail; - } + goto fail; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ start_offset = 0x70; - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x08,streamFile); vgmstream->coding_type = coding_NGC_DSP; vgmstream->num_samples = read_32bitBE(0x04,streamFile)/channel_count/8*14; @@ -220,27 +204,25 @@ VGMSTREAM * init_vgmstream_idsp4(STREAMFILE *streamFile) { vgmstream->loop_end_sample = read_32bitBE(0x04,streamFile)/channel_count/8*14; } - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; + if (channel_count == 1) { + vgmstream->layout_type = layout_none; } - else - { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x10,streamFile); + else { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0x10,streamFile); } vgmstream->meta_type = meta_IDSP; - { - int i; - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x14+i*2,streamFile); - if (channel_count == 2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x42+i*2,streamFile); - } - } + { + int i; + for (i=0;i<16;i++) + vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x14+i*2,streamFile); + if (channel_count == 2) { + for (i=0;i<16;i++) + vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x42+i*2,streamFile); + } + } /* open the file for reading */ { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ish_isd.c b/Frameworks/vgmstream/vgmstream/src/meta/ish_isd.c index 6b3c734a9..84d2f22f3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ish_isd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ish_isd.c @@ -1,91 +1,60 @@ #include "meta.h" -#include "../util.h" - -/* -ISH+ISD -*/ +#include "../coding/coding.h" +/* ISH+ISD - from various games [Chaos Field (GC), Pokemon XD - Gale of Darkness (GC)] */ VGMSTREAM * init_vgmstream_ish_isd(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileISH = NULL; - char filename[PATH_LIMIT]; - char filenameISH[PATH_LIMIT]; - int i; - int channel_count; - int loop_flag; + STREAMFILE * streamHeader = NULL; + off_t start_offset; + int channel_count, loop_flag; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("isd",filename_extension(filename))) goto fail; - strcpy(filenameISH,filename); - strcpy(filenameISH+strlen(filenameISH)-3,"ish"); - - streamFileISH = streamFile->open(streamFile,filenameISH,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileISH) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFileISH) != 0x495F5346) /* "I_SF" */ + /* checks */ + if (!check_extensions(streamFile, "isd")) goto fail; - - channel_count = read_32bitBE(0x14,streamFileISH); - loop_flag = (read_32bitBE(0x1C,streamFileISH) !=0); + + streamHeader = open_streamfile_by_ext(streamFile,"ish"); + if (!streamHeader) goto fail; + + if (read_32bitBE(0x00,streamHeader) != 0x495F5346) /* "I_SF" */ + goto fail; + + channel_count = read_32bitBE(0x14,streamHeader); + loop_flag = (read_32bitBE(0x1C,streamHeader) != 0); + start_offset = 0; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x08,streamFileISH); - vgmstream->num_samples=read_32bitBE(0x0C,streamFileISH); - vgmstream->coding_type = coding_NGC_DSP; - if(loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x20,streamFileISH)*14/8/channel_count; - vgmstream->loop_end_sample = read_32bitBE(0x24,streamFileISH)*14/8/channel_count; - } - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else if (channel_count == 2) { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x18,streamFileISH); + vgmstream->sample_rate = read_32bitBE(0x08,streamHeader); + vgmstream->num_samples = read_32bitBE(0x0C,streamHeader); + if (loop_flag) { + vgmstream->loop_start_sample = read_32bitBE(0x20,streamHeader)*14 / 0x08 /channel_count; + vgmstream->loop_end_sample = read_32bitBE(0x24,streamHeader)*14 / 0x08 / channel_count; } vgmstream->meta_type = meta_ISH_ISD; - - /* open the file for reading by each channel */ - { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); - - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; - } - } - - - if (vgmstream->coding_type == coding_NGC_DSP) { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x40+i*2,streamFileISH); - } - if (vgmstream->channels == 2) { - for (i=0;i<16;i++) { - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x80+i*2,streamFileISH); - } - } + vgmstream->coding_type = coding_NGC_DSP; + if (channel_count == 1) { + vgmstream->layout_type = layout_none; + } else { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0x18,streamHeader); } - close_streamfile(streamFileISH); streamFileISH=NULL; - + dsp_read_coefs_be(vgmstream,streamHeader,0x40,0x40); + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + close_streamfile(streamHeader); return vgmstream; - /* clean up anything we may have opened */ fail: - if (streamFileISH) close_streamfile(streamFileISH); - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(streamHeader); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c index 9884b3b42..463ac152c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c @@ -10,7 +10,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { int32_t loop_length, coef_start_offset, coef_spacing; off_t start_offset; - if (!check_extensions(streamFile, "ktss")) + if (!check_extensions(streamFile, "kns,ktss")) goto fail; if (read_32bitBE(0, streamFile) != 0x4B545353) /* "KTSS" */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 89e049d6f..c32d47152 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -76,8 +76,6 @@ VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_xbox_xwav(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile); @@ -97,7 +95,6 @@ typedef struct { int loop_end_found; int32_t loop_end; meta_t meta_type; - layout_t layout_type; off_t stream_size; int total_subsongs; @@ -106,11 +103,11 @@ typedef struct { void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); uint8_t scd_xor; off_t scd_xor_length; - uint32_t sngw_xor; + uint32_t xor_value; -} vgm_vorbis_info_t; +} ogg_vorbis_meta_info_t; -VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks, off_t other_header_bytes, const vgm_vorbis_info_t *vgm_inf); +VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks, off_t other_header_bytes, const ogg_vorbis_meta_info_t *ovmi); VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile); #endif @@ -128,7 +125,7 @@ VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); -VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_akb_mp4(STREAMFILE *streamFile); #endif VGMSTREAM * init_vgmstream_sfl(STREAMFILE * streamFile); @@ -224,8 +221,6 @@ VGMSTREAM * init_vgmstream_ps2_pcm(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_rkv(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ps2_psw(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_ps2_vas(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_tec(STREAMFILE * streamFile); @@ -288,6 +283,7 @@ VGMSTREAM * init_vgmstream_psx_fag(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_mihb(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ngc_pdt_split(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_wii_mus(STREAMFILE * streamFile); @@ -309,6 +305,8 @@ VGMSTREAM * init_vgmstream_rsd6xadp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_rsd6radp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_rsd6oogv(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_rsd6xma(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_rsd6at3p(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_rsd6wma(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_dc_asd(STREAMFILE * streamFile); @@ -319,7 +317,8 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_ass(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_waa_wac_wad_wam(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ubi_jade_container(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_seg(STREAMFILE * streamFile); @@ -478,7 +477,7 @@ VGMSTREAM * init_vgmstream_ps2_khv(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_pc_smp(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_bo2(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_ngc_rkv(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile); @@ -623,8 +622,8 @@ VGMSTREAM * init_vgmstream_x360_cxs(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_dsp_adx(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_akb_multi(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_akb2_multi(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_x360_ast(STREAMFILE *streamFile); @@ -647,6 +646,7 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ta_aac_mobile_vorbis(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile); @@ -669,7 +669,10 @@ VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_awc(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_std(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile); @@ -699,11 +702,52 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_waf(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_wave(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_wave_segmented(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_smv(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_nxap(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ea_wve_au00(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ea_wve_ad10(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_sthd(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_pcm_sre(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_txtp(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_smc_smh(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile); #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mogg.c b/Frameworks/vgmstream/vgmstream/src/meta/mogg.c index d14e7e1a7..9054d2350 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mogg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mogg.c @@ -1,9 +1,9 @@ /* 2017-12-10: Preliminary MOGG Support. As long as the stream is unencrypted, this should be fine. - This will also work on unconventional 5 channel Vorbis streams but some sound cards might not like it. - TODO (Eventually): Add decryption for encrypted MOGG types (Rock Band, etc.) + This will also work on unconventional 5 channel Vorbis streams but some sound cards might not like it. + TODO (Eventually): Add decryption for encrypted MOGG types (Rock Band, etc.) - -bxaimc + -bxaimc */ #include "meta.h" @@ -12,31 +12,28 @@ /* MOGG - Harmonix Music Systems (Guitar Hero)[Unencrypted Type] */ VGMSTREAM * init_vgmstream_mogg(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS - char filename[PATH_LIMIT]; - off_t start_offset; + off_t start_offset; - /* check extension, case insensitive */ - streamFile->get_name(streamFile, filename, sizeof(filename)); - if (strcasecmp("mogg", filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "mogg")) + goto fail; - { - vgm_vorbis_info_t inf; - VGMSTREAM * result = NULL; + { + ogg_vorbis_meta_info_t ovmi = {0}; + VGMSTREAM * result = NULL; - memset(&inf, 0, sizeof(inf)); - inf.layout_type = layout_ogg_vorbis; - inf.meta_type = meta_MOGG; + ovmi.meta_type = meta_MOGG; - start_offset = read_32bitLE(0x04, streamFile); - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + start_offset = read_32bitLE(0x04, streamFile); + result = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); - if (result != NULL) { - return result; - } - } + if (result != NULL) { + return result; + } + } fail: - /* clean up anything we may have opened */ + /* clean up anything we may have opened */ #endif - return NULL; -} \ No newline at end of file + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c index ee8e56c7b..ac53aa53e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c @@ -181,8 +181,9 @@ VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) { /* check extension, case insensitive */ - /* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS) */ - if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin")) + /* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS) + * .msd: UNO (iOS) */ + if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin,msd")) goto fail; filesize = streamFile->get_size(streamFile); @@ -239,7 +240,7 @@ VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) { vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_FFmpeg; + vgmstream->meta_type = meta_MP4; vgmstream->num_samples = ffmpeg_data->totalSamples; vgmstream->sample_rate = ffmpeg_data->sampleRate; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c b/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c new file mode 100644 index 000000000..0333716f4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c @@ -0,0 +1,85 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* MSB+MSH - Sony sfx container companion of MIH+MIB [namCollection - Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */ +VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + off_t start_offset, header_offset = 0; + size_t stream_size; + int loop_flag = 0, channel_count, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "msb")) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile, "msh"); + if (!streamHeader) goto fail; + + if (read_32bitLE(0x00,streamHeader) != get_streamfile_size(streamHeader)) + goto fail; + /* 0x04: unknown */ + + /* parse entries */ + { + int i; + int entries = read_32bitLE(0x08,streamHeader); + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + for (i = 0; i < entries; i++) { + if (read_32bitLE(0x0c + 0x10*i, streamHeader) == 0) /* size 0 = empty entry */ + continue; + + total_subsongs++; + if (total_subsongs == target_subsong && !header_offset) { + header_offset = 0x0c + 0x10*i; + } + } + + if (!header_offset) goto fail; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + } + + + + loop_flag = 0; + channel_count = 1; + + stream_size = read_32bitLE(header_offset+0x00, streamHeader); + if (read_32bitLE(header_offset+0x04, streamHeader) != 0) /* stereo flag? */ + goto fail; + start_offset = read_32bitLE(header_offset+0x08, streamHeader); + sample_rate = read_32bitLE(header_offset+0x0c, streamHeader); /* Ace Combat 2 seems to set wrong values but probably their bug */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->meta_type = meta_MSB_MSH; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + close_streamfile(streamHeader); + return vgmstream; + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mss.c b/Frameworks/vgmstream/vgmstream/src/meta/mss.c index c7a1d15e2..e3596daec 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mss.c @@ -18,7 +18,7 @@ VGMSTREAM * init_vgmstream_mss(STREAMFILE *streamFile) { loop_flag = 0; channel_count = read_16bitLE(0x16,streamFile); - if (read_32bitLE(0x18,streamFile) == 0x4800 && vgmstream->channels > 2) + if (read_32bitLE(0x18,streamFile) == 0x4800 && channel_count > 2) channel_count = 2; //todo add support for interleave stereo streams /* build the VGMSTREAM */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c b/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c index 185e29367..84cfc4e21 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c @@ -1,7 +1,6 @@ -#include "../vgmstream.h" #include "meta.h" -#include "../util.h" -#include "../streamfile.h" +#include "../layout/layout.h" +#include "../coding/coding.h" #include "../coding/acm_decoder.h" #include #include @@ -13,11 +12,107 @@ #define DIRSEP '/' #endif -#define NAME_LENGTH PATH_LIMIT + +static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index); +static void clean_mus(char** mus_filenames, int file_count); + +/* .MUS - playlist for InterPlay games [Planescape: Torment (PC), Baldur's Gate Enhanced Edition (PC)] */ +VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + segmented_layout_data *data = NULL; + + int channel_count, loop_flag = 0, loop_start_index = -1, loop_end_index = -1; + int32_t num_samples = 0, loop_start_samples = 0, loop_end_samples = 0; + + char** mus_filenames = NULL; + int i, segment_count = 0; + + + /* checks */ + if (!check_extensions(streamFile, "mus")) + goto fail; + + /* get file paths from the .MUS text file */ + mus_filenames = parse_mus(streamFile, &segment_count, &loop_flag, &loop_start_index, &loop_end_index); + if (!mus_filenames) goto fail; + + /* init layout */ + data = init_layout_segmented(segment_count); + if (!data) goto fail; + + /* open each segment subfile */ + for (i = 0; i < segment_count; i++) { + STREAMFILE* temp_streamFile = streamFile->open(streamFile, mus_filenames[i], STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!temp_streamFile) goto fail; + + /* find .ACM type */ + switch(read_32bitBE(0x00,temp_streamFile)) { + case 0x97280301: /* ACM header id [Planescape: Torment (PC)] */ + data->segments[i] = init_vgmstream_acm(temp_streamFile); + break; + case 0x4F676753: /* "OggS" [Planescape: Torment Enhanced Edition (PC)] */ + data->segments[i] = init_vgmstream_ogg_vorbis(temp_streamFile); + break; + default: + data->segments[i] = NULL; + break; + } + close_streamfile(temp_streamFile); + + if (!data->segments[i]) goto fail; + + + if (i==loop_start_index) + loop_start_samples = num_samples; + if (i==loop_end_index) + loop_end_samples = num_samples; + + num_samples += data->segments[i]->num_samples; + } + + if (i==loop_end_index) + loop_end_samples = num_samples; + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + + channel_count = data->segments[0]->channels; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = data->segments[0]->sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_samples; + vgmstream->loop_end_sample = loop_end_samples; + + vgmstream->meta_type = meta_MUS_ACM; + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + + vgmstream->layout_data = data; + data->loop_segment = loop_start_index; + + clean_mus(mus_filenames, segment_count); + return vgmstream; + +fail: + clean_mus(mus_filenames, segment_count); + free_layout_segmented(data); + close_vgmstream(vgmstream); + return NULL; +} + +/* .mus text file parsing */ + +#define NAME_LENGTH PATH_LIMIT static int exists(char *filename, STREAMFILE *streamfile) { - STREAMFILE * temp = - streamfile->open(streamfile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + STREAMFILE * temp = streamfile->open(streamfile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!temp) return 0; close_streamfile(temp); @@ -91,57 +186,40 @@ fail: return 1; } -/* MUS playlist for InterPlay ACM */ -VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - ACMStream *acm_stream = NULL; - mus_acm_codec_data *data = NULL; +static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index) { + char** names = NULL; char filename[NAME_LENGTH]; char line_buffer[NAME_LENGTH]; char * end_ptr; char name_base[NAME_LENGTH]; - char (*names)[NAME_LENGTH] = NULL; char dir_name[NAME_LENGTH]; char subdir_name[NAME_LENGTH]; - int i; - int loop_flag = 0; - int channel_count; int file_count; size_t line_bytes; int whole_line_read = 0; off_t mus_offset = 0; - int loop_end_index = -1; - int loop_start_index = -1; - int32_t loop_start_samples = -1; - int32_t loop_end_samples = -1; + int i; + int loop_flag = 0, loop_start_index = -1, loop_end_index = -1; - int32_t total_samples = 0; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("mus",filename_extension(filename))) goto fail; /* read file name base */ - line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, - mus_offset, streamFile, &whole_line_read); + line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, mus_offset, streamFile, &whole_line_read); if (!whole_line_read) goto fail; mus_offset += line_bytes; memcpy(name_base,line_buffer,sizeof(name_base)); /* uppercase name_base */ { - int i; - for (i=0;name_base[i];i++) name_base[i]=toupper(name_base[i]); + int j; + for (j=0;name_base[j];j++) + name_base[j] = toupper(name_base[j]); } - /*printf("name base: %s\n",name_base);*/ - /* read track entry count */ - line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, - mus_offset, streamFile, &whole_line_read); + line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, mus_offset, streamFile, &whole_line_read); if (!whole_line_read) goto fail; if (line_buffer[0] == '\0') goto fail; mus_offset += line_bytes; @@ -149,24 +227,26 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { /* didn't parse whole line as an integer (optional opening whitespace) */ if (*end_ptr != '\0') goto fail; - /*printf("entries: %d\n",file_count);*/ - - names = calloc(file_count,sizeof(names[0])); + /* set names */ + names = calloc(file_count,sizeof(char*)); /* array of strings (size NAME_LENGTH) */ if (!names) goto fail; + for (i = 0; i < file_count; i++) { + names[i] = calloc(1,sizeof(char)*NAME_LENGTH); + if (!names[i]) goto fail; + } + dir_name[0]='\0'; + streamFile->get_name(streamFile,filename,sizeof(filename)); concatn(sizeof(dir_name),dir_name,filename); + /* find directory name for the directory contianing the MUS */ { - /* find directory name for the directory contianing the MUS */ - char * last_slash; - last_slash = strrchr(dir_name,DIRSEP); + char * last_slash = strrchr(dir_name,DIRSEP); if (last_slash != NULL) { - /* trim off the file name */ - last_slash[1]='\0'; + last_slash[1]='\0'; /* trim off the file name */ } else { - /* no dir name? annihilate! */ - dir_name[0] = '\0'; + dir_name[0] = '\0'; /* no dir name? annihilate! */ } } @@ -180,7 +260,8 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { char loop_name_temp[NAME_LENGTH]; char loop_name_base[NAME_LENGTH]; char loop_name[NAME_LENGTH]; - for (i=0;ifiles = calloc(file_count,sizeof(ACMStream *)); - if (!data->files) { - free(data); data = NULL; - goto fail; - } - /* open each file... */ - for (i=0;ifiles[i]=acm_stream; - - if (i==loop_start_index) loop_start_samples = total_samples; - if (i==loop_end_index) loop_end_samples = total_samples; - - total_samples += acm_stream->total_values / acm_stream->info.channels; - - if (i>0) { - if (acm_stream->info.channels != data->files[0]->info.channels || - acm_stream->info.rate != data->files[0]->info.rate) goto fail; - } - } - - if (i==loop_end_index) loop_end_samples = total_samples; - - channel_count = data->files[0]->info.channels; - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->channels = channel_count; - vgmstream->sample_rate = data->files[0]->info.rate; - vgmstream->coding_type = coding_ACM; - vgmstream->num_samples = total_samples; - vgmstream->loop_start_sample = loop_start_samples; - vgmstream->loop_end_sample = loop_end_samples; - vgmstream->layout_type = layout_mus_acm; - vgmstream->meta_type = meta_MUS_ACM; - - data->file_count = file_count; - data->current_file = 0; - data->loop_start_file = loop_start_index; - data->loop_end_file = loop_end_index; - /*data->end_file = -1;*/ - - vgmstream->codec_data = data; - - free(names); - - return vgmstream; - - /* clean up anything we may have opened */ + return names; fail: - if (data) { - int i; - for (i=0;ifile_count;i++) { - if (data->files[i]) { - acm_close(data->files[i]); - data->files[i] = NULL; - } - } - } - if (names) free(names); - if (vgmstream) close_vgmstream(vgmstream); + clean_mus(names, file_count); return NULL; } + +static void clean_mus(char** mus_filenames, int file_count) { + int i; + + if (!mus_filenames) return; + + for (i = 0; i < file_count; i++) { + free(mus_filenames[i]); + } + free(mus_filenames); +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/musx.c b/Frameworks/vgmstream/vgmstream/src/meta/musx.c index e174b4b2c..b3f211fdd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/musx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/musx.c @@ -2,278 +2,243 @@ #include "../util.h" -/* MUSX (Version 004) --------------------------------------->*/ +/* MUSX (Version 004) */ VGMSTREAM * init_vgmstream_musx_v004(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag; - int channel_count; off_t start_offset; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("musx",filename_extension(filename))) goto fail; - /* check header */ + /* checks */ + if (!check_extensions(streamFile, "musx")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; - if (read_32bitBE(0x08,streamFile) != 0x04000000) /* "0x04000000" */ - goto fail; + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x04000000) + goto fail; loop_flag = (read_32bitLE(0x840,streamFile) != 0xFFFFFFFF); channel_count = 2; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - switch (read_32bitBE(0x10,streamFile)) - { - case 0x5053325F: /* PS2_ */ - start_offset = read_32bitLE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PSX; // PS2 ADPCM - vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x80; - if (loop_flag) - { - vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; - } - break; - case 0x47435F5F: /* GC__ */ - start_offset = read_32bitBE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_DAT4_IMA; // Eurocom DAT4 4-bit IMA ADPCM - vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - if (loop_flag) - { - vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))/16/channel_count*28; - } - break; - case 0x58425F5F: /* XB__ */ - start_offset = read_32bitLE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_DAT4_IMA; // Eurocom DAT4 4-bit IMA ADPCM - vgmstream->num_samples = (read_32bitLE(0x2C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - if (loop_flag) - { - vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; - } - break; + switch (read_32bitBE(0x10,streamFile)) { + case 0x5053325F: /* PS2_ */ + start_offset = read_32bitLE(0x28,streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = 32000; + vgmstream->coding_type = coding_PSX; + vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))/16/channel_count*28; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x80; + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; + vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; + } + break; + case 0x47435F5F: /* GC__ */ + start_offset = read_32bitBE(0x28,streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = 32000; + vgmstream->coding_type = coding_DAT4_IMA; + vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))/16/channel_count*28; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x20; + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))/16/channel_count*28; + vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))/16/channel_count*28; + } + break; + case 0x58425F5F: /* XB__ */ + start_offset = read_32bitLE(0x28,streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = 44100; + vgmstream->coding_type = coding_DAT4_IMA; + vgmstream->num_samples = (read_32bitLE(0x2C,streamFile))/16/channel_count*28; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x20; + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; + vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; + } + break; default: - goto fail; + goto fail; } vgmstream->meta_type = meta_MUSX_V004; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - } - + 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; } -/* <--------------------------------------- MUSX (Version 004) */ -/* MUSX (Version 005) --------------------------------------->*/ +/* MUSX (Version 005) [Predator: Concrete Jungle (PS2/Xbox) ] */ VGMSTREAM * init_vgmstream_musx_v005(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag; - int channel_count; off_t start_offset; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("musx",filename_extension(filename))) goto fail; - /* check header */ + /* checks */ + /* .sfx: Batman Begins, .musx: header id */ + if (!check_extensions(streamFile, "musx,sfx")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; - if (read_32bitBE(0x08,streamFile) != 0x05000000) /* "0x04000000" */ - goto fail; + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x05000000) + goto fail; loop_flag = (read_32bitLE(0x840,streamFile) != 0xFFFFFFFF); channel_count = 2; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - switch (read_32bitBE(0x10,streamFile)) - { - case 0x47435F5F: /* GC__ */ - start_offset = read_32bitBE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_DAT4_IMA; // Eurocom DAT4 4-bit IMA ADPCM - vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))/16/channel_count*28; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - if (loop_flag) - { - vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))/16/channel_count*28; - vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))/16/channel_count*28; - } - break; + switch (read_32bitBE(0x10,streamFile)) { + case 0x5053325F: /* PS2_ */ + start_offset = read_32bitLE(0x28,streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = 32000; + vgmstream->coding_type = coding_PSX; + vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))/16/channel_count*28; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x80; + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; + vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; + } + break; + case 0x47435F5F: /* GC__ */ + start_offset = read_32bitBE(0x28,streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = 32000; + vgmstream->coding_type = coding_DAT4_IMA; + vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))/16/channel_count*28; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x20; + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))/16/channel_count*28; + vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))/16/channel_count*28; + } + break; + case 0x58425F5F: /* XB__ */ + start_offset = read_32bitLE(0x28,streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = 44100; + vgmstream->coding_type = coding_DAT4_IMA; + vgmstream->num_samples = (read_32bitLE(0x2C,streamFile))/16/channel_count*28; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x20; + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28; + vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28; + } + break; default: - goto fail; + goto fail; } vgmstream->meta_type = meta_MUSX_V005; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - } + 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; } -/* <--------------------------------------- MUSX (Version 005) */ - -/* MUSX (Version 006) ---------------------------------------> */ +/* MUSX (Version 006) [Batman Begins (GC)] */ VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag; - int channel_count; off_t start_offset; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("musx",filename_extension(filename))) goto fail; - /* check header */ + /* checks */ + /* .sfx: Batman Begins, .musx: header id */ + if (!check_extensions(streamFile, "sfx,musx")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; - - if (read_32bitBE(0x08,streamFile) != 0x06000000) /* "0x06000000" */ - goto fail; + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x06000000) + goto fail; loop_flag = (read_32bitLE(0x840,streamFile)!=0xFFFFFFFF); channel_count = 2; - /* build the VGMSTREAM */ + //todo some files (ex. Batman Begins) have a subsong table at 0x800, not sure what signals it (flags at 0x04/0x14?) + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; + if (!vgmstream) goto fail; - /* fill in the vital statistics */ - switch (read_32bitBE(0x10,streamFile)) - { - case 0x5053325F: /* PS2_ */ - start_offset = read_32bitLE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))*28/16/channel_count; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x80; - vgmstream->meta_type = meta_MUSX_V006; - if (loop_flag) - { - vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))*28/16/channel_count; - vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))*28/16/channel_count; - } - break; - case 0x47435F5F: /* GC__ */ - start_offset = read_32bitBE(0x28,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_DAT4_IMA; - vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))*28/16/channel_count; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x20; - vgmstream->meta_type = meta_MUSX_V006; - if (loop_flag) - { - vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))*28/16/channel_count; - vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))*28/16/channel_count; - } - break; - default: - goto fail; + switch (read_32bitBE(0x10,streamFile)) { + case 0x5053325F: /* PS2_ */ + start_offset = read_32bitLE(0x28,streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = 32000; + vgmstream->coding_type = coding_PSX; + vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))*28/16/channel_count; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x80; + vgmstream->meta_type = meta_MUSX_V006; + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))*28/16/channel_count; + vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))*28/16/channel_count; + } + break; + case 0x47435F5F: /* GC__ */ + start_offset = read_32bitBE(0x28,streamFile); + if (start_offset == 0 || start_offset == 0xABABABAB) goto fail; /* some are empty */ + + vgmstream->channels = channel_count; + vgmstream->sample_rate = 32000; + vgmstream->coding_type = coding_DAT4_IMA; + vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))*28/16/channel_count; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x20; + vgmstream->meta_type = meta_MUSX_V006; + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))*28/16/channel_count; + vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))*28/16/channel_count; + } + break; + default: + goto fail; } - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - 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; } -/* <--------------------------------------- MUSX (Version 006) */ -/* MUSX (Version 010) --------------------------------------->*/ -/* WII_ in Dead Space: Extraction */ +/* MUSX (Version 010) [Dead Space: Extraction (Wii), Rio (PS3), Pirates of the Caribbean: At World's End (PSP)] */ VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; off_t start_offset; - int musx_type; /* determining the decoder by strings like "PS2_", "GC__" and so on */ - //int musx_version; /* 0x08 provides a "version" byte */ - int loop_flag = 0; - int channel_count; + int musx_type; /* determining the decoder by strings like "PS2_", "GC__" and so on */ + //int musx_version; /* 0x08 provides a "version" byte */ + int loop_flag = 0; + int channel_count; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); @@ -281,17 +246,17 @@ VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { /* check header */ if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; + goto fail; if (read_32bitBE(0x800,streamFile) == 0x53424E4B) /* "SBNK", */ // SoundBank, refuse - goto fail; - if (read_32bitBE(0x08,streamFile) != 0x0A000000) /* "0x0A000000" */ - goto fail; + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x0A000000) /* "0x0A000000" */ + goto fail; - loop_flag = ((read_32bitLE(0x34,streamFile)!=0x00000000) && - (read_32bitLE(0x34,streamFile)!=0xABABABAB)); + loop_flag = ((read_32bitLE(0x34,streamFile)!=0x00000000) && + (read_32bitLE(0x34,streamFile)!=0xABABABAB)); channel_count = 2; - musx_type=(read_32bitBE(0x10,streamFile)); + musx_type=(read_32bitBE(0x10,streamFile)); if (musx_type == 0x5749495F && /* WII_ */ (read_16bitBE(0x40,streamFile) == 0x4441) && /* DA */ @@ -300,7 +265,7 @@ VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { channel_count = read_32bitLE(0x48,streamFile); loop_flag = (read_32bitLE(0x64,streamFile) != -1); } - if (musx_type == 0x5053335F && /* PS3_ */ + if (musx_type == 0x5053335F && /* PS3_ */ (read_16bitBE(0x40,streamFile) == 0x4441) && /* DA */ (read_8bit(0x42,streamFile) == 0x54)) /* T */ { @@ -312,11 +277,11 @@ VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { loop_flag = 0; } - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - - /* fill in the vital statistics */ + + /* fill in the vital statistics */ switch (musx_type) { case 0x5053325F: /* PS2_ */ start_offset = 0x800; @@ -351,24 +316,24 @@ VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x20; vgmstream->meta_type = meta_MUSX_V010; - if (read_32bitBE(0x40,streamFile)==0x44415438){ + if (read_32bitBE(0x40,streamFile)==0x44415438){ vgmstream->num_samples = read_32bitLE(0x60,streamFile); - vgmstream->sample_rate = read_32bitLE(0x4C,streamFile); - if (loop_flag) + vgmstream->sample_rate = read_32bitLE(0x4C,streamFile); + if (loop_flag) { vgmstream->loop_start_sample = read_32bitLE(0x64,streamFile); vgmstream->loop_end_sample = read_32bitLE(0x60,streamFile); } - } - else { - vgmstream->sample_rate = 44100; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/2/(0x20)*((0x20-4)*2); - if (loop_flag) + } + else { + vgmstream->sample_rate = 44100; + vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/2/(0x20)*((0x20-4)*2); + if (loop_flag) { vgmstream->loop_start_sample = read_32bitLE(0x44,streamFile); vgmstream->loop_end_sample = read_32bitLE(0x40,streamFile); } - } + } break; case 0x5749495F: /* WII_ */ start_offset = 0x800; @@ -377,7 +342,7 @@ VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { switch (read_32bitBE(0x40,streamFile)) { case 0x44415434: /* DAT4 */ - case 0x44415438: /* DAT8 */ + case 0x44415438: /* DAT8 [GoldenEye 007 (Wii)] */ vgmstream->coding_type = coding_DAT4_IMA; break; default: @@ -406,7 +371,7 @@ VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { break; default: goto fail; - } + } /* open the file for reading */ { @@ -426,79 +391,78 @@ VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) { return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } -/* <--------------------------------------- MUSX (Version 010) */ -/* MUSX (Version 201) --------------------------------------->*/ +/* MUSX (Version 201) */ VGMSTREAM * init_vgmstream_musx_v201(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; off_t start_offset; - //int musx_version; /* 0x08 provides a "version" byte */ - int loop_flag; - int channel_count; - int loop_detect; - int loop_offsets; - + //int musx_version; /* 0x08 provides a "version" byte */ + int loop_flag; + int channel_count; + int loop_detect; + int loop_offsets; + /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("musx",filename_extension(filename))) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ - goto fail; - if ((read_32bitBE(0x08,streamFile) != 0xC9000000) && - (read_32bitLE(0x08,streamFile) != 0xC9000000)) /* "0xC9000000" */ - goto fail; + goto fail; + if ((read_32bitBE(0x08,streamFile) != 0xC9000000) && + (read_32bitLE(0x08,streamFile) != 0xC9000000)) /* "0xC9000000" */ + goto fail; channel_count = 2; - loop_detect = read_32bitBE(0x800,streamFile); - switch (loop_detect) { - case 0x02000000: - loop_offsets = 0x8E0; - break; - case 0x03000000: - loop_offsets = 0x880; - break; - case 0x04000000: - loop_offsets = 0x8B4; - break; - case 0x05000000: - loop_offsets = 0x8E8; - break; - case 0x06000000: - loop_offsets = 0x91C; - break; - default: - goto fail; - } + loop_detect = read_32bitBE(0x800,streamFile); + switch (loop_detect) { + case 0x02000000: + loop_offsets = 0x8E0; + break; + case 0x03000000: + loop_offsets = 0x880; + break; + case 0x04000000: + loop_offsets = 0x8B4; + break; + case 0x05000000: + loop_offsets = 0x8E8; + break; + case 0x06000000: + loop_offsets = 0x91C; + break; + default: + goto fail; + } - loop_flag = (read_32bitLE(loop_offsets+0x04,streamFile) !=0x00000000); + loop_flag = (read_32bitLE(loop_offsets+0x04,streamFile) !=0x00000000); + start_offset = read_32bitLE(0x18,streamFile); - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x18,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(loop_offsets,streamFile)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(loop_offsets+0x10,streamFile)*28/16/channel_count; - vgmstream->loop_end_sample = read_32bitLE(loop_offsets,streamFile)*28/16/channel_count; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x80; - vgmstream->meta_type = meta_MUSX_V201; - + { + vgmstream->channels = channel_count; + vgmstream->sample_rate = 32000; + vgmstream->coding_type = coding_PSX; + vgmstream->num_samples = read_32bitLE(loop_offsets,streamFile)*28/16/channel_count; + if (loop_flag) { + vgmstream->loop_start_sample = read_32bitLE(loop_offsets+0x10,streamFile)*28/16/channel_count; + vgmstream->loop_end_sample = read_32bitLE(loop_offsets,streamFile)*28/16/channel_count; + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x80; + vgmstream->meta_type = meta_MUSX_V201; + } + /* open the file for reading */ { int i; @@ -517,9 +481,7 @@ VGMSTREAM * init_vgmstream_musx_v201(STREAMFILE *streamFile) { return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } -/* <--------------------------------------- MUSX (Version 201) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/naomi_adpcm.c b/Frameworks/vgmstream/vgmstream/src/meta/naomi_adpcm.c index 7ca6ec969..764f3a1b1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/naomi_adpcm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/naomi_adpcm.c @@ -1,67 +1,44 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* ADPCM (from NAOMI/NAOMI2 Arcade games) */ +/* ADPCM - from NAOMI/NAOMI2 Arcade games [F355 Challenge (Naomi)] */ VGMSTREAM * init_vgmstream_naomi_adpcm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int loop_flag = 0; - int channel_count; + int loop_flag, channel_count; + size_t data_size; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("adpcm",filename_extension(filename))) goto fail; -#if 0 - /* check header */ - if ((read_32bitBE(0x00,streamFile) != 0x41445043) || /* "ADPC" */ - (read_32bitBE(0x04,streamFile) != 0x41445043)) /* "M_v0" */ - goto fail; -#endif + /* checks */ + if (!check_extensions(streamFile, "adpcm")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41445043 || /* "ADPC" */ + read_32bitBE(0x04,streamFile) != 0x4D5F7630) /* "M_v0" */ + goto fail; + /* there is some kind of info in the end padding, loop related? */ loop_flag = 0; channel_count = 2; - - /* build the VGMSTREAM */ + start_offset = 0x40; + data_size = read_32bitLE(0x10,streamFile) * 0x100; /* data has padding */ + + /* 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_AICA; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-start_offset); - } + vgmstream->num_samples = aica_bytes_to_samples(data_size, channel_count); + vgmstream->coding_type = coding_AICA_int; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile) * 0x80; + vgmstream->interleave_block_size = data_size / channel_count; vgmstream->meta_type = meta_NAOMI_ADPCM; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - vgmstream->ch[i].adpcm_step_index = 0x7f; /* AICA */ - } - } - + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/naomi_spsd.c b/Frameworks/vgmstream/vgmstream/src/meta/naomi_spsd.c index d8d683cd5..5780774b4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/naomi_spsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/naomi_spsd.c @@ -1,88 +1,112 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* SPSD (Guilty Gear X [NAOMI GD-ROM]) */ +/* SPSD - Naomi (arcade) streams [Guilty Gear X (Naomi), Crazy Taxi (Naomi), Virtua Tennis 2 (Naomi)] */ VGMSTREAM * init_vgmstream_naomi_spsd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int coding; - int loop_flag; - int channel_count; + size_t data_size; + int loop_flag, channel_count, codec, flags, index; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("spsd",filename_extension(filename))) goto fail; - /* check header */ + /* checks */ + /* .spsd: header id */ + if (!check_extensions(streamFile, "spsd")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x53505344) /* "SPSD" */ goto fail; - loop_flag = 0; - channel_count = 2; - + if (read_32bitBE(0x04,streamFile) != 0x01010004 && /* standard version */ + read_32bitBE(0x04,streamFile) != 0x00010004) /* uncommon version [Crazy Taxi (Naomi)] */ + goto fail; + + + codec = read_8bit(0x08,streamFile); + flags = read_8bit(0x09,streamFile); + index = read_16bitLE(0x0a,streamFile); + data_size = read_32bitLE(0x0c,streamFile); + //if (data_size + start_offset != get_streamfile_size(streamFile)) + // goto fail; /* some rips out there have incorrect padding */ + + //todo with 0x80 seems 0x2c is a loop_start_sample but must be adjusted to +1 block? (uncommon flag though) + loop_flag = (flags & 0x80); + channel_count = ((flags & 0x01) || (flags & 0x02)) ? 2 : 1; /* 0x02 is rare but looks normal (Virtua Tennis 2) */ + start_offset = 0x40; + + /* 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 = (uint16_t)read_16bitLE(0x2A,streamFile); - - switch (read_8bit(0x8,streamFile)) - { - case 0x01: - coding = coding_PCM8; - break; - case 0x03: - coding = coding_AICA; - break; - default: - goto fail; - } - - vgmstream->coding_type = coding; - vgmstream->num_samples = read_32bitLE(0x0C,streamFile); - -#if 0 - if (loop_flag) - { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitLE(0x0C,streamFile); - } -#endif - - vgmstream->interleave_block_size = 0x2000; - if (channel_count > 1) { - vgmstream->interleave_smallblock_size = ((get_streamfile_size(streamFile)-start_offset)%(vgmstream->channels*vgmstream->interleave_block_size))/vgmstream->channels; - vgmstream->layout_type = layout_interleave_shortblock; - } else { - vgmstream->layout_type = layout_none; - } vgmstream->meta_type = meta_NAOMI_SPSD; + switch (codec) { + case 0x00: /* [Virtua Tennis 2 (Naomi), Club Kart - European Session (Naomi)] */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->num_samples = pcm_bytes_to_samples(data_size,channel_count,16); + vgmstream->loop_start_sample = read_32bitLE(0x2c,streamFile) + pcm_bytes_to_samples(0x2000*channel_count,channel_count,16); + vgmstream->loop_end_sample = vgmstream->num_samples; + break; - /* open the file for reading */ + case 0x01: /* [Virtua Tennis 2 (Naomi)] */ + vgmstream->coding_type = coding_PCM8; + vgmstream->num_samples = pcm_bytes_to_samples(data_size,channel_count,8); + vgmstream->loop_start_sample = read_32bitLE(0x2c,streamFile) + pcm_bytes_to_samples(0x2000*channel_count,channel_count,8); + vgmstream->loop_end_sample = vgmstream->num_samples; + + break; + + case 0x03: /* standard */ + vgmstream->coding_type = coding_AICA_int; + vgmstream->num_samples = aica_bytes_to_samples(data_size,channel_count); + vgmstream->loop_start_sample = /*read_32bitLE(0x2c,streamFile) +*/ aica_bytes_to_samples(0x2000*channel_count,channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + + default: + goto fail; + } + + /* interleave index, maybe */ + switch(index) { + case 0x0000: + if (channel_count != 1) goto fail; + vgmstream->layout_type = layout_none; + break; + + case 0x000d: + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2000; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + break; + + case 0x00ff: + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = data_size / channel_count; + break; + + default: + goto fail; + } + + /* todo seems to decode slightly incorrectly in after certain data (loop section start?) + * may depend on values in 0x20 or 0x2c [ex. Marvel vs Capcom 2 (Naomi)] + * at 0x30(4*ch) is some config per channel but doesn't seem to affect ADPCM (found with PCM too) */ { int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - vgmstream->ch[i].adpcm_step_index = 0x7f; /* AICA */ + for (i = 0; i < channel_count; i++) { + vgmstream->ch[i].adpcm_step_index = 0x7f; } } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c b/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c index e50e614ea..37d78ff22 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c @@ -8,7 +8,8 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) { off_t start_offset; int channel_count, loop_flag = 0; - /* check extension, case insensitive (made-up extension) */ + /* checks */ + /* .hwas: usually in archives but also found named (ex. Guitar Hero On Tour) */ if (!check_extensions(streamFile,"hwas")) goto fail; @@ -34,14 +35,14 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) { vgmstream->meta_type = meta_NDS_HWAS; vgmstream->coding_type = coding_IMA_int; - vgmstream->layout_type = layout_hwas_blocked; + vgmstream->layout_type = layout_blocked_hwas; vgmstream->full_block_size = read_32bitLE(0x04,streamFile); /* usually 0x2000, 0x4000 or 0x8000 */ /* open the file for reading by each channel */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - hwas_block_update(start_offset, vgmstream); + block_update_hwas(start_offset, vgmstream); return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nds_strm.c b/Frameworks/vgmstream/vgmstream/src/meta/nds_strm.c index adef717aa..cbd534f5a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nds_strm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nds_strm.c @@ -4,103 +4,66 @@ /* STRM - common Nintendo NDS streaming format */ VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - coding_t coding_type; - - int codec_number; - int channel_count; - int loop_flag; - off_t start_offset; + int channel_count, loop_flag, codec; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("strm",filename_extension(filename))) goto fail; - /* check header */ - if ((uint32_t)read_32bitBE(0x00,streamFile)!=0x5354524D) /* STRM */ - goto fail; - if ((uint32_t)read_32bitBE(0x04,streamFile)!=0xFFFE0001 && /* Old Header Check */ - ((uint32_t)read_32bitBE(0x04,streamFile)!=0xFEFF0001)) /* Some newer games have a new flag */ + /* checks */ + if (!check_extensions(streamFile, "strm")) goto fail; - - - /* check for HEAD section */ - if ((uint32_t)read_32bitBE(0x10,streamFile)!=0x48454144 && /* "HEAD" */ - (uint32_t)read_32bitLE(0x14,streamFile)!=0x50) /* 0x50-sized head is all I've seen */ + if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0xFFFE0001 && /* Old Header Check */ + (read_32bitBE(0x04,streamFile) != 0xFEFF0001)) /* Some newer games have a new flag */ goto fail; - /* check type details */ - codec_number = read_8bit(0x18,streamFile); + if (read_32bitBE(0x10,streamFile) != 0x48454144 && /* "HEAD" */ + read_32bitLE(0x14,streamFile) != 0x50) /* 0x50-sized head is all I've seen */ + goto fail; + + codec = read_8bit(0x18,streamFile); loop_flag = read_8bit(0x19,streamFile); channel_count = read_8bit(0x1a,streamFile); + if (channel_count > 2) goto fail; - switch (codec_number) { - case 0: - coding_type = coding_PCM8; + start_offset = read_32bitLE(0x28,streamFile); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = (uint16_t)read_16bitLE(0x1c,streamFile); + vgmstream->num_samples = read_32bitLE(0x24,streamFile); + vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile); + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_STRM; + + switch (codec) { + case 0x00: /* [Bleach - Dark Souls (DS)] */ + vgmstream->coding_type = coding_PCM8; break; - case 1: - coding_type = coding_PCM16LE; + case 0x01: + vgmstream->coding_type = coding_PCM16LE; break; - case 2: - coding_type = coding_NDS_IMA; + case 0x02: /* [SaGa 2 (DS)] */ + vgmstream->coding_type = coding_NDS_IMA; break; default: goto fail; } - - /* TODO: only mono and stereo supported */ - if (channel_count < 1 || 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->num_samples = read_32bitLE(0x24,streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x1c,streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile); - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->coding_type = coding_type; - vgmstream->meta_type = meta_STRM; - + vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = read_32bitLE(0x30,streamFile); - vgmstream->interleave_smallblock_size = read_32bitLE(0x38,streamFile); + vgmstream->interleave_last_block_size = read_32bitLE(0x38,streamFile); - if (coding_type==coding_PCM8 || coding_type==coding_PCM16LE) - vgmstream->layout_type = layout_none; - else - vgmstream->layout_type = layout_interleave_shortblock; - - start_offset = read_32bitLE(0x28,streamFile); - - /* open the file for reading by each channel */ - { - int i; - for (i=0;ilayout_type==layout_interleave_shortblock) - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename, - vgmstream->interleave_block_size); - else - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename, - 0x1000); - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - start_offset + i*vgmstream->interleave_block_size; - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c index 82e0a70d5..ab3852c6f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c @@ -2,17 +2,19 @@ #include "meta.h" #include "../util.h" +/* DTK - headerless Nintendo DTK file [Harvest Moon - Another Wonderful Life (GC), XGRA (GC)] */ VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset = 0; int channel_count = 2, loop_flag = 0; /* always stereo, no loop */ - /* check extension, case insensitive */ - if ( !check_extensions(streamFile,"dtk,adp")) + /* checks */ + /* dtk: standard [XGRA (GC)], adp: standard [Harvest Moon AWL (GC)], wav/lwav: Alien Hominid (GC) */ + if ( !check_extensions(streamFile,"dtk,adp,wav,lwav")) goto fail; - /* .adp files have no header, and the ext is common, so all we can do is look for valid first frames */ - if (check_extensions(streamFile,"adp")) { + /* files have no header, and the ext is common, so all we can do is look for valid first frames */ + if (check_extensions(streamFile,"adp,wav,lwav")) { int i; for (i = 0; i < 10; i++) { /* try a bunch of frames */ if (read_8bit(0x00 + i*0x20,streamFile) != read_8bit(0x02 + i*0x20,streamFile) || diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_bo2.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_bo2.c deleted file mode 100644 index 74ee379f1..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_bo2.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* BO2 (Blood Omen 2 NGC) */ -VGMSTREAM * init_vgmstream_ngc_bo2(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag; - int channels; - int channel_count; - off_t start_offset; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("bo2",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x0) /* "IDSP" */ - goto fail; - - switch (read_32bitBE(0x10,streamFile)) - { - case 0x0: - channels = 1; - break; - case 0x1: - channels = 2; - break; - default: - goto fail; - } - - if ((get_streamfile_size(streamFile)) < ((read_32bitBE(0x0C,streamFile)/14*8*channels)+0x800)) - { - goto fail; - } - - channel_count = channels; - loop_flag = (read_32bitBE(0x08,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->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x04,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x08,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x0C,streamFile); - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else - { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x400; - } - - vgmstream->meta_type = meta_NGC_BO2; - - { - int i; - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x24+i*2,streamFile); - if (channel_count == 2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x52+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;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_caf.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_caf.c deleted file mode 100644 index fce124882..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_caf.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../util.h" - -VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - // Calculate sample length ... - int32_t num_of_samples=0; - int32_t block_count=0; - - uint32_t loop_start=-1; - - off_t offset=0; - off_t next_block; - off_t file_length; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("cfn",filename_extension(filename))) goto fail; - - /* Check "CAF " ID */ - if (read_32bitBE(0,streamFile)!=0x43414620) goto fail; - - // Calculate sample length ... - file_length=(off_t)get_streamfile_size(streamFile); - - do { - next_block=read_32bitBE(offset+0x04,streamFile); - num_of_samples+=read_32bitBE(offset+0x14,streamFile)/8*14; - - if(read_32bitBE(offset+0x20,streamFile)==read_32bitBE(offset+0x08,streamFile)) { - loop_start=num_of_samples-read_32bitBE(offset+0x14,streamFile)/8*14; - } - offset+=next_block; - block_count++; - } while(offsetchannels=2; - vgmstream->sample_rate=32000; - vgmstream->num_samples=num_of_samples; - - if(loop_start!=-1) { - vgmstream->loop_start_sample=loop_start; - vgmstream->loop_end_sample=num_of_samples; - } - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_caf_blocked; - vgmstream->meta_type = meta_CFN; - - /* open the file for reading by each channel */ - { - for (i=0;i<2;i++) { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x8000); - - if (!vgmstream->ch[i].streamfile) goto fail; - } - } - - caf_block_update(0,vgmstream); - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index dd9a58cd3..990a1c210 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -24,80 +24,288 @@ struct dsp_header { uint16_t loop_ps; int16_t loop_hist1; int16_t loop_hist2; - int16_t channel_count; + int16_t channel_count; /* DSPADPCM.exe ~v2.7 extension */ int16_t block_size; + /* padding/reserved up to 0x60 */ + /* DSPADPCM.exe from GC adds some extra data here (uninitialized MSVC memory?) */ }; -/* nonzero on failure */ -static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE *file) { +/* read the above struct; returns nonzero on failure */ +static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE *streamFile, int big_endian) { + int32_t (*get_32bit)(uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; + int16_t (*get_16bit)(uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; int i; - uint8_t buf[0x4e]; /* usually padded out to 0x60 */ - if (read_streamfile(buf, offset, 0x4e, file) != 0x4e) return 1; + uint8_t buf[0x4e]; - header->sample_count = - get_32bitBE(buf+0x00); - header->nibble_count = - get_32bitBE(buf+0x04); - header->sample_rate = - get_32bitBE(buf+0x08); - header->loop_flag = - get_16bitBE(buf+0x0c); - header->format = - get_16bitBE(buf+0x0e); - header->loop_start_offset = - get_32bitBE(buf+0x10); - header->loop_end_offset = - get_32bitBE(buf+0x14); - header->ca = - get_32bitBE(buf+0x18); + if (read_streamfile(buf, offset, 0x4e, streamFile) != 0x4e) + return 1; + header->sample_count = get_32bit(buf+0x00); + header->nibble_count = get_32bit(buf+0x04); + header->sample_rate = get_32bit(buf+0x08); + header->loop_flag = get_16bit(buf+0x0c); + header->format = get_16bit(buf+0x0e); + header->loop_start_offset = get_32bit(buf+0x10); + header->loop_end_offset = get_32bit(buf+0x14); + header->ca = get_32bit(buf+0x18); for (i=0; i < 16; i++) - header->coef[i] = - get_16bitBE(buf+0x1c+i*2); - header->gain = - get_16bitBE(buf+0x3c); - header->initial_ps = - get_16bitBE(buf+0x3e); - header->initial_hist1 = - get_16bitBE(buf+0x40); - header->initial_hist2 = - get_16bitBE(buf+0x42); - header->loop_ps = - get_16bitBE(buf+0x44); - header->loop_hist1 = - get_16bitBE(buf+0x46); - header->loop_hist2 = - get_16bitBE(buf+0x48); - header->channel_count = - get_16bitBE(buf+0x4a); - header->block_size = - get_16bitBE(buf+0x4c); + header->coef[i] = get_16bit(buf+0x1c+i*0x02); + header->gain = get_16bit(buf+0x3c); + header->initial_ps = get_16bit(buf+0x3e); + header->initial_hist1 = get_16bit(buf+0x40); + header->initial_hist2 = get_16bit(buf+0x42); + header->loop_ps = get_16bit(buf+0x44); + header->loop_hist1 = get_16bit(buf+0x46); + header->loop_hist2 = get_16bit(buf+0x48); + header->channel_count = get_16bit(buf+0x4a); + header->block_size = get_16bit(buf+0x4c); + return 0; +} +static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE *file) { + return read_dsp_header_endian(header, offset, file, 1); +} +static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFILE *file) { + return read_dsp_header_endian(header, offset, file, 0); +} + +static void setup_vgmstream_dsp(VGMSTREAM* vgmstream, struct dsp_header* ch_header) { + int i, j; + + /* set coeffs and initial history (usually 0) */ + for (i = 0; i < vgmstream->channels; i++){ + for (j = 0; j < 16; j++) { + vgmstream->ch[i].adpcm_coef[j] = ch_header[i].coef[j]; + } + vgmstream->ch[i].adpcm_history1_16 = ch_header[i].initial_hist1; + vgmstream->ch[i].adpcm_history2_16 = ch_header[i].initial_hist2; + } +} + +static int dsp_load_header_endian(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing, int big_endian) { + int i; + + /* load standard dsp header per channel */ + for (i = 0; i < channels; i++) { + if (read_dsp_header_endian(&ch_header[i], offset + i*spacing, streamFile, big_endian)) + goto fail; + } + + return 1; +fail: + return 0; +} +static int dsp_load_header(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) { + return dsp_load_header_endian(ch_header, channels, streamFile, offset, spacing, 1); +} +//static int dsp_load_header_le(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) { +// return dsp_load_header_endian(ch_header, channels, streamFile, offset, spacing, 0); +//} +static int check_dsp_format(struct dsp_header* ch_header, int channels) { + int i; + + /* check type==0 and gain==0 */ + for (i = 0; i < channels; i++) { + if (ch_header[i].format || ch_header[i].gain) + goto fail; + } + + return 1; +fail: + return 0; +} +static int check_dsp_samples(struct dsp_header* ch_header, int channels) { + int i; + + /* check for agreement between channels */ + for (i = 0; i < channels - 1; i++) { + if (ch_header[i].sample_count != ch_header[i+1].sample_count || + ch_header[i].nibble_count != ch_header[i+1].nibble_count || + ch_header[i].sample_rate != ch_header[i+1].sample_rate || + ch_header[i].loop_flag != ch_header[i+1].loop_flag || + ch_header[i].loop_start_offset != ch_header[i+1].loop_start_offset || + ch_header[i].loop_end_offset != ch_header[i+1].loop_end_offset ) { + goto fail; + } + } + + return 1; +fail: + return 0; +} +static int check_dsp_initial_ps(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t interleave) { + int i; + + /* check initial predictor/scale */ + for (i = 0; i < channels; i++) { + off_t start_offset = offset + i*interleave; + if (ch_header[i].initial_ps != (uint8_t)read_8bit(start_offset, streamFile)){ + goto fail; + } + } + + return 1; +fail: + return 0; +} +static int check_dsp_loop_ps(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t interleave) { + int i; + + if (!ch_header[0].loop_flag) + return 1; + + /* check loop predictor/scale */ + for (i = 0; i < channels; i++) { + off_t loop_offset = ch_header[i].loop_start_offset; + if (interleave) { + loop_offset = loop_offset / 16 * 8; + loop_offset = (loop_offset / interleave * interleave * channels) + (loop_offset % interleave); + } + + if (ch_header[i].loop_ps != (uint8_t)read_8bit(offset + i*interleave + loop_offset,streamFile)) + goto fail; + } + + return 1; +fail: return 0; } -/* the standard .dsp, as generated by DSPADPCM.exe */ +/* ********************************* */ +/* common parser config as most DSPs are basically the same with minor changes */ +typedef struct { + int little_endian; + int channel_count; + int max_channels; + + int force_loop; /* force full loop */ + int fix_looping; /* fix loop end going past num_samples */ + int fix_loop_start; /* weird files with bad loop start */ + int single_header; /* all channels share header, thus totals are off */ + int ignore_header_agreement; /* sometimes there are minor differences between headers */ + int ignore_loop_check; /* loop info in header should match data, but sometimes it's weird */ //todo check if needed anymore + + off_t header_offset; + size_t header_spacing; + off_t start_offset; + size_t interleave; + + meta_t meta_type; +} dsp_meta; + +#define COMMON_DSP_MAX_CHANNELS 6 +static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *dspm) { + VGMSTREAM * vgmstream = NULL; + int loop_flag; + struct dsp_header ch_header[COMMON_DSP_MAX_CHANNELS]; + + if (dspm->channel_count > dspm->max_channels) + goto fail; + if (dspm->channel_count > COMMON_DSP_MAX_CHANNELS) + goto fail; + + + /* read dsp */ + if (!dsp_load_header_endian(ch_header, dspm->channel_count, streamFile,dspm->header_offset,dspm->header_spacing, !dspm->little_endian)) + goto fail; + + if (dspm->fix_loop_start) { + int i; + for (i = 0; i < dspm->channel_count; i++) { + /* bad/fixed value in loop start */ + if (ch_header[i].loop_flag) + ch_header[i].loop_start_offset = 0x00; + } + } + + if (!check_dsp_format(ch_header, dspm->channel_count)) + goto fail; + + if (!dspm->ignore_header_agreement && !check_dsp_samples(ch_header, dspm->channel_count)) + goto fail; + + if (dspm->single_header && !check_dsp_initial_ps(ch_header, 1, streamFile,dspm->start_offset,dspm->interleave)) + goto fail; + if (!dspm->single_header && !check_dsp_initial_ps(ch_header, dspm->channel_count, streamFile,dspm->start_offset,dspm->interleave)) + goto fail; + + if (!dspm->ignore_loop_check) { + if (dspm->single_header && !check_dsp_loop_ps(ch_header, 1, streamFile,dspm->start_offset,dspm->interleave)) + goto fail; + if (!dspm->single_header && !check_dsp_loop_ps(ch_header, dspm->channel_count, streamFile,dspm->start_offset,dspm->interleave)) + goto fail; + } + + + loop_flag = ch_header[0].loop_flag; + if (dspm->force_loop) + loop_flag = 1; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(dspm->channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = ch_header[0].sample_rate; + vgmstream->num_samples = ch_header[0].sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_header[0].loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset)+1; + + vgmstream->meta_type = dspm->meta_type; + vgmstream->coding_type = coding_NGC_DSP; + if (dspm->interleave > 0 && dspm->interleave < 0x08) + vgmstream->coding_type = coding_NGC_DSP_subint; + vgmstream->layout_type = layout_interleave; + if (dspm->interleave == 0 || vgmstream->coding_type == coding_NGC_DSP_subint) + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = dspm->interleave; + + setup_vgmstream_dsp(vgmstream, ch_header); + + /* don't know why, but it does happen*/ + if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->loop_end_sample = vgmstream->num_samples; + + if (dspm->single_header) { + vgmstream->num_samples /= dspm->channel_count; + vgmstream->loop_start_sample /= dspm->channel_count; + vgmstream->loop_end_sample /= dspm->channel_count; + } + + + if (!vgmstream_open_stream(vgmstream,streamFile,dspm->start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* ********************************* */ + +/* .dsp - standard dsp as generated by DSPADPCM.exe */ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header header; - const off_t start_offset = 0x60; - int i; + const size_t header_size = 0x60; + off_t start_offset; + int i, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("dsp",filename_extension(filename))) goto fail; + /* checks */ + /* .dsp: standard, .adp: Dr. Muto/Battalion Wars (GC) mono files */ + if (!check_extensions(streamFile, "dsp,adp")) + goto fail; - if (read_dsp_header(&header, 0, streamFile)) goto fail; + if (read_dsp_header(&header, 0x00, streamFile)) + goto fail; + + channel_count = 1; + start_offset = header_size; - /* check initial predictor/scale */ if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) - goto fail; - - /* check type==0 and gain==0 */ + goto fail; /* check initial predictor/scale */ if (header.format || header.gain) - goto fail; + goto fail; /* check type==0 and gain==0 */ /* Check for a matching second header. If we find one and it checks * out thoroughly, we're probably not dealing with a genuine mono DSP. @@ -105,13 +313,14 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { * predictor/scale check if the first byte is 0 */ { struct dsp_header header2; - - read_dsp_header(&header2, 0x60, streamFile); + read_dsp_header(&header2, header_size, streamFile); if (header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && header.sample_rate == header2.sample_rate && - header.loop_flag == header2.loop_flag) goto fail; + header.loop_flag == header2.loop_flag) { + goto fail; + } } if (header.loop_flag) { @@ -119,7 +328,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { - /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter + /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ VGM_LOG("DSP (std): bad loop_predictor\n"); //header.loop_flag = 0; @@ -127,123 +336,176 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { } } - /* compare num_samples with nibble count */ - /* - fprintf(stderr,"num samples (literal): %d\n",read_32bitBE(0,streamFile)); - fprintf(stderr,"num samples (nibbles): %d\n",dsp_nibbles_to_samples(read_32bitBE(4,streamFile))); - */ /* build the VGMSTREAM */ - - - vgmstream = allocate_vgmstream(1,header.loop_flag); + vgmstream = allocate_vgmstream(channel_count,header.loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = header.sample_count; vgmstream->sample_rate = header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - header.loop_end_offset)+1; - - /* don't know why, but it does happen*/ - if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->num_samples = header.sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset)+1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen */ vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->meta_type = meta_DSP_STD; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_DSP_STD; - /* coeffs */ - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[0].streamfile) goto fail; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset=start_offset; + { + /* adpcm coeffs/history */ + for (i = 0; i < 16; i++) + vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; + vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; + vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; + } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } -/* the standard multi-channel .dsp, as generated by DSPADPCM.exe */ +/* .dsp - little endian dsp, possibly main Switch .dsp [LEGO Worlds (Switch)] */ +VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + struct dsp_header header; + const size_t header_size = 0x60; + off_t start_offset; + int i, channel_count; + /* checks */ + /* .adpcm: LEGO Worlds */ + if (!check_extensions(streamFile, "adpcm")) + goto fail; + + if (read_dsp_header_le(&header, 0x00, streamFile)) + goto fail; + + channel_count = 1; + start_offset = header_size; + + if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) + goto fail; /* check initial predictor/scale */ + if (header.format || header.gain) + goto fail; /* check type==0 and gain==0 */ + + /* Check for a matching second header. If we find one and it checks + * out thoroughly, we're probably not dealing with a genuine mono DSP. + * In many cases these will pass all the other checks, including the + * predictor/scale check if the first byte is 0 */ + { + struct dsp_header header2; + read_dsp_header_le(&header2, header_size, streamFile); + + if (header.sample_count == header2.sample_count && + header.nibble_count == header2.nibble_count && + header.sample_rate == header2.sample_rate && + header.loop_flag == header2.loop_flag) { + goto fail; + } + } + + if (header.loop_flag) { + off_t loop_off; + /* check loop predictor/scale */ + loop_off = header.loop_start_offset/16*8; + if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { + /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter + * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ + VGM_LOG("DSP (std): bad loop_predictor\n"); + //header.loop_flag = 0; + //goto fail; + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,header.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = header.sample_rate; + vgmstream->num_samples = header.sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset)+1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen */ + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_DSP_STD; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + + { + /* adpcm coeffs/history */ + for (i = 0; i < 16; i++) + vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; + vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; + vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* .dsp - standard multi-channel dsp as generated by DSPADPCM.exe (later revisions) */ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header header; - const off_t header_size = 0x60; + const size_t header_size = 0x60; off_t start_offset; int i, c, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile, filename, sizeof(filename)); - if (strcasecmp("dsp", filename_extension(filename)) && - strcasecmp("mdsp", filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "dsp,mdsp")) + goto fail; - if (read_dsp_header(&header, 0, streamFile)) goto fail; + if (read_dsp_header(&header, 0x00, streamFile)) + goto fail; channel_count = header.channel_count==0 ? 1 : header.channel_count; start_offset = header_size * channel_count; - /* check initial predictor/scale */ - if (header.initial_ps != (uint8_t)read_8bit(start_offset, streamFile)) + /* named .dsp and no channels? likely another interleaved dsp */ + if (check_extensions(streamFile,"dsp") && header.channel_count == 0) goto fail; - /* check type==0 and gain==0 */ + if (header.initial_ps != (uint8_t)read_8bit(start_offset, streamFile)) + goto fail; /* check initial predictor/scale */ if (header.format || header.gain) - goto fail; + goto fail; /* check type==0 and gain==0 */ /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, header.loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = header.sample_count; vgmstream->sample_rate = header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - header.loop_end_offset) + 1; - - /* don't know why, but it does happen*/ - if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->num_samples = header.sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset) + 1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen*/ vgmstream->loop_end_sample = vgmstream->num_samples; - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave_shortblock; vgmstream->meta_type = meta_DSP_STD; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = header.block_size * 8; if (vgmstream->interleave_block_size) - vgmstream->interleave_smallblock_size = (header.nibble_count / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; + vgmstream->interleave_last_block_size = (header.nibble_count / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; for (i = 0; i < channel_count; i++) { if (read_dsp_header(&header, header_size * i, streamFile)) goto fail; - /* coeffs */ + /* adpcm coeffs/history */ for (c = 0; c < 16; c++) vgmstream->ch[i].adpcm_coef[c] = header.coef[c]; - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ vgmstream->ch[i].adpcm_history1_16 = header.initial_hist1; vgmstream->ch[i].adpcm_history2_16 = header.initial_hist2; } @@ -253,554 +515,272 @@ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { return vgmstream; fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } + /* Some very simple stereo variants of standard dsp just use the standard header - * twice and add interleave, or just concatenate the channels. We'll support - * them all here. - * Note that Cstr isn't here, despite using the form of the standard header, - * because its loop values are wacky. */ + * twice and add interleave, or just concatenate the channels. We'll support them all here. */ -/* .stm - * Used in Paper Mario 2, Fire Emblem: Path of Radiance, Cubivore - * I suspected that this was an Intelligent Systems format, but its use in - * Cubivore calls that into question. */ +/* .stm - Intelligent Systems + others (same programmers) full interleaved dsp [Paper Mario TTYD (GC), Fire Emblem: POR (GC), Cubivore (GC)] */ VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + dsp_meta dspm = {0}; - struct dsp_header ch0_header, ch1_header; - int i; - int stm_header_sample_rate; - int channel_count; - const off_t start_offset = 0x100; - off_t first_channel_size; - off_t second_channel_start; + /* checks */ + /* .lstm/dsp: renamed to avoid hijacking Scream Tracker 2 Modules */ + if (!check_extensions(streamFile, "stm,lstm,dsp")) + goto fail; + if (read_16bitBE(0x00, streamFile) != 0x0200) + goto fail; + /* 0x02(2): sample rate, 0x08+: channel sizes/loop offsets? */ - /* check extension, case insensitive */ - /* to avoid collision with Scream Tracker 2 Modules, also ending in .stm - * and supported by default in Winamp, it was policy in the old days to - * rename these files to .dsp */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("stm",filename_extension(filename)) && - strcasecmp("dsp",filename_extension(filename))) goto fail; + dspm.channel_count = read_32bitBE(0x04, streamFile); + dspm.max_channels = 2; + dspm.fix_looping = 1; - /* check intro magic */ - if (read_16bitBE(0, streamFile) != 0x0200) goto fail; - - channel_count = read_32bitBE(4, streamFile); - /* only stereo and mono are known */ - if (channel_count != 1 && channel_count != 2) goto fail; - - first_channel_size = read_32bitBE(8, streamFile); - /* this is bad rounding, wastes space, but it looks like that's what's - * used */ - second_channel_start = ((start_offset+first_channel_size)+0x20)/0x20*0x20; - - /* an additional check */ - stm_header_sample_rate = (uint16_t)read_16bitBE(2, streamFile); - - /* read the DSP headers */ - if (read_dsp_header(&ch0_header, 0x40, streamFile)) goto fail; - if (channel_count == 2) { - if (read_dsp_header(&ch1_header, 0xa0, streamFile)) goto fail; - } - - /* checks for fist channel */ - { - if (ch0_header.sample_rate != stm_header_sample_rate) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(start_offset, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - - if (ch0_header.loop_flag) { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - if (ch0_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) - goto fail; - } - } - - - /* checks for second channel */ - if (channel_count == 2) { - if (ch1_header.sample_rate != stm_header_sample_rate) goto fail; - - /* check for agreement with first channel header */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - /* check initial predictor/scale */ - if (ch1_header.initial_ps != (uint8_t)read_8bit(second_channel_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch1_header.format || ch1_header.gain) - goto fail; - - if (ch1_header.loop_flag) { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch1_header.loop_start_offset/16*8; - /*printf("loop_start_offset=%x\nloop_ps=%x\nloop_off=%x\n",ch1_header.loop_start_offset,ch1_header.loop_ps,second_channel_start+loop_off);*/ - if (ch1_header.loop_ps != (uint8_t)read_8bit(second_channel_start+loop_off,streamFile)) - goto fail; - } - } - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(channel_count, ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - /* don't know why, but it does happen*/ - if (vgmstream->loop_end_sample > vgmstream->num_samples) - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_DSP_STM; - - /* coeffs */ - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - - if (channel_count == 2) { - /* coeffs */ - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - } - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[0].streamfile) goto fail; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset=start_offset; - - if (channel_count == 2) { - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[1].streamfile) goto fail; - - vgmstream->ch[1].channel_start_offset= - vgmstream->ch[1].offset=second_channel_start; - } - - return vgmstream; + dspm.header_offset = 0x40; + dspm.header_spacing = 0x60; + dspm.start_offset = 0x100; + dspm.interleave = (read_32bitBE(0x08, streamFile) + 0x20) / 0x20 * 0x20; /* strange rounding, but works */ + dspm.meta_type = meta_DSP_STM; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } -/* mpdsp: looks like a standard .dsp header, but the data is actually - * interleaved stereo - * The files originally had a .dsp extension, we rename them to .mpdsp so we - * can catch this. - */ - +/* .(mp)dsp - single header + interleaved dsp [Monopoly Party! (GC)] */ VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + dsp_meta dspm = {0}; - struct dsp_header header; - const off_t start_offset = 0x60; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("mpdsp",filename_extension(filename))) goto fail; - - if (read_dsp_header(&header, 0, streamFile)) goto fail; - - /* none have loop flag set, save us from loop code that involves them */ - if (header.loop_flag) goto fail; - - /* check initial predictor/scale */ - if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) + /* checks */ + /* .mpdsp: renamed since standard .dsp would catch it otherwise */ + if (!check_extensions(streamFile, "mpdsp")) goto fail; - /* check type==0 and gain==0 */ - if (header.format || header.gain) - goto fail; - - /* build the VGMSTREAM */ + /* at 0x48 is extra data that could help differenciating these DSPs, but other games + * put similar stuff there, needs more checks (ex. Battallion Wars, Army Men) */ + //0x00005300 60A94000 64FF1200 00000000 00000000 00000000 + /* 0x02(2): sample rate, 0x08+: channel sizes/loop offsets? */ + dspm.channel_count = 2; + dspm.max_channels = 2; + dspm.single_header = 1; - /* no loop flag, but they do loop */ - vgmstream = allocate_vgmstream(2,0); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = header.sample_count/2; - vgmstream->sample_rate = header.sample_rate; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0xf000; - vgmstream->meta_type = meta_DSP_MPDSP; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = header.initial_hist2; - - /* open the file for reading */ - for (i=0;i<2;i++) { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename, - vgmstream->interleave_block_size); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - - return vgmstream; + dspm.header_offset = 0x00; + dspm.header_spacing = 0x00; /* same header for both channels */ + dspm.start_offset = 0x60; + dspm.interleave = 0xf000; + dspm.meta_type = meta_DSP_MPDSP; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } -/* str: a very simple header format with implicit loop values - * it's allways in interleaved stereo format - */ -VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - const off_t start_offset = 0x60; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("str",filename_extension(filename))) goto fail; - - /* always 0xFAAF0001 @ offset 0 */ - if (read_32bitBE(0x00,streamFile)!=0xFAAF0001) goto fail; - - /* build the VGMSTREAM */ - /* always loop & stereo */ - vgmstream = allocate_vgmstream(2,1); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitBE(0x08,streamFile); - vgmstream->sample_rate = read_32bitBE(0x04,streamFile); - - /* always loop to the beginning */ - vgmstream->loop_start_sample=0; - vgmstream->loop_end_sample=vgmstream->num_samples; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x0C,streamFile); - vgmstream->meta_type = meta_DSP_STR; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x10+(i*2),streamFile); - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x30+(i*2),streamFile); - } - - /* open the file for reading */ - for (i=0;i<2;i++) { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename, - vgmstream->interleave_block_size); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -/* a bunch of formats that are identical except for file extension, - * but have different interleaves */ - +/* various dsp with differing extensions and interleave values */ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; + dsp_meta dspm = {0}; char filename[PATH_LIMIT]; - const off_t start_offset = 0xc0; - off_t interleave; - int meta_type; + /* checks */ + if (!check_extensions(streamFile, "dsp,mss,gcm")) + goto fail; - struct dsp_header ch0_header,ch1_header; + dspm.channel_count = 2; + dspm.max_channels = 2; + dspm.fix_looping = 1; - int i; + dspm.header_offset = 0x00; + dspm.header_spacing = 0x60; + dspm.start_offset = 0xc0; - /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strlen(filename) > 7 && !strcasecmp("_lr.dsp",filename+strlen(filename)-7)) { - /* Bomberman Jetters */ - interleave = 0x14180; - meta_type = meta_DSP_JETTERS; + dspm.interleave = 0x14180; + dspm.meta_type = meta_DSP_JETTERS; /* Bomberman Jetters (GC) */ } else if (!strcasecmp("mss",filename_extension(filename))) { - interleave = 0x1000; - meta_type = meta_DSP_MSS; - } else if (!strcasecmp("gcm",filename_extension(filename))) { - interleave = 0x8000; - meta_type = meta_DSP_GCM; - } else goto fail; - - if (read_dsp_header(&ch0_header, 0, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, 0x60, streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(start_offset+interleave,streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain || - ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) { + dspm.interleave = 0x1000; + dspm.meta_type = meta_DSP_MSS; /* Free Radical GC games */ /* Timesplitters 2 GC's ts2_atom_smasher_44_fx.mss differs slightly in samples but plays ok */ - if (meta_type != meta_DSP_MSS) - goto fail; + dspm.ignore_header_agreement = 1; + } else if (!strcasecmp("gcm",filename_extension(filename))) { + dspm.interleave = 0x8000; + dspm.meta_type = meta_DSP_GCM; /* some of Traveller's Tales games */ + } else { + goto fail; } - if (ch0_header.loop_flag) { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - loop_off = (loop_off/interleave*interleave*2) + (loop_off%interleave); - if (ch0_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off+interleave,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(2,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - /* TODO: adjust for interleave? */ - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - vgmstream->meta_type = meta_type; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - for (i=0;i<2;i++) { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+i*interleave; - } - - return vgmstream; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } -/* IDSP with multiple standard DSP headers - from SSB4 (3DS), Tekken Tag Tournament 2 (Wii U) */ -#define MULTI_IDSP_MAX_CHANNELS 8 +/* IDSP - Namco header + interleaved dsp [SSB4 (3DS), Tekken Tag Tournament 2 (WiiU)] */ VGMSTREAM * init_vgmstream_3ds_idsp(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + off_t offset; + + /* checks */ + if (!check_extensions(streamFile, "idsp,nus3bank")) + goto fail; + + /* try NUS3BANK container */ + if (read_32bitBE(0x00,streamFile) == 0x4E555333) { /* "NUS3" */ + offset = 0x14 + read_32bitLE(0x10, streamFile); /* header size */ + offset += read_32bitLE(0x1C, streamFile) + 0x08; + offset += read_32bitLE(0x24, streamFile) + 0x08; + offset += read_32bitLE(0x2C, streamFile) + 0x08; + offset += read_32bitLE(0x34, streamFile) + 0x08; + offset += read_32bitLE(0x3C, streamFile) + 0x08; + offset += read_32bitLE(0x44, streamFile) + 0x08; + offset += 0x08; + } + else { + offset = 0x00; + } + + if (read_32bitBE(offset,streamFile) != 0x49445350) /* "IDSP" */ + goto fail; + /* 0x0c: sample rate, 0x10: num_samples, 0x14: loop_start_sample, 0x18: loop_start_sample */ + + dspm.channel_count = read_32bitBE(offset+0x08, streamFile); + dspm.max_channels = 8; + /* games do adjust loop_end if bigger than num_samples (only happens in user-created IDSPs) */ + dspm.fix_looping = 1; + + dspm.header_offset = read_32bitBE(offset+0x20,streamFile) + offset; + dspm.header_spacing = read_32bitBE(offset+0x24,streamFile); + dspm.start_offset = read_32bitBE(offset+0x28,streamFile) + offset; + dspm.interleave = read_32bitBE(offset+0x1c,streamFile); /* usually 0x10 */ + if (dspm.interleave == 0) /* Taiko no Tatsujin: Atsumete Tomodachi Daisakusen (WiiU) */ + dspm.interleave = read_32bitBE(offset+0x2c,streamFile); /* half interleave, use channel size */ + + dspm.meta_type = meta_3DS_IDSP; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +/* sadb - Procyon Studio header + interleaved dsp [Shiren the Wanderer 3 (Wii), Disaster: Day of Crisis (Wii)] */ +VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(streamFile, "sad")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x73616462) /* "sadb" */ + goto fail; + + dspm.channel_count = read_8bit(0x32, streamFile); + dspm.max_channels = 2; + + dspm.header_offset = 0x80; + dspm.header_spacing = 0x60; + dspm.start_offset = read_32bitBE(0x48,streamFile); + dspm.interleave = 0x10; + + dspm.meta_type = meta_DSP_SADB; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +#define WSI_MAX_CHANNELS 2 +/* .wsi - blocked dsp [Alone in the Dark (Wii)] */ +VGMSTREAM * init_vgmstream_wsi(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - - off_t idsp_offset = 0; - off_t start_offset; - off_t interleave; - - struct dsp_header ch_headers[MULTI_IDSP_MAX_CHANNELS]; - int i, ch; + off_t start_offset, header_offset; + size_t header_spacing; + struct dsp_header ch_header[WSI_MAX_CHANNELS]; int channel_count; - /* check extension, case insensitive */ - //if (check_extensions(streamFile,"idsp,nus3bank")) goto fail; + /* checks */ + if (!check_extensions(streamFile, "wsi")) + goto fail; - /* check header magic */ - if( read_32bitBE(0x0,streamFile) != 0x49445350 ) /* "IDSP" */ + /* I don't know if this is actually the channel count, or a block type + * for the first block. Won't know until I see a mono .wsi */ + channel_count = read_32bitBE(0x04,streamFile); + if (channel_count != 2) goto fail; + + /* check for consistent block headers */ { - /* try NUS3 format instead */ - if (read_32bitBE(0,streamFile) != 0x4E555333) goto fail; /* "NUS3" */ - - /* Header size */ - idsp_offset = 0x14 + read_32bitLE( 0x10, streamFile ); - - idsp_offset += read_32bitLE( 0x1C, streamFile ) + 8; - idsp_offset += read_32bitLE( 0x24, streamFile ) + 8; - idsp_offset += read_32bitLE( 0x2C, streamFile ) + 8; - idsp_offset += read_32bitLE( 0x34, streamFile ) + 8; - idsp_offset += read_32bitLE( 0x3C, streamFile ) + 8; - idsp_offset += read_32bitLE( 0x44, streamFile ) + 8; - idsp_offset += 8; + off_t block_offset; + off_t block_size_has_been; + int i; + + block_offset = read_32bitBE(0x00,streamFile); + if (block_offset < 0x08) goto fail; - /* check magic */ - if (read_32bitBE(idsp_offset,streamFile) != 0x49445350) goto fail; /* "IDSP" */ - } - - channel_count = read_32bitBE(idsp_offset+0x8, streamFile); - if (channel_count > MULTI_IDSP_MAX_CHANNELS) goto fail; + block_size_has_been = block_offset; - start_offset = read_32bitBE(idsp_offset+0x28,streamFile) + idsp_offset; - interleave = 0x10; + /* check 4 blocks, to get an idea */ + for (i = 0; i < 4*channel_count; i++) { + off_t block_size = read_32bitBE(block_offset,streamFile); - /* read standard dsp header per channel and do some validations */ - for (ch=0; ch < channel_count; ch++) { - /* read 0x60 header per channel */ - if (read_dsp_header(&ch_headers[ch], idsp_offset + 0x40 + 0x60*ch, streamFile)) goto fail; + if (block_size < 0x10) + goto fail; /* expect at least the block header */ + if (i%channel_count+1 != read_32bitBE(block_offset+0x08,streamFile)) + goto fail; /* expect the channel numbers to alternate */ - /* check initial values */ - if (ch_headers[ch].initial_ps != (uint8_t)read_8bit(start_offset + interleave*ch, streamFile)) goto fail; - if (ch_headers[ch].format || ch_headers[ch].gain) goto fail; + if (i%channel_count==0) + block_size_has_been = block_size; + else if (block_size != block_size_has_been) + goto fail; /* expect every block in a set of channels to have the same size */ - /* check for agreement with prev channel*/ - if (ch > 0 && ( - ch_headers[ch].sample_count != ch_headers[ch-1].sample_count || - ch_headers[ch].nibble_count != ch_headers[ch-1].nibble_count || - ch_headers[ch].sample_rate != ch_headers[ch-1].sample_rate || - ch_headers[ch].loop_flag != ch_headers[ch-1].loop_flag || - ch_headers[ch].loop_start_offset != ch_headers[ch-1].loop_start_offset || - ch_headers[ch].loop_end_offset != ch_headers[ch-1].loop_end_offset - )) goto fail; - - -#if 0 //this is wrong for >2ch and will fail - /* check loop predictor/scale */ - if (ch_headers[ch].loop_flag) { - off_t loop_off; - loop_off = ch_headers[ch].loop_start_offset / 8 / channel_count * 8; - loop_off = (loop_off / interleave * interleave * channel_count) + (loop_off%interleave); - if (ch_headers[ch].loop_ps != (uint8_t)read_8bit(start_offset + loop_off + interleave*ch, streamFile)) goto fail; + block_offset += block_size; } -#endif } - /* check first channel (implicitly all ch) agree with main sample rate */ - if (ch_headers[0].sample_rate != read_32bitBE(idsp_offset+0xc, streamFile)) goto fail; + + start_offset = read_32bitBE(0x00, streamFile); + header_offset = start_offset + 0x10; + header_spacing = read_32bitBE(start_offset,streamFile); + + /* read dsp */ + if (!dsp_load_header(ch_header, channel_count, streamFile,header_offset,header_spacing)) goto fail; + if (!check_dsp_format(ch_header, channel_count)) goto fail; + if (!check_dsp_samples(ch_header, channel_count)) goto fail; + //if (!check_dsp_initial_ps(ch_header, channel_count, streamFile,start_offset,interleave)) goto fail; + //if (!check_dsp_loop_ps(ch_header, channel_count, streamFile,start_offset,interleave)) goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,ch_headers[0].loop_flag); + vgmstream = allocate_vgmstream(channel_count,ch_header[0].loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = ch_headers[0].sample_count; - vgmstream->sample_rate = ch_headers[0].sample_rate; + vgmstream->sample_rate = ch_header[0].sample_rate; - /* TODO: adjust for interleave? */ - vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_headers[0].loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_headers[0].loop_end_offset) + 1; - /* games will ignore loop_end and use num_samples if going over it - * only needed for user-created IDSPs, but it's possible loop_end_sample shouldn't add +1 above */ - if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->num_samples = ch_header[0].sample_count / 14 * 14; /* remove incomplete last frame */ + vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_header[0].loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset)+1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen*/ vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->meta_type = meta_DSP_WSI; vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = channel_count > 1 ? layout_interleave : layout_none; - vgmstream->interleave_block_size = interleave; - vgmstream->meta_type = meta_3DS_IDSP; + vgmstream->layout_type = layout_blocked_wsi; + + setup_vgmstream_dsp(vgmstream, ch_header); - /* set DSP coefs/history */ - for (ch=0; ch < channel_count; ch++) { - for (i=0;i<16;i++) { - vgmstream->ch[ch].adpcm_coef[i] = ch_headers[ch].coef[i]; - } - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[ch].adpcm_history1_16 = ch_headers[ch].initial_hist1; - vgmstream->ch[ch].adpcm_history2_16 = ch_headers[ch].initial_hist2; - } - - /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; + block_update_wsi(start_offset,vgmstream); + + /* first block has DSP header */ + { + int i; + + vgmstream->current_block_size -= 0x60; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset += 0x60; + } + } + return vgmstream; fail: @@ -809,408 +789,30 @@ fail: } -/* sadb - .SAD files, two standard DSP headers */ -VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - off_t start_offset; - off_t interleave; - - struct dsp_header ch0_header,ch1_header; - int i; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sad",filename_extension(filename))) goto fail; - - /* check header magic */ - if (read_32bitBE(0x0,streamFile) != 0x73616462) goto fail; /* "sadb" */ - - channel_count = read_8bit(0x32, streamFile); - if (channel_count != 1 && channel_count != 2) goto fail; - - if (read_dsp_header(&ch0_header, 0x80, streamFile)) goto fail; - if (channel_count == 2 && read_dsp_header(&ch1_header, 0xe0, streamFile)) goto fail; - - start_offset = read_32bitBE(0x48,streamFile); - interleave = 16; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) - goto fail; - if (channel_count == 2 && ch1_header.initial_ps != (uint8_t)read_8bit(start_offset+interleave,streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain || - (channel_count == 2 &&(ch1_header.format || ch1_header.gain))) - goto fail; - - /* check for agreement */ - if ( channel_count == 2 &&( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - )) goto fail; - - if (ch0_header.loop_flag) { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/8/channel_count*8; - loop_off = (loop_off/interleave*interleave*channel_count) + (loop_off%interleave); - if (ch0_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) - goto fail; - if (channel_count == 2 && - ch1_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off+interleave,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(channel_count,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - /* TODO: adjust for interleave? */ - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = channel_count == 2 ? layout_interleave : layout_none; - vgmstream->interleave_block_size = interleave; - vgmstream->meta_type = meta_DSP_SADB; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - if (channel_count == 2) - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (channel_count == 2) { - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - vgmstream->ch[1].streamfile = vgmstream->ch[0].streamfile; - } - - if (!vgmstream->ch[0].streamfile) goto fail; - /* open the file for reading */ - for (i=0;ich[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+i*interleave; - } - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -/* .wsi as found in Alone in the Dark for Wii */ -/* These appear to be standard .dsp, but interleaved in a blocked format */ - -VGMSTREAM * init_vgmstream_wsi(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header header[2]; - off_t start_offset[2]; - - int channel_count; - size_t est_block_size = 0; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("wsi",filename_extension(filename))) goto fail; - - /* I don't know if this is actually the channel count, or a block type - for the first block. Won't know until I see a mono .wsi */ - channel_count = read_32bitBE(0x04,streamFile); - - /* I've only allocated two headers, and I want to be alerted if a mono - .wsi shows up */ - if (channel_count != 2) goto fail; - - /* check for consistent block headers */ - { - off_t check_offset; - off_t block_size_has_been; - int i; - - check_offset = read_32bitBE(0x0,streamFile); - if (check_offset < 8) goto fail; - - block_size_has_been = check_offset; - - /* check 4 blocks, to get an idea */ - for (i=0;i<4*channel_count;i++) { - off_t block_size; - block_size = read_32bitBE(check_offset,streamFile); - - /* expect at least the block header */ - if (block_size < 0x10) goto fail; - - /* expect the channel numbers to alternate */ - if (i%channel_count+1 != read_32bitBE(check_offset+8,streamFile)) goto fail; - - /* expect every block in a set of channels to have the same size */ - if (i%channel_count==0) block_size_has_been = block_size; - else if (block_size != block_size_has_been) goto fail; - - /* get an estimate of block size for buffer sizing */ - if (block_size > est_block_size) est_block_size = block_size; - - check_offset += block_size; - } - } - - /* look at DSP headers */ - - { - off_t check_offset; - int i; - - check_offset = read_32bitBE(0x0,streamFile); - - for (i=0;inum_samples = header[0].sample_count/14*14; - vgmstream->sample_rate = header[0].sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - header[0].loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - header[0].loop_end_offset)+1; - - /* don't know why, but it does happen*/ - if (vgmstream->loop_end_sample > vgmstream->num_samples) - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_wsi_blocked; - vgmstream->meta_type = meta_DSP_WSI; - - /* coeffs */ - { - int i,j; - for (j=0;jch[j].adpcm_coef[i] = header[j].coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[j].adpcm_history1_16 = header[j].initial_hist1; - vgmstream->ch[j].adpcm_history2_16 = header[j].initial_hist2; - } - } - - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,est_block_size*4); - - if (!vgmstream->ch[0].streamfile) goto fail; - - wsi_block_update(read_32bitBE(0,streamFile),vgmstream); - - { - int i; - - for (i=0;ich[i].streamfile = vgmstream->ch[0].streamfile; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset[i]; - } - - } - - /* first block isn't full of musics */ - vgmstream->current_block_size -= 0x60; - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -/* SWD (found in Conflict - Desert Storm 1 & 2 */ +/* SWD - PSF chunks + interleaved dsps [Conflict: Desert Storm 1 & 2] */ VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - off_t interleave; + dsp_meta dspm = {0}; - struct dsp_header ch0_header, ch1_header; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("swd",filename_extension(filename))) goto fail; - - if (read_dsp_header(&ch0_header, 0x08, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, 0x68, streamFile)) goto fail; - - /* check header magic */ - if (read_32bitBE(0x00,streamFile) != 0x505346D1) /* PSF\0xD1 */ + /* checks */ + if (!check_extensions(streamFile, "swd")) goto fail; - start_offset = 0xC8; - interleave = 0x8; + //todo blocked layout when first chunk is 0x50534631 (count + table of 0x0c with offset/sizes) -#if 0 - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(start_offset+interleave,streamFile)) - goto fail; -#endif - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain || - ch1_header.format || ch1_header.gain) + if (read_32bitBE(0x00,streamFile) != 0x505346d1) /* PSF\0xd1 */ goto fail; - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; + dspm.channel_count = 2; + dspm.max_channels = 2; -#if 0 - if (ch0_header.loop_flag) { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - loop_off = (loop_off/interleave*interleave*2) + (loop_off%interleave); - if (ch0_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off+interleave,streamFile)) - goto fail; - } -#endif - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(2,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - /* TODO: adjust for interleave? */ - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - vgmstream->meta_type = meta_NGC_SWD; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - vgmstream->ch[1].streamfile = vgmstream->ch[0].streamfile; - - if (!vgmstream->ch[0].streamfile) goto fail; - /* open the file for reading */ - for (i=0;i<2;i++) { - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+i*interleave; - } - - return vgmstream; + dspm.header_offset = 0x08; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + 0x60 * dspm.channel_count; + dspm.interleave = 0x08; + dspm.meta_type = meta_NGC_SWD; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } @@ -1219,7 +821,7 @@ fail: Lego Indiana Jones - The Original Adventures (Wii) Lego Indiana Jones 2 - The Adventure Continues (Wii) Lego Star Wars - The Complete Saga (Wii) - Lego The Lord of the Rings (Wii) + Lego The Lord of the Rings (Wii) The Chronicles of Narnia - Prince Caspian (Wii) */ VGMSTREAM * init_vgmstream_wii_idsp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; @@ -1232,7 +834,7 @@ VGMSTREAM * init_vgmstream_wii_idsp(STREAMFILE *streamFile) { /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if ((strcasecmp("gcm",filename_extension(filename))) && - (strcasecmp("idsp",filename_extension(filename)))) + (strcasecmp("idsp",filename_extension(filename)))) goto fail; /* check header magic */ @@ -1255,14 +857,14 @@ VGMSTREAM * init_vgmstream_wii_idsp(STREAMFILE *streamFile) { start_offset = 0xe0; } - else if (read_32bitBE(0x4, streamFile) == 3 && //Lego The Lord of the Rings (Wii) - read_32bitBE(0x8, streamFile) == 0x12c) - { - if (read_dsp_header(&ch0_header, 0x20, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, 0x80, streamFile)) goto fail; + else if (read_32bitBE(0x4, streamFile) == 3 && //Lego The Lord of the Rings (Wii) + read_32bitBE(0x8, streamFile) == 0x12c) + { + if (read_dsp_header(&ch0_header, 0x20, streamFile)) goto fail; + if (read_dsp_header(&ch1_header, 0x80, streamFile)) goto fail; - start_offset = 0xe0; - } + start_offset = 0xe0; + } else goto fail; interleave = read_32bitBE(0xc, streamFile); @@ -1309,10 +911,8 @@ VGMSTREAM * init_vgmstream_wii_idsp(STREAMFILE *streamFile) { vgmstream->sample_rate = ch0_header.sample_rate; /* TODO: adjust for interleave? */ - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch0_header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch0_header.loop_end_offset)+1; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; @@ -1350,1281 +950,306 @@ fail: return NULL; } -/* .wsd files, two DSP files stuck together */ -/* found in Phantom Brave Wii */ +/* .wsd - Custom header + full interleaved dsp [Phantom Brave (Wii)] */ VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + dsp_meta dspm = {0}; - off_t channel_1_start, channel_2_start, channel_1_size, channel_2_size; - - struct dsp_header ch0_header,ch1_header; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("wsd",filename_extension(filename))) goto fail; - - /* read .wsd header */ - channel_1_start = read_32bitBE(0x0,streamFile); - channel_2_start = read_32bitBE(0x4,streamFile); - channel_1_size = read_32bitBE(0x8,streamFile); - channel_2_size = read_32bitBE(0xc,streamFile); - - /* check header */ - if (channel_1_size != channel_2_size) goto fail; - if (channel_1_start != 0x20) goto fail; - if (channel_1_size < 0x20 || channel_2_size < 0x20) goto fail; - if (channel_1_start + channel_1_size > channel_2_start) goto fail; - if (channel_2_start + channel_2_size > get_streamfile_size(streamFile)) + /* checks */ + if (!check_extensions(streamFile, "wsd")) + goto fail; + if (read_32bitBE(0x08,streamFile) != read_32bitBE(0x0c,streamFile)) /* channel sizes */ goto fail; - /* get DSP headers */ - if (read_dsp_header(&ch0_header, channel_1_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, channel_2_start, streamFile)) goto fail; + dspm.channel_count = 2; + dspm.max_channels = 2; - /* check initial predictor/scale */ - if (ch0_header.initial_ps != - (uint8_t)read_8bit(channel_1_start + 0x60, streamFile)) - goto fail; - - if (ch1_header.initial_ps != - (uint8_t)read_8bit(channel_2_start + 0x60, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain || - ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - if (ch0_header.loop_flag) { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - if (ch0_header.loop_ps != - (uint8_t)read_8bit(channel_1_start+0x60+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != - (uint8_t)read_8bit(channel_2_start+0x60+loop_off,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(2,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_DSP_WII_WSD; - - /* coeffs */ - { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) goto fail; - - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) goto fail; - - vgmstream->ch[0].channel_start_offset = - vgmstream->ch[0].offset=channel_1_start+0x60; - vgmstream->ch[1].channel_start_offset = - vgmstream->ch[1].offset=channel_2_start+0x60; - - return vgmstream; + dspm.header_offset = read_32bitBE(0x00,streamFile); + dspm.header_spacing = read_32bitBE(0x04,streamFile) - dspm.header_offset; + dspm.start_offset = dspm.header_offset + 0x60; + dspm.interleave = dspm.header_spacing; + dspm.meta_type = meta_DSP_WII_WSD; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } - -/* .ddsp files, two DSP files stuck together, without additional header */ +/* .ddsp - full interleaved dsp [The Sims 2 - Pets (Wii)] */ VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + dsp_meta dspm = {0}; - off_t channel_1_start, channel_2_start, channel_1_size, channel_2_size; - - struct dsp_header ch0_header,ch1_header; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ddsp",filename_extension(filename))) goto fail; - - /* read .wsd header */ - channel_1_start = 0; - channel_2_start = (get_streamfile_size(streamFile)/2); - channel_1_size = (get_streamfile_size(streamFile)/2)-0x60; - channel_2_size = (get_streamfile_size(streamFile)/2)-0x60; - - /* check header */ - if (channel_1_size != channel_2_size) goto fail; - if (channel_1_start != 0x0) goto fail; - if (channel_1_size < 0x20 || channel_2_size < 0x20) goto fail; - if (channel_1_start + channel_1_size > channel_2_start) goto fail; - if (channel_2_start + channel_2_size > get_streamfile_size(streamFile)) + /* checks */ + if (!check_extensions(streamFile, "ddsp")) goto fail; - /* get DSP headers */ - if (read_dsp_header(&ch0_header, channel_1_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, channel_2_start, streamFile)) goto fail; + dspm.channel_count = 2; + dspm.max_channels = 2; - /* check initial predictor/scale */ - if (ch0_header.initial_ps != - (uint8_t)read_8bit(channel_1_start + 0x60, streamFile)) - goto fail; - - if (ch1_header.initial_ps != - (uint8_t)read_8bit(channel_2_start + 0x60, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain || - ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - if (ch0_header.loop_flag) { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - if (ch0_header.loop_ps != - (uint8_t)read_8bit(channel_1_start+0x60+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != - (uint8_t)read_8bit(channel_2_start+0x60+loop_off,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(2,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_DSP_DDSP; - - /* coeffs */ - { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) goto fail; - - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) goto fail; - - vgmstream->ch[0].channel_start_offset = - vgmstream->ch[0].offset=channel_1_start+0x60; - vgmstream->ch[1].channel_start_offset = - vgmstream->ch[1].offset=channel_2_start+0x60; - - return vgmstream; + dspm.header_offset = 0x00; + dspm.header_spacing = (get_streamfile_size(streamFile) / dspm.channel_count); + dspm.start_offset = 0x60; + dspm.interleave = dspm.header_spacing; + dspm.meta_type = meta_DSP_DDSP; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } - -/* .was files, DSP file(s), with additional iSWS header */ +/* iSWS - Sumo Digital header + interleaved dsp [DiRT 2 (Wii), F1 2009 (Wii)] */ VGMSTREAM * init_vgmstream_wii_was(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header ch0_header,ch1_header; - off_t ch1_header_start, ch2_header_start, ch1_start, ch2_start; - int channel_count; - int i; + dsp_meta dspm = {0}; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if ((strcasecmp("dsp",filename_extension(filename))) && - (strcasecmp("isws",filename_extension(filename))) && - (strcasecmp("was",filename_extension(filename)))) - goto fail; + /* checks */ + if (!check_extensions(streamFile, "was,dsp,isws")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x69535753) /* "iSWS" */ + goto fail; - /* read iSWS header */ - if (read_32bitBE(0x0,streamFile) != 0x69535753) - goto fail; + dspm.channel_count = read_32bitBE(0x08,streamFile); + dspm.max_channels = 2; - channel_count = read_32bitBE(0x08,streamFile); - - if (channel_count == 1) - { - - ch1_header_start = 0x20; - ch1_start = 0x80; - - /* get DSP headers */ - if (read_dsp_header(&ch0_header, ch1_header_start, streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(ch1_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - - if (ch0_header.loop_flag) - { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - if (ch0_header.loop_ps != (uint8_t)read_8bit(ch1_start+loop_off,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(1,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_WII_WAS; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) - goto fail; - vgmstream->ch[0].channel_start_offset = vgmstream->ch[0].offset=ch1_start; - - } - else if (channel_count == 2) - { - - - ch1_header_start = 0x20; - ch2_header_start = 0x80; - ch1_start = 0xE0; - ch2_start = 0xE0 + (read_32bitBE(0x10,streamFile)); - - /* get DSP headers */ - if (read_dsp_header(&ch0_header, ch1_header_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, ch2_header_start, streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(ch1_start, streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(ch2_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - if (ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - if (ch0_header.loop_flag) - { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - - if (ch0_header.loop_ps != (uint8_t)read_8bit(ch1_start+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != (uint8_t)read_8bit(ch2_start+loop_off,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(2,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x10,streamFile); - vgmstream->meta_type = meta_WII_WAS; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) - goto fail; - vgmstream->ch[0].channel_start_offset = vgmstream->ch[0].offset=ch1_start; - - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) - goto fail; - vgmstream->ch[1].channel_start_offset = vgmstream->ch[1].offset=ch2_start; - - } - else - { - goto fail; - } - - return vgmstream; - + dspm.header_offset = 0x08 + read_32bitBE(0x04,streamFile); + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; + dspm.interleave = read_32bitBE(0x10,streamFile); + dspm.meta_type = meta_WII_WAS; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } - -/* .str found in Micro Machines, Superman: Shadow of Apokolips */ +/* .str - Infogrames raw interleaved dsp [Micro Machines (GC), Superman: Shadow of Apokolips (GC)] */ VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header ch0_header,ch1_header; - off_t ch1_header_start, ch2_header_start, ch1_start, ch2_start; - int channel_count; - int i; + dsp_meta dspm = {0}; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("str",filename_extension(filename))) - goto fail; + /* checks */ + if (!check_extensions(streamFile, "str")) + goto fail; - channel_count = 2; + dspm.channel_count = 2; + dspm.max_channels = 2; - ch1_header_start = 0x00; - ch2_header_start = 0x80; - ch1_start = 0x800; - ch2_start = 0x4800; - - /* get DSP headers */ - if (read_dsp_header(&ch0_header, ch1_header_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, ch2_header_start, streamFile)) goto fail; + dspm.header_offset = 0x00; + dspm.header_spacing = 0x80; + dspm.start_offset = 0x800; + dspm.interleave = 0x4000; - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(ch1_start, streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(ch2_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - if (ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - if (ch0_header.loop_flag) - { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - - if (ch0_header.loop_ps != (uint8_t)read_8bit(ch1_start+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != (uint8_t)read_8bit(ch2_start+loop_off,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x4000; - vgmstream->meta_type = meta_DSP_STR_IG; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) - goto fail; - vgmstream->ch[0].channel_start_offset = vgmstream->ch[0].offset=ch1_start; - - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) - goto fail; - vgmstream->ch[1].channel_start_offset = vgmstream->ch[1].offset=ch2_start; - - return vgmstream; - - + dspm.meta_type = meta_DSP_STR_IG; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } -/* .dsp found in: Speed Challenge - Jacques Villeneuve's Racing Vision (NGC) - XIII (NGC) - always 2 channels, and an interleave of 0x8 */ +/* .dsp - Ubisoft interleaved dsp with bad loop start [Speed Challenge: Jacques Villeneuve's Racing Vision (GC), XIII (GC)] */ VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header ch0_header,ch1_header; - off_t ch1_header_start, ch2_header_start, ch1_start, ch2_start; - int channel_count; - int i; + dsp_meta dspm = {0}; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("dsp",filename_extension(filename))) - goto fail; + /* checks */ + if (!check_extensions(streamFile, "dsp")) + goto fail; - channel_count = 2; - - ch1_header_start = 0x00; - ch2_header_start = 0x60; - ch1_start = 0xC0; - ch2_start = 0xC8; - - /* get DSP headers */ - if (read_dsp_header(&ch0_header, ch1_header_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, ch2_header_start, streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(ch1_start, streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(ch2_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - if (ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - //ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - if (ch0_header.loop_flag) - { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = 0x0; //ch0_header.loop_start_offset/16*8; - - if (ch0_header.loop_ps != (uint8_t)read_8bit(ch1_start+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != (uint8_t)read_8bit(ch2_start+loop_off,streamFile)) - goto fail; - } - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, ch1_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = 0x0; //dsp_nibbles_to_samples(ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x8; - vgmstream->meta_type = meta_DSP_XIII; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) - goto fail; - vgmstream->ch[0].channel_start_offset = vgmstream->ch[0].offset=ch1_start; - - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) - goto fail; - vgmstream->ch[1].channel_start_offset = vgmstream->ch[1].offset=ch2_start; - - return vgmstream; + dspm.channel_count = 2; + dspm.max_channels = 2; + dspm.fix_loop_start = 1; /* loop flag but strange loop start instead of 0 (maybe shouldn't loop) */ + dspm.header_offset = 0x00; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.header_spacing * dspm.channel_count; + dspm.interleave = 0x08; + dspm.meta_type = meta_DSP_XIII; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } - -/* .ndp found in Vertigo (WII) */ +/* NPD - Icon Games header + subinterleaved DSPs [Vertigo (Wii), Build n' Race (Wii)] */ VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header ch0_header,ch1_header; - off_t ch1_header_start, ch2_header_start, ch1_start, ch2_start; - int i; + dsp_meta dspm = {0}; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ndp",filename_extension(filename))) - goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x4E445000) /* NDP */ - goto fail; - - /* check size */ - if ((read_32bitLE(0x8,streamFile)+0x18 != get_streamfile_size(streamFile))) /* NDP */ - goto fail; - - //channel_count = (read_16bitLE(0x10,streamFile) != 2); - - ch1_header_start = 0x18; - ch2_header_start = 0x78; - ch1_start = 0xD8; - ch2_start = 0xDC; - - /* get DSP headers */ - if (read_dsp_header(&ch0_header, ch1_header_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, ch2_header_start, streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(ch1_start, streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(ch2_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - if (ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - if (ch0_header.loop_flag) - { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - - if (ch0_header.loop_ps != (uint8_t)read_8bit(ch1_start+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != (uint8_t)read_8bit(ch2_start+loop_off,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(2, ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP_subint; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x4; - vgmstream->meta_type = meta_WII_NDP; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream,streamFile,ch1_start)) + /* checks */ + if (!check_extensions(streamFile, "ndp")) goto fail; - return vgmstream; + if (read_32bitBE(0x00,streamFile) != 0x4E445000) /* "NDP\0" */ + goto fail; + if (read_32bitLE(0x08,streamFile) + 0x18 != get_streamfile_size(streamFile)) + goto fail; + /* 0x0c: sample rate */ + dspm.channel_count = read_32bitLE(0x10,streamFile); + dspm.max_channels = 2; + + dspm.header_offset = 0x18; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; + dspm.interleave = 0x04; + + dspm.meta_type = meta_WII_NDP; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - close_vgmstream(vgmstream); return NULL; } -/* found in "Cabelas" games, always stereo, looped and an interleave of 0x10 bytes */ +/* Cabela's series (Magic Wand dev?) - header + interleaved dsp [Cabela's Big Game Hunt 2005 Adventures (GC), Cabela's Outdoor Adventures (GC)] */ VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header ch0_header,ch1_header; - off_t ch1_header_start, ch2_header_start, ch1_start, ch2_start; - int channel_count; - int i; + dsp_meta dspm = {0}; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("dsp",filename_extension(filename))) - goto fail; - - channel_count = 2; - - ch1_header_start = 0x00; - ch2_header_start = 0x60; - ch1_start = 0xC0; - ch2_start = 0xD0; - - /* get DSP headers */ - if (read_dsp_header(&ch0_header, ch1_header_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, ch2_header_start, streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(ch1_start, streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(ch2_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - if (ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, 1); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - vgmstream->meta_type = meta_DSP_CABELAS; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) - goto fail; - vgmstream->ch[0].channel_start_offset = vgmstream->ch[0].offset=ch1_start; - - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) - goto fail; - vgmstream->ch[1].channel_start_offset = vgmstream->ch[1].offset=ch2_start; - - return vgmstream; - - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - - -/* dual dsp header with additional "AAAp" header, found in Vexx (NGC) and Turok: Evolution (NGC) */ -VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header ch0_header,ch1_header; - off_t ch1_header_start, ch2_header_start, ch1_start, ch2_start; - int channel_count; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("dsp",filename_extension(filename))) - goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x41414170) /* AAAp */ - goto fail; - - channel_count = (uint16_t)read_16bitBE(0x6,streamFile); - - ch1_header_start = 0x08; - ch2_header_start = 0x68; - ch1_start = 0xC8; - ch2_start = ch1_start + (uint16_t)read_16bitBE(0x4,streamFile); - - /* get DSP headers */ - if (read_dsp_header(&ch0_header, ch1_header_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, ch2_header_start, streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(ch1_start, streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(ch2_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - if (ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - if (ch0_header.loop_flag) - { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - - if (ch0_header.loop_ps != (uint8_t)read_8bit(ch1_start+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != (uint8_t)read_8bit(ch2_start+loop_off,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = ch2_start-ch1_start; - vgmstream->meta_type = meta_NGC_DSP_AAAP; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) - goto fail; - vgmstream->ch[0].channel_start_offset = vgmstream->ch[0].offset=ch1_start; - - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) - goto fail; - vgmstream->ch[1].channel_start_offset = vgmstream->ch[1].offset=ch2_start; - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -/* .dspw files, multiple DSP files stuck together */ -/* found in Sengoku Basara 3 Wii */ -VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - off_t streamSize, mrkrOffset, channelSpacing; - int channel_count, i, j; - int found_mrkr = 0; - VARDECL(struct dsp_header, ch_header); - VARDECL(off_t, channel_start); - - channel_count = (unsigned char)read_8bit(0x1B, streamFile); - - ALLOC(ch_header, channel_count, struct dsp_header); - ALLOC(channel_start, channel_count, off_t); - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("dspw",filename_extension(filename))) goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x44535057) // DSPW - goto fail; - - streamSize = read_32bitBE(0x8, streamFile); - - - if (read_32bitBE(streamSize - 0x10, streamFile) == 0x74494D45) // tIME - streamSize -= 0x10; - - mrkrOffset = streamSize - 4; - while ((mrkrOffset > streamSize - 0x1000) && !(found_mrkr)) { // some files have a mrkr section with multiple loop regions at the end - if (read_32bitBE(mrkrOffset, streamFile) != 0x6D726B72) // mrkr - mrkrOffset -= 4; - else { - streamSize = mrkrOffset; - found_mrkr++; - } - } - streamSize -= 0x20; // minus the main header - channelSpacing = streamSize / channel_count; - - /* read .dspw header */ - for (i = 0; i < channel_count; i++) { - channel_start[i] = 0x20 + i*channelSpacing; - - /* get DSP headers */ - if (read_dsp_header(&ch_header[i], channel_start[i], streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch_header[i].initial_ps != - (uint8_t)read_8bit(channel_start[i] + 0x60, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch_header[i].format || ch_header[i].gain) - goto fail; - - /* check for agreement */ - if (i > 0) { - if ( - ch_header[i].sample_count != ch_header[i-1].sample_count || - ch_header[i].nibble_count != ch_header[i-1].nibble_count || - ch_header[i].sample_rate != ch_header[i-1].sample_rate || - ch_header[i].loop_flag != ch_header[i-1].loop_flag || - ch_header[i].loop_start_offset != ch_header[i-1].loop_start_offset || - ch_header[i].loop_end_offset != ch_header[i-1].loop_end_offset - ) goto fail; - } - - if (ch_header[0].loop_flag) { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch_header[0].loop_start_offset/16*8; - if (ch_header[i].loop_ps != - (uint8_t)read_8bit(channel_start[i]+0x60+loop_off,streamFile)) - goto fail; - } - } - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(channel_count,ch_header[0].loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch_header[0].sample_count; - vgmstream->sample_rate = ch_header[0].sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch_header[0].loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch_header[0].loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_none; - // vgmstream->layout_type = layout_interleave; - // vgmstream->interleave_block_size = channelSpacing; - vgmstream->meta_type = meta_DSP_DSPW; - - /* coeffs */ - for (i=0;ich[i].adpcm_coef[j] = ch_header[i].coef[j]; - } - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[i].adpcm_history1_16 = ch_header[i].initial_hist1; - vgmstream->ch[i].adpcm_history2_16 = ch_header[i].initial_hist2; - } - - - - /* open the file for reading */ - - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset=channel_start[i]+0x60; - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -/* dual dsp header with additional "iadp" header, found in Dr. Muto (NGC) */ -VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header ch0_header,ch1_header; - off_t ch1_header_start, ch2_header_start, ch1_start, ch2_start; - int channel_count; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("iadp",filename_extension(filename))) - goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x69616470) /* iadp */ - goto fail; - - channel_count = read_32bitBE(0x4,streamFile); - - if (channel_count != 0x2) + /* checks */ + if (!check_extensions(streamFile, "dsp")) + goto fail; + /* has extra stuff in the reserved data, without it this meta may catch other DSPs it shouldn't */ + if (read_32bitBE(0x50,streamFile) == 0 || read_32bitBE(0x54,streamFile) == 0) goto fail; - - ch1_header_start = 0x20; - ch2_header_start = 0x80; - ch1_start = read_32bitBE(0x1C,streamFile); - ch2_start = ch1_start + read_32bitBE(0x8,streamFile); + /* sfx are mono, but standard dsp will catch them tho */ + dspm.channel_count = read_32bitBE(0x00,streamFile) == read_32bitBE(0x60,streamFile) ? 2 : 1; + dspm.max_channels = 2; + dspm.force_loop = (dspm.channel_count > 1); - /* get DSP headers */ - if (read_dsp_header(&ch0_header, ch1_header_start, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, ch2_header_start, streamFile)) goto fail; - - /* check initial predictor/scale */ - if (ch0_header.initial_ps != (uint8_t)read_8bit(ch1_start, streamFile)) - goto fail; - if (ch1_header.initial_ps != (uint8_t)read_8bit(ch2_start, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain) - goto fail; - if (ch1_header.format || ch1_header.gain) - goto fail; - - /* check for agreement */ - if ( - ch0_header.sample_count != ch1_header.sample_count || - ch0_header.nibble_count != ch1_header.nibble_count || - ch0_header.sample_rate != ch1_header.sample_rate || - ch0_header.loop_flag != ch1_header.loop_flag || - ch0_header.loop_start_offset != ch1_header.loop_start_offset || - ch0_header.loop_end_offset != ch1_header.loop_end_offset - ) goto fail; - - if (ch0_header.loop_flag) - { - off_t loop_off; - /* check loop predictor/scale */ - loop_off = ch0_header.loop_start_offset/16*8; - - if (ch0_header.loop_ps != (uint8_t)read_8bit(ch1_start+loop_off,streamFile)) - goto fail; - if (ch1_header.loop_ps != (uint8_t)read_8bit(ch2_start+loop_off,streamFile)) - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,ch0_header.loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = ch0_header.sample_count; - vgmstream->sample_rate = ch0_header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - ch0_header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - ch0_header.loop_end_offset)+1; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->interleave_block_size = read_32bitBE(0x8,streamFile); - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_NGC_DSP_IADP; - - /* coeffs */ - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; - vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; - } - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; - vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; - vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) - goto fail; - vgmstream->ch[0].channel_start_offset = vgmstream->ch[0].offset=ch1_start; - - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) - goto fail; - vgmstream->ch[1].channel_start_offset = vgmstream->ch[1].offset=ch2_start; - - return vgmstream; + dspm.header_offset = 0x00; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; + dspm.interleave = 0x10; + dspm.meta_type = meta_DSP_CABELAS; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } -/* the csmp format from Metroid Prime 3 and DKCR */ +/* AAAp - Acclaim Austin Audio header + interleaved dsp [Vexx (GC), Turok: Evolution (GC)] */ +VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; -#define CSMP_MAGIC 0x43534D50 -#define CSMP_DATA 0x44415441 + /* checks */ + if (!check_extensions(streamFile, "dsp")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41414170) /* "AAAp" */ + goto fail; -struct csmp_chunk { - uint32_t id; - uint32_t length; -}; + dspm.channel_count = read_16bitBE(0x06,streamFile); + dspm.max_channels = 2; + dspm.header_offset = 0x08; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; + dspm.interleave = (uint16_t)read_16bitBE(0x04,streamFile); + + dspm.meta_type = meta_NGC_DSP_AAAP; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +/* DSPW - Capcom header + full interleaved DSP [Sengoku Basara 3 (Wii), Monster Hunter 3 Ultimate (WiiU)] */ +VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + size_t data_size; + + /* check extension */ + if (!check_extensions(streamFile, "dspw")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x44535057) /* "DSPW" */ + goto fail; + + /* ignore time marker */ + data_size = read_32bitBE(0x08, streamFile); + if (read_32bitBE(data_size - 0x10, streamFile) == 0x74494D45) /* "tIME" */ + data_size -= 0x10; /* (ignore, 2 ints in YYYYMMDD hhmmss00) */ + + /* some files have a mrkr section with multiple loop regions added at the end (variable size) */ + { + off_t mrkr_offset = data_size - 0x04; + off_t max_offset = data_size - 0x1000; + while (mrkr_offset > max_offset) { + if (read_32bitBE(mrkr_offset, streamFile) != 0x6D726B72) { /* "mrkr" */ + mrkr_offset -= 0x04; + } else { + data_size = mrkr_offset; + break; + } + } + } + data_size -= 0x20; /* header size */ + /* 0x10: loop start, 0x14: loop end, 0x1c: num_samples */ + + dspm.channel_count = read_32bitBE(0x18, streamFile); + dspm.max_channels = 6; /* 6ch in Monster Hunter 3 Ultimate */ + + dspm.header_offset = 0x20; + dspm.header_spacing = data_size / dspm.channel_count; + dspm.start_offset = dspm.header_offset + 0x60; + dspm.interleave = data_size / dspm.channel_count; + + dspm.meta_type = meta_DSP_DSPW; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +/* iadp - custom header + interleaved dsp [Dr. Muto (GC)] */ +VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + /* .adp: actual extension, .iadp: header id */ + if (!check_extensions(streamFile, "adp,iadp")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x69616470) /* "iadp" */ + goto fail; + + dspm.channel_count = read_32bitBE(0x04,streamFile); + dspm.max_channels = 2; + + dspm.header_offset = 0x20; + dspm.header_spacing = 0x60; + dspm.start_offset = read_32bitBE(0x1C,streamFile); + dspm.interleave = read_32bitBE(0x08,streamFile); + + dspm.meta_type = meta_NGC_DSP_IADP; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +//todo might be only part of a full header? +/* CSMP - Retro Studios header + interleaved DSPs [Metroid Prime 3 (Wii), Donkey Kong Country Returns (Wii)] */ VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + off_t start_offset; char filename[PATH_LIMIT]; long current_offset; int tries; - struct dsp_header header; - const off_t start_offset = 0x60; - int i; - int csmp_magic; - int csmp_version; + int chanel_count, i; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("csmp",filename_extension(filename))) goto fail; - current_offset = 0; + if (read_32bitBE(0x00, streamFile) != 0x43534D50) /* "CSMP" */ + goto fail; + if (read_32bitBE(0x04, streamFile) != 1) /* version? */ + goto fail; - csmp_magic = read_32bitBE(current_offset, streamFile); - if (csmp_magic != CSMP_MAGIC) goto fail; + chanel_count = 1; + start_offset = 0x60; - current_offset += 4; - - csmp_version = read_32bitBE(current_offset, streamFile); - if (csmp_version != 1) goto fail; - - current_offset += 4; - - tries =0; - while (1) - { - struct csmp_chunk chunk; + current_offset = 0x08; + tries = 0; + while (1) { + uint32_t chunk_id, chunk_size; if (tries > 4) goto fail; - - chunk.id = read_32bitBE(current_offset, streamFile); - chunk.length = read_32bitBE(current_offset + 4, streamFile); - current_offset += 8; - if (chunk.id != CSMP_DATA) - { - current_offset += chunk.length; + + chunk_id = read_32bitBE(current_offset + 0x00, streamFile); + chunk_size = read_32bitBE(current_offset + 0x04, streamFile); + current_offset += 0x08; + if (chunk_id != 0x44415441) { /* "DATA" */ + current_offset += chunk_size; tries++; continue; } @@ -2632,10 +1257,12 @@ VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile) { break; } - if (read_dsp_header(&header, current_offset, streamFile)) goto fail; + if (read_dsp_header(&header, current_offset, streamFile)) goto fail; + + /* check initial predictor/scale */ - /* Retro doesn't seem to abide by this */ + /* Retro doesn't seem to abide by this */ //if (header.initial_ps != (uint8_t)read_8bit(current_offset + start_offset,streamFile)) // goto fail; @@ -2643,35 +1270,22 @@ VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile) { if (header.format || header.gain) goto fail; + /* Retro doesn't seem to abide by this */ + /* check loop predictor/scale */ if (header.loop_flag) { -// off_t loop_off; - /* check loop predictor/scale */ -// loop_off = header.loop_start_offset/16*8; - /* Retro doesn't seem to abide by this */ +// off_t loop_off = header.loop_start_offset/16*8; // if (header.loop_ps != (uint8_t)read_8bit(current_offset + start_offset+loop_off,streamFile)) // goto fail; } - /* compare num_samples with nibble count */ - /* - fprintf(stderr,"num samples (literal): %d\n",read_32bitBE(0,streamFile)); - fprintf(stderr,"num samples (nibbles): %d\n",dsp_nibbles_to_samples(read_32bitBE(4,streamFile))); - */ - /* build the VGMSTREAM */ - - - vgmstream = allocate_vgmstream(1,header.loop_flag); + vgmstream = allocate_vgmstream(chanel_count,header.loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = header.sample_count; vgmstream->sample_rate = header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - header.loop_end_offset)+1; + vgmstream->num_samples = header.sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset)+1; /* don't know why, but it does happen*/ if (vgmstream->loop_end_sample > vgmstream->num_samples) @@ -2684,25 +1298,73 @@ VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile) { /* coeffs */ for (i=0;i<16;i++) vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; /* open the file for reading */ vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) goto fail; - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset=current_offset + start_offset; + vgmstream->ch[0].offset=current_offset + start_offset; return vgmstream; fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } +/* .mcadpcm - Custom header + full interleaved dsp [Skyrim (Switch)] */ +VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(streamFile, "mcadpcm")) + goto fail; + /* could validate dsp sizes but only for +1ch, check_dsp_samples will do it anyway */ + //if (read_32bitLE(0x08,streamFile) != read_32bitLE(0x10,streamFile)) + // goto fail; + + dspm.channel_count = read_32bitLE(0x00,streamFile); + dspm.max_channels = 2; + dspm.little_endian = 1; + + dspm.header_offset = read_32bitLE(0x04,streamFile); + dspm.header_spacing = dspm.channel_count == 1 ? 0 : + read_32bitLE(0x0c,streamFile) - dspm.header_offset; /* channel 2 start, only with Nch */ + dspm.start_offset = dspm.header_offset + 0x60; + dspm.interleave = dspm.header_spacing; + + dspm.meta_type = meta_DSP_MCADPCM; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +/* .switch_audio - UE4 standard LE header + full interleaved dsp [Gal Gun 2 (Switch)] */ +VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + /* .switch_audio: possibly UE4 class name rather than extension, .dsp: assumed */ + if (!check_extensions(streamFile, "switch_audio,dsp")) + goto fail; + + /* manual double header test */ + if (read_32bitLE(0x00, streamFile) == read_32bitLE(get_streamfile_size(streamFile) / 2, streamFile)) + dspm.channel_count = 2; + else + dspm.channel_count = 1; + dspm.max_channels = 2; + dspm.little_endian = 1; + + dspm.header_offset = 0x00; + dspm.header_spacing = get_streamfile_size(streamFile) / dspm.channel_count; + dspm.start_offset = dspm.header_offset + 0x60; + dspm.interleave = dspm.header_spacing; + + dspm.meta_type = meta_DSP_SWITCH_AUDIO; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_pdt.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_pdt.c index 8417c74d3..71c0556fb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_pdt.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_pdt.c @@ -1,101 +1,213 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* PDT - Custom Generated File (Mario Party) */ +/* PDT - Hudson's stream container [Adventure Island (GC), Muscle Champion (GC), Mario Party series (GC)] */ VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag; - int channel_count; + int loop_flag, channel_count, sample_rate; + size_t entries, nibble_size, loop_start; + off_t entries_offset, coefs_offset, header_offset; + off_t channel1_offset = 0, channel2_offset = 0, coef_offset = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "pdt")) + goto fail; + + if (read_16bitBE(0x00,streamFile) != 0x01) /* version? */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x04) /* entry size? */ + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x7d00) /* not-sample rate? */ + goto fail; + if (read_32bitBE(0x0c,streamFile) != 0x02 && /* not-channels? */ + read_32bitBE(0x0c,streamFile) != 0x04) + goto fail; + + entries = read_16bitBE(0x02,streamFile); + entries_offset = read_32bitBE(0x10,streamFile); + coefs_offset = read_32bitBE(0x14,streamFile); + //headers_offset = read_32bitBE(0x18,streamFile); /* we'll have pointers to those two */ + //streams_offset = read_32bitBE(0x1c,streamFile); + + /* find subsongs and target header, as entries can be empty/repeated */ + { + /* tables to cache reads as it can be kinda slow with so many loops */ + uint32_t data_offsets[0x2000]; + uint32_t entry_offset, data_offset; + int i,j; + + if (entries > 0x2000) + goto fail; + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + header_offset = 0; + for (i = 0; i < entries; i++) { + int is_unique = 1; + + entry_offset = read_32bitBE(entries_offset + i*0x04,streamFile); + if (entry_offset == 0x00) + continue; + data_offset = read_32bitBE(entry_offset+0x10,streamFile); + + /* check if current entry header was repeated (same file offset, difference in flags only) */ + for (j = 0; j < total_subsongs; j++) { + if (data_offsets[j] == data_offset) { + is_unique = 0; + break; + } + } + if (!is_unique) + continue; + + data_offsets[total_subsongs] = data_offset; + total_subsongs++; + + /* target GET, but keep going to count subsongs */ + if (!header_offset && target_subsong == total_subsongs) { + header_offset = entry_offset; + } + } + } + + /* parse header */ + { + uint8_t flags; + size_t coef1_entry; + off_t coef1_offset; + + flags = read_8bit(header_offset+0x00,streamFile); + sample_rate = read_32bitBE(header_offset+0x04,streamFile); + /* 0x01: unknown + 0x4000 */ + sample_rate = read_32bitBE(header_offset+0x04,streamFile); + nibble_size = read_32bitBE(header_offset+0x08,streamFile); + loop_start = read_32bitBE(header_offset+0x0c,streamFile); + + channel1_offset = read_32bitBE(header_offset+0x10,streamFile); + coef1_entry = read_16bitBE(header_offset+0x14,streamFile); + coef1_offset = coefs_offset + coef1_entry*0x20; + + if (flags & 0x01) { + //size_t coef2_entry; + //off_t coef2_offset; + + channel2_offset = read_32bitBE(header_offset+0x18,streamFile); + /* always after coef1 in practice */ + //coef2_entry = read_16bitBE(header_offset+0x1c,streamFile); + //coef2_offset = coefs_offset + coef2_entry*0x20; + //if (coef1_offset + 0x20 != coef2_offset) + // goto fail; + } + + coef_offset = coef1_offset; + loop_flag = (flags & 0x02); + channel_count = (flags & 0x01) ? 2 : 1; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + //vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);//todo remove + vgmstream->num_samples = dsp_nibbles_to_samples(nibble_size); + //vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channel_count);//todo remove + vgmstream->loop_start_sample = dsp_nibbles_to_samples(loop_start); + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_NGC_PDT; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + dsp_read_coefs_be(vgmstream, streamFile, coef_offset, 0x20); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = nibble_size / 2 * channel_count; + + if (!vgmstream_open_stream(vgmstream,streamFile,channel1_offset)) + goto fail; + + /* channels may start at slightly separated offsets */ + if (channel_count == 2) { + vgmstream->ch[1].channel_start_offset = + vgmstream->ch[1].offset = channel2_offset; + } + + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* PDT - custom fake header for split (PDTExt) .ptd [Mario Party (GC)] */ +VGMSTREAM * init_vgmstream_ngc_pdt_split(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; off_t start_offset; - int second_channel_start = -1; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("pdt",filename_extension(filename))) goto fail; + int loop_flag, channel_count; - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x50445420) /* "PDT " */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x44535020) /* "DSP " */ - goto fail; - if (read_32bitBE(0x08,streamFile) != 0x48454144) /* "HEAD " */ - goto fail; - if (read_16bitBE(0x0C,streamFile) != 0x4552) /* "ER " */ + + /* checks */ + if (!check_extensions(streamFile, "pdt")) goto fail; - loop_flag = (read_32bitBE(0x1C,streamFile)!=2); + /* 0x10 fake header + chunks of the original header / data pasted together */ + if (read_32bitBE(0x00,streamFile) != 0x50445420 && /* "PDT " */ + read_32bitBE(0x04,streamFile) != 0x44535020 && /* "DSP " */ + read_32bitBE(0x08,streamFile) != 0x48454144 && /* "HEAD " */ + read_16bitBE(0x0C,streamFile) != 0x4552) /* "ER " */ + goto fail; + + start_offset = 0x800; channel_count = (uint16_t)(read_16bitLE(0x0E,streamFile)); + loop_flag = (read_32bitBE(0x1C,streamFile) != 2); - /* build the VGMSTREAM */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x14,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - if (channel_count == 1) - { - vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count/2; + if (channel_count == 1) { + vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count/2; if (loop_flag) { vgmstream->loop_start_sample = read_32bitBE(0x1C,streamFile)*14/8/channel_count/2; vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile)*14/8/channel_count/2; } } - else if (channel_count == 2) - { - vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count; + else if (channel_count == 2) { + vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count; if (loop_flag) { vgmstream->loop_start_sample = read_32bitBE(0x1C,streamFile)*14/8/channel_count; vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile)*14/8/channel_count; } - second_channel_start = (get_streamfile_size(streamFile)+start_offset)/2; } - else - { - goto fail; + else { + goto fail; } - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGC_PDT; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + dsp_read_coefs_be(vgmstream, streamFile, 0x50, 0x20); - if (vgmstream->coding_type == coding_NGC_DSP) { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x50+i*2,streamFile); - } - if (vgmstream->channels == 2) { - for (i=0;i<16;i++) { - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x70+i*2,streamFile); - } - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[0].channel_start_offset=start_offset; - if (channel_count == 2) { - if (second_channel_start == -1) goto fail; - vgmstream->ch[1].channel_start_offset=second_channel_start; - } - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; - } + if (channel_count == 2) { + vgmstream->ch[1].channel_start_offset = + vgmstream->ch[1].offset = ((get_streamfile_size(streamFile)+start_offset) / channel_count); } return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_str_cauldron.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_str_cauldron.c new file mode 100644 index 000000000..7ef70aeba --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_str_cauldron.c @@ -0,0 +1,46 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .str - Cauldron/Conan mini-header + interleaved dsp data [Conan (GC)] */ +VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int channel_count, loop_flag; + + + /* checks */ + if (!check_extensions(streamFile, "str")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0xFAAF0001) /* header id */ + goto fail; + + channel_count = 2; /* always loop & stereo */ + loop_flag = 1; + start_offset = 0x60; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitBE(0x04,streamFile); + vgmstream->num_samples = read_32bitBE(0x08,streamFile); + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_DSP_STR; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0x0C,streamFile); + + dsp_read_coefs_be(vgmstream, streamFile, 0x10, 0x20); + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_ulw.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_ulw.c index 662bdcf57..e873ac1d6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_ulw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_ulw.c @@ -14,29 +14,20 @@ VGMSTREAM * init_vgmstream_ngc_ulw(STREAMFILE *streamFile) { /* raw data, the info is in the filename (really!) */ { - char* path; - char basename[PATH_LIMIT]; - char filename[PATH_LIMIT]; + char filename[PATH_LIMIT] = {0}; - /* get base name */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - path = strrchr(filename,DIR_SEPARATOR); - if (path!=NULL) - path = path+1; - else - path = filename; - strcpy(basename,path); + get_streamfile_filename(streamFile, filename,PATH_LIMIT); /* first letter gives the channels */ - if (basename[0]=='M') /* Mono */ + if (filename[0]=='M') /* Mono */ channel_count = 1; - else if (basename[0]=='S' || basename[0]=='D') /* Stereo/Dolby */ + else if (filename[0]=='S' || filename[0]=='D') /* Stereo/Dolby */ channel_count = 2; else goto fail; /* not very robust but meh (other tracks don't loop) */ - if (strcmp(basename,"MMenu.ulw")==0 || strcmp(basename,"DMenu.ulw")==0) { + if (strcmp(filename,"MMenu.ulw")==0 || strcmp(filename,"DMenu.ulw")==0) { loop_flag = 1; } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c b/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c deleted file mode 100644 index 7794f77b1..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "meta.h" -#include "../util.h" -#include "../coding/coding.h" - -/* .OPUS - from Switch games (Lego City Undercover, Ultra SF II, Disgaea 5) */ -VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag = 0, channel_count; - int num_samples = 0, loop_start = 0, loop_end = 0; - off_t offset = 0, data_offset; - size_t data_size, skip = 0; - - /* check extension, case insensitive */ - if ( !check_extensions(streamFile,"opus,lopus,nop")) /* no relation to Ogg Opus */ - goto fail; - - /* variations, maybe custom */ - if (read_32bitBE(0x00,streamFile) == 0x01000080) { /* Lego City Undercover */ - offset = 0x00; - } - else if ((read_32bitBE(0x04,streamFile) == 0x00000000 && read_32bitBE(0x0c,streamFile) == 0x00000000) || - (read_32bitBE(0x04,streamFile) == 0xFFFFFFFF && read_32bitBE(0x0c,streamFile) == 0xFFFFFFFF)) { /* Disgaea 5 */ - offset = 0x10; - - loop_start = read_32bitLE(0x00,streamFile); - loop_end = read_32bitLE(0x08,streamFile); - } - else if (read_32bitLE(0x04,streamFile) == 0x02) { /* Ultra Street Fighter II */ - offset = read_32bitLE(0x1c,streamFile); - - num_samples = read_32bitLE(0x00,streamFile); - loop_start = read_32bitLE(0x08,streamFile); - loop_end = read_32bitLE(0x0c,streamFile); - } - else if (read_32bitBE(0x00, streamFile) == 0x73616466 && read_32bitBE(0x08, streamFile) == 0x6f707573) { /* Xenoblade Chronicles 2 */ - offset = read_32bitLE(0x1c, streamFile); - - num_samples = read_32bitLE(0x28, streamFile); - loop_flag = read_8bit(0x19, streamFile); - if (loop_flag) { - loop_start = read_32bitLE(0x2c, streamFile); - loop_end = read_32bitLE(0x30, streamFile); - } - } - else { - offset = 0x00; - } - - if ((uint32_t)read_32bitLE(offset + 0x00,streamFile) != 0x80000001) - goto fail; - - channel_count = read_8bit(offset + 0x09, streamFile); - /* 0x0a: packet size if CBR, 0 if VBR */ - data_offset = offset + read_32bitLE(offset + 0x10, streamFile); - skip = read_32bitLE(offset + 0x1c, streamFile); - - if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) - goto fail; - - data_size = read_32bitLE(data_offset + 0x04, streamFile); - - start_offset = data_offset + 0x08; - loop_flag = (loop_end > 0); /* -1 when not set */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitLE(offset + 0x0c,streamFile); - vgmstream->meta_type = meta_NSW_OPUS; - - vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; - -#ifdef VGM_USE_FFMPEG - { - uint8_t buf[0x100]; - size_t bytes; - ffmpeg_custom_config cfg; - ffmpeg_codec_data *ffmpeg_data; - - bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate); - if (bytes <= 0) goto fail; - - memset(&cfg, 0, sizeof(ffmpeg_custom_config)); - cfg.type = FFMPEG_SWITCH_OPUS; - - ffmpeg_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg); - if (!ffmpeg_data) goto fail; - - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - if (ffmpeg_data->skipSamples <= 0) { - ffmpeg_set_skip_samples(ffmpeg_data, skip); - } - - if (vgmstream->num_samples == 0) { - vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, - vgmstream->sample_rate, streamFile) - skip; - } - } -#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; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nwa.c b/Frameworks/vgmstream/vgmstream/src/meta/nwa.c index d4743a5c1..26003fbbd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nwa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nwa.c @@ -4,306 +4,88 @@ #include #include -#ifdef WIN32 -#define DIRSEP '\\' -#else -#define DIRSEP '/' -#endif -/* NWA - Visual Art's streams */ +static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start); +static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end); +static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile); +static void free_nwa_vgmstream(nwa_codec_data *data); +/* NWA - Visual Art's streams [Air (PC), Clannad (PC)] */ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int i; - int channel_count; - int loop_flag = 0; - int32_t loop_start_sample = 0; - int32_t loop_end_sample = 0; - int nwainfo_ini_found = 0; - int gameexe_ini_found = 0; - int just_pcm = 0; - int comp_level = -2; - nwa_codec_data *data = NULL; + off_t start_offset; + int channel_count, loop_flag = 0; + int32_t loop_start_sample = 0, loop_end_sample = 0; + int nwainfo_ini_found = 0, gameexe_ini_found = 0; + int compression_level; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("nwa",filename_extension(filename))) goto fail; + + /* checks */ + if (!check_extensions(streamFile, "nwa")) + goto fail; channel_count = read_16bitLE(0x00,streamFile); if (channel_count != 1 && channel_count != 2) goto fail; /* check if we're using raw pcm */ - if ( - read_32bitLE(0x08,streamFile)==-1 || /* compression level */ - read_32bitLE(0x10,streamFile)==0 || /* block count */ - read_32bitLE(0x18,streamFile)==0 || /* compressed data size */ - read_32bitLE(0x20,streamFile)==0 || /* block size */ - read_32bitLE(0x24,streamFile)==0 /* restsize */ - ) - { - just_pcm = 1; - } - else - { - comp_level = read_32bitLE(0x08,streamFile); - - data = malloc(sizeof(nwa_codec_data)); - if (!data) goto fail; - - data->nwa = open_nwa(streamFile,filename); - if (!data->nwa) goto fail; + if ( read_32bitLE(0x08,streamFile)==-1 || /* compression level */ + read_32bitLE(0x10,streamFile)==0 || /* block count */ + read_32bitLE(0x18,streamFile)==0 || /* compressed data size */ + read_32bitLE(0x20,streamFile)==0 || /* block size */ + read_32bitLE(0x24,streamFile)==0 ) { /* restsize */ + compression_level = -1; + } else { + compression_level = read_32bitLE(0x08,streamFile); } - /* try to locate NWAINFO.INI in the same directory */ - { - char ininame[PATH_LIMIT]; - char * ini_lastslash; - char namebase_array[PATH_LIMIT]; - char *namebase; - STREAMFILE *inistreamfile; + /* loop points come from external files */ + nwainfo_ini_found = get_loops_nwainfo_ini(streamFile, &loop_flag, &loop_start_sample); + gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(streamFile, &loop_flag, &loop_start_sample, &loop_end_sample); - /* here we assume that the "special encoding" does not affect - * the directory separator */ - strncpy(ininame,filename,sizeof(ininame)); - ininame[sizeof(ininame)-1]='\0'; /* a pox on the stdlib! */ - - streamFile->get_realname(streamFile,namebase_array,sizeof(namebase_array)); - - ini_lastslash = strrchr(ininame,DIRSEP); - if (!ini_lastslash) { - strncpy(ininame,"NWAINFO.INI",sizeof(ininame)); - namebase = namebase_array; - } else { - strncpy(ini_lastslash+1,"NWAINFO.INI", - sizeof(ininame)-(ini_lastslash+1-ininame)); - namebase = strrchr(namebase_array,DIRSEP)+1; - } - ininame[sizeof(ininame)-1]='\0'; /* curse you, strncpy! */ - - inistreamfile = streamFile->open(streamFile,ininame,4096); - - if (inistreamfile) { - /* ini found, try to find our name */ - const char * ext; - int length; - int found; - off_t offset; - off_t file_size; - off_t found_off = -1; - - nwainfo_ini_found = 1; - - ext = filename_extension(namebase); - length = ext-1-namebase; - file_size = get_streamfile_size(inistreamfile); - - for (found = 0, offset = 0; !found && offset 0) loop_flag = 1; - } - } /* if found file name in INI */ - - close_streamfile(inistreamfile); - } /* if opened INI ok */ - } /* INI block */ - - /* try to locate Gameexe.ini in the same directory */ - { - char ininame[PATH_LIMIT]; - char * ini_lastslash; - char namebase_array[PATH_LIMIT]; - char * namebase; - STREAMFILE *inistreamfile; - - strncpy(ininame,filename,sizeof(ininame)); - ininame[sizeof(ininame)-1]='\0'; /* a pox on the stdlib! */ - - streamFile->get_realname(streamFile,namebase_array,sizeof(namebase_array)); - - ini_lastslash = strrchr(ininame,DIRSEP); - if (!ini_lastslash) { - strncpy(ininame,"Gameexe.ini",sizeof(ininame)); - namebase = namebase_array; - } else { - strncpy(ini_lastslash+1,"Gameexe.ini", - sizeof(ininame)-(ini_lastslash+1-ininame)); - namebase = strrchr(namebase_array,DIRSEP)+1; - } - ininame[sizeof(ininame)-1]='\0'; /* curse you, strncpy! */ - - inistreamfile = streamFile->open(streamFile,ininame,4096); - - if (inistreamfile) { - /* ini found, try to find our name */ - const char * ext; - int length; - int found; - off_t offset; - off_t file_size; - off_t found_off = -1; - - gameexe_ini_found = 1; - - ext = filename_extension(namebase); - length = ext-1-namebase; - file_size = get_streamfile_size(inistreamfile); - - /* format of line is: - * #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?" - * ^22 ^33 ^45 ^57 - */ - - for (found = 0, offset = 0; !found && offsetchannels = channel_count; vgmstream->sample_rate = read_32bitLE(0x04,streamFile); + vgmstream->num_samples = read_32bitLE(0x1c,streamFile) / channel_count; - vgmstream->num_samples = read_32bitLE(0x1c,streamFile)/channel_count; - - if (just_pcm) { - switch (read_16bitLE(0x02,streamFile)) { - case 8: - vgmstream->coding_type = coding_PCM8; - vgmstream->interleave_block_size = 1; - break; - case 16: - vgmstream->coding_type = coding_PCM16LE; - vgmstream->interleave_block_size = 2; - break; - default: - goto fail; - } - if (channel_count > 1) { + switch(compression_level) { + case -1: + switch (read_16bitLE(0x02,streamFile)) { + case 8: + vgmstream->coding_type = coding_PCM8; + vgmstream->interleave_block_size = 0x01; + break; + case 16: + vgmstream->coding_type = coding_PCM16LE; + vgmstream->interleave_block_size = 0x02; + break; + default: + goto fail; + } vgmstream->layout_type = layout_interleave; - } else { + break; + + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + vgmstream->coding_type = coding_NWA; vgmstream->layout_type = layout_none; - } - } - else - { - switch (comp_level) - { - case 0: - vgmstream->coding_type = coding_NWA0; - break; - case 1: - vgmstream->coding_type = coding_NWA1; - break; - case 2: - vgmstream->coding_type = coding_NWA2; - break; - case 3: - vgmstream->coding_type = coding_NWA3; - break; - case 4: - vgmstream->coding_type = coding_NWA4; - break; - case 5: - vgmstream->coding_type = coding_NWA5; - break; - default: - goto fail; - break; - } - vgmstream->layout_type = layout_none; + vgmstream->codec_data = open_nwa_vgmstream(streamFile); + if (!vgmstream->codec_data) goto fail; + break; + + default: + goto fail; + break; } + if (nwainfo_ini_found) { vgmstream->meta_type = meta_NWA_NWAINFOINI; if (loop_flag) { @@ -321,38 +103,205 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { } - if (just_pcm) { - /* open the file for reading by each channel */ - STREAMFILE *chstreamfile; - - /* have both channels use the same buffer, as interleave is so small */ - chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!chstreamfile) goto fail; - - for (i=0;ich[i].streamfile = chstreamfile; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=0x2c+(off_t)(i*vgmstream->interleave_block_size); - } - } - else - { - vgmstream->codec_data = data; - } - + 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); - if (data) { - if (data->nwa) + close_vgmstream(vgmstream); + return NULL; +} + + +/* try to locate NWAINFO.INI in the same directory */ +static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start) { + STREAMFILE *streamLoops; + char namebase[PATH_LIMIT]; + const char * ext; + int length; + int found; + off_t offset; + size_t file_size; + off_t found_off = -1; + int loop_flag = 0; + int32_t loop_start_sample = 0; + + + streamLoops = open_streamfile_by_filename(streamFile, "NWAINFO.INI"); + if (!streamLoops) goto fail; + + get_streamfile_filename(streamFile,namebase,PATH_LIMIT); + + /* ini found, try to find our name */ + ext = filename_extension(namebase); + length = ext - 1 - namebase; + file_size = get_streamfile_size(streamLoops); + + for (found = 0, offset = 0; !found && offset < file_size; offset++) { + off_t suboffset; + /* Go for an n*m search 'cause it's easier than building an + * FSA for the search string. Just wanted to make the point that + * I'm not ignorant, just lazy. */ + for (suboffset = offset; + suboffset < file_size && + suboffset-offset < length && + read_8bit(suboffset,streamLoops) == namebase[suboffset-offset]; + suboffset++) { + /* skip */ + } + + if (suboffset-offset==length && read_8bit(suboffset,streamLoops)==0x09) { /* tab */ + found = 1; + found_off = suboffset+1; + } + } + + /* if found file name in INI */ + if (found) { + char loopstring[9] = {0}; + + if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops) == 8) { + loop_start_sample = atol(loopstring); + if (loop_start_sample > 0) + loop_flag = 1; + } + } + + + *out_loop_flag = loop_flag; + *out_loop_start = loop_start_sample; + + close_streamfile(streamLoops); + return 1; + +fail: + close_streamfile(streamLoops); + return 0; +} + +/* try to locate Gameexe.ini in the same directory */ +static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end) { + STREAMFILE *streamLoops; + char namebase[PATH_LIMIT]; + const char * ext; + int length; + int found; + off_t offset; + off_t file_size; + off_t found_off = -1; + int loop_flag = 0; + int32_t loop_start_sample = 0, loop_end_sample = 0; + + + streamLoops = open_streamfile_by_filename(streamFile, "Gameexe.ini"); + if (!streamLoops) goto fail; + + get_streamfile_filename(streamFile,namebase,PATH_LIMIT); + + /* ini found, try to find our name */ + ext = filename_extension(namebase); + length = ext-1-namebase; + file_size = get_streamfile_size(streamLoops); + + /* format of line is: + * #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?" + * ^22 ^33 ^45 ^57 + */ + + for (found = 0, offset = 0; !found && offsetget_name(streamFile,filename,sizeof(filename)); + + data = malloc(sizeof(nwa_codec_data)); + if (!data) goto fail; + + data->nwa = open_nwa(streamFile,filename); + if (!data->nwa) goto fail; + + return data; + +fail: + free_nwa_vgmstream(data); + return NULL; +} + +static void free_nwa_vgmstream(nwa_codec_data *data) { + if (data) { + if (data->nwa) { close_nwa(data->nwa); } free(data); } - return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nxap.c b/Frameworks/vgmstream/vgmstream/src/meta/nxap.c new file mode 100644 index 000000000..813ec164e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/nxap.c @@ -0,0 +1,49 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* NXAP - Nex Entertainment header [Time Crisis 4 (PS3), Time Crisis Razing Storm (PS3)] */ +VGMSTREAM * init_vgmstream_nxap(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "adp")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4E584150) /* "NXAP" */ + goto fail; + if (read_32bitLE(0x14,streamFile) != 0x40 || /* expected frame size? */ + read_32bitLE(0x18,streamFile) != 0x40) /* expected interleave? */ + goto fail; + + start_offset = read_32bitLE(0x04,streamFile); + channel_count = read_32bitLE(0x0c,streamFile); + loop_flag = 0; //(read_32bitLE(0x24,streamFile) > 0); //todo + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x10, streamFile); + vgmstream->num_samples = read_32bitLE(0x1c,streamFile) * (0x40-0x04)*2 / channel_count; /* number of frames */ + + /* unknown loop format, also 0x28/2c values seem related */ + //vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile) * (0x40-0x04)*2 / channel_count; + //vgmstream->loop_end_sample = read_32bitLE(0x24,streamFile) * (0x40-0x04)*2 / channel_count; + //vgmstream->loop_end_sample = vgmstream->loop_start_sample + vgmstream->loop_end_sample; + + vgmstream->meta_type = meta_NXAP; + vgmstream->coding_type = coding_YAMAHA_NXAP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x40; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index 79704ec14..67d21149a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -111,8 +111,7 @@ static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb int i; /* add 0x23 ('#') */ - { - for (i = 0; i < bytes_read; i++) + for (i = 0; i < bytes_read; i++) { ((uint8_t*)ptr)[i] += 0x23; } } @@ -124,19 +123,16 @@ static void sngw_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v char *header_id = "OggS"; uint8_t key[4]; - put_32bitBE(key, ov_streamfile->sngw_xor); + put_32bitBE(key, ov_streamfile->xor_value); - /* bytes are xor'd with key and nibble-swapped */ - { - 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 { - uint8_t val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4]; - ((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f); - } + /* bytes are xor'd with key and nibble-swapped, first "OggS" is changed */ + 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 val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4]; + ((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f); } } } @@ -150,9 +146,8 @@ static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo int i; /* bytes are xor'd with key */ - { - for (i = 0; i < bytes_read; i++) - ((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16]; + for (i = 0; i < bytes_read; i++) { + ((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16]; } } @@ -162,25 +157,67 @@ static void l2sd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v int i; char *header_id = "OggS"; - /* 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 { - break; - } + /* 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 { + break; } } } +static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + static const uint8_t header[16] = { /* OggS, packet type, granule, stream id(empty) */ + 0x4F,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + size_t bytes_read = size*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 + * (key is also in (game)/www/data/System.json "encryptionKey") */ + for (i = 0; i < bytes_read; i++) { + if (ov_streamfile->offset+i < 0x10) { + ((uint8_t*)ptr)[i] = header[(ov_streamfile->offset + i) % 16]; + + /* last two bytes are the stream id, get from next OggS */ + if (ov_streamfile->offset+i == 0x0e) + ((uint8_t*)ptr)[i] = read_8bit(0x58, ov_streamfile->streamfile); + if (ov_streamfile->offset+i == 0x0f) + ((uint8_t*)ptr)[i] = read_8bit(0x59, ov_streamfile->streamfile); + } + } +} + +static void eno_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 with key */ + for (i = 0; i < bytes_read; i++) { + ((uint8_t*)ptr)[i] ^= (uint8_t)ov_streamfile->xor_value; + } +} + +static void ys8_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 with key 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); + } +} /* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { - char filename[PATH_LIMIT]; - vgm_vorbis_info_t inf = {0}; + ogg_vorbis_meta_info_t ovmi = {0}; off_t start_offset = 0; int is_ogg = 0; @@ -190,45 +227,57 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { int is_sngw = 0; int is_isd = 0; int is_l2sd = 0; + int is_rpgmvo = 0; + int is_eno = 0; + int is_ys8 = 0; /* check extension */ - if (check_extensions(streamFile,"ogg,logg")) { /* .ogg: standard/psychic, .logg: renamed for plugins */ + /* .ogg: standard/psychic, .logg: renamed for plugins, + * .adx: KID [Remember11 (PC)], + * .rof: The Rhythm of Fighters (Mobile) + * .acm: Planescape Torment Enhanced Edition (PC) */ + if (check_extensions(streamFile,"ogg,logg,adx,rof,acm")) { is_ogg = 1; } else if (check_extensions(streamFile,"um3")) { is_um3 = 1; } else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie (PC), kovs: header id only? */ is_kovs = 1; - } else if (check_extensions(streamFile,"sngw")) { /* .sngw: Devil May Cry 4 SE (PC), Biohazard 6 (PC) */ + } else if (check_extensions(streamFile,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */ is_sngw = 1; } else if (check_extensions(streamFile,"isd")) { /* .isd: Azure Striker Gunvolt (PC) */ is_isd = 1; + } else if (check_extensions(streamFile,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */ + is_rpgmvo = 1; + } else if (check_extensions(streamFile,"eno")) { /* .eno: Metronomicon (PC) */ + is_eno = 1; } else { goto fail; } - streamFile->get_name(streamFile,filename,sizeof(filename)); /* check standard Ogg Vorbis */ if (is_ogg) { - - /* check Psychic Software obfuscation (Darkwind: War on Wheels PC) */ - if (read_32bitBE(0x00,streamFile) == 0x2c444430) { + if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software obfuscation [Darkwind: War on Wheels (PC)] */ is_psychic = 1; - inf.decryption_callback = psychic_ogg_decryption_callback; + ovmi.decryption_callback = psychic_ogg_decryption_callback; } else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" [Lineage II Chronicle 4 (PC)] */ is_l2sd = 1; - inf.decryption_callback = l2sd_ogg_decryption_callback; + ovmi.decryption_callback = l2sd_ogg_decryption_callback; + } + else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* XOR'ed + bitswapped "OggS" [Ys VIII (PC)] */ + is_ys8 = 1; + ovmi.decryption_callback = ys8_ogg_decryption_callback; } else if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ - goto fail; /* not known (ex. Wwise) */ + goto fail; /* unknown/not ogg (ex. Wwise) */ } } /* check "Ultramarine3" (???), may be encrypted */ if (is_um3) { if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ - inf.decryption_callback = um3_ogg_decryption_callback; + ovmi.decryption_callback = um3_ogg_decryption_callback; } } @@ -237,9 +286,9 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */ goto fail; } - inf.loop_start = read_32bitLE(0x08,streamFile); - inf.loop_flag = (inf.loop_start != 0); - inf.decryption_callback = kovs_ogg_decryption_callback; + ovmi.loop_start = read_32bitLE(0x08,streamFile); + ovmi.loop_flag = (ovmi.loop_start != 0); + ovmi.decryption_callback = kovs_ogg_decryption_callback; start_offset = 0x20; } @@ -247,14 +296,14 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { /* check SNGW (Capcom's MT Framework PC games), may be encrypted */ if (is_sngw) { if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ - inf.sngw_xor = read_32bitBE(0x00,streamFile); - inf.decryption_callback = sngw_ogg_decryption_callback; + ovmi.xor_value = read_32bitBE(0x00,streamFile); + ovmi.decryption_callback = sngw_ogg_decryption_callback; } } /* check ISD (Gunvolt PC) */ if (is_isd) { - inf.decryption_callback = isd_ogg_decryption_callback; + ovmi.decryption_callback = isd_ogg_decryption_callback; //todo looping unknown, not in Ogg comments // game has sound/GV_steam.* files with info about sound/stream/*.isd @@ -265,44 +314,73 @@ 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), header + minor encryption */ + if (is_rpgmvo) { + 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; + + start_offset = 0x10; + } + + /* check ENO [Metronomicon (PC)] */ + if (is_eno) { + /* 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; + start_offset = 0x01; + } + + /* check Ys VIII (PC) */ + if (is_ys8) { + ovmi.xor_value = 0xF0; + ovmi.decryption_callback = ys8_ogg_decryption_callback; + } if (is_um3) { - inf.meta_type = meta_OGG_UM3; + ovmi.meta_type = meta_OGG_UM3; } else if (is_kovs) { - inf.meta_type = meta_OGG_KOVS; + ovmi.meta_type = meta_OGG_KOVS; } else if (is_psychic) { - inf.meta_type = meta_OGG_PSYCHIC; + ovmi.meta_type = meta_OGG_PSYCHIC; } else if (is_sngw) { - inf.meta_type = meta_OGG_SNGW; + ovmi.meta_type = meta_OGG_SNGW; } else if (is_isd) { - inf.meta_type = meta_OGG_ISD; + ovmi.meta_type = meta_OGG_ISD; } else if (is_l2sd) { - inf.meta_type = meta_OGG_L2SD; + ovmi.meta_type = meta_OGG_L2SD; + } else if (is_rpgmvo) { + ovmi.meta_type = meta_OGG_RPGMV; + } else if (is_eno) { + ovmi.meta_type = meta_OGG_ENO; + } else if (is_ys8) { + ovmi.meta_type = meta_OGG_YS8; } else { - inf.meta_type = meta_OGG_VORBIS; + ovmi.meta_type = meta_OGG_VORBIS; } - inf.layout_type = layout_ogg_vorbis; - return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); fail: return NULL; } -VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t start, const vgm_vorbis_info_t *vgm_inf) { +VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks_p, off_t start, const ogg_vorbis_meta_info_t *ovmi) { VGMSTREAM * vgmstream = NULL; ogg_vorbis_codec_data * data = NULL; OggVorbis_File *ovf = NULL; vorbis_info *vi; - int loop_flag = vgm_inf->loop_flag; - int32_t loop_start = vgm_inf->loop_start; - int loop_length_found = vgm_inf->loop_length_found; - int32_t loop_length = vgm_inf->loop_length; - int loop_end_found = vgm_inf->loop_end_found; - int32_t loop_end = vgm_inf->loop_end; - size_t stream_size = vgm_inf->stream_size ? - vgm_inf->stream_size : + int loop_flag = ovmi->loop_flag; + int32_t loop_start = ovmi->loop_start; + int loop_length_found = ovmi->loop_length_found; + int32_t loop_length = ovmi->loop_length; + int loop_end_found = ovmi->loop_end_found; + int32_t loop_end = ovmi->loop_end; + size_t stream_size = ovmi->stream_size ? + ovmi->stream_size : get_streamfile_size(streamFile) - start; ov_callbacks default_callbacks; @@ -327,10 +405,10 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch temp_streamfile.offset = 0; temp_streamfile.size = stream_size; - temp_streamfile.decryption_callback = vgm_inf->decryption_callback; - temp_streamfile.scd_xor = vgm_inf->scd_xor; - temp_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - temp_streamfile.sngw_xor = vgm_inf->sngw_xor; + temp_streamfile.decryption_callback = ovmi->decryption_callback; + temp_streamfile.scd_xor = ovmi->scd_xor; + temp_streamfile.scd_xor_length = ovmi->scd_xor_length; + temp_streamfile.xor_value = ovmi->xor_value; /* open the ogg vorbis file for testing */ memset(&temp_ovf, 0, sizeof(temp_ovf)); @@ -344,9 +422,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch /* proceed to init codec_data and reopen a STREAMFILE for this stream */ { + char filename[PATH_LIMIT]; + data = calloc(1,sizeof(ogg_vorbis_codec_data)); if (!data) goto fail; + streamFile->get_name(streamFile,filename,sizeof(filename)); data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); if (!data->ov_streamfile.streamfile) goto fail; @@ -354,10 +435,10 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch data->ov_streamfile.offset = 0; data->ov_streamfile.size = stream_size; - data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback; - data->ov_streamfile.scd_xor = vgm_inf->scd_xor; - data->ov_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - data->ov_streamfile.sngw_xor = vgm_inf->sngw_xor; + data->ov_streamfile.decryption_callback = ovmi->decryption_callback; + data->ov_streamfile.scd_xor = ovmi->scd_xor; + data->ov_streamfile.scd_xor_length = ovmi->scd_xor_length; + data->ov_streamfile.xor_value = ovmi->xor_value; /* open the ogg vorbis file for real */ if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, 0, *callbacks_p)) @@ -382,7 +463,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch strstr(user_comment,"LOOPSTART=")==user_comment || strstr(user_comment,"um3.stream.looppoint.start=")==user_comment || strstr(user_comment,"LOOP_BEGIN=")==user_comment || /* Hatsune Miku: Project Diva F (PS3) */ - strstr(user_comment,"LoopStart=")==user_comment) { /* Devil May Cry 4 (PC) */ + strstr(user_comment,"LoopStart=")==user_comment || /* Devil May Cry 4 (PC) */ + strstr(user_comment,"XIPH_CUE_LOOPSTART=")==user_comment) { /* Super Mario Run (Android) */ loop_start = atol(strrchr(user_comment,'=')+1); loop_flag = (loop_start >= 0); } @@ -390,7 +472,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch loop_length = atol(strrchr(user_comment,'=')+1); loop_length_found = 1; } - else if (strstr(user_comment,"title=-lps")==user_comment) { /* Memories Off #5 (PC) */ + else if (strstr(user_comment,"title=-lps")==user_comment) { /* KID [Memories Off #5 (PC), Remember11 (PC)] */ loop_start = atol(user_comment+10); loop_flag = (loop_start >= 0); } @@ -426,6 +508,14 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch loop_flag = 1; loop_end_found = 1; } + else if (strstr(user_comment, "XIPH_CUE_LOOPEND=") == user_comment) { /* XIPH_CUE_LOOPSTART pair */ + if (loop_flag) { + loop_length = atol(strrchr(user_comment, '=') + 1) - loop_start; + loop_length_found = 1; + } + } + + //;VGM_LOG("OGG: user_comment=%s\n", user_comment); } } @@ -437,7 +527,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch vgmstream->codec_data = data; /* store our fun extra datas */ vgmstream->channels = vi->channels; vgmstream->sample_rate = vi->rate; - vgmstream->num_streams = vgm_inf->total_subsongs; + vgmstream->num_streams = ovmi->total_subsongs; vgmstream->stream_size = stream_size; vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */ @@ -455,9 +545,9 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch vgmstream->loop_end_sample = vgmstream->num_samples; } - vgmstream->coding_type = coding_ogg_vorbis; - vgmstream->layout_type = vgm_inf->layout_type; - vgmstream->meta_type = vgm_inf->meta_type; + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = ovmi->meta_type; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c new file mode 100644 index 000000000..2a4f152b0 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -0,0 +1,235 @@ +#include "meta.h" +#include "../util.h" +#include "../coding/coding.h" +#include "../layout/layout.h" +#include "opus_interleave_streamfile.h" + +/* Nintendo OPUS - from Switch games, including header variations (not the same as Ogg Opus) */ + +static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, off_t offset, int32_t num_samples, int32_t loop_start, int32_t loop_end) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count; + off_t data_offset; + size_t data_size, skip = 0; + + + if ((uint32_t)read_32bitLE(offset + 0x00,streamFile) != 0x80000001) + goto fail; + + channel_count = read_8bit(offset + 0x09, streamFile); + /* 0x0a: packet size if CBR, 0 if VBR */ + data_offset = offset + read_32bitLE(offset + 0x10, streamFile); + skip = read_32bitLE(offset + 0x1c, streamFile); + + if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) + goto fail; + + data_size = read_32bitLE(data_offset + 0x04, streamFile); + + start_offset = data_offset + 0x08; + loop_flag = (loop_end > 0); /* -1 when not set */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(offset + 0x0c,streamFile); + vgmstream->meta_type = meta_OPUS; + + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + +#ifdef VGM_USE_FFMPEG + { + uint8_t buf[0x100]; + size_t bytes; + ffmpeg_custom_config cfg; + ffmpeg_codec_data *ffmpeg_data; + + bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate); + if (bytes <= 0) goto fail; + + memset(&cfg, 0, sizeof(ffmpeg_custom_config)); + cfg.type = FFMPEG_SWITCH_OPUS; + + ffmpeg_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg); + if (!ffmpeg_data) goto fail; + + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + if (ffmpeg_data->skipSamples <= 0) { + ffmpeg_set_skip_samples(ffmpeg_data, skip); + } + + if (vgmstream->num_samples == 0) { + vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, + vgmstream->sample_rate, streamFile) - skip; + } + } +#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; +} + + +/* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */ +VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { + off_t offset = 0; + int num_samples = 0, loop_start = 0, loop_end = 0; + + /* checks */ + if (!check_extensions(streamFile,"opus,lopus")) + goto fail; + + offset = 0x00; + num_samples = 0; + loop_start = 0; + loop_end = 0; + + return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); +fail: + return NULL; +} + +/* 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; + + /* 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; + + offset = 0x10; + num_samples = 0; + loop_start = read_32bitLE(0x00,streamFile); + loop_end = read_32bitLE(0x08,streamFile); + + return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); +fail: + return NULL; +} + +/* Capcom variation [Ultra Street Fighter II (Switch), Resident Evil: Revelations (Switch)] */ +VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + off_t offset = 0; + int num_samples = 0, loop_start = 0, loop_end = 0; + int channel_count; + + /* checks */ + if ( !check_extensions(streamFile,"opus,lopus")) + goto fail; + + channel_count = read_32bitLE(0x04,streamFile); + if (channel_count != 1 && channel_count != 2 && channel_count != 6) + goto fail; /* unknown stream layout */ + + num_samples = read_32bitLE(0x00,streamFile); + /* 0x04: channels, >2 uses interleaved streams (2ch+2ch+2ch) */ + loop_start = read_32bitLE(0x08,streamFile); + loop_end = read_32bitLE(0x0c,streamFile); + /* 0x10: frame size (with extra data) */ + /* 0x14: extra chunk count */ + /* 0x18: null */ + offset = read_32bitLE(0x1c,streamFile); + /* 0x20-8: config? (0x0077C102 04000000 E107070C) */ + /* 0x2c: some size? */ + /* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */ + + if (channel_count == 6) { + /* 2ch multistream hacky-hacks, don't try this at home. We'll end up with: + * main vgmstream > N vgmstream layers > substream IO deinterleaver > opus meta > Opus IO transmogrifier (phew) */ + //todo deinterleave has some problems with reading after total_size + layered_layout_data* data = NULL; + int layers = channel_count / 2; + int i; + int loop_flag = (loop_end > 0); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->layout_type = layout_layered; + + /* init layout */ + data = init_layout_layered(layers); + if (!data) goto fail; + vgmstream->layout_data = data; + + /* open each layer subfile */ + for (i = 0; i < layers; i++) { + STREAMFILE* temp_streamFile = setup_opus_interleave_streamfile(streamFile, offset+0x28*i, layers); + if (!temp_streamFile) goto fail; + + data->layers[i] = init_vgmstream_opus(temp_streamFile, meta_OPUS, 0x00, num_samples,loop_start,loop_end); + close_streamfile(temp_streamFile); + if (!data->layers[i]) goto fail; + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + + vgmstream->sample_rate = data->layers[0]->sample_rate; + vgmstream->num_samples = data->layers[0]->num_samples; + vgmstream->loop_start_sample = data->layers[0]->loop_start_sample; + vgmstream->loop_end_sample = data->layers[0]->loop_end_sample; + vgmstream->meta_type = meta_OPUS; + vgmstream->coding_type = data->layers[0]->coding_type; + + return vgmstream; + } + else { + return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + } + + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* 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; + + /* checks */ + if (!check_extensions(streamFile,"nop")) + goto fail; + if (read_32bitBE(0x00, streamFile) != 0x73616466 || /* "sadf" */ + read_32bitBE(0x08, streamFile) != 0x6f707573) /* "opus" */ + goto fail; + + offset = read_32bitLE(0x1c, streamFile); + num_samples = read_32bitLE(0x28, streamFile); + loop_flag = read_8bit(0x19, streamFile); + if (loop_flag) { + loop_start = read_32bitLE(0x2c, streamFile); + loop_end = read_32bitLE(0x30, streamFile); + } + + return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); +fail: + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus_interleave_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/opus_interleave_streamfile.h new file mode 100644 index 000000000..f4680f07e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus_interleave_streamfile.h @@ -0,0 +1,146 @@ +#ifndef _OPUS_CAPCOM_STREAMFILE_H_ +#define _OPUS_CAPCOM_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + int skip_frames; /* frames to skip from other streams at points */ + + /* config */ + int version; + int streams; + off_t start_offset; /* pointing to the stream's beginning */ + size_t total_size; /* size of the resulting substream */ +} opus_interleave_io_data; + + +/* Reads skipping other streams */ +static size_t opus_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, opus_interleave_io_data* data) { + size_t total_read = 0; + + /* ignore bad reads */ + if (offset < 0 || offset > data->total_size) { + return total_read; + } + + /* previous offset: re-start as we can't map logical<>physical offsets, since it may be VBR + * (kinda slow as it trashes buffers, but shouldn't happen often) */ + if (offset < data->logical_offset) { + data->physical_offset = data->start_offset; + data->logical_offset = 0x00; + } + + /* read doing one frame at a time */ + while (length > 0) { + size_t to_read, bytes_read; + off_t intrablock_offset, intradata_offset; + uint32_t data_size; + + data_size = read_32bitBE(data->physical_offset+0x00,streamfile); + + //if (offset >= data->total_size) //todo fix + // return total_read; + + /* Nintendo Opus header rather than a frame */ + if ((uint32_t)data_size == 0x01000080) { + data_size = read_32bitLE(data->physical_offset+0x10,streamfile); + data_size += 0x08; + } + else { + data_size += 0x08; + } + + /* skip frames from other streams */ + if (data->skip_frames) { + data->physical_offset += data_size; + data->skip_frames--; + continue; + } + + /* requested offset is outside current block, try next */ + if (offset >= data->logical_offset + data_size) { + data->physical_offset += data_size; + data->logical_offset += data_size; + data->skip_frames = data->streams - 1; + continue; + } + + /* reads could fall in the middle of the block */ + intradata_offset = offset - data->logical_offset; + intrablock_offset = intradata_offset; + + /* clamp reads up to this block's end */ + to_read = (data_size - intradata_offset); + if (to_read > length) + to_read = length; + if (to_read == 0) + return total_read; /* should never happen... */ + + /* finally read and move buffer/offsets */ + bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile); + total_read += bytes_read; + if (bytes_read != to_read) + return total_read; /* couldn't read fully */ + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + + /* block fully read, go next */ + if (intradata_offset + bytes_read == data_size) { + data->physical_offset += data_size; + data->logical_offset += data_size; + data->skip_frames = data->streams - 1; + } + } + + return total_read; +} + +static size_t opus_interleave_io_size(STREAMFILE *streamfile, opus_interleave_io_data* data) { + off_t info_offset; + + if (data->total_size) + return data->total_size; + + info_offset = read_32bitLE(data->start_offset+0x10,streamfile); + return read_32bitLE(data->start_offset + info_offset+0x04,streamfile); +} + + +/* Prepares custom IO for multistream Opus, interleaves 1 packet per stream */ +static STREAMFILE* setup_opus_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, int streams) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + opus_interleave_io_data io_data = {0}; + size_t io_data_size = sizeof(opus_interleave_io_data); + + io_data.start_offset = start_offset; + io_data.streams = streams; + io_data.physical_offset = start_offset; + io_data.total_size = opus_interleave_io_size(streamFile, &io_data); /* force init */ + + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, opus_interleave_io_read,opus_interleave_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _OPUS_CAPCOM_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c b/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c new file mode 100644 index 000000000..6c69b05b6 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c @@ -0,0 +1,120 @@ +#include "meta.h" +#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) { + VGMSTREAM * vgmstream = NULL; + off_t segment_offset; + int loop_flag, channel_count; + int i; + + segmented_layout_data *data = NULL; + int segment_count, loop_start_segment = 0, loop_end_segment = 0; + int num_samples = 0, loop_start_sample = 0, loop_end_sample = 0; + + + /* checks */ + if (!check_extensions(streamFile, "at9")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x09000000) /* file type? DSPs had 08 */ + 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) */ + + segment_count = 3; /* intro/loop/end */ + loop_start_segment = 1; + loop_end_segment = 1; + loop_flag = (segment_count > 0); + + /* init layout */ + data = init_layout_segmented(segment_count); + if (!data) goto fail; + + /* open each segment subfile */ + segment_offset = 0x1c; + for (i = 0; i < segment_count; i++) { + STREAMFILE* temp_streamFile; + size_t segment_size = read_32bitLE(0x10+0x04*i,streamFile); + + if (!segment_size) + goto fail; + + temp_streamFile = setup_opus_ppp_streamfile(streamFile, segment_offset,segment_size, "opus"); + if (!temp_streamFile) goto fail; + + data->segments[i] = init_vgmstream_opus_std(temp_streamFile); + close_streamfile(temp_streamFile); + if (!data->segments[i]) goto fail; + + segment_offset += segment_size; + + //todo there are some trailing samples that must be removed for smooth loops, start skip seems ok + data->segments[i]->num_samples -= 374; //not correct for all files, no idea how to calculate + + /* get looping and samples */ + if (loop_flag && loop_start_segment == i) + loop_start_sample = num_samples; + + num_samples += data->segments[i]->num_samples; + + if (loop_flag && loop_end_segment == i) + loop_end_sample = num_samples; + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + + channel_count = data->segments[0]->channels; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + + vgmstream->meta_type = meta_OPUS_PPP; + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + + vgmstream->layout_data = data; + data->loop_segment = loop_start_segment; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + 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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/p3d.c b/Frameworks/vgmstream/vgmstream/src/meta/p3d.c index 7e5350a79..d03e44587 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/p3d.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/p3d.c @@ -119,7 +119,7 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) { break; default: - VGM_LOG("unknown codec 0x%04x\n", codec); + VGM_LOG("P3D: unknown codec 0x%04x\n", codec); goto fail; } @@ -175,7 +175,7 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) { #endif default: - VGM_LOG("unknown codec 0x%04x\n", codec); + VGM_LOG("P3D: unknown codec 0x%04x\n", codec); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_mxst.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_mxst.c index 2e79f0f13..c33cfc268 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_mxst.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/pc_mxst.c @@ -179,7 +179,7 @@ VGMSTREAM * init_vgmstream_pc_mxst(STREAMFILE *streamFile) { /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = sample_rate; - vgmstream->layout_type = layout_mxch_blocked; + vgmstream->layout_type = layout_blocked_mxch; vgmstream->meta_type = meta_PC_MXST; if(bits_per_sample == 8) @@ -207,7 +207,7 @@ VGMSTREAM * init_vgmstream_pc_mxst(STREAMFILE *streamFile) { } } - mxch_block_update(start_offset, vgmstream); + block_update_mxch(start_offset, vgmstream); return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_xa30.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_xa30.c index f6662cb1e..7b615bcd2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_xa30.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/pc_xa30.c @@ -1,37 +1,36 @@ #include "meta.h" #include "../coding/coding.h" -/* XA30 - found in Driver: Parallel Lines (PC) */ +/* XA30 - found in Reflections games [Driver: Parallel Lines (PC), Driver 3 (PC)] */ VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count, codec; - size_t file_size, data_size; + size_t stream_size; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ - /* ".xa30" is just the ID, the real filename should be .XA */ - if (!check_extensions(streamFile,"xa,xa30")) + /* ".xa30/e4x" is just the ID, the real filename should be .XA */ + if (!check_extensions(streamFile,"xa,xa30,e4x")) goto fail; /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x58413330) /* "XA30" */ + if (read_32bitBE(0x00,streamFile) != 0x58413330 && /* "XA30" [Driver: Parallel Lines (PC)]*/ + read_32bitBE(0x00,streamFile) != 0x65347892) /* "e4x\92" [Driver 3 (PC)]*/ + goto fail; + if (read_32bitLE(0x04,streamFile) != 2) /* channels?, also extra check to avoid PS2/PC XA30 mixup */ goto fail; - if (read_32bitLE(0x04,streamFile) > 2) goto fail; /* extra check to avoid PS2/PC XA30 mixup */ - - /* reportedly from XA2WAV those are offset+data from a second stream (not seen) */ - if (read_32bitLE(0x14,streamFile) != 0 || read_32bitLE(0x1c,streamFile) != 0) goto fail; + total_subsongs = read_32bitLE(0x14,streamFile) != 0 ? 2 : 1; /* second stream offset (only in Driver 3) */ + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; loop_flag = 0; channel_count = 2; /* 0x04: channels? (always 2 in practice) */ - - codec = read_32bitLE(0x0c,streamFile); /* reportedly from XA2WAV (not seen) */ - start_offset = read_32bitLE(0x10,streamFile); - - file_size = get_streamfile_size(streamFile); - data_size = read_32bitLE(0x18,streamFile); - if (data_size+start_offset != file_size) goto fail; + codec = read_32bitLE(0x0c,streamFile); + start_offset = read_32bitLE(0x10 + 0x04*(target_subsong-1),streamFile); + stream_size = read_32bitLE(0x18 + 0x04*(target_subsong-1),streamFile); /* build the VGMSTREAM */ @@ -39,19 +38,26 @@ VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - /* 0x20: always 00016000?, rest of the header is null */ - + /* 0x20: always IMA=00016000, PCM=00056000 PCM?, rest of the header is null */ + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_PC_XA30; switch(codec) { + case 0x00: /* PCM (rare, seen in Driver 3) */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile) / 2; + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); + break; + case 0x01: /* MS-IMA variation */ vgmstream->coding_type = coding_REF_IMA; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); - vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, vgmstream->interleave_block_size, channel_count); + vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, vgmstream->interleave_block_size, channel_count); break; - case 0x00: /* PCM? */ default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pcm_sre.c b/Frameworks/vgmstream/vgmstream/src/meta/pcm_sre.c new file mode 100644 index 000000000..3a51c1fc3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/pcm_sre.c @@ -0,0 +1,76 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .PCM+SRE. - Capcom's header+data container thing [Viewtiful Joe (PS2)] */ +VGMSTREAM * init_vgmstream_pcm_sre(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + off_t start_offset; + int loop_flag, channel_count; + size_t table1_entries, table2_entries; + off_t table1_offset, table2_offset, header_offset; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + /* .pcm=data, .sre=header */ + if (!check_extensions(streamFile, "pcm")) + goto fail; + + /* first PS-ADPCM frame should be is null */ + if (read_32bitBE(0x00,streamFile) != 0x00020000 || + read_32bitBE(0x04,streamFile) != 0x00000000 || + read_32bitBE(0x08,streamFile) != 0x00000000 || + read_32bitBE(0x0c,streamFile) != 0x00000000) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile, "sre"); + if (!streamHeader) goto fail; + + table1_entries = read_32bitLE(0x00, streamHeader); + table1_offset = read_32bitLE(0x04, streamHeader); + table2_entries = read_32bitLE(0x08, streamHeader); + table2_offset = read_32bitLE(0x0c, streamHeader); + if (table1_entries*0x60 + table1_offset != table2_offset) + goto fail; /* just in case */ + + total_subsongs = table2_entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + header_offset = table2_offset + (target_subsong-1)*0x20; + + channel_count = read_32bitLE(header_offset+0x00,streamHeader); + loop_flag = read_32bitLE(header_offset+0x18,streamHeader); + start_offset = read_32bitLE(header_offset+0x08,streamHeader); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_16bitLE(header_offset+0x04,streamHeader); + vgmstream->meta_type = meta_PCM_SRE; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x1000; + + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(header_offset+0x0c,streamHeader), channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(header_offset+0x10,streamHeader)*channel_count, channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(header_offset+0x14,streamHeader)*channel_count, channel_count); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = read_32bitLE(header_offset+0x0c,streamHeader); + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + close_streamfile(streamHeader); + return vgmstream; + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ppst.c b/Frameworks/vgmstream/vgmstream/src/meta/ppst.c new file mode 100644 index 000000000..3802b662b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ppst.c @@ -0,0 +1,60 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "ppst_streamfile.h" + + +/* PPST - ParaPpa STream (maybe), extracted from .img bigfile [Parappa the Rapper (PSP)] */ +VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "sng")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x50505354) /* "PPST" */ + goto fail; + + /* header has some control and comment fields then interleaved RIFF .at3 */ + + /* count subsongs (mainly 4, rarely 1) */ + { + off_t offset = 0xa0; + + total_subsongs = 0; + while (offset < 0x800) { + if (read_32bitLE(offset + 0x04, streamFile) == 0) /* subsong size */ + break; + total_subsongs++; + offset += 0x08; + } + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + } + + { + off_t start_offset = 0x800; + size_t interleave_size = 0x4000; + size_t stride_size = 0x4000*total_subsongs; + /* subsong header at 0xa0, 0x00(1): id, 0x01(3): blocks of interleave */ + size_t stream_size = read_32bitLE(0xA0+0x08*(target_subsong-1)+0x04, streamFile); + + STREAMFILE* temp_streamFile = setup_ppst_streamfile(streamFile, start_offset+interleave_size*(target_subsong-1), interleave_size, stride_size, stream_size); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_riff(temp_streamFile); + close_streamfile(temp_streamFile); + if (!vgmstream) goto fail; + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->meta_type = meta_PPST; + } + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ppst_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ppst_streamfile.h new file mode 100644 index 000000000..c3965e11e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ppst_streamfile.h @@ -0,0 +1,101 @@ +#ifndef _PPST_STREAMFILE_H_ +#define _PPST_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + off_t start_physical_offset; /* interleaved data start, for this substream */ + size_t interleave_block_size; /* max size that can be read before encountering other substreams */ + size_t stride_size; /* step size between interleave blocks (interleave*channels) */ + size_t stream_size; /* final size of the deinterleaved substream */ +} ppst_io_data; + + +/* Handles deinterleaving of complete files, skipping portions or other substreams. */ +static size_t ppst_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ppst_io_data* data) { + size_t total_read = 0; + + + while (length > 0) { + size_t to_read; + size_t length_available; + off_t block_num; + off_t intrablock_offset; + off_t physical_offset; + + if (offset >= data->stream_size) + return total_read; + + block_num = offset / data->interleave_block_size; + intrablock_offset = offset % data->interleave_block_size; + physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset; + length_available = data->interleave_block_size - intrablock_offset; + if (length_available > data->stream_size - offset) + length_available = data->stream_size - offset; + + if (length < length_available) { + to_read = length; + } + else { + to_read = length_available; + } + + if (to_read > 0) { + size_t bytes_read; + + bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile); + total_read += bytes_read; + + if (bytes_read != to_read) { + return total_read; + } + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + } + } + + return total_read; +} + +static size_t ppst_io_size(STREAMFILE *streamfile, ppst_io_data* data) { + return data->stream_size; +} + + +static STREAMFILE* setup_ppst_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t stream_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + ppst_io_data io_data = {0}; + size_t io_data_size = sizeof(ppst_io_data); + + io_data.start_physical_offset = start_offset; + io_data.interleave_block_size = interleave_block_size; + io_data.stride_size = stride_size; + io_data.stream_size = stream_size; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, ppst_io_read,ppst_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"at3"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _SCD_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_adm.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_adm.c index 82e2f51bc..b1c6d3dbc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_adm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_adm.c @@ -1,75 +1,107 @@ #include "meta.h" #include "../layout/layout.h" -#include "../util.h" +#include "../coding/coding.h" +#include -/* WAD (from The golden Compass) */ +static int get_adm_loop_info(STREAMFILE *streamFile, off_t *loop_start_offset); + +/* .adm - from Dragon Quest V (PS2) */ VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 0; - int channel_count; - int i; - off_t start_offset; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("adm",filename_extension(filename))) goto fail; + int channel_count, loop_flag = 0; + off_t start_offset, loop_start_offset = 0; - loop_flag = 0; - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; + /* checks */ + if (!check_extensions(streamFile,"adm")) + goto fail; - /* fill in the vital statistics */ - start_offset = 0x0; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_PSX; - -#if 0 - vgmstream->num_samples = read_32bitLE(0x0,streamFile)/channel_count/16*28; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitLE(0x0,streamFile)/channel_count/16*28; - } -#endif - - vgmstream->layout_type = layout_ps2_adm_blocked; - vgmstream->interleave_block_size = 0x400; - - - vgmstream->meta_type = meta_PS2_ADM; - - /* open the file for reading by each channel */ + /* raw data, but test some .ADM blocks as they always start with PS-ADPCM flag 0x06 every 0x1000 */ { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); - - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; + int i; + for (i = 0; i < 10; i++) { + if (read_8bit(0x1000*i + 0x01, streamFile) != 0x06) + goto fail; } } + start_offset = 0x00; + loop_flag = get_adm_loop_info(streamFile, &loop_start_offset); + channel_count = 2; - /* Calc num_samples */ - ps2_adm_block_update(start_offset,vgmstream); - vgmstream->num_samples=0; //(get_streamfile_size(streamFile)/0x1000*0xFE0)/32*28; + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; - do { - - vgmstream->num_samples += 0xFE0*14/16; - ps2_adm_block_update(vgmstream->next_block_offset,vgmstream); - } while (vgmstream->next_block_offsetsample_rate = 44100; + vgmstream->meta_type = meta_PS2_ADM; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_adm; - ps2_adm_block_update(start_offset,vgmstream); + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + /* calc num_samples as playable data size varies between files/blocks */ + { + vgmstream->next_block_offset = start_offset; + do { + block_update_adm(vgmstream->next_block_offset,vgmstream); + + if (loop_flag && vgmstream->current_block_offset == loop_start_offset) + vgmstream->loop_start_sample = vgmstream->num_samples; + vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + + if (loop_flag) + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + block_update_adm(start_offset,vgmstream); return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } + +/* loops are not in the .ADM or .DAT bigfile containing them but in the exe; manually get them (a bit meh but whatevs) */ +static int get_adm_loop_info(STREAMFILE *streamFile, off_t *loop_start_offset) { + char file_name[PATH_LIMIT]; + char index_name[PATH_LIMIT]; + STREAMFILE *streamExe = NULL; + int i, name_index = -1, loop_flag; + off_t offset; + + streamExe = open_streamfile_by_filename(streamFile, "SLPM_655.55"); + if (!streamExe) goto fail; + + get_streamfile_filename(streamFile, file_name, PATH_LIMIT); + + /* get file index from name list (file_name == index_name = index number */ + offset = 0x23B3c0; + for (i = 0; i < 51; i++) { + read_string(index_name,0x20+1, offset,streamExe); + + if (strcmp(index_name, file_name)==0) { + name_index = i; + break; + } + offset += 0x20; + } + if (name_index < 0) + goto fail; + + /* get file info using index */ + offset = 0x23BAEC + 0x1c*name_index; + loop_flag = (read_32bitLE(offset + 0x10, streamExe) == 0); /* 1: don't loop, 0: loop */ + if (loop_flag) { /* loop flag */ + *loop_start_offset = read_32bitLE(offset + 0x04, streamExe); + } + /* 0x08: num_samples/loop_end, 0x0c: sample rate (always 44100), 0x14/18: some size? */ + + close_streamfile(streamExe); + return loop_flag; +fail: + close_streamfile(streamExe); + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_bmdx.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_bmdx.c index a1e735af3..229b7332f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_bmdx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_bmdx.c @@ -1,92 +1,116 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +static STREAMFILE* setup_bmdx_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size); + + +/* .bmdx - from Beatmania IIDX (PS2) games */ VGMSTREAM * init_vgmstream_ps2_bmdx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag=0; - int channel_count; + STREAMFILE *temp_streamFile = NULL; off_t start_offset; - int i; + size_t data_size; + int loop_flag, channel_count, encryption; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("bmdx",filename_extension(filename))) goto fail; - /* check NPSF Header */ + /* checks */ + if (!check_extensions(streamFile, "bmdx")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x01006408 || - read_32bitBE(0x04,streamFile) != 0) + read_32bitBE(0x04,streamFile) != 0x00) goto fail; - /* check loop */ - loop_flag = (read_32bitLE(0x10,streamFile)!=0); - channel_count=read_32bitLE(0x1C,streamFile); - + start_offset = read_32bitLE(0x08,streamFile); + data_size = read_32bitLE(0x0c,streamFile); + loop_flag = (read_32bitLE(0x10,streamFile) != 0x00); + channel_count = read_32bitLE(0x1C,streamFile); + encryption = (read_32bitLE(0x20,streamFile) == 1); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - /* Check for Compression Scheme */ - if (read_32bitLE(0x20,streamFile) == 1) - vgmstream->coding_type = coding_PSX_bmdx; - else - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x0c,streamFile)*28/16/channel_count; - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x10,streamFile)*28/16/channel_count; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else if (channel_count > 1) { - vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); - vgmstream->layout_type = layout_interleave; - } + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x10,streamFile), channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_PS2_BMDX; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); - start_offset = read_32bitLE(0x08,streamFile); - - if (vgmstream->coding_type == coding_PSX_bmdx) - { - uint8_t xor = read_8bit(start_offset,streamFile); - uint8_t add = (~(uint8_t)read_8bit(start_offset+2,streamFile))+1; - int c; - for (c=0;cch[c].bmdx_xor = xor; - vgmstream->ch[c].bmdx_add = add; - } + /* later games are encrypted [beatmaniaIIDX 14 GOLD (PS2)] */ + if (encryption) { + temp_streamFile = setup_bmdx_streamfile(streamFile, start_offset, data_size); + if (!temp_streamFile) goto fail; } - /* open the file for reading by each channel */ - { - for (i=0;ich[0].streamfile) { - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,0x8000); - } - vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile; + if (!vgmstream_open_stream(vgmstream,encryption ? temp_streamFile : streamFile,start_offset)) + goto fail; - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(start_offset+vgmstream->interleave_block_size*i); - } - } - + close_streamfile(temp_streamFile); return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +typedef struct { + uint8_t xor; + uint8_t add; + off_t start_offset; + size_t data_size; +} bmdx_decryption_data; + +static size_t bmdx_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bmdx_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + if (offset+i >= data->start_offset && offset+i < data->start_offset + data->data_size) { + if (((offset+i) % 0x10) == 0) /* XOR header byte per frame */ + dest[i] = dest[i] ^ data->xor; + else if (((offset+i) % 0x10) == 2) /* ADD first data byte per frame */ + dest[i] = (uint8_t)(dest[i] + data->add); + } + } + + return bytes_read; +} + +static STREAMFILE* setup_bmdx_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + bmdx_decryption_data io_data = {0}; + size_t io_data_size = sizeof(bmdx_decryption_data); + + /* setup decryption (usually xor=0xFF and add=0x02) */ + io_data.xor = read_8bit(start_offset,streamFile); + io_data.add = (~(uint8_t)read_8bit(start_offset+2,streamFile)) + 0x01; + io_data.start_offset = start_offset; + io_data.data_size = data_size; + + + /* setup custom streamfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bmdx_decryption_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_filp.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_filp.c index ec5d6757f..967cf2cc7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_filp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_filp.c @@ -37,7 +37,7 @@ VGMSTREAM * init_vgmstream_filp(STREAMFILE *streamFile) { vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x110,streamFile); vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_filp_blocked; + vgmstream->layout_type = layout_blocked_filp; vgmstream->meta_type = meta_FILP; /* open the file for reading */ @@ -50,7 +50,7 @@ VGMSTREAM * init_vgmstream_filp(STREAMFILE *streamFile) { } } - filp_block_update(start_offset,vgmstream); + block_update_filp(start_offset,vgmstream); vgmstream->num_samples = read_32bitLE(0x10C,streamFile)/16*28; if (loop_flag) { vgmstream->loop_start_sample = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_iab.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_iab.c index 0d9fdb782..593cb4f89 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_iab.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_iab.c @@ -1,70 +1,55 @@ #include "meta.h" #include "../layout/layout.h" +#include "../coding/coding.h" #include "../util.h" -/* IAB: Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */ +/* .IAB - from Runtime(?) games [Ueki no Housoku - Taosu ze Robert Juudan!! (PS2), RPG Maker 3 (PS2)] */ VGMSTREAM * init_vgmstream_ps2_iab(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 0; - int channel_count; - int i; - off_t start_offset; + off_t start_offset; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("iab",filename_extension(filename))) goto fail; - /* check header */ + /* checks */ + if (!check_extensions(streamFile,"iab")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x10000000) goto fail; - - /* check file size */ if (read_32bitLE(0x1C,streamFile) != get_streamfile_size(streamFile)) goto fail; loop_flag = 0; channel_count = 2; - + start_offset = 0x40; + /* 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 = read_32bitLE(0x4,streamFile); - vgmstream->coding_type = coding_PSX; - - vgmstream->layout_type = layout_ps2_iab_blocked; - vgmstream->interleave_block_size = read_32bitLE(0xC, streamFile); + vgmstream->sample_rate = read_32bitLE(0x04,streamFile); vgmstream->meta_type = meta_PS2_IAB; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_ps2_iab; + //vgmstream->interleave_block_size = read_32bitLE(0x0C, streamFile); /* unneeded */ - /* open the file for reading by each channel */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples */ { - for (i=0;ich[i].streamfile = streamFile->open(streamFile, filename, vgmstream->interleave_block_size); - if (!vgmstream->ch[i].streamfile) goto fail; + vgmstream->next_block_offset = start_offset; + do { + block_update_ps2_iab(vgmstream->next_block_offset, vgmstream); + vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); } - /* Calc num_samples */ - ps2_iab_block_update(start_offset, vgmstream); - vgmstream->num_samples=0; - - do - { - vgmstream->num_samples += 0x4000 * 14 / 16; - ps2_iab_block_update(vgmstream->next_block_offset, vgmstream); - } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - - ps2_iab_block_update(start_offset, vgmstream); - + block_update_ps2_iab(start_offset, vgmstream); return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_jstm.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_jstm.c index bb7399958..9f78093c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_jstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_jstm.c @@ -1,63 +1,103 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* JSTM (.STM (renamed .JSTM) from Tantei Jinguji Saburo - Kind of Blue) */ +static STREAMFILE* setup_jstm_streamfile(STREAMFILE *streamFile, off_t start_offset); + +/* JSTM - from Tantei Jinguji Saburo - Kind of Blue (PS2) */ VGMSTREAM * init_vgmstream_ps2_jstm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset = 0x20; - int loop_flag; - int channel_count; - char filename[PATH_LIMIT]; + STREAMFILE *temp_streamFile = NULL; + off_t start_offset; + int loop_flag, channel_count; - /* check extension */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("stm",filename_extension(filename)) && - strcasecmp("jstm",filename_extension(filename))) goto fail; - /* check header (JSTM) */ - if (read_32bitBE(0x0,streamFile) != 0x4A53544D) goto fail; + /* checks */ + /* .stm: original, .jstm: header id */ + if (!check_extensions(streamFile, "stm,jstm")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4A53544D) /* "JSTM" */ + goto fail; - loop_flag = (read_32bitLE(0x14,streamFile) != 0); - channel_count = read_16bitLE(0x4,streamFile); - - // hmm, don't know what 6 is, one is probably bytes per sample and the - // other is channels, but who can say? - if (channel_count != read_16bitLE(0x6,streamFile)) goto fail; + start_offset = 0x20; + channel_count = read_16bitLE(0x04,streamFile); + if (channel_count != read_16bitLE(0x06,streamFile)) /* 0x02, interleave? */ + goto fail; + loop_flag = (read_32bitLE(0x14,streamFile) != -1); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the statistics vitale */ - vgmstream->sample_rate = read_32bitLE(0x8,streamFile); - vgmstream->coding_type = coding_PCM16LE_XOR_int; - vgmstream->num_samples = read_32bitLE(0xC,streamFile)/2/channel_count; - vgmstream->layout_type = layout_none; + vgmstream->sample_rate = read_32bitLE(0x08,streamFile); + vgmstream->num_samples = pcm_bytes_to_samples(read_32bitLE(0x0C,streamFile),channel_count,16); + if (loop_flag) { + vgmstream->loop_start_sample = pcm_bytes_to_samples(read_32bitLE(0x14,streamFile),channel_count,16); + vgmstream->loop_end_sample = vgmstream->num_samples; + } vgmstream->meta_type = meta_PS2_JSTM; + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; - if (loop_flag) { - vgmstream->loop_start_sample=read_32bitLE(0x14,streamFile)/2/channel_count; - vgmstream->loop_end_sample=vgmstream->num_samples; - } - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - - for (i=0; i < channel_count; i++) { - vgmstream->ch[i].streamfile = file; - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = start_offset + 2*i; - vgmstream->ch[i].key_xor = 0x5A5A; - } - } + temp_streamFile = setup_jstm_streamfile(streamFile, start_offset); + if (!temp_streamFile) goto fail; + if (!vgmstream_open_stream(vgmstream,temp_streamFile,start_offset)) + goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +typedef struct { + off_t start_offset; +} jstm_decryption_data; + +static size_t jstm_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, jstm_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + if (offset+i >= data->start_offset) { + dest[i] = dest[i] ^ 0x5A; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_jstm_streamfile(STREAMFILE *streamFile, off_t start_offset) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + jstm_decryption_data io_data = {0}; + size_t io_data_size = sizeof(jstm_decryption_data); + + /* setup decryption */ + io_data.start_offset = start_offset; + + + /* setup custom streamfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, jstm_decryption_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c index 3e4fff1f8..dfb23f4f9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c @@ -1,353 +1,338 @@ #include "meta.h" #include "../util.h" -/* MIB +static int check_psadpcm(STREAMFILE *streamFile); - PS2 MIB format is a headerless format. - The interleave value can be found by checking the body of the data. - - The interleave start allways at offset 0 with a int value (which can have - many values : 0x0000, 0x0002, 0x0006 etc...) follow by 12 empty (zero) values. - - The interleave value is the offset where you found the same 16 bytes. - - The n° of channels can be found by checking each time you found this 16 bytes. - - The interleave value can be very "large" (up to 0x20000 found so far) and is allways - a 0x10 multiply value. - - The loop values can be found by checking the 'tags' offset (found @ 0x02 each 0x10 bytes). - 06 = start of the loop point (can be found for each channel) - 03 - end of the loop point (can be found for each channel) - - The .MIH header contains all informations about frequency, numbers of channels, interleave - but has, afaik, no loop values. - - known extensions : MIB (MIH for the header) MIC (concatenation of MIB+MIH) - Nota : the MIC stuff is not supported here as there is - another MIC format which can be found in Koei Games. - - 2008-05-14 - Fastelbja : First version ... - 2008-05-20 - Fastelbja : Fix loop value when loopEnd==0 -*/ +/* MIB+MIH - from namCollection: Ace Combat 2 (PS2), Rampage - Total Destruction (PS2) */ +/* headerless PS-ADPCM - from Katamary Damacy (PS2), Air (PS2), Aladdin: Nasira's Revenge (PS1) + * (guesses interleave and channels by testing data and using the file extension, and finds + * loops in PS-ADPCM flags; this is a crutch for convenience, consider using GENH/TXTH instead). */ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; + VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileMIH = NULL; + off_t start_offset = 0x00; char filename[PATH_LIMIT]; - - uint8_t mibBuffer[0x10]; - uint8_t testBuffer[0x10]; - uint8_t doChannelUpdate=1; - uint8_t bDoUpdateInterleave=1; - size_t fileLength; - - off_t loopStart = 0; - off_t loopEnd = 0; + uint8_t mibBuffer[0x10]; + uint8_t testBuffer[0x10]; - off_t interleave = 0; + size_t fileLength; + off_t loopStart = 0; + off_t loopEnd = 0; + off_t interleave = 0; - off_t readOffset = 0; + off_t readOffset = 0; - char filenameMIH[PATH_LIMIT]; - off_t loopStartPoints[0x10]; - int loopStartPointsCount=0; + off_t loopStartPoints[0x10] = {0}; + int loopStartPointsCount=0; + off_t loopEndPoints[0x10] = {0}; + int loopEndPointsCount=0; + int loopToEnd=0; + int forceNoLoop=0; + int gotEmptyLine=0; - off_t loopEndPoints[0x10]; - int loopEndPointsCount=0; + uint8_t gotMIH=0; - int loopToEnd=0; - int forceNoLoop=0; - int gotEmptyLine=0; + int i, channel_count=0; - uint8_t gotMIH=0; - int i, channel_count=0; - - // Initialize loop point to 0 - for(i=0; i<0x10; i++) { - loopStartPoints[i]=0; - loopEndPoints[i]=0; - } - - /* check extension, case insensitive */ + /* checks + * .mib: common, but many ext-less files are renamed to this. + * .mi4: fake .mib to force another sample rate + * .cvs: Aladdin - Nasira's Revenge (PS1) + * .snds: The Incredibles (PS2) + * .vb: Tantei Jinguuji Saburo - Mikan no Rupo (PS1) + * .xag: Hagane no Renkinjutsushi - Dream Carnival (PS2) + * */ streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("mib",filename_extension(filename)) && - strcasecmp("mi4",filename_extension(filename)) && - strcasecmp("vb",filename_extension(filename)) && - strcasecmp("xag",filename_extension(filename))) goto fail; + if (strcasecmp("cvs",filename_extension(filename)) && + strcasecmp("mib",filename_extension(filename)) && + strcasecmp("mi4",filename_extension(filename)) && + strcasecmp("snds",filename_extension(filename))&& + strcasecmp("vb",filename_extension(filename)) && + strcasecmp("xag",filename_extension(filename))) + goto fail; - /* check for .MIH file */ - strcpy(filenameMIH,filename); - strcpy(filenameMIH+strlen(filenameMIH)-3,"MIH"); + /* test if raw PS-ADPCM */ + if (!check_psadpcm(streamFile)) + goto fail; - streamFileMIH = streamFile->open(streamFile,filenameMIH,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (streamFileMIH) gotMIH = 1; - /* Search for interleave value & loop points */ - /* Get the first 16 values */ - fileLength = get_streamfile_size(streamFile); - - readOffset+=(off_t)read_streamfile(mibBuffer,0,0x10,streamFile); - readOffset=0; - mibBuffer[0]=0; + /* .MIB may come with a .MIH header file */ + if (strcasecmp("mib",filename_extension(filename))==0) { + streamFileMIH = open_streamfile_by_ext(streamFile,"mih"); + if (streamFileMIH) + gotMIH = 1; + } - do { - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - // be sure to point to an interleave value - if(readOffset<(int32_t)(fileLength*0.5)) { - if(memcmp(testBuffer+2, mibBuffer+2,0x0e)) { - if(doChannelUpdate) { - doChannelUpdate=0; - channel_count++; - } - if(channel_count<2) - bDoUpdateInterleave=1; - } + fileLength = get_streamfile_size(streamFile); - testBuffer[0]=0; - if(!memcmp(testBuffer,mibBuffer,0x10)) { - - gotEmptyLine=1; - - if(bDoUpdateInterleave) { - bDoUpdateInterleave=0; - interleave=readOffset-0x10; - } - if(((readOffset-0x10)==(channel_count*interleave))) { - doChannelUpdate=1; - } - } - } - - // Loop Start ... - if(testBuffer[0x01]==0x06) - { - if(loopStartPointsCount<0x10) - { - loopStartPoints[loopStartPointsCount] = readOffset-0x10; - loopStartPointsCount++; - } - } - - // Loop End ... - if((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) { - if(loopEndPointsCount<0x10) - { - loopEndPoints[loopEndPointsCount] = readOffset; - loopEndPointsCount++; - } - } - - if(testBuffer[0x01]==0x04) - { - // 0x04 loop points flag can't be with a 0x03 loop points flag - if(loopStartPointsCount<0x10) - { - loopStartPoints[loopStartPointsCount] = readOffset-0x10; - loopStartPointsCount++; - - // Loop end value is not set by flags ... - // go until end of file - loopToEnd=1; - } - } - - } while (streamFile->get_offset(streamFile)<((int32_t)fileLength)); - - if((testBuffer[0]==0x0c) && (testBuffer[1]==0)) - forceNoLoop=1; - - if(channel_count==0) - channel_count=1; - - if(gotMIH) - channel_count=read_32bitLE(0x08,streamFileMIH); - - // force no loop - if(!strcasecmp("vb",filename_extension(filename))) - loopStart=0; - - if(!strcasecmp("xag",filename_extension(filename))) - channel_count=2; - - // Calc Loop Points & Interleave ... - if(loopStartPointsCount>=2) - { - // can't get more then 0x10 loop point ! - if(loopStartPointsCount<=0x0F) { - // Always took the first 2 loop points - interleave=loopStartPoints[1]-loopStartPoints[0]; - loopStart=loopStartPoints[1]; - - // Can't be one channel .mib with interleave values - if((interleave>0) && (channel_count==1)) - channel_count=2; - } else - loopStart=0; - } - - if(loopEndPointsCount>=2) - { - // can't get more then 0x10 loop point ! - if(loopEndPointsCount<=0x0F) { - // No need to recalculate interleave value ... - loopEnd=loopEndPoints[loopEndPointsCount-1]; - - // Can't be one channel .mib with interleave values - if(channel_count==1) channel_count=2; - } else { - loopToEnd=0; - loopEnd=0; - } - } - - if (loopToEnd) - loopEnd=fileLength; - - // force no loop - if(forceNoLoop) - loopEnd=0; - - if((interleave>0x10) && (channel_count==1)) - channel_count=2; - - if(interleave==0) interleave=0x10; - - // further check on channel_count ... - if(gotEmptyLine) - { - int newChannelCount = 0; - - readOffset=0; - - do - { - newChannelCount++; - read_streamfile(testBuffer,readOffset,0x10,streamFile); - readOffset+=interleave; - } while(!memcmp(testBuffer,mibBuffer,16)); - - newChannelCount--; - - if(newChannelCount>channel_count) - channel_count=newChannelCount; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,(loopEnd!=0)); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - - if(gotMIH) { - // Read stuff from the MIH file - vgmstream->channels = read_32bitLE(0x08,streamFileMIH); - vgmstream->sample_rate = read_32bitLE(0x0C,streamFileMIH); - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFileMIH); - vgmstream->num_samples=((read_32bitLE(0x10,streamFileMIH)* - (read_32bitLE(0x14,streamFileMIH)-1)*2)+ - ((read_32bitLE(0x04,streamFileMIH)>>8)*2))/16*28/2; - } else { - vgmstream->channels = channel_count; - vgmstream->interleave_block_size = interleave; - - if(!strcasecmp("mib",filename_extension(filename))) - vgmstream->sample_rate = 44100; - - if(!strcasecmp("mi4",filename_extension(filename))) - vgmstream->sample_rate = 48000; - - if(!strcasecmp("xag",filename_extension(filename))) { - vgmstream->channels=2; - vgmstream->sample_rate = 44100; - } - - if(!strcasecmp("vb",filename_extension(filename))) - { - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size=0; - vgmstream->sample_rate = 22050; - vgmstream->channels = 1; - } - - vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28); - } - - if(loopEnd!=0) { - if(vgmstream->channels==1) { - vgmstream->loop_start_sample = loopStart/16*18; - vgmstream->loop_end_sample = loopEnd/16*28; - } else { - vgmstream->loop_start_sample = ((((loopStart/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*14*channel_count)/channel_count; - if(loopStart%vgmstream->interleave_block_size) { - vgmstream->loop_start_sample += (((loopStart%vgmstream->interleave_block_size)-1)/16*14*channel_count); - } - - if(loopEnd==fileLength) - { - vgmstream->loop_end_sample=(loopEnd/16*28)/channel_count; - } else { - vgmstream->loop_end_sample = ((((loopEnd/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*14*channel_count)/channel_count; - - if(loopEnd%vgmstream->interleave_block_size) { - vgmstream->loop_end_sample += (((loopEnd%vgmstream->interleave_block_size)-1)/16*14*channel_count); - } - } - } - } - - if(loopToEnd) - { - // try to find if there's no empty line ... - int emptySamples=0; - - for(i=0; i<16;i++) { - mibBuffer[i]=0; - } - - readOffset=fileLength-0x10; - - do { - read_streamfile(testBuffer,readOffset,0x10,streamFile); - if(!memcmp(mibBuffer,testBuffer,16)) - { - emptySamples+=28; - } - readOffset-=0x10; - } while(!memcmp(testBuffer,mibBuffer,16)); - - vgmstream->loop_end_sample-=(emptySamples*channel_count); - } - vgmstream->meta_type = meta_PS2_MIB; - - if (gotMIH) { - vgmstream->meta_type = meta_PS2_MIB_MIH; - close_streamfile(streamFileMIH); streamFileMIH=NULL; - } - - /* open the file for reading by each channel */ + /* Search for interleave value (checking channel starts) and loop points (using PS-ADPCM flags). + * Channel start will by 0x0000, 0x0002, 0x0006 followed by 12 zero values. + * Interleave value is the offset where those repeat, and channels the number of times. + * Loop flags in second byte are: 0x06 = start, 0x03 = end (per channel). + * Interleave can be large (up to 0x20000 found so far) and is always a 0x10 multiple value. */ + readOffset+=(off_t)read_streamfile(mibBuffer,0,0x10,streamFile); + mibBuffer[0]=0; { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x8000); + uint8_t doChannelUpdate=1; + uint8_t bDoUpdateInterleave=1; - if (!vgmstream->ch[i].streamfile) goto fail; + readOffset=0; + do { + readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); + // be sure to point to an interleave value + if(readOffset<(int32_t)(fileLength*0.5)) { - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; + if(memcmp(testBuffer+2, mibBuffer+2,0x0e)) { + if(doChannelUpdate) { + doChannelUpdate=0; + channel_count++; + } + if(channel_count<2) + bDoUpdateInterleave=1; + } + + testBuffer[0]=0; + if(!memcmp(testBuffer,mibBuffer,0x10)) { + gotEmptyLine=1; + + if(bDoUpdateInterleave) { + bDoUpdateInterleave=0; + interleave=readOffset-0x10; + } + if(readOffset-0x10 == channel_count*interleave) { + doChannelUpdate=1; + } + } + } + + // Loop Start ... + if(testBuffer[0x01]==0x06) { + if(loopStartPointsCount<0x10) { + loopStartPoints[loopStartPointsCount] = readOffset-0x10; + loopStartPointsCount++; + } + } + + // Loop End ... + if(testBuffer[0x01]==0x03 && testBuffer[0x03]!=0x77) { + if(loopEndPointsCount<0x10) { + loopEndPoints[loopEndPointsCount] = readOffset; + loopEndPointsCount++; + } + } + + if(testBuffer[0x01]==0x04) { + // 0x04 loop points flag can't be with a 0x03 loop points flag + if(loopStartPointsCount<0x10) { + loopStartPoints[loopStartPointsCount] = readOffset-0x10; + loopStartPointsCount++; + + // Loop end value is not set by flags ... + // go until end of file + loopToEnd=1; + } + } + + } while (streamFile->get_offset(streamFile)<((int32_t)fileLength)); + } + + if(testBuffer[0]==0x0c && testBuffer[1]==0) + forceNoLoop=1; + + if(channel_count==0) + channel_count=1; + + if(gotMIH) + channel_count=read_32bitLE(0x08,streamFileMIH); + + // force no loop + if(!strcasecmp("vb",filename_extension(filename))) + loopStart=0; + + if(!strcasecmp("xag",filename_extension(filename))) + channel_count=2; + + // Calc Loop Points & Interleave ... + if(loopStartPointsCount>=2) { + // can't get more then 0x10 loop point ! + if(loopStartPointsCount<=0x0F) { + // Always took the first 2 loop points + interleave=loopStartPoints[1]-loopStartPoints[0]; + loopStart=loopStartPoints[1]; + + // Can't be one channel .mib with interleave values + if(interleave>0 && channel_count==1) + channel_count=2; + } else { + loopStart=0; } } + if(loopEndPointsCount>=2) { + // can't get more then 0x10 loop point ! + if(loopEndPointsCount<=0x0F) { + // No need to recalculate interleave value ... + loopEnd=loopEndPoints[loopEndPointsCount-1]; + + // Can't be one channel .mib with interleave values + if(channel_count==1) + channel_count=2; + } else { + loopToEnd=0; + loopEnd=0; + } + } + + if (loopToEnd) + loopEnd=fileLength; + + if(forceNoLoop) + loopEnd=0; + + if(interleave>0x10 && channel_count==1) + channel_count=2; + + if(interleave==0) + interleave=0x10; + + // further check on channel_count ... + if(gotEmptyLine) { + int newChannelCount = 0; + + readOffset=0; + + /* count empty lines at interleave = channels */ + do { + newChannelCount++; + read_streamfile(testBuffer,readOffset,0x10,streamFile); + readOffset+=interleave; + } while(!memcmp(testBuffer,mibBuffer,16)); + + newChannelCount--; + if(newChannelCount>channel_count) + channel_count=newChannelCount; + } + + if (!strcasecmp("cvs", filename_extension(filename)) || + !strcasecmp("vb",filename_extension(filename))) + channel_count=1; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,(loopEnd!=0)); + if (!vgmstream) goto fail; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + + if(gotMIH) { + // Read stuff from the MIH file + vgmstream->sample_rate = read_32bitLE(0x0C,streamFileMIH); + vgmstream->interleave_block_size = read_32bitLE(0x10,streamFileMIH); + vgmstream->num_samples=((read_32bitLE(0x10,streamFileMIH)* + (read_32bitLE(0x14,streamFileMIH)-1)*2)+ + ((read_32bitLE(0x04,streamFileMIH)>>8)*2))/16*28/2; + } else { + vgmstream->interleave_block_size = interleave; + + if(!strcasecmp("mib",filename_extension(filename))) + vgmstream->sample_rate = 44100; + + if(!strcasecmp("mi4",filename_extension(filename))) + vgmstream->sample_rate = 48000; + + if(!strcasecmp("snds", filename_extension(filename))) + vgmstream->sample_rate = 48000; + + if(!strcasecmp("xag",filename_extension(filename))) + vgmstream->sample_rate = 44100; + + if (!strcasecmp("cvs", filename_extension(filename)) || + !strcasecmp("vb",filename_extension(filename))) + vgmstream->sample_rate = 22050; + + vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28); + } + + if(loopEnd!=0) { + if(vgmstream->channels==1) { + vgmstream->loop_start_sample = loopStart/16*18; //todo 18 instead of 28 probably a bug + vgmstream->loop_end_sample = loopEnd/16*28; + } else { + vgmstream->loop_start_sample = ((((loopStart/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*14*channel_count)/channel_count; + if(loopStart%vgmstream->interleave_block_size) { + vgmstream->loop_start_sample += (((loopStart%vgmstream->interleave_block_size)-1)/16*14*channel_count); + } + + if(loopEnd==fileLength) { + vgmstream->loop_end_sample=(loopEnd/16*28)/channel_count; + } else { + vgmstream->loop_end_sample = ((((loopEnd/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*14*channel_count)/channel_count; + if(loopEnd%vgmstream->interleave_block_size) { + vgmstream->loop_end_sample += (((loopEnd%vgmstream->interleave_block_size)-1)/16*14*channel_count); + } + } + } + } + + if(loopToEnd) { + // try to find if there's no empty line ... + int emptySamples=0; + + for(i=0; i<16;i++) { + mibBuffer[i]=0; //memset + } + + readOffset=fileLength-0x10; + do { + read_streamfile(testBuffer,readOffset,0x10,streamFile); + if(!memcmp(mibBuffer,testBuffer,16)) { + emptySamples+=28; + } + readOffset-=0x10; + } while(!memcmp(testBuffer,mibBuffer,16)); + + vgmstream->loop_end_sample-=(emptySamples*channel_count); + } + + vgmstream->meta_type = gotMIH ? meta_PS2_MIB_MIH : meta_PS2_MIB; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + + close_streamfile(streamFileMIH); return vgmstream; - /* clean up anything we may have opened */ fail: - if (streamFileMIH) close_streamfile(streamFileMIH); - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(streamFileMIH); + close_vgmstream(vgmstream); return NULL; } + +/* tests some PS-ADPCM frames */ +static int check_psadpcm(STREAMFILE *streamFile) { + off_t offset, max_offset; + + max_offset = get_streamfile_size(streamFile); + if (max_offset > 0x2000) + max_offset = 0x2000; + + offset = 0x00; + while (offset < max_offset) { + uint8_t predictor = (read_8bit(offset+0x00,streamFile) >> 4) & 0x0f; + uint8_t flags = read_8bit(offset+0x01,streamFile); + + if (predictor > 5 || flags > 7) + goto fail; + offset += 0x10; + } + + return 1; +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mihb.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mihb.c index 826d1d5c4..9f3118db7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mihb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mihb.c @@ -1,72 +1,53 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* MIHB (Merged MIH+MIB) */ +/* MIC/MIHB - Merged MIH+MIB [Rogue Trooper (PS2), The Sims 2 (PS2)] */ VGMSTREAM * init_vgmstream_ps2_mihb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int mib_blocks; - int loop_flag = 0; - int channel_count; + size_t data_size, frame_size, frame_last, frame_count; + int channel_count, loop_flag; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("mihb",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x40000000) + /* check extension */ + /* .mic: Rebellion Dev. games, .mihb: assumed? */ + if (!check_extensions(streamFile, "mic,mihb")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x40000000) /* header size */ goto fail; - mib_blocks = read_32bitLE(0x14,streamFile); loop_flag = 0; channel_count = read_32bitLE(0x08,streamFile); - + start_offset = 0x40; + + /* frame_size * frame_count * channels = data_size, but last frame has less usable data */ + { + /* 0x04(1): 0x20? */ + frame_last = (uint16_t)read_16bitLE(0x05,streamFile); + frame_size = read_32bitLE(0x10,streamFile); + frame_count = read_32bitLE(0x14,streamFile); + + data_size = frame_count * frame_size; + data_size -= frame_last ? (frame_size-frame_last) : 0; + data_size *= channel_count; + } + /* 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 = read_32bitLE(0x0C,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + + vgmstream->meta_type = meta_PS2_MIHB; vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = ((read_32bitLE(0x10,streamFile))*mib_blocks)*28/16; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = ((read_32bitLE(0x10,streamFile))*mib_blocks)*28/16; - } - - - if (vgmstream->channels > 1) { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); - } else { - vgmstream->layout_type = layout_none; - } - - vgmstream->meta_type = meta_PS2_MIHB; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = frame_size; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c index 197726aa8..22a184e11 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c @@ -1,47 +1,46 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* MSA (from Psyvariar -Complete Edition-) */ +/* MSA - from Psyvariar -Complete Edition- (PS2) */ VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, datasize, filesize; + off_t start_offset, data_size, file_size; int loop_flag, channel_count; - /* check extension, case insensitive */ - if (!check_extensions(streamFile, "msa")) goto fail; - - /* check header */ + /* checks */ + if (!check_extensions(streamFile, "msa")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x00000000) goto fail; loop_flag = 0; channel_count = 2; + start_offset = 0x14; + data_size = read_32bitLE(0x4,streamFile); + file_size = get_streamfile_size(streamFile); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - start_offset = 0x14; - datasize = read_32bitLE(0x4,streamFile); - filesize = get_streamfile_size(streamFile); - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = datasize*28/(16*channel_count); + if (vgmstream->sample_rate == 0) /* ex. AME.MSA */ + vgmstream->sample_rate = 44100; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);//data_size*28/(0x10*channel_count); vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x4000; vgmstream->meta_type = meta_PS2_MSA; - /* MSAs are strangely truncated, so manually calculate samples - * data after last usable block is always silence or garbage */ - if (datasize > filesize) { - off_t usable_size = filesize - start_offset; + /* MSAs are strangely truncated, so manually calculate samples. + * Data after last usable block is always silence or garbage. */ + if (data_size > file_size) { + off_t usable_size = file_size - start_offset; usable_size -= usable_size % (vgmstream->interleave_block_size*channel_count);/* block-aligned */ - vgmstream->num_samples = usable_size * 28 / (16*channel_count); + vgmstream->num_samples = ps_bytes_to_samples(usable_size, channel_count);//usable_size * 28 / (16*channel_count); } - - /* open the file for reading */ if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh.c index e502a1ee6..cb9d3a1b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh.c @@ -1,11 +1,10 @@ #include "meta.h" #include "../util.h" -/* PSH (from Dawn of Mana - Seiken Densetsu 4) */ +/* PSH (from Dawn of Mana - Seiken Densetsu 4, Kingdom Hearts Re:Chain of Memories) */ /* probably Square Vag Stream */ VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; uint8_t testBuffer[0x10]; off_t loopEnd = 0; @@ -16,8 +15,8 @@ VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE *streamFile) { int channel_count; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("psh",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile, "psh,vsv")) // vsv seems to be official extension + goto fail; /* check header */ if (read_16bitBE(0x02,streamFile) != 0x6400) @@ -65,25 +64,12 @@ VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE *streamFile) { vgmstream->meta_type = meta_PS2_PSH; /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; return vgmstream; /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_psw.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_psw.c deleted file mode 100644 index 6ac4ffa2b..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_psw.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* PSW (from Rayman Raving Rabbids) -...coefs are missing for the dsp type... */ -VGMSTREAM * init_vgmstream_ps2_psw(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("psw",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494646 && /* "RIFF" */ - read_32bitBE(0x08,streamFile) != 0x57415645 && /* "WAVE" */ - read_32bitBE(0x26,streamFile) != 0x64617461) /* "data" */ - goto fail; - - loop_flag = 0; - channel_count = read_16bitLE(0x16,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - switch ((uint16_t)read_16bitBE(0x14,streamFile)) { - case 0xFFFF: - start_offset = 0x2E; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_16bitLE(0x1C,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x2A,streamFile)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = read_32bitLE(0x2A,streamFile)*28/16/channel_count; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x6400; - vgmstream->meta_type = meta_PS2_PSW; - - break; - case 0xFEFF: - start_offset = 0x2E; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_16bitLE(0x1C,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitLE(0x2A,streamFile)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = read_32bitLE(0x2A,streamFile)*28/16/channel_count; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x12C00; - vgmstream->meta_type = meta_PS2_PSW; - - break; -default: - goto fail; -} - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c index d32b69e03..2cffb9946 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c @@ -24,7 +24,7 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { (read_32bitBE(0x00,streamFile) == 0x444E4257)) /* "DNBW" (BE) */ goto fail; - streamHeader = open_stream_ext(streamFile, "xwh"); + streamHeader = open_streamfile_by_ext(streamFile, "xwh"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_sl3.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_sl3.c index b145cc506..69fc81d38 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_sl3.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_sl3.c @@ -1,65 +1,45 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* SL3 (from Test Drive Unlimited, Transformers) */ +/* SL3 - Atari Melbourne House games [ Test Drive Unlimited (PS2), Transformers (PS2)] */ VGMSTREAM * init_vgmstream_sl3(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + int loop_flag = 0, channel_count; - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sl3",filename_extension(filename))) goto fail; - - /* check header */ + /* checks */ + /* .ms: actual extension, sl3: header id */ + if (!check_extensions(streamFile, "ms,sl3")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x534C3300) /* "SL3\0" */ goto fail; loop_flag = 0; channel_count = read_32bitLE(0x14,streamFile); - + start_offset = 0x8000; /* also at 0x24? */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x8000; - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x8000)*28/16/channel_count; + vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(streamFile)-start_offset,channel_count); if (loop_flag) { vgmstream->loop_start_sample = 0; vgmstream->loop_end_sample = read_32bitLE(0x1C,streamFile); } + vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = read_32bitLE(0x20,streamFile); vgmstream->meta_type = meta_SL3; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_snd.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_snd.c index e1be6525f..42378f97d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_snd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_snd.c @@ -1,79 +1,53 @@ #include "meta.h" #include "../util.h" -/* SND (Warriors of Might and Magic Heroes of M&M:Dragonbone Staff) */ +/* SND - Might and Magic games [Warriors of M&M (PS2), Heroes of M&M: Quest for the DragonBone Staff (PS2)] */ VGMSTREAM * init_vgmstream_ps2_snd(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; + /* checks */ + if (!check_extensions(streamFile, "snd")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x53534E44) /* "SSND" */ + goto fail; - int loop_flag; - int channel_count; + start_offset = read_32bitLE(0x04,streamFile)+0x08; + data_size = get_streamfile_size(streamFile) - start_offset; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("snd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) !=0x53534e44) goto fail; - - /* Force Loop 0->end */ - loop_flag = 1; + loop_flag = 1; /* force full Loop */ channel_count = read_16bitLE(0x0a,streamFile); - + /* 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)+8; - vgmstream->sample_rate = (uint16_t)read_16bitLE(0xe,streamFile); - - if(read_8bit(0x08,streamFile)==1) { - vgmstream->coding_type = coding_DVI_IMA_int; - } - else - vgmstream->coding_type = coding_PCM16LE; - + vgmstream->sample_rate = (uint16_t)read_16bitLE(0x0e,streamFile); vgmstream->num_samples = read_32bitLE(0x16,streamFile); + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; - vgmstream->interleave_block_size = (uint16_t)read_16bitLE(0x12,streamFile); - - if((get_streamfile_size(streamFile)-start_offset)%vgmstream->interleave_block_size) - { - /* not sure if this is right ... */ - vgmstream->layout_type = layout_interleave_shortblock; - vgmstream->interleave_smallblock_size = ((get_streamfile_size(streamFile)-start_offset)%vgmstream->interleave_block_size)/vgmstream->channels; - } else { - vgmstream->layout_type = layout_interleave; - } vgmstream->meta_type = meta_PS2_SND; - if(loop_flag) { - vgmstream->loop_start_sample=0; - vgmstream->loop_end_sample=vgmstream->num_samples; - } + if (read_8bit(0x08,streamFile)==1) { + vgmstream->coding_type = coding_DVI_IMA_int; /* Warriors of M&M DragonBone */ + } + else { + vgmstream->coding_type = coding_PCM16LE; /* Heroes of M&M */ + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = (uint16_t)read_16bitLE(0x12,streamFile); + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; - return vgmstream; - - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; + close_vgmstream(vgmstream); + return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c index 0ef63ea59..2003f3795 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c @@ -46,7 +46,7 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { vgmstream->sample_rate = 48000; vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_ps2_strlr_blocked; + vgmstream->layout_type = layout_blocked_ps2_strlr; //vgmstream->interleave_block_size = read_32bitLE(0xC, streamFile); vgmstream->meta_type = meta_PS2_STRLR; @@ -60,16 +60,16 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { } /* Calc num_samples */ - ps2_strlr_block_update(start_offset, vgmstream); + block_update_ps2_strlr(start_offset, vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size * 14 / 16; - ps2_strlr_block_update(vgmstream->next_block_offset, vgmstream); + block_update_ps2_strlr(vgmstream->next_block_offset, vgmstream); } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - ps2_strlr_block_update(start_offset, vgmstream); + block_update_ps2_strlr(start_offset, vgmstream); return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c index ee9204180..beae5882e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c @@ -1,85 +1,53 @@ #include "meta.h" -#include "../util.h" - -/* SVAG - - PS2 SVAG format is an interleaved format found in many konami Games - The header start with a Svag id and have the sentence : - "ALL RIGHTS RESERVED.KONAMITYO Sound Design Dept. " - or "ALL RIGHTS RESERVED.KCE-Tokyo Sound Design Dept. " - - 2008-05-13 - Fastelbja : First version ... - Thx to HCS for his awesome work on shortblock interleave -*/ +#include "../coding/coding.h" +/* SVAG - from Konami Tokyo games [OZ (PS2), Neo Contra (PS2)]] */ VGMSTREAM * init_vgmstream_ps2_svag(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=0; - int channel_count; - int i; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("svag",filename_extension(filename))) goto fail; - - /* check SVAG Header */ - if (read_32bitBE(0x00,streamFile) != 0x53766167) + /* checks */ + if (!check_extensions(streamFile, "svag")) goto fail; - /* check loop */ + if (read_32bitBE(0x00,streamFile) != 0x53766167) /* "Svag" */ + goto fail; + + channel_count = read_16bitLE(0x0C,streamFile); /* always 2? ("S"tereo vag?) */ loop_flag = (read_32bitLE(0x14,streamFile)==1); - channel_count=read_16bitLE(0x0C,streamFile); + start_offset = 0x800; /* header repeated at 0x400 too */ + data_size = read_32bitLE(0x04,streamFile); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = read_16bitLE(0x0C,streamFile); vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - /* Compression Scheme */ - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x04,streamFile)/16*28/vgmstream->channels; - - /* Get loop point values */ + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile), vgmstream->channels); if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x18,streamFile)/16*28; - vgmstream->loop_end_sample = read_32bitLE(0x04,streamFile)/16*28/vgmstream->channels; + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x18,streamFile)*vgmstream->channels, vgmstream->channels); + vgmstream->loop_end_sample = vgmstream->num_samples; } - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); - if (channel_count > 1) { - vgmstream->interleave_smallblock_size = (read_32bitLE(0x04,streamFile)%(vgmstream->channels*vgmstream->interleave_block_size))/vgmstream->channels; - vgmstream->layout_type = layout_interleave_shortblock; - } else { - vgmstream->layout_type = layout_none; - } vgmstream->meta_type = meta_PS2_SVAG; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; - /* open the file for reading by each channel */ - { - for (i=0;i 1) - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); - else - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(0x800+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; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_tk5.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_tk5.c index bf7efc3a1..aefb4f8e2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_tk5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_tk5.c @@ -1,6 +1,5 @@ - #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" /* TK5 (Tekken 5 Streams) */ VGMSTREAM * init_vgmstream_ps2_tk5(STREAMFILE *streamFile) { @@ -65,65 +64,45 @@ fail: return NULL; } -/* TK1 (Tekken 5 Streams from Tekken (NamCollection)) */ +/* OVB - Tekken 5 Streams from Tekken (NamCollection) */ VGMSTREAM * init_vgmstream_ps2_tk1(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int loop_flag = 0; - int channel_count; + int loop_flag = 0, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("tk1",filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "ovb")) + goto fail; - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x544B3553) goto fail; loop_flag = (read_32bitLE(0x0C,streamFile)!=0); channel_count = 2; - + start_offset = 0x800; + /* NamCollection uses 44100 while Tekken 5 48000, no apparent way to tell them apart */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; vgmstream->sample_rate = 44100; + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x08,streamFile)*channel_count, channel_count); + if (vgmstream->loop_flag) { + vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile)/16*28; + vgmstream->loop_end_sample = vgmstream->loop_start_sample + ps_bytes_to_samples(read_32bitLE(0x0c,streamFile)*channel_count, channel_count); + } + vgmstream->coding_type = coding_PSX_badflags; - vgmstream->num_samples = read_32bitLE(0x08,streamFile)/16*28; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; vgmstream->meta_type = meta_PS2_TK1; - if (vgmstream->loop_flag) - { - vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile)/16*28; - vgmstream->loop_end_sample = vgmstream->loop_start_sample + (read_32bitLE(0x0C,streamFile)/16*28); - } - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c index 9af6a79e1..9d5303c8e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c @@ -17,8 +17,9 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { int channel_count = 0; int is_swag = 0; - /* check extension (.swag: Frantix PSP, .str: Ben10 Galactic Racing, .vig: MX vs. ATV Untamed PS2) */ - if ( !check_extensions(streamFile,"vag,swag,str,vig") ) + /* checks */ + /* .swag: Frantix (PSP), .str: Ben10 Galactic Racing, .vig: MX vs. ATV Untamed (PS2) .l/r: Crash Nitro Kart (PS2) */ + if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r") ) goto fail; /* check VAG Header */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vds_vdm.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vds_vdm.c index dd29f9c87..1b4006a45 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vds_vdm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vds_vdm.c @@ -1,43 +1,46 @@ #include "meta.h" +#include "../coding/coding.h" -/* VDS/VDM - from Grafitti Kingdom / Rakugaki Oukoku 2 */ +/* VDS/VDM - from Procyon Studio games [Grafitti Kingdom / Rakugaki Oukoku 2 (PS2), Tsukiyo ni Saraba (PS2)] */ VGMSTREAM * init_vgmstream_ps2_vds_vdm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count; - /* check extension, case insensitive */ + + /* checks */ if ( !check_extensions(streamFile,"vds,vdm")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x56445320 && /* "VDS " (music)*/ read_32bitBE(0x00,streamFile) != 0x56444D20) /* "VDM " (voices) */ goto fail; loop_flag = read_8bit(0x20,streamFile); channel_count = read_32bitLE(0x10,streamFile); + start_offset = 0x800; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = channel_count > 1 ? layout_interleave : layout_none; - vgmstream->meta_type = meta_PS2_VDS_VDM; - start_offset = 0x800; - vgmstream->num_samples = read_32bitLE(0x04,streamFile) * 28 / 16 / channel_count; - /* 0x08: unknown, always 10 */ + /* 0x08: unknown, always 0x10 */ vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); - vgmstream->channels = channel_count; /*0x10*/ - vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); - vgmstream->loop_start_sample = (read_32bitLE(0x18,streamFile) - start_offset) * 28 / 16 / channel_count; - vgmstream->loop_end_sample = (read_32bitLE(0x1c,streamFile) - start_offset) * 28 / 16 / channel_count; - vgmstream->loop_flag = loop_flag; /*0x20*/ - /*0x21: volume? */ - /*0x22: pan? */ - /*0x23: 02=VDS 04=VDM? */ - /* open the file for reading */ + /* when looping (or maybe when stereo) data_size at 0x04 is actually smaller than file_size, + * sometimes cutting outros with loop disabled; doesn't affect looping though */ + if (!loop_flag) + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile), channel_count); + else + vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x18,streamFile) - start_offset, channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x1c,streamFile) - start_offset, channel_count); + /* 0x21: volume?, 0x22: pan?, 0x23: 02=VDS 04=VDM? 02/05=VDM in Tsukiyo ni Saraba? */ + + vgmstream->meta_type = meta_PS2_VDS_VDM; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_xa2_rrp.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_xa2_rrp.c index 2eff2f3ad..64091d17a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_xa2_rrp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_xa2_rrp.c @@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_ps2_xa2_rrp(STREAMFILE *streamFile) { if (strcasecmp("xa2",filename_extension(filename))) goto fail; /* check header */ - if (read_32bitBE(0xC,streamFile) != 0x00000000) + if (read_32bitBE(0x50,streamFile) != 0x00000000) goto fail; loop_flag = 0; @@ -32,6 +32,9 @@ VGMSTREAM * init_vgmstream_ps2_xa2_rrp(STREAMFILE *streamFile) { vgmstream->coding_type = coding_PSX; vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)*28/16/channel_count; vgmstream->layout_type = layout_interleave; + if (channel_count > 2) + vgmstream->interleave_block_size = 0x400; + else vgmstream->interleave_block_size = 0x1000; vgmstream->meta_type = meta_PS2_XA2_RRP; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c index fc4e3cdba..d76850efc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c @@ -1,6 +1,5 @@ #include "meta.h" #include "../coding/coding.h" -#include "../util.h" /* MSF - Sony's PS3 SDK format (MultiStream File) */ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { @@ -11,10 +10,12 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { int loop_flag = 0, channel_count; - /* check extension, case insensitive (.at3: Silent Hill HD Collection, .mp3: Darkstalkers Resurrection) */ - if (!check_extensions(streamFile,"msf,at3,mp3")) goto fail; + /* checks */ + /* .msf: standard, .at3: Silent Hill HD Collection, .mp3: Darkstalkers Resurrection */ + if (!check_extensions(streamFile,"msf,at3,mp3")) + goto fail; - /* "WMSF" variation with a mini header over the MSFC header, same extension */ + /* "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; } @@ -35,7 +36,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { /* byte flags, not in MSFv1 or v2 * 0x01/02/04/08: loop marker 0/1/2/3 * 0x10: resample options (force 44/48khz) - * 0x20: VBR MP3 + * 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); @@ -57,7 +58,7 @@ 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 a specified frequency */ + /* 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 = 48000; @@ -65,43 +66,43 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream->meta_type = meta_PS3_MSF; switch (codec_id) { - case 0x0: /* PCM (Big Endian) */ - case 0x1: { /* PCM (Little Endian) */ + case 0x00: /* PCM (Big Endian) */ + case 0x01: { /* PCM (Little Endian) [Smash Cars (PS3)] */ vgmstream->coding_type = codec_id==0 ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 2; - vgmstream->num_samples = data_size/2/channel_count; + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count,16); if (loop_flag){ - vgmstream->loop_start_sample = loop_start/2/channel_count; - vgmstream->loop_end_sample = loop_end/2/channel_count; + 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); } break; } - case 0x2: { /* PCM 32 (Float) */ - goto fail; //probably unused/spec only + case 0x02: { /* PCM 32 (Float) */ + goto fail; /* probably unused/spec only */ } - case 0x3: { /* PS ADPCM */ + case 0x03: { /* PS ADPCM [Smash Cars (PS3)] */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x10; - vgmstream->num_samples = data_size*28/16/channel_count; + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); if (loop_flag) { - vgmstream->loop_start_sample = loop_start*28/16/channel_count; - vgmstream->loop_end_sample = loop_end*28/16/channel_count; + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start,channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end,channel_count); } break; } #ifdef VGM_USE_FFMPEG - case 0x4: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) */ - case 0x5: /* ATRAC3 mid (105 kbps, frame size 152) */ - case 0x6: { /* ATRAC3 high (132 kbps, frame size 192) */ + case 0x04: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) [Silent Hill HD (PS3)] */ + case 0x05: /* ATRAC3 mid (105 kbps, frame size 152) [Atelier Rorona (PS3)] */ + case 0x06: { /* ATRAC3 high (132 kbps, frame size 192) [Tekken Tag Tournament HD (PS3)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[100]; int32_t bytes, block_size, encoder_delay, joint_stereo; @@ -141,67 +142,55 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { break; } #endif -#ifdef VGM_USE_FFMPEG - case 0x7: { /* MPEG (LAME MP3 of any quality) */ - /* delegate to FFMpeg, it can parse MSF files */ - ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); +#if defined(VGM_USE_FFMPEG) && !defined(VGM_USE_MPEG) + case 0x07: { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */ + ffmpeg_codec_data *ffmpeg_data = NULL; + + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, streamFile->get_size(streamFile) ); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - /* vgmstream->num_samples = ffmpeg_data->totalSamples; */ /* duration may not be set/inaccurate */ vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; if (loop_flag) { - //todo properly apply encoder delay, which seems to vary between 1152 (1f), 528, 576 or 528+576 - int frame_size = ffmpeg_data->frameSize; vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; - vgmstream->loop_start_sample -= vgmstream->loop_start_sample==frame_size ? frame_size - : vgmstream->loop_start_sample % frame_size; vgmstream->loop_end_sample = (int64_t)loop_end * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; - vgmstream->loop_end_sample -= vgmstream->loop_end_sample==frame_size ? frame_size - : vgmstream->loop_end_sample % frame_size; + /* loops are always CBR frame beginnings */ } + /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; } #endif -#if defined(VGM_USE_MPEG) && !defined(VGM_USE_FFMPEG) - case 0x7: { /* MPEG (LAME MP3 of any quality) */ - int frame_size = 576; /* todo incorrect looping calcs */ - +#ifdef VGM_USE_MPEG + case 0x07: { /* MPEG (CBR LAME MP3) []Dengeki Bunko Fighting Climax (PS3) */ mpeg_codec_data *mpeg_data = NULL; - coding_t ct; - mpeg_data = init_mpeg(streamFile, start_offset, &ct, vgmstream->channels); + mpeg_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; - - vgmstream->coding_type = ct; vgmstream->layout_type = layout_none; + vgmstream->num_samples = mpeg_bytes_to_samples(data_size, mpeg_data); - vgmstream->num_samples -= vgmstream->num_samples % frame_size; if (loop_flag) { vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); - vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size; vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); - vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size; + /* loops are always CBR frame beginnings */ } - vgmstream->interleave_block_size = 0; + /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; } #endif - default: /* 8+: not defined */ + default: /* 0x08+: not defined */ goto fail; } - /* open the file for reading */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c index 1dd04d61d..efdb52f6e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c @@ -105,7 +105,7 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { vgmstream->xa_channel = xa_channel; vgmstream->coding_type = coding_XA; - vgmstream->layout_type = layout_xa_blocked; + vgmstream->layout_type = layout_blocked_xa; vgmstream->meta_type = meta_PSX_XA; if (is_blocked) @@ -115,7 +115,7 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - xa_block_update(start_offset,vgmstream); + block_update_xa(start_offset,vgmstream); return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 04d0cc14a..c6a5953b2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -90,7 +90,7 @@ typedef struct { int interleave; } riff_fmt_chunk; -static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int sns, int mwv) { +static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int mwv) { int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; @@ -106,15 +106,22 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); switch (fmt->codec) { + case 0x00: /* Yamaha ADPCM (raw) [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */ + if (fmt->bps != 4) goto fail; + if (fmt->block_size != 0x02*fmt->channel_count) goto fail; + fmt->coding_type = coding_AICA_int; + fmt->interleave = 0x01; + break; + case 0x01: /* PCM */ switch (fmt->bps) { case 16: fmt->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; - fmt->interleave = 2; + fmt->interleave = 0x02; break; case 8: fmt->coding_type = coding_PCM8_U_int; - fmt->interleave = 1; + fmt->interleave = 0x01; break; default: goto fail; @@ -131,12 +138,17 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->coding_type = coding_MS_IMA; break; - case 0x69: /* XBOX IMA ADPCM [Rayman Raving Rabbids 2 (PC) --maybe waa/wac/wam/wad?] */ + case 0x20: /* Yamaha ADPCM (raw) [Takuyo/Dynamix/etc DC games] */ + if (fmt->bps != 4) goto fail; + fmt->coding_type = coding_AICA; + break; + + case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */ if (fmt->bps != 4) goto fail; fmt->coding_type = coding_XBOX_IMA; break; - case 0x007A: /* MS IMA ADPCM [LA Rush (PC), Psi Ops (PC)] */ + case 0x007A: /* MS IMA ADPCM [LA Rush (PC), Psi Ops (PC)] (unofficial) */ /* 0x007A is apparently "Voxware SC3" but in .MED it's just MS-IMA (0x11) */ if (!check_extensions(streamFile,"med")) goto fail; @@ -149,21 +161,15 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk goto fail; break; - case 0x0555: /* Level-5 0x555 ADPCM */ + case 0x0555: /* Level-5 0x555 ADPCM (unofficial) */ if (!mwv) goto fail; fmt->coding_type = coding_L5_555; fmt->interleave = 0x12; break; - case 0x5050: /* Ubisoft LyN engine's DSP */ - if (!sns) goto fail; - fmt->coding_type = coding_NGC_DSP; - fmt->interleave = 0x08; - break; - #ifdef VGM_USE_VORBIS case 0x6771: /* Ogg Vorbis (mode 3+) */ - fmt->coding_type = coding_ogg_vorbis; + fmt->coding_type = coding_OGG_VORBIS; break; #else goto fail; @@ -192,6 +198,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->block_size = (bztmp & 0x3FF) * 8 + 8; //should match fmt->block_size #elif defined(VGM_USE_FFMPEG) fmt->coding_type = coding_FFmpeg; + break; #else goto fail; #endif @@ -204,12 +211,13 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk read_32bitBE(current_chunk+0x2c,streamFile) == 0x4F8C836C) { #ifdef VGM_USE_ATRAC9 fmt->coding_type = coding_ATRAC9; + break; #else goto fail; #endif } - break; + goto fail; /* unknown GUID */ default: goto fail; @@ -232,32 +240,30 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { int fact_sample_skip = 0; int loop_flag = 0; - long loop_start_ms = -1; - long loop_end_ms = -1; - off_t loop_start_offset = -1; - off_t loop_end_offset = -1; + long loop_start_ms = -1, loop_end_ms = -1; + int32_t loop_start_wsmp = -1, loop_end_wsmp = -1; + int32_t loop_start_smpl = -1, loop_end_smpl = -1; int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; int mwv = 0; /* Level-5 .mwv (Dragon Quest VIII, Rogue Galaxy) */ off_t mwv_pflt_offset = -1; off_t mwv_ctrl_offset = -1; - int sns = 0; /* Ubisoft .sns LyN engine (Red Steel 2, Just Dance 3) */ int at3 = 0; /* Sony ATRAC3 / ATRAC3plus */ int at9 = 0; /* Sony ATRAC9 */ /* check extension */ - /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android) */ - if ( check_extensions(streamFile, "wav,lwav,da,cd,med,snd") ) { + /* .lwav: to avoid hijacking .wav, .xwav: fake for Xbox games (unneded anymore) */ + /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android), + * .adx: Remember11 (PC) sfx + * .adp: Headhunter (DC) */ + if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp") ) { ; } else if ( check_extensions(streamFile, "mwv") ) { mwv = 1; } - else if ( check_extensions(streamFile, "sns") ) { - sns = 1; - } /* .rws: Climax games (Silent Hill Origins PSP, Oblivion PSP), .aud: EA Replay */ else if ( check_extensions(streamFile, "at3,rws,aud") ) { at3 = 1; @@ -282,6 +288,14 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (riff_size+0x08+0x01 == file_size) riff_size += 0x01; + /* some Xbox games do this [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */ + if (riff_size == file_size && read_16bitLE(0x14,streamFile)==0x0069) + riff_size -= 0x08; + /* 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; + + /* check for truncated RIFF */ if (file_size < riff_size+0x08) goto fail; @@ -296,35 +310,37 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (fmt.codec == 0x6771 && chunk_type == 0x64617461) /* Liar-soft again */ chunk_size += (chunk_size%2) ? 0x01 : 0x00; - if (current_chunk+8+chunk_size > file_size) goto fail; + if (current_chunk+0x08+chunk_size > file_size) goto fail; switch(chunk_type) { case 0x666d7420: /* "fmt " */ - /* only one per file */ - if (FormatChunkFound) goto fail; + if (FormatChunkFound) goto fail; /* only one per file */ FormatChunkFound = 1; if (-1 == read_fmt(0, /* big endian == false*/ streamFile, current_chunk, &fmt, - sns, mwv)) goto fail; + /* some Dreamcast/Naomi games again [Headhunter (DC), Bomber hehhe (DC)] */ + if (fmt.codec == 0x0000 && chunk_size == 0x12) + chunk_size += 0x02; break; - case 0x64617461: /* data */ - /* at most one per file */ - if (DataChunkFound) goto fail; + + case 0x64617461: /* "data" */ + if (DataChunkFound) goto fail; /* only one per file */ DataChunkFound = 1; - start_offset = current_chunk + 8; + start_offset = current_chunk + 0x08; data_size = chunk_size; break; - case 0x4C495354: /* LIST */ + + case 0x4C495354: /* "LIST" */ /* what lurks within?? */ - switch (read_32bitBE(current_chunk + 8, streamFile)) { - case 0x6164746C: /* adtl */ + switch (read_32bitBE(current_chunk+0x08, streamFile)) { + case 0x6164746C: /* "adtl" */ /* yay, atdl is its own little world */ parse_adtl(current_chunk + 8, chunk_size, streamFile, @@ -334,32 +350,44 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { break; } break; - case 0x736D706C: /* smpl */ - /* check loop count and loop info */ - if (read_32bitLE(current_chunk+0x24, streamFile)==1) { - if (read_32bitLE(current_chunk+0x2c+4, streamFile)==0) { + + case 0x736D706C: /* "smpl" (RIFFMIDISample + MIDILoop chunk) */ + /* check loop count/loop info (most common) *///todo double check values + /* 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) { + /* 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) { loop_flag = 1; - loop_start_offset = read_32bitLE(current_chunk+0x2c+8, streamFile); - loop_end_offset = read_32bitLE(current_chunk+0x2c+0xc,streamFile); + loop_start_smpl = read_32bitLE(current_chunk+0x08+0x2c, streamFile); + loop_end_smpl = read_32bitLE(current_chunk+0x08+0x30, streamFile); } } break; - case 0x70666c74: /* pflt */ - if (!mwv) break; /* ignore if not in an mwv */ - mwv_pflt_offset = current_chunk; /* predictor filters */ + case 0x77736D70: /* "wsmp" (RIFFDLSSample + DLSLoop chunk) */ + /* check loop count/info (found in some Xbox games: Halo (non-looping), Dynasty Warriors 3, Crimson Sea) */ + /* 0x00: size, 0x04: unity note, 0x06: fine tune, 0x08: gain, 0x10: loop count */ + if (chunk_size >= 0x24 + && read_32bitLE(current_chunk+0x08+0x00, streamFile) == 0x14 + && 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) { + 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 += loop_start_wsmp; + } + } break; - case 0x6374726c: /* ctrl */ - if (!mwv) break; - loop_flag = read_32bitLE(current_chunk+0x08, streamFile); - mwv_ctrl_offset = current_chunk; - break; - case 0x66616374: /* fact */ + case 0x66616374: /* "fact" */ if (chunk_size == 0x04) { /* standard, usually found with ADPCM */ fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); - } else if (sns && chunk_size == 0x10) { - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + } else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, streamFile) == 0x4C794E20) { /* "LyN " */ + goto fail; /* parsed elsewhere */ } else if ((at3 || at9) && chunk_size == 0x08) { fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_skip = read_32bitLE(current_chunk+0x0c, streamFile); @@ -367,11 +395,22 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); } - break; - case 0x4A554E4B: /* JUNK */ + + case 0x70666c74: /* "pflt" (.mwv extension) */ + if (!mwv) break; /* ignore if not in an mwv */ + mwv_pflt_offset = current_chunk; /* predictor filters */ + break; + case 0x6374726c: /* "ctrl" (.mwv extension) */ + if (!mwv) break; + loop_flag = read_32bitLE(current_chunk+0x08, streamFile); + mwv_ctrl_offset = current_chunk; + break; + + case 0x4A554E4B: /* "JUNK" */ JunkFound = 1; break; + default: /* ignorance is bliss */ break; @@ -383,7 +422,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (!FormatChunkFound || !DataChunkFound) goto fail; - //todo improve detection using fmt sizes/values as Wwise's don't match the RIFF standard + //todo improve detection using fmt sizes/values as Wwise's don't match the RIFF standard /* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice). * To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always. * As JUNK is legal (if unusual) we only reject those codecs. @@ -393,10 +432,18 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { && (fmt.coding_type==coding_MSADPCM /*|| fmt.coding_type==coding_MS_IMA*/ || fmt.coding_type==coding_XBOX_IMA)) goto fail; + /* ignore Beyond Good & Evil HD PS3 evil reuse of PCM codec */ + if (fmt.coding_type == coding_PCM16LE && + read_32bitBE(start_offset+0x00, streamFile) == 0x4D534643 && /* "MSF\43" */ + read_32bitBE(start_offset+0x34, streamFile) == 0xFFFFFFFF && /* always */ + read_32bitBE(start_offset+0x38, streamFile) == 0xFFFFFFFF && + read_32bitBE(start_offset+0x3c, streamFile) == 0xFFFFFFFF) + goto fail; + #ifdef VGM_USE_VORBIS /* special case using init_vgmstream_ogg_vorbis */ - if (fmt.coding_type == coding_ogg_vorbis) { + if (fmt.coding_type == coding_OGG_VORBIS) { return parse_riff_ogg(streamFile, start_offset, data_size); } #endif @@ -445,41 +492,26 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { break; case coding_MSADPCM: - vgmstream->num_samples = fact_sample_count ? fact_sample_count : - msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + if (fact_sample_count && fact_sample_count < vgmstream->num_samples) + vgmstream->num_samples = fact_sample_count; break; case coding_MS_IMA: - vgmstream->num_samples = fact_sample_count ? fact_sample_count : - ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + if (fact_sample_count && fact_sample_count < vgmstream->num_samples) + vgmstream->num_samples = fact_sample_count; + break; + + case coding_AICA: + case coding_AICA_int: + vgmstream->num_samples = aica_bytes_to_samples(data_size, fmt.channel_count); break; case coding_XBOX_IMA: - vgmstream->num_samples = fact_sample_count ? fact_sample_count : - xbox_ima_bytes_to_samples(data_size, fmt.channel_count); - break; - - case coding_NGC_DSP: - if (!sns) goto fail; - if (fact_sample_count <= 0) goto fail; - vgmstream->num_samples = fact_sample_count; - //vgmstream->num_samples = dsp_bytes_to_samples(data_size, fmt.channel_count); - - /* coefs */ - { - int i, ch; - static const int16_t coef[16] = { /* common codebook? */ - 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, - 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5 - }; - - for (ch = 0; ch < fmt.channel_count; ch++) { - for (i = 0; i < 16; i++) { - vgmstream->ch[ch].adpcm_coef[i] = coef[i]; - } - } - } - + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, fmt.channel_count); + if (fact_sample_count && fact_sample_count < vgmstream->num_samples) + vgmstream->num_samples = fact_sample_count; /* some (converted?) Xbox games have bigger fact_samples */ break; #ifdef VGM_USE_FFMPEG @@ -499,8 +531,8 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* RIFF loop/sample values are absolute (with skip samples), adjust */ if (loop_flag) { - loop_start_offset -= ffmpeg_data->skipSamples; - loop_end_offset -= ffmpeg_data->skipSamples; + loop_start_smpl -= (int32_t)ffmpeg_data->skipSamples; + loop_end_smpl -= (int32_t)ffmpeg_data->skipSamples; } } break; @@ -531,8 +563,8 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = fact_sample_count; /* RIFF loop/sample values are absolute (with skip samples), adjust */ if (loop_flag) { - loop_start_offset -= fact_sample_skip; - loop_end_offset -= fact_sample_skip; + loop_start_smpl -= fact_sample_skip; + loop_end_smpl -= fact_sample_skip; } break; @@ -547,6 +579,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { switch (fmt.coding_type) { case coding_MSADPCM: case coding_MS_IMA: + case coding_AICA: case coding_XBOX_IMA: #ifdef VGM_USE_FFMPEG case coding_FFmpeg: @@ -574,11 +607,16 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->loop_end_sample = (long long)loop_end_ms*fmt.sample_rate/1000; vgmstream->meta_type = meta_RIFF_WAVE_labl; } - else if (loop_start_offset >= 0) { - vgmstream->loop_start_sample = loop_start_offset; - vgmstream->loop_end_sample = loop_end_offset; + else if (loop_start_smpl >= 0) { + vgmstream->loop_start_sample = loop_start_smpl; + vgmstream->loop_end_sample = loop_end_smpl; vgmstream->meta_type = meta_RIFF_WAVE_smpl; } + else if (loop_start_wsmp >= 0) { + vgmstream->loop_start_sample = loop_start_wsmp; + vgmstream->loop_end_sample = loop_end_wsmp; + vgmstream->meta_type = meta_RIFF_WAVE_wsmp; + } else if (mwv && mwv_ctrl_offset != -1) { vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; @@ -587,9 +625,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (mwv) { vgmstream->meta_type = meta_RIFF_WAVE_MWV; } - if (sns) { - vgmstream->meta_type = meta_RIFF_WAVE_SNS; - } if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) @@ -652,7 +687,6 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { streamFile, current_chunk, &fmt, - 0, /* sns == false */ 0)) /* mwv == false */ goto fail; @@ -803,24 +837,21 @@ static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, si { VGMSTREAM *vgmstream = NULL; STREAMFILE *custom_streamFile = NULL; - char filename[PATH_LIMIT]; - vgm_vorbis_info_t inf = {0}; + ogg_vorbis_meta_info_t ovmi = {0}; riff_ogg_io_data io_data = {0}; size_t io_data_size = sizeof(riff_ogg_io_data); - inf.layout_type = layout_ogg_vorbis; - inf.meta_type = meta_RIFF_WAVE; - inf.stream_size = real_size; + ovmi.meta_type = meta_RIFF_WAVE; + ovmi.stream_size = real_size; //inf.loop_flag = 0; /* not observed */ io_data.patch_offset = patch_offset; - custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, riff_ogg_io_read); + custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, riff_ogg_io_read,NULL); if (!custom_streamFile) return NULL; - streamFile->get_name(streamFile,filename,sizeof(filename)); - vgmstream = init_vgmstream_ogg_vorbis_callbacks(custom_streamFile, filename, NULL, start_offset, &inf); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(custom_streamFile, NULL, start_offset, &ovmi); close_streamfile(custom_streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rkv.c b/Frameworks/vgmstream/vgmstream/src/meta/rkv.c index 56f7efb3d..a880c58db 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rkv.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rkv.c @@ -1,77 +1,115 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* RKV (from Legacy of Kain - Blood Omen 2) */ +/* RKV - from Legacy of Kain - Blood Omen 2 (PS2) */ VGMSTREAM * init_vgmstream_ps2_rkv(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset=0; - int loop_flag; - int channel_count; + off_t start_offset, header_offset; + size_t data_size; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("rkv",filename_extension(filename))) goto fail; - // Some RKV got info @ offset 0 - // Some other @ offset 4 - if(read_32bitLE(0,streamFile)==0) - start_offset=4; + /* checks */ + if (!check_extensions(streamFile, "rkv")) + goto fail; + if (read_32bitBE(0x24,streamFile) != 0x00) /* quick test vs GC rkv (coef position) */ + goto fail; - loop_flag = (read_32bitLE(start_offset+4,streamFile)!=0xFFFFFFFF); - channel_count = read_32bitLE(start_offset+0x0c,streamFile)+1; - - /* build the VGMSTREAM */ + /* some RKV got info at offset 0x00, some other at 0x0 4 */ + if (read_32bitLE(0x00,streamFile)==0) + header_offset = 0x04; + else + header_offset = 0x00; + + switch (read_32bitLE(header_offset+0x0c,streamFile)) { + case 0x00: channel_count = 1; break; + case 0x01: channel_count = 2; break; + default: goto fail; + } + loop_flag = (read_32bitLE(header_offset+0x04,streamFile) != 0xFFFFFFFF); + start_offset = 0x800; + data_size = get_streamfile_size(streamFile) - start_offset; + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(start_offset,streamFile); + vgmstream->sample_rate = read_32bitLE(header_offset,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + //vgmstream->num_samples = read_32bitLE(header_offset+0x08,streamFile); /* sometimes not set */ + if (loop_flag) { + vgmstream->loop_start_sample = read_32bitLE(header_offset+0x04,streamFile); + vgmstream->loop_end_sample = read_32bitLE(header_offset+0x08,streamFile); + } + + vgmstream->meta_type = meta_PS2_RKV; vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x400; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; - // sometimes sample count is not set on the header - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/16*28/channel_count; - - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(start_offset+4,streamFile); - vgmstream->loop_end_sample = read_32bitLE(start_offset+8,streamFile); - } - - start_offset = 0x800; - - if((get_streamfile_size(streamFile)-0x800)%0x400) - { - vgmstream->layout_type = layout_interleave_shortblock; - vgmstream->interleave_smallblock_size=((get_streamfile_size(streamFile)-0x800)%0x400)/channel_count; - } else { - vgmstream->layout_type = layout_interleave; - } - - vgmstream->interleave_block_size = 0x400; - vgmstream->meta_type = meta_PS2_RKV; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } + 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; } +/* RKV - from Legacy of Kain - Blood Omen 2 (GC) */ +VGMSTREAM * init_vgmstream_ngc_rkv(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + /* checks */ + /* "": empty (files have names but no extensions), .rkv: container bigfile extension, .bo2: fake extension */ + if (!check_extensions(streamFile, ",rkv,bo2")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x00) + goto fail; + if (read_32bitBE(0x24,streamFile) == 0x00) /* quick test vs GC rkv (coef position) */ + goto fail; + + switch (read_32bitBE(0x10,streamFile)) { + case 0x00: channel_count = 1; break; + case 0x01: channel_count = 2; break; + default: goto fail; + } + loop_flag = (read_32bitBE(0x08,streamFile) != 0xFFFFFFFF); + start_offset = 0x800; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitBE(0x04,streamFile); + vgmstream->num_samples = read_32bitBE(0x0C,streamFile); + if (loop_flag) { + vgmstream->loop_start_sample = read_32bitBE(0x08,streamFile); + vgmstream->loop_end_sample = read_32bitBE(0x0C,streamFile); + } + + vgmstream->meta_type = meta_NGC_RKV; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x400; + + dsp_read_coefs_be(vgmstream,streamFile,0x24,0x2e); + /* hist at 0x44/0x72? */ + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rs03.c b/Frameworks/vgmstream/vgmstream/src/meta/rs03.c index 03307961e..6244e2eaa 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rs03.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rs03.c @@ -1,81 +1,54 @@ #include "meta.h" #include "../coding/coding.h" -#include "../util.h" - -/* .dsp w/ RS03 header - from Metroid Prime 2 */ +/* RS03 - from Metroid Prime 2 (GC) */ VGMSTREAM * init_vgmstream_rs03(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - int channel_count; - int loop_flag; off_t start_offset; - int i; + size_t data_size; + int channel_count, loop_flag; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("dsp",filename_extension(filename))) goto fail; - /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)!=0x52530003) /* "RS03" */ + /* checks */ + if (!check_extensions(streamFile, "dsp")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x52530003) /* "RS03" */ goto fail; - channel_count = read_32bitBE(4,streamFile); + channel_count = read_32bitBE(0x04,streamFile); if (channel_count != 1 && channel_count != 2) goto fail; - - /* build the VGMSTREAM */ - loop_flag = read_16bitBE(0x14,streamFile); + start_offset = 0x60; + data_size = (get_streamfile_size(streamFile) - start_offset); + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitBE(8,streamFile); vgmstream->sample_rate = read_32bitBE(0xc,streamFile); - + vgmstream->num_samples = read_32bitBE(8,streamFile); if (loop_flag) { vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile)/8*14; vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile)/8*14; } - start_offset = 0x60; - - vgmstream->coding_type = coding_NGC_DSP; - if (channel_count == 2) { - vgmstream->layout_type = layout_interleave_shortblock; - vgmstream->interleave_block_size = 0x8f00; - vgmstream->interleave_smallblock_size = (((get_streamfile_size(streamFile)-start_offset)%(0x8f00*2))/2+7)/8*8; - } else - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_DSP_RS03; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x8f00; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = ((data_size % (vgmstream->interleave_block_size*vgmstream->channels))/2+7)/8*8; - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x20+i*2,streamFile); - if (channel_count==2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i]=read_16bitBE(0x40+i*2,streamFile); - } + dsp_read_coefs_be(vgmstream,streamFile,0x20,0x20); - /* open the file for reading by each channel */ - { - int i; - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x8f00); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - start_offset+0x8f00*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; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c index ca1beed1c..c4af75e60 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c @@ -903,12 +903,11 @@ fail: /* RSD6OGG */ VGMSTREAM * init_vgmstream_rsd6oogv(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS - char filename[PATH_LIMIT]; off_t start_offset; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("rsd",filename_extension(filename))) goto fail; + /* check extension */ + if (!check_extensions(streamFile, "rsd")) + goto fail; /* check header */ if (read_32bitBE(0x0,streamFile) != 0x52534436) /* RSD6 */ @@ -917,15 +916,13 @@ VGMSTREAM * init_vgmstream_rsd6oogv(STREAMFILE *streamFile) { goto fail; { - vgm_vorbis_info_t inf; + ogg_vorbis_meta_info_t ovmi = {0}; VGMSTREAM * result = NULL; - memset(&inf, 0, sizeof(inf)); - inf.layout_type = layout_ogg_vorbis; - inf.meta_type = meta_RSD6OOGV; + ovmi.meta_type = meta_RSD6OOGV; start_offset = 0x800; - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + result = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); if (result != NULL) { return result; @@ -938,7 +935,7 @@ fail: return NULL; } -/* RSD6XADP - from Crash Tag Team Racing (Xbox) */ +/* RSD6XADP - from Crash Tag Team Racing (Xbox), Scarface (Xbox) */ VGMSTREAM * init_vgmstream_rsd6xadp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; @@ -966,9 +963,10 @@ VGMSTREAM * init_vgmstream_rsd6xadp(STREAMFILE *streamFile) { vgmstream->sample_rate = read_32bitLE(0x10,streamFile); vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_RSD6XADP; + vgmstream->coding_type = (channel_count > 2) ? coding_XBOX_IMA_mch : coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; @@ -1081,3 +1079,131 @@ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; } + +/* RSD6AT3+ [Crash of the Titans (PSP)] */ +VGMSTREAM * init_vgmstream_rsd6at3p(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* check extension, case insensitive */ + if (!check_extensions(streamFile,"rsd")) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,streamFile) != 0x52534436) /* "RSD6" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x4154332B) /* "AT3+" */ + goto fail; + + loop_flag = 0; + channel_count = read_32bitLE(0x08, streamFile); + start_offset = 0x800; + data_size = get_streamfile_size(streamFile) - start_offset; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_RSD6AT3P; + vgmstream->sample_rate = read_32bitLE(0x10, streamFile); + +#ifdef VGM_USE_FFMPEG + { + ffmpeg_codec_data *ffmpeg_data = NULL; + + /* full RIFF header at start_offset */ + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,data_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + if (channel_count != ffmpeg_data->channels) goto fail; + + vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact samples */ + + /* manually read skip_samples if FFmpeg didn't do it */ + if (ffmpeg_data->skipSamples <= 0) { + off_t chunk_offset; + size_t chunk_size, fact_skip_samples = 0; + if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */ + goto fail; + if (chunk_size == 0x08) { + fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile); + } else if (chunk_size == 0xc) { + fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile); + } + ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples); + } + } +#else + goto fail; +#endif + + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* RSD6WMA [Scarface (Xbox)] */ +VGMSTREAM * init_vgmstream_rsd6wma(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile,"rsd")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x52534436) /* "RSD6" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x574D4120) /* "WMA " */ + goto fail; + + loop_flag = 0; + channel_count = read_32bitLE(0x08, streamFile); + start_offset = 0x800; + data_size = get_streamfile_size(streamFile) - start_offset; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_RSD6WMA; + //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile) / channel_count / 2; /* may be PCM data size, but not exact */ + vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, streamFile); + +#ifdef VGM_USE_FFMPEG + { + ffmpeg_codec_data *ffmpeg_data = NULL; + + /* mini header + WMA header at start_offset */ + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset+0x08,data_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */ + } +#else + goto fail; +#endif + + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rws.c b/Frameworks/vgmstream/vgmstream/src/meta/rws.c index 0f4796f1d..c40898752 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rws.c @@ -155,7 +155,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); - vgmstream->layout_type = layout_rws_blocked; + vgmstream->layout_type = layout_blocked_rws; vgmstream->current_block_size = block_size / vgmstream->channels; vgmstream->full_block_size = block_size_total; @@ -207,7 +207,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - rws_block_update(start_offset, vgmstream); /* block init */ + block_update_rws(start_offset, vgmstream); /* block init */ return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sab.c b/Frameworks/vgmstream/vgmstream/src/meta/sab.c index 6ce952b1c..3fce2b609 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sab.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sab.c @@ -109,7 +109,7 @@ static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int targ size_t name_size = 0; off_t name_offset = 0x10; - streamInfo = open_stream_ext(streamFile, "sob"); + streamInfo = open_streamfile_by_ext(streamFile, "sob"); if (!streamInfo) goto end; if (read_32bitBE(0x00,streamInfo) != 0x43544632) /* "CTF2" */ goto end; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index 33c04628c..e37444632 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -24,7 +24,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* SGB+SGH: use SGH as header; otherwise use the current file as header */ if (is_sgb) { - streamHeader = open_stream_ext(streamFile, "sgh"); + streamHeader = open_streamfile_by_ext(streamFile, "sgh"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/smc_smh.c b/Frameworks/vgmstream/vgmstream/src/meta/smc_smh.c new file mode 100644 index 000000000..9f65e45ff --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/smc_smh.c @@ -0,0 +1,65 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* SMC+SMH - from Wangan Midnight 1/R (System246) */ +VGMSTREAM * init_vgmstream_smc_smh(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + off_t start_offset, header_offset = 0; + size_t stream_size; + int loop_flag = 0, channel_count, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "smc")) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile, "smh"); + if (!streamHeader) goto fail; + + + total_subsongs = read_32bitLE(0x00,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + if (total_subsongs*0x10 + 0x10 != get_streamfile_size(streamHeader)) + goto fail; + + header_offset = 0x10 + (target_subsong-1)*0x10; + + start_offset = read_32bitLE(header_offset+0x00, streamHeader); + stream_size = read_32bitLE(header_offset+0x04, streamHeader); + sample_rate = read_32bitLE(header_offset+0x08, streamHeader); + /* 0x0c(2): always 0x10, frame size? */ + channel_count = read_16bitLE(header_offset+0x0e, streamHeader); + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->meta_type = meta_SMC_SMH; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x04, streamHeader); + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + close_streamfile(streamHeader); + return vgmstream; + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/smv.c b/Frameworks/vgmstream/vgmstream/src/meta/smv.c new file mode 100644 index 000000000..6691a0846 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/smv.c @@ -0,0 +1,50 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .SMV - from Cho Aniki Zero (PSP) */ +VGMSTREAM * init_vgmstream_smv(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + size_t channel_size, loop_start; + + + /* check extension */ + if (!check_extensions(streamFile, "smv")) + goto fail; + + channel_size = read_32bitLE(0x00,streamFile); + /* 0x08: number of full interleave blocks */ + channel_count = read_16bitLE(0x0a,streamFile); + loop_start = read_32bitLE(0x18,streamFile); + loop_flag = (loop_start != -1); + start_offset = 0x800; + + if (channel_size * channel_count + start_offset != get_streamfile_size(streamFile)) + goto fail; + + channel_size -= 0x10; /* last value has SPU end frame without flag 0x7 as it should */ + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x10, streamFile); + vgmstream->num_samples = ps_bytes_to_samples(channel_size*channel_count, channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start*channel_count, channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_SMV; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x04, streamFile); + vgmstream->interleave_last_block_size = read_32bitLE(0x0c, streamFile); + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c index 5ac38d25f..6eca13000 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c @@ -44,9 +44,6 @@ VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) { goto fail; } - //VGM_LOG(vgmstream->num_samples != num_samples, - // "SPS: sps num_samples and subfile num_samples don't match\n"); - //vgmstream->num_samples = num_samples; //todo adjusted for MAIATRAC3 vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */ close_streamfile(temp_streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index 7c524c06b..dd14dbf4a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -1,5 +1,6 @@ #include "meta.h" #include "../coding/coding.h" +#include "../layout/layout.h" #include "sqex_scd_streamfile.h" @@ -11,9 +12,8 @@ static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, /* SCD - Square-Enix games (FF XIII, XIV) */ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset, tables_offset, meta_offset, post_meta_offset, name_offset = 0; - int32_t stream_size, subheader_size, loop_start, loop_end; + off_t start_offset, tables_offset, meta_offset, extradata_offset, name_offset = 0; + int32_t stream_size, extradata_size, loop_start, loop_end; int loop_flag = 0, channel_count, codec, sample_rate; int version, target_entry, aux_chunk_count; @@ -26,7 +26,6 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* check extension, case insensitive */ if ( !check_extensions(streamFile, "scd") ) goto fail; - streamFile->get_name(streamFile,filename,sizeof(filename)); /** main header **/ if (read_32bitBE(0x00,streamFile) != 0x53454442 && /* "SEDB" */ @@ -113,13 +112,13 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { loop_start = read_32bit(meta_offset+0x10,streamFile); loop_end = read_32bit(meta_offset+0x14,streamFile); - subheader_size = read_32bit(meta_offset+0x18,streamFile); + extradata_size = read_32bit(meta_offset+0x18,streamFile); aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile); /* 0x01e(2): unknown, seen in some FF XIV sfx (MSADPCM) */ loop_flag = (loop_end > 0); - post_meta_offset = meta_offset + 0x20; - start_offset = post_meta_offset + subheader_size; + extradata_offset = meta_offset + 0x20; + start_offset = extradata_offset + extradata_size; /* only "MARK" chunk is known (some FF XIV PS3 have "STBL" but it's not counted) */ if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx have 0x01000000 */ @@ -128,8 +127,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { } /* skips aux chunks, sometimes needed (Lightning Returns X360, FF XIV PC) */ - if (aux_chunk_count && read_32bitBE(post_meta_offset, streamFile) == 0x4D41524B) { /* "MARK" */ - post_meta_offset += read_32bit(post_meta_offset+0x04, streamFile); + if (aux_chunk_count && read_32bitBE(extradata_offset, streamFile) == 0x4D41524B) { /* "MARK" */ + extradata_offset += read_32bit(extradata_offset+0x04, streamFile); } /* find name if possible */ @@ -151,41 +150,40 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { if (codec == 0x06) { VGMSTREAM *ogg_vgmstream; uint8_t ogg_version, ogg_byte; - vgm_vorbis_info_t inf = {0}; + ogg_vorbis_meta_info_t ovmi = {0}; - inf.layout_type = layout_ogg_vorbis; - inf.meta_type = meta_SQEX_SCD; - inf.total_subsongs = total_subsongs; + ovmi.meta_type = meta_SQEX_SCD; + ovmi.total_subsongs = total_subsongs; /* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */ - ogg_version = read_8bit(post_meta_offset + 0x00, streamFile); + ogg_version = read_8bit(extradata_offset + 0x00, streamFile); /* 0x01(1): 0x20 in v2/3, this ogg miniheader size? */ - ogg_byte = read_8bit(post_meta_offset + 0x02, streamFile); + ogg_byte = read_8bit(extradata_offset + 0x02, streamFile); /* 0x03(1): ? in v3 */ if (ogg_version == 0) { /* 0x10? header, then custom Vorbis header before regular Ogg (FF XIV PC v1) */ - inf.stream_size = stream_size; + ovmi.stream_size = stream_size; } else { /* 0x20 header, then seek table */ - size_t seek_table_size = read_32bit(post_meta_offset+0x10, streamFile); - size_t vorb_header_size = read_32bit(post_meta_offset+0x14, streamFile); + size_t seek_table_size = read_32bit(extradata_offset+0x10, streamFile); + size_t vorb_header_size = read_32bit(extradata_offset+0x14, streamFile); /* 0x18(4): ? (can be 0) */ - if ((post_meta_offset-meta_offset) + seek_table_size + vorb_header_size != subheader_size) + if ((extradata_offset-meta_offset) + seek_table_size + vorb_header_size != extradata_size) goto fail; - inf.stream_size = vorb_header_size + stream_size; - start_offset = post_meta_offset + 0x20 + seek_table_size; /* subheader_size skips vorb_header */ + ovmi.stream_size = vorb_header_size + stream_size; + start_offset = extradata_offset + 0x20 + seek_table_size; /* extradata_size skips vorb_header */ if (ogg_version == 2) { /* header is XOR'ed using byte (FF XIV PC) */ - inf.decryption_callback = scd_ogg_v2_decryption_callback; - inf.scd_xor = ogg_byte; - inf.scd_xor_length = vorb_header_size; + ovmi.decryption_callback = scd_ogg_v2_decryption_callback; + ovmi.scd_xor = ogg_byte; + ovmi.scd_xor_length = vorb_header_size; } else if (ogg_version == 3) { /* file is XOR'ed using table (FF XIV Heavensward PC) */ - inf.decryption_callback = scd_ogg_v3_decryption_callback; - inf.scd_xor = stream_size & 0xFF; /* ogg_byte not used? */ - inf.scd_xor_length = vorb_header_size + stream_size; + ovmi.decryption_callback = scd_ogg_v3_decryption_callback; + ovmi.scd_xor = stream_size & 0xFF; /* ogg_byte not used? */ + ovmi.scd_xor_length = vorb_header_size + stream_size; } else { VGM_LOG("SCD: unknown ogg_version 0x%x\n", ogg_version); @@ -193,7 +191,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { } /* actual Ogg init */ - ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); if (ogg_vgmstream && name_offset) read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile); return ogg_vgmstream; @@ -270,8 +268,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { case 0x0C: /* MS ADPCM [Final Fantasy XIV (PC) sfx] */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x0c,streamFile); - /* in post_meta_offset is a WAVEFORMATEX (including coefs and all) */ + vgmstream->interleave_block_size = read_16bit(extradata_offset+0x0c,streamFile); + /* in extradata_offset is a WAVEFORMATEX (including coefs and all) */ vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); if (loop_flag) { @@ -282,61 +280,54 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { case 0x0A: /* DSP ADPCM [Dragon Quest X (Wii)] */ case 0x15: { /* DSP ADPCM [Dragon Quest X (Wii U)] (no apparent differences except higher sample rate) */ - STREAMFILE * file; - int i; const off_t interleave_size = 0x800; const off_t stride_size = interleave_size * channel_count; + int i; size_t total_size; - scd_int_codec_data * data = NULL; - + layered_layout_data * data = NULL; + /* interleaved DSPs including the header (so the first 0x800 is 0x60 header + 0x740 data) + * so interleave layout can't used; we'll setup de-interleaving streamfiles as layers/channels instead */ + //todo this could be simplified using a block layout or adding interleave_first_block vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_scd_int; + vgmstream->layout_type = layout_layered; - /* a normal DSP header... */ - total_size = (read_32bitBE(start_offset+0x04,streamFile)+1)/2; - vgmstream->num_samples = read_32bitBE(start_offset+0x00,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end+1; - } + /* read from the first DSP header and verify other channel headers */ + { + total_size = (read_32bitBE(start_offset+0x04,streamFile)+1)/2; /* rounded nibbles / 2 */ + vgmstream->num_samples = read_32bitBE(start_offset+0x00,streamFile); + if (loop_flag) { + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end+1; + } - /* verify other channel headers */ - for (i = 1; i < channel_count; i++) { - if (read_32bitBE(start_offset+interleave_size*i+0,streamFile) != vgmstream->num_samples || - (read_32bitBE(start_offset+4,streamFile)+1)/2 != total_size) { - goto fail; + for (i = 1; i < channel_count; i++) { + if ((read_32bitBE(start_offset+4,streamFile)+1)/2 != total_size || + read_32bitBE(start_offset+interleave_size*i+0x00,streamFile) != vgmstream->num_samples) { + goto fail; + } } } - /* the primary streamfile we'll be using */ - file = streamFile->open(streamFile,filename,stride_size); - if (!file) goto fail; + /* init layout */ + data = init_layout_layered(channel_count); + if (!data) goto fail; + vgmstream->layout_data = data; - vgmstream->ch[0].streamfile = file; + /* open each layer subfile */ + for (i = 0; i < channel_count; i++) { + STREAMFILE* temp_streamFile = setup_scd_dsp_streamfile(streamFile, start_offset+interleave_size*i, interleave_size, stride_size, total_size); + if (!temp_streamFile) goto fail; - data = malloc(sizeof(scd_int_codec_data)); - data->substream_count = channel_count; - data->substreams = calloc(channel_count, sizeof(VGMSTREAM *)); - data->intfiles = calloc(channel_count, sizeof(STREAMFILE *)); - - vgmstream->codec_data = data; - - for (i=0;isubstreams[i] = init_vgmstream_ngc_dsp_std(intfile); - data->intfiles[i] = intfile; - if (!data->substreams[i]) goto fail; - - /* TODO: only handles mono substreams, though that's all we have with DSP */ - /* save start things so we can restart for seeking/looping */ - memcpy(data->substreams[i]->start_ch,data->substreams[i]->ch,sizeof(VGMSTREAMCHANNEL)*1); - memcpy(data->substreams[i]->start_vgmstream,data->substreams[i],sizeof(VGMSTREAM)); + data->layers[i] = init_vgmstream_ngc_dsp_std(temp_streamFile); + close_streamfile(temp_streamFile); + if (!data->layers[i]) goto fail; } + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + break; } @@ -346,8 +337,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { uint8_t buf[200]; int32_t bytes; - /* post_meta_offset+0x00: fmt0x166 header (BE), post_meta_offset+0x34: seek table */ - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, post_meta_offset,0x34, stream_size, streamFile, 1); + /* 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); @@ -365,7 +356,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { case 0x0E: { /* ATRAC3/ATRAC3plus [Lord of Arcana (PSP), Final Fantasy Type-0] */ ffmpeg_codec_data *ffmpeg_data = NULL; - /* full RIFF header at start_offset/post_meta_offset (same) */ + /* full RIFF header at start_offset/extradata_offset (same) */ ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; @@ -401,17 +392,17 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* post header has various typical ATRAC9 values */ cfg.channels = vgmstream->channels; - cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile); - cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile); + cfg.config_data = read_32bit(extradata_offset+0x0c,streamFile); + cfg.encoder_delay = read_32bit(extradata_offset+0x18,streamFile); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; - vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */ - vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start - vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end + vgmstream->num_samples = read_32bit(extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */ + vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start + vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd_streamfile.h index 2d9f67c76..2d54bc831 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd_streamfile.h @@ -2,72 +2,30 @@ #define _SQEX_SCD_STREAMFILE_H_ #include "../streamfile.h" -/* special streamfile type to handle deinterleaving of complete files (based heavily on AIXSTREAMFILE */ -typedef struct _SCDINTSTREAMFILE { - STREAMFILE sf; - STREAMFILE *real_file; - const char * filename; - off_t start_physical_offset; - off_t current_logical_offset; - off_t interleave_block_size; - off_t stride_size; - size_t total_size; -} SCDINTSTREAMFILE; +typedef struct { + off_t start_physical_offset; /* interleaved data start, for this substream */ + size_t interleave_block_size; /* max size that can be read before encountering other substreams */ + size_t stride_size; /* step size between interleave blocks (interleave*channels) */ + size_t total_size; /* final size of the deinterleaved substream */ +} scd_dsp_io_data; -/*static*/ STREAMFILE *open_scdint_with_STREAMFILE(STREAMFILE *file, const char * filename, off_t start_offset, off_t interleave_block_size, off_t stride_size, size_t total_size); - - -static STREAMFILE *open_scdint_impl(SCDINTSTREAMFILE *streamfile,const char * const filename,size_t buffersize) { - SCDINTSTREAMFILE *newfile; - - if (strcmp(filename, streamfile->filename)) - return NULL; - - newfile = malloc(sizeof(SCDINTSTREAMFILE)); - if (!newfile) - return NULL; - - memcpy(newfile,streamfile,sizeof(SCDINTSTREAMFILE)); - return &newfile->sf; -} - -static void close_scdint(SCDINTSTREAMFILE *streamfile) { - free(streamfile); - return; -} - -static size_t get_size_scdint(SCDINTSTREAMFILE *streamfile) { - return streamfile->total_size; -} - -static size_t get_offset_scdint(SCDINTSTREAMFILE *streamfile) { - return streamfile->current_logical_offset; -} - -static void get_name_scdint(SCDINTSTREAMFILE *streamfile, char *buffer, size_t length) { - strncpy(buffer,streamfile->filename,length); - buffer[length-1]='\0'; -} - -static size_t read_scdint(SCDINTSTREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { - size_t sz = 0; +/* Handles deinterleaving of complete files, skipping portions or other substreams. */ +static size_t scd_dsp_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, scd_dsp_io_data* data) { + size_t total_read = 0; while (length > 0) { - off_t to_read; - off_t length_available; + size_t to_read; + size_t length_available; off_t block_num; off_t intrablock_offset; off_t physical_offset; - - block_num = offset / streamfile->interleave_block_size; - intrablock_offset = offset % streamfile->interleave_block_size; - streamfile->current_logical_offset = offset; - physical_offset = streamfile->start_physical_offset + block_num * streamfile->stride_size + intrablock_offset; - - length_available = streamfile->interleave_block_size - intrablock_offset; + block_num = offset / data->interleave_block_size; + intrablock_offset = offset % data->interleave_block_size; + physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset; + length_available = data->interleave_block_size - intrablock_offset; if (length < length_available) { to_read = length; @@ -79,16 +37,11 @@ static size_t read_scdint(SCDINTSTREAMFILE *streamfile, uint8_t *dest, off_t off if (to_read > 0) { size_t bytes_read; - bytes_read = read_streamfile(dest, - physical_offset, - to_read, streamfile->real_file); - - sz += bytes_read; - - streamfile->current_logical_offset = offset + bytes_read; + bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile); + total_read += bytes_read; if (bytes_read != to_read) { - return sz; /* an error which we will not attempt to handle here */ + return total_read; } dest += bytes_read; @@ -97,38 +50,43 @@ static size_t read_scdint(SCDINTSTREAMFILE *streamfile, uint8_t *dest, off_t off } } - return sz; + return total_read; } -/* start_offset is for *this* interleaved stream */ -/*static*/ STREAMFILE *open_scdint_with_STREAMFILE(STREAMFILE *file, const char * filename, off_t start_offset, off_t interleave_block_size, off_t stride_size, size_t total_size) { - SCDINTSTREAMFILE * scd = NULL; +static size_t scd_dsp_io_size(STREAMFILE *streamfile, scd_dsp_io_data* data) { + return data->total_size; +} - /* _scdint funcs can't handle this case */ - if (start_offset + total_size > file->get_size(file)) - return NULL; - scd = malloc(sizeof(SCDINTSTREAMFILE)); - if (!scd) - return NULL; +static STREAMFILE* setup_scd_dsp_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t total_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + scd_dsp_io_data io_data = {0}; + size_t io_data_size = sizeof(scd_dsp_io_data); - scd->sf.read = (void*)read_scdint; - scd->sf.get_size = (void*)get_size_scdint; - scd->sf.get_offset = (void*)get_offset_scdint; - scd->sf.get_name = (void*)get_name_scdint; - scd->sf.get_realname = (void*)get_name_scdint; - scd->sf.open = (void*)open_scdint_impl; - scd->sf.close = (void*)close_scdint; + io_data.start_physical_offset = start_offset; + io_data.interleave_block_size = interleave_block_size; + io_data.stride_size = stride_size; + io_data.total_size = total_size; - scd->real_file = file; - scd->filename = filename; - scd->start_physical_offset = start_offset; - scd->current_logical_offset = 0; - scd->interleave_block_size = interleave_block_size; - scd->stride_size = stride_size; - scd->total_size = total_size; - return &scd->sf; + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, scd_dsp_io_read,scd_dsp_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"dsp"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; } #endif /* _SCD_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c index cd2cbf5cc..be1cafc53 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c @@ -7,8 +7,8 @@ static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfi /* SABF/MABF - Square Enix's "sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, tables_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0; - size_t stream_size, descriptor_size, subheader_size, special_size; //, name_size = 0; + off_t start_offset, tables_offset, mtrl_offset, meta_offset, extradata_offset; //, info_offset, name_offset = 0; + size_t stream_size, descriptor_size, extradata_size, special_size; //, name_size = 0; int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end; @@ -118,12 +118,12 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { loop_start = read_32bit(meta_offset+0x0c,streamFile); /* in samples but usually ignored */ loop_end = read_32bit(meta_offset+0x10,streamFile); - subheader_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header */ + extradata_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header, can be 0 */ stream_size = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */ special_size = read_32bit(meta_offset+0x1c,streamFile); loop_flag = (loop_end > 0); - post_meta_offset = meta_offset + 0x20; + extradata_offset = meta_offset + 0x20; /** info section (get stream name) **/ @@ -155,38 +155,47 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { switch(codec) { + case 0x01: { /* PCM [Chrono Trigger sfx (PC)] */ + start_offset = extradata_offset + extradata_size; + + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; + } + case 0x02: { /* MSADPCM [Dragon Quest Builders (Vita) sfx] */ - start_offset = post_meta_offset + subheader_size; + start_offset = extradata_offset + extradata_size; /* 0x00 (2): null?, 0x02(2): entry size? */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x04,streamFile); + vgmstream->interleave_block_size = read_16bit(extradata_offset+0x04,streamFile); /* much like AKBs, there are slightly different loop values here, probably more accurate * (if no loop, loop_end doubles as num_samples) */ vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); - vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x08, streamFile); //loop_start - vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x0c, streamFile); //loop_end + vgmstream->loop_start_sample = read_32bit(extradata_offset+0x08, streamFile); //loop_start + vgmstream->loop_end_sample = read_32bit(extradata_offset+0x0c, streamFile); //loop_end break; } #ifdef VGM_USE_VORBIS case 0x03: { /* OGG [Final Fantasy XV Benchmark sfx (PC)] */ VGMSTREAM *ogg_vgmstream = NULL; - vgm_vorbis_info_t inf = {0}; - off_t subfile_offset = post_meta_offset + subheader_size; - char filename[PATH_LIMIT]; + ogg_vorbis_meta_info_t ovmi = {0}; + off_t subfile_offset = extradata_offset + extradata_size; - streamFile->get_name(streamFile,filename,sizeof(filename)); - - inf.layout_type = layout_ogg_vorbis; - inf.meta_type = vgmstream->meta_type; - inf.total_subsongs = total_subsongs; - inf.stream_size = stream_size; + ovmi.meta_type = vgmstream->meta_type; + ovmi.total_subsongs = total_subsongs; + ovmi.stream_size = stream_size; /* post header has some kind of repeated values, config/table? */ - ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, subfile_offset, &inf); + ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, subfile_offset, &ovmi); if (ogg_vgmstream) { ogg_vgmstream->num_streams = vgmstream->num_streams; ogg_vgmstream->stream_size = vgmstream->stream_size; @@ -206,21 +215,21 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */ atrac9_config cfg = {0}; - start_offset = post_meta_offset + subheader_size; + start_offset = extradata_offset + extradata_size; /* post header has various typical ATRAC9 values */ cfg.channels = vgmstream->channels; - cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile); - cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile); + cfg.config_data = read_32bit(extradata_offset+0x0c,streamFile); + cfg.encoder_delay = read_32bit(extradata_offset+0x18,streamFile); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; - vgmstream->sample_rate = read_32bit(post_meta_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */ - vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */ - vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start - vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end + vgmstream->sample_rate = read_32bit(extradata_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */ + vgmstream->num_samples = read_32bit(extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */ + vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start + vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end break; } #endif @@ -230,7 +239,7 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; - start_offset = post_meta_offset + subheader_size; + start_offset = extradata_offset + extradata_size; /* post header is a proper MSF, but sample rate/loops are ignored in favor of SAB's */ mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); @@ -249,13 +258,13 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { //todo there is no easy way to use the HCA decoder; try subfile hack for now VGMSTREAM *temp_vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; - off_t subfile_offset = post_meta_offset + 0x10; - size_t subfile_size = stream_size + subheader_size - 0x10; + off_t subfile_offset = extradata_offset + 0x10; + size_t subfile_size = stream_size + extradata_size - 0x10; /* post header: values from the HCA header, in file endianness + HCA header */ size_t key_start = special_size & 0xff; - size_t header_size = read_16bit(post_meta_offset+0x02, streamFile); - int encryption = read_16bit(post_meta_offset+0x0c, streamFile); //maybe 8bit? + size_t header_size = read_16bit(extradata_offset+0x02, streamFile); + int encryption = read_16bit(extradata_offset+0x0c, streamFile); //maybe 8bit? /* encryption type 0x01 found in Final Fantasy XII TZA (PS4/PC) */ temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size, encryption, header_size, key_start); @@ -304,32 +313,33 @@ typedef struct { static size_t sead_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, sead_decryption_data* data) { /* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */ static const uint8_t encryption_key[0x100] = { - 0x3A, 0x32, 0x32, 0x32, 0x03, 0x7E, 0x12, 0xF7, 0xB2, 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x22, 0x32, // 00-0F - 0x32, 0x52, 0x16, 0x1B, 0x3C, 0xA1, 0x54, 0x7B, 0x1B, 0x97, 0xA6, 0x93, 0x1A, 0x4B, 0xAA, 0xA6, // 10-1F - 0x7A, 0x7B, 0x1B, 0x97, 0xA6, 0xF7, 0x02, 0xBB, 0xAA, 0xA6, 0xBB, 0xF7, 0x2A, 0x51, 0xBE, 0x03, // 20-2F - 0xF4, 0x2A, 0x51, 0xBE, 0x03, 0xF4, 0x2A, 0x51, 0xBE, 0x12, 0x06, 0x56, 0x27, 0x32, 0x32, 0x36, // 30-3F - 0x32, 0xB2, 0x1A, 0x3B, 0xBC, 0x91, 0xD4, 0x7B, 0x58, 0xFC, 0x0B, 0x55, 0x2A, 0x15, 0xBC, 0x40, // 40-4F - 0x92, 0x0B, 0x5B, 0x7C, 0x0A, 0x95, 0x12, 0x35, 0xB8, 0x63, 0xD2, 0x0B, 0x3B, 0xF0, 0xC7, 0x14, // 50-5F - 0x51, 0x5C, 0x94, 0x86, 0x94, 0x59, 0x5C, 0xFC, 0x1B, 0x17, 0x3A, 0x3F, 0x6B, 0x37, 0x32, 0x32, // 60-6F - 0x30, 0x32, 0x72, 0x7A, 0x13, 0xB7, 0x26, 0x60, 0x7A, 0x13, 0xB7, 0x26, 0x50, 0xBA, 0x13, 0xB4, // 70-7F - 0x2A, 0x50, 0xBA, 0x13, 0xB5, 0x2E, 0x40, 0xFA, 0x13, 0x95, 0xAE, 0x40, 0x38, 0x18, 0x9A, 0x92, // 80-8F - 0xB0, 0x38, 0x00, 0xFA, 0x12, 0xB1, 0x7E, 0x00, 0xDB, 0x96, 0xA1, 0x7C, 0x08, 0xDB, 0x9A, 0x91, // 90-9F - 0xBC, 0x08, 0xD8, 0x1A, 0x86, 0xE2, 0x70, 0x39, 0x1F, 0x86, 0xE0, 0x78, 0x7E, 0x03, 0xE7, 0x64, // A0-AF - 0x51, 0x9C, 0x8F, 0x34, 0x6F, 0x4E, 0x41, 0xFC, 0x0B, 0xD5, 0xAE, 0x41, 0xFC, 0x0B, 0xD5, 0xAE, // B0-BF - 0x41, 0xFC, 0x3B, 0x70, 0x71, 0x64, 0x33, 0x32, 0x12, 0x32, 0x32, 0x36, 0x70, 0x34, 0x2B, 0x56, // C0-CF - 0x22, 0x70, 0x3A, 0x13, 0xB7, 0x26, 0x60, 0xBA, 0x1B, 0x94, 0xAA, 0x40, 0x38, 0x00, 0xFA, 0xB2, // D0-DF - 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x12, 0x32, 0xB2, 0x32, 0x32, 0x32, 0x32, 0x75, 0xA3, 0x26, 0x7B, // E0-EF - 0x83, 0x26, 0xF9, 0x83, 0x2E, 0xFF, 0xE3, 0x16, 0x7D, 0xC0, 0x1E, 0x63, 0x21, 0x07, 0xE3, 0x01, // F0-FF + 0x3A,0x32,0x32,0x32,0x03,0x7E,0x12,0xF7,0xB2,0xE2,0xA2,0x67,0x32,0x32,0x22,0x32, // 00-0F + 0x32,0x52,0x16,0x1B,0x3C,0xA1,0x54,0x7B,0x1B,0x97,0xA6,0x93,0x1A,0x4B,0xAA,0xA6, // 10-1F + 0x7A,0x7B,0x1B,0x97,0xA6,0xF7,0x02,0xBB,0xAA,0xA6,0xBB,0xF7,0x2A,0x51,0xBE,0x03, // 20-2F + 0xF4,0x2A,0x51,0xBE,0x03,0xF4,0x2A,0x51,0xBE,0x12,0x06,0x56,0x27,0x32,0x32,0x36, // 30-3F + 0x32,0xB2,0x1A,0x3B,0xBC,0x91,0xD4,0x7B,0x58,0xFC,0x0B,0x55,0x2A,0x15,0xBC,0x40, // 40-4F + 0x92,0x0B,0x5B,0x7C,0x0A,0x95,0x12,0x35,0xB8,0x63,0xD2,0x0B,0x3B,0xF0,0xC7,0x14, // 50-5F + 0x51,0x5C,0x94,0x86,0x94,0x59,0x5C,0xFC,0x1B,0x17,0x3A,0x3F,0x6B,0x37,0x32,0x32, // 60-6F + 0x30,0x32,0x72,0x7A,0x13,0xB7,0x26,0x60,0x7A,0x13,0xB7,0x26,0x50,0xBA,0x13,0xB4, // 70-7F + 0x2A,0x50,0xBA,0x13,0xB5,0x2E,0x40,0xFA,0x13,0x95,0xAE,0x40,0x38,0x18,0x9A,0x92, // 80-8F + 0xB0,0x38,0x00,0xFA,0x12,0xB1,0x7E,0x00,0xDB,0x96,0xA1,0x7C,0x08,0xDB,0x9A,0x91, // 90-9F + 0xBC,0x08,0xD8,0x1A,0x86,0xE2,0x70,0x39,0x1F,0x86,0xE0,0x78,0x7E,0x03,0xE7,0x64, // A0-AF + 0x51,0x9C,0x8F,0x34,0x6F,0x4E,0x41,0xFC,0x0B,0xD5,0xAE,0x41,0xFC,0x0B,0xD5,0xAE, // B0-BF + 0x41,0xFC,0x3B,0x70,0x71,0x64,0x33,0x32,0x12,0x32,0x32,0x36,0x70,0x34,0x2B,0x56, // C0-CF + 0x22,0x70,0x3A,0x13,0xB7,0x26,0x60,0xBA,0x1B,0x94,0xAA,0x40,0x38,0x00,0xFA,0xB2, // D0-DF + 0xE2,0xA2,0x67,0x32,0x32,0x12,0x32,0xB2,0x32,0x32,0x32,0x32,0x75,0xA3,0x26,0x7B, // E0-EF + 0x83,0x26,0xF9,0x83,0x2E,0xFF,0xE3,0x16,0x7D,0xC0,0x1E,0x63,0x21,0x07,0xE3,0x01, // F0-FF }; size_t bytes_read; + off_t encrypted_offset = data->header_size; int i; bytes_read = streamfile->read(streamfile, dest, offset, length); /* decrypt data (xor) */ - if (offset >= data->header_size) { + if (offset >= encrypted_offset) { for (i = 0; i < bytes_read; i++) { - dest[i] ^= encryption_key[(data->key_start + (offset - data->header_size) + i) % 0x100]; + dest[i] ^= encryption_key[(data->key_start + (offset - encrypted_offset) + i) % 0x100]; } } @@ -355,7 +365,7 @@ static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfi io_data.header_size = header_size; io_data.key_start = key_start; - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sead_decryption_read); + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sead_decryption_read,NULL); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sthd.c b/Frameworks/vgmstream/vgmstream/src/meta/sthd.c new file mode 100644 index 000000000..9c83a0134 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sthd.c @@ -0,0 +1,68 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + +/* STHD - Dream Factory .stx [Kakuto Chojin (Xbox)] */ +VGMSTREAM * init_vgmstream_sthd(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "stx")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x53544844) /* "STHD" */ + goto fail; + /* first block has special values */ + if (read_32bitLE(0x04,streamFile) != 0x0800 && + read_32bitLE(0x0c,streamFile) != 0x0001 && + read_32bitLE(0x14,streamFile) != 0x0000) + goto fail; + + channel_count = read_16bitLE(0x06,streamFile); + loop_flag = read_16bitLE(0x18,streamFile) != -1; + start_offset = 0x800; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x20, streamFile); /* repeated ~8 times? */ + vgmstream->meta_type = meta_STHD; + vgmstream->coding_type = coding_XBOX_IMA_int; + vgmstream->layout_type = layout_blocked_sthd; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples manually (blocks data varies in size) */ + { + /* loop values may change to +1 in first actual block, but this works ok enough */ + int loop_start_block = (uint16_t)read_16bitLE(0x1a,streamFile); + int loop_end_block = (uint16_t)read_16bitLE(0x1c,streamFile); + int block_count = 1; /* header block = 0 */ + + vgmstream->next_block_offset = start_offset; + do { + block_update_sthd(vgmstream->next_block_offset,vgmstream); + + if (block_count == loop_start_block) + vgmstream->loop_start_sample = vgmstream->num_samples; + if (block_count == loop_end_block) + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->num_samples += xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); + block_count++; + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + } + + block_update_sthd(start_offset, vgmstream); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_snds.c b/Frameworks/vgmstream/vgmstream/src/meta/str_snds.c index b731f27cd..6a43997aa 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/str_snds.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_snds.c @@ -122,7 +122,7 @@ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) { default: goto fail; } - vgmstream->layout_type = layout_str_snds_blocked; + vgmstream->layout_type = layout_blocked_str_snds; vgmstream->meta_type = meta_STR_SNDS; /* channels and loop flag are set by allocate_vgmstream */ @@ -144,7 +144,7 @@ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) { } /* start me up */ - str_snds_block_update(0,vgmstream); + block_update_str_snds(0,vgmstream); return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c index 224dc80ac..8a8c85ac1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c @@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { if (is_dual) { if (read_32bitBE(0x00,streamFile) != 0x53584453) /* "SXDS" */ goto fail; - streamHeader = open_stream_ext(streamFile, "sxd1"); + streamHeader = open_streamfile_by_ext(streamFile, "sxd1"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c index f7984bd9b..c6ce49f0b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c @@ -210,13 +210,11 @@ fail: } /* Android/iOS Variants (Star Ocean Anamnesis (APK v1.9.2), Heaven x Inferno (iOS)) */ -VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_ta_aac_mobile_vorbis(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS off_t start_offset; - char filename[PATH_LIMIT]; int8_t codec_id; - streamFile->get_name(streamFile, filename, sizeof(filename)); /* check extension, case insensitive */ /* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */ if (!check_extensions(streamFile, "aac,laac,ace")) @@ -231,19 +229,17 @@ VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) { codec_id = read_8bit(0x104, streamFile); if (codec_id == 0xe) /* Vorbis */ { - vgm_vorbis_info_t inf; + ogg_vorbis_meta_info_t ovmi = {0}; VGMSTREAM * result = NULL; - memset(&inf, 0, sizeof(inf)); - inf.layout_type = layout_ogg_vorbis; - inf.meta_type = meta_TA_AAC_VORBIS; - inf.loop_start = read_32bitLE(0x140, streamFile); - inf.loop_end = read_32bitLE(0x144, streamFile); - inf.loop_flag = inf.loop_end > inf.loop_start; - inf.loop_end_found = inf.loop_flag; + ovmi.meta_type = meta_TA_AAC_MOBILE; + ovmi.loop_start = read_32bitLE(0x140, streamFile); + ovmi.loop_end = read_32bitLE(0x144, streamFile); + ovmi.loop_flag = ovmi.loop_end > ovmi.loop_start; + ovmi.loop_end_found = ovmi.loop_flag; start_offset = read_32bitLE(0x120, streamFile); - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + result = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); if (result != NULL) { return result; @@ -255,3 +251,65 @@ fail: #endif return NULL; } + +/* Android/iOS Variants, before they switched to Vorbis (Star Ocean Anamnesis (Android), Heaven x Inferno (iOS)) */ +VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int channel_count, loop_flag, codec; + size_t data_size; + + + /* check extension, case insensitive */ + /* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */ + if (!check_extensions(streamFile, "aac,laac,ace")) + goto fail; + + if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ + goto fail; + + if (read_32bitLE(0xf0, streamFile) != 0x57415645) /* "WAVE" */ + goto fail; + + codec = read_8bit(0x104, streamFile); + channel_count = read_8bit(0x105, streamFile); + /* 0x106: 0x01?, 0x107: 0x10? */ + data_size = read_32bitLE(0x10c, streamFile); /* usable data only, cuts last frame */ + start_offset = read_32bitLE(0x120, streamFile); + /* 0x124: full data size */ + loop_flag = (read_32bitLE(0x134, streamFile) > 0); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x108, streamFile); + vgmstream->meta_type = meta_TA_AAC_MOBILE; + + switch(codec) { + case 0x0d: + if (read_32bitLE(0x144, streamFile) != 0x40) goto fail; /* frame size */ + if (read_32bitLE(0x148, streamFile) != (0x40-0x04*channel_count)*2 / channel_count) goto fail; /* frame samples */ + if (channel_count > 2) goto fail; /* unknown data layout */ + + vgmstream->coding_type = coding_YAMAHA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = yamaha_bytes_to_samples(data_size, channel_count); + vgmstream->loop_start_sample = yamaha_bytes_to_samples(read_32bitLE(0x130, streamFile), channel_count);; + vgmstream->loop_end_sample = yamaha_bytes_to_samples(read_32bitLE(0x134, streamFile), channel_count);; + break; + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/thp.c b/Frameworks/vgmstream/vgmstream/src/meta/thp.c index 2a9c95b9d..1244ff58a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/thp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/thp.c @@ -88,10 +88,10 @@ VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile) { } vgmstream->full_block_size = read_32bitBE(0x18,streamFile); /* block size of current block, changes every time */ - thp_block_update(start_offset,vgmstream); + block_update_thp(start_offset,vgmstream); vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_thp_blocked; + vgmstream->layout_type = layout_blocked_thp; vgmstream->meta_type = meta_THP; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 82d6e560f..25a7a6bd7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -1,7 +1,6 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" -#include "../util.h" #define TXT_LINE_MAX 0x2000 @@ -82,6 +81,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding_t coding; int i, j; + + /* reject .txth as the CLI can open and decode with itself */ + if (check_extensions(streamFile, "txth")) + goto fail; + /* no need for ID or ext checks -- if a .TXTH exists all is good * (player still needs to accept the streamfile's ext, so at worst rename to .vgmstream) */ streamText = open_txth(streamFile); @@ -168,6 +172,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding = coding_DVI_IMA_int; if (coding == coding_IMA) coding = coding_IMA_int; + if (coding == coding_AICA) + coding = coding_AICA_int; } /* to avoid endless loops */ @@ -176,7 +182,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding == coding_PSX_badflags || coding == coding_IMA_int || coding == coding_DVI_IMA_int || - coding == coding_SDX2_int) ) { + coding == coding_SDX2_int || + coding == coding_AICA_int) ) { goto fail; } } else { @@ -184,7 +191,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { } /* setup adpcm */ - if (coding == coding_AICA) { + if (coding == coding_AICA || coding == coding_AICA_int) { int i; for (i=0;ichannels;i++) { vgmstream->ch[i].adpcm_step_index = 0x7f; @@ -206,7 +213,15 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; break; case coding_XBOX_IMA: - vgmstream->layout_type = layout_none; + if (txth.codec_mode == 1) { + if (!txth.interleave) goto fail; /* creates garbage */ + coding = coding_XBOX_IMA_int; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = txth.interleave; + } + else { + vgmstream->layout_type = layout_none; + } break; case coding_NGC_DTK: if (vgmstream->channels != 2) goto fail; @@ -374,14 +389,14 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { STREAMFILE * streamText; /* try "(path/)(name.ext).txth" */ - if (!get_streamfile_name(streamFile,filename,PATH_LIMIT)) goto fail; + get_streamfile_name(streamFile,filename,PATH_LIMIT); strcat(filename, ".txth"); streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamText) return streamText; /* try "(path/)(.ext).txth" */ - if (!get_streamfile_path(streamFile,filename,PATH_LIMIT)) goto fail; - if (!get_streamfile_ext(streamFile,fileext,PATH_LIMIT)) goto fail; + get_streamfile_path(streamFile,filename,PATH_LIMIT); + get_streamfile_ext(streamFile,fileext,PATH_LIMIT); strcat(filename,"."); strcat(filename, fileext); strcat(filename, ".txth"); @@ -389,14 +404,13 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { if (streamText) return streamText; /* try "(path/).txth" */ - if (!get_streamfile_path(streamFile,filename,PATH_LIMIT)) goto fail; + get_streamfile_path(streamFile,filename,PATH_LIMIT); strcat(filename, ".txth"); streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamText) return streamText; -fail: /* not found */ - return 0; + return NULL; } /* Simple text parser of "key = value" lines. diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c new file mode 100644 index 000000000..1a5176222 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -0,0 +1,357 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + + +#define TXT_LINE_MAX 0x2000 + +typedef struct { + char filename[TXT_LINE_MAX]; + int subsong; + uint32_t channel_mask; +} txtp_entry; + +typedef struct { + txtp_entry *entry; + size_t entry_count; + size_t entry_max; + + size_t loop_start_segment; + size_t loop_end_segment; +} txtp_header; + +static txtp_header* parse_txtp(STREAMFILE* streamFile); +static void clean_txtp(txtp_header* txtp); + + +/* TXTP - an artificial playlist-like format to play segmented files with config */ +VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + txtp_header* txtp = NULL; + segmented_layout_data *data = NULL; + + + /* checks */ + if (!check_extensions(streamFile, "txtp")) + goto fail; + + /* read .txtp text file to get segments */ + txtp = parse_txtp(streamFile); + if (!txtp) goto fail; + + + if (txtp->entry_count == 0) + goto fail; + + + if (txtp->entry_count == 1 && !txtp->loop_start_segment) { + /* single file */ + STREAMFILE* temp_streamFile = open_streamfile_by_filename(streamFile, txtp->entry[0].filename); + if (!temp_streamFile) goto fail; + temp_streamFile->stream_index = txtp->entry[0].subsong; + + vgmstream = init_vgmstream_from_STREAMFILE(temp_streamFile); + close_streamfile(temp_streamFile); + if (!vgmstream) goto fail; + + vgmstream->channel_mask = txtp->entry[0].channel_mask; + } + else { + /* multi file */ + int num_samples, loop_start_sample = 0, loop_end_sample = 0; + int i; + int loop_flag, channel_count; + + + /* init layout */ + data = init_layout_segmented(txtp->entry_count); + if (!data) goto fail; + + /* open each segment subfile */ + for (i = 0; i < txtp->entry_count; i++) { + STREAMFILE* temp_streamFile = open_streamfile_by_filename(streamFile, txtp->entry[i].filename); + if (!temp_streamFile) goto fail; + temp_streamFile->stream_index = txtp->entry[i].subsong; + + data->segments[i] = init_vgmstream_from_STREAMFILE(temp_streamFile); + close_streamfile(temp_streamFile); + if (!data->segments[i]) goto fail; + + data->segments[i]->channel_mask = txtp->entry[0].channel_mask; + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + /* get looping and samples */ + if (txtp->loop_start_segment && !txtp->loop_end_segment) + txtp->loop_end_segment = txtp->entry_count; + loop_flag = (txtp->loop_start_segment > 0 && txtp->loop_start_segment <= txtp->entry_count); + num_samples = 0; + for (i = 0; i < data->segment_count; i++) { + + if (loop_flag && txtp->loop_start_segment == i+1) { + loop_start_sample = num_samples; + } + + num_samples += data->segments[i]->num_samples; + + if (loop_flag && txtp->loop_end_segment == i+1) { + loop_end_sample = num_samples; + } + } + + channel_count = data->segments[0]->channels; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = data->segments[0]->sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + + vgmstream->meta_type = meta_TXTP; + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + + vgmstream->layout_data = data; + if (loop_flag) + data->loop_segment = txtp->loop_start_segment-1; + } + + + clean_txtp(txtp); + return vgmstream; + +fail: + clean_txtp(txtp); + close_vgmstream(vgmstream); + free_layout_segmented(data); + return NULL; +} + + +static int add_filename(txtp_header * txtp, char *filename) { + int i; + uint32_t channel_mask = 0; + size_t range_start, range_end; + + //;VGM_LOG("TXTP: filename=%s\n", filename); + + /* parse config: + * - file.ext#2 = play subsong 2 + * - file.ext#2~10 = play subsongs in 2 to 10 range + * - file.ext#c1,2 = play channels 1,2 */ + { + char *config; + + /* position in base extension */ + config = strrchr(filename,'.'); + if (!config) /* needed...? */ + config = filename; + + range_start = 0; + range_end = 1; + do { + /* get config pointer but remove config from filename */ + config = strchr(config, '#'); + if (!config) + continue; + + config[0] = '\0'; + config++; + + + if (config[0] == 'c') { + /* mask channels */ + int n, ch; + + config++; + channel_mask = 0; + while (sscanf(config, "%d%n", &ch,&n) == 1) { + if (ch > 0 && ch < 32) + channel_mask |= (1 << (ch-1)); + + config += n; + if (config[0]== ',' || config[0]== '-') /* "-" for PowerShell, may have problems with "," */ + config++; + else if (config[0] != '\0') + break; + }; + } + else { + /* subsong range */ + int subsong_start = 0, subsong_end = 0; + + if (sscanf(config, "%d~%d", &subsong_start, &subsong_end) == 2) { + if (subsong_start > 0 && subsong_end > 0) { + range_start = subsong_start-1; + range_end = subsong_end-1; + } + } + else if (sscanf(config, "%u", &subsong_start) == 1) { + if (subsong_start > 0) { + range_start = subsong_start-1; + range_end = subsong_start; + } + } + else { + config = NULL; /* wrong config, ignore */ + } + } + + } while (config != NULL); + + //;VGM_LOG("TXTP: config: range %i~%i, mask=%x\n", range_start, range_end, channel_mask); + } + + + /* hack to allow relative paths in various OSs */ + { + char c; + + i = 0; + while ((c = filename[i]) != '\0') { + if ((c == '\\' && DIR_SEPARATOR == '/') || (c == '/' && DIR_SEPARATOR == '\\')) + filename[i] = DIR_SEPARATOR; + i++; + } + } + + + /* add filesnames */ + for (i = range_start; i < range_end; i++){ + /* resize in steps if not enough */ + if (txtp->entry_count+1 > txtp->entry_max) { + txtp_entry *temp_entry; + + txtp->entry_max += 5; + temp_entry = realloc(txtp->entry, sizeof(txtp_entry) * txtp->entry_max); + if (!temp_entry) goto fail; + txtp->entry = temp_entry; + } + + /* new entry */ + memset(&txtp->entry[txtp->entry_count],0, sizeof(txtp_entry)); + + strcpy(txtp->entry[txtp->entry_count].filename, filename); + txtp->entry[txtp->entry_count].channel_mask = channel_mask; + txtp->entry[txtp->entry_count].subsong = (i+1); + txtp->entry_count++; + } + + return 1; +fail: + return 0; +} + +static int parse_num(const char * val, uint32_t * out_value) { + int hex = (val[0]=='0' && val[1]=='x'); + if (sscanf(val, hex ? "%x" : "%u", out_value)!=1) + goto fail; + + return 1; +fail: + return 0; +} + +static int parse_keyval(txtp_header * txtp, const char * key, const char * val) { + //;VGM_LOG("TXTP: key %s = val %s\n", key,val); + + if (0==strcmp(key,"loop_start_segment")) { + if (!parse_num(val, &txtp->loop_start_segment)) goto fail; + } + else if (0==strcmp(key,"loop_end_segment")) { + if (!parse_num(val, &txtp->loop_end_segment)) goto fail; + } + else { + VGM_LOG("TXTP: unknown key=%s, val=%s\n", key,val); + goto fail; + } + + return 1; +fail: + return 0; +} + +static txtp_header* parse_txtp(STREAMFILE* streamFile) { + txtp_header* txtp = NULL; + off_t txt_offset = 0x00; + off_t file_size = get_streamfile_size(streamFile); + + + txtp = calloc(1,sizeof(txtp_header)); + if (!txtp) goto fail; + + + /* empty file: use filename with config (ex. "song.ext#3.txtp") */ + if (get_streamfile_size(streamFile) == 0) { + char filename[PATH_LIMIT] = {0}; + char* ext; + get_streamfile_filename(streamFile, filename,PATH_LIMIT); + + /* remove ".txtp" */ + ext = strrchr(filename,'.'); + if (!ext) goto fail; /* ??? */ + ext[0] = '\0'; + + if (!add_filename(txtp, filename)) + goto fail; + + return txtp; + } + + + /* skip BOM if needed */ + if (read_16bitLE(0x00, streamFile) == 0xFFFE || read_16bitLE(0x00, streamFile) == 0xFEFF) + txt_offset = 0x02; + + /* read lines */ + while (txt_offset < file_size) { + char line[TXT_LINE_MAX] = {0}; + char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */ + char filename[TXT_LINE_MAX] = {0}; + int ok, bytes_read, line_done; + + bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,streamFile, &line_done); + if (!line_done) goto fail; + + txt_offset += bytes_read; + + /* get key/val (ignores lead/trail spaces, stops at space/comment/separator) */ + ok = sscanf(line, " %[^ \t#=] = %[^ \t#\r\n] ", key,val); + if (ok == 2) { /* no key=val */ + if (!parse_keyval(txtp, key, val)) /* read key/val */ + goto fail; + continue; + } + + /* must be a filename (only remove spaces from start/end, as filenames con contain mid spaces/#/etc) */ + ok = sscanf(line, " %[^\t\r\n] ", filename); + if (ok != 1) /* not a filename either */ + continue; + if (filename[0] == '#') + continue; /* simple comment */ + + /* filename with config */ + if (!add_filename(txtp, filename)) + goto fail; + } + + + return txtp; +fail: + clean_txtp(txtp); + return NULL; +} + +static void clean_txtp(txtp_header* txtp) { + if (!txtp) + return; + + free(txtp->entry); + free(txtp); +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c new file mode 100644 index 000000000..1ce698ccf --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -0,0 +1,517 @@ +#include "meta.h" +#include "../coding/coding.h" + + +typedef enum { NONE = 0, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_XMA1, RAW_XMA2, RAW_AT3, FMT_AT3, RAW_DSP, FMT_OGG } ubi_bao_codec; +typedef struct { + ubi_bao_codec codec; + int big_endian; + int total_subsongs; + + /* stream info */ + size_t header_size; + size_t stream_size; + off_t stream_offset; + uint32_t stream_id; + off_t extradata_offset; + int is_external; + + int header_codec; + int num_samples; + int sample_rate; + int channels; + + char resource_name[255]; + int types_count[9]; +} ubi_bao_header; + +static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset); +static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile); +static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE *streamFile); + + +/* .PK - packages with BAOs from Ubisoft's sound engine ("DARE") games in 2008+ */ +VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile) { + ubi_bao_header bao = {0}; + + /* checks */ + if (!check_extensions(streamFile, "pk,lpk")) + goto fail; + + /* .pk+spk (or .lpk+lspk) is a database-like format, evolved from Ubi sb0/sm0+sp0. + * .pk has "BAO" headers pointing to internal or external .spk resources (also BAOs). */ + + /* main parse */ + if ( !parse_pk_header(&bao, streamFile) ) + goto fail; + + return init_vgmstream_ubi_bao_main(&bao, streamFile); +fail: + return NULL; +} + +#if 0 +/* .BAO - files with a single BAO from Ubisoft's sound engine ("DARE") games in 2008+ */ +VGMSTREAM * init_vgmstream_ubi_bao_file(STREAMFILE *streamFile) { + ubi_bao_header bao = {0}; + + /* checks */ + if (!check_extensions(streamFile, "bao")) + goto fail; + + /* single .bao+sbao found in .forge and similar bigfiles (containing compressed + * "BAO_0xNNNNNNNN" headers/links, or "Common/English/(etc)_BAO_0xNNNNNNNN" streams). + * The bigfile acts as index, but external files can be opened as are named after their id. + * Extension isn't always given but is .bao in some games. */ + + /* main parse */ + if ( !parse_bao_header(&bao, streamFile) ) + goto fail; + + return init_vgmstream_ubi_bao_main(&bao, streamFile); +fail: + return NULL; +} +#endif + + +static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE *streamData = NULL; + off_t start_offset; + int loop_flag = 0; + + + /* open external stream if needed */ + if (bao->is_external) { + streamData = open_streamfile_by_filename(streamFile,bao->resource_name); + if (!streamData) { + VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); + goto fail; + } + } + else { + streamData = streamFile; + } + + start_offset = bao->stream_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(bao->channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->num_samples = bao->num_samples; + vgmstream->sample_rate = bao->sample_rate; + vgmstream->num_streams = bao->total_subsongs; + vgmstream->stream_size = bao->stream_size; + vgmstream->meta_type = meta_UBI_BAO; + + switch(bao->codec) { +#if 0 + case UBI_ADPCM: { + vgmstream->coding_type = coding_UBI_IMA; + vgmstream->layout_type = layout_none; + break; + } +#endif + + case RAW_PCM: + vgmstream->coding_type = coding_PCM16LE; /* always LE even on Wii */ + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + + case RAW_PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = bao->stream_size / bao->channels; + break; + + case RAW_DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = bao->stream_size / bao->channels; + dsp_read_coefs_be(vgmstream,streamFile,bao->extradata_offset+0x10, 0x40); + break; + +#ifdef VGM_USE_FFMPEG + case RAW_XMA1: + case RAW_XMA2: { + uint8_t buf[0x100]; + size_t bytes, chunk_size; + + chunk_size = (bao->codec == RAW_XMA1) ? 0x20 : 0x34; + + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, bao->extradata_offset,chunk_size, bao->stream_size, streamFile, 1); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,bao->stream_size); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } + + case RAW_AT3: { + uint8_t buf[0x100]; + int32_t bytes, block_size, encoder_delay, joint_stereo; + + block_size = 0xc0 * vgmstream->channels; + joint_stereo = 0; + encoder_delay = 0x00;//todo not correct + + bytes = ffmpeg_make_riff_atrac3(buf,0x100, vgmstream->num_samples, bao->stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,bao->stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } + + case FMT_AT3: { + ffmpeg_codec_data *ffmpeg_data; + + ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, bao->stream_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* manually read skip_samples if FFmpeg didn't do it */ + if (ffmpeg_data->skipSamples <= 0) { + off_t chunk_offset; + size_t chunk_size, fact_skip_samples = 0; + if (!find_chunk_le(streamData, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */ + goto fail; + if (chunk_size == 0x8) { + fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamData); + } else if (chunk_size == 0xc) { + fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamData); + } + ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples); + } + break; + } + + case FMT_OGG: { + ffmpeg_codec_data *ffmpeg_data; + + ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, bao->stream_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = bao->num_samples; /* ffmpeg_data->totalSamples */ + VGM_ASSERT(bao->num_samples != ffmpeg_data->totalSamples, "UBI BAO: header samples differ\n"); + break; + } + +#endif + default: + goto fail; + } + + /* open the file for reading (can be an external stream, different from the current .pk) */ + if ( !vgmstream_open_stream(vgmstream, streamData, start_offset) ) + goto fail; + + if (bao->is_external && streamData) close_streamfile(streamData); + return vgmstream; + +fail: + if (bao->is_external && streamData) close_streamfile(streamData); + close_vgmstream(vgmstream); + return NULL; +} + +/* parse a .pk (package) file: index + BAOs + external .spk resource table. We want header + * BAOs pointing to internal/external stream BAOs (.spk is the same, with stream BAOs only). */ +static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { + int i; + int index_entries; + size_t index_size, index_header_size; + off_t bao_offset, resources_offset; + int target_subsong = streamFile->stream_index; + + + /* class: 0x01=index, 0x02=BAO */ + if (read_8bit(0x00, streamFile) != 0x01) + goto fail; + /* index and resources always LE */ + + /* 0x01(3): version, major/minor/release (numbering continues from .sb0/sm0) */ + index_size = read_32bitLE(0x04, streamFile); /* can be 0 */ + resources_offset = read_32bitLE(0x08, streamFile); /* always found even if not used */ + /* 0x0c: always 0? */ + /* 0x10: unknown, null if no entries */ + /* 0x14: config/flags/time? (changes a bit between files), null if no entries */ + /* 0x18(10): file GUID? clones may share it */ + /* 0x24: unknown */ + /* 0x2c: unknown, may be same as 0x14, can be null */ + /* 0x30(10): parent GUID? may be same as 0x18, may be shared with other files */ + /* (the above values seem ignored by games, probably just info for their tools) */ + + index_entries = index_size / 0x08; + index_header_size = 0x40; + + /* parse index to get target subsong N = Nth header BAO */ + bao_offset = index_header_size + index_size; + for (i = 0; i < index_entries; i++) { + //uint32_t bao_id = read_32bitLE(index_header_size+0x08*i+0x00, streamFile); + size_t bao_size = read_32bitLE(index_header_size+0x08*i+0x04, streamFile); + + /* parse and continue to find out total_subsongs */ + if (!parse_bao(bao, streamFile, bao_offset)) + goto fail; + + bao_offset += bao_size; /* files simply concat BAOs */ + } + + ;VGM_LOG("BAO types: 10=%i,20=%i,30=%i,40=%i,50=%i,70=%i,80=%i\n", + bao->types_count[1],bao->types_count[2],bao->types_count[3],bao->types_count[4],bao->types_count[5],bao->types_count[7],bao->types_count[8]); + + if (bao->total_subsongs == 0) { + VGM_LOG("UBI BAO: no streams\n"); + goto fail; /* not uncommon */ + } + if (target_subsong < 0 || target_subsong > bao->total_subsongs || bao->total_subsongs < 1) goto fail; + + + /* get stream pointed by header */ + if (bao->is_external) { + /* parse resource table, LE (may be empty, or exist even with nothing in the file) */ + off_t offset; + int resources_count = read_32bitLE(resources_offset+0x00, streamFile); + size_t strings_size = read_32bitLE(resources_offset+0x04, streamFile); + + offset = resources_offset + 0x04+0x04 + strings_size; + for (i = 0; i < resources_count; i++) { + uint32_t resource_id = read_32bitLE(offset+0x10*i+0x00, streamFile); + off_t name_offset = read_32bitLE(offset+0x10*i+0x04, streamFile); + off_t resource_offset = read_32bitLE(offset+0x10*i+0x08, streamFile); + size_t resource_size = read_32bitLE(offset+0x10*i+0x0c, streamFile); + + if (resource_id == bao->stream_id) { + bao->stream_offset = resource_offset + bao->header_size; + read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, streamFile); + + VGM_ASSERT(bao->stream_size != resource_size - bao->header_size, "UBI BAO: stream vs resource size mismatch\n"); + break; + } + } + + + //todo find flag and fix + /* some songs divide data in internal+external resource and data may be split arbitrarily, + * must join on reads (needs multifile_streamfile); resources may use block layout in XMA too */ + bao_offset = index_header_size + index_size; + for (i = 0; i < index_entries; i++) { + uint32_t bao_id = read_32bitLE(index_header_size+0x08*i+0x00, streamFile); + size_t bao_size = read_32bitLE(index_header_size+0x08*i+0x04, streamFile); + + if (bao_id == bao->stream_id) { + VGM_LOG("UBI BAO: found internal+external at offset=%lx\n",bao_offset); + goto fail; + } + + bao_offset += bao_size; + } + } + else { + /* find within index */ + + bao_offset = index_header_size + index_size; + for (i = 0; i < index_entries; i++) { + uint32_t bao_id = read_32bitLE(index_header_size+0x08*i+0x00, streamFile); + size_t bao_size = read_32bitLE(index_header_size+0x08*i+0x04, streamFile); + + if (bao_id == bao->stream_id) { + bao->stream_offset = bao_offset + bao->header_size; /* relative, adjust to skip descriptor */ + break; + } + + bao_offset += bao_size; + } + } + + if (!bao->stream_offset) { + VGM_LOG("UBI BAO: stream not found (id=%08x, external=%i)\n", bao->stream_id, bao->is_external); + goto fail; + } + + ;VGM_LOG("BAO stream: id=%x, offset=%lx, size=%x, res=%s\n", bao->stream_id, bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal")); + + return 1; +fail: + return 0; +} + +/* parse a single BAO (binary audio object) descriptor */ +static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + uint32_t bao_version, descriptor_type; + size_t header_size; + int target_subsong = streamFile->stream_index; + + + /* 0x00(1): class? usually 0x02 but older BAOs have 0x01 too */ + bao_version = read_32bitBE(offset + 0x00, streamFile) & 0x00FFFFFF; + + /* detect endianness */ + if (read_32bitLE(offset+0x04, streamFile) < 0x0000FFFF) { + read_32bit = read_32bitLE; + } else { + read_32bit = read_32bitBE; + bao->big_endian = 1; + } + + header_size = read_32bit(offset+0x04, streamFile); /* mainly 0x28, rarely 0x24 */ + /* 0x08(10): descriptor GUID? */ + /* 0x18: null */ + /* 0x1c: null */ + descriptor_type = read_32bit(offset+0x20, streamFile); + /* 0x28: subtype? usually 0x02/0x01, games may crash if changed */ + + /* for debugging purposes */ + switch(descriptor_type) { + case 0x10000000: bao->types_count[1]++; break; /* link by id to another descriptor (link or header) */ + case 0x20000000: bao->types_count[2]++; break; /* stream header (and subtypes) */ + case 0x30000000: bao->types_count[3]++; break; /* internal stream (in .pk) */ + case 0x40000000: bao->types_count[4]++; break; /* package info? */ + case 0x50000000: bao->types_count[5]++; break; /* external stream (in .spk) */ + case 0x70000000: bao->types_count[7]++; break; /* project info? (sometimes special id 0x7fffffff)*/ + case 0x80000000: bao->types_count[8]++; break; /* unknown (some id/info?) */ + default: + VGM_LOG("UBI BAO: unknown descriptor type at %lx\n", offset); + goto fail; + } + + /* only parse headers */ + if (descriptor_type != 0x20000000) + return 1; + /* ignore other header subtypes, 0x01=sound header, 0x04=info? (like Ubi .sb0) */ + if (read_32bit(offset+header_size+0x04, streamFile) != 0x01) + return 1; + + bao->total_subsongs++; + if (target_subsong == 0) target_subsong = 1; + + if (target_subsong != bao->total_subsongs) + return 1; + + /* parse BAO per version. Structure is mostly the same with some extra fields. + * - descriptor id (ignored by game) + * - type (may crash on game startup if changed) + * - stream size + * - stream id, corresponding to an internal (0x30) or external (0x50) stream + * - various flags/config fields + * - channels, ?, sample rate, average bit rate?, samples, full stream_size?, codec, etc + * - subtable entries, subtable size (may contain offsets/ids, cues, etc) + * - extra data per codec (ex. XMA header in some versions) */ + //todo skip tables when getting extradata + ;VGM_LOG("BAO header at %lx\n", offset); + + switch(bao_version) { + + case 0x001F0011: /* Naruto: The Broken Bond (X360)-pk */ + case 0x0022000D: /* Just Dance (Wii)-pk */ + bao->stream_size = read_32bit(offset+header_size+0x08, streamFile); + bao->stream_id = read_32bit(offset+header_size+0x1c, streamFile); + bao->is_external = read_32bit(offset+header_size+0x28, streamFile); /* maybe 0x30 */ + bao->channels = read_32bit(offset+header_size+0x44, streamFile); + bao->sample_rate = read_32bit(offset+header_size+0x4c, streamFile); + bao->num_samples = read_32bit(offset+header_size+0x54, streamFile); + bao->header_codec = read_32bit(offset+header_size+0x64, streamFile); + + switch(bao->header_codec) { + case 0x01: bao->codec = RAW_PCM; break; + case 0x05: bao->codec = RAW_XMA1; break; + case 0x09: bao->codec = RAW_DSP; break; + default: VGM_LOG("UBI BAO: unknown codec at %lx\n", offset); goto fail; + } + + //todo use flags? + if (bao->header_codec == 0x09) { + bao->extradata_offset = offset+header_size+0x80; /* mini DSP header */ + } + if (bao->header_codec == 0x05 && !bao->is_external) { + bao->extradata_offset = offset+header_size + 0x7c; /* XMA header */ + } + //todo external XMA may use blocked layout + layered layout + + break; + + case 0x00220015: /* James Cameron's Avatar: The Game (PSP)-pk */ + case 0x0022001E: /* Prince of Persia: The Forgotten Sands (PSP)-pk */ + bao->stream_size = read_32bit(offset+header_size+0x08, streamFile); + bao->stream_id = read_32bit(offset+header_size+0x1c, streamFile); + bao->is_external = read_32bit(offset+header_size+0x20, streamFile) & 0x04; + bao->channels = read_32bit(offset+header_size+0x28, streamFile); + bao->sample_rate = read_32bit(offset+header_size+0x30, streamFile); + if (read_32bit(offset+header_size+0x20, streamFile) & 0x20) { + bao->num_samples = read_32bit(offset+header_size+0x40, streamFile); + } + else { + bao->num_samples = read_32bit(offset+header_size+0x38, streamFile); /* from "fact" if AT3 */ + } + bao->header_codec = read_32bit(offset+header_size+0x48, streamFile); + + switch(bao->header_codec) { + case 0x06: bao->codec = RAW_PSX; break; + case 0x07: bao->codec = FMT_AT3; break; + default: VGM_LOG("UBI BAO: unknown codec at %lx\n", offset); goto fail; + } + + if (read_32bit(offset+header_size+0x20, streamFile) & 0x10) { + VGM_LOG("UBI BAO: possible full loop at %lx\n", offset); + /* RIFFs may have "smpl" and this flag, even when data shouldn't loop... */ + } + + break; + + case 0x00250108: /* Scott Pilgrim vs the World (PS3/X360)-pk */ + case 0x0025010A: /* Prince of Persia: The Forgotten Sands (PS3/X360)-file */ + bao->stream_size = read_32bit(offset+header_size+0x08, streamFile); + bao->stream_id = read_32bit(offset+header_size+0x24, streamFile); + bao->is_external = read_32bit(offset+header_size+0x30, streamFile); + bao->channels = read_32bit(offset+header_size+0x48, streamFile); + bao->sample_rate = read_32bit(offset+header_size+0x50, streamFile); + if (read_32bit(offset+header_size+0x38, streamFile) & 0x01) { /* single flag? */ + bao->num_samples = read_32bit(offset+header_size+0x60, streamFile); + } + else { + bao->num_samples = read_32bit(offset+header_size+0x58, streamFile); + } + bao->header_codec = read_32bit(offset+header_size+0x68, streamFile); + /* when is internal+external (flag 0x2c?), 0xa0: internal data size */ + + switch(bao->header_codec) { + case 0x01: bao->codec = RAW_PCM; break; + case 0x04: bao->codec = RAW_XMA2; break; + case 0x05: bao->codec = RAW_PSX; break; + case 0x06: bao->codec = RAW_AT3; break; + default: VGM_LOG("UBI BAO: unknown codec at %lx\n", offset); goto fail; + } + + if (bao->header_codec == 0x04 && !bao->is_external) { + bao->extradata_offset = offset+header_size + 0x8c; /* XMA header */ + } + + break; + + case 0x001B0100: /* Assassin's Creed (PS3/X360/PC)-file */ + case 0x001B0200: /* Beowulf (PS3)-file */ + case 0x001F0010: /* Prince of Persia 2008 (PS3/X360)-file, Far Cry 2 (PS3)-file */ + case 0x00280306: /* Far Cry 3: Blood Dragon (X360)-file */ + case 0x00290106: /* Splinter Cell Blacklist? */ + default: + VGM_LOG("UBI BAO: unknown BAO version at %lx\n", offset); + goto fail; + } + + bao->header_size = header_size; + + return 1; +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c new file mode 100644 index 000000000..aab40b98c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c @@ -0,0 +1,401 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_jade_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext); +static int get_loop_points(STREAMFILE *streamFile, int *out_loop_start, int *out_loop_end); + +/* Jade RIFF - from Ubisoft Jade engine games [Beyond Good & Evil (multi), Rayman Raving Rabbids 1/2 (multi)] */ +VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, first_offset = 0xc; + off_t fmt_offset, data_offset; + size_t fmt_size, data_size; + int loop_flag, channel_count, sample_rate, codec, block_size; + int loop_start = 0, loop_end = 0; + int is_jade_v2 = 0; + + + /* checks */ + /* .waa: ambiances, .wam: music, .wac: sfx, .wad: dialogs (usually) + * .wav: Beyond Good & Evil HD (PS3), .psw: fake/badly extracted names [ex. Rayman Raving Rabbids (PS2)] */ + if (!check_extensions(streamFile,"waa,wac,wad,wam,wav,lwav,psw")) + goto fail; + + /* a slightly twisted RIFF with custom codecs */ + if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */ + read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ + goto fail; + + if (check_extensions(streamFile,"psw")) { /* .psw are incorrectly extracted missing 0x04 at the end */ + if (read_32bitLE(0x04,streamFile)+0x04 != get_streamfile_size(streamFile)) + goto fail; + } + else { + if (read_32bitLE(0x04,streamFile)+0x04+0x04 != get_streamfile_size(streamFile)) + goto fail; + } + + if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */ + goto fail; + if (!find_chunk(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */ + goto fail; + + /* ignore LyN RIFF (needed as codec 0xFFFE is reused) */ + { + off_t fact_offset; + size_t fact_size; + + if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */ + if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */ + goto fail; /* parsed elsewhere */ + /* Jade doesn't use "fact", though */ + } + } + + + /* parse format */ + { + if (fmt_size < 0x10) + goto fail; + codec = (uint16_t)read_16bitLE(fmt_offset+0x00,streamFile); + channel_count = read_16bitLE(fmt_offset+0x02,streamFile); + sample_rate = read_32bitLE(fmt_offset+0x04,streamFile); + block_size = (uint16_t)read_16bitLE(fmt_offset+0x0c,streamFile); + /* 0x08: average bytes, 0x0e: bps, etc */ + + /* autodetect Jade "v2", uses a different interleave [Rayman Raving Rabbids (PS2/Wii)] */ + switch(codec) { + case 0xFFFF: { /* PS2 */ + int i; + + /* half interleave check as there is no flag (ends with the PS-ADPCM stop frame) */ + for (i = 0; i < channel_count; i++) { + off_t end_frame = data_offset + (data_size / channel_count) * (i+1) - 0x10; + if (read_32bitBE(end_frame+0x00,streamFile) != 0x07007777 || + read_32bitBE(end_frame+0x04,streamFile) != 0x77777777 || + read_32bitBE(end_frame+0x08,streamFile) != 0x77777777 || + read_32bitBE(end_frame+0x0c,streamFile) != 0x77777777) { + is_jade_v2 = 1; + break; + } + } + break; + } + + case 0xFFFE: /* GC/Wii */ + is_jade_v2 = (read_16bitLE(fmt_offset+0x10,streamFile) == 0); /* extra data size (0x2e*channels) */ + break; + } + + /* hopefully catches PC Rabbids */ + if (find_chunk(streamFile, 0x63756520,first_offset,0, NULL,NULL, 0, 0)) { /* "cue " */ + is_jade_v2 = 1; + } + } + + + /* get loop points */ + if (is_jade_v2) { + loop_flag = get_loop_points(streamFile, &loop_start, &loop_end); /* loops in "LIST" */ + } + else { + /* BG&E files don't contain looping information, so the looping is done by extension. + * wam and waa contain ambient sounds and music, so often they contain looped music. + * Later, if the file is too short looping will be disabled. */ + loop_flag = check_extensions(streamFile,"waa,wam"); + } + + start_offset = data_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_UBI_JADE; + if (is_jade_v2) { + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + } + + switch(codec) { + + case 0x0069: /* Xbox */ + if (block_size != 0x24*channel_count) + goto fail; + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channel_count); + if (!is_jade_v2) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + break; + + case 0xFFFF: /* PS2 */ + if (block_size != 0x10) + goto fail; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + + if (is_jade_v2) { + vgmstream->interleave_block_size = 0x6400; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + } + else { + vgmstream->interleave_block_size = data_size / channel_count; + } + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + if (!is_jade_v2) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + break; + + case 0xFFFE: /* GC/Wii */ + if (block_size != 0x08) + goto fail; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + if (!is_jade_v2) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + /* coefs / interleave */ + if (is_jade_v2) { + vgmstream->interleave_block_size = 0x6400; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = ((data_size % (vgmstream->interleave_block_size*vgmstream->channels))/2+7)/8*8; + + { + static const int16_t coef[16] = { /* default Ubisoft coefs, from ELF */ + 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, + 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5 + }; + int i, ch; + + for (ch = 0; ch < channel_count; ch++) { + for (i = 0; i < 16; i++) { + vgmstream->ch[ch].adpcm_coef[i] = coef[i]; + } + } + } + } + else { + /* has extra 0x2e coefs before each channel, not counted in data_size */ + vgmstream->interleave_block_size = (data_size + 0x2e*channel_count) / channel_count; + + dsp_read_coefs_be(vgmstream, streamFile, start_offset+0x00, vgmstream->interleave_block_size); + dsp_read_hist_be (vgmstream, streamFile, start_offset+0x20, vgmstream->interleave_block_size); + start_offset += 0x2e; + } + break; + + case 0x0002: /* PC */ + if (block_size != 0x24*channel_count) + goto fail; + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x24*channel_count; + + vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->interleave_block_size, channel_count); + if (!is_jade_v2) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + break; + + case 0x0001: { /* PS3 */ + VGMSTREAM *temp_vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + + if (block_size != 0x02*channel_count) + goto fail; + + /* a MSF (usually ATRAC3) masquerading as PCM */ + if (read_32bitBE(start_offset, streamFile) != 0x4D534643) /* "MSF\43" */ + goto fail; + + temp_streamFile = setup_jade_streamfile(streamFile, start_offset, data_size, "msf"); + if (!temp_streamFile) goto fail; + + temp_vgmstream = init_vgmstream_ps3_msf(temp_streamFile); + close_streamfile(temp_streamFile); + if (!temp_vgmstream) goto fail; + + temp_vgmstream->meta_type = vgmstream->meta_type; + close_vgmstream(vgmstream); + return temp_vgmstream; + } + + default: /* X360 uses .XMA */ + goto fail; + } + + /* V1 loops by extension, try to detect incorrectly looped jingles (too short) */ + if (!is_jade_v2) { + if(loop_flag + && vgmstream->num_samples < 15*sample_rate) { /* in seconds */ + vgmstream->loop_flag = 0; + } + } + + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* extract loops from "cue /LIST", returns if loops (info from Droolie) */ +static int get_loop_points(STREAMFILE *streamFile, int *out_loop_start, int *out_loop_end) { + off_t cue_offset, list_offset; + size_t cue_size, list_size; + off_t offset, first_offset = 0x0c; + int i, cue_count, loop_id = 0, loop_start = 0, loop_end = 0; + + + /* unlooped files may contain LIST, but also may not */ + if (!find_chunk(streamFile, 0x63756520,first_offset,0, &cue_offset,&cue_size, 0, 0)) /* "cue " */ + goto fail; + if (!find_chunk(streamFile, 0x4C495354,first_offset,0, &list_offset,&list_size, 0, 0)) /* "LIST" */ + goto fail; + + offset = list_offset + 0x04; + while (offset < list_offset + list_size) { + uint32_t chunk_id = read_32bitBE(offset+0x00, streamFile); + uint32_t chunk_size = read_32bitLE(offset+0x04, streamFile); + offset += 0x08; + + switch(chunk_id) { + case 0x6C61626C: /* "labl" */ + if (read_32bitBE(offset+0x04, streamFile) == 0x6C6F6F70) /* "loop", actually an string tho */ + loop_id = read_32bitLE(offset+0x00, streamFile); + chunk_size += (chunk_size % 2) ? 1 : 0; /* string is even-padded after size */ + break; + case 0x6C747874: /* "ltxt" */ + if (loop_id == read_32bitLE(offset+0x00, streamFile)) + loop_end = read_32bitLE(offset+0x04, streamFile); + break; + + default: + VGM_LOG("Jade: unknown LIST chunk at %lx\n", offset); + goto fail; + } + + offset += chunk_size; + } + + if (!loop_end) + return 0; + + cue_count = read_32bitLE(cue_offset+0x00, streamFile); + for (i = 0; i < cue_count; i++) { + if (loop_id == read_32bitLE(cue_offset+0x04 + i*0x18 + 0x00, streamFile)) { + loop_start = read_32bitLE(cue_offset+0x04 + i*0x18 + 0x04, streamFile); + loop_end += loop_start; + break; + } + } + + *out_loop_start = loop_start; + *out_loop_end = loop_end; + return 1; + +fail: + return 0; +} + + +/* Jade RIFF in containers */ +VGMSTREAM * init_vgmstream_ubi_jade_container(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset; + size_t subfile_size; + + /* Jade packs files in bigfiles, and once extracted the sound files have extra engine data before + * the RIFF + padding after. Most extractors don't remove the padding correctly, so here we add support. */ + + /* checks */ + /* standard Jade exts + .xma for padded XMA used in Beyond Good & Evil HD (X360) */ + if (!check_extensions(streamFile,"waa,wac,wad,wam,wav,lwav,xma")) + goto fail; + + if (read_32bitBE(0x04,streamFile) == 0x52494646 && + read_32bitLE(0x00,streamFile)+0x04 == get_streamfile_size(streamFile)) { + /* data size + RIFF + padding */ + subfile_offset = 0x04; + } + else if (read_32bitBE(0x00,streamFile) == 0x52494646 && + read_32bitLE(0x04,streamFile)+0x04+0x04 < get_streamfile_size(streamFile) && + (get_streamfile_size(streamFile) + 0x04) % 0x800 == 0) { + /* RIFF + padding with data size removed (bad extraction) */ + subfile_offset = 0x00; + } + else if (read_32bitBE(0x04,streamFile) == 0x52494646 && + read_32bitLE(0x00,streamFile) == get_streamfile_size(streamFile)) { + /* data_size + RIFF + padding - 0x04 (bad extraction) */ + subfile_offset = 0x04; + } + else { + goto fail; + } + + subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04; + + temp_streamFile = setup_jade_streamfile(streamFile, subfile_offset,subfile_size, NULL); + if (!temp_streamFile) goto fail; + + if (check_extensions(streamFile,"xma")) { + vgmstream = init_vgmstream_xma(temp_streamFile); + } else { + vgmstream = init_vgmstream_ubi_jade(temp_streamFile); + } + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_jade_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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c new file mode 100644 index 000000000..77c714a16 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c @@ -0,0 +1,293 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" +#include "ubi_lyn_ogg_streamfile.h" + +static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); + +/* LyN RIFF - from Ubisoft LyN engine games [Red Steel 2 (Wii), Adventures of Tintin (Multi), From Dust (Multi), Just Dance 3/4 (multi)] */ +VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, first_offset = 0xc; + off_t fmt_offset, data_offset, fact_offset; + size_t fmt_size, data_size, fact_size; + int loop_flag, channel_count, sample_rate, codec; + int num_samples; + + + /* checks */ + /* .sns: Red Steel 2, .wav: Tintin, .son: From Dust */ + if (!check_extensions(streamFile,"sns,wav,lwav,son")) + goto fail; + + /* a slightly eccentric RIFF with custom codecs */ + if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */ + read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ + goto fail; + if (read_32bitLE(0x04,streamFile)+0x04+0x04 != get_streamfile_size(streamFile)) + goto fail; + + if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */ + goto fail; + if (!find_chunk(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */ + goto fail; + + /* always found, even with PCM (LyN subchunk seems to contain the engine version, ex. 0x0d/10) */ + if (!find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) /* "fact" */ + goto fail; + if (fact_size != 0x10 || read_32bitBE(fact_offset+0x04, streamFile) != 0x4C794E20) /* "LyN " */ + goto fail; + num_samples = read_32bitLE(fact_offset+0x00, streamFile); + /* sometimes there is a LySE chunk */ + + + /* parse format */ + { + if (fmt_size < 0x12) + goto fail; + codec = (uint16_t)read_16bitLE(fmt_offset+0x00,streamFile); + channel_count = read_16bitLE(fmt_offset+0x02,streamFile); + sample_rate = read_32bitLE(fmt_offset+0x04,streamFile); + /* 0x08: average bytes, 0x0c: block align, 0x0e: bps, etc */ + + /* fake WAVEFORMATEX, used with > 2ch */ + if (codec == 0xFFFE) { + if (fmt_size < 0x28) + goto fail; + /* fake GUID with first value doubling as codec */ + codec = read_32bitLE(fmt_offset+0x18,streamFile); + if (read_32bitBE(fmt_offset+0x1c,streamFile) != 0x00001000 && + read_32bitBE(fmt_offset+0x20,streamFile) != 0x800000AA && + read_32bitBE(fmt_offset+0x24,streamFile) != 0x00389B71) { + goto fail; + } + } + } + + /* most songs simply repeat, loop if it looks long enough */ + loop_flag = (num_samples > 20*sample_rate); /* in seconds */ + start_offset = data_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_UBI_LYN; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + + switch(codec) { + case 0x0001: /* PCM */ + vgmstream->coding_type = coding_PCM16LE; /* LE even in X360 */ + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + + case 0x5050: /* DSP (Wii) */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; + + /* setup default Ubisoft coefs */ + { + static const int16_t coef[16] = { + 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, + 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5 + }; + int i, ch; + + for (ch = 0; ch < channel_count; ch++) { + for (i = 0; i < 16; i++) { + vgmstream->ch[ch].adpcm_coef[i] = coef[i]; + } + } + } + + break; + +#ifdef VGM_USE_VORBIS + case 0x3157: { /* Ogg (PC), interleaved 1ch */ + size_t interleave_size, stride_size; + layered_layout_data* data = NULL; + int i; + + if (read_32bitLE(start_offset+0x00,streamFile) != 1) /* id? */ + goto fail; + + interleave_size = read_32bitLE(start_offset+0x04,streamFile); + stride_size = interleave_size * channel_count; + /* interleave is adjusted so there is no smaller last block, it seems */ + + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_layered; + + /* init layout */ + data = init_layout_layered(channel_count); + if (!data) goto fail; + vgmstream->layout_data = data; + + /* open each layer subfile */ + for (i = 0; i < channel_count; i++) { + STREAMFILE* temp_streamFile = NULL; + size_t total_size = read_32bitLE(start_offset+0x08 + 0x04*i,streamFile); + off_t layer_offset = start_offset+0x08 + 0x04*channel_count + interleave_size*i; + + temp_streamFile = setup_lyn_ogg_streamfile(streamFile, layer_offset, interleave_size, stride_size, total_size); + if (!temp_streamFile) goto fail; + + data->layers[i] = init_vgmstream_ogg_vorbis(temp_streamFile); + close_streamfile(temp_streamFile); + if (!data->layers[i]) goto fail; + + /* could validate between layers, meh */ + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x5051: { /* MPEG (PS3/PC), interleaved 1ch */ + mpeg_codec_data *mpeg_data = NULL; + mpeg_custom_config cfg = {0}; + int i; + + if (read_32bitLE(start_offset+0x00,streamFile) != 2) /* id? */ + goto fail; + + cfg.interleave = read_32bitLE(start_offset+0x04,streamFile); + cfg.chunk_size = read_32bitLE(start_offset+0x08,streamFile); + /* 0x08: frame size, 0x0c: frame per interleave, 0x10: samples per frame */ + + /* skip seek tables and find actual start */ + start_offset += 0x14; + data_size -= 0x14; + for (i = 0; i < channel_count; i++) { + int entries = read_32bitLE(start_offset,streamFile); + + start_offset += 0x04 + entries*0x08; + data_size -= 0x04 + entries*0x08; + } + + cfg.data_size = data_size; + + //todo data parsing looks correct but some files decode a bit wrong at the end (ex. Tintin: Music~Boss~Allan~Victory~02) + mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; + vgmstream->layout_type = layout_none; + + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x0166: { /* XMA (X360), standard */ + uint8_t buf[0x100]; + int bytes; + off_t chunk_offset; + size_t chunk_size, seek_size; + + if (read_32bitLE(start_offset+0x00,streamFile) != 3) /* id? */ + goto fail; + + /* skip standard XMA header + seek table */ + chunk_offset = start_offset + 0x04 + 0x04; + chunk_size = read_32bitLE(start_offset + 0x04, streamFile); + seek_size = read_32bitLE(chunk_offset+chunk_size, streamFile); + start_offset += (0x04 + 0x04 + chunk_size + 0x04 + seek_size); + data_size -= (0x04 + 0x04 + chunk_size + 0x04 + seek_size); + + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1); + 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; + + break; + } +#endif + + default: + goto fail; + } + + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* LyN RIFF in containers */ +VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset; + size_t subfile_size; + + /* LyN packs files in bigfiles, and once extracted the sound files have extra engine + * data before the RIFF. Might as well support them in case the RIFF wasn't extracted. */ + + /* checks */ + if (!check_extensions(streamFile,"sns,wav,lwav,son")) + goto fail; + + /* find "RIFF" position */ + if (read_32bitBE(0x00,streamFile) == 0x4C795345 && /* "LySE" */ + read_32bitBE(0x14,streamFile) == 0x52494646) { /* "RIFF" */ + subfile_offset = 0x14; /* Adventures of Tintin */ + } + else if (read_32bitLE(0x00,streamFile)+0x20 == get_streamfile_size(streamFile) && + read_32bitBE(0x20,streamFile) == 0x52494646) { /* "RIFF" */ + subfile_offset = 0x20; /* Red Steel 2, From Dust */ + } + else { + goto fail; + } + + subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04; + + temp_streamFile = setup_lyn_streamfile(streamFile, subfile_offset,subfile_size); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_ubi_lyn(temp_streamFile); + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + 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; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn_ogg_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn_ogg_streamfile.h new file mode 100644 index 000000000..aeec84f87 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn_ogg_streamfile.h @@ -0,0 +1,92 @@ +#ifndef _LYN_OGG_STREAMFILE_H_ +#define _LYN_OGG_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + off_t start_physical_offset; /* interleaved data start, for this substream */ + size_t interleave_block_size; /* max size that can be read before encountering other substreams */ + size_t stride_size; /* step size between interleave blocks (interleave*channels) */ + size_t total_size; /* final size of the deinterleaved substream */ +} lyn_ogg_io_data; + + +/* Handles deinterleaving of complete files, skipping portions or other substreams. */ +static size_t scd_dsp_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, lyn_ogg_io_data* data) { + size_t total_read = 0; + + while (length > 0) { + size_t to_read; + size_t length_available; + off_t block_num; + off_t intrablock_offset; + off_t physical_offset; + + block_num = offset / data->interleave_block_size; + intrablock_offset = offset % data->interleave_block_size; + physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset; + length_available = data->interleave_block_size - intrablock_offset; + + if (length < length_available) { + to_read = length; + } + else { + to_read = length_available; + } + + if (to_read > 0) { + size_t bytes_read; + + bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile); + total_read += bytes_read; + + if (bytes_read != to_read) { + return total_read; + } + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + } + } + + return total_read; +} + +static size_t scd_dsp_io_size(STREAMFILE *streamfile, lyn_ogg_io_data* data) { + return data->total_size; +} + + +static STREAMFILE* setup_lyn_ogg_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t total_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + lyn_ogg_io_data io_data = {0}; + size_t io_data_size = sizeof(lyn_ogg_io_data); + + io_data.start_physical_offset = start_offset; + io_data.interleave_block_size = interleave_block_size; + io_data.stride_size = stride_size; + io_data.total_size = total_size; + + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, scd_dsp_io_read,scd_dsp_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"ogg"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _LYN_OGG_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index ad0d9abb0..5b9c33395 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -90,7 +90,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { if (sb.autodetect_external) { /* works most of the time but could give false positives */ VGM_LOG("UBI SB: autodetecting external stream '%s'\n", sb.stream_name); - streamData = open_stream_name(streamFile,sb.stream_name); + streamData = open_streamfile_by_filename(streamFile,sb.stream_name); if (!streamData) { streamData = streamFile; /* assume internal */ if (sb.stream_size > get_streamfile_size(streamData)) { @@ -102,7 +102,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { } } else if (sb.is_external) { - streamData = open_stream_name(streamFile,sb.stream_name); + streamData = open_streamfile_by_filename(streamFile,sb.stream_name); if (!streamData) { VGM_LOG("UBI SB: external stream '%s' not found\n", sb.stream_name); goto fail; @@ -674,7 +674,7 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) /* two games with same id; use project file as identifier */ if (sb->version == 0x0012000C && is_sb4) { - STREAMFILE * streamTest = open_stream_name(streamFile, "BIAAUDIO.SP4"); + STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP4"); if (streamTest) { is_biadd_psp = 1; close_streamfile(streamTest); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vs.c b/Frameworks/vgmstream/vgmstream/src/meta/vs.c index 9fe5ec34b..0e7aa209a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vs.c @@ -42,7 +42,7 @@ VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile) { } #endif - vgmstream->layout_type = layout_vs_blocked; + vgmstream->layout_type = layout_blocked_vs; vgmstream->meta_type = meta_VS; @@ -55,15 +55,15 @@ VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile) { } /* Calc num_samples */ - vs_block_update(start_offset,vgmstream); + block_update_vs(start_offset,vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size*28/16; - vs_block_update(vgmstream->next_block_offset,vgmstream); + block_update_vs(vgmstream->next_block_offset,vgmstream); } while (vgmstream->next_block_offsetget_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vsf",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile, "vsf")) + goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x56534600) /* "VSF" */ goto fail; loop_flag = (read_32bitLE(0x1c,streamFile)==0x13); - if(read_32bitLE(0x8,streamFile)==0x0) - channel_count = 1; - else - channel_count = 2; + if(read_8bit(0x1C,streamFile)==0x0) + channel_count = 1; + else + channel_count = 2; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); @@ -44,25 +41,12 @@ VGMSTREAM * init_vgmstream_ps2_vsf(STREAMFILE *streamFile) { vgmstream->meta_type = meta_PS2_VSF; /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; return vgmstream; /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/waa_wac_wad_wam.c b/Frameworks/vgmstream/vgmstream/src/meta/waa_wac_wad_wam.c deleted file mode 100644 index 0b7b50d4e..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/waa_wac_wad_wam.c +++ /dev/null @@ -1,182 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* -const short wad_coef[16][2] = -{ - {0x4002,0x2003}, - {0x2016,0xc600}, - {0xC600,0x98ab}, - {0x96bf,0x29c5}, - {0x2003,0x0081}, - {0x0e00,0x2004}, - {0x8e01,0xc500}, - {0x70bf,0x8128}, - {0x288e,0xc600}, - {0x016e,0x0e5b}, - {0xbe20,0x2003}, - {0x03c6,0xc600}, - {0x0048,0xe85a}, - {0xbe28,0x28c6}, - {0xc600,0x00F6}, - {0xbeab,0x5520} -};*/ -const short wad_coef[16] = -{ - 0x04ab, 0xfced, - 0x0789, 0xfedf, - 0x09a2, 0xfae5, - 0x0c90, 0xfac1, - 0x084d, 0xfaa4, - 0x0982, 0xfdf7, - 0x0af6, 0xfafa, - 0x0be6, 0xfbf5 -}; - - -/* WAC/WAD/WAM/WAA - from Beyond Good & Evil (PS2/Xbox/GC/Wii) */ -VGMSTREAM * init_vgmstream_waa_wac_wad_wam(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int i; - off_t start_offset; - int loop_flag; - int channel_count; - int coef1_start; - int coef2_start; - int second_channel_start = -1; - - // Check file extensions - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("waa",filename_extension(filename)) && - strcasecmp("wac",filename_extension(filename)) && - strcasecmp("wad",filename_extension(filename)) && - strcasecmp("wam",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */ - read_32bitBE(0x08,streamFile) != 0x57415645 || /* "WAVE" */ - read_32bitBE(0x0C,streamFile) != 0x666D7420 || /* "fmt " */ - read_32bitBE(0x10,streamFile) != 0x12000000) /* "0x12000000" */ - goto fail; - - /* files don't contain looping information, - so the looping is not done depending on extension. - wam and waa contain ambient sounds and music, so often they contain - looped music. Change extension to wac or wad to make the sound non-looping. - */ - loop_flag = strcasecmp("wac",filename_extension(filename)) && - strcasecmp("wad",filename_extension(filename)); - channel_count = (uint16_t)read_16bitLE(0x16,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* Check what encoder is needed */ - //FIXME: //PC version uses pcm, but which encoder? - - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - vgmstream->meta_type = meta_WAA_WAC_WAD_WAM; - vgmstream->layout_type = layout_none; - - switch((uint16_t)read_16bitLE(0x14,streamFile)) { - case 0x0069: // XBOX IMA ADPCM - start_offset = 0x2E; - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitLE(0x2A,streamFile), channel_count); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(read_32bitLE(0x2A,streamFile),channel_count); - } - break; - case 0xFFFF: // PS2 ADPCM - start_offset = 0x2E; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x2A,streamFile))/16*28/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitLE(0x2A,streamFile))/16*28/channel_count; - } - second_channel_start = (read_32bitLE(0x2A,streamFile)/2)+start_offset; - break; - case 0xFFFE: // GameCube/WII DSP - start_offset = 0x5C; - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = (read_32bitLE(0x2A,streamFile))*14/8/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitLE(0x2A,streamFile))*14/8/channel_count; - } - if(read_16bitLE(0x24,streamFile)==0x00)//is a wii file with no coeff table - { - //FIXME: WII version of WAM/WAD/WAC need some coeff table from somewhere - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = wad_coef[i]; - if (channel_count == 2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = wad_coef[i]; - } - goto fail; - } - else - { - second_channel_start = (read_32bitLE(0x2A,streamFile)/2)+0x8A; - /* Retrieveing the coef tables */ - coef1_start = 0x2E; - coef2_start = (read_32bitLE(0x2A,streamFile)/2)+0x5C; - - { - int i; - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(coef1_start+i*2,streamFile); - if (channel_count == 2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(coef2_start+i*2,streamFile); - } - } - } - break; - default: - goto fail; - } - - - - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - if (vgmstream->coding_type == coding_XBOX_IMA) { - /* xbox interleaving is a little odd */ - vgmstream->ch[i].channel_start_offset=start_offset; - } else { - vgmstream->ch[0].channel_start_offset=start_offset; - if (channel_count == 2) { - if (second_channel_start == -1) goto fail; - vgmstream->ch[1].channel_start_offset=second_channel_start; - } - } - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; - } - } - - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - - - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/waf.c b/Frameworks/vgmstream/vgmstream/src/meta/waf.c new file mode 100644 index 000000000..dbb944eda --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/waf.c @@ -0,0 +1,43 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* WAF - KID's earlier PC games [ever17 (PC)] (for RLE-compressed WAFs see https://github.com/dsp2003/e17p) */ +VGMSTREAM * init_vgmstream_waf(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "waf")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x57414600) /* "WAF\0" "*/ + goto fail; + if (read_32bitLE(0x34,streamFile) + 0x38 != get_streamfile_size(streamFile)) + goto fail; + + channel_count = read_16bitLE(0x06,streamFile); + loop_flag = 0; + start_offset = 0x38; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x08, streamFile); + vgmstream->meta_type = meta_WAF; + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = read_16bitLE(0x10, streamFile); + vgmstream->num_samples = msadpcm_bytes_to_samples(read_32bitLE(0x34,streamFile), vgmstream->interleave_block_size, channel_count); + /* 0x04: null?, 0x0c: avg br, 0x12: bps, 0x14: s_p_f, 0x16~34: count + standard MSADPCM coefs (a modified RIFF fmt) */ + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wave.c b/Frameworks/vgmstream/vgmstream/src/meta/wave.c new file mode 100644 index 000000000..fe6c69453 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wave.c @@ -0,0 +1,108 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .WAVE - WayForward "EngineBlack" games [Mighty Switch Force! (3DS), Adventure Time: Hey Ice King! Why'd You Steal Our Garbage?! (3DS)] */ +VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, extradata_offset; + int loop_flag = 0, channel_count, sample_rate, codec; + int32_t num_samples, loop_start = 0, loop_end = 0; + size_t interleave; + + int big_endian; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + //int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + /* checks */ + if (!check_extensions(streamFile, "wave")) + goto fail; + + if (read_32bitLE(0x00,streamFile) != 0xE5B7ECFE && /* header id */ + read_32bitBE(0x00,streamFile) != 0xE5B7ECFE) + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x00) /* version? */ + goto fail; + + /* assumed */ + big_endian = read_32bitBE(0x00,streamFile) == 0xE5B7ECFE; + if (big_endian) { + read_32bit = read_32bitBE; + //read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + //read_16bit = read_16bitLE; + } + + channel_count = read_8bit(0x05,streamFile); + + if (read_32bit(0x08,streamFile) != get_streamfile_size(streamFile)) + goto fail; + if (read_8bit(0x0c,streamFile) != 0x00) /* ? */ + goto fail; + + /* sample rate in 32b float (WHY?)*/ + { + uint32_t sample_int = (uint32_t)read_32bit(0x0c, streamFile); + float* sample_float; + sample_float = (float*)&sample_int; + + sample_rate = (int)(*sample_float); + } + + num_samples = read_32bit(0x10, streamFile); + loop_start = read_32bit(0x14, streamFile); + loop_end = read_32bit(0x18, streamFile); + + codec = read_8bit(0x1c, streamFile); + channel_count = read_8bit(0x1d, streamFile); + if (read_8bit(0x1e, streamFile) != 0x00) goto fail; /* unknown */ + if (read_8bit(0x1f, streamFile) != 0x00) goto fail; /* unknown */ + + start_offset = read_32bit(0x20, streamFile); + interleave = read_32bit(0x24, streamFile); /* typically half data_size */ + extradata_offset = read_32bit(0x28, streamFile); /* OR: extradata size (0x2c) */ + + loop_flag = (loop_start > 0); + /* some songs (ex. Adventure Time's m_candykingdom_overworld.wave) do full loops, but there is no way + * to tell them apart from sfx/voices, so we try to detect if it's long enough. */ + if(!loop_flag + && loop_start == 0 && loop_end == num_samples /* full loop */ + && channel_count > 1 + && num_samples > 20*sample_rate) { /* in seconds */ + loop_flag = 1; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->meta_type = meta_WAVE; + /* not sure if there are other codecs but anyway */ + switch(codec) { + case 0x02: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + /* ADPCM setup: 0x20 coefs + 0x06 initial ps/hist1/hist2 + 0x06 loop ps/hist1/hist2, per channel */ + dsp_read_coefs(vgmstream, streamFile, extradata_offset+0x00, 0x2c, big_endian); + dsp_read_hist(vgmstream, streamFile, extradata_offset+0x22, 0x2c, big_endian); + break; + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wave_segmented.c b/Frameworks/vgmstream/vgmstream/src/meta/wave_segmented.c new file mode 100644 index 000000000..a4af548c3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wave_segmented.c @@ -0,0 +1,219 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + +#define MAX_SEGMENTS 4 + +/* .WAVE - "EngineBlack" games, segmented [Shantae and the Pirate's Curse (PC/3DS), TMNT: Danger of the Ooze (PS3/3DS)] */ +VGMSTREAM * init_vgmstream_wave_segmented(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t segments_offset; + int loop_flag = 0, channel_count, sample_rate; + int32_t num_samples, loop_start_sample = 0, loop_end_sample = 0; + + segmented_layout_data *data = NULL; + int segment_count, loop_start_segment = 0, loop_end_segment = 0; + + int big_endian; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + + /* checks */ + if (!check_extensions(streamFile, "wave")) + goto fail; + + if (read_32bitLE(0x00,streamFile) != 0x4DF72D4A && /* header id */ + read_32bitBE(0x00,streamFile) != 0x4DF72D4A) + goto fail; + if (read_8bit(0x04,streamFile) != 0x01) /* version? */ + goto fail; + + /* PS3/X360 games */ + big_endian = read_32bitBE(0x00,streamFile) == 0x4DF72D4A; + if (big_endian) { + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + channel_count = read_8bit(0x05,streamFile); + segment_count = read_16bit(0x06,streamFile); + if (segment_count > MAX_SEGMENTS || segment_count <= 0) goto fail; + + loop_start_segment = read_16bit(0x08, streamFile); + loop_end_segment = read_16bit(0x0a, streamFile); + segments_offset = read_32bit(0x0c, streamFile); + + sample_rate = read_32bit(0x10, streamFile); + num_samples = read_32bit(0x14, streamFile); + /* 0x18: unknown (usually 0, maybe some count) */ + + + /* init layout */ + data = init_layout_segmented(segment_count); + if (!data) goto fail; + + /* parse segments (usually: preload + intro + loop + ending, intro/ending may be skipped) + * Often first segment is ADPCM and rest Ogg; may only have one segment. */ + { + off_t extradata_offset, table_offset, segment_offset; + size_t segment_size; + int32_t segment_samples; + int codec; + int i, ch; + + /* open each segment subfile */ + for (i = 0; i < segment_count; i++) { + codec = read_8bit(segments_offset+0x10*i+0x00, streamFile); + /* 0x01(1): unknown (flag? usually 0x00/0x01/0x02) */ + if (read_8bit(segments_offset+0x10*i+0x02, streamFile) != 0x01) goto fail; /* unknown */ + if (read_8bit(segments_offset+0x10*i+0x03, streamFile) != 0x00) goto fail; /* unknown */ + + segment_samples = read_32bit(segments_offset+0x10*i+0x04, streamFile); + extradata_offset = read_32bit(segments_offset+0x10*i+0x08, streamFile); + table_offset = read_32bit(segments_offset+0x10*i+0x0c, streamFile); + + /* create a sub-VGMSTREAM per segment + * (we'll reopen this streamFile as needed, so each sub-VGMSTREAM is fully independent) */ + switch(codec) { + case 0x02: { /* "adpcm" */ + data->segments[i] = allocate_vgmstream(channel_count, 0); + if (!data->segments[i]) goto fail; + + data->segments[i]->sample_rate = sample_rate; + data->segments[i]->meta_type = meta_WAVE; + data->segments[i]->coding_type = coding_IMA_int; + data->segments[i]->layout_type = layout_none; + data->segments[i]->num_samples = segment_samples; + + if (!vgmstream_open_stream(data->segments[i],streamFile,0x00)) + goto fail; + + /* bizarrely enough channel data isn't sequential (segment0 ch1+ may go after all other segments) */ + for (ch = 0; ch < channel_count; ch++) { + segment_offset = read_32bit(table_offset + 0x04*ch, streamFile); + data->segments[i]->ch[ch].channel_start_offset = + data->segments[i]->ch[ch].offset = segment_offset; + + /* ADPCM setup */ + data->segments[i]->ch[ch].adpcm_history1_32 = read_16bit(extradata_offset+0x04*ch+0x00, streamFile); + data->segments[i]->ch[ch].adpcm_step_index = read_8bit(extradata_offset+0x04*ch+0x02, streamFile); + /* 0x03: reserved */ + } + + break; + } + + case 0x03: { /* "dsp-adpcm" */ + data->segments[i] = allocate_vgmstream(channel_count, 0); + if (!data->segments[i]) goto fail; + + data->segments[i]->sample_rate = sample_rate; + data->segments[i]->meta_type = meta_WAVE; + data->segments[i]->coding_type = coding_NGC_DSP; + data->segments[i]->layout_type = layout_none; + data->segments[i]->num_samples = segment_samples; + + if (!vgmstream_open_stream(data->segments[i],streamFile,0x00)) + goto fail; + + /* bizarrely enough channel data isn't sequential (segment0 ch1+ may go after all other segments) */ + for (ch = 0; ch < channel_count; ch++) { + segment_offset = read_32bit(table_offset + 0x04*ch, streamFile); + data->segments[i]->ch[ch].channel_start_offset = + data->segments[i]->ch[ch].offset = segment_offset; + } + + /* ADPCM setup: 0x06 initial ps/hist1/hist2 (per channel) + 0x20 coefs (per channel) */ + dsp_read_hist(data->segments[i], streamFile, extradata_offset+0x02, 0x06, big_endian); + dsp_read_coefs(data->segments[i], streamFile, extradata_offset+0x06*channel_count+0x00, 0x20, big_endian); + + break; + } + + case 0x04: { /* "vorbis" */ + ogg_vorbis_meta_info_t ovmi = {0}; + + segment_offset = read_32bit(table_offset, streamFile); + segment_size = read_32bitBE(segment_offset, streamFile); /* always BE */ + + ovmi.meta_type = meta_WAVE; + ovmi.stream_size = segment_size; + + data->segments[i] = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, segment_offset+0x04, &ovmi); + if (!data->segments[i]) goto fail; + + if (data->segments[i]->num_samples != segment_samples) { + VGM_LOG("WAVE: segment %i samples != num_samples\n", i); + goto fail; + } + + break; + } + + default: /* others: s16be/s16le/mp3 as referenced in the exe? */ + VGM_LOG("WAVE: unknown codec\n"); + goto fail; + } + } + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + + /* parse samples */ + { + int32_t sample_count = 0; + int i; + + loop_flag = (loop_start_segment > 0); + + for (i = 0; i < segment_count; i++) { + if (loop_flag && loop_start_segment == i) { + loop_start_sample = sample_count; + } + + sample_count += data->segments[i]->num_samples; + + if (loop_flag && loop_end_segment-1 == i) { + loop_end_sample = sample_count; + } + } + + if (sample_count != num_samples) { + VGM_LOG("WAVE: total segments samples %i != num_samples %i\n", sample_count, num_samples); + goto fail; + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + + vgmstream->meta_type = meta_WAVE_segmented; + /* .wave can mix codecs, usually first segment is a small ADPCM section) */ + vgmstream->coding_type = (segment_count == 1 ? data->segments[0]->coding_type : data->segments[1]->coding_type); + vgmstream->layout_type = layout_segmented; + + vgmstream->layout_data = data; + if (loop_flag) + data->loop_segment = (loop_start_segment); + + return vgmstream; + +fail: + free_layout_segmented(data); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wii_04sw.c b/Frameworks/vgmstream/vgmstream/src/meta/wii_04sw.c index 845b6fa6d..e1559ca98 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wii_04sw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wii_04sw.c @@ -9,12 +9,10 @@ VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE *streamFile) { size_t file_size, data_size; - /* check extension, case insensitive */ + /* checks */ /* ".04sw" is just the ID, the real filename inside the file uses .XA */ if (!check_extensions(streamFile,"xa,04sw")) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x30345357) /* "04SW" */ goto fail; @@ -39,9 +37,9 @@ VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE *streamFile) { vgmstream->num_samples = read_32bitBE(0x04,streamFile); vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave_shortblock; + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x8000; - vgmstream->interleave_smallblock_size = (read_32bitBE(0x08,streamFile) / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; + vgmstream->interleave_last_block_size = (read_32bitBE(0x08,streamFile) / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; dsp_read_coefs_be(vgmstream,streamFile,0x20, 0x60); /* the initial history offset seems different thatn standard DSP and possibly always zero */ @@ -50,10 +48,8 @@ VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE *streamFile) { /* the rest of the header has unknown values (several repeats) and the filename */ - /* open the file for reading */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wii_ras.c b/Frameworks/vgmstream/vgmstream/src/meta/wii_ras.c index d84bc94b2..fe3c0d8bc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wii_ras.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wii_ras.c @@ -1,20 +1,17 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* RAS (from Donkey Kong Country Returns) */ +/* RAS_ - from Donkey Kong Country Returns (Wii) */ VGMSTREAM * init_vgmstream_wii_ras(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("ras",filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "ras")) + goto fail; - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x5241535F) /* "RAS_" */ goto fail; @@ -26,63 +23,32 @@ VGMSTREAM * init_vgmstream_wii_ras(STREAMFILE *streamFile) { loop_flag = 1; } channel_count = 2; + start_offset = read_32bitBE(0x18,streamFile); - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = read_32bitBE(0x18,streamFile); - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x14,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitBE(0x1c,streamFile)/channel_count/8*14; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x20,streamFile); vgmstream->meta_type = meta_WII_RAS; - if (loop_flag) { - // loop is block + samples into block - vgmstream->loop_start_sample = - read_32bitBE(0x30,streamFile)*vgmstream->interleave_block_size/8*14 + - read_32bitBE(0x34,streamFile); - vgmstream->loop_end_sample = - read_32bitBE(0x38,streamFile)*vgmstream->interleave_block_size/8*14 + - read_32bitBE(0x3C,streamFile); + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0x20,streamFile); + + vgmstream->num_samples = read_32bitBE(0x1c,streamFile)/channel_count/8*14; + if (loop_flag) { /* loop is block + samples into block */ + vgmstream->loop_start_sample = read_32bitBE(0x30,streamFile)*vgmstream->interleave_block_size/8*14 + + read_32bitBE(0x34,streamFile); + vgmstream->loop_end_sample = read_32bitBE(0x38,streamFile)*vgmstream->interleave_block_size/8*14 + + read_32bitBE(0x3C,streamFile); } - if (vgmstream->coding_type == coding_NGC_DSP) { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x40+i*2,streamFile); - } - if (channel_count == 2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x70+i*2,streamFile); - } - } else { + dsp_read_coefs_be(vgmstream,streamFile,0x40,0x30); + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - } - - /* open the file for reading */ - { - int i; - for (i=0;ilayout_type==layout_interleave_shortblock) - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename, - vgmstream->interleave_block_size); - else - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename, - 0x1000); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - start_offset + i*vgmstream->interleave_block_size; - } - } - return vgmstream; /* clean up anything we may have opened */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ws_aud.c b/Frameworks/vgmstream/vgmstream/src/meta/ws_aud.c index cd13606c4..41ae998af 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ws_aud.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ws_aud.c @@ -100,7 +100,7 @@ VGMSTREAM * init_vgmstream_ws_aud(STREAMFILE *streamFile) { vgmstream->meta_type = meta_WS_AUD_old; } - vgmstream->layout_type = layout_ws_aud_blocked; + vgmstream->layout_type = layout_blocked_ws_aud; /* open the file for reading by each channel */ { @@ -118,9 +118,9 @@ VGMSTREAM * init_vgmstream_ws_aud(STREAMFILE *streamFile) { /* start me up */ if (new_type) { - ws_aud_block_update(0xc,vgmstream); + block_update_ws_aud(0xc,vgmstream); } else { - ws_aud_block_update(0x8,vgmstream); + block_update_ws_aud(0x8,vgmstream); } return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 8cd588f8d..53d1e3e3d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -78,6 +78,18 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } #endif + /* ignore LyN RIFF */ + { + off_t fact_offset; + size_t fact_size; + + if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */ + if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */ + goto fail; /* parsed elsewhere */ + /* Wwise doesn't use "fact", though */ + } + } + /* parse format (roughly spec-compliant but some massaging is needed) */ { @@ -123,9 +135,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { //todo fix repeat looping } } - else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */ - //todo parse "adtl" (does it ever contain loop info in Wwise?) - } + //else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */ + // /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */ + //} /* other Wwise specific: */ //"JUNK": optional padding for usually aligment (0-size JUNK exists too) @@ -194,15 +206,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { break; case IMA: /* common */ - /* slightly modified XBOX-IMA with interleaved sub-blocks and LE/BE header */ - - /* Wwise uses common codecs (ex. 0x0002 MSADPCM) so this parser should go AFTER riff.c avoid misdetection */ + /* slightly modified XBOX-IMA */ + /* Wwise reuses common codec ids (ex. 0x0002 MSADPCM) for IMA so this parser should go AFTER riff.c avoid misdetection */ if (ww.bits_per_sample != 4) goto fail; if (ww.block_align != 0x24 * ww.channels) goto fail; vgmstream->coding_type = coding_WWISE_IMA; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = ww.block_align; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = ww.block_align / ww.channels; vgmstream->codec_endian = ww.big_endian; if (ww.truncated) /* enough to get real samples */ @@ -230,8 +241,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* older Wwise (~<2012) */ switch(vorb_size) { - //case 0x2C: /* early (~2009), some EVE Online Apocrypha files? */ - case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest PC */ + case 0x2C: /* earliest (~2009), ex. UFC Undisputed 2009 (PS3), some EVE Online Apocrypha files? */ + case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest (PC) */ data_offsets = 0x18; block_offsets = 0; /* no need, full headers are present */ cfg.header_type = WWV_TYPE_8; @@ -452,7 +463,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { vgmstream->num_samples = msd.num_samples; if (!vgmstream->num_samples) - vgmstream->num_samples = ffmpeg_data->totalSamples; /* very wrong, from avg-br */ + vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* very wrong, from avg-br */ //num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2 } @@ -471,7 +482,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = ffmpeg_data->totalSamples; + vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; break; } @@ -590,15 +601,15 @@ fail: 0x31 (1): blocksize_0_exp (large) 0x32 (2): empty -"vorb" size 0x28 / 0x2a +"vorb" size 0x28 / 0x2c / 0x2a 0x00 (4): num_samples 0x04 (4): data start offset after seek table+setup, or loop start when "smpl" is present 0x08 (4): data end offset after seek table (setup+packets), or loop end when "smpl" is present -0x0c (2): ? (small, 0..~0x400) +0x0c (2): ? (small, 0..~0x400) [(4) when size is 0x2C] 0x10 (4): setup_offset within data (0 = no seek table) 0x14 (4): audio_offset within data 0x18 (2): biggest packet size (not including header)? -0x1a (2): ? (small, N..~0x100) uLastGranuleExtra? +0x1a (2): ? (small, N..~0x100) uLastGranuleExtra? [(4) when size is 0x2C] 0x1c (4): ? (mid, 0~0x5000) dwDecodeAllocSize? 0x20 (4): ? (mid, 0~0x5000) dwDecodeX64AllocSize? 0x24 (4): parent bank/event id? uHashCodebook? (shared by several .wem a game, but not all need to share it) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/x360_tra.c b/Frameworks/vgmstream/vgmstream/src/meta/x360_tra.c index b94189113..8554076cd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/x360_tra.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/x360_tra.c @@ -38,7 +38,7 @@ VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE *streamFile) { vgmstream->coding_type = coding_DVI_IMA_int; vgmstream->num_samples = (int32_t)(get_streamfile_size(streamFile) - ((get_streamfile_size(streamFile)/0x204)*4)); - vgmstream->layout_type = layout_tra_blocked; + vgmstream->layout_type = layout_blocked_tra; vgmstream->meta_type = meta_X360_TRA; @@ -51,7 +51,7 @@ VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE *streamFile) { } } - tra_block_update(0,vgmstream); + block_update_tra(0,vgmstream); return vgmstream; /* clean up anything we may have opened */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xbox_ims.c b/Frameworks/vgmstream/vgmstream/src/meta/xbox_ims.c index 89aeef604..220eeac13 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xbox_ims.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xbox_ims.c @@ -31,7 +31,7 @@ VGMSTREAM * init_vgmstream_xbox_matx(STREAMFILE *streamFile) { vgmstream->sample_rate = read_16bitLE(0x06,streamFile) & 0xffff; vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_matx_blocked; + vgmstream->layout_type = layout_blocked_matx; vgmstream->meta_type = meta_XBOX_MATX; /* open the file for reading by each channel */ @@ -43,15 +43,15 @@ VGMSTREAM * init_vgmstream_xbox_matx(STREAMFILE *streamFile) { } /* Calc num_samples */ - matx_block_update(0,vgmstream); + block_update_matx(0,vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size/36*64; - matx_block_update(vgmstream->next_block_offset,vgmstream); + block_update_matx(vgmstream->next_block_offset,vgmstream); } while (vgmstream->next_block_offsetcoding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_xvas_blocked; + vgmstream->layout_type = layout_blocked_xvas; vgmstream->meta_type = meta_XBOX_XVAS; if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; - xvas_block_update(start_offset,vgmstream); + block_update_xvas(start_offset,vgmstream); return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xbox_xwav.c b/Frameworks/vgmstream/vgmstream/src/meta/xbox_xwav.c deleted file mode 100644 index 735a11203..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/xbox_xwav.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* XWAV - renamed WAV with XBOX-IMA - * (could be parsed as RIFF/.lwav but has a custom loop chunk and multichannel) */ -VGMSTREAM * init_vgmstream_xbox_xwav(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - int loop_flag, channel_count; - off_t start_offset; - - /* check extension */ - if (!check_extensions(streamFile,"xwav")) - goto fail; - - /* check for headers */ - if(!((read_32bitBE(0x00,streamFile) == 0x52494646) && /* "RIFF" */ - (read_32bitBE(0x08,streamFile) == 0x57415645) && /* "WAVE" */ - (read_32bitBE(0x0C,streamFile) == 0x666D7420) && /* "fmt " */ - (read_16bitLE(0x14,streamFile) == 0x0069))) /* codec */ - goto fail; - - /* loop chunk found on Koei/Omega Force games [Crimson Sea, Dynasty Warriors 5] */ - loop_flag = (read_32bitBE(0x28,streamFile) == 0x77736D70); /* "wsmp" */ - channel_count = read_16bitLE(0x16,streamFile); - - /* search for "data" */ - start_offset = 0x1C; - do { - if (read_32bitBE(start_offset,streamFile)==0x64617461) - break; - start_offset += 0x04; - } while (start_offset < (off_t)get_streamfile_size(streamFile)); - - if (start_offset >= (off_t)get_streamfile_size(streamFile)) - goto fail; - start_offset += 0x04; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitLE(start_offset,streamFile), vgmstream->channels); - vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x4C,streamFile); - vgmstream->loop_end_sample = vgmstream->loop_start_sample + read_32bitLE(0x50,streamFile); - } - - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_XBOX_RIFF; - - //if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - // goto fail; - - //custom init - { - int i, ch; - char filename[PATH_LIMIT]; - streamFile->get_name(streamFile,filename,sizeof(filename)); - - if (channel_count > 2) { /* multichannel interleaved init */ - for (i=0, ch=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x24); - vgmstream->ch[i].offset = start_offset + 0x04; - - if (!vgmstream->ch[i].streamfile) goto fail; - } - } - else { - for (i=0; i < channel_count; i++) { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x24); - vgmstream->ch[i].offset = start_offset + 0x04; - - if (!vgmstream->ch[i].streamfile) goto fail; - } - } - } - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xnb.c b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c index 98aac0989..c20efb11f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xnb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c @@ -41,7 +41,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { { char reader_name[255+1]; off_t current_offset = 0x0a; - int reader_string_len; + size_t reader_string_len; uint32_t fmt_chunk_size; const char * type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */ //const char * type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */ @@ -76,6 +76,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { codec = read_16bit(current_offset+0x00, streamFile); channel_count = read_16bit(current_offset+0x02, streamFile); sample_rate = read_32bit(current_offset+0x04, streamFile); + /* 0x08: byte rate */ block_size = read_16bit(current_offset+0x0c, streamFile); bps = read_16bit(current_offset+0x0e, streamFile); @@ -102,6 +103,10 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { switch (codec) { case 0x01: /* Dragon's Blade (Android) */ + /* null in Metagalactic Blitz (PC) */ + if (!block_size) + block_size = (bps == 8 ? 0x01 : 0x02) * channel_count; + vgmstream->coding_type = bps == 8 ? coding_PCM8_U_int : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = block_size / channel_count; @@ -109,6 +114,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { break; case 0x02: /* White Noise Online (PC) */ + if (!block_size) goto fail; vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = block_size; @@ -116,6 +122,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { break; case 0x11: + if (!block_size) goto fail; vgmstream->coding_type = coding_MS_IMA; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = block_size; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 383b31cc6..8c104f465 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -35,6 +35,11 @@ typedef struct { size_t base_size; off_t entry_offset; size_t entry_size; + off_t names_offset; + size_t names_size; + size_t names_entry_size; + off_t extra_offset; + size_t extra_size; off_t data_offset; size_t data_size; @@ -44,7 +49,7 @@ typedef struct { uint32_t base_flags; size_t entry_elem_size; size_t entry_alignment; - int streams; + int total_subsongs; uint32_t entry_flags; uint32_t format; @@ -63,27 +68,25 @@ typedef struct { uint32_t loop_end_sample; } xwb_header; -static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_header * xwb, STREAMFILE *streamFile); +static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile); /* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, off, suboff; - xwb_header xwb; - int target_stream = streamFile->stream_index; + xwb_header xwb = {0}; + int target_subsong = streamFile->stream_index; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - /* basic checks */ - if (!check_extensions(streamFile,"xwb")) goto fail; - + /* checks */ + if (!check_extensions(streamFile,"xwb")) + goto fail; if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */ (read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */ goto fail; - memset(&xwb,0,sizeof(xwb_header)); - xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44;/* WBND */ if (xwb.little_endian) { read_32bit = read_32bitLE; @@ -101,13 +104,21 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* read segment offsets (SEGIDX) */ if (xwb.version <= XACT1_0_MAX) { - xwb.streams = read_32bit(0x0c, streamFile); - /* 0x10: bank name */ + xwb.total_subsongs = read_32bit(0x0c, streamFile); + /* 0x10: bank name (size 0x10) */ + xwb.base_offset = 0; + xwb.base_size = 0; + xwb.entry_offset = 0x50; + xwb.entry_size = xwb.entry_elem_size * xwb.total_subsongs; xwb.entry_elem_size = 0x14; - xwb.entry_offset= 0x50; - xwb.entry_size = xwb.entry_elem_size * xwb.streams; - xwb.data_offset = xwb.entry_offset + xwb.entry_size; - xwb.data_size = get_streamfile_size(streamFile) - xwb.data_offset; + xwb.data_offset = xwb.entry_offset + xwb.entry_size; + xwb.data_size = get_streamfile_size(streamFile) - xwb.data_offset; + + xwb.names_offset = 0; + xwb.names_size = 0; + xwb.names_entry_size= 0; + xwb.extra_offset = 0; + xwb.extra_size = 0; } else { off = xwb.version <= XACT2_2_MAX ? 0x08 : 0x0c; @@ -115,10 +126,32 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { xwb.base_size = read_32bit(off+0x04, streamFile); xwb.entry_offset= read_32bit(off+0x08, streamFile);//ENTRYMETADATA xwb.entry_size = read_32bit(off+0x0c, streamFile); - /* go to last segment (XACT2/3 have 5 segments, XACT1 4) */ - //0x10: XACT1/2: ENTRYNAMES, XACT3: SEEKTABLES - //0x14: XACT1: none (ENTRYWAVEDATA), XACT2: EXTRA, XACT3: ENTRYNAMES - suboff = xwb.version <= XACT1_1_MAX ? 0x08 : 0x08+0x08; + + /* read extra segments (values can be 0 == no segment) */ + if (xwb.version <= XACT1_1_MAX) { + xwb.names_offset = read_32bit(off+0x10, streamFile);//ENTRYNAMES + xwb.names_size = read_32bit(off+0x14, streamFile); + xwb.names_entry_size= 0x40; + xwb.extra_offset = 0; + xwb.extra_size = 0; + suboff = 0x04*2; + } + else if (xwb.version <= XACT2_1_MAX) { + xwb.names_offset = read_32bit(off+0x10, streamFile);//ENTRYNAMES + xwb.names_size = read_32bit(off+0x14, streamFile); + xwb.names_entry_size= 0x40; + xwb.extra_offset = read_32bit(off+0x18, streamFile);//EXTRA + xwb.extra_size = read_32bit(off+0x1c, streamFile); + suboff = 0x04*2 + 0x04*2; + } else { + xwb.extra_offset = read_32bit(off+0x10, streamFile);//SEEKTABLES + xwb.extra_size = read_32bit(off+0x14, streamFile); + xwb.names_offset = read_32bit(off+0x18, streamFile);//ENTRYNAMES + xwb.names_size = read_32bit(off+0x1c, streamFile); + xwb.names_entry_size= 0x40; + suboff = 0x04*2 + 0x04*2; + } + xwb.data_offset = read_32bit(off+0x10+suboff, streamFile);//ENTRYWAVEDATA xwb.data_size = read_32bit(off+0x14+suboff, streamFile); @@ -128,8 +161,8 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* read base entry (WAVEBANKDATA) */ off = xwb.base_offset; xwb.base_flags = (uint32_t)read_32bit(off+0x00, streamFile); - xwb.streams = read_32bit(off+0x04, streamFile); - /* 0x08 bank_name */ + xwb.total_subsongs = read_32bit(off+0x04, streamFile); + /* 0x08: bank name (size 0x40) */ suboff = 0x08 + (xwb.version <= XACT1_1_MAX ? 0x10 : 0x40); xwb.entry_elem_size = read_32bit(off+suboff+0x00, streamFile); /* suboff+0x04: meta name entry size */ @@ -138,26 +171,32 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* suboff+0x10: build time 64b (XACT2/3) */ } - if (target_stream == 0) target_stream = 1; /* auto: default to 1 */ - if (target_stream < 0 || target_stream > xwb.streams || xwb.streams < 1) goto fail; + if (target_subsong == 0) target_subsong = 1; /* auto: default to 1 */ + if (target_subsong < 0 || target_subsong > xwb.total_subsongs || xwb.total_subsongs < 1) goto fail; /* read stream entry (WAVEBANKENTRY) */ - off = xwb.entry_offset + (target_stream-1) * xwb.entry_elem_size; + off = xwb.entry_offset + (target_subsong-1) * xwb.entry_elem_size; - if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { /* compact entry */ - /* offset_in_sectors:21 and sector_alignment_in_bytes:11 */ - uint32_t entry = (uint32_t)read_32bit(off+0x00, streamFile); - xwb.stream_offset = xwb.data_offset + (entry >> 11) * xwb.entry_alignment + (entry & 0x7FF); + if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { /* compact entry [NFL Fever 2004 demo from Amped 2 (Xbox)] */ + uint32_t entry, size_deviation, sector_offset; + off_t next_stream_offset; - /* find size (up to next entry or data end) */ - if (xwb.streams > 1) { - entry = (uint32_t)read_32bit(off+xwb.entry_size, streamFile); - xwb.stream_size = xwb.stream_offset - - (xwb.data_offset + (entry >> 11) * xwb.entry_alignment + (entry & 0x7FF)); - } else { - xwb.stream_size = xwb.data_size; + entry = (uint32_t)read_32bit(off+0x00, streamFile); + size_deviation = ((entry >> 21) & 0x7FF); /* 11b, padding data for sector alignment in bytes*/ + sector_offset = (entry & 0x1FFFFF); /* 21b, offset within data in sectors */ + + xwb.stream_offset = xwb.data_offset + sector_offset*xwb.entry_alignment; + + /* find size using next offset */ + if (target_subsong < xwb.total_subsongs) { + uint32_t next_entry = (uint32_t)read_32bit(off+0x04, streamFile); + next_stream_offset = xwb.data_offset + (next_entry & 0x1FFFFF)*xwb.entry_alignment; } + else { /* for last entry (or first, when subsongs = 1) */ + next_stream_offset = xwb.data_offset + xwb.data_size; + } + xwb.stream_size = next_stream_offset - xwb.stream_offset - size_deviation; } else if (xwb.version <= XACT1_0_MAX) { xwb.format = (uint32_t)read_32bit(off+0x00, streamFile); @@ -311,8 +350,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { else if (xwb.version <= XACT2_1_MAX && (xwb.codec == XMA1 || xwb.codec == XMA2) && xwb.loop_flag) { /* v38: byte offset, v40+: sample offset, v39: ? */ /* need to manually find sample offsets, thanks to Microsoft dumb headers */ - ms_sample_data msd; - memset(&msd,0,sizeof(ms_sample_data)); + ms_sample_data msd = {0}; msd.xma_version = xwb.codec == XMA1 ? 1 : 2; msd.channels = xwb.channels; @@ -353,10 +391,10 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { vgmstream->num_samples = xwb.num_samples; vgmstream->loop_start_sample = xwb.loop_start_sample; vgmstream->loop_end_sample = xwb.loop_end_sample; - vgmstream->num_streams = xwb.streams; + vgmstream->num_streams = xwb.total_subsongs; vgmstream->stream_size = xwb.stream_size; vgmstream->meta_type = meta_XWB; - get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile); + get_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_subsong, &xwb, streamFile); switch(xwb.codec) { case PCM: /* Unreal Championship (Xbox)[PCM8], KOF2003 (Xbox)[PCM16LE], Otomedius (X360)[PCM16BE] */ @@ -420,7 +458,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* no wma_bytes_to_samples, this should be ok */ if (!vgmstream->num_samples) - vgmstream->num_samples = ffmpeg_data->totalSamples; + vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; break; } @@ -492,6 +530,24 @@ fail: } +/* ****************************************************************************** */ + +/* try to get the stream name in the .xwb, though they are very rarely included */ +static int get_xwb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) { + size_t read; + + if (!xwb->names_offset || !xwb->names_size || xwb->names_entry_size > maxsize) + goto fail; + + read = read_string(buf,xwb->names_entry_size, xwb->names_offset + xwb->names_entry_size*(target_subsong-1),streamFile); + if (read == 0) goto fail; + + return 1; + +fail: + return 0; +} + /* ****************************************************************************** */ /* XSB parsing from xwb_split (mostly untouched), could be improved */ @@ -535,24 +591,19 @@ typedef struct { /* try to find the stream name in a companion XSB file, a comically complex cue format. */ -static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_header * xwb, STREAMFILE *streamXwb) { +static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb) { STREAMFILE *streamFile = NULL; int i,j, start_sound, cfg__start_sound = 0, cfg__selected_wavebank = 0; int xsb_version; off_t off, suboff, name_offset = 0; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - xsb_header xsb; - - memset(&xsb,0,sizeof(xsb_header)); /* before any "fail"! */ + xsb_header xsb = {0}; - streamFile = open_stream_ext(streamXwb, "xsb"); + streamFile = open_streamfile_by_ext(streamXwb, "xsb"); if (!streamFile) goto fail; - //todo try common names (xwb and xsb often are named slightly differently using a common convention) - - /* check header */ if ((read_32bitBE(0x00,streamFile) != 0x5344424B) && /* "SDBK" (LE) */ (read_32bitBE(0x00,streamFile) != 0x4B424453)) /* "KBDS" (BE) */ @@ -609,8 +660,8 @@ static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_head xsb.xsb_sounds_offset = read_32bit(0x46, streamFile); } - VGM_ASSERT(xsb.xsb_sounds_count < xwb->streams, - "XSB: number of streams in xsb lower than xwb (xsb %i vs xwb %i)\n", xsb.xsb_sounds_count, xwb->streams); + VGM_ASSERT(xsb.xsb_sounds_count < xwb->total_subsongs, + "XSB: number of streams in xsb lower than xwb (xsb %i vs xwb %i)\n", xsb.xsb_sounds_count, xwb->total_subsongs); VGM_ASSERT(xsb.xsb_simple_sounds_count + xsb.xsb_complex_sounds_count != xsb.xsb_sounds_count, "XSB: number of xsb sounds doesn't match simple + complex sounds (simple %i, complex %i, total %i)\n", xsb.xsb_simple_sounds_count, xsb.xsb_complex_sounds_count, xsb.xsb_sounds_count); @@ -747,7 +798,7 @@ static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_head //CHECK_EXIT(w->sound_count == 0, "ERROR: xsb wavebank %i has no sounds", i); //Ikaruga PC - if (w->sound_count == xwb->streams) { + if (w->sound_count == xwb->total_subsongs) { if (!cfg__selected_wavebank) { VGM_LOG("XSB: multiple xsb wavebanks with the same number of sounds, use -w to specify one of the wavebanks\n"); goto fail; @@ -773,22 +824,22 @@ static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_head } if (cfg__start_sound) { - if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - (cfg__start_sound-1) < xwb->streams) { - VGM_LOG("XSB: starting sound too high (max in selected wavebank is %i)\n", xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - xwb->streams + 1); + if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - (cfg__start_sound-1) < xwb->total_subsongs) { + VGM_LOG("XSB: starting sound too high (max in selected wavebank is %i)\n", xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - xwb->total_subsongs + 1); goto fail; } } else { /* if (!cfg->ignore_names_not_found) - CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count > xwb->streams_count, "ERROR: number of streams in xsb wavebank bigger than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count); + CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count > xwb->streams_count, "ERROR: number of streams in xsb wavebank bigger than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->total_subsongs); if (!cfg->ignore_names_not_found) - CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count < xwb->streams_count, "ERROR: number of streams in xsb wavebank lower than xwb (xsb %i vs xwb %i), use -n to ignore (some names won't be extracted)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count); + CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count < xwb->streams_count, "ERROR: number of streams in xsb wavebank lower than xwb (xsb %i vs xwb %i), use -n to ignore (some names won't be extracted)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->total_subsongs); */ //if (!cfg->ignore_names_not_found) - // CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count != xwb->streams_count, "ERROR: number of streams in xsb wavebank different than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count); + // CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count != xwb->streams_count, "ERROR: number of streams in xsb wavebank different than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->total_subsongs); } /* *************************** */ @@ -799,7 +850,7 @@ static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_head for (i = start_sound; i < xsb.xsb_sounds_count; i++) { xsb_sound *s = &(xsb.xsb_sounds[i]); if (s->wavebank == cfg__selected_wavebank-1 - && s->stream_index == target_stream-1){ + && s->stream_index == target_subsong-1){ name_offset = s->name_offset; break; } @@ -813,6 +864,25 @@ static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_head fail: free(xsb.xsb_sounds); free(xsb.xsb_wavebanks); - if (streamFile) close_streamfile(streamFile); - return; + close_streamfile(streamFile); + + return (name_offset); +} + +static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) { + int name_found; + + /* try inside this xwb */ + name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, streamFile); + if (name_found) return; + + /* try again in external .xsb */ + get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile); + + + //todo try again with common names (xwb and xsb often are named slightly differently using a common convention): + // InGameMusic.xwb + ingamemusic.xsb + // UIMusicBank.xwb + UISoundBank.xsb + // NB_BGM_m0100_WB.xwb + NB_BGM_m0100_SB.xsb + // Wave Bank.xwb + Sound Bank.xsb } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index f62b78422..b285164ea 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -6,12 +6,6 @@ #include "vgmstream.h" -/* On EOF reads we can return length 0, or ignore and return the requested length + 0-set the buffer. - * Some decoders don't check for EOF and may decode garbage if returned 0, as read_Nbit() funcs return -1. - * Only matters for metas that get num_samples wrong (bigger than total data). */ -#define STREAMFILE_IGNORE_EOF 0 - - /* a STREAMFILE that operates via standard IO using a buffer */ typedef struct { STREAMFILE sf; /* callbacks */ @@ -27,20 +21,26 @@ typedef struct { static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize); static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize); -static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOSTREAMFILE * streamfile) { - size_t length_read_total=0; +static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) { + size_t length_read_total = 0; + + if (!streamfile || !dest || length <= 0 || offset < 0) + return 0; /* is the part of the requested length in the buffer? */ if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) { - size_t length_read; + size_t length_to_read; off_t offset_into_buffer = offset - streamfile->offset; - length_read = streamfile->validsize - offset_into_buffer; - memcpy(dest,streamfile->buffer + offset_into_buffer,length_read); - length_read_total += length_read; - length -= length_read; - offset += length_read; - dest += length_read; + length_to_read = streamfile->validsize - offset_into_buffer; + if (length_to_read > length) + length_to_read = length; + + memcpy(dest,streamfile->buffer + offset_into_buffer,length_to_read); + length_read_total += length_to_read; + length -= length_to_read; + offset += length_to_read; + dest += length_to_read; } /* What would make more sense here is to read the whole request @@ -49,25 +49,18 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST * The destination buffer is supposed to be much smaller than the * STREAMFILE buffer, though. Maybe we should only ever return up * to the buffer size to avoid having to deal with things like this - * which are outside of my intended use. - */ + * which are outside of my intended use. */ + /* read the rest of the requested length */ while (length > 0) { - size_t length_to_read; - size_t length_read; + size_t length_to_read, length_read; streamfile->validsize = 0; /* buffer is empty now */ /* request outside file: ignore to avoid seek/read */ if (offset > streamfile->filesize) { streamfile->offset = streamfile->filesize; VGM_LOG_ONCE("ERROR: reading over filesize 0x%x @ 0x%lx + 0x%x (buggy meta?)\n", streamfile->filesize, offset, length); - -#if STREAMFILE_IGNORE_EOF - memset(dest,0,length); /* dest is already shifted */ - return length_read_total + length; /* partially-read + 0-set buffer */ -#else return length_read_total; /* partially-read buffer */ -#endif } /* position to new offset */ @@ -90,13 +83,7 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST /* if we can't get enough to satisfy the request (EOF) we give up */ if (length_read < length_to_read) { memcpy(dest,streamfile->buffer,length_read); - -#if STREAMFILE_IGNORE_EOF - memset(dest+length_read,0,length-length_read); - return length_read_total + length; /* partially-read + 0-set buffer */ -#else return length_read_total + length_read; /* partially-read buffer */ -#endif } /* use the new buffer */ @@ -110,38 +97,6 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST return length_read_total; } -static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) { - - if (!streamfile || !dest || length<=0) - return 0; - - /* request outside file: ignore to avoid seek/read in read_the_rest() */ - if (offset > streamfile->filesize) { - streamfile->offset = streamfile->filesize; - VGM_LOG_ONCE("ERROR: offset over filesize 0x%x @ 0x%lx + 0x%x (buggy meta?)\n", streamfile->filesize, offset, length); - -#if STREAMFILE_IGNORE_EOF - memset(dest,0,length); - return length; /* 0-set buffer */ -#else - return 0; /* nothing to read */ -#endif - } - - /* just copy if entire request is within the buffer */ - if (offset >= streamfile->offset && offset + length <= streamfile->offset + streamfile->validsize) { - off_t offset_into_buffer = offset - streamfile->offset; - memcpy(dest,streamfile->buffer + offset_into_buffer,length); - return length; - } - - /* request outside buffer: new fread */ - { - size_t length_read = read_the_rest(dest,offset,length,streamfile); - return length_read; - } -} - static void close_stdio(STDIOSTREAMFILE * streamfile) { fclose(streamfile->infile); free(streamfile->buffer); @@ -168,7 +123,7 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil if (!filename) return NULL; - +#if !defined (__ANDROID__) // if same name, duplicate the file pointer we already have open if (!strcmp(streamFile->name,filename)) { if (((newfd = dup(fileno(streamFile->infile))) >= 0) && @@ -182,6 +137,7 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil fclose(newfile); } } +#endif // a normal open, open a new file return open_stdio_streamfile_buffer(filename,buffersize); } @@ -205,7 +161,6 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char streamfile->sf.get_size = (void*)get_size_stdio; streamfile->sf.get_offset = (void*)get_offset_stdio; streamfile->sf.get_name = (void*)get_name_stdio; - streamfile->sf.get_realname = (void*)get_name_stdio; streamfile->sf.open = (void*)open_stdio; streamfile->sf.close = (void*)close_stdio; @@ -249,6 +204,145 @@ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) { /* **************************************************** */ +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; + off_t offset; /* current buffer data start */ + uint8_t * buffer; /* data buffer */ + size_t buffersize; /* max buffer size */ + size_t validsize; /* current buffer size */ + size_t filesize; /* buffered file size */ +} BUFFER_STREAMFILE; + + +static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + size_t length_read_total = 0; + + if (!streamfile || !dest || length <= 0 || offset < 0) + return 0; + + /* is the part of the requested length in the buffer? */ + if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) { + size_t length_to_read; + off_t offset_into_buffer = offset - streamfile->offset; + + length_to_read = streamfile->validsize - offset_into_buffer; + if (length_to_read > length) + length_to_read = length; + + memcpy(dest,streamfile->buffer + offset_into_buffer,length_to_read); + length_read_total += length_to_read; + length -= length_to_read; + offset += length_to_read; + dest += length_to_read; + } + + /* What would make more sense here is to read the whole request + * at once into the dest buffer, as it must be large enough, and then + * copy some part of that into our own buffer. + * The destination buffer is supposed to be much smaller than the + * STREAMFILE buffer, though. Maybe we should only ever return up + * to the buffer size to avoid having to deal with things like this + * which are outside of my intended use. */ + + /* read the rest of the requested length */ + while (length > 0) { + size_t length_to_read, length_read; + streamfile->validsize = 0; /* buffer is empty now */ + + /* request outside file: ignore to avoid seek/read */ + if (offset > streamfile->filesize) { + streamfile->offset = streamfile->filesize; + VGM_LOG_ONCE("ERROR: reading over filesize 0x%x @ 0x%lx + 0x%x (buggy meta?)\n", streamfile->filesize, offset, length); + return length_read_total; /* partially-read buffer */ + } + + streamfile->offset = offset; + + /* decide how much must be read this time */ + if (length > streamfile->buffersize) + length_to_read = streamfile->buffersize; + else + length_to_read = length; + + /* fill the buffer */ + length_read = streamfile->inner_sf->read(streamfile->inner_sf, streamfile->buffer, streamfile->offset, streamfile->buffersize); + streamfile->validsize = length_read; + + /* if we can't get enough to satisfy the request (EOF) we give up */ + if (length_read < length_to_read) { + memcpy(dest,streamfile->buffer,length_read); + return length_read_total + length_read; /* partially-read buffer */ + } + + /* use the new buffer */ + memcpy(dest,streamfile->buffer,length_to_read); + length_read_total += length_to_read; + length -= length_to_read; + dest += length_to_read; + offset += length_to_read; + } + + return length_read_total; +} +static size_t buffer_get_size(BUFFER_STREAMFILE * streamfile) { + return streamfile->filesize; /* cache */ +} +static size_t buffer_get_offset(BUFFER_STREAMFILE * streamfile) { + return streamfile->offset; /* cache */ //todo internal offset? +} +static void buffer_get_name(BUFFER_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ +} +static STREAMFILE *buffer_open(BUFFER_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); + return open_buffer_streamfile(new_inner_sf, buffersize); /* original buffer size is preferable? */ +} +static void buffer_close(BUFFER_STREAMFILE *streamfile) { + streamfile->inner_sf->close(streamfile->inner_sf); + free(streamfile->buffer); + free(streamfile); +} + +STREAMFILE *open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size) { + BUFFER_STREAMFILE *this_sf = NULL; + + if (!streamfile) goto fail; + + this_sf = calloc(1,sizeof(BUFFER_STREAMFILE)); + if (!this_sf) goto fail; + + this_sf->buffersize = buffer_size; + if (this_sf->buffersize == 0) + this_sf->buffersize = STREAMFILE_DEFAULT_BUFFER_SIZE; + + this_sf->buffer = calloc(this_sf->buffersize,1); + if (!this_sf->buffer) goto fail; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)buffer_read; + this_sf->sf.get_size = (void*)buffer_get_size; + this_sf->sf.get_offset = (void*)buffer_get_offset; + this_sf->sf.get_name = (void*)buffer_get_name; + this_sf->sf.open = (void*)buffer_open; + this_sf->sf.close = (void*)buffer_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + + this_sf->filesize = streamfile->get_size(streamfile); + + return &this_sf->sf; + +fail: + if (this_sf) free(this_sf->buffer); + free(this_sf); + return NULL; +} + +/* **************************************************** */ + //todo stream_index: copy? pass? funtion? external? //todo use realnames on reopen? simplify? //todo use safe string ops, this ain't easy @@ -271,9 +365,6 @@ static size_t wrap_get_offset(WRAP_STREAMFILE * streamfile) { static void wrap_get_name(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) { streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ } -static void wrap_get_realname(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) { - streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ -} static void wrap_open(WRAP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize); /* default (don't wrap) */ } @@ -295,7 +386,6 @@ STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile) { this_sf->sf.get_size = (void*)wrap_get_size; this_sf->sf.get_offset = (void*)wrap_get_offset; this_sf->sf.get_name = (void*)wrap_get_name; - this_sf->sf.get_realname = (void*)wrap_get_realname; this_sf->sf.open = (void*)wrap_open; this_sf->sf.close = (void*)wrap_close; this_sf->sf.stream_index = streamfile->stream_index; @@ -329,9 +419,6 @@ static off_t clamp_get_offset(CLAMP_STREAMFILE *streamfile) { static void clamp_get_name(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) { streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ } -static void clamp_get_realname(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) { - streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ -} static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { char original_filename[PATH_LIMIT]; STREAMFILE *new_inner_sf; @@ -365,7 +452,6 @@ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si this_sf->sf.get_size = (void*)clamp_get_size; this_sf->sf.get_offset = (void*)clamp_get_offset; this_sf->sf.get_name = (void*)clamp_get_name; - this_sf->sf.get_realname = (void*)clamp_get_realname; this_sf->sf.open = (void*)clamp_open; this_sf->sf.close = (void*)clamp_close; this_sf->sf.stream_index = streamfile->stream_index; @@ -383,16 +469,22 @@ typedef struct { STREAMFILE sf; STREAMFILE *inner_sf; - void* data; + void* data; /* state for custom reads, malloc'ed + copied on open (to re-open streamfiles cleanly) */ size_t data_size; - size_t (*read_callback)(STREAMFILE *, uint8_t *, off_t, size_t, void*); + size_t (*read_callback)(STREAMFILE *, uint8_t *, off_t, size_t, void*); /* custom read to modify data before copying into buffer */ + size_t (*size_callback)(STREAMFILE *, void*); /* size when custom reads make data smaller/bigger than underlying streamfile */ + //todo would need to make sure re-opened streamfiles work with this, maybe should use init_data_callback per call + //size_t (*close_data_callback)(STREAMFILE *, void*); /* called during close, allows to free stuff in data */ } IO_STREAMFILE; static size_t io_read(IO_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { return streamfile->read_callback(streamfile->inner_sf, dest, offset, length, streamfile->data); } static size_t io_get_size(IO_STREAMFILE *streamfile) { - return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ + if (streamfile->size_callback) + return streamfile->size_callback(streamfile->inner_sf, streamfile->data); + else + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ } static off_t io_get_offset(IO_STREAMFILE *streamfile) { return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ @@ -400,13 +492,10 @@ static off_t io_get_offset(IO_STREAMFILE *streamfile) { static void io_get_name(IO_STREAMFILE *streamfile, char *buffer, size_t length) { streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ } -static void io_get_realname(IO_STREAMFILE *streamfile, char *buffer, size_t length) { - streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ -} static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { //todo should have some flag to decide if opening other files with IO STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); - return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback); + return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback, streamfile->size_callback); } static void io_close(IO_STREAMFILE *streamfile) { streamfile->inner_sf->close(streamfile->inner_sf); @@ -414,7 +503,7 @@ static void io_close(IO_STREAMFILE *streamfile) { free(streamfile); } -STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback) { +STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback, void* size_callback) { IO_STREAMFILE *this_sf; if (!streamfile) return NULL; @@ -428,7 +517,6 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s this_sf->sf.get_size = (void*)io_get_size; this_sf->sf.get_offset = (void*)io_get_offset; this_sf->sf.get_name = (void*)io_get_name; - this_sf->sf.get_realname = (void*)io_get_realname; this_sf->sf.open = (void*)io_open; this_sf->sf.close = (void*)io_close; this_sf->sf.stream_index = streamfile->stream_index; @@ -444,6 +532,7 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s } this_sf->data_size = data_size; this_sf->read_callback = read_callback; + this_sf->size_callback = size_callback; return &this_sf->sf; } @@ -470,9 +559,6 @@ static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, siz strncpy(buffer,streamfile->fakename,length); buffer[length-1]='\0'; } -static void fakename_get_realname(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { - fakename_get_name(streamfile, buffer, length); -} static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { /* detect re-opening the file */ if (strcmp(filename, streamfile->fakename) == 0) { @@ -492,7 +578,7 @@ static void fakename_close(FAKENAME_STREAMFILE *streamfile) { free(streamfile); } -STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char* fakeext) { +STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakename, const char* fakeext) { FAKENAME_STREAMFILE *this_sf; if (!streamfile || (!fakename && !fakeext)) return NULL; @@ -505,7 +591,6 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, ch this_sf->sf.get_size = (void*)fakename_get_size; this_sf->sf.get_offset = (void*)fakename_get_offset; this_sf->sf.get_name = (void*)fakename_get_name; - this_sf->sf.get_realname = (void*)fakename_get_realname; this_sf->sf.open = (void*)fakename_open; this_sf->sf.close = (void*)fakename_close; this_sf->sf.stream_index = streamfile->stream_index; @@ -586,9 +671,6 @@ static size_t multifile_get_offset(MULTIFILE_STREAMFILE * streamfile) { static void multifile_get_name(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) { streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], buffer, length); } -static void multifile_get_realname(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) { - multifile_get_name(streamfile, buffer, length); -} static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { char original_filename[PATH_LIMIT]; STREAMFILE *new_sf = NULL; @@ -628,8 +710,9 @@ fail: static void multifile_close(MULTIFILE_STREAMFILE *streamfile) { int i; for (i = 0; i < streamfile->inner_sfs_size; i++) { - for (i = 0; i < streamfile->inner_sfs_size; i++) + for (i = 0; i < streamfile->inner_sfs_size; i++) { close_streamfile(streamfile->inner_sfs[i]); + } } free(streamfile->inner_sfs); free(streamfile->sizes); @@ -653,7 +736,6 @@ STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfil this_sf->sf.get_size = (void*)multifile_get_size; this_sf->sf.get_offset = (void*)multifile_get_offset; this_sf->sf.get_name = (void*)multifile_get_name; - this_sf->sf.get_realname = (void*)multifile_get_realname; this_sf->sf.open = (void*)multifile_open; this_sf->sf.close = (void*)multifile_close; this_sf->sf.stream_index = streamfiles[0]->stream_index; @@ -683,6 +765,37 @@ fail: /* **************************************************** */ +STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) { + char filename_ext[PATH_LIMIT]; + + streamFile->get_name(streamFile,filename_ext,sizeof(filename_ext)); + strcpy(filename_ext + strlen(filename_ext) - strlen(filename_extension(filename_ext)), ext); + + return streamFile->open(streamFile,filename_ext,STREAMFILE_DEFAULT_BUFFER_SIZE); +} + +STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * name) { + char foldername[PATH_LIMIT]; + char filename[PATH_LIMIT]; + const char *path; + + streamFile->get_name(streamFile,foldername,sizeof(foldername)); + + path = strrchr(foldername,DIR_SEPARATOR); + if (path!=NULL) path = path+1; + + if (path) { + strcpy(filename, foldername); + filename[path-foldername] = '\0'; + strcat(filename, name); + } else { + strcpy(filename, name); + } + + return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); +} + + /* Read a line into dst. The source files are lines separated by CRLF (Windows) / LF (Unux) / CR (Mac). * The line will be null-terminated and CR/LF removed if found. * @@ -739,17 +852,17 @@ size_t get_streamfile_text_line(int dst_length, char * dst, off_t offset, STREAM } -/* reads a c-string, up to maxsize or NULL, returning size. buf is optional. */ -int read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamFile) { - int i; +/* reads a c-string (ANSI only), up to maxsize or NULL, returning size. buf is optional (works as get_string_size). */ +size_t read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamFile) { + size_t pos; - for (i=0; i < maxsize; i++) { - char c = read_8bit(offset + i, streamFile); - if (buf) buf[i] = c; + for (pos = 0; pos < maxsize; pos++) { + char c = read_8bit(offset + pos, streamFile); + if (buf) buf[pos] = c; if (c == '\0') - return i; - if (i+1 == maxsize) { /* null at maxsize and don't validate (expected to be garbage) */ - if (buf) buf[i] = '\0'; + return pos; + if (pos+1 == maxsize) { /* null at maxsize and don't validate (expected to be garbage) */ + if (buf) buf[pos] = '\0'; return maxsize; } if (c < 0x20 || c > 0xA5) @@ -761,37 +874,6 @@ fail: return 0; } -/* Opens an stream using the base streamFile name plus a new extension (ex. for headers in a separate file) */ -STREAMFILE * open_stream_ext(STREAMFILE *streamFile, const char * ext) { - char filename_ext[PATH_LIMIT]; - - streamFile->get_name(streamFile,filename_ext,sizeof(filename_ext)); - strcpy(filename_ext + strlen(filename_ext) - strlen(filename_extension(filename_ext)), ext); - - return streamFile->open(streamFile,filename_ext,STREAMFILE_DEFAULT_BUFFER_SIZE); -} - -/* Opens an stream using the passed name (in the same folder) */ -STREAMFILE * open_stream_name(STREAMFILE *streamFile, const char * name) { - char foldername[PATH_LIMIT]; - char filename[PATH_LIMIT]; - const char *path; - - streamFile->get_name(streamFile,foldername,sizeof(foldername)); - - path = strrchr(foldername,DIR_SEPARATOR); - if (path!=NULL) path = path+1; - - if (path) { - strcpy(filename, foldername); - filename[path-foldername] = '\0'; - strcat(filename, name); - } else { - strcpy(filename, name); - } - - return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); -} /* Opens a file containing decryption keys and copies to buffer. * Tries combinations of keynames based on the original filename. @@ -868,53 +950,10 @@ fail: return 0; } -/** - * open file containing looping data and copy to buffer - * - * returns true if found and copied - */ -int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { - char posname[PATH_LIMIT]; - char filename[PATH_LIMIT]; - /*size_t bytes_read;*/ - STREAMFILE * streamFilePos= NULL; - - streamFile->get_name(streamFile,filename,sizeof(filename)); - - if (strlen(filename)+4 > sizeof(posname)) goto fail; - - /* try to open a posfile using variations: "(name.ext).pos" */ - { - strcpy(posname, filename); - strcat(posname, ".pos"); - streamFilePos = streamFile->open(streamFile,posname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (streamFilePos) goto found; - - goto fail; - } - -found: - //if (get_streamfile_size(streamFilePos) != bufsize) goto fail; - - /* allow pos files to be of different sizes in case of new features, just fill all we can */ - memset(buf, 0, bufsize); - read_streamfile(buf, 0, bufsize, streamFilePos); - - close_streamfile(streamFilePos); - - return 1; - -fail: - if (streamFilePos) close_streamfile(streamFilePos); - - return 0; -} - /** - * checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix") - * - * returns 0 on failure + * Checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix"). + * Empty is ok to accept files without extension ("", "adx,,aix"). Returns 0 on failure */ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { char filename[PATH_LIMIT]; @@ -989,11 +1028,32 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in return 0; } -int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { +void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { streamFile->get_name(streamFile,buffer,size); - return 1; } -int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { +/* copies the filename without path */ +void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) { + char foldername[PATH_LIMIT]; + const char *path; + + + streamFile->get_name(streamFile,foldername,sizeof(foldername)); + + //todo Windows CMD accepts both \\ and /, better way to handle this? + path = strrchr(foldername,'\\'); + if (!path) + path = strrchr(foldername,'/'); + if (path != NULL) + path = path+1; + + //todo validate sizes and copy sensible max + if (path) { + strcpy(buffer, path); + } else { + strcpy(buffer, foldername); + } +} +void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { const char *path; streamFile->get_name(streamFile,buffer,size); @@ -1006,11 +1066,8 @@ int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { } else { buffer[0] = '\0'; } - - return 1; } -int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) { +void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) { streamFile->get_name(streamFile,filename,size); strcpy(filename, filename_extension(filename)); - return 1; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 49818e9ca..9e5333f4a 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -15,11 +15,8 @@ #include #include "streamtypes.h" #include "util.h" -#if defined(ANDROID) -#include -#endif -#if (defined(__MSVCRT__) || defined(_MSC_VER)) && !defined(ANDROID) +#if defined(__MSVCRT__) || defined(_MSC_VER) #include #define fseeko fseek #define ftello ftell @@ -46,15 +43,14 @@ #endif /* struct representing a file with callbacks. Code should use STREAMFILEs and not std C functions - * to do file operations, as plugins may need to provide their own callbacks. */ + * to do file operations, as plugins may need to provide their own callbacks. + * Reads from arbitrary offsets, meaning internally may need fseek equivalents during reads. */ typedef struct _STREAMFILE { size_t (*read)(struct _STREAMFILE *,uint8_t * dest, off_t offset, size_t length); size_t (*get_size)(struct _STREAMFILE *); off_t (*get_offset)(struct _STREAMFILE *); /* for dual-file support */ void (*get_name)(struct _STREAMFILE *,char *name,size_t length); - /* for when the "name" is encoded specially, this is the actual user visible name */ - void (*get_realname)(struct _STREAMFILE *,char *name,size_t length); struct _STREAMFILE * (*open)(struct _STREAMFILE *,const char * const filename,size_t buffersize); void (*close)(struct _STREAMFILE *); @@ -65,41 +61,56 @@ typedef struct _STREAMFILE { } STREAMFILE; -/* create a STREAMFILE from path */ -STREAMFILE * open_stdio_streamfile(const char * filename); +/* Opens a standard STREAMFILE, opening from path. + * Uses stdio (FILE) for operations, thus plugins may not want to use it. */ +STREAMFILE *open_stdio_streamfile(const char * filename); -/* create a STREAMFILE from pre-opened file path */ -STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename); +/* Opens a standard STREAMFILE from a pre-opened FILE. */ +STREAMFILE *open_stdio_streamfile_by_file(FILE * file, const char * filename); -/* A STREAMFILE that doesn't close the underlying stream. +/* Opens a STREAMFILE that does buffered IO. + * Can be used when the underlying IO may be slow (like when using custom IO). + * Buffer size is optional. */ +STREAMFILE *open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size); + +/* Opens a STREAMFILE that doesn't close the underlying streamfile. * Calls to open won't wrap the new SF (assumes it needs to be closed). * Can be used in metas to test custom IO without closing the external SF. */ STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile); -/* A STREAMFILE that clamps IO to a section of a larger stream. - * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ +/* Opens a STREAMFILE that clamps reads to a section of a larger streamfile. + * Can be used with subfiles inside a bigger file (to fool metas, or to simplify custom IO). */ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size); -/* A STREAMFILE with custom IO, that clamps IO to a section of a larger stream. - * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ -STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback);//void* size_callback, void* seek_callback); +/* Opens a STREAMFILE that uses custom IO for streamfile reads. + * Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another. */ +STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback, void* size_callback); -/* A STREAMFILE that reports a fake name, but still re-opens itself properly. +/* Opens a STREAMFILE that reports a fake name, but still re-opens itself properly. * Can be used to trick a meta's extension check (to call from another, with a modified SF). * When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext. * If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */ -STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char * fakeext); +STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakename, const char * fakeext); -/* A streamfile formed from multiple streamfiles, their data joined during reads. +//todo probably could simply use custom IO +/* Opens streamfile formed from multiple streamfiles, their data joined during reads. * Can be used when data is segmented in multiple separate files. * The first streamfile is used to get names, stream index and so on. */ STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size); +/* Opens a STREAMFILE from a base pathname + new extension + * Can be used to get companion headers. */ +STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext); + +/* Opens a STREAMFILE from a base path + new filename + * Can be used to get companion files. */ +STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename); + /* close a file, destroy the STREAMFILE object */ static inline void close_streamfile(STREAMFILE * streamfile) { - if (streamfile==NULL) return; - streamfile->close(streamfile); + if (streamfile!=NULL) + streamfile->close(streamfile); } /* read from a file, returns number of bytes read */ @@ -164,13 +175,9 @@ static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) { size_t get_streamfile_text_line(int dst_length, char * dst, off_t offset, STREAMFILE * streamfile, int *line_done_ptr); -STREAMFILE * open_stream_ext(STREAMFILE *streamFile, const char * ext); -STREAMFILE * open_stream_name(STREAMFILE *streamFile, const char * ext); - -int read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile); +size_t read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile); size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); -int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); @@ -178,7 +185,8 @@ int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end); -int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); -int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); -int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); +void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); +void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size); +void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); +void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index c9d883605..832b36b12 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -30,6 +30,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_halpst, init_vgmstream_rs03, init_vgmstream_ngc_dsp_std, + init_vgmstream_ngc_dsp_std_le, init_vgmstream_ngc_mdsp_std, init_vgmstream_ngc_dsp_csmp, init_vgmstream_cstr, @@ -55,7 +56,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, init_vgmstream_xbox_wavm, - init_vgmstream_xbox_xwav, init_vgmstream_ngc_str, init_vgmstream_ea_schl, init_vgmstream_caf, @@ -70,7 +70,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_mp4_aac, #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - init_vgmstream_akb, + init_vgmstream_akb_mp4, #endif init_vgmstream_sadb, init_vgmstream_ps2_bmdx, @@ -119,7 +119,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_scd_pcm, init_vgmstream_ps2_pcm, init_vgmstream_ps2_rkv, - init_vgmstream_ps2_psw, init_vgmstream_ps2_vas, init_vgmstream_ps2_tec, init_vgmstream_ps2_enth, @@ -155,6 +154,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_ccc, init_vgmstream_psx_fag, init_vgmstream_ps2_mihb, + init_vgmstream_ngc_pdt_split, init_vgmstream_ngc_pdt, init_vgmstream_wii_mus, init_vgmstream_dc_asd, @@ -177,7 +177,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_bgw, init_vgmstream_spw, init_vgmstream_ps2_ass, - init_vgmstream_waa_wac_wad_wam, + init_vgmstream_ubi_jade, + init_vgmstream_ubi_jade_container, init_vgmstream_seg, init_vgmstream_nds_strm_ffta2, init_vgmstream_str_asr, @@ -258,7 +259,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_voi, init_vgmstream_ps2_khv, init_vgmstream_pc_smp, - init_vgmstream_ngc_bo2, + init_vgmstream_ngc_rkv, init_vgmstream_dsp_ddsp, init_vgmstream_p3d, init_vgmstream_ps2_tk1, @@ -331,8 +332,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_vds_vdm, init_vgmstream_x360_cxs, init_vgmstream_dsp_adx, - init_vgmstream_akb_multi, - init_vgmstream_akb2_multi, + init_vgmstream_akb, + init_vgmstream_akb2, #ifdef VGM_USE_FFMPEG init_vgmstream_mp4_aac_ffmpeg, #endif @@ -351,6 +352,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ta_aac_x360, init_vgmstream_ta_aac_ps3, init_vgmstream_ta_aac_mobile, + init_vgmstream_ta_aac_mobile_vorbis, init_vgmstream_ps3_mta2, init_vgmstream_ngc_ulw, init_vgmstream_pc_xa30, @@ -361,7 +363,10 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_stm, init_vgmstream_ea_snu, init_vgmstream_awc, - init_vgmstream_nsw_opus, + init_vgmstream_opus_std, + init_vgmstream_opus_n1, + init_vgmstream_opus_capcom, + init_vgmstream_opus_nop, init_vgmstream_pc_al2, init_vgmstream_pc_ast, init_vgmstream_naac, @@ -376,10 +381,32 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_kma9, init_vgmstream_fsb_encrypted, init_vgmstream_xwc, - init_vgmstream_atsl3, + init_vgmstream_atsl, init_vgmstream_sps_n1, init_vgmstream_atx, init_vgmstream_sqex_sead, + init_vgmstream_waf, + init_vgmstream_wave, + init_vgmstream_wave_segmented, + init_vgmstream_rsd6at3p, + init_vgmstream_rsd6wma, + init_vgmstream_smv, + init_vgmstream_nxap, + init_vgmstream_ea_wve_au00, + init_vgmstream_ea_wve_ad10, + init_vgmstream_sthd, + init_vgmstream_pcm_sre, + init_vgmstream_dsp_mcadpcm, + init_vgmstream_ubi_lyn, + init_vgmstream_ubi_lyn_container, + init_vgmstream_msb_msh, + init_vgmstream_txtp, + init_vgmstream_smc_smh, + init_vgmstream_ea_sps_fb, + init_vgmstream_ppst, + init_vgmstream_opus_ppp, + init_vgmstream_ubi_bao_pk, + init_vgmstream_dsp_switch_audio, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -453,14 +480,16 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { /* check FFmpeg streams here, for lack of a better place */ if (vgmstream->coding_type == coding_FFmpeg) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - if (data->streamCount && !vgmstream->num_streams) { + if (data && data->streamCount && !vgmstream->num_streams) { vgmstream->num_streams = data->streamCount; } } #endif /* save info */ - vgmstream->stream_index = streamFile->stream_index; + /* stream_index 0 may be used by plugins to signal "vgmstream default" (IOW don't force to 1) */ + if (!vgmstream->stream_index) + vgmstream->stream_index = streamFile->stream_index; /* save start things so we can restart for seeking */ memcpy(vgmstream->start_ch,vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); @@ -503,7 +532,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { * really hit the loop start. */ #ifdef VGM_USE_VORBIS - if (vgmstream->coding_type==coding_ogg_vorbis) { + if (vgmstream->coding_type==coding_OGG_VORBIS) { reset_ogg_vorbis(vgmstream); } @@ -568,25 +597,13 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #endif if (vgmstream->coding_type==coding_ACM) { - mus_acm_codec_data *data = vgmstream->codec_data; - int i; - - data->current_file = 0; - for (i=0;ifile_count;i++) { - acm_reset(data->files[i]); - } + reset_acm(vgmstream); } - if ( - vgmstream->coding_type == coding_NWA0 || - vgmstream->coding_type == coding_NWA1 || - vgmstream->coding_type == coding_NWA2 || - vgmstream->coding_type == coding_NWA3 || - vgmstream->coding_type == coding_NWA4 || - vgmstream->coding_type == coding_NWA5 - ) { + if (vgmstream->coding_type == coding_NWA) { nwa_codec_data *data = vgmstream->codec_data; - reset_nwa(data->nwa); + if (data) + reset_nwa(data->nwa); } @@ -600,23 +617,12 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { } } - if (vgmstream->layout_type==layout_aax) { - aax_codec_data *data = vgmstream->codec_data; - int i; - - data->current_segment = 0; - for (i=0;isegment_count;i++) { - reset_vgmstream(data->adxs[i]); - } + if (vgmstream->layout_type==layout_segmented) { + reset_layout_segmented(vgmstream->layout_data); } - if (vgmstream->layout_type==layout_scd_int) { - scd_int_codec_data *data = vgmstream->codec_data; - int i; - - for (i=0;isubstream_count;i++) { - reset_vgmstream(data->substreams[i]); - } + if (vgmstream->layout_type==layout_layered) { + reset_layout_layered(vgmstream->layout_data); } } @@ -628,7 +634,9 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int looped) { VGMSTREAMCHANNEL * start_channels; VGMSTREAMCHANNEL * loop_channels; - if (channel_count <= 0) return NULL; + /* up to ~16 aren't too rare for multilayered files, more is probably a bug */ + if (channel_count <= 0 || channel_count > 64) + return NULL; vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!vgmstream) return NULL; @@ -687,7 +695,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) { return; #ifdef VGM_USE_VORBIS - if (vgmstream->coding_type==coding_ogg_vorbis) { + if (vgmstream->coding_type==coding_OGG_VORBIS) { free_ogg_vorbis(vgmstream->codec_data); vgmstream->codec_data = NULL; } @@ -763,40 +771,18 @@ void close_vgmstream(VGMSTREAM * vgmstream) { #endif if (vgmstream->coding_type==coding_ACM) { - mus_acm_codec_data *data = (mus_acm_codec_data *) vgmstream->codec_data; - - if (data) { - if (data->files) { - int i; - for (i=0; ifile_count; i++) { - /* shouldn't be duplicates */ - if (data->files[i]) { - acm_close(data->files[i]); - data->files[i] = NULL; - } - } - free(data->files); - data->files = NULL; - } - - free(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } + free_acm(vgmstream->codec_data); + vgmstream->codec_data = NULL; } - if ( - vgmstream->coding_type == coding_NWA0 || - vgmstream->coding_type == coding_NWA1 || - vgmstream->coding_type == coding_NWA2 || - vgmstream->coding_type == coding_NWA3 || - vgmstream->coding_type == coding_NWA4 || - vgmstream->coding_type == coding_NWA5 - ) { - nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; - close_nwa(data->nwa); - free(data); - - vgmstream->codec_data = NULL; + if (vgmstream->coding_type == coding_NWA) { + if (vgmstream->codec_data) { + nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; + if (data->nwa) + close_nwa(data->nwa); + free(data); + vgmstream->codec_data = NULL; + } } @@ -822,47 +808,14 @@ void close_vgmstream(VGMSTREAM * vgmstream) { vgmstream->codec_data = NULL; } - if (vgmstream->layout_type==layout_aax) { - aax_codec_data *data = (aax_codec_data *) vgmstream->codec_data; - - if (data) { - if (data->adxs) { - int i; - for (i=0;isegment_count;i++) { - /* note that the close_streamfile won't do anything but deallocate itself, - * there is only one open file in vgmstream->ch[0].streamfile */ - close_vgmstream(data->adxs[i]); - } - free(data->adxs); - } - if (data->sample_counts) { - free(data->sample_counts); - } - - free(data); - } - vgmstream->codec_data = NULL; + if (vgmstream->layout_type==layout_segmented) { + free_layout_segmented(vgmstream->layout_data); + vgmstream->layout_data = NULL; } - if (vgmstream->layout_type==layout_scd_int) { - scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data; - - if (data) { - if (data->substreams) { - int i; - for (i=0;isubstream_count;i++) { - /* note that the close_streamfile won't do anything but deallocate itself, - * there is only one open file in vgmstream->ch[0].streamfile */ - close_vgmstream(data->substreams[i]); - if(data->intfiles[i]) close_streamfile(data->intfiles[i]); - } - free(data->substreams); - free(data->intfiles); - } - - free(data); - } - vgmstream->codec_data = NULL; + if (vgmstream->layout_type==layout_layered) { + free_layout_layered(vgmstream->layout_data); + vgmstream->layout_data = NULL; } @@ -910,9 +863,9 @@ int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double + (vgmstream->num_samples - vgmstream->loop_end_sample); } else { - return vgmstream->loop_start_sample + return (int32_t)(vgmstream->loop_start_sample + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes - + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate; + + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate); } } else { @@ -939,66 +892,75 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { switch (vgmstream->layout_type) { case layout_interleave: - case layout_interleave_shortblock: render_vgmstream_interleave(buffer,sample_count,vgmstream); break; -#ifdef VGM_USE_VORBIS - case layout_ogg_vorbis: -#endif case layout_none: render_vgmstream_nolayout(buffer,sample_count,vgmstream); break; - case layout_mxch_blocked: - case layout_ast_blocked: - case layout_halpst_blocked: - case layout_xa_blocked: + case layout_blocked_mxch: + case layout_blocked_ast: + case layout_blocked_halpst: + case layout_blocked_xa: case layout_blocked_ea_schl: case layout_blocked_ea_1snh: - case layout_caf_blocked: - case layout_wsi_blocked: - case layout_str_snds_blocked: - case layout_ws_aud_blocked: - case layout_matx_blocked: + case layout_blocked_caf: + case layout_blocked_wsi: + case layout_blocked_str_snds: + case layout_blocked_ws_aud: + case layout_blocked_matx: case layout_blocked_dec: - case layout_vs_blocked: - case layout_emff_ps2_blocked: - case layout_emff_ngc_blocked: - case layout_gsb_blocked: - case layout_xvas_blocked: - case layout_thp_blocked: - case layout_filp_blocked: + case layout_blocked_vs: + case layout_blocked_emff_ps2: + case layout_blocked_emff_ngc: + case layout_blocked_gsb: + case layout_blocked_xvas: + case layout_blocked_thp: + case layout_blocked_filp: case layout_blocked_ivaud: case layout_blocked_ea_swvr: - case layout_ps2_adm_blocked: - case layout_dsp_bdsp_blocked: - case layout_tra_blocked: - case layout_ps2_iab_blocked: - case layout_ps2_strlr_blocked: - case layout_rws_blocked: - case layout_hwas_blocked: + case layout_blocked_adm: + case layout_blocked_bdsp: + case layout_blocked_tra: + case layout_blocked_ps2_iab: + case layout_blocked_ps2_strlr: + case layout_blocked_rws: + case layout_blocked_hwas: case layout_blocked_ea_sns: case layout_blocked_awc: case layout_blocked_vgs: case layout_blocked_vawx: case layout_blocked_xvag_subsong: + case layout_blocked_ea_wve_au00: + case layout_blocked_ea_wve_ad10: + case layout_blocked_sthd: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; - case layout_acm: - case layout_mus_acm: - render_vgmstream_mus_acm(buffer,sample_count,vgmstream); - break; case layout_aix: render_vgmstream_aix(buffer,sample_count,vgmstream); break; - case layout_aax: - render_vgmstream_aax(buffer,sample_count,vgmstream); + case layout_segmented: + render_vgmstream_segmented(buffer,sample_count,vgmstream); break; - case layout_scd_int: - render_vgmstream_scd_int(buffer,sample_count,vgmstream); + case layout_layered: + render_vgmstream_layered(buffer,sample_count,vgmstream); break; default: break; } + + + /* channel bitmask to silence non-set channels (up to 32) + * can be used for 'crossfading subsongs' or layered channels, where a set of channels make a song section */ + if (vgmstream->channel_mask) { + int ch,s; + for (s = 0; s < sample_count; s++) { + for (ch = 0; ch < vgmstream->channels; ch++) { + if ((vgmstream->channel_mask >> ch) & 1) + continue; + buffer[s*vgmstream->channels + ch] = 0; + } + } + } } /* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ @@ -1022,7 +984,6 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 1; case coding_PCM16LE: - case coding_PCM16LE_XOR_int: case coding_PCM16BE: case coding_PCM16_int: case coding_PCM8: @@ -1036,7 +997,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCMFLOAT: return 1; #ifdef VGM_USE_VORBIS - case coding_ogg_vorbis: + case coding_OGG_VORBIS: case coding_VORBIS_custom: #endif #ifdef VGM_USE_MPEG @@ -1050,12 +1011,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_SDX2_int: case coding_CBD2: case coding_ACM: - case coding_NWA0: - case coding_NWA1: - case coding_NWA2: - case coding_NWA3: - case coding_NWA4: - case coding_NWA5: + case coding_NWA: case coding_SASSC: return 1; @@ -1070,8 +1026,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_3DS_IMA: return 2; case coding_XBOX_IMA: + case coding_XBOX_IMA_mch: case coding_XBOX_IMA_int: case coding_FSB_IMA: + case coding_WWISE_IMA: return 64; case coding_APPLE_IMA4: return 64; @@ -1079,7 +1037,6 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_REF_IMA: return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; case coding_RAD_IMA: - case coding_WWISE_IMA: return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; case coding_NDS_IMA: case coding_DAT4_IMA: @@ -1092,7 +1049,6 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_XA: case coding_PSX: case coding_PSX_badflags: - case coding_PSX_bmdx: case coding_HEVAG: return 28; case coding_PSX_cfg: @@ -1111,7 +1067,13 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ return vgmstream->ws_output_size; case coding_AICA: + return 1; + case coding_AICA_int: return 2; + case coding_YAMAHA: + return (0x40-0x04*vgmstream->channels) * 2 / vgmstream->channels; + case coding_YAMAHA_NXAP: + return (0x40-0x04) * 2; case coding_NDS_PROCYON: return 30; case coding_L5_555: @@ -1146,6 +1108,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 128*2; case coding_MC3: return 10; + case coding_FADPCM: + return 256; /* (0x8c - 0xc) * 2 */ case coding_EA_MT: return 432; case coding_CRI_HCA: @@ -1189,7 +1153,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0; case coding_PCM16LE: - case coding_PCM16LE_XOR_int: case coding_PCM16BE: case coding_PCM16_int: return 0x02; @@ -1208,12 +1171,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_SDX2: case coding_SDX2_int: case coding_CBD2: - case coding_NWA0: - case coding_NWA1: - case coding_NWA2: - case coding_NWA3: - case coding_NWA4: - case coding_NWA5: + case coding_NWA: case coding_SASSC: return 0x01; @@ -1227,7 +1185,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_RAD_IMA: case coding_NDS_IMA: case coding_DAT4_IMA: - case coding_WWISE_IMA: case coding_REF_IMA: return vgmstream->interleave_block_size; case coding_AWC_IMA: @@ -1240,9 +1197,14 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_UBI_IMA: /* variable (PCM then IMA) */ return 0; case coding_XBOX_IMA: + //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs + return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; case coding_XBOX_IMA_int: - case coding_FSB_IMA: + case coding_WWISE_IMA: return 0x24; + case coding_XBOX_IMA_mch: + case coding_FSB_IMA: + return 0x24 * vgmstream->channels; case coding_APPLE_IMA4: return 0x22; @@ -1250,7 +1212,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x0e*vgmstream->channels; case coding_PSX: case coding_PSX_badflags: - case coding_PSX_bmdx: case coding_HEVAG: return 0x10; case coding_PSX_cfg: @@ -1272,7 +1233,11 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_WS: return vgmstream->current_block_size; case coding_AICA: + case coding_AICA_int: return 0x01; + case coding_YAMAHA: + case coding_YAMAHA_NXAP: + return 0x40; case coding_NDS_PROCYON: return 0x10; case coding_L5_555: @@ -1299,6 +1264,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x90; case coding_MC3: return 0x04; + case coding_FADPCM: + return 0x8c; case coding_EA_MT: return 0; /* variable (frames of bit counts or PCM frames) */ #ifdef VGM_USE_ATRAC9 @@ -1314,7 +1281,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream) { switch (vgmstream->coding_type) { case coding_NDS_IMA: - return (vgmstream->interleave_smallblock_size-4)*2; + return (vgmstream->interleave_last_block_size-4)*2; default: return get_vgmstream_samples_per_frame(vgmstream); } @@ -1322,7 +1289,7 @@ int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream) { int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream) { switch (vgmstream->coding_type) { case coding_NDS_IMA: - return vgmstream->interleave_smallblock_size; + return vgmstream->interleave_last_block_size; default: return get_vgmstream_frame_size(vgmstream); } @@ -1393,13 +1360,6 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; - case coding_PCM16LE_XOR_int: - for (chan=0;chanchannels;chan++) { - decode_pcm16LE_XOR_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; case coding_PCM16BE: for (chan=0;chanchannels;chan++) { decode_pcm16BE(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1496,7 +1456,14 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to break; case coding_XBOX_IMA: for (chan=0;chanchannels;chan++) { - decode_xbox_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + decode_xbox_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do,chan); + } + break; + case coding_XBOX_IMA_mch: + for (chan=0;chanchannels;chan++) { + decode_xbox_ima_mch(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } @@ -1564,13 +1531,6 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; - case coding_PSX_bmdx: - for (chan=0;chanchannels;chan++) { - decode_psx_bmdx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; case coding_HEVAG: for (chan=0;chanchannels;chan++) { decode_hevag(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1628,7 +1588,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to } break; #ifdef VGM_USE_VORBIS - case coding_ogg_vorbis: + case coding_OGG_VORBIS: decode_ogg_vorbis(vgmstream->codec_data, buffer+samples_written*vgmstream->channels,samples_to_do, vgmstream->channels); @@ -1830,14 +1790,11 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to break; #endif case coding_ACM: - /* handled in its own layout, here to quiet compiler */ + decode_acm(vgmstream->codec_data, + buffer+samples_written*vgmstream->channels, + samples_to_do, vgmstream->channels); break; - case coding_NWA0: - case coding_NWA1: - case coding_NWA2: - case coding_NWA3: - case coding_NWA4: - case coding_NWA5: + case coding_NWA: decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa, buffer+samples_written*vgmstream->channels, samples_to_do @@ -1859,8 +1816,25 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to } break; case coding_AICA: + case coding_AICA_int: for (chan=0;chanchannels;chan++) { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA); + decode_aica(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do, chan, is_stereo); + } + break; + case coding_YAMAHA: + for (chan=0;chanchannels;chan++) { + decode_yamaha(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do, chan); + } + break; + case coding_YAMAHA_NXAP: + for (chan=0;chanchannels;chan++) { + decode_yamaha_nxap(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } @@ -1916,6 +1890,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to chan); } break; + case coding_FADPCM: + for (chan=0;chanchannels;chan++) { + decode_fadpcm(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_EA_MT: for (chan=0;chanchannels;chan++) { decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan, @@ -1981,7 +1962,6 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { vgmstream->meta_type == meta_DSP_RS03 || vgmstream->meta_type == meta_DSP_CSTR || vgmstream->coding_type == coding_PSX || - vgmstream->coding_type == coding_PSX_bmdx || vgmstream->coding_type == coding_PSX_badflags) { int i; for (i=0;ichannels;i++) { @@ -2004,7 +1984,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { } #ifdef VGM_USE_VORBIS - if (vgmstream->coding_type==coding_ogg_vorbis) { + if (vgmstream->coding_type==coding_OGG_VORBIS) { seek_ogg_vorbis(vgmstream, vgmstream->loop_sample); } @@ -2047,15 +2027,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { } #endif - if (vgmstream->coding_type == coding_NWA0 || - vgmstream->coding_type == coding_NWA1 || - vgmstream->coding_type == coding_NWA2 || - vgmstream->coding_type == coding_NWA3 || - vgmstream->coding_type == coding_NWA4 || - vgmstream->coding_type == coding_NWA5) - { + if (vgmstream->coding_type == coding_NWA) { nwa_codec_data *data = vgmstream->codec_data; - seek_nwa(data->nwa, vgmstream->loop_sample); + if (data) + seek_nwa(data->nwa, vgmstream->loop_sample); } /* restore! */ @@ -2173,17 +2148,16 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { "\n"); concatn(length,desc,temp); - if (vgmstream->layout_type == layout_interleave - || vgmstream->layout_type == layout_interleave_shortblock) { + if (vgmstream->layout_type == layout_interleave) { snprintf(temp,TEMPSIZE, "interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size); concatn(length,desc,temp); - if (vgmstream->layout_type == layout_interleave_shortblock) { + if (vgmstream->interleave_last_block_size) { snprintf(temp,TEMPSIZE, - "last block interleave: %#x bytes\n", - (int32_t)vgmstream->interleave_smallblock_size); + "interleave last block: %#x bytes\n", + (int32_t)vgmstream->interleave_last_block_size); concatn(length,desc,temp); } } @@ -2256,7 +2230,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea {"l","r"}, {"left","right"}, {"Left","Right"}, - {".V0",".V1"}, + {".V0",".V1"}, /* Homura (PS2) */ + {".L",".R"}, /* Crash Nitro Racing (PS2) */ {"_0","_1"}, //unneeded? }; char new_filename[PATH_LIMIT]; @@ -2333,7 +2308,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea /* check even if the layout doesn't use them, because it is * difficult to determine when it does, and they should be zero otherwise, anyway */ new_vgmstream->interleave_block_size == opened_vgmstream->interleave_block_size && - new_vgmstream->interleave_smallblock_size == opened_vgmstream->interleave_smallblock_size)) { + new_vgmstream->interleave_last_block_size == opened_vgmstream->interleave_last_block_size)) { goto fail; } @@ -2409,12 +2384,12 @@ static int get_vgmstream_average_bitrate_channel_count(VGMSTREAM * vgmstream) { //AAX, AIX, ACM? - if (vgmstream->layout_type==layout_scd_int) { - scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data; - return (data) ? data->substream_count : 0; + if (vgmstream->layout_type==layout_layered) { + layered_layout_data *data = (layered_layout_data *) vgmstream->layout_data; + return (data) ? data->layer_count : 0; } #ifdef VGM_USE_VORBIS - if (vgmstream->coding_type==coding_ogg_vorbis) { + if (vgmstream->coding_type==coding_OGG_VORBIS) { ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *) vgmstream->codec_data; return (data) ? 1 : 0; } @@ -2441,14 +2416,22 @@ static int get_vgmstream_average_bitrate_channel_count(VGMSTREAM * vgmstream) /* average bitrate helper */ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int channel) { - //AAX, AIX, ACM? + //AAX, AIX? - if (vgmstream->layout_type==layout_scd_int) { - scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data; - return data->intfiles[channel]; + if (vgmstream->coding_type==coding_NWA) { + nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; + if (data && data->nwa) + return data->nwa->file; } + + if (vgmstream->coding_type==coding_ACM) { + acm_codec_data *data = (acm_codec_data *) vgmstream->codec_data; + if (data && data->file) + return data->file->streamfile; + } + #ifdef VGM_USE_VORBIS - if (vgmstream->coding_type==coding_ogg_vorbis) { + if (vgmstream->coding_type==coding_OGG_VORBIS) { ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *) vgmstream->codec_data; return data->ov_streamfile.streamfile; } @@ -2488,10 +2471,10 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { int bitrate = 0; int sample_rate = vgmstream->sample_rate; int length_samples = vgmstream->num_samples; - int channels = get_vgmstream_average_bitrate_channel_count(vgmstream); + int channels; STREAMFILE * streamFile; - if (!sample_rate || !channels || !length_samples) + if (!sample_rate || !length_samples) return 0; /* subsongs need to report this to properly calculate */ @@ -2499,6 +2482,21 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { return get_vgmstream_average_bitrate_from_size(vgmstream->stream_size, sample_rate, length_samples); } + /* segmented layout is handled differently as it has multiple sub-VGMSTREAMs (may include special codecs) */ + //todo not correct with multifile segments (ex. .ACM Ogg) + if (vgmstream->layout_type==layout_segmented) { + segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data; + return get_vgmstream_average_bitrate(data->segments[0]); + } + if (vgmstream->layout_type==layout_layered) { + layered_layout_data *data = (layered_layout_data *) vgmstream->layout_data; + return get_vgmstream_average_bitrate(data->layers[0]); + } + + + channels = get_vgmstream_average_bitrate_channel_count(vgmstream); + if (!channels) return 0; + if (channels >= 1) { streamFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, 0); if (streamFile) { @@ -2529,10 +2527,10 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { /** - * Inits vgmstreams' channels doing two things: + * Inits vgmstream, doing two things: * - sets the starting offset per channel (depending on the layout) * - opens its own streamfile from on a base one. One streamfile per channel may be open (to improve read/seeks). - * Should be called in metas before returning the VGMSTREAM.. + * Should be called in metas before returning the VGMSTREAM. */ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) { STREAMFILE * file; @@ -2542,14 +2540,18 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s int use_same_offset_per_channel = 0; - /* stream/offsets not needed, manages themselves */ + /* stream/offsets not needed, managed by layout */ if (vgmstream->layout_type == layout_aix || - vgmstream->layout_type == layout_aax || - vgmstream->layout_type == layout_scd_int) + vgmstream->layout_type == layout_segmented || + vgmstream->layout_type == layout_layered) + return 1; + + /* stream/offsets not needed, managed by decoder */ + if (vgmstream->coding_type == coding_NWA) return 1; #ifdef VGM_USE_FFMPEG - /* stream/offsets not needed, FFmpeg manages itself */ + /* stream/offsets not needed, managed by decoder */ if (vgmstream->coding_type == coding_FFmpeg) return 1; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index d498196bc..d53fa4197 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -85,7 +85,6 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ typedef enum { /* PCM */ coding_PCM16LE, /* little endian 16-bit PCM */ - coding_PCM16LE_XOR_int, /* little endian 16-bit PCM with sample-level xor (for blocks) */ coding_PCM16BE, /* big endian 16-bit PCM */ coding_PCM16_int, /* 16-bit PCM with sample-level interleave (for blocks) */ @@ -118,7 +117,6 @@ typedef enum { coding_XA, /* CD-ROM XA */ coding_PSX, /* Sony PS ADPCM (VAG) */ coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */ - coding_PSX_bmdx, /* Sony PS ADPCM with BMDX encryption */ coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (FF XI, SGXD type 5, Bizarre Creations) */ coding_HEVAG, /* Sony PSVita ADPCM */ @@ -135,11 +133,12 @@ typedef enum { coding_3DS_IMA, /* 3DS IMA ADPCM */ coding_MS_IMA, /* Microsoft IMA ADPCM */ coding_XBOX_IMA, /* XBOX IMA ADPCM */ - coding_XBOX_IMA_int, /* XBOX IMA ADPCM (interleaved/mono) */ + coding_XBOX_IMA_mch, /* XBOX IMA ADPCM (multichannel) */ + coding_XBOX_IMA_int, /* XBOX IMA ADPCM (mono/interleave) */ coding_NDS_IMA, /* IMA ADPCM w/ NDS layout */ coding_DAT4_IMA, /* Eurocom 'DAT4' IMA ADPCM */ coding_RAD_IMA, /* Radical IMA ADPCM */ - coding_RAD_IMA_mono, /* Radical IMA ADPCM, mono (for interleave) */ + coding_RAD_IMA_mono, /* Radical IMA ADPCM (mono/interleave) */ coding_APPLE_IMA4, /* Apple Quicktime IMA4 */ coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */ @@ -151,7 +150,10 @@ typedef enum { coding_MSADPCM, /* Microsoft ADPCM */ coding_WS, /* Westwood Studios VBR ADPCM */ - coding_AICA, /* Yamaha AICA ADPCM */ + coding_AICA, /* Yamaha AICA ADPCM (stereo) */ + coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */ + coding_YAMAHA, /* Yamaha ADPCM */ + coding_YAMAHA_NXAP, /* Yamaha ADPCM (NXAP variation) */ coding_NDS_PROCYON, /* Procyon Studio ADPCM */ coding_L5_555, /* Level-5 0x555 ADPCM */ coding_SASSC, /* Activision EXAKT SASSC DPCM */ @@ -159,6 +161,7 @@ typedef enum { coding_MTAF, /* Konami MTAF ADPCM */ coding_MTA2, /* Konami MTA2 ADPCM */ coding_MC3, /* Paradigm MC3 3-bit ADPCM */ + coding_FADPCM, /* FMOD FADPCM 4-bit ADPCM */ /* others */ coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ @@ -166,19 +169,14 @@ typedef enum { coding_CBD2, /* CBD2 2:1 Cuberoot-Delta-Exact compression DPCM */ coding_CBD2_int, /* CBD2 2:1 Cuberoot-Delta-Exact compression, with sample-level interleave */ coding_ACM, /* InterPlay ACM */ - coding_NWA0, /* Visual Art's NWA (compressed at various levels) */ - coding_NWA1, - coding_NWA2, - coding_NWA3, - coding_NWA4, - coding_NWA5, + coding_NWA, /* VisualArt's NWA */ coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */ coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */ #ifdef VGM_USE_VORBIS - coding_ogg_vorbis, /* Xiph Vorbis with Ogg layer (MDCT-based) */ + coding_OGG_VORBIS, /* Xiph Vorbis with Ogg layer (MDCT-based) */ coding_VORBIS_custom, /* Xiph Vorbis with custom layer (MDCT-based) */ #endif @@ -223,53 +221,50 @@ typedef enum { /* interleave */ layout_interleave, /* equal interleave throughout the stream */ - layout_interleave_shortblock, /* interleave with a short last block */ /* headered blocks */ - layout_ast_blocked, - layout_halpst_blocked, - layout_xa_blocked, + layout_blocked_ast, + layout_blocked_halpst, + layout_blocked_xa, layout_blocked_ea_schl, layout_blocked_ea_1snh, - layout_caf_blocked, - layout_wsi_blocked, - layout_str_snds_blocked, - layout_ws_aud_blocked, - layout_matx_blocked, + layout_blocked_caf, + layout_blocked_wsi, + layout_blocked_str_snds, + layout_blocked_ws_aud, + layout_blocked_matx, layout_blocked_dec, - layout_xvas_blocked, - layout_vs_blocked, - layout_emff_ps2_blocked, - layout_emff_ngc_blocked, - layout_gsb_blocked, - layout_thp_blocked, - layout_filp_blocked, + layout_blocked_xvas, + layout_blocked_vs, + layout_blocked_emff_ps2, + layout_blocked_emff_ngc, + layout_blocked_gsb, + layout_blocked_thp, + layout_blocked_filp, layout_blocked_ea_swvr, - layout_ps2_adm_blocked, - layout_dsp_bdsp_blocked, - layout_mxch_blocked, + layout_blocked_adm, + layout_blocked_bdsp, + layout_blocked_mxch, layout_blocked_ivaud, /* GTA IV .ivaud blocks */ - layout_tra_blocked, /* DefJam Rapstar .tra blocks */ - layout_ps2_iab_blocked, - layout_ps2_strlr_blocked, - layout_rws_blocked, - layout_hwas_blocked, + layout_blocked_tra, /* DefJam Rapstar .tra blocks */ + layout_blocked_ps2_iab, + layout_blocked_ps2_strlr, + layout_blocked_rws, + layout_blocked_hwas, layout_blocked_ea_sns, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ layout_blocked_awc, /* Rockstar AWC */ layout_blocked_vgs, /* Guitar Hero II (PS2) */ layout_blocked_vawx, /* No More Heroes 6ch (PS3) */ layout_blocked_xvag_subsong, /* XVAG subsongs [God of War III (PS4)] */ + layout_blocked_ea_wve_au00, /* EA WVE au00 blocks */ + layout_blocked_ea_wve_ad10, /* EA WVE Ad10 blocks */ + layout_blocked_sthd, /* Dream Factory STHD */ /* otherwise odd */ - layout_acm, /* libacm layout */ - layout_mus_acm, /* mus has multi-files to deal with */ layout_aix, /* CRI AIX's wheels within wheels */ - layout_aax, /* CRI AAX's wheels within databases */ - layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */ + layout_segmented, /* song divided in segments (song sections) */ + layout_layered, /* song divided in layers (song channels) */ -#ifdef VGM_USE_VORBIS - layout_ogg_vorbis, /* ogg vorbis file */ -#endif } layout_t; /* The meta type specifies how we know what we know about the file. @@ -284,8 +279,8 @@ typedef enum { meta_DSP_AGSC, /* Retro: Metroid Prime 2 title */ meta_DSP_MPDSP, /* Monopoly Party single header stereo */ meta_DSP_JETTERS, /* Bomberman Jetters .dsp */ - meta_DSP_MSS, /* ? */ - meta_DSP_GCM, /* ? */ + meta_DSP_MSS, /* Free Radical GC games */ + meta_DSP_GCM, /* some of Traveller's Tales games */ meta_DSP_STR, /* Conan .str files */ meta_DSP_SADB, /* .sad */ meta_DSP_WSI, /* .wsi */ @@ -325,7 +320,7 @@ typedef enum { meta_RSF, /* Retro Studios RSF (Metroid Prime .rsf) [no header_id] */ meta_HALPST, /* HAL Labs HALPST */ meta_GCSW, /* GCSW (PCM) */ - meta_CFN, /* Namco CAF Audio File */ + meta_CAF, /* tri-Crescendo CAF */ meta_MYSPD, /* U-Sing .myspd */ meta_HIS, /* Her Ineractive .his */ meta_BNSF, /* Bandai Namco Sound Format */ @@ -381,11 +376,10 @@ typedef enum { meta_PS2_RSTM, /* Midnight Club 3 */ meta_PS2_KCES, /* Dance Dance Revolution */ meta_PS2_DXH, /* Tokobot Plus - Myteries of the Karakuri */ - meta_PS2_PSH, /* Dawn of Mana - Seiken Densetsu 4 */ + meta_PS2_PSH, /* Square Enix PSH/VSV (Dawn of Mana/KH Re:CoM) */ meta_SCD_PCM, /* Lunar - Eternal Blue */ meta_PS2_PCM, /* Konami KCEJ East: Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades */ - meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 */ - meta_PS2_PSW, /* Rayman Raving Rabbids */ + meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 (PS2) */ meta_PS2_VAS, /* Pro Baseball Spirits 5 */ meta_PS2_TEC, /* TECMO badflagged stream */ meta_PS2_ENTH, /* Enthusia */ @@ -407,13 +401,11 @@ typedef enum { meta_GSP_GSB, /* Tecmo games (Super Swing Golf 1 & 2, Quamtum Theory) */ meta_YDSP, /* WWE Day of Reckoning */ meta_FFCC_STR, /* Final Fantasy: Crystal Chronicles */ - - meta_WAA_WAC_WAD_WAM, /* Beyond Good & Evil */ + meta_UBI_JADE, /* Beyond Good & Evil, Rayman Raving Rabbids */ meta_GCA, /* Metal Slug Anthology */ meta_MSVP, /* Popcap Hits */ meta_NGC_SSM, /* Golden Gashbell Full Power */ meta_PS2_JOE, /* Wall-E / Pixar games */ - meta_NGC_YMF, /* WWE WrestleMania X8 */ meta_SADL, /* .sad */ meta_PS2_CCC, /* Tokyo Xtreme Racer DRIFT 2 */ @@ -440,6 +432,8 @@ typedef enum { meta_RSD6RADP, /* RSD6RADP */ meta_RSD6OOGV, /* RSD6OOGV */ meta_RSD6XMA, /* RSD6XMA */ + meta_RSD6AT3P, /* RSD6AT3+ */ + meta_RSD6WMA, /* RSD6WMA */ meta_PS2_ASS, /* ASS */ meta_PS2_SEG, /* Eragon */ @@ -463,7 +457,6 @@ typedef enum { meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ meta_XBOX_WAVM, /* XBOX WAVM File */ - meta_XBOX_RIFF, /* XBOX RIFF/WAVE File */ meta_XBOX_WVS, /* XBOX WVS */ meta_NGC_WVS, /* Metal Arms - Glitch in the System */ meta_XBOX_MATX, /* XBOX MATX */ @@ -488,8 +481,8 @@ typedef enum { meta_RIFF_WAVE_POS, /* .wav + .pos for looping (Ys Complete PC) */ meta_RIFF_WAVE_labl, /* RIFF w/ loop Markers in LIST-adtl-labl */ meta_RIFF_WAVE_smpl, /* RIFF w/ loop data in smpl chunk */ + meta_RIFF_WAVE_wsmp, /* RIFF w/ loop data in wsmp chunk */ meta_RIFF_WAVE_MWV, /* .mwv RIFF w/ loop data in ctrl chunk pflt */ - meta_RIFF_WAVE_SNS, /* .sns RIFF */ meta_RIFX_WAVE, /* RIFX, for big-endian WAVs */ meta_RIFX_WAVE_smpl, /* RIFX w/ loop data in smpl chunk */ meta_XNB, /* XNA Game Studio 4.0 */ @@ -547,7 +540,7 @@ typedef enum { meta_P3D, /* Prototype P3D */ meta_PS2_TK1, /* Tekken (NamCollection) */ meta_PS2_ADSC, /* Kenka Bancho 2: Full Throttle */ - meta_NGC_BO2, /* Blood Omen 2 (NGC) */ + meta_NGC_RKV, /* Legacy of Kain - Blood Omen 2 (GC) */ meta_DSP_DDSP, /* Various (2 dsp files stuck together */ meta_NGC_DSP_MPDS, /* Big Air Freestyle, Terminator 3 */ meta_DSP_STR_IG, /* Micro Machines, Superman Superman: Shadow of Apokolis */ @@ -557,7 +550,7 @@ typedef enum { meta_PS2_WAD, /* The golden Compass */ meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */ meta_DSP_CABELAS, /* Cabelas games */ - meta_PS2_ADM, /* Dragon Quest 5 */ + meta_PS2_ADM, /* Dragon Quest V (PS2) */ meta_PS2_LPCM, /* Ah! My Goddess */ meta_DSP_BDSP, /* Ah! My Goddess */ meta_PS2_VMS, /* Autobahn Raser - Police Madness */ @@ -609,7 +602,7 @@ typedef enum { meta_FSTM, /* Nintendo Wii U FSTM (caFe? Stream) */ meta_3DS_IDSP, /* Nintendo 3DS/Wii U IDSP */ meta_KT_WIIBGM, /* Koei Tecmo WiiBGM */ - meta_KTSS, /* Koei Tecmo Switch Sound */ + meta_KTSS, /* Koei Tecmo Nintendo Stream (KNS) */ meta_MCA, /* Capcom MCA "MADP" */ meta_XB3D_ADX, /* Xenoblade Chronicles 3D ADX */ meta_HCA, /* CRI HCA */ @@ -629,7 +622,7 @@ typedef enum { meta_GTD, /* Knights Contract (X360/PS3), Valhalla Knights 3 (PSV) */ meta_TA_AAC_X360, /* tri-Ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ meta_TA_AAC_PS3, /* tri-Ace AAC (Star Ocean International, Resonance of Fate) */ - meta_TA_AAC_VORBIS, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */ + meta_TA_AAC_MOBILE, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */ meta_PS3_MTA2, /* Metal Gear Solid 4 MTA2 */ meta_NGC_ULW, /* Burnout 1 (GC only) */ meta_PC_XA30, /* Driver - Parallel Lines (PC) */ @@ -641,7 +634,7 @@ typedef enum { meta_BINK, /* RAD Game Tools BINK audio/video */ meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */ meta_AWC, /* Rockstar AWC (GTA5, RDR) */ - meta_NSW_OPUS, /* Lego City Undercover (Switch) */ + meta_OPUS, /* Nintendo Opus [Lego City Undercover (Switch)] */ meta_PC_AL2, /* Conquest of Elysium 3 (PC) */ meta_PC_AST, /* Dead Rising (PC) */ meta_NAAC, /* Namco AAC (3DS) */ @@ -667,10 +660,29 @@ typedef enum { meta_SQEX_SAB, /* Square-Enix newest middleware (sound) */ meta_SQEX_MAB, /* Square-Enix newest middleware (music) */ meta_OGG_L2SD, /* Ogg Vorbis with obfuscation [Lineage II Chronicle 4 (PC)] */ + meta_WAF, /* KID WAF [Ever 17 (PC)] */ + meta_WAVE, /* EngineBlack games [Mighty Switch Force! (3DS)] */ + meta_WAVE_segmented, /* EngineBlack games, segmented [Shantae and the Pirate's Curse (PC)] */ + meta_SMV, /* Cho Aniki Zero (PSP) */ + meta_NXAP, /* Nex Entertainment games [Time Crisis 4 (PS3), Time Crisis Razing Storm (PS3)] */ + meta_EA_WVE_AU00, /* Electronic Arts PS movies [Future Cop - L.A.P.D. (PS), Supercross 2000 (PS)] */ + meta_EA_WVE_AD10, /* Electronic Arts PS movies [Wing Commander 3/4 (PS)] */ + meta_STHD, /* STHD .stx [Kakuto Chojin (Xbox)] */ + meta_MP4, /* MP4/AAC */ + meta_PCM_SRE, /* .PCM+SRE [Viewtiful Joe (PS2)] */ + meta_DSP_MCADPCM, /* Skyrim (Switch) */ + meta_UBI_LYN, /* Ubisoft LyN engine [The Adventures of Tintin (multi)] */ + meta_MSB_MSH, /* sfx companion of MIH+MIB */ + meta_OGG_RPGMV, /* Ogg Vorbis with encryption [RPG Maker MV games (PC)] */ + meta_OGG_ENO, /* Ogg Vorbis with encryption [Metronomicon (PC)] */ + meta_TXTP, /* generic text playlist */ + meta_SMC_SMH, /* Wangan Midnight (System 246) */ + meta_OGG_YS8, /* Ogg Vorbis with encryption (Ys VIII PC) */ + meta_PPST, /* PPST [Parappa the Rapper (PSP)] */ + meta_OPUS_PPP, /* .at9 Opus [Penny-Punching Princess (Switch)] */ + meta_UBI_BAO, /* Ubisoft BAO */ + meta_DSP_SWITCH_AUDIO, /* Gal Gun 2 (Switch) */ -#ifdef VGM_USE_MP4V2 - meta_MP4, /* AAC (iOS) */ -#endif #ifdef VGM_USE_FFMPEG meta_FFmpeg, #endif @@ -723,12 +735,6 @@ typedef struct { uint16_t adx_mult; uint16_t adx_add; - /* BMDX encryption */ - uint8_t bmdx_xor; - uint8_t bmdx_add; - - /* generic encryption */ - uint16_t key_xor; } VGMSTREAMCHANNEL; /* main vgmstream info */ @@ -746,6 +752,7 @@ typedef struct { int stream_index; /* selected stream (also 1-based) */ char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */ size_t stream_size; /* info to properly calculate bitrate */ + uint32_t channel_mask; /* to silence crossfading subsongs/layers */ /* looping */ int loop_flag; /* is this stream looped? */ @@ -754,7 +761,7 @@ typedef struct { /* layouts/block */ size_t interleave_block_size; /* interleave, or block/frame size (depending on the codec) */ - size_t interleave_smallblock_size; /* smaller interleave for last block */ + size_t interleave_last_block_size; /* smaller interleave for last block */ /* channel state */ VGMSTREAMCHANNEL * ch; /* pointer to array of channels */ @@ -802,6 +809,10 @@ typedef struct { * Note also that support must be added for resetting, looping and * closing for every codec that uses this, as it will not be handled. */ void * codec_data; + /* Same, for special layouts. + * Reusing the above pointer causes bugs when it's using special layout + codec + * (vgmstream may try to free/loop/etc codec_data). */ + void * layout_data; } VGMSTREAM; #ifdef VGM_USE_VORBIS @@ -816,7 +827,7 @@ typedef struct { void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); uint8_t scd_xor; off_t scd_xor_length; - uint32_t sngw_xor; + uint32_t xor_value; } ogg_vorbis_streamfile; @@ -890,6 +901,8 @@ typedef struct { off_t block_offset; size_t block_size; + int prev_block_samples; /* count for optimization */ + } vorbis_custom_codec_data; #endif @@ -971,6 +984,7 @@ typedef struct { mpeg_custom_t type; /* mpeg subtype */ mpeg_custom_config config; /* config depending on the mode */ + size_t default_buffer_size; mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */ size_t streams_size; @@ -1041,20 +1055,10 @@ typedef struct { } atrac9_codec_data; #endif -/* with one file this is also used for just ACM */ +/* libacm interface */ typedef struct { - int file_count; - int current_file; - /* the index we return to upon loop completion */ - int loop_start_file; - /* one after the index of the last file, typically - * will be equal to file_count */ - int loop_end_file; - /* Upon exit from a loop, which file to play. */ - /* -1 if there is no such file */ - /*int end_file;*/ - ACMStream **files; -} mus_acm_codec_data; + ACMStream *file; +} acm_codec_data; #define AIX_BUFFER_SIZE 0x1000 /* AIXery */ @@ -1070,26 +1074,25 @@ typedef struct { VGMSTREAM **adxs; } aix_codec_data; +/* for files made of "vertical" segments, one per section of a song (using a complete sub-VGMSTREAM) */ typedef struct { int segment_count; + VGMSTREAM **segments; int current_segment; int loop_segment; - /* one per segment */ - int32_t *sample_counts; - VGMSTREAM **adxs; -} aax_codec_data; +} segmented_layout_data; + +/* for files made of "horizontal" layers, one per group of channels (using a complete sub-VGMSTREAM) */ +typedef struct { + int layer_count; + VGMSTREAM **layers; +} layered_layout_data; /* for compressed NWA */ typedef struct { NWAData *nwa; } nwa_codec_data; -/* SQEX SCD interleaved */ -typedef struct { - int substream_count; - VGMSTREAM **substreams; - STREAMFILE **intfiles; -} scd_int_codec_data; typedef struct { STREAMFILE *streamfile; diff --git a/Plugins/vgmstream/vgmstream/VGMInterface.m b/Plugins/vgmstream/vgmstream/VGMInterface.m index 093f2267a..5dfad90ce 100644 --- a/Plugins/vgmstream/vgmstream/VGMInterface.m +++ b/Plugins/vgmstream/vgmstream/VGMInterface.m @@ -71,7 +71,6 @@ static STREAMFILE *cogsf_create(id file, const char *path) { streamfile->sf.get_size = (void*)cogsf_get_size; streamfile->sf.get_offset = (void*)cogsf_get_offset; streamfile->sf.get_name = (void*)cogsf_get_name; - streamfile->sf.get_realname = (void*)cogsf_get_name; streamfile->sf.open = (void*)cogsf_open; streamfile->sf.close = (void*)cogsf_close; streamfile->file = (void*)CFBridgingRetain(file);