diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 4a429400c..285a98e33 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -94,7 +94,6 @@ 83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.c */; }; 83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; }; 832C70BF1E9335E400BD7B4E /* Vorbis.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F4128F1E932F9A002E37D0 /* Vorbis.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 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 */; }; 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; @@ -135,13 +134,64 @@ 8349A91F1FE6258200E26435 /* naac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9061FE6258100E26435 /* naac.c */; }; 834D3A6E19F47C98001C54F6 /* g1l.c in Sources */ = {isa = PBXBuildFile; fileRef = 834D3A6D19F47C98001C54F6 /* g1l.c */; }; 834D795520E4F0D400C4A5CC /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83F4128F1E932F9A002E37D0 /* Vorbis.framework */; }; + 834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0AA215C798A000A5D3D /* acm_decoder_util.c */; }; + 834FE0B4215C798C000A5D3D /* ffmpeg_decoder_custom_opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */; }; + 834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0AC215C798B000A5D3D /* acm_decoder_libacm.h */; }; + 834FE0B6215C798C000A5D3D /* derf_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0AD215C798B000A5D3D /* derf_decoder.c */; }; + 834FE0B7215C798C000A5D3D /* atrac9_decoder.c.orig in Resources */ = {isa = PBXBuildFile; fileRef = 834FE0AE215C798B000A5D3D /* atrac9_decoder.c.orig */; }; + 834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0AF215C798C000A5D3D /* acm_decoder_decode.c */; }; + 834FE0B9215C798C000A5D3D /* xmd_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0B0215C798C000A5D3D /* xmd_decoder.c */; }; + 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */; }; + 834FE0BB215C798C000A5D3D /* celt_fsb_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */; }; + 834FE0BE215C79A9000A5D3D /* blocked_xa_aiff.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0BC215C79A8000A5D3D /* blocked_xa_aiff.c */; }; + 834FE0BF215C79A9000A5D3D /* flat.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0BD215C79A9000A5D3D /* flat.c */; }; + 834FE0E9215C79ED000A5D3D /* ao.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0C0215C79E5000A5D3D /* ao.c */; }; + 834FE0EA215C79ED000A5D3D /* aif_asobo.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0C1215C79E5000A5D3D /* aif_asobo.c */; }; + 834FE0EB215C79ED000A5D3D /* str_wav.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0C2215C79E6000A5D3D /* str_wav.c */; }; + 834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */; }; + 834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0C4215C79E6000A5D3D /* fsb_interleave_streamfile.h */; }; + 834FE0EE215C79ED000A5D3D /* ue4opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0C5215C79E6000A5D3D /* ue4opus.c */; }; + 834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */; }; + 834FE0F0215C79ED000A5D3D /* apc.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0C7215C79E7000A5D3D /* apc.c */; }; + 834FE0F1215C79ED000A5D3D /* a2m.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0C8215C79E7000A5D3D /* a2m.c */; }; + 834FE0F2215C79ED000A5D3D /* wv6.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0C9215C79E7000A5D3D /* wv6.c */; }; + 834FE0F3215C79ED000A5D3D /* bnk_sony.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0CA215C79E7000A5D3D /* bnk_sony.c */; }; + 834FE0F4215C79ED000A5D3D /* wsi.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0CB215C79E8000A5D3D /* wsi.c */; }; + 834FE0F5215C79ED000A5D3D /* wv2.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0CC215C79E8000A5D3D /* wv2.c */; }; + 834FE0F6215C79ED000A5D3D /* derf.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0CD215C79E8000A5D3D /* derf.c */; }; + 834FE0F7215C79ED000A5D3D /* vis.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0CE215C79E8000A5D3D /* vis.c */; }; + 834FE0F8215C79ED000A5D3D /* adpcm_capcom.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */; }; + 834FE0F9215C79ED000A5D3D /* wavebatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D0215C79E8000A5D3D /* wavebatch.c */; }; + 834FE0FA215C79ED000A5D3D /* nus3bank.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D1215C79E9000A5D3D /* nus3bank.c */; }; + 834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D2215C79E9000A5D3D /* xau_konami.c */; }; + 834FE0FC215C79ED000A5D3D /* vai.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D3215C79E9000A5D3D /* vai.c */; }; + 834FE0FD215C79ED000A5D3D /* vpk.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D4215C79E9000A5D3D /* vpk.c */; }; + 834FE0FE215C79ED000A5D3D /* ps_headerless.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D5215C79E9000A5D3D /* ps_headerless.c */; }; + 834FE0FF215C79ED000A5D3D /* sqex_scd_sscf.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D6215C79E9000A5D3D /* sqex_scd_sscf.c */; }; + 834FE100215C79ED000A5D3D /* svg.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D7215C79EA000A5D3D /* svg.c */; }; + 834FE101215C79ED000A5D3D /* csmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D8215C79EA000A5D3D /* csmp.c */; }; + 834FE102215C79ED000A5D3D /* rfrm.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D9215C79EA000A5D3D /* rfrm.c */; }; + 834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0DA215C79EA000A5D3D /* ea_schl_streamfile.h */; }; + 834FE104215C79ED000A5D3D /* nxa.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DB215C79EA000A5D3D /* nxa.c */; }; + 834FE105215C79ED000A5D3D /* xmd.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DC215C79EA000A5D3D /* xmd.c */; }; + 834FE106215C79ED000A5D3D /* utk.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DD215C79EB000A5D3D /* utk.c */; }; + 834FE107215C79ED000A5D3D /* mib_mih.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DE215C79EB000A5D3D /* mib_mih.c */; }; + 834FE108215C79ED000A5D3D /* hd3_bd3.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */; }; + 834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E0215C79EB000A5D3D /* idsp_ie.c */; }; + 834FE10A215C79ED000A5D3D /* nub_idsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E1215C79EB000A5D3D /* nub_idsp.c */; }; + 834FE10B215C79ED000A5D3D /* ps2_psh_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0E2215C79EB000A5D3D /* ps2_psh_streamfile.h */; }; + 834FE10C215C79ED000A5D3D /* fsb5_interleave_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */; }; + 834FE10D215C79ED000A5D3D /* vag.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E4215C79EC000A5D3D /* vag.c */; }; + 834FE10E215C79ED000A5D3D /* ahv.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E5215C79EC000A5D3D /* ahv.c */; }; + 834FE10F215C79ED000A5D3D /* sdf.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E6215C79EC000A5D3D /* sdf.c */; }; + 834FE110215C79ED000A5D3D /* msv.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E7215C79EC000A5D3D /* msv.c */; }; + 834FE111215C79ED000A5D3D /* ck.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E8215C79EC000A5D3D /* ck.c */; }; 8350270D1ED119D200C25929 /* ps3_mta2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350270C1ED119D200C25929 /* ps3_mta2.c */; }; 835027131ED119E000C25929 /* mta2_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 835027121ED119E000C25929 /* mta2_decoder.c */; }; 8350C0551E071881009E0A93 /* xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0541E071881009E0A93 /* xma.c */; }; 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0591E071990009E0A93 /* ps2_svag_snk.c */; }; 836F6B4718BDB8880095E648 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 836F6B4518BDB8880095E648 /* InfoPlist.strings */; }; 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 */; }; 836F6F2318BDC2190095E648 /* coding.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6DE518BDC2180095E648 /* coding.h */; }; 836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE718BDC2180095E648 /* g721_decoder.c */; }; @@ -171,7 +221,6 @@ 836F6F4118BDC2190095E648 /* blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0418BDC2180095E648 /* blocked.c */; }; 836F6F4A18BDC2190095E648 /* interleave.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0D18BDC2180095E648 /* interleave.c */; }; 836F6F4D18BDC2190095E648 /* layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6E1018BDC2180095E648 /* layout.h */; }; - 836F6F5118BDC2190095E648 /* nolayout.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E1418BDC2180095E648 /* nolayout.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 */; }; @@ -197,7 +246,6 @@ 836F6F7D18BDC2190095E648 /* dc_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4118BDC2180095E648 /* dc_str.c */; }; 836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4318BDC2180095E648 /* dmsg_segh.c */; }; 836F6F8018BDC2190095E648 /* dsp_bdsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4418BDC2180095E648 /* dsp_bdsp.c */; }; - 836F6F8118BDC2190095E648 /* dsp_sth_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4518BDC2180095E648 /* dsp_sth_str.c */; }; 836F6F8218BDC2190095E648 /* ea_schl.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4618BDC2180095E648 /* ea_schl.c */; }; 836F6F8418BDC2190095E648 /* emff.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4818BDC2180095E648 /* emff.c */; }; 836F6F8518BDC2190095E648 /* exakt_sc.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4918BDC2180095E648 /* exakt_sc.c */; }; @@ -210,7 +258,6 @@ 836F6F8D18BDC2190095E648 /* gsp_gsb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5118BDC2180095E648 /* gsp_gsb.c */; }; 836F6F8E18BDC2190095E648 /* halpst.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5218BDC2180095E648 /* halpst.c */; }; 836F6F8F18BDC2190095E648 /* his.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5318BDC2180095E648 /* his.c */; }; - 836F6F9018BDC2190095E648 /* idsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5418BDC2180095E648 /* idsp.c */; }; 836F6F9118BDC2190095E648 /* ios_psnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5518BDC2180095E648 /* ios_psnd.c */; }; 836F6F9218BDC2190095E648 /* ish_isd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5618BDC2180095E648 /* ish_isd.c */; }; 836F6F9318BDC2190095E648 /* ivaud.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5718BDC2180095E648 /* ivaud.c */; }; @@ -287,7 +334,6 @@ 836F6FE418BDC2190095E648 /* ps2_leg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA818BDC2180095E648 /* ps2_leg.c */; }; 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA918BDC2180095E648 /* ps2_lpcm.c */; }; 836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAA18BDC2180095E648 /* ps2_mcg.c */; }; - 836F6FE718BDC2190095E648 /* ps2_mib.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAB18BDC2180095E648 /* ps2_mib.c */; }; 836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAC18BDC2180095E648 /* ps2_mic.c */; }; 836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAD18BDC2180095E648 /* ps2_mihb.c */; }; 836F6FEA18BDC2190095E648 /* ps2_msa.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAE18BDC2180095E648 /* ps2_msa.c */; }; @@ -306,19 +352,16 @@ 836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBE18BDC2190095E648 /* ps2_spm.c */; }; 836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBF18BDC2190095E648 /* ps2_sps.c */; }; 836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC018BDC2190095E648 /* ps2_ster.c */; }; - 836F6FFE18BDC2190095E648 /* ps2_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC218BDC2190095E648 /* ps2_str.c */; }; 836F6FFF18BDC2190095E648 /* ps2_strlr.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC318BDC2190095E648 /* ps2_strlr.c */; }; 836F700018BDC2190095E648 /* ps2_svag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC418BDC2190095E648 /* ps2_svag.c */; }; 836F700118BDC2190095E648 /* ps2_tec.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC518BDC2190095E648 /* ps2_tec.c */; }; 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC618BDC2190095E648 /* ps2_tk5.c */; }; - 836F700318BDC2190095E648 /* ps2_vag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC718BDC2190095E648 /* ps2_vag.c */; }; 836F700418BDC2190095E648 /* ps2_vas.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC818BDC2190095E648 /* ps2_vas.c */; }; 836F700518BDC2190095E648 /* ps2_vbk.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC918BDC2190095E648 /* ps2_vbk.c */; }; 836F700618BDC2190095E648 /* ps2_vgs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ECA18BDC2190095E648 /* ps2_vgs.c */; }; 836F700718BDC2190095E648 /* ps2_vgv.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ECB18BDC2190095E648 /* ps2_vgv.c */; }; 836F700818BDC2190095E648 /* ps2_vms.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ECC18BDC2190095E648 /* ps2_vms.c */; }; 836F700918BDC2190095E648 /* ps2_voi.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ECD18BDC2190095E648 /* ps2_voi.c */; }; - 836F700A18BDC2190095E648 /* ps2_vpk.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ECE18BDC2190095E648 /* ps2_vpk.c */; }; 836F700B18BDC2190095E648 /* ps2_wad.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ECF18BDC2190095E648 /* ps2_wad.c */; }; 836F700C18BDC2190095E648 /* ps2_wb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED018BDC2190095E648 /* ps2_wb.c */; }; 836F700D18BDC2190095E648 /* ps2_wmus.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED118BDC2190095E648 /* ps2_wmus.c */; }; @@ -326,7 +369,6 @@ 836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED318BDC2190095E648 /* ps2_xa30.c */; }; 836F701118BDC2190095E648 /* ps3_cps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED518BDC2190095E648 /* ps3_cps.c */; }; 836F701218BDC2190095E648 /* ps3_ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED618BDC2190095E648 /* ps3_ivag.c */; }; - 836F701318BDC2190095E648 /* ps3_klbs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED718BDC2190095E648 /* ps3_klbs.c */; }; 836F701418BDC2190095E648 /* ps3_msf.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED818BDC2190095E648 /* ps3_msf.c */; }; 836F701518BDC2190095E648 /* ps3_past.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED918BDC2190095E648 /* ps3_past.c */; }; 836F701918BDC2190095E648 /* psx_cdxa.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EDD18BDC2190095E648 /* psx_cdxa.c */; }; @@ -367,7 +409,6 @@ 836F703E18BDC2190095E648 /* wii_ras.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0218BDC2190095E648 /* wii_ras.c */; }; 836F703F18BDC2190095E648 /* wii_smp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0318BDC2190095E648 /* wii_smp.c */; }; 836F704018BDC2190095E648 /* wii_sng.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0418BDC2190095E648 /* wii_sng.c */; }; - 836F704118BDC2190095E648 /* wii_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0518BDC2190095E648 /* wii_str.c */; }; 836F704218BDC2190095E648 /* wii_sts.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0618BDC2190095E648 /* wii_sts.c */; }; 836F704318BDC2190095E648 /* wpd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0718BDC2190095E648 /* wpd.c */; }; 836F704418BDC2190095E648 /* ws_aud.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0818BDC2190095E648 /* ws_aud.c */; }; @@ -397,9 +438,6 @@ 83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E031ECBC1A4005C03D3 /* ta_aac.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 */; }; - 8374EE401F787AB600033E90 /* ffmpeg_decoder_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */; }; - 8374EE411F787AB600033E90 /* ffmpeg_decoder_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */; }; 838BDB641D3AF08C0022CA6F /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB611D3AF08C0022CA6F /* libavcodec.a */; }; 838BDB651D3AF08C0022CA6F /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB621D3AF08C0022CA6F /* libavformat.a */; }; 838BDB661D3AF08C0022CA6F /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB631D3AF08C0022CA6F /* libavutil.a */; }; @@ -678,7 +716,6 @@ 832389511D224C0800482226 /* hca_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca_decoder.c; sourceTree = ""; }; 83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = ""; }; 83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = ""; }; - 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 = ""; }; 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = ""; }; @@ -718,6 +755,58 @@ 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 = ""; }; + 834FE0AA215C798A000A5D3D /* acm_decoder_util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm_decoder_util.c; sourceTree = ""; }; + 834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_custom_opus.c; sourceTree = ""; }; + 834FE0AC215C798B000A5D3D /* acm_decoder_libacm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acm_decoder_libacm.h; sourceTree = ""; }; + 834FE0AD215C798B000A5D3D /* derf_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = derf_decoder.c; sourceTree = ""; }; + 834FE0AE215C798B000A5D3D /* atrac9_decoder.c.orig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = atrac9_decoder.c.orig; sourceTree = ""; }; + 834FE0AF215C798C000A5D3D /* acm_decoder_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm_decoder_decode.c; sourceTree = ""; }; + 834FE0B0215C798C000A5D3D /* xmd_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmd_decoder.c; sourceTree = ""; }; + 834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_mt_decoder_utk.h; sourceTree = ""; }; + 834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = celt_fsb_decoder.c; sourceTree = ""; }; + 834FE0BC215C79A8000A5D3D /* blocked_xa_aiff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xa_aiff.c; sourceTree = ""; }; + 834FE0BD215C79A9000A5D3D /* flat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = flat.c; sourceTree = ""; }; + 834FE0C0215C79E5000A5D3D /* ao.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ao.c; sourceTree = ""; }; + 834FE0C1215C79E5000A5D3D /* aif_asobo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aif_asobo.c; sourceTree = ""; }; + 834FE0C2215C79E6000A5D3D /* str_wav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = str_wav.c; sourceTree = ""; }; + 834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kma9_streamfile.h; sourceTree = ""; }; + 834FE0C4215C79E6000A5D3D /* fsb_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_interleave_streamfile.h; sourceTree = ""; }; + 834FE0C5215C79E6000A5D3D /* ue4opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ue4opus.c; sourceTree = ""; }; + 834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xvag_streamfile.h; sourceTree = ""; }; + 834FE0C7215C79E7000A5D3D /* apc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apc.c; sourceTree = ""; }; + 834FE0C8215C79E7000A5D3D /* a2m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = a2m.c; sourceTree = ""; }; + 834FE0C9215C79E7000A5D3D /* wv6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wv6.c; sourceTree = ""; }; + 834FE0CA215C79E7000A5D3D /* bnk_sony.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bnk_sony.c; sourceTree = ""; }; + 834FE0CB215C79E8000A5D3D /* wsi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wsi.c; sourceTree = ""; }; + 834FE0CC215C79E8000A5D3D /* wv2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wv2.c; sourceTree = ""; }; + 834FE0CD215C79E8000A5D3D /* derf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = derf.c; sourceTree = ""; }; + 834FE0CE215C79E8000A5D3D /* vis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vis.c; sourceTree = ""; }; + 834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adpcm_capcom.c; sourceTree = ""; }; + 834FE0D0215C79E8000A5D3D /* wavebatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wavebatch.c; sourceTree = ""; }; + 834FE0D1215C79E9000A5D3D /* nus3bank.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3bank.c; sourceTree = ""; }; + 834FE0D2215C79E9000A5D3D /* xau_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau_konami.c; sourceTree = ""; }; + 834FE0D3215C79E9000A5D3D /* vai.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vai.c; sourceTree = ""; }; + 834FE0D4215C79E9000A5D3D /* vpk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vpk.c; sourceTree = ""; }; + 834FE0D5215C79E9000A5D3D /* ps_headerless.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps_headerless.c; sourceTree = ""; }; + 834FE0D6215C79E9000A5D3D /* sqex_scd_sscf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqex_scd_sscf.c; sourceTree = ""; }; + 834FE0D7215C79EA000A5D3D /* svg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svg.c; sourceTree = ""; }; + 834FE0D8215C79EA000A5D3D /* csmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = csmp.c; sourceTree = ""; }; + 834FE0D9215C79EA000A5D3D /* rfrm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rfrm.c; sourceTree = ""; }; + 834FE0DA215C79EA000A5D3D /* ea_schl_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_schl_streamfile.h; sourceTree = ""; }; + 834FE0DB215C79EA000A5D3D /* nxa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nxa.c; sourceTree = ""; }; + 834FE0DC215C79EA000A5D3D /* xmd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmd.c; sourceTree = ""; }; + 834FE0DD215C79EB000A5D3D /* utk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utk.c; sourceTree = ""; }; + 834FE0DE215C79EB000A5D3D /* mib_mih.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mib_mih.c; sourceTree = ""; }; + 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hd3_bd3.c; sourceTree = ""; }; + 834FE0E0215C79EB000A5D3D /* idsp_ie.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idsp_ie.c; sourceTree = ""; }; + 834FE0E1215C79EB000A5D3D /* nub_idsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_idsp.c; sourceTree = ""; }; + 834FE0E2215C79EB000A5D3D /* ps2_psh_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ps2_psh_streamfile.h; sourceTree = ""; }; + 834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb5_interleave_streamfile.h; sourceTree = ""; }; + 834FE0E4215C79EC000A5D3D /* vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vag.c; sourceTree = ""; }; + 834FE0E5215C79EC000A5D3D /* ahv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ahv.c; sourceTree = ""; }; + 834FE0E6215C79EC000A5D3D /* sdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sdf.c; sourceTree = ""; }; + 834FE0E7215C79EC000A5D3D /* msv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msv.c; sourceTree = ""; }; + 834FE0E8215C79EC000A5D3D /* ck.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ck.c; sourceTree = ""; }; 8350270C1ED119D200C25929 /* ps3_mta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_mta2.c; sourceTree = ""; }; 835027121ED119E000C25929 /* mta2_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mta2_decoder.c; sourceTree = ""; }; 8350C0541E071881009E0A93 /* xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma.c; sourceTree = ""; }; @@ -726,7 +815,6 @@ 836F6B4418BDB8880095E648 /* libvgmstream-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "libvgmstream-Info.plist"; sourceTree = ""; }; 836F6B4618BDB8880095E648 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -756,7 +844,6 @@ 836F6E0418BDC2180095E648 /* blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked.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 = ""; }; - 836F6E1418BDC2180095E648 /* nolayout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nolayout.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 = ""; }; @@ -782,7 +869,6 @@ 836F6E4118BDC2180095E648 /* dc_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dc_str.c; sourceTree = ""; }; 836F6E4318BDC2180095E648 /* dmsg_segh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dmsg_segh.c; sourceTree = ""; }; 836F6E4418BDC2180095E648 /* dsp_bdsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_bdsp.c; sourceTree = ""; }; - 836F6E4518BDC2180095E648 /* dsp_sth_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_sth_str.c; sourceTree = ""; }; 836F6E4618BDC2180095E648 /* ea_schl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl.c; sourceTree = ""; }; 836F6E4818BDC2180095E648 /* emff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emff.c; sourceTree = ""; }; 836F6E4918BDC2180095E648 /* exakt_sc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exakt_sc.c; sourceTree = ""; }; @@ -795,7 +881,6 @@ 836F6E5118BDC2180095E648 /* gsp_gsb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gsp_gsb.c; sourceTree = ""; }; 836F6E5218BDC2180095E648 /* halpst.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = halpst.c; sourceTree = ""; }; 836F6E5318BDC2180095E648 /* his.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = his.c; sourceTree = ""; }; - 836F6E5418BDC2180095E648 /* idsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idsp.c; sourceTree = ""; }; 836F6E5518BDC2180095E648 /* ios_psnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ios_psnd.c; sourceTree = ""; }; 836F6E5618BDC2180095E648 /* ish_isd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ish_isd.c; sourceTree = ""; }; 836F6E5718BDC2180095E648 /* ivaud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ivaud.c; sourceTree = ""; }; @@ -872,7 +957,6 @@ 836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = ""; }; 836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = ""; }; 836F6EAA18BDC2180095E648 /* ps2_mcg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mcg.c; sourceTree = ""; }; - 836F6EAB18BDC2180095E648 /* ps2_mib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mib.c; sourceTree = ""; }; 836F6EAC18BDC2180095E648 /* ps2_mic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mic.c; sourceTree = ""; }; 836F6EAD18BDC2180095E648 /* ps2_mihb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mihb.c; sourceTree = ""; }; 836F6EAE18BDC2180095E648 /* ps2_msa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_msa.c; sourceTree = ""; }; @@ -891,19 +975,16 @@ 836F6EBE18BDC2190095E648 /* ps2_spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_spm.c; sourceTree = ""; }; 836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = ""; }; 836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = ""; }; - 836F6EC218BDC2190095E648 /* ps2_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_str.c; sourceTree = ""; }; 836F6EC318BDC2190095E648 /* ps2_strlr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_strlr.c; sourceTree = ""; }; 836F6EC418BDC2190095E648 /* ps2_svag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag.c; sourceTree = ""; }; 836F6EC518BDC2190095E648 /* ps2_tec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tec.c; sourceTree = ""; }; 836F6EC618BDC2190095E648 /* ps2_tk5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tk5.c; sourceTree = ""; }; - 836F6EC718BDC2190095E648 /* ps2_vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vag.c; sourceTree = ""; }; 836F6EC818BDC2190095E648 /* ps2_vas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vas.c; sourceTree = ""; }; 836F6EC918BDC2190095E648 /* ps2_vbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vbk.c; sourceTree = ""; }; 836F6ECA18BDC2190095E648 /* ps2_vgs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vgs.c; sourceTree = ""; }; 836F6ECB18BDC2190095E648 /* ps2_vgv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vgv.c; sourceTree = ""; }; 836F6ECC18BDC2190095E648 /* ps2_vms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vms.c; sourceTree = ""; }; 836F6ECD18BDC2190095E648 /* ps2_voi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_voi.c; sourceTree = ""; }; - 836F6ECE18BDC2190095E648 /* ps2_vpk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vpk.c; sourceTree = ""; }; 836F6ECF18BDC2190095E648 /* ps2_wad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wad.c; sourceTree = ""; }; 836F6ED018BDC2190095E648 /* ps2_wb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wb.c; sourceTree = ""; }; 836F6ED118BDC2190095E648 /* ps2_wmus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wmus.c; sourceTree = ""; }; @@ -911,7 +992,6 @@ 836F6ED318BDC2190095E648 /* ps2_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa30.c; sourceTree = ""; }; 836F6ED518BDC2190095E648 /* ps3_cps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_cps.c; sourceTree = ""; }; 836F6ED618BDC2190095E648 /* ps3_ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_ivag.c; sourceTree = ""; }; - 836F6ED718BDC2190095E648 /* ps3_klbs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_klbs.c; sourceTree = ""; }; 836F6ED818BDC2190095E648 /* ps3_msf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_msf.c; sourceTree = ""; }; 836F6ED918BDC2190095E648 /* ps3_past.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_past.c; sourceTree = ""; }; 836F6EDD18BDC2190095E648 /* psx_cdxa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psx_cdxa.c; sourceTree = ""; }; @@ -952,7 +1032,6 @@ 836F6F0218BDC2190095E648 /* wii_ras.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_ras.c; sourceTree = ""; }; 836F6F0318BDC2190095E648 /* wii_smp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_smp.c; sourceTree = ""; }; 836F6F0418BDC2190095E648 /* wii_sng.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_sng.c; sourceTree = ""; }; - 836F6F0518BDC2190095E648 /* wii_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_str.c; sourceTree = ""; }; 836F6F0618BDC2190095E648 /* wii_sts.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_sts.c; sourceTree = ""; }; 836F6F0718BDC2190095E648 /* wpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wpd.c; sourceTree = ""; }; 836F6F0818BDC2190095E648 /* ws_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ws_aud.c; sourceTree = ""; }; @@ -982,9 +1061,6 @@ 83709E031ECBC1A4005C03D3 /* ta_aac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ta_aac.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 = ""; }; - 8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ffmpeg_decoder_utils.h; sourceTree = ""; }; - 8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils.c; sourceTree = ""; }; 838BDB611D3AF08C0022CA6F /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = ""; }; 838BDB621D3AF08C0022CA6F /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = ""; }; 838BDB631D3AF08C0022CA6F /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = ""; }; @@ -1200,22 +1276,25 @@ 836F6DDF18BDC2180095E648 /* coding */ = { isa = PBXGroup; children = ( + 834FE0AF215C798C000A5D3D /* acm_decoder_decode.c */, + 834FE0AC215C798B000A5D3D /* acm_decoder_libacm.h */, + 834FE0AA215C798A000A5D3D /* acm_decoder_util.c */, 836F6DE018BDC2180095E648 /* acm_decoder.c */, - 836F6DE118BDC2180095E648 /* acm_decoder.h */, 836F6DE218BDC2180095E648 /* adx_decoder.c */, 8315958320FEC831007002F0 /* asf_decoder.c */, 8306B08120984517000302D4 /* at3plus_decoder.c */, 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, + 834FE0AE215C798B000A5D3D /* atrac9_decoder.c.orig */, + 834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */, 831BA6221EAC61CB00CF89B0 /* coding_utils.c */, 836F6DE518BDC2180095E648 /* coding.h */, + 834FE0AD215C798B000A5D3D /* derf_decoder.c */, + 834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.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 */, - 8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */, + 834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */, 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */, 836F6DE918BDC2180095E648 /* g72x_state.h */, 83D7318B1A749EEE00CA1366 /* g719_decoder.c */, @@ -1259,6 +1338,7 @@ 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, 836F6DFD18BDC2180095E648 /* ws_decoder.c */, 836F6DFE18BDC2180095E648 /* xa_decoder.c */, + 834FE0B0215C798C000A5D3D /* xmd_decoder.c */, 8306B08220984517000302D4 /* yamaha_decoder.c */, ); path = coding; @@ -1301,14 +1381,15 @@ 8306B0A120984551000302D4 /* blocked_vs.c */, 8306B09D20984551000302D4 /* blocked_ws_aud.c */, 8306B09F20984551000302D4 /* blocked_wsi.c */, + 834FE0BC215C79A8000A5D3D /* blocked_xa_aiff.c */, 8306B0912098454E000302D4 /* blocked_xa.c */, 83A21F7A201D895B000F04B9 /* blocked_xvag.c */, 8306B08C2098454D000302D4 /* blocked_xvas.c */, 836F6E0418BDC2180095E648 /* blocked.c */, + 834FE0BD215C79A9000A5D3D /* flat.c */, 836F6E0D18BDC2180095E648 /* interleave.c */, 8306B0902098454E000302D4 /* layered.c */, 836F6E1018BDC2180095E648 /* layout.h */, - 836F6E1418BDC2180095E648 /* nolayout.c */, 8306B0892098454D000302D4 /* segmented.c */, ); path = layout; @@ -1318,19 +1399,25 @@ isa = PBXGroup; children = ( 836F6E2918BDC2180095E648 /* 2dx9.c */, + 834FE0C8215C79E7000A5D3D /* a2m.c */, 8306B0C82098458D000302D4 /* aax_utf.h */, 836F6E2A18BDC2180095E648 /* aax.c */, 836F6E2B18BDC2180095E648 /* acm.c */, + 834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */, 836F6E2C18BDC2180095E648 /* ads.c */, 8349A9021FE6258100E26435 /* adx_keys.h */, 831BA60E1EAC61A500CF89B0 /* adx.c */, 8349A9001FE6258000E26435 /* afc.c */, 836F6E2F18BDC2180095E648 /* agsc.c */, + 834FE0E5215C79EC000A5D3D /* ahv.c */, 836F6E3018BDC2180095E648 /* ahx.c */, + 834FE0C1215C79E5000A5D3D /* aif_asobo.c */, 836F6E3118BDC2180095E648 /* aifc.c */, 8349A8F61FE6257E00E26435 /* aix_streamfile.h */, 836F6E3218BDC2180095E648 /* aix.c */, 836F6E3318BDC2180095E648 /* akb.c */, + 834FE0C0215C79E5000A5D3D /* ao.c */, + 834FE0C7215C79E7000A5D3D /* apc.c */, 836F6E3418BDC2180095E648 /* apple_caff.c */, 8315958820FEC83F007002F0 /* asf.c */, 836F6E3518BDC2180095E648 /* ast.c */, @@ -1347,11 +1434,14 @@ 83A5F75E198DF021009AF94C /* bfwav.c */, 836F6E3818BDC2180095E648 /* bgw.c */, 83299FCE1E7660C7003A3242 /* bik.c */, + 834FE0CA215C79E7000A5D3D /* bnk_sony.c */, 836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3A18BDC2180095E648 /* brstm.c */, 83EDE5D71A70951A005F5D84 /* btsnd.c */, 8306B0CF2098458F000302D4 /* caf.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */, + 834FE0E8215C79EC000A5D3D /* ck.c */, + 834FE0D8215C79EA000A5D3D /* csmp.c */, 836F6E3C18BDC2180095E648 /* Cstr.c */, 836F6E3D18BDC2180095E648 /* dc_asd.c */, 836F6E3E18BDC2180095E648 /* dc_dcsw_dcs.c */, @@ -1359,14 +1449,15 @@ 836F6E4018BDC2180095E648 /* dc_kcey.c */, 836F6E4118BDC2180095E648 /* dc_str.c */, 8349A8EE1FE6257C00E26435 /* dec.c */, + 834FE0CD215C79E8000A5D3D /* derf.c */, 836F6E4318BDC2180095E648 /* dmsg_segh.c */, 83299FCF1E7660C7003A3242 /* dsp_adx.c */, 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 */, + 834FE0DA215C79EA000A5D3D /* ea_schl_streamfile.h */, 836F6E4618BDC2180095E648 /* ea_schl.c */, 83EED5D1203A8BC7008BEB45 /* ea_swvr.c */, 8306B0C42098458D000302D4 /* ea_wve_ad10.c */, @@ -1379,8 +1470,10 @@ 836F6E4B18BDC2180095E648 /* ffw.c */, 8349A8FD1FE6257F00E26435 /* flx.c */, 83A21F81201D8981000F04B9 /* fsb_encrypted.c */, + 834FE0C4215C79E6000A5D3D /* fsb_interleave_streamfile.h */, 83A21F7E201D8980000F04B9 /* fsb_keys.h */, 836F6E4C18BDC2180095E648 /* fsb.c */, + 834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */, 83F5F8821908D0A400C8E65F /* fsb5.c */, 834D3A6D19F47C98001C54F6 /* g1l.c */, 836F6E4D18BDC2180095E648 /* gca.c */, @@ -1392,12 +1485,14 @@ 836F6E5218BDC2180095E648 /* halpst.c */, 83AA5D211F6E2F9C0020821C /* hca_keys.h */, 8323894F1D2246C300482226 /* hca.c */, + 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */, 836F6E5318BDC2180095E648 /* his.c */, - 836F6E5418BDC2180095E648 /* idsp.c */, + 834FE0E0215C79EB000A5D3D /* idsp_ie.c */, 836F6E5518BDC2180095E648 /* ios_psnd.c */, 836F6E5618BDC2180095E648 /* ish_isd.c */, 836F6E5718BDC2180095E648 /* ivaud.c */, 836F6E5818BDC2180095E648 /* ivb.c */, + 834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */, 83A21F83201D8981000F04B9 /* kma9.c */, 836F6E5918BDC2180095E648 /* kraw.c */, 830EBE122004656E0023AA10 /* ktss.c */, @@ -1407,11 +1502,13 @@ 83709E001ECBC1A4005C03D3 /* mc3.c */, 83EDE5D61A70951A005F5D84 /* mca.c */, 836F6E5E18BDC2180095E648 /* meta.h */, + 834FE0DE215C79EB000A5D3D /* mib_mih.c */, 836F6E5F18BDC2180095E648 /* mn_str.c */, 8349A9031FE6258100E26435 /* mogg.c */, 836F6E6018BDC2180095E648 /* mp4.c */, 8306B0CB2098458E000302D4 /* msb_msh.c */, 83709E011ECBC1A4005C03D3 /* mss.c */, + 834FE0E7215C79EC000A5D3D /* msv.c */, 836F6E6118BDC2180095E648 /* msvp.c */, 836F6E6218BDC2180095E648 /* mus_acm.c */, 836F6E6318BDC2180095E648 /* musc.c */, @@ -1445,9 +1542,12 @@ 8349A8FA1FE6257E00E26435 /* ngc_vid1.c */, 836F6E7E18BDC2180095E648 /* ngc_ymf.c */, 836F6E7F18BDC2180095E648 /* ngca.c */, + 834FE0E1215C79EB000A5D3D /* nub_idsp.c */, 83AB8C731E8072A100086084 /* nub_vag.c */, 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */, + 834FE0D1215C79E9000A5D3D /* nus3bank.c */, 836F6E8118BDC2180095E648 /* nwa.c */, + 834FE0DB215C79EA000A5D3D /* nxa.c */, 8306B0C02098458C000302D4 /* nxap.c */, 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */, 831BA60F1EAC61A500CF89B0 /* ogl.c */, @@ -1470,6 +1570,7 @@ 836F6E8C18BDC2180095E648 /* pos.c */, 8306B0C52098458D000302D4 /* ppst_streamfile.h */, 8306B0D620984590000302D4 /* ppst.c */, + 834FE0D5215C79E9000A5D3D /* ps_headerless.c */, 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */, 836F6E8E18BDC2180095E648 /* ps2_adm.c */, 836F6E8F18BDC2180095E648 /* ps2_ads.c */, @@ -1498,7 +1599,6 @@ 836F6EA818BDC2180095E648 /* ps2_leg.c */, 836F6EA918BDC2180095E648 /* ps2_lpcm.c */, 836F6EAA18BDC2180095E648 /* ps2_mcg.c */, - 836F6EAB18BDC2180095E648 /* ps2_mib.c */, 836F6EAC18BDC2180095E648 /* ps2_mic.c */, 836F6EAD18BDC2180095E648 /* ps2_mihb.c */, 836F6EAE18BDC2180095E648 /* ps2_msa.c */, @@ -1507,6 +1607,7 @@ 836F6EB218BDC2180095E648 /* ps2_p2bt.c */, 8349A8F21FE6257D00E26435 /* ps2_pcm.c */, 836F6EB318BDC2180095E648 /* ps2_pnb.c */, + 834FE0E2215C79EB000A5D3D /* ps2_psh_streamfile.h */, 836F6EB418BDC2180095E648 /* ps2_psh.c */, 836F6EB618BDC2180095E648 /* ps2_rnd.c */, 836F6EB718BDC2180095E648 /* ps2_rstm.c */, @@ -1518,13 +1619,11 @@ 836F6EBE18BDC2190095E648 /* ps2_spm.c */, 836F6EBF18BDC2190095E648 /* ps2_sps.c */, 836F6EC018BDC2190095E648 /* ps2_ster.c */, - 836F6EC218BDC2190095E648 /* ps2_str.c */, 836F6EC318BDC2190095E648 /* ps2_strlr.c */, 8350C0591E071990009E0A93 /* ps2_svag_snk.c */, 836F6EC418BDC2190095E648 /* ps2_svag.c */, 836F6EC518BDC2190095E648 /* ps2_tec.c */, 836F6EC618BDC2190095E648 /* ps2_tk5.c */, - 836F6EC718BDC2190095E648 /* ps2_vag.c */, 836F6EC818BDC2190095E648 /* ps2_vas.c */, 836F6EC918BDC2190095E648 /* ps2_vbk.c */, 831BA6101EAC61A500CF89B0 /* ps2_vds_vdm.c */, @@ -1532,7 +1631,6 @@ 836F6ECB18BDC2190095E648 /* ps2_vgv.c */, 836F6ECC18BDC2190095E648 /* ps2_vms.c */, 836F6ECD18BDC2190095E648 /* ps2_voi.c */, - 836F6ECE18BDC2190095E648 /* ps2_vpk.c */, 836F6ECF18BDC2190095E648 /* ps2_wad.c */, 836F6ED018BDC2190095E648 /* ps2_wb.c */, 836F6ED118BDC2190095E648 /* ps2_wmus.c */, @@ -1541,7 +1639,6 @@ 836F6ED318BDC2190095E648 /* ps2_xa30.c */, 836F6ED518BDC2190095E648 /* ps3_cps.c */, 836F6ED618BDC2190095E648 /* ps3_ivag.c */, - 836F6ED718BDC2190095E648 /* ps3_klbs.c */, 836F6ED818BDC2190095E648 /* ps3_msf.c */, 8350270C1ED119D200C25929 /* ps3_mta2.c */, 836F6ED918BDC2190095E648 /* ps3_past.c */, @@ -1550,6 +1647,7 @@ 836F6EDF18BDC2190095E648 /* psx_gms.c */, 836F6EE118BDC2190095E648 /* raw.c */, 836F6EE218BDC2190095E648 /* redspark.c */, + 834FE0D9215C79EA000A5D3D /* rfrm.c */, 836F6EE318BDC2190095E648 /* riff.c */, 836F6EE418BDC2190095E648 /* rkv.c */, 836F6EE518BDC2190095E648 /* rs03.c */, @@ -1565,6 +1663,7 @@ 836F6EED18BDC2190095E648 /* sat_sap.c */, 8349A8F51FE6257D00E26435 /* scd_pcm.c */, 836F6EEE18BDC2190095E648 /* sd9.c */, + 834FE0E6215C79EC000A5D3D /* sdf.c */, 836F6EEF18BDC2190095E648 /* sdt.c */, 836F6EF018BDC2190095E648 /* seg.c */, 836F6EF118BDC2190095E648 /* sfl.c */, @@ -1575,6 +1674,7 @@ 8306B0C72098458D000302D4 /* smv.c */, 83A21F82201D8981000F04B9 /* sps_n1.c */, 836F6EF318BDC2190095E648 /* spt_spd.c */, + 834FE0D6215C79E9000A5D3D /* sqex_scd_sscf.c */, 8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */, 836F6EF418BDC2190095E648 /* sqex_scd.c */, 83A21F84201D8981000F04B9 /* sqex_sead.c */, @@ -1582,7 +1682,9 @@ 83AA5D231F6E2F9C0020821C /* stm.c */, 836F6EF618BDC2190095E648 /* str_asr.c */, 836F6EF718BDC2190095E648 /* str_snds.c */, + 834FE0C2215C79E6000A5D3D /* str_wav.c */, 836F6EF818BDC2190095E648 /* stx.c */, + 834FE0D7215C79EA000A5D3D /* svg.c */, 836F6EF918BDC2190095E648 /* svs.c */, 831BA6121EAC61A500CF89B0 /* sxd.c */, 83709E031ECBC1A4005C03D3 /* ta_aac.c */, @@ -1597,8 +1699,14 @@ 8306B0CA2098458E000302D4 /* ubi_lyn.c */, 831BA6131EAC61A500CF89B0 /* ubi_raki.c */, 8349A8F41FE6257D00E26435 /* ubi_sb.c */, + 834FE0C5215C79E6000A5D3D /* ue4opus.c */, + 834FE0DD215C79EB000A5D3D /* utk.c */, + 834FE0E4215C79EC000A5D3D /* vag.c */, + 834FE0D3215C79E9000A5D3D /* vai.c */, 831BA6141EAC61A500CF89B0 /* vawx.c */, 836F6EFD18BDC2190095E648 /* vgs.c */, + 834FE0CE215C79E8000A5D3D /* vis.c */, + 834FE0D4215C79E9000A5D3D /* vpk.c */, 836F6EFE18BDC2190095E648 /* vs.c */, 8349A8F91FE6257E00E26435 /* vsf_tta.c */, 836F6EFF18BDC2190095E648 /* vsf.c */, @@ -1606,22 +1714,26 @@ 8306B0C22098458C000302D4 /* waf.c */, 8306B0D02098458F000302D4 /* wave_segmented.c */, 8306B0C92098458E000302D4 /* wave.c */, + 834FE0D0215C79E8000A5D3D /* wavebatch.c */, 83CAB8E11F0B0745001BC993 /* wii_04sw.c */, 836F6F0018BDC2190095E648 /* wii_bns.c */, 836F6F0118BDC2190095E648 /* wii_mus.c */, 836F6F0218BDC2190095E648 /* wii_ras.c */, 836F6F0318BDC2190095E648 /* wii_smp.c */, 836F6F0418BDC2190095E648 /* wii_sng.c */, - 836F6F0518BDC2190095E648 /* wii_str.c */, 836F6F0618BDC2190095E648 /* wii_sts.c */, 836F6F0718BDC2190095E648 /* wpd.c */, 836F6F0818BDC2190095E648 /* ws_aud.c */, + 834FE0CB215C79E8000A5D3D /* wsi.c */, + 834FE0CC215C79E8000A5D3D /* wv2.c */, + 834FE0C9215C79E7000A5D3D /* wv6.c */, 836F6F0918BDC2190095E648 /* wvs.c */, 83FF0EBB1E93282100C58054 /* wwise.c */, 83AB8C741E8072A100086084 /* x360_ast.c */, 831BA6151EAC61A500CF89B0 /* x360_cxs.c */, 831BA6171EAC61A500CF89B0 /* x360_pasx.c */, 836F6F0A18BDC2190095E648 /* x360_tra.c */, + 834FE0D2215C79E9000A5D3D /* xau_konami.c */, 833A7A2D1ED11961003EC53E /* xau.c */, 836F6F0B18BDC2190095E648 /* xbox_hlwav.c */, 836F6F0C18BDC2190095E648 /* xbox_ims.c */, @@ -1629,8 +1741,10 @@ 836F6F0F18BDC2190095E648 /* xbox_xmu.c */, 836F6F1018BDC2190095E648 /* xbox_xvas.c */, 8350C0541E071881009E0A93 /* xma.c */, + 834FE0DC215C79EA000A5D3D /* xmd.c */, 830EBE112004656E0023AA10 /* xnb.c */, 836F6F1218BDC2190095E648 /* xss.c */, + 834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */, 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, 836F6F1318BDC2190095E648 /* xwb.c */, 83A21F7D201D8980000F04B9 /* xwc.c */, @@ -1676,30 +1790,36 @@ buildActionMask = 2147483647; files = ( 836F705518BDC2190095E648 /* streamtypes.h in Headers */, - 836F6F1F18BDC2190095E648 /* acm_decoder.h in Headers */, 836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */, 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, + 834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, + 834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */, 8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */, 836F6F2718BDC2190095E648 /* g72x_state.h in Headers */, 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, + 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */, 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */, 836F705418BDC2190095E648 /* streamfile.h in Headers */, - 8374EE401F787AB600033E90 /* ffmpeg_decoder_utils.h in Headers */, 83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */, 8323894B1D22419B00482226 /* clHCA.h in Headers */, 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */, + 834FE10C215C79ED000A5D3D /* fsb5_interleave_streamfile.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 */, + 834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */, 839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */, + 834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */, 48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */, + 834FE10B215C79ED000A5D3D /* ps2_psh_streamfile.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 */, + 834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */, 8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */, 8349A91B1FE6258200E26435 /* adx_keys.h in Headers */, 836F6F4D18BDC2190095E648 /* layout.h in Headers */, @@ -1854,6 +1974,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 834FE0B7215C798C000A5D3D /* atrac9_decoder.c.orig in Resources */, 836F6B4718BDB8880095E648 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1867,6 +1988,7 @@ files = ( 839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */, 83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */, + 834FE104215C79ED000A5D3D /* nxa.c in Sources */, 8306B0B020984552000302D4 /* blocked_ps2_strlr.c in Sources */, 8349A9071FE6258200E26435 /* dec.c in Sources */, 839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */, @@ -1876,11 +1998,13 @@ 839E21E31F2EDAF100EE54D7 /* mpeg_custom_utils_ahx.c in Sources */, 8306B0AF20984552000302D4 /* blocked_rws.c in Sources */, 839E21EB1F2EDB0600EE54D7 /* sk_aud.c in Sources */, + 834FE0F1215C79ED000A5D3D /* a2m.c in Sources */, 8301659A1F256BD000CA0941 /* txth.c in Sources */, 8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */, 8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */, 83CAB8E31F0B0755001BC993 /* pc_xa30.c in Sources */, 83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */, + 834FE0EB215C79ED000A5D3D /* str_wav.c in Sources */, 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */, 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */, 83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */, @@ -1888,7 +2012,6 @@ 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 */, 836F703818BDC2190095E648 /* ubi_ckd.c in Sources */, 836F705318BDC2190095E648 /* streamfile.c in Sources */, 836F6F7418BDC2190095E648 /* bgw.c in Sources */, @@ -1899,6 +2022,7 @@ 836F6F7D18BDC2190095E648 /* dc_str.c in Sources */, 83A5F75F198DF021009AF94C /* bfwav.c in Sources */, 836F702018BDC2190095E648 /* rkv.c in Sources */, + 834FE0F4215C79ED000A5D3D /* wsi.c in Sources */, 836F703218BDC2190095E648 /* str_asr.c in Sources */, 836F6FB218BDC2190095E648 /* ngc_gcub.c in Sources */, 836F702818BDC2190095E648 /* sat_dvi.c in Sources */, @@ -1919,13 +2043,16 @@ 836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */, 8306B0DD20984590000302D4 /* waf.c in Sources */, 8306B0B320984552000302D4 /* blocked_thp.c in Sources */, + 834FE0B6215C798C000A5D3D /* derf_decoder.c in Sources */, 836F702318BDC2190095E648 /* rsf.c in Sources */, + 834FE109215C79ED000A5D3D /* idsp_ie.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 */, + 834FE110215C79ED000A5D3D /* msv.c in Sources */, 832389521D224C0800482226 /* hca_decoder.c in Sources */, 836F6F9418BDC2190095E648 /* ivb.c in Sources */, 836F6F8D18BDC2190095E648 /* gsp_gsb.c in Sources */, @@ -1942,6 +2069,8 @@ 836F6F6818BDC2190095E648 /* ads.c in Sources */, 8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */, 83AB8C761E8072A100086084 /* x360_ast.c in Sources */, + 834FE105215C79ED000A5D3D /* xmd.c in Sources */, + 834FE0F6215C79ED000A5D3D /* derf.c in Sources */, 836F6F8B18BDC2190095E648 /* genh.c in Sources */, 8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */, 836F705118BDC2190095E648 /* zsd.c in Sources */, @@ -1962,6 +2091,7 @@ 836F705618BDC2190095E648 /* util.c in Sources */, 8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */, 8306B0EA20984590000302D4 /* caf.c in Sources */, + 834FE107215C79ED000A5D3D /* mib_mih.c in Sources */, 836F703618BDC2190095E648 /* thp.c in Sources */, 836F6F7818BDC2190095E648 /* Cstr.c in Sources */, 836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */, @@ -1971,8 +2101,8 @@ 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */, 83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */, 836F6FA818BDC2190095E648 /* nds_swav.c in Sources */, - 8374EE411F787AB600033E90 /* ffmpeg_decoder_utils.c in Sources */, 8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */, + 834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */, 833A7A2E1ED11961003EC53E /* xau.c in Sources */, 836F6FB518BDC2190095E648 /* ngc_pdt.c in Sources */, 836F6F6C18BDC2190095E648 /* ahx.c in Sources */, @@ -1993,10 +2123,13 @@ 836F6F6618BDC2190095E648 /* aax.c in Sources */, 836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */, 8306B0BC20984552000302D4 /* blocked_vs.c in Sources */, + 834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */, 836F6F6718BDC2190095E648 /* acm.c in Sources */, + 834FE0FD215C79ED000A5D3D /* vpk.c in Sources */, 8306B0DF20984590000302D4 /* ea_wve_ad10.c in Sources */, 836F6F8A18BDC2190095E648 /* gcsw.c in Sources */, 836F6F9C18BDC2190095E648 /* mp4.c in Sources */, + 834FE101215C79ED000A5D3D /* csmp.c in Sources */, 8306B0A720984552000302D4 /* blocked_xvas.c in Sources */, 8349A9101FE6258200E26435 /* ea_eaac.c in Sources */, 836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */, @@ -2008,11 +2141,14 @@ 836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */, 83709E071ECBC1A4005C03D3 /* mss.c in Sources */, 836F6F8F18BDC2190095E648 /* his.c in Sources */, + 834FE0E9215C79ED000A5D3D /* ao.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 */, + 834FE108215C79ED000A5D3D /* hd3_bd3.c in Sources */, 836F6FD318BDC2190095E648 /* ps2_ccc.c in Sources */, + 834FE0FC215C79ED000A5D3D /* vai.c in Sources */, 83AA5D171F6E2F600020821C /* mpeg_custom_utils_ealayer3.c in Sources */, 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */, 836F704C18BDC2190095E648 /* xbox_xvas.c in Sources */, @@ -2021,7 +2157,6 @@ 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 */, 836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */, 836F6F7918BDC2190095E648 /* dc_asd.c in Sources */, @@ -2029,15 +2164,16 @@ 836F701A18BDC2190095E648 /* psx_fag.c in Sources */, 836F703B18BDC2190095E648 /* vsf.c in Sources */, 836F6F8218BDC2190095E648 /* ea_schl.c in Sources */, - 836F700A18BDC2190095E648 /* ps2_vpk.c in Sources */, 836F6F7318BDC2190095E648 /* bcstm.c in Sources */, 83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */, 836F704018BDC2190095E648 /* wii_sng.c in Sources */, + 834FE10D215C79ED000A5D3D /* vag.c in Sources */, 8349A9131FE6258200E26435 /* ngc_vid1.c in Sources */, 8350C0551E071881009E0A93 /* xma.c in Sources */, 836F6F3218BDC2190095E648 /* ngc_dsp_decoder.c in Sources */, 836F704218BDC2190095E648 /* wii_sts.c in Sources */, 836F703918BDC2190095E648 /* vgs.c in Sources */, + 834FE0F8215C79ED000A5D3D /* adpcm_capcom.c in Sources */, 836F6F2C18BDC2190095E648 /* mp4_aac_decoder.c in Sources */, 836F701F18BDC2190095E648 /* riff.c in Sources */, 83AA5D191F6E2F600020821C /* ea_xas_decoder.c in Sources */, @@ -2064,6 +2200,7 @@ 836F6FFF18BDC2190095E648 /* ps2_strlr.c in Sources */, 836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */, 8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */, + 834FE0EA215C79ED000A5D3D /* aif_asobo.c in Sources */, 836F700418BDC2190095E648 /* ps2_vas.c in Sources */, 836F6F9818BDC2190095E648 /* mattel_hyperscan.c in Sources */, 8306B0BA20984552000302D4 /* blocked_wsi.c in Sources */, @@ -2074,22 +2211,21 @@ 836F702218BDC2190095E648 /* rsd.c in Sources */, 8349A90D1FE6258200E26435 /* ubi_sb.c in Sources */, 83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */, + 834FE0BB215C798C000A5D3D /* celt_fsb_decoder.c in Sources */, 836F702518BDC2190095E648 /* rwx.c in Sources */, 836F6F3A18BDC2190095E648 /* sdx2_decoder.c in Sources */, 836F6FAF18BDC2190095E648 /* ngc_dsp_std.c in Sources */, 836F6F7118BDC2190095E648 /* ast.c in Sources */, + 834FE0BF215C79A9000A5D3D /* flat.c in Sources */, 836F6FE318BDC2190095E648 /* ps2_khv.c in Sources */, - 836F701318BDC2190095E648 /* ps3_klbs.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 */, 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 */, 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */, 836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */, @@ -2097,12 +2233,12 @@ 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 */, + 834FE106215C79ED000A5D3D /* utk.c in Sources */, 831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */, 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */, 83AA5D271F6E2F9C0020821C /* stm.c in Sources */, @@ -2121,9 +2257,9 @@ 831BA61F1EAC61A500CF89B0 /* x360_cxs.c in Sources */, 836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */, 8349A90B1FE6258200E26435 /* ps2_pcm.c in Sources */, - 836F6FFE18BDC2190095E648 /* ps2_str.c in Sources */, 836F6F4A18BDC2190095E648 /* interleave.c in Sources */, 83EDE5D81A70951A005F5D84 /* mca.c in Sources */, + 834FE0F3215C79ED000A5D3D /* bnk_sony.c in Sources */, 8306B0AE20984552000302D4 /* blocked_filp.c in Sources */, 831BA61B1EAC61A500CF89B0 /* sgxd.c in Sources */, 838BDB6C1D3AFAB10022CA6F /* ffmpeg_decoder.c in Sources */, @@ -2131,20 +2267,22 @@ 836F6FA518BDC2190095E648 /* nds_rrds.c in Sources */, 836F702F18BDC2190095E648 /* spt_spd.c in Sources */, 836F704618BDC2190095E648 /* x360_tra.c in Sources */, + 834FE0F0215C79ED000A5D3D /* apc.c in Sources */, 836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */, 836F701918BDC2190095E648 /* psx_cdxa.c in Sources */, 834D3A6E19F47C98001C54F6 /* g1l.c in Sources */, 836F6FC318BDC2190095E648 /* pc_smp.c in Sources */, 836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */, + 834FE0B4215C798C000A5D3D /* ffmpeg_decoder_custom_opus.c in Sources */, 8349A9091FE6258200E26435 /* pc_ast.c in Sources */, 8349A91C1FE6258200E26435 /* mogg.c in Sources */, + 834FE0F5215C79ED000A5D3D /* wv2.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 */, 8306B0EC20984590000302D4 /* pcm_sre.c in Sources */, 836F6FC818BDC2190095E648 /* pos.c in Sources */, 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */, @@ -2155,6 +2293,7 @@ 836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */, 836F701218BDC2190095E648 /* ps3_ivag.c in Sources */, 83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */, + 834FE0FE215C79ED000A5D3D /* ps_headerless.c in Sources */, 8306B0BB20984552000302D4 /* blocked_gsb.c in Sources */, 836F6F7718BDC2190095E648 /* capdsp.c in Sources */, 836F6FB018BDC2190095E648 /* ngc_dsp_ygo.c in Sources */, @@ -2163,13 +2302,16 @@ 836F703718BDC2190095E648 /* tun.c in Sources */, 836F700B18BDC2190095E648 /* ps2_wad.c in Sources */, 8349A9161FE6258200E26435 /* flx.c in Sources */, + 834FE0BE215C79A9000A5D3D /* blocked_xa_aiff.c in Sources */, 831BA61E1EAC61A500CF89B0 /* vawx.c in Sources */, 836F702A18BDC2190095E648 /* sd9.c in Sources */, 836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */, 836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */, 836F6FF618BDC2190095E648 /* ps2_sfs.c in Sources */, + 834FE10E215C79ED000A5D3D /* ahv.c in Sources */, 8306B08420984518000302D4 /* at3plus_decoder.c in Sources */, 8349A90E1FE6258200E26435 /* scd_pcm.c in Sources */, + 834FE0F9215C79ED000A5D3D /* wavebatch.c in Sources */, 836F6F9518BDC2190095E648 /* kraw.c in Sources */, 836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */, 8306B0E920984590000302D4 /* opus.c in Sources */, @@ -2178,17 +2320,22 @@ 83A21F86201D8981000F04B9 /* xwc.c in Sources */, 8306B0A620984552000302D4 /* blocked_ea_wve_ad10.c in Sources */, 83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */, + 834FE0FA215C79ED000A5D3D /* nus3bank.c in Sources */, 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */, + 834FE0B9215C798C000A5D3D /* xmd_decoder.c in Sources */, 8306B0B220984552000302D4 /* blocked_mxch.c in Sources */, 836F6F8618BDC2190095E648 /* excitebots.c in Sources */, 836F6FF418BDC2190095E648 /* rws.c in Sources */, + 834FE100215C79ED000A5D3D /* svg.c in Sources */, 836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */, 836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */, 836F6F3C18BDC2190095E648 /* xa_decoder.c in Sources */, + 834FE10A215C79ED000A5D3D /* nub_idsp.c in Sources */, 83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */, 836F6F9118BDC2190095E648 /* ios_psnd.c in Sources */, 836F700618BDC2190095E648 /* ps2_vgs.c in Sources */, - 836F700318BDC2190095E648 /* ps2_vag.c in Sources */, + 834FE10F215C79ED000A5D3D /* sdf.c in Sources */, + 834FE0FF215C79ED000A5D3D /* sqex_scd_sscf.c in Sources */, 836F6FAA18BDC2190095E648 /* ngc_bh2pcm.c in Sources */, 831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */, 836F6F3018BDC2190095E648 /* nds_procyon_decoder.c in Sources */, @@ -2208,12 +2355,14 @@ 836F6F8E18BDC2190095E648 /* halpst.c in Sources */, 836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */, 836F702618BDC2190095E648 /* s14_sss.c in Sources */, + 834FE102215C79ED000A5D3D /* rfrm.c in Sources */, 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, 8323894A1D22419B00482226 /* clHCA.c in Sources */, 836F702E18BDC2190095E648 /* sli.c in Sources */, 836F701D18BDC2190095E648 /* raw.c in Sources */, 836F6FDE18BDC2190095E648 /* ps2_ild.c in Sources */, 836F703E18BDC2190095E648 /* wii_ras.c in Sources */, + 834FE0EE215C79ED000A5D3D /* ue4opus.c in Sources */, 836F6FEA18BDC2190095E648 /* ps2_msa.c in Sources */, 836F6F3618BDC2190095E648 /* ogg_vorbis_decoder.c in Sources */, 836F704718BDC2190095E648 /* xbox_hlwav.c in Sources */, @@ -2232,15 +2381,19 @@ 836F6F3818BDC2190095E648 /* psx_decoder.c in Sources */, 836F6F2D18BDC2190095E648 /* mpeg_decoder.c in Sources */, 836F6F2618BDC2190095E648 /* g7221_decoder.c in Sources */, + 834FE0F2215C79ED000A5D3D /* wv6.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 */, + 834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */, 8306B0B520984552000302D4 /* blocked_emff.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */, + 834FE0F7215C79ED000A5D3D /* vis.c in Sources */, 83A21F8C201D8982000F04B9 /* kma9.c in Sources */, 8342469420C4D23000926E48 /* h4m.c in Sources */, 836F6FC418BDC2190095E648 /* pc_snds.c in Sources */, + 834FE111215C79ED000A5D3D /* ck.c in Sources */, 836F704E18BDC2190095E648 /* xss.c in Sources */, 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, 836F6F4118BDC2190095E648 /* blocked.c in Sources */, @@ -2260,7 +2413,6 @@ 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 */, diff --git a/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c b/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c index c2935d60a..c0b301115 100644 --- a/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c +++ b/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c @@ -1,1712 +1,1641 @@ +/** + * clHCA DECODER + * + * - Original decompilation and C++ decoder by nyaga + * https://github.com/Nyagamon/HCADecoder + * - Ported to C by kode54 + * https://gist.github.com/kode54/ce2bf799b445002e125f06ed833903c0 + * - Cleaned up by bnnm using Thealexbarney's VGAudio decoder as reference + * https://github.com/Thealexbarney/VGAudio + */ +/* TODO: + * - improve portability on types and float casts, sizeof(int) isn't necessarily sizeof(float) + * - check "packed_noise_level" vs VGAudio (CriHcaPacking.UnpackFrameHeader), weird behaviour + * - check "delta scalefactors" vs VGAudio (CriHcaPacking.DeltaDecode), may be setting wrong values on bad data + * - check "read intensity" vs VGAudio (CriHcaPacking.ReadIntensity), skips intensities if first is 15 + * - simplify DCT4 code + * - add extra validations: encoder_delay/padding < sample_count, bands/totals (max: 128?), track count==1, etc + * - calling clHCA_clear multiple times will not deallocate "comment" correctly + */ //-------------------------------------------------- -// インクルード +// Includes //-------------------------------------------------- #include "clHCA.h" -//#include +#include #include #include -#ifdef _MSC_VER -#define inline __inline -#endif +#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted (ciph type > 0) */ +#define HCA_SUBFRAMES_PER_FRAME 8 +#define HCA_SAMPLES_PER_SUBFRAME 128 +#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES_PER_FRAME*HCA_SAMPLES_PER_SUBFRAME) +#define HCA_MDCT_BITS 7 /* (1<<7) = 128 */ + +#define HCA_MAX_CHANNELS 16 /* internal max? in practice only 8 can be encoded */ //-------------------------------------------------- -// インライン関数 +// Decoder config/state //-------------------------------------------------- -#if 0 -static inline unsigned short get_le16(unsigned short v_){const unsigned char *v=(const unsigned char *)&v_;unsigned short r=v[1];r<<=8;r|=v[0];return r;} -static inline unsigned short get_be16(unsigned short v_){const unsigned char *v=(const unsigned char *)&v_;unsigned short r=v[0];r<<=8;r|=v[1];return r;} -static inline unsigned int get_be24(unsigned int v_){const unsigned char *v=(const unsigned char *)&v_;unsigned int r=v[0];r<<=8;r|=v[1];r<<=8;r|=v[2];return r;}; -static inline unsigned int get_le32(unsigned int v_){const unsigned char *v=(const unsigned char *)&v_;unsigned int r=v[3];r<<=8;r|=v[2];r<<=8;r|=v[1];r<<=8;r|=v[0];return r;} -static inline unsigned int get_be32(unsigned int v_){const unsigned char *v=(const unsigned char *)&v_;unsigned int r=v[0];r<<=8;r|=v[1];r<<=8;r|=v[2];r<<=8;r|=v[3];return r;} -static inline float get_bef32(float v_){union{float f;unsigned int i;}v;v.f=v_;v.i=get_be32(v.i);return v.f;} +typedef enum { DISCRETE = 0, STEREO_PRIMARY = 1, STEREO_SECONDARY = 2 } channel_type_t; +typedef struct stChannel { + /* HCA channel config */ + int type; /* discrete / stereo-primary / stereo-secondary */ + unsigned int coded_scalefactor_count; /* scalefactors used (depending on channel type) */ + unsigned char *hfr_scales; /* high frequency scales, pointing to higher scalefactors (simplification) */ -static union { unsigned int i; unsigned char c[4]; } g_is_le = {1}; -static inline unsigned short swap_u16(unsigned short v){unsigned short r=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;return r;} -static inline unsigned short swap_u32(unsigned int v){unsigned int r=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;return r;} -static inline unsigned short ret_le16(unsigned short v){if (g_is_le.c[0]) return v; else return swap_u16(v);} -static inline unsigned short ret_le32(unsigned int v){if (g_is_le.c[0]) return v; else return swap_u32(v);} -#endif + /* subframe state */ + unsigned char intensity[HCA_SUBFRAMES_PER_FRAME]; /* intensity indexes (value max: 15 / 4b) */ + unsigned char scalefactors[HCA_SAMPLES_PER_SUBFRAME]; /* scale indexes (value max: 64 / 6b)*/ + unsigned char resolution[HCA_SAMPLES_PER_SUBFRAME]; /* resolution indexes (value max: 15 / 4b) */ -static inline unsigned int ceil2(unsigned int a,unsigned int b){return (b>0)?(a/b+((a%b)?1:0)):0;} + float gain[HCA_SAMPLES_PER_SUBFRAME]; /* gain to apply to quantized spectral data */ + float spectra[HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */ + float temp[HCA_SAMPLES_PER_SUBFRAME]; /* temp for DCT-IV */ + float dct[HCA_SAMPLES_PER_SUBFRAME]; /* result of DCT-IV */ + float imdct_previous[HCA_SAMPLES_PER_SUBFRAME]; /* IMDCT */ -//-------------------------------------------------- -// structure definitions -//-------------------------------------------------- -typedef struct stHeader{//ファイル情報 (必須) - unsigned int hca; // 'HCA' - unsigned short version; // バージョン。v1.3とv2.0の存在を確認 - unsigned short dataOffset; // データオフセット -} stHeader; -typedef struct stFormat{//フォーマット情報 (必須) - unsigned int fmt; // 'fmt' - unsigned int channelCount:8; // チャンネル数 1〜16 - unsigned int samplingRate:24; // サンプリングレート 1〜0x7FFFFF - unsigned int blockCount; // ブロック数 0以上 - unsigned short r01; // 先頭の無音部分(ブロック数*0x400+0x80) - unsigned short r02; // 末尾の無音部分?計算方法不明(0x226) -} stFormat; -typedef struct stCompress{//圧縮情報 (圧縮情報かデコード情報のどちらか一つが必須) - unsigned int comp; // 'comp' - unsigned short blockSize; // ブロックサイズ(CBRのときに有効?) 8〜0xFFFF、0のときはVBR - unsigned char r01; // 不明(1) 0〜r02 v2.0現在1のみ対応 - unsigned char r02; // 不明(15) r01〜0x1F v2.0現在15のみ対応 - unsigned char r03; // 不明(1)(1) - unsigned char r04; // 不明(1)(0) - unsigned char r05; // 不明(0x80)(0x80) - unsigned char r06; // 不明(0x80)(0x20) - unsigned char r07; // 不明(0)(0x20) - unsigned char r08; // 不明(0)(8) - unsigned char reserve1; // 予約 - unsigned char reserve2; // 予約 -} stCompress; -typedef struct stDecode{//デコード情報 (圧縮情報かデコード情報のどちらか一つが必須) - unsigned int dec; // 'dec' - unsigned short blockSize; // ブロックサイズ(CBRのときに有効?) 8〜0xFFFF、0のときはVBR - unsigned char r01; // 不明(1) 0〜r02 v2.0現在1のみ対応 - unsigned char r02; // 不明(15) r01〜0x1F v2.0現在15のみ対応 - unsigned char count1; // type0とtype1の数-1 - unsigned char count2; // type2の数-1 - unsigned char r03:4; // 不明(0) - unsigned char r04:4; // 不明(0) 0は1に修正される - unsigned char enableCount2; // count2を使うフラグ -} stDecode; -typedef struct stVBR{//可変ビットレート情報 (廃止?) - unsigned int vbr; // 'vbr' - unsigned short r01; // 不明 0〜0x1FF - unsigned short r02; // 不明 -} stVBR; -typedef struct stATH{//ATHテーブル情報 (v2.0から廃止?) - unsigned int ath; // 'ath' - unsigned short type; // テーブルの種類(0:全て0 1:テーブル1) -} stATH; -typedef struct stLoop{//ループ情報 - unsigned int loop; // 'loop' - unsigned int loopStart; // ループ開始ブロックインデックス 0〜loopEnd - unsigned int loopEnd; // ループ終了ブロックインデックス loopStart〜(stFormat::blockCount-1) - unsigned short r01; // 不明(0x80)ループフラグ?ループ回数? - unsigned short r02; // 不明(0x226) -} stLoop; -typedef struct stCipher{//暗号テーブル情報 - unsigned int ciph; // 'ciph' - unsigned short type; // 暗号化の種類(0:暗号化なし 1:鍵なし暗号化 0x38:鍵あり暗号化) -} stCipher; -typedef struct stRVA{//相対ボリューム調節情報 - unsigned int rva; // 'rva' - float volume; // ボリューム -} stRVA; -typedef struct stComment{//コメント情報 - unsigned int comm; // 'comm' - unsigned char len; // コメントの長さ? - //char comment[]; -} stComment; -typedef struct stPadding{//パディング - unsigned int pad; // 'pad' -} stPadding; -typedef struct clATH{ - unsigned char _table[0x80]; -} clATH; -typedef struct clCipher{ - unsigned char _table[0x100]; -} clCipher; -typedef struct stChannel{ - float block[0x80]; - float base[0x80]; - char value[0x80]; - char scale[0x80]; - char value2[8]; - int type; - char *value3; - unsigned int count; - float wav1[0x80]; - float wav2[0x80]; - float wav3[0x80]; - float wave[8][0x80]; + /* frame state */ + float wave[HCA_SUBFRAMES_PER_FRAME][HCA_SAMPLES_PER_SUBFRAME]; /* resulting samples */ } stChannel; -typedef struct clHCA{ - unsigned int _validFile; - unsigned int _version; - unsigned int _dataOffset; - unsigned int _channelCount; - unsigned int _samplingRate; - unsigned int _blockCount; - unsigned int _fmt_r01; - unsigned int _fmt_r02; - unsigned int _blockSize; - unsigned int _comp_r01; - unsigned int _comp_r02; - unsigned int _comp_r03; - unsigned int _comp_r04; - unsigned int _comp_r05; - unsigned int _comp_r06; - unsigned int _comp_r07; - unsigned int _comp_r08; - unsigned int _comp_r09; - unsigned int _vbr_r01; - unsigned int _vbr_r02; - unsigned int _ath_type; - unsigned int _loopStart; - unsigned int _loopEnd; - unsigned int _loop_r01; - unsigned int _loop_r02; - unsigned int _loopFlg; - unsigned int _ciph_type; - unsigned int _ciph_key1; - unsigned int _ciph_key2; - float _rva_volume; - unsigned int _comm_len; - char *_comm_comment; - clATH _ath; - clCipher _cipher; - stChannel _channel[0x10]; + +typedef struct clHCA { + /* header config */ + unsigned int is_valid; + /* hca chunk */ + unsigned int version; + unsigned int header_size; + /* fmt chunk */ + unsigned int channels; + unsigned int sample_rate; + unsigned int frame_count; + unsigned int encoder_delay; + unsigned int encoder_padding; + /* comp/dec chunk */ + unsigned int frame_size; + unsigned int min_resolution; + unsigned int max_resolution; + unsigned int track_count; + unsigned int channel_config; + unsigned int stereo_type; + unsigned int total_band_count; + unsigned int base_band_count; + unsigned int stereo_band_count; + unsigned int bands_per_hfr_group; + unsigned int reserved1; + unsigned int reserved2; + /* vbr chunk */ + unsigned int vbr_max_frame_size; + unsigned int vbr_noise_Level; + /* ath chunk */ + unsigned int ath_type; + /* loop chunk */ + unsigned int loop_start_frame; + unsigned int loop_end_frame; + unsigned int loop_start_delay; + unsigned int loop_end_padding; + unsigned int loop_flag; + /* ciph chunk */ + unsigned int ciph_type; + unsigned long long keycode; + /* rva chunk */ + float rva_volume; + /* comm chunk */ + unsigned int comment_len; + char *comment; + + /* initial state */ + unsigned int hfr_group_count; + unsigned char ath_curve[HCA_SAMPLES_PER_SUBFRAME]; + unsigned char cipher_table[256]; + /* variable state */ + stChannel channel[HCA_MAX_CHANNELS]; } clHCA; -typedef struct clData{ - const unsigned char *_data; - int _size; - int _bit; + +typedef struct clData { + const unsigned char *data; + int size; + int bit; } clData; -//-------------------------------------------------- -// コンストラクタ -//-------------------------------------------------- -static void clATH_constructor(clATH *ath); -static void clCipher_constructor(clCipher *cipher); - -static void clHCA_constructor(clHCA *hca,unsigned int ciphKey1,unsigned int ciphKey2){ - memset(hca,0,sizeof(*hca)); - hca->_ciph_key1 = ciphKey1; - hca->_ciph_key2 = ciphKey2; - clATH_constructor(&hca->_ath); - clCipher_constructor(&hca->_cipher); - hca->_validFile = 0; - hca->_comm_comment = 0; -} - -static void clHCA_destructor(clHCA *hca) -{ - if(hca->_comm_comment){free(hca->_comm_comment);hca->_comm_comment=0;} -} //-------------------------------------------------- -// HCAチェック +// Checksum //-------------------------------------------------- -#if 0 -static int clHCA_CheckFile(void *data,unsigned int size){ - return (data&&size>=4&&(get_be32(*(unsigned int *)data)&0x7F7F7F7F)==0x48434100);/*'HCA\0'*/ -} -#endif - -//-------------------------------------------------- -// チェックサム -//-------------------------------------------------- -static const unsigned short clHCA_CheckSum_v[]={ - 0x0000,0x8005,0x800F,0x000A,0x801B,0x001E,0x0014,0x8011,0x8033,0x0036,0x003C,0x8039,0x0028,0x802D,0x8027,0x0022, - 0x8063,0x0066,0x006C,0x8069,0x0078,0x807D,0x8077,0x0072,0x0050,0x8055,0x805F,0x005A,0x804B,0x004E,0x0044,0x8041, - 0x80C3,0x00C6,0x00CC,0x80C9,0x00D8,0x80DD,0x80D7,0x00D2,0x00F0,0x80F5,0x80FF,0x00FA,0x80EB,0x00EE,0x00E4,0x80E1, - 0x00A0,0x80A5,0x80AF,0x00AA,0x80BB,0x00BE,0x00B4,0x80B1,0x8093,0x0096,0x009C,0x8099,0x0088,0x808D,0x8087,0x0082, - 0x8183,0x0186,0x018C,0x8189,0x0198,0x819D,0x8197,0x0192,0x01B0,0x81B5,0x81BF,0x01BA,0x81AB,0x01AE,0x01A4,0x81A1, - 0x01E0,0x81E5,0x81EF,0x01EA,0x81FB,0x01FE,0x01F4,0x81F1,0x81D3,0x01D6,0x01DC,0x81D9,0x01C8,0x81CD,0x81C7,0x01C2, - 0x0140,0x8145,0x814F,0x014A,0x815B,0x015E,0x0154,0x8151,0x8173,0x0176,0x017C,0x8179,0x0168,0x816D,0x8167,0x0162, - 0x8123,0x0126,0x012C,0x8129,0x0138,0x813D,0x8137,0x0132,0x0110,0x8115,0x811F,0x011A,0x810B,0x010E,0x0104,0x8101, - 0x8303,0x0306,0x030C,0x8309,0x0318,0x831D,0x8317,0x0312,0x0330,0x8335,0x833F,0x033A,0x832B,0x032E,0x0324,0x8321, - 0x0360,0x8365,0x836F,0x036A,0x837B,0x037E,0x0374,0x8371,0x8353,0x0356,0x035C,0x8359,0x0348,0x834D,0x8347,0x0342, - 0x03C0,0x83C5,0x83CF,0x03CA,0x83DB,0x03DE,0x03D4,0x83D1,0x83F3,0x03F6,0x03FC,0x83F9,0x03E8,0x83ED,0x83E7,0x03E2, - 0x83A3,0x03A6,0x03AC,0x83A9,0x03B8,0x83BD,0x83B7,0x03B2,0x0390,0x8395,0x839F,0x039A,0x838B,0x038E,0x0384,0x8381, - 0x0280,0x8285,0x828F,0x028A,0x829B,0x029E,0x0294,0x8291,0x82B3,0x02B6,0x02BC,0x82B9,0x02A8,0x82AD,0x82A7,0x02A2, - 0x82E3,0x02E6,0x02EC,0x82E9,0x02F8,0x82FD,0x82F7,0x02F2,0x02D0,0x82D5,0x82DF,0x02DA,0x82CB,0x02CE,0x02C4,0x82C1, - 0x8243,0x0246,0x024C,0x8249,0x0258,0x825D,0x8257,0x0252,0x0270,0x8275,0x827F,0x027A,0x826B,0x026E,0x0264,0x8261, - 0x0220,0x8225,0x822F,0x022A,0x823B,0x023E,0x0234,0x8231,0x8213,0x0216,0x021C,0x8219,0x0208,0x820D,0x8207,0x0202, +static const unsigned short crc16_lookup_table[256] = { + 0x0000,0x8005,0x800F,0x000A,0x801B,0x001E,0x0014,0x8011,0x8033,0x0036,0x003C,0x8039,0x0028,0x802D,0x8027,0x0022, + 0x8063,0x0066,0x006C,0x8069,0x0078,0x807D,0x8077,0x0072,0x0050,0x8055,0x805F,0x005A,0x804B,0x004E,0x0044,0x8041, + 0x80C3,0x00C6,0x00CC,0x80C9,0x00D8,0x80DD,0x80D7,0x00D2,0x00F0,0x80F5,0x80FF,0x00FA,0x80EB,0x00EE,0x00E4,0x80E1, + 0x00A0,0x80A5,0x80AF,0x00AA,0x80BB,0x00BE,0x00B4,0x80B1,0x8093,0x0096,0x009C,0x8099,0x0088,0x808D,0x8087,0x0082, + 0x8183,0x0186,0x018C,0x8189,0x0198,0x819D,0x8197,0x0192,0x01B0,0x81B5,0x81BF,0x01BA,0x81AB,0x01AE,0x01A4,0x81A1, + 0x01E0,0x81E5,0x81EF,0x01EA,0x81FB,0x01FE,0x01F4,0x81F1,0x81D3,0x01D6,0x01DC,0x81D9,0x01C8,0x81CD,0x81C7,0x01C2, + 0x0140,0x8145,0x814F,0x014A,0x815B,0x015E,0x0154,0x8151,0x8173,0x0176,0x017C,0x8179,0x0168,0x816D,0x8167,0x0162, + 0x8123,0x0126,0x012C,0x8129,0x0138,0x813D,0x8137,0x0132,0x0110,0x8115,0x811F,0x011A,0x810B,0x010E,0x0104,0x8101, + 0x8303,0x0306,0x030C,0x8309,0x0318,0x831D,0x8317,0x0312,0x0330,0x8335,0x833F,0x033A,0x832B,0x032E,0x0324,0x8321, + 0x0360,0x8365,0x836F,0x036A,0x837B,0x037E,0x0374,0x8371,0x8353,0x0356,0x035C,0x8359,0x0348,0x834D,0x8347,0x0342, + 0x03C0,0x83C5,0x83CF,0x03CA,0x83DB,0x03DE,0x03D4,0x83D1,0x83F3,0x03F6,0x03FC,0x83F9,0x03E8,0x83ED,0x83E7,0x03E2, + 0x83A3,0x03A6,0x03AC,0x83A9,0x03B8,0x83BD,0x83B7,0x03B2,0x0390,0x8395,0x839F,0x039A,0x838B,0x038E,0x0384,0x8381, + 0x0280,0x8285,0x828F,0x028A,0x829B,0x029E,0x0294,0x8291,0x82B3,0x02B6,0x02BC,0x82B9,0x02A8,0x82AD,0x82A7,0x02A2, + 0x82E3,0x02E6,0x02EC,0x82E9,0x02F8,0x82FD,0x82F7,0x02F2,0x02D0,0x82D5,0x82DF,0x02DA,0x82CB,0x02CE,0x02C4,0x82C1, + 0x8243,0x0246,0x024C,0x8249,0x0258,0x825D,0x8257,0x0252,0x0270,0x8275,0x827F,0x027A,0x826B,0x026E,0x0264,0x8261, + 0x0220,0x8225,0x822F,0x022A,0x823B,0x023E,0x0234,0x8231,0x8213,0x0216,0x021C,0x8219,0x0208,0x820D,0x8207,0x0202, }; -static unsigned short clHCA_CheckSum(const void *data,int size,unsigned short sum){ - const unsigned char *s, *e; - for(s=(const unsigned char *)data,e=s+size;s>8)^*s]; - return sum; +static unsigned short crc16_checksum(const unsigned char *data, unsigned int size) { + unsigned int i; + unsigned short sum = 0; + + /* HCA header/frames should always have checksum 0 (checksum(size-16b) = last 16b) */ + for (i = 0; i < size; i++) { + sum = (sum << 8) ^ crc16_lookup_table[(sum >> 8) ^ data[i]]; + } + return sum; } //-------------------------------------------------- -// データ +// Bitstream reader //-------------------------------------------------- -void clData_constructor(clData *ds,const void *data,int size){ - ds->_data=(const unsigned char *)data; - ds->_size=size*8; - ds->_bit=0; +static void bitreader_init(clData *br, const void *data, int size) { + br->data = data; + br->size = size * 8; + br->bit = 0; } -static unsigned int clData_CheckBit(clData *ds,int bitSize){ - unsigned int v=0; - if(ds->_bit+bitSize<=ds->_size){ - unsigned int bitOffset=bitSize+(ds->_bit&7); - if((ds->_size-ds->_bit)>=32&&bitOffset>=25){ - static const int mask[]={0xFFFFFFFF,0x7FFFFFFF,0x3FFFFFFF,0x1FFFFFFF,0x0FFFFFFF,0x07FFFFFF,0x03FFFFFF,0x01FFFFFF}; - const unsigned int _bit=ds->_bit; - const unsigned char *data=&ds->_data[_bit>>3]; - v=data[0];v=(v<<8)|data[1];v=(v<<8)|data[2];v=(v<<8)|data[3]; - v&=mask[_bit&7]; - v>>=32-(_bit&7)-bitSize; - } - else if((ds->_size-ds->_bit)>=24&&bitOffset>=17){ - static const int mask[]={0xFFFFFF,0x7FFFFF,0x3FFFFF,0x1FFFFF,0x0FFFFF,0x07FFFF,0x03FFFF,0x01FFFF}; - const unsigned int _bit=ds->_bit; - const unsigned char *data=&ds->_data[_bit>>3]; - v=data[0];v=(v<<8)|data[1];v=(v<<8)|data[2]; - v&=mask[_bit&7]; - v>>=24-(_bit&7)-bitSize; - } - else if((ds->_size-ds->_bit)>=16&&bitOffset>=9){ - static const int mask[]={0xFFFF,0x7FFF,0x3FFF,0x1FFF,0x0FFF,0x07FF,0x03FF,0x01FF}; - const unsigned int _bit=ds->_bit; - const unsigned char *data=&ds->_data[_bit>>3]; - v=data[0];v=(v<<8)|data[1]; - v&=mask[_bit&7]; - v>>=16-(_bit&7)-bitSize; - } - else{ - static const int mask[]={0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01}; - const unsigned int _bit=ds->_bit; - const unsigned char *data=&ds->_data[_bit>>3]; - v=data[0]; - v&=mask[_bit&7]; - v>>=8-(_bit&7)-bitSize; - } - } - return v; +static unsigned int bitreader_peek(clData *br, int bitsize) { + const unsigned int bit = br->bit; + const unsigned int bit_rem = bit & 7; + const unsigned int size = br->size; + unsigned int v = 0; + unsigned int bit_offset, bit_left; + + if (!(bit + bitsize <= size)) + return v; + + bit_offset = bitsize + bit_rem; + bit_left = size - bit; + if (bit_left >= 32 && bit_offset >= 25) { + static const unsigned int mask[8] = { + 0xFFFFFFFF,0x7FFFFFFF,0x3FFFFFFF,0x1FFFFFFF, + 0x0FFFFFFF,0x07FFFFFF,0x03FFFFFF,0x01FFFFFF + }; + const unsigned char *data = &br->data[bit >> 3]; + v = data[0]; + v = (v << 8) | data[1]; + v = (v << 8) | data[2]; + v = (v << 8) | data[3]; + v &= mask[bit_rem]; + v >>= 32 - bit_rem - bitsize; + } + else if (bit_left >= 24 && bit_offset >= 17) { + static const unsigned int mask[8] = { + 0xFFFFFF,0x7FFFFF,0x3FFFFF,0x1FFFFF, + 0x0FFFFF,0x07FFFF,0x03FFFF,0x01FFFF + }; + const unsigned char *data = &br->data[bit >> 3]; + v = data[0]; + v = (v << 8) | data[1]; + v = (v << 8) | data[2]; + v &= mask[bit_rem]; + v >>= 24 - bit_rem - bitsize; + } + else if (bit_left >= 16 && bit_offset >= 9) { + static const unsigned int mask[8] = { + 0xFFFF,0x7FFF,0x3FFF,0x1FFF,0x0FFF,0x07FF,0x03FF,0x01FF + }; + const unsigned char *data = &br->data[bit >> 3]; + v = data[0]; + v = (v << 8) | data[1]; + v &= mask[bit_rem]; + v >>= 16 - bit_rem - bitsize; + } + else { + static const unsigned int mask[8] = { + 0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01 + }; + const unsigned char *data = &br->data[bit >> 3]; + v = data[0]; + v &= mask[bit_rem]; + v >>= 8 - bit_rem - bitsize; + } + return v; } -static unsigned int clData_GetBit(clData *ds,int bitSize){ - unsigned int v=clData_CheckBit(ds,bitSize); - ds->_bit+=bitSize; - return v; +static unsigned int bitreader_read(clData *br, int bitsize) { + unsigned int v = bitreader_peek(br, bitsize); + br->bit += bitsize; + return v; } -static void clData_AddBit(clData *ds,int bitSize){ - ds->_bit+=bitSize; +static void bitreader_skip(clData *br, int bitsize) { + br->bit += bitsize; } //-------------------------------------------------- -// ヘッダ情報をコンソール出力 -//-------------------------------------------------- -#if 0 -static int clHCA_PrintInfo(const char *filenameHCA){ - FILE *fp; - stHeader header; - unsigned char *data; - unsigned int size; - clData d; - - // temporaries, so we don't need a state structure - unsigned int _version; - unsigned int _dataOffset; - unsigned int _channelCount; - unsigned int _samplingRate; - unsigned int _blockCount; - unsigned int _fmt_r01; - unsigned int _fmt_r02; - unsigned int _blockSize; - unsigned int _comp_r01; - unsigned int _comp_r02; - unsigned int _comp_r03; - unsigned int _comp_r04; - unsigned int _comp_r05; - unsigned int _comp_r06; - unsigned int _comp_r07; - unsigned int _comp_r08; - unsigned int _comp_r09; - unsigned int _vbr_r01; - unsigned int _vbr_r02; - unsigned int _ath_type; - unsigned int _loopStart; - unsigned int _loopEnd; - unsigned int _loop_r01; - unsigned int _loop_r02; - unsigned int _loopFlg; - unsigned int _ciph_type; - unsigned int _ciph_key1; - unsigned int _ciph_key2; - float _rva_volume; - unsigned int _comm_len; - char *_comm_comment; - - // チェック - if(!(filenameHCA))return -1; - - // HCAファイルを開く - if((fp = fopen(filenameHCA,"rb")) == NULL){ - printf("Error: ファイルが開けませんでした。\n"); - return -1; - } - - // ヘッダチェック - memset(&header,0,sizeof(header)); - fread(&header,sizeof(header),1,fp); - if(!clHCA_CheckFile(&header,sizeof(header))){ - printf("Error: HCAファイルではありません。\n"); - fclose(fp);return -1; - } - - // ヘッダ解析 - clData_constructor(&d, &header, sizeof(header)); - clData_AddBit(&d, 32); - header.dataOffset=clData_CheckBit(&d, 16); - data=(unsigned char*) malloc(header.dataOffset); - if(!data){ - printf("Error: メモリ不足です。\n"); - fclose(fp);return -1; - } - fseek(fp,0,SEEK_SET); - fread(data,header.dataOffset,1,fp); - - size=header.dataOffset; - - clData_constructor(&d, data, size); - - // サイズチェック - if(size=sizeof(stHeader) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x48434100){/*'HCA\0'*/ - clData_AddBit(&d,32); - _version=clData_GetBit(&d,16); - _dataOffset=clData_GetBit(&d,16); - printf("コーデック: HCA\n"); - printf("バージョン: %d.%d\n",_version>>8,_version&0xFF); - //if(size<_dataOffset)return -1; - if(clHCA_CheckSum(data,_dataOffset,0))printf("※ ヘッダが破損しています。改変してる場合もこの警告が出ます。\n"); - size-=(16+16+32)/8; - }else{ - printf("※ HCAチャンクがありません。再生に必要な情報です。\n"); - } - - // fmt - if(size>=sizeof(stFormat) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x666D7400){/*'fmt\0'*/ - clData_AddBit(&d,32); - _channelCount=clData_GetBit(&d,8); - _samplingRate=clData_GetBit(&d,24); - _blockCount=clData_GetBit(&d,32); - _fmt_r01=clData_GetBit(&d,16); - _fmt_r02=clData_GetBit(&d,16); - switch(_channelCount){ - case 1:printf("チャンネル数: モノラル (1チャンネル)\n");break; - case 2:printf("チャンネル数: ステレオ (2チャンネル)\n");break; - default:printf("チャンネル数: %dチャンネル\n",_channelCount);break; - } - if(!(_channelCount>=1&&_channelCount<=16)){ - printf("※ チャンネル数の範囲は1〜16です。\n"); - } - printf("サンプリングレート: %dHz\n",_samplingRate); - if(!(_samplingRate>=1&&_samplingRate<=0x7FFFFF)){ - printf("※ サンプリングレートの範囲は1〜8388607(0x7FFFFF)です。\n"); - } - printf("ブロック数: %d\n",_blockCount); - printf("先頭無音ブロック数: %d\n",(_fmt_r01-0x80)/0x400); - printf("末尾無音サンプル数?: %d\n",_fmt_r02); - size-=(16+16+32+24+8+32)/8; - }else{ - printf("※ fmtチャンクがありません。再生に必要な情報です。\n"); - } - - // comp - if(size>=sizeof(stCompress) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x636F6D70){/*'comp'*/ - clData_AddBit(&d,32); - _blockSize=clData_GetBit(&d,16); - _comp_r01=clData_GetBit(&d,8); - _comp_r02=clData_GetBit(&d,8); - _comp_r03=clData_GetBit(&d,8); - _comp_r04=clData_GetBit(&d,8); - _comp_r05=clData_GetBit(&d,8); - _comp_r06=clData_GetBit(&d,8); - _comp_r07=clData_GetBit(&d,8); - _comp_r08=clData_GetBit(&d,8); - clData_AddBit(&d,8+8); - printf("ビットレート: CBR (固定ビットレート)\n"); - printf("ブロックサイズ: 0x%04X\n",_blockSize); - if(!(_blockSize>=8&&_blockSize<=0xFFFF)){ - printf("※ ブロックサイズの範囲は8〜65535(0xFFFF)です。v1.3では0でVBRになるようになってましたが、v2.0から廃止されたようです。\n"); - } - printf("comp1: %d\n",_comp_r01); - printf("comp2: %d\n",_comp_r02); - if(!(_comp_r01>=0&&_comp_r01<=_comp_r02&&_comp_r02<=0x1F)){ - printf("※ comp1とcomp2の範囲は0<=comp1<=comp2<=31です。v2.0現在、comp1は1、comp2は15で固定されています。\n"); - } - printf("comp3: %d\n",_comp_r03); - if(!_comp_r03){ - printf("※ comp3は1以上の値です。\n"); - } - printf("comp4: %d\n",_comp_r04); - printf("comp5: %d\n",_comp_r05); - printf("comp6: %d\n",_comp_r06); - printf("comp7: %d\n",_comp_r07); - printf("comp8: %d\n",_comp_r08); - size-=(32+16+8*8)/8; - } - - // dec - else if(size>=sizeof(stDecode) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x64656300){/*'dec\0'*/ - unsigned char count1,count2,enableCount2; - clData_AddBit(&d, 32); - _blockSize=clData_GetBit(&d,16); - _comp_r01=clData_GetBit(&d,8); - _comp_r02=clData_GetBit(&d,8); - count1=clData_GetBit(&d,8); - count2=clData_GetBit(&d,8); - _comp_r03=clData_GetBit(&d,4); - _comp_r04=clData_GetBit(&d,4); - enableCount2=clData_GetBit(&d,8); - _comp_r05=count1+1; - _comp_r06=((enableCount2)?count2:count1)+1; - _comp_r07=_comp_r05-_comp_r06; - _comp_r08=0; - printf("ビットレート: CBR (固定ビットレート)\n"); - printf("ブロックサイズ: 0x%04X\n",_blockSize); - if(!(_blockSize>=8&&_blockSize<=0xFFFF)){ - printf("※ ブロックサイズの範囲は8〜65535(0xFFFF)です。v1.3では0でVBRになるようになってましたが、v2.0から廃止されたようです。\n"); - } - printf("dec1: %d\n",_comp_r01); - printf("dec2: %d\n",_comp_r02); - if(!(_comp_r01>=0&&_comp_r01<=_comp_r02&&_comp_r02<=0x1F)){ - printf("※ dec1とdec2の範囲は0<=dec1<=dec2<=31です。v2.0現在、dec1は1、dec2は15で固定されています。\n"); - } - printf("dec3: %d\n",_comp_r03); - if(!_comp_r03){ - printf("※ dec3は再生時に1以上の値に修正されます。\n"); - } - printf("dec4: %d\n",_comp_r04); - printf("dec5: %d\n",_comp_r05); - printf("dec6: %d\n",_comp_r06); - printf("dec7: %d\n",_comp_r07); - size-=(8+4+4+8+8+8+8+16+32)/8; - }else{ - printf("※ compチャンクまたはdecチャンクがありません。再生に必要な情報です。\n"); - } - - // vbr - if(size>=sizeof(stVBR) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x76627200){/*'vbr\0'*/ - clData_AddBit(&d, 32); - _vbr_r01=clData_GetBit(&d,16); - _vbr_r02=clData_GetBit(&d,16); - printf("ビットレート: VBR (可変ビットレート) ※v2.0で廃止されています。\n"); - if(!(_blockSize==0)){ - printf("※ compまたはdecチャンクですでにCBRが指定されています。\n"); - } - printf("vbr1: %d\n",_vbr_r01); - if(!(_vbr_r01>=0&&_vbr_r01<=0x1FF)){ - printf("※ vbr1の範囲は0〜511(0x1FF)です。\n"); - } - printf("vbr2: %d\n",_vbr_r02); - size-=(16+16+32)/8; - }else{ - _vbr_r01=0; - _vbr_r02=0; - } - - // ath - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x61746800){/*'ath\0'*/ - clData_AddBit(&d,32); - _ath_type=clData_GetBit(&d,16); - printf("ATHタイプ:%d ※v2.0から廃止されています。\n",_ath_type); - size-=(16+32)/8; - }else{ - if(_version<0x200){ - printf("ATHタイプ:1 ※v2.0から廃止されています。\n"); - } - } - - // loop - if(size>=sizeof(stLoop) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x6C6F6F70){/*'loop'*/ - clData_AddBit(&d, 32); - _loopStart=clData_GetBit(&d,32); - _loopEnd=clData_GetBit(&d,32); - _loop_r01=clData_GetBit(&d,16); - _loop_r02=clData_GetBit(&d,16); - printf("ループ開始ブロック: %d\n",_loopStart); - printf("ループ終了ブロック: %d\n",_loopEnd); - if(!(_loopStart>=0&&_loopStart<=_loopEnd&&_loopEnd<_blockCount)){ - printf("※ ループ開始ブロックとループ終了ブロックの範囲は、0<=ループ開始ブロック<=ループ終了ブロック<ブロック数 です。\n"); - } - printf("ループ情報1: %d\n",_loop_r01); - printf("ループ情報2: %d\n",_loop_r02); - size-=(16+16+32+32+32)/8; - } - - // ciph - if(size>=6 && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x63697068){/*'ciph'*/ - clData_AddBit(&d,32); - _ciph_type=clData_GetBit(&d,16); - switch(_ciph_type){ - case 0:printf("暗号化タイプ: なし\n");break; - case 1:printf("暗号化タイプ: 鍵無し暗号化\n");break; - case 0x38:printf("暗号化タイプ: 鍵有り暗号化 ※正しい鍵を使わないと出力波形がおかしくなります。\n");break; - default:printf("暗号化タイプ: %d\n",_ciph_type);break; - } - if(!(_ciph_type==0||_ciph_type==1||_ciph_type==0x38)){ - printf("※ この暗号化タイプは、v2.0現在再生できません。\n"); - } - size-=6; - } - - // rva - if(size>=sizeof(stRVA) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x72766100){/*'rva\0'*/ - union { unsigned int i; float f; } v; - clData_AddBit(&d,32); - v.i=clData_GetBit(&d,32); - _rva_volume=v.f; - printf("相対ボリューム調節: %g倍\n",_rva_volume); - size-=(32+32)/8; - } - - // comm - if(size>=5 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D6D){/*'comm'*/ - int i; - clData_AddBit(&d,32); - _comm_len=clData_GetBit(&d,8); - _comm_comment=malloc(_comm_len+1); - for(i=0;i<_comm_len;++i)_comm_comment[i]=clData_GetBit(&d,8); - _comm_comment[i]='\0'; - printf("コメント: %s\n",_comm_comment); - free(_comm_comment); - } - - free(data); - - // 閉じる - fclose(fp); - - return 0; -} -#endif - -//-------------------------------------------------- -// デコードしてWAVEファイルに保存 -//-------------------------------------------------- -#if 0 -static int clHCA_DecodeToWavefile_Decode(void *fp1,void *fp2,unsigned int address,unsigned int count,void *data,void *modeFunction); - -static void clHCA_DecodeToWavefile_DecodeModeFloat(float f,void *fp){fwrite(&f,sizeof(f),1,(FILE *)fp);} -static void clHCA_DecodeToWavefile_DecodeMode8bit(float f,void *fp){int v=(int)(f*0x7F)+0x80;fwrite(&v,1,1,(FILE *)fp);} -static void clHCA_DecodeToWavefile_DecodeMode16bit(float f,void *fp){int v=(int)(f*0x7FFF);fwrite(&v,2,1,(FILE *)fp);} -static void clHCA_DecodeToWavefile_DecodeMode24bit(float f,void *fp){int v=(int)(f*0x7FFFFF);fwrite(&v,3,1,(FILE *)fp);} -static void clHCA_DecodeToWavefile_DecodeMode32bit(float f,void *fp){int v=(int)(f*0x7FFFFFFF);fwrite(&v,4,1,(FILE *)fp);} - -static int clHCA_DecodeToWavefile(clHCA *hca,const char *filenameHCA,const char *filenameWAV,float volume,int mode,int loop){ - FILE *fp1; - FILE *fp2; - stHeader header; - unsigned char *data1; - unsigned char *data2; - struct stWAVEHeader{ - char riff[4]; - unsigned int riffSize; - char wave[4]; - char fmt[4]; - unsigned int fmtSize; - unsigned short fmtType; - unsigned short fmtChannelCount; - unsigned int fmtSamplingRate; - unsigned int fmtSamplesPerSec; - unsigned short fmtSamplingSize; - unsigned short fmtBitCount; - }wavRiff={'R','I','F','F',0,'W','A','V','E','f','m','t',' ',ret_le32(0x10),0,0,0,0,0,0}; - struct stWAVEsmpl{ - char smpl[4]; - unsigned int smplSize; - unsigned int manufacturer; - unsigned int product; - unsigned int samplePeriod; - unsigned int MIDIUnityNote; - unsigned int MIDIPitchFraction; - unsigned int SMPTEFormat; - unsigned int SMPTEOffset; - unsigned int sampleLoops; - unsigned int samplerData; - unsigned int loop_Identifier; - unsigned int loop_Type; - unsigned int loop_Start; - unsigned int loop_End; - unsigned int loop_Fraction; - unsigned int loop_PlayCount; - }wavSmpl={'s','m','p','l',ret_le32(0x3C),0,0,0,ret_le32(0x3C),0,0,0,ret_le32(1),ret_le32(0x18),0,0,0,0,0,0}; - struct stWAVEnote{ - char note[4]; - unsigned int noteSize; - unsigned int dwName; - }wavNote={'n','o','t','e',0,0}; - struct stWAVEdata{ - char data[4]; - unsigned int dataSize; - }wavData={'d','a','t','a',0}; - - // チェック - if(!(filenameHCA&&filenameWAV&&(mode==0||mode==8||mode==16||mode==24||mode==32)&&loop>=0))return -1; - - // HCAファイルを開く - if((fp1 = fopen(filenameHCA,"rb")) == NULL)return -1; - - // ヘッダチェック - memset(&header,0,sizeof(header)); - fread(&header,sizeof(header),1,fp1); - if(!CheckFile(&header,sizeof(header))){fclose(fp1);return -1;} - - // ヘッダ解析 - header.dataOffset=get_be16(header.dataOffset); - data1=(unsigned char*) malloc(header.dataOffset); - if(!data1){fclose(fp1);return -1;} - fseek(fp1,0,SEEK_SET); - fread(data1,header.dataOffset,1,fp1); - if(clHCA_Decode(cl,data1,header.dataOffset,0) < 0){free(data1);fclose(fp1);return -1;} - - // WAVEファイルを開く - if((fp2 = fopen(filenameWAV,"wb")) == NULL){free(data1);fclose(fp1);return -1;} - - // WAVEヘッダを書き込み - wavRiff.fmtType=ret_le16((mode>0)?1:3); - wavRiff.fmtChannelCount=ret_le16(hca->_channelCount); - wavRiff.fmtBitCount=ret_le16((mode>0)?mode:32); - wavRiff.fmtSamplingRate=ret_le32(hca->_samplingRate); - wavRiff.fmtSamplingSize=ret_le16(wavRiff.fmtBitCount/8*wavRiff.fmtChannelCount); - wavRiff.fmtSamplesPerSec=ret_le32(wavRiff.fmtSamplingRate*wavRiff.fmtSamplingSize); - if(hca->_loopFlg){ - wavSmpl.samplePeriod=ret_le32((unsigned int)(1/(double)wavRiff.fmtSamplingRate*1000000000)); - wavSmpl.loop_Start=ret_le32(hca->_loopStart*0x80*8*wavRiff.fmtSamplingSize); - wavSmpl.loop_End=ret_le32(hca->_loopEnd*0x80*8*wavRiff.fmtSamplingSize); - wavSmpl.loop_PlayCount=ret_le32((hca->_loop_r01==0x80)?0:hca->_loop_r01); - }else if(loop){ - wavSmpl.loop_Start=0; - wavSmpl.loop_End=ret_le32(hca->_blockCount*0x80*8*wavRiff.fmtSamplingSize); - hca->_loopStart=0; - hca->_loopEnd=hca->_blockCount; - } - if(_comm_comment){ - unsigned int noteSize=4+hca->_comm_len+1; - if(noteSize&3)noteSize+=4-(noteSize&3); - waveNote.noteSize=ret_le32(noteSize); - } - wavData.dataSize=ret_le32(hca->_blockCount*0x80*8*wavRiff.fmtSamplingSize+(wavSmpl.loop_End-wavSmpl.loop_Start)*loop); - wavRiff.riffSize=ret_le32(0x1C+((hca->_loopFlg&&!loop)?sizeof(wavSmpl):0)+(hca->_comm_comment?8+wavNote.noteSize:0)+sizeof(wavData)+wavData.dataSize); - fwrite(&wavRiff,sizeof(wavRiff),1,fp2); - if(hca->_loopFlg&&!loop)fwrite(&wavSmpl,sizeof(wavSmpl),1,fp2); - if(hca->_comm_comment){ - int address=ftell(fp2); - fwrite(&wavNote,sizeof(wavNote),1,fp2); - fputs(hca->_comm_comment,fp2); - fseek(fp2,address+8+wavNote.noteSize,SEEK_SET); - } - fwrite(&wavData,sizeof(wavData),1,fp2); - - // 相対ボリュームを調節 - hca->_rva_volume*=volume; - - // デコード - void *modeFunction; - switch(mode){ - case 0:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeModeFloat;break; - case 8:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeMode8bit;break; - case 16:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeMode16bit;break; - case 24:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeMode24bit;break; - case 32:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeMode32bit;break; - } - data2=(unsigned char*) malloc(_blockSize); - if(!data2){free(data1);fclose(fp2);fclose(fp1);return -1;} - if(!loop){ - if(clHCA_DecodeToWavefile_Decode(cl,fp1,fp2,_dataOffset,_blockCount,data2,modeFunction) < 0){free(data2);free(data1);fclose(fp2);fclose(fp1);return -1;} - }else{ - unsigned int loopBlockOffset=hca->_dataOffset+hca->_loopStart*hca->_blockSize; - unsigned int loopBlockCount=hca->_loopEnd-hca->_loopStart; - int i; - if(clHCA_DecodeToWavefile_Decode(cl,fp1,fp2,_dataOffset,_loopEnd,data2,modeFunction) < 0){free(data2);free(data1);fclose(fp2);fclose(fp1);return -1;} - for(i=1;i_blockSize,1,(FILE *)fp1); - if(clHCA_Decode(cl,data,hca->_blockSize,address) < 0)return -1; - for(i=0;i<8;i++){ - for(j=0;j<0x80;j++){ - for(k=0,m=hca->_channelCount;k_rva_volume; - if(f>1){f=1;}else if(f<-1){f=-1;} - ((void (*)(float,void *))modeFunction)(f,fp2); - } - } - } - } - return 0; -} -#endif - -//-------------------------------------------------- -// Decoder utilities +// API/Utilities //-------------------------------------------------- -int clHCA_isOurFile0(const void *data){ - clData d; - clData_constructor(&d, data, 8); - if((clData_CheckBit(&d,32)&0x7F7F7F7F)==0x48434100){/*'HCA\0'*/ - clData_AddBit(&d,32+16); - return clData_CheckBit(&d,16); - } - return -1; +int clHCA_isOurFile(const void *data, unsigned int size) { + clData br; + unsigned int header_size = 0; + + if (!data || size < 0x08) + return -1; + + bitreader_init(&br, data, 8); + if ((bitreader_peek(&br, 32) & HCA_MASK) == 0x48434100) {/*'HCA\0'*/ + bitreader_skip(&br, 32 + 16); + header_size = bitreader_read(&br, 16); + } + + if (header_size == 0) + return -1; + return header_size; } -int clHCA_isOurFile1(const void *data, unsigned int size){ - int minsize; - if (size<8)return -1; - minsize = clHCA_isOurFile0(data); - if (minsize < 0 || (unsigned int)minsize > size)return -1; - if (clHCA_CheckSum(data, minsize, 0))return -1; - return 0; +int clHCA_getInfo(clHCA *hca, clHCA_stInfo *info) { + if (!hca || !info || !hca->is_valid) + return -1; + + info->version = hca->version; + info->headerSize = hca->header_size; + info->samplingRate = hca->sample_rate; + info->channelCount = hca->channels; + info->blockSize = hca->frame_size; + info->blockCount = hca->frame_count; + info->encoderDelay = hca->encoder_delay; + info->encoderPadding = hca->encoder_padding; + info->loopEnabled = hca->loop_flag; + info->loopStartBlock = hca->loop_start_frame; + info->loopEndBlock = hca->loop_end_frame; + info->loopStartDelay = hca->loop_start_delay; + info->loopEndPadding = hca->loop_end_padding; + info->samplesPerBlock = HCA_SAMPLES_PER_FRAME; + info->comment = hca->comment; + info->encryptionEnabled = hca->ciph_type == 56; /* keycode encryption */ + return 0; } -int clHCA_getInfo(clHCA *hca, clHCA_stInfo *info){ - if (!hca->_validFile)return -1; - info->version=hca->_version; - info->dataOffset=hca->_dataOffset; - info->samplingRate=hca->_samplingRate; - info->channelCount=hca->_channelCount; - info->blockSize=hca->_blockSize; - info->blockCount=hca->_blockCount; - info->loopEnabled=hca->_loopFlg; - info->loopStart=hca->_loopStart; - info->loopEnd=hca->_loopEnd; - info->comment=hca->_comm_comment; - return 0; +void clHCA_ReadSamples16(clHCA *hca, signed short *samples) { + const float scale = 32768.0f; + float f; + signed int s; + unsigned int i, j, k; + + for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { + for (j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) { + for (k = 0; k < hca->channels; k++) { + f = hca->channel[k].wave[i][j]; + //f = f * hca->rva_volume; /* rare, won't apply for now */ + if (f > 1) { + f = 1; + } else if (f < -1) { + f = -1; + } + s = (signed int) (f * scale); + if ((unsigned) (s + 0x8000) & 0xFFFF0000) + s = (s >> 31) ^ 0x7FFF; + *samples++ = (signed short) s; + } + } + } } -void clHCA_DecodeSamples16(clHCA *hca,signed short *samples){ - const float scale = 32768.0f; - float f; - signed int s; - int i, j; - unsigned int k, l; - //const float _rva_volume=hca->_rva_volume; - for(i=0;i<8;i++){ - for(j=0;j<0x80;j++){ - for(k=0,l=hca->_channelCount;k_channel[k].wave[i][j]/**_rva_volume*/; - if(f>1){f=1;}else if(f<-1){f=-1;} - s=(signed int)(f*scale); - if ((unsigned)(s+0x8000)&0xFFFF0000)s=(s>>31)^0x7FFF; - *samples++=(signed short)s; - } - } - } -} //-------------------------------------------------- // Allocation and creation //-------------------------------------------------- - -int clHCA_sizeof(){ - return sizeof(clHCA); +static void clHCA_constructor(clHCA *hca) { + if (!hca) + return; + memset(hca, 0, sizeof(*hca)); + hca->is_valid = 0; + hca->comment = 0; } -void clHCA_clear(clHCA *hca,unsigned int ciphKey1,unsigned int ciphKey2){ - clHCA_constructor(hca,ciphKey1,ciphKey2); +static void clHCA_destructor(clHCA *hca) { + if (!hca) + return; + free(hca->comment); + hca->comment = 0; } -void clHCA_done(clHCA *hca) -{ - clHCA_destructor(hca); +int clHCA_sizeof() { + return sizeof(clHCA); } -clHCA * clHCA_new(unsigned int ciphKey1, unsigned int ciphKey2){ - clHCA *hca = (clHCA *) malloc(clHCA_sizeof()); - if (hca){ - clHCA_constructor(hca,ciphKey1,ciphKey2); - } - return hca; +void clHCA_clear(clHCA *hca) { + clHCA_constructor(hca); } -void clHCA_delete(clHCA *hca){ - if (hca){ - clHCA_destructor(hca); - free(hca); - } +void clHCA_done(clHCA *hca) { + clHCA_destructor(hca); +} + +clHCA * clHCA_new() { + clHCA *hca = (clHCA *) malloc(clHCA_sizeof()); + if (hca) { + clHCA_constructor(hca); + } + return hca; +} + +void clHCA_delete(clHCA *hca) { + clHCA_destructor(hca); + free(hca); } //-------------------------------------------------- // ATH //-------------------------------------------------- -static void clATH_Init0(clATH *ath); -static void clATH_Init1(clATH *ath,unsigned int key); - -void clATH_constructor(clATH *ath){ - memset(ath,0,sizeof(*ath)); - clATH_Init0(ath); -} - -static int clATH_Init(clATH *ath,int type,unsigned int key){ - switch(type){ - case 0:clATH_Init0(ath);break; - case 1:clATH_Init1(ath,key);break; - default:return -1; - } - return 0; -} - -static const unsigned char * clATH_GetTable(clATH *ath){ - return ath->_table; -} - -void clATH_Init0(clATH *ath){ - memset(ath->_table,0,sizeof(ath->_table)); -} - -static const unsigned char clATH_list[]={ - 0x78,0x5F,0x56,0x51,0x4E,0x4C,0x4B,0x49,0x48,0x48,0x47,0x46,0x46,0x45,0x45,0x45, - 0x44,0x44,0x44,0x44,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x42,0x42,0x42,0x42,0x42, - 0x42,0x42,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x40,0x40,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, - 0x3F,0x3F,0x3F,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, - 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, - 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, - 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, - 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3F, - 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, - 0x3F,0x3F,0x3F,0x3F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x41,0x41,0x41,0x41, - 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, - 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, - 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x43,0x43,0x43, - 0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x44,0x44, - 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x45,0x45,0x45,0x45, - 0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46, - 0x46,0x46,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x48,0x48,0x48,0x48, - 0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4A,0x4A,0x4A,0x4A, - 0x4A,0x4A,0x4A,0x4A,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4C,0x4C,0x4C,0x4C,0x4C, - 0x4C,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4E,0x4E,0x4E,0x4E,0x4E,0x4E,0x4F,0x4F,0x4F, - 0x4F,0x4F,0x4F,0x50,0x50,0x50,0x50,0x50,0x51,0x51,0x51,0x51,0x51,0x52,0x52,0x52, - 0x52,0x52,0x53,0x53,0x53,0x53,0x54,0x54,0x54,0x54,0x54,0x55,0x55,0x55,0x55,0x56, - 0x56,0x56,0x56,0x57,0x57,0x57,0x57,0x57,0x58,0x58,0x58,0x59,0x59,0x59,0x59,0x5A, - 0x5A,0x5A,0x5A,0x5B,0x5B,0x5B,0x5B,0x5C,0x5C,0x5C,0x5D,0x5D,0x5D,0x5D,0x5E,0x5E, - 0x5E,0x5F,0x5F,0x5F,0x60,0x60,0x60,0x61,0x61,0x61,0x61,0x62,0x62,0x62,0x63,0x63, - 0x63,0x64,0x64,0x64,0x65,0x65,0x66,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x68,0x69, - 0x69,0x6A,0x6A,0x6A,0x6B,0x6B,0x6B,0x6C,0x6C,0x6D,0x6D,0x6D,0x6E,0x6E,0x6F,0x6F, - 0x70,0x70,0x70,0x71,0x71,0x72,0x72,0x73,0x73,0x73,0x74,0x74,0x75,0x75,0x76,0x76, - 0x77,0x77,0x78,0x78,0x78,0x79,0x79,0x7A,0x7A,0x7B,0x7B,0x7C,0x7C,0x7D,0x7D,0x7E, - 0x7E,0x7F,0x7F,0x80,0x80,0x81,0x81,0x82,0x83,0x83,0x84,0x84,0x85,0x85,0x86,0x86, - 0x87,0x88,0x88,0x89,0x89,0x8A,0x8A,0x8B,0x8C,0x8C,0x8D,0x8D,0x8E,0x8F,0x8F,0x90, - 0x90,0x91,0x92,0x92,0x93,0x94,0x94,0x95,0x95,0x96,0x97,0x97,0x98,0x99,0x99,0x9A, - 0x9B,0x9B,0x9C,0x9D,0x9D,0x9E,0x9F,0xA0,0xA0,0xA1,0xA2,0xA2,0xA3,0xA4,0xA5,0xA5, - 0xA6,0xA7,0xA7,0xA8,0xA9,0xAA,0xAA,0xAB,0xAC,0xAD,0xAE,0xAE,0xAF,0xB0,0xB1,0xB1, - 0xB2,0xB3,0xB4,0xB5,0xB6,0xB6,0xB7,0xB8,0xB9,0xBA,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, - 0xC0,0xC1,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xC9,0xCA,0xCB,0xCC,0xCD, - 0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD, - 0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xED,0xEE, - 0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFF,0xFF, +/* Base ATH (Absolute Threshold of Hearing) curve (for 41856hz). + * May be a slight modification of the standard Painter & Spanias ATH curve formula. */ +static const unsigned char ath_base_curve[656] = { + 0x78,0x5F,0x56,0x51,0x4E,0x4C,0x4B,0x49,0x48,0x48,0x47,0x46,0x46,0x45,0x45,0x45, + 0x44,0x44,0x44,0x44,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x42,0x42,0x42,0x42,0x42, + 0x42,0x42,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, + 0x3F,0x3F,0x3F,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, + 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, + 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, + 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, + 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3F, + 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, + 0x3F,0x3F,0x3F,0x3F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, + 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x43,0x43,0x43, + 0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x44,0x44, + 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x45,0x45,0x45,0x45, + 0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46, + 0x46,0x46,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x48,0x48,0x48,0x48, + 0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4A,0x4A,0x4A,0x4A, + 0x4A,0x4A,0x4A,0x4A,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4C,0x4C,0x4C,0x4C,0x4C, + 0x4C,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4E,0x4E,0x4E,0x4E,0x4E,0x4E,0x4F,0x4F,0x4F, + 0x4F,0x4F,0x4F,0x50,0x50,0x50,0x50,0x50,0x51,0x51,0x51,0x51,0x51,0x52,0x52,0x52, + 0x52,0x52,0x53,0x53,0x53,0x53,0x54,0x54,0x54,0x54,0x54,0x55,0x55,0x55,0x55,0x56, + 0x56,0x56,0x56,0x57,0x57,0x57,0x57,0x57,0x58,0x58,0x58,0x59,0x59,0x59,0x59,0x5A, + 0x5A,0x5A,0x5A,0x5B,0x5B,0x5B,0x5B,0x5C,0x5C,0x5C,0x5D,0x5D,0x5D,0x5D,0x5E,0x5E, + 0x5E,0x5F,0x5F,0x5F,0x60,0x60,0x60,0x61,0x61,0x61,0x61,0x62,0x62,0x62,0x63,0x63, + 0x63,0x64,0x64,0x64,0x65,0x65,0x66,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x68,0x69, + 0x69,0x6A,0x6A,0x6A,0x6B,0x6B,0x6B,0x6C,0x6C,0x6D,0x6D,0x6D,0x6E,0x6E,0x6F,0x6F, + 0x70,0x70,0x70,0x71,0x71,0x72,0x72,0x73,0x73,0x73,0x74,0x74,0x75,0x75,0x76,0x76, + 0x77,0x77,0x78,0x78,0x78,0x79,0x79,0x7A,0x7A,0x7B,0x7B,0x7C,0x7C,0x7D,0x7D,0x7E, + 0x7E,0x7F,0x7F,0x80,0x80,0x81,0x81,0x82,0x83,0x83,0x84,0x84,0x85,0x85,0x86,0x86, + 0x87,0x88,0x88,0x89,0x89,0x8A,0x8A,0x8B,0x8C,0x8C,0x8D,0x8D,0x8E,0x8F,0x8F,0x90, + 0x90,0x91,0x92,0x92,0x93,0x94,0x94,0x95,0x95,0x96,0x97,0x97,0x98,0x99,0x99,0x9A, + 0x9B,0x9B,0x9C,0x9D,0x9D,0x9E,0x9F,0xA0,0xA0,0xA1,0xA2,0xA2,0xA3,0xA4,0xA5,0xA5, + 0xA6,0xA7,0xA7,0xA8,0xA9,0xAA,0xAA,0xAB,0xAC,0xAD,0xAE,0xAE,0xAF,0xB0,0xB1,0xB1, + 0xB2,0xB3,0xB4,0xB5,0xB6,0xB6,0xB7,0xB8,0xB9,0xBA,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xC9,0xCA,0xCB,0xCC,0xCD, + 0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD, + 0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xED,0xEE, + 0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFF,0xFF, }; -void clATH_Init1(clATH *ath, unsigned int key){ - unsigned int i, v; - unsigned char *_table=ath->_table; - for(i=0,v=0;i<0x80;i++,v+=key){ - unsigned int index=v>>13; - if(index>=0x28E){ - memset(&_table[i],0xFF,0x80-i); - break; - } - _table[i]=clATH_list[index]; - } +static void ath_init0(unsigned char *ath_curve) { + /* disable curve */ + memset(ath_curve, 0, sizeof(ath_curve[0]) * HCA_SAMPLES_PER_SUBFRAME); +} + +static void ath_init1(unsigned char *ath_curve, unsigned int sample_rate) { + unsigned int i, index; + unsigned int acc = 0; + + /* scale ATH curve depending on frequency */ + for (i = 0; i < HCA_SAMPLES_PER_SUBFRAME; i++) { + acc += sample_rate; + index = acc >> 13; + + if (index >= 654) { + memset(ath_curve+i, 0xFF, sizeof(ath_curve[0]) * (HCA_SAMPLES_PER_SUBFRAME - i)); + break; + } + ath_curve[i] = ath_base_curve[index]; + } +} + +static int ath_init(unsigned char *ath_curve, int type, unsigned int sample_rate) { + switch (type) { + case 0: + ath_init0(ath_curve); + break; + case 1: + ath_init1(ath_curve, sample_rate); + break; + default: + return -1; + } + return 0; +} + + +//-------------------------------------------------- +// Encryption +//-------------------------------------------------- +static void cipher_decrypt(unsigned char *cipher_table, unsigned char *data, int size) { + unsigned int i; + + for (i = 0; i < size; i++) { + data[i] = cipher_table[data[i]]; + } +} + +static void cipher_init0(unsigned char *cipher_table) { + unsigned int i; + + /* no encryption */ + for (i = 0; i < 256; i++) { + cipher_table[i] = i; + } +} + +static void cipher_init1(unsigned char *cipher_table) { + const int mul = 13; + const int add = 11; + unsigned int i, v = 0; + + /* keyless encryption (unused?) */ + for (i = 1; i < 256 - 1; i++) { + v = (v * mul + add) & 0xFF; + if (v == 0 || v == 0xFF) + v = (v * mul + add) & 0xFF; + cipher_table[i] = v; + } + cipher_table[0] = 0; + cipher_table[0xFF] = 0xFF; +} + +static void cipher_init56_create_table(unsigned char *r, unsigned char key) { + const int mul = ((key & 1) << 3) | 5; + const int add = (key & 0xE) | 1; + unsigned int i; + + key >>= 4; + for (i = 0; i < 16; i++) { + key = (key * mul + add) & 0xF; + r[i] = key; + } +} + +static void cipher_init56(unsigned char *cipher_table, unsigned long long keycode) { + unsigned char kc[8]; + unsigned char seed[16]; + unsigned char base[256], base_r[16], base_c[16]; + unsigned int r, c; + + /* 56bit keycode encryption (given as a uint64_t number, but upper 8b aren't used) */ + + /* keycode = keycode - 1 */ + if (keycode != 0) + keycode--; + + /* init keycode table */ + for (r = 0; r < (8-1); r++) { + kc[r] = keycode & 0xFF; + keycode = keycode >> 8; + } + + /* init seed table */ + seed[0x00] = kc[1]; + seed[0x01] = kc[1] ^ kc[6]; + seed[0x02] = kc[2] ^ kc[3]; + seed[0x03] = kc[2]; + seed[0x04] = kc[2] ^ kc[1]; + seed[0x05] = kc[3] ^ kc[4]; + seed[0x06] = kc[3]; + seed[0x07] = kc[3] ^ kc[2]; + seed[0x08] = kc[4] ^ kc[5]; + seed[0x09] = kc[4]; + seed[0x0A] = kc[4] ^ kc[3]; + seed[0x0B] = kc[5] ^ kc[6]; + seed[0x0C] = kc[5]; + seed[0x0D] = kc[5] ^ kc[4]; + seed[0x0E] = kc[6] ^ kc[1]; + seed[0x0F] = kc[6]; + + /* init base table */ + cipher_init56_create_table(base_r, kc[0]); + for (r = 0; r < 16; r++) { + unsigned char nb; + cipher_init56_create_table(base_c, seed[r]); + nb = base_r[r] << 4; + for (c = 0; c < 16; c++) { + base[r*16 + c] = nb | base_c[c]; /* combine nibbles */ + } + } + + /* final shuffle table */ + { + unsigned int i; + unsigned int x = 0; + unsigned int pos = 1; + + for (i = 0; i < 256; i++) { + x = (x + 17) & 0xFF; + if (base[x] != 0 && base[x] != 0xFF) + cipher_table[pos++] = base[x]; + } + cipher_table[0] = 0; + cipher_table[0xFF] = 0xFF; + } +} + +static int cipher_init(unsigned char *cipher_table, int type, unsigned long long keycode) { + if (!(keycode)) + type = 0; + + switch (type) { + case 0: + cipher_init0(cipher_table); + break; + case 1: + cipher_init1(cipher_table); + break; + case 56: + cipher_init56(cipher_table, keycode); + break; + default: + return -1; + } + return 0; } //-------------------------------------------------- -// 暗号化テーブル +// Parse //-------------------------------------------------- -static void clCipher_Init0(clCipher *cipher); -static void clCipher_Init1(clCipher *cipher); -static void clCipher_Init56(clCipher *cipher,unsigned int key1,unsigned int key2); - -void clCipher_constructor(clCipher *cipher){ - memset(cipher,0,sizeof(*cipher)); - clCipher_Init0(cipher); +static unsigned int header_ceil2(unsigned int a, unsigned int b) { + return (b > 0) ? (a / b + ((a % b) ? 1 : 0)) : 0; } -static int clCipher_Init(clCipher *cipher,int type,unsigned int key1,unsigned int key2){ - if(!(key1|key2))type=0; - switch(type){ - case 0:clCipher_Init0(cipher);break; - case 1:clCipher_Init1(cipher);break; - case 56:clCipher_Init56(cipher,key1,key2);break; - default:return -1; - } - return 0; +int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) { + clData br; + + if (!hca || !data ) + return -1; + + hca->is_valid = 0; + + if (size < 0x08) + return -1; + + bitreader_init(&br, data, size); + + /* read header chunks */ + + /* HCA base header */ + if ((bitreader_peek(&br, 32) & HCA_MASK) == 0x48434100) { /* "HCA\0" */ + bitreader_skip(&br, 32); + hca->version = bitreader_read(&br, 16); + hca->header_size = bitreader_read(&br, 16); + +#if 0 // play unknown versions anyway (confirmed to exist: v1.1/v1.2/v1.3/v2.0) + if (hca->version != 0x0101 && + hca->version != 0x0102 && + hca->version != 0x0103 && + hca->version != 0x0200) + return -1; +#endif + if (size < hca->header_size) + return -1; + + if (crc16_checksum(data,hca->header_size)) + return -1; + + size -= 0x08; + } + else { + return -1; + } + + /* format info */ + if (size >= 0x10 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x666D7400) { /* "fmt\0" */ + bitreader_skip(&br, 32); + hca->channels = bitreader_read(&br, 8); + hca->sample_rate = bitreader_read(&br, 24); + hca->frame_count = bitreader_read(&br, 32); + hca->encoder_delay = bitreader_read(&br, 16); + hca->encoder_padding = bitreader_read(&br, 16); + + if (!(hca->channels >= 1 && hca->channels <= HCA_MAX_CHANNELS)) + return -1; + + if (hca->frame_count == 0) + return -1; + + if (!(hca->sample_rate >= 1 && hca->sample_rate <= 0x7FFFFF)) /* encoder max seems 48000 */ + return -1; + + size -= 0x10; + } + else { + return -1; + } + + /* compression (v2.0) or decode (v1.x) info */ + if (size >= 0x10 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x636F6D70) { /* "comp" */ + bitreader_skip(&br, 32); + hca->frame_size = bitreader_read(&br, 16); + hca->min_resolution = bitreader_read(&br, 8); + hca->max_resolution = bitreader_read(&br, 8); + hca->track_count = bitreader_read(&br, 8); + hca->channel_config = bitreader_read(&br, 8); + hca->total_band_count = bitreader_read(&br, 8); + hca->base_band_count = bitreader_read(&br, 8); + hca->stereo_band_count = bitreader_read(&br, 8); + hca->bands_per_hfr_group = bitreader_read(&br, 8); + hca->reserved1 = bitreader_read(&br, 8); + hca->reserved2 = bitreader_read(&br, 8); + + size -= 0x10; + } + else if (size >= 0x0c && (bitreader_peek(&br, 32) & HCA_MASK) == 0x64656300) { /* "dec\0" */ + bitreader_skip(&br, 32); + hca->frame_size = bitreader_read(&br, 16); + hca->min_resolution = bitreader_read(&br, 8); + hca->max_resolution = bitreader_read(&br, 8); + hca->total_band_count = bitreader_read(&br, 8) + 1; + hca->base_band_count = bitreader_read(&br, 8) + 1; + hca->track_count = bitreader_read(&br, 4); + hca->channel_config = bitreader_read(&br, 4); + hca->stereo_type = bitreader_read(&br, 8); + + if (hca->stereo_type == 0) + hca->base_band_count = hca->total_band_count; + hca->stereo_band_count = hca->total_band_count - hca->base_band_count; + hca->bands_per_hfr_group = 0; + + size -= 0x0c; + } + else { + return -1; + } + + /* VBR (variable bit rate) info */ + if (size >= 0x08 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x76627200) { /* "vbr\0" */ + bitreader_skip(&br, 32); + hca->vbr_max_frame_size = bitreader_read(&br, 16); + hca->vbr_noise_Level = bitreader_read(&br, 16); + + if (!(hca->frame_size == 0 && hca->vbr_max_frame_size > 8 && hca->vbr_max_frame_size <= 0x1FF)) + return -1; + + size -= 0x08; + } + else { + /* removed in v2.0, probably unused in v1.x */ + hca->vbr_max_frame_size = 0; + hca->vbr_noise_Level = 0; + } + + /* ATH (Absolute Threshold of Hearing) info */ + if (size >= 0x06 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x61746800) { /* "ath\0" */ + bitreader_skip(&br, 32); + hca->ath_type = bitreader_read(&br, 16); + } + else { + /* removed in v2.0, default in v1.x (maybe only used in v1.1, as v1.2/v1.3 set ath_type = 0) */ + hca->ath_type = (hca->version < 0x200) ? 1 : 0; + } + + /* loop info */ + if (size >= 0x10 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x6C6F6F70) { /* "loop" */ + bitreader_skip(&br, 32); + hca->loop_start_frame = bitreader_read(&br, 32); + hca->loop_end_frame = bitreader_read(&br, 32); + hca->loop_start_delay = bitreader_read(&br, 16); + hca->loop_end_padding = bitreader_read(&br, 16); + + hca->loop_flag = 1; + + if (!(hca->loop_start_frame >= 0 && hca->loop_start_frame <= hca->loop_end_frame + && hca->loop_end_frame < hca->frame_count)) + return -1; + + size -= 0x10; + } + else { + hca->loop_start_frame = 0; + hca->loop_end_frame = 0; + hca->loop_start_delay = 0; + hca->loop_end_padding = 0; + + hca->loop_flag = 0; + } + + /* cipher/encryption info */ + if (size >= 0x06 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x63697068) { /* "ciph" */ + bitreader_skip(&br, 32); + hca->ciph_type = bitreader_read(&br, 16); + + if (!(hca->ciph_type == 0 || hca->ciph_type == 1 || hca->ciph_type == 56)) + return -1; + + size -= 0x06; + } + else { + hca->ciph_type = 0; + } + + /* RVA (relative volume adjustment) info */ + if (size >= 0x08 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x72766100) { /* "rva\0" */ + union { + unsigned int i; + float f; + } rva_volume_cast; + bitreader_skip(&br, 32); + rva_volume_cast.i = bitreader_read(&br, 32); + hca->rva_volume = rva_volume_cast.f; + + size -= 0x08; + } else { + hca->rva_volume = 1; + } + + /* comment */ + if (size >= 0x05 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x636F6D6D) {/* "comm" */ + unsigned int i; + char *temp; + bitreader_skip(&br, 32); + hca->comment_len = bitreader_read(&br, 8); + + if (hca->comment_len > size) + return -1; + + temp = realloc(hca->comment, hca->comment_len + 1); + if (!temp) + return -1; + hca->comment = temp; + for (i = 0; i < hca->comment_len; ++i) + hca->comment[i] = bitreader_read(&br, 8); + hca->comment[i] = '\0'; /* should be null terminated but make sure */ + + size -= 0x05 + hca->comment_len; + } + else { + hca->comment_len = 0; + hca->comment = NULL; + } + + /* padding info */ + if (size >= 0x04 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x70616400) { /* "pad\0" */ + size -= (size - 0x02); /* fills up to header_size, sans checksum */ + } + + /* should be fully read, but allow data buffer may be bigger than header_size */ + //if (size != 0x02) + // return -1; + + + /* extra validations */ + if (!(hca->frame_size >= 0x08 && hca->frame_size <= 0xFFFF)) /* actual max seems 0x155*channels */ + return -1; /* theoretically can be 0 if VBR (not seen) */ + + if (!(hca->min_resolution == 1 && hca->max_resolution == 15)) + return -1; + + + /* inits state */ + if (hca->track_count == 0) + hca->track_count = 1; /* default to avoid division by zero */ + + hca->hfr_group_count = header_ceil2( + hca->total_band_count - hca->base_band_count - hca->stereo_band_count, + hca->bands_per_hfr_group); + + if (ath_init(hca->ath_curve, hca->ath_type, hca->sample_rate) < 0) + return -1; + if (cipher_init(hca->cipher_table, hca->ciph_type, hca->keycode) < 0) + return -1; + + + /* init channels */ + { + int channel_types[HCA_MAX_CHANNELS] = {0}; + unsigned int i, channels_per_track; + + channels_per_track = hca->channels / hca->track_count; + if (hca->stereo_band_count && channels_per_track > 1) { + int *ct = channel_types; + for (i = 0; i < hca->track_count; i++, ct += channels_per_track) { + switch (channels_per_track) { + case 2: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + break; + case 3: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + break; + case 4: + ct[0] = STEREO_PRIMARY; + ct[1] = 2; + if (hca->channel_config == 0) { + ct[2] = STEREO_PRIMARY; + ct[3] = STEREO_SECONDARY; + } else { + ct[2] = DISCRETE; + ct[3] = DISCRETE; + } + break; + case 5: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + if (hca->channel_config <= 2) { + ct[3] = STEREO_PRIMARY; + ct[4] = STEREO_SECONDARY; + } else { + ct[3] = DISCRETE; + ct[4] = DISCRETE; + } + break; + case 6: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + ct[3] = DISCRETE; + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + break; + case 7: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + ct[3] = DISCRETE; + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + ct[6] = DISCRETE; + break; + case 8: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + ct[3] = DISCRETE; + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + ct[6] = STEREO_PRIMARY; + ct[7] = STEREO_SECONDARY; + break; + } + } + } + + memset(hca->channel, 0, sizeof(hca->channel)); + for (i = 0; i < hca->channels; i++) { + hca->channel[i].type = channel_types[i]; + hca->channel[i].coded_scalefactor_count = (channel_types[i] != 2) ? + hca->base_band_count + hca->stereo_band_count : + hca->base_band_count; + hca->channel[i].hfr_scales = &hca->channel[i].scalefactors[hca->base_band_count + hca->stereo_band_count]; + } + } + + + /* clHCA is correctly initialized and decoder state reset + * (keycode is not changed between calls) */ + hca->is_valid = 1; + + return 0; } -static void clCipher_Mask(clCipher *cipher,void *data,int size){ - unsigned char *_table=cipher->_table; - unsigned char *d; - for(d=(unsigned char *)data;size>0;d++,size--)*d=_table[*d]; +void clHCA_SetKey(clHCA *hca, unsigned long long keycode) { + if (!hca) + return; + hca->keycode = keycode; + + /* May be called even if clHCA is not valid (header not parsed), as the + * key will be used during DecodeHeader ciph init. If header was already + * parsed reinitializes the decryption table using the new key. */ + if (hca->is_valid) { + /* ignore error since it can't really fail */ + cipher_init(hca->cipher_table, hca->ciph_type, hca->keycode); + } } -void clCipher_Init0(clCipher *cipher){ - unsigned char *_table=cipher->_table; - int i; - for(i=0;i<0x100;i++)_table[i]=i; +int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size) { + const float scale = 32768.0f; + unsigned int ch, sf, s; + int status; + int clips = 0, blanks = 0; + float fsample; + signed int psample; + + /* return if decode fails (happens sometimes with wrong keys) */ + status = clHCA_DecodeBlock(hca, data, size); + if (status < 0) + return -1; + + /* check decode results */ + for (ch = 0; ch < hca->channels; ch++) { + for (sf = 0; sf < HCA_SUBFRAMES_PER_FRAME; sf++) { + for (s = 0; s < HCA_SAMPLES_PER_SUBFRAME; s++) { + fsample = hca->channel[ch].wave[sf][s]; + psample = (signed int) (fsample * scale); + if (fsample > 1.0f || fsample < -1.0f) + clips++; + else if (psample == 0 || psample == -1) + blanks++; + } + } + } + + /* the more clips the less likely block was correctly decrypted */ + if (clips > 0) + return clips; + /* if block is silent result is not useful */ + if (blanks == hca->channels * HCA_SUBFRAMES_PER_FRAME * HCA_SAMPLES_PER_SUBFRAME) + return 0; + + /* block may be correct (but wrong keys can get this too) */ + return 1; } -void clCipher_Init1(clCipher *cipher){ - unsigned char *_table=cipher->_table; - int i, v; - for(i=1,v=0;i<0xFF;i++){ - v=(v*13+11)&0xFF; - if(v==0||v==0xFF)v=(v*13+11)&0xFF; - _table[i]=v; - } - _table[0]=0; - _table[0xFF]=0xFF; + +#if 0 +// it'd seem like resetting IMDCT (others get overwritten) would matter when restarting the +// stream from 0, but doesn't seem any different, maybe because the first frame acts as setup/empty +void clHCA_DecodeReset(clHCA * hca) { + unsigned int i; + + if (!hca || !hca->is_valid) + return; + + for (i = 0; i < hca->channels; i++) { + stChannel *ch = &hca->channel[i]; + + memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME); + memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->imdct_previous, 0, sizeof(ch->imdct_previous[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME); + } } +#endif -static void clCipher_Init56_CreateTable(unsigned char *r,unsigned char key); +//-------------------------------------------------- +// Decode +//-------------------------------------------------- +static int decode1_unpack_channel(stChannel *ch, clData *br, + unsigned int hfr_group_count, unsigned int packed_noise_level, const unsigned char *ath_curve); -void clCipher_Init56(clCipher *cipher,unsigned int key1,unsigned int key2){ - unsigned char *_table=cipher->_table; +static void decode2_dequantize_coefficients(stChannel *ch, clData *br); - // テーブル1を生成 - unsigned char t1[8]; - unsigned char t2[0x10]; - unsigned char t3[0x100],t31[0x10],t32[0x10],*t; +static void decode3_reconstruct_high_frequency(stChannel *ch, + unsigned int hfr_group_count, unsigned int bands_per_hfr_group, + unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count); - int i, j, v; +static void decode4_apply_intensity_stereo(stChannel *ch, int subframe, + unsigned int usable_band_count, unsigned int base_band_count, unsigned int stereo_band_count); - if(!key1)key2--; - key1--; - for(i=0;i<7;i++){ - t1[i]=key1; - key1=(key1>>8)|(key2<<24); - key2>>=8; - } +static void decoder5_run_imdct(stChannel *ch, int subframe); - // テーブル2 - t2[0x00]=t1[1]; t2[0x01]=t1[1]^t1[6]; - t2[0x02]=t1[2]^t1[3]; t2[0x03]=t1[2]; - t2[0x04]=t1[2]^t1[1]; t2[0x05]=t1[3]^t1[4]; - t2[0x06]=t1[3]; t2[0x07]=t1[3]^t1[2]; - t2[0x08]=t1[4]^t1[5]; t2[0x09]=t1[4]; - t2[0x0A]=t1[4]^t1[3]; t2[0x0B]=t1[5]^t1[6]; - t2[0x0C]=t1[5]; t2[0x0D]=t1[5]^t1[4]; - t2[0x0E]=t1[6]^t1[1]; t2[0x0F]=t1[6]; - // テーブル3 - t=t3; - clCipher_Init56_CreateTable(t31,t1[0]); - for(i=0;i<0x10;i++){ - unsigned char v; - clCipher_Init56_CreateTable(t32,t2[i]); - v=t31[i]<<4; - for(j=0;j<0x10;j++){ - *(t++)=v|t32[j]; - } - } +int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) { + clData br; + unsigned short sync; + unsigned int subframe, ch; - // CIPHテーブル - t=&_table[1]; - for(i=0,v=0;i<0x100;i++){ - unsigned char a; - v=(v+0x11)&0xFF; - a=t3[v]; - if(a!=0&&a!=0xFF)*(t++)=a; - } - _table[0]=0; - _table[0xFF]=0xFF; + if (!data || !hca || !hca->is_valid) + return -1; + if (size < hca->frame_size) + return -1; -} + bitreader_init(&br, data, hca->frame_size); -void clCipher_Init56_CreateTable(unsigned char *r,unsigned char key){ - int mul=((key&1)<<3)|5; - int add=(key&0xE)|1; - int i; - key>>=4; - for(i=0;i<0x10;i++){ - key=(key*mul+add)&0xF; - *(r++)=key; - } + /* test sync (not encrypted) */ + sync = bitreader_read(&br, 16); + if (sync != 0xFFFF) + return -1; + + if (crc16_checksum(data, hca->frame_size)) + return -1; + + cipher_decrypt(hca->cipher_table, data, hca->frame_size); + + + /* unpack frame values */ + { + unsigned int frame_acceptable_noise_level = bitreader_read(&br, 9); + unsigned int frame_evaluation_boundary = bitreader_read(&br, 7); + unsigned int packed_noise_level = (frame_acceptable_noise_level << 8) - frame_evaluation_boundary; + + for (ch = 0; ch < hca->channels; ch++) { + int unpack = decode1_unpack_channel(&hca->channel[ch], &br, + hca->hfr_group_count, packed_noise_level, hca->ath_curve); + if (unpack < 0) + return -1; + } + } + + for (subframe = 0; subframe < HCA_SUBFRAMES_PER_FRAME; subframe++) { + + /* unpack channel data and get dequantized spectra */ + for (ch = 0; ch < hca->channels; ch++){ + decode2_dequantize_coefficients(&hca->channel[ch], &br); + } + + /* restore missing bands from spectra 1 */ + for (ch = 0; ch < hca->channels; ch++) { + decode3_reconstruct_high_frequency(&hca->channel[ch], + hca->hfr_group_count, hca->bands_per_hfr_group, + hca->stereo_band_count, hca->base_band_count, hca->total_band_count); + } + + /* restore missing bands from spectra 2 */ + for (ch = 0; ch < hca->channels - 1; ch++) { + decode4_apply_intensity_stereo(&hca->channel[ch], subframe, + hca->total_band_count, hca->base_band_count, hca->stereo_band_count); + } + + /* apply imdct */ + for (ch = 0; ch < hca->channels; ch++) { + decoder5_run_imdct(&hca->channel[ch], subframe); + } + } + + /* should read all frame sans checksum at most */ + if (br.bit > br.size - 16) { + return -1; + } + + return 0; } //-------------------------------------------------- -// デコード +// Decode 1st step //-------------------------------------------------- -static void stChannel_Decode1(stChannel *ch,clData *data,unsigned int a,int b,const unsigned char *ath); -static void stChannel_Decode2(stChannel *ch,clData *data); -static void stChannel_Decode3(stChannel *ch,unsigned int a,unsigned int b,unsigned int c,unsigned int d); -static void stChannel_Decode4(stChannel *ch,int index,unsigned int a,unsigned int b,unsigned int c); -static void stChannel_Decode5(stChannel *ch,int index); - -int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ - - // チェック - if(!(data))return -1; - - // ヘッダ - if(address==0){ - char r[0x10]; - unsigned int i, b; - clData d; - - hca->_validFile = 0; - - clData_constructor(&d, data, size); - - // サイズチェック - if(size_version=clData_GetBit(&d,16); - hca->_dataOffset=clData_GetBit(&d,16); - //if(!(_version<=0x200&&_version>0x101))return -1; // バージョンチェック(無効) - if(size_dataOffset)return -1; - //if(clHCA_CheckSum(hca,_dataOffset,0))return -1; // ヘッダの破損チェック(ヘッダ改変を有効にするため破損チェック無効) - size-=sizeof(stHeader); - }else{ - return -1; - } - - // fmt - if(size>=sizeof(stFormat) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x666D7400){/*'fmt\0'*/ - clData_AddBit(&d,32); - hca->_channelCount=clData_GetBit(&d,8); - hca->_samplingRate=clData_GetBit(&d,24); - hca->_blockCount=clData_GetBit(&d,32); - hca->_fmt_r01=clData_GetBit(&d,16); - hca->_fmt_r02=clData_GetBit(&d,16); - if(!(hca->_channelCount>=1&&hca->_channelCount<=16))return -1; - if(!(hca->_samplingRate>=1&&hca->_samplingRate<=0x7FFFFF))return -1; - size-=sizeof(stFormat); - }else{ - return -1; - } - - // comp - if(size>=sizeof(stCompress) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D70){/*'comp'*/ - clData_AddBit(&d,32); - hca->_blockSize=clData_GetBit(&d,16); - hca->_comp_r01=clData_GetBit(&d,8); - hca->_comp_r02=clData_GetBit(&d,8); - hca->_comp_r03=clData_GetBit(&d,8); - hca->_comp_r04=clData_GetBit(&d,8); - hca->_comp_r05=clData_GetBit(&d,8); - hca->_comp_r06=clData_GetBit(&d,8); - hca->_comp_r07=clData_GetBit(&d,8); - hca->_comp_r08=clData_GetBit(&d,8); - clData_AddBit(&d,8+8); - if(!((hca->_blockSize>=8&&hca->_blockSize<=0xFFFF)||(hca->_blockSize==0)))return -1; - if(!(/*hca->_comp_r01>=0&&*/hca->_comp_r01<=hca->_comp_r02&&hca->_comp_r02<=0x1F))return -1; - size-=sizeof(stCompress); - } - - // dec - else if(size>=sizeof(stDecode) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x64656300){/*'dec\0'*/ - unsigned char count1,count2,enableCount2; - clData_AddBit(&d,32); - hca->_blockSize=clData_GetBit(&d,16); - hca->_comp_r01=clData_GetBit(&d,8); - hca->_comp_r02=clData_GetBit(&d,8); - count1=clData_GetBit(&d,8); - count2=clData_GetBit(&d,8); - hca->_comp_r03=clData_GetBit(&d,4); - hca->_comp_r04=clData_GetBit(&d,4); - enableCount2=clData_GetBit(&d,8); - hca->_comp_r05=count1+1; - hca->_comp_r06=((enableCount2)?count2:count1)+1; - hca->_comp_r07=hca->_comp_r05-hca->_comp_r06; - hca->_comp_r08=0; - if(!((hca->_blockSize>=8&&hca->_blockSize<=0xFFFF)||(hca->_blockSize==0)))return -1; - if(!(/*hca->_comp_r01>=0&&*/hca->_comp_r01<=hca->_comp_r02&&hca->_comp_r02<=0x1F))return -1; - if(!hca->_comp_r03)hca->_comp_r03=1; - size-=sizeof(stDecode); - }else{ - return -1; - } - - // vbr - if(size>=sizeof(stVBR) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x76627200){/*'vbr\0'*/ - clData_AddBit(&d,32); - hca->_vbr_r01=clData_GetBit(&d,16); - hca->_vbr_r02=clData_GetBit(&d,16); - if(!(hca->_blockSize==0&&/*hca->_vbr_r01>=0&&*/hca->_vbr_r01<=0x1FF))return -1; - }else{ - hca->_vbr_r01=0; - hca->_vbr_r02=0; - } - - // ath - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x61746800){/*'ath\0'*/ - clData_AddBit(&d,32); - hca->_ath_type=clData_GetBit(&d,16); - }else{ - hca->_ath_type=(hca->_version<0x200)?1:0;//v1.3ではデフォルト値が1になってたが、v2.0からATHテーブルが廃止されてるみたいなので0に - } - - // loop - if(size>=sizeof(stLoop) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x6C6F6F70){/*'loop'*/ - clData_AddBit(&d,32); - hca->_loopStart=clData_GetBit(&d,32); - hca->_loopEnd=clData_GetBit(&d,32); - hca->_loop_r01=clData_GetBit(&d,16); - hca->_loop_r02=clData_GetBit(&d,16); - hca->_loopFlg=1; - if(!(/*hca->_loopStart>=0&&*/hca->_loopStart<=hca->_loopEnd&&hca->_loopEnd_blockCount))return -1; - size-=sizeof(stLoop); - }else{ - hca->_loopStart=0; - hca->_loopEnd=0; - hca->_loop_r01=0; - hca->_loop_r02=0x400; - hca->_loopFlg=0; - } - - // ciph - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x63697068){/*'ciph'*/ - clData_AddBit(&d,32); - hca->_ciph_type=clData_GetBit(&d,16); - if(!(hca->_ciph_type==0||hca->_ciph_type==1||hca->_ciph_type==0x38))return -1; - size-=6; - }else{ - hca->_ciph_type=0; - } - - // rva - if(size>=sizeof(stRVA) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x72766100){/*'rva\0'*/ - union { unsigned int i; float f; } v; - clData_AddBit(&d,32); - v.i=clData_GetBit(&d,32); - hca->_rva_volume=v.f; - size-=sizeof(stRVA); - }else{ - hca->_rva_volume=1; - } - - // comm - if(size>=5 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D6D){/*'comm'*/ - void * newmem; - unsigned int i; - clData_AddBit(&d,32); - hca->_comm_len=clData_GetBit(&d,8); - if(hca->_comm_len>size)return -1; - newmem=realloc(hca->_comm_comment,hca->_comm_len+1); - if(!newmem)return -1; - hca->_comm_comment=(char *)newmem; - for(i=0;i_comm_len;++i) - ((char *)newmem)[i]=clData_GetBit(&d,8); - ((char *)newmem)[i]='\0'; - size-=5+hca->_comm_len; - }else{ - hca->_comm_len=0; - hca->_comm_comment=NULL; - } - - // 初期化 - if(clATH_Init(&hca->_ath,hca->_ath_type,hca->_samplingRate) < 0)return -1; - if(clCipher_Init(&hca->_cipher,hca->_ciph_type,hca->_ciph_key1,hca->_ciph_key2) < 0)return -1; - - // 値チェック(ヘッダの改変ミスによるエラーを回避するため) - if(!hca->_comp_r03)hca->_comp_r03=1;//0での除算を防ぐため - - // デコード準備 - memset(hca->_channel,0,sizeof(hca->_channel)); - - if(!(hca->_comp_r01==1&&hca->_comp_r02==15))return -1; - - hca->_comp_r09=ceil2(hca->_comp_r05-(hca->_comp_r06+hca->_comp_r07),hca->_comp_r08); - - memset(r,0,sizeof(r)); - b=hca->_channelCount/hca->_comp_r03; - if(hca->_comp_r07&&b>1){ - char *c=r; - for(i=0;i_comp_r03;i++,c+=b){ - switch(b){ - case 2:c[0]=1;c[1]=2;break; - case 3:c[0]=1;c[1]=2;break; - case 4:c[0]=1;c[1]=2;if(hca->_comp_r04==0){c[2]=1;c[3]=2;}break; - case 5:c[0]=1;c[1]=2;if(hca->_comp_r04<=2){c[3]=1;c[4]=2;}break; - case 6:c[0]=1;c[1]=2;c[4]=1;c[5]=2;break; - case 7:c[0]=1;c[1]=2;c[4]=1;c[5]=2;break; - case 8:c[0]=1;c[1]=2;c[4]=1;c[5]=2;c[6]=1;c[7]=2;break; - } - } - } - for(i=0;i_channelCount;i++){ - hca->_channel[i].type=r[i]; - hca->_channel[i].value3=&hca->_channel[i].value[hca->_comp_r06+hca->_comp_r07]; - hca->_channel[i].count=hca->_comp_r06+((r[i]!=2)?hca->_comp_r07:0); - } - - hca->_validFile = 1; - } - - // ブロックデータ - else if(address>=hca->_dataOffset){ - clData d; - int magic; - unsigned int i, j; - if(!hca->_validFile)return -1; - if(size_blockSize)return -1; - if(clHCA_CheckSum(data,hca->_blockSize,0))return -1; - clCipher_Mask(&hca->_cipher,data,hca->_blockSize); - clData_constructor(&d,data,hca->_blockSize); - magic=clData_GetBit(&d,16);//0xFFFF固定 - if(magic==0xFFFF){ - const unsigned int _channelCount=hca->_channelCount; - int a=(clData_GetBit(&d,9)<<8)-clData_GetBit(&d,7); - for(i=0;i<_channelCount;i++)stChannel_Decode1(&hca->_channel[i],&d,hca->_comp_r09,a,clATH_GetTable(&hca->_ath)); - for(i=0;i<8;i++){ - for(j=0;j<_channelCount;j++)stChannel_Decode2(&hca->_channel[j],&d); - for(j=0;j<_channelCount;j++)stChannel_Decode3(&hca->_channel[j],hca->_comp_r09,hca->_comp_r08,hca->_comp_r07+hca->_comp_r06,hca->_comp_r05); - for(j=0;j<_channelCount-1;j++)stChannel_Decode4(&hca->_channel[j],i,hca->_comp_r05-hca->_comp_r06,hca->_comp_r06,hca->_comp_r07); - for(j=0;j<_channelCount;j++)stChannel_Decode5(&hca->_channel[j],i); - } - } - } - - return 0; -} - -//-------------------------------------------------- -// デコード第一段階 -// ベースデータの読み込み -//-------------------------------------------------- -static const unsigned char stChannel_Decode1_scalelist[]={ - // v2.0 - 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0D,0x0D, - 0x0D,0x0D,0x0D,0x0D,0x0C,0x0C,0x0C,0x0C, - 0x0C,0x0C,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, - 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x09, - 0x09,0x09,0x09,0x09,0x09,0x08,0x08,0x08, - 0x08,0x08,0x08,0x07,0x06,0x06,0x05,0x04, - 0x04,0x04,0x03,0x03,0x03,0x02,0x02,0x02, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - // v1.3 - //0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0D,0x0D, - //0x0D,0x0D,0x0D,0x0D,0x0C,0x0C,0x0C,0x0C, - //0x0C,0x0C,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, - //0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x09, - //0x09,0x09,0x09,0x09,0x09,0x08,0x08,0x08, - //0x08,0x08,0x08,0x07,0x06,0x06,0x05,0x04, - //0x04,0x04,0x03,0x03,0x03,0x02,0x02,0x02, - //0x02,0x01,0x01,0x01,0x01,0x01,0x01,0x01, -}; -static const unsigned int stChannel_Decode1_valueInt[]={ - 0x342A8D26,0x34633F89,0x3497657D,0x34C9B9BE,0x35066491,0x353311C4,0x356E9910,0x359EF532, - 0x35D3CCF1,0x360D1ADF,0x363C034A,0x367A83B3,0x36A6E595,0x36DE60F5,0x371426FF,0x3745672A, - 0x37838359,0x37AF3B79,0x37E97C38,0x381B8D3A,0x384F4319,0x388A14D5,0x38B7FBF0,0x38F5257D, - 0x3923520F,0x39599D16,0x3990FA4D,0x39C12C4D,0x3A00B1ED,0x3A2B7A3A,0x3A647B6D,0x3A9837F0, - 0x3ACAD226,0x3B071F62,0x3B340AAF,0x3B6FE4BA,0x3B9FD228,0x3BD4F35B,0x3C0DDF04,0x3C3D08A4, - 0x3C7BDFED,0x3CA7CD94,0x3CDF9613,0x3D14F4F0,0x3D467991,0x3D843A29,0x3DB02F0E,0x3DEAC0C7, - 0x3E1C6573,0x3E506334,0x3E8AD4C6,0x3EB8FBAF,0x3EF67A41,0x3F243516,0x3F5ACB94,0x3F91C3D3, - 0x3FC238D2,0x400164D2,0x402C6897,0x4065B907,0x40990B88,0x40CBEC15,0x4107DB35,0x413504F3, -}; -static const unsigned int stChannel_Decode1_scaleInt[]={ - 0x00000000,0x3F2AAAAB,0x3ECCCCCD,0x3E924925,0x3E638E39,0x3E3A2E8C,0x3E1D89D9,0x3E088889, - 0x3D842108,0x3D020821,0x3C810204,0x3C008081,0x3B804020,0x3B002008,0x3A801002,0x3A000801, -}; -static const float *stChannel_Decode1_valueFloat=(const float *)stChannel_Decode1_valueInt; -static const float *stChannel_Decode1_scaleFloat=(const float *)stChannel_Decode1_scaleInt; - -void stChannel_Decode1(stChannel *ch,clData *data,unsigned int a,int b,const unsigned char *ath){ - unsigned int i; - const unsigned int count=ch->count; - char *value=ch->value; - char *value2=ch->value2; - char *value3=ch->value3; - char *scale=ch->scale; - float *base=ch->base; - int v=clData_GetBit(data,3); - if(v>=6){ - for(i=0;i>1,v4; - value[0]=v1; - for(i=1;itype==2){ - v=clData_CheckBit(data,4);value2[0]=v; - if(v<15)for(i=0;i<8;i++)value2[i]=clData_GetBit(data,4); - }else{ - for(i=0;i>8)-((v*5)>>1)+1; - if(v<0)v=15; - else if(v>=0x39)v=1; - else v=stChannel_Decode1_scalelist[v]; - } - scale[i]=v; - } - memset(&scale[count],0,0x80-count); - for(i=0;icount; - const char *scale=ch->scale; - const float *base=ch->base; - float *block=ch->block; - for(i=0;i>1); - if(!v)clData_AddBit(data,-1); - f=(float)v; - } - block[i]=base[i]*f; - } - memset(&block[count],0,sizeof(float)*(0x80-count)); +/* scalefactor-to-scaling table, generated from sqrt(128) * (2^(53/128))^(scale_factor - 63) */ +static const unsigned int decode1_dequantizer_scaling_table_int[64] = { + 0x342A8D26,0x34633F89,0x3497657D,0x34C9B9BE,0x35066491,0x353311C4,0x356E9910,0x359EF532, + 0x35D3CCF1,0x360D1ADF,0x363C034A,0x367A83B3,0x36A6E595,0x36DE60F5,0x371426FF,0x3745672A, + 0x37838359,0x37AF3B79,0x37E97C38,0x381B8D3A,0x384F4319,0x388A14D5,0x38B7FBF0,0x38F5257D, + 0x3923520F,0x39599D16,0x3990FA4D,0x39C12C4D,0x3A00B1ED,0x3A2B7A3A,0x3A647B6D,0x3A9837F0, + 0x3ACAD226,0x3B071F62,0x3B340AAF,0x3B6FE4BA,0x3B9FD228,0x3BD4F35B,0x3C0DDF04,0x3C3D08A4, + 0x3C7BDFED,0x3CA7CD94,0x3CDF9613,0x3D14F4F0,0x3D467991,0x3D843A29,0x3DB02F0E,0x3DEAC0C7, + 0x3E1C6573,0x3E506334,0x3E8AD4C6,0x3EB8FBAF,0x3EF67A41,0x3F243516,0x3F5ACB94,0x3F91C3D3, + 0x3FC238D2,0x400164D2,0x402C6897,0x4065B907,0x40990B88,0x40CBEC15,0x4107DB35,0x413504F3, +}; +static const float *decode1_dequantizer_scaling_table = (const float *)decode1_dequantizer_scaling_table_int; + +static const unsigned int decode1_quantizer_step_size_int[16] = { + 0x00000000,0x3F2AAAAB,0x3ECCCCCD,0x3E924925,0x3E638E39,0x3E3A2E8C,0x3E1D89D9,0x3E088889, + 0x3D842108,0x3D020821,0x3C810204,0x3C008081,0x3B804020,0x3B002008,0x3A801002,0x3A000801, +}; +static const float *decode1_quantizer_step_size = (const float *)decode1_quantizer_step_size_int; + +static int decode1_unpack_channel(stChannel *ch, clData *br, + unsigned int hfr_group_count, unsigned int packed_noise_level, const unsigned char *ath_curve) { + unsigned int i; + const unsigned int csf_count = ch->coded_scalefactor_count; + + + /* read scalefactors */ + { + /* scale indexes to normalize dequantized coefficients */ + unsigned char scalefactor_delta_bits = bitreader_read(br, 3); + if (scalefactor_delta_bits >= 6) { + /* normal scalefactors */ + for (i = 0; i < csf_count; i++) { + ch->scalefactors[i] = bitreader_read(br, 6); + } + } + else if (scalefactor_delta_bits > 0) { + /* delta scalefactors */ + const unsigned char expected_delta = (1 << scalefactor_delta_bits) - 1; + const unsigned char extra_delta = expected_delta >> 1; + unsigned char scalefactor_prev = bitreader_read(br, 6); + + ch->scalefactors[0] = scalefactor_prev; + for (i = 1; i < csf_count; i++) { + unsigned char delta = bitreader_read(br, scalefactor_delta_bits); + + if (delta != expected_delta) { + /* may happen with bad keycodes, scalefactors must be 6b indexes */ + int scalefactor_test = (int)scalefactor_prev + ((int)delta - (int)extra_delta); + if (scalefactor_test < 0 || scalefactor_test > 64) { + return -1; + } + + scalefactor_prev += delta - extra_delta; + } else { + scalefactor_prev = bitreader_read(br, 6); + } + ch->scalefactors[i] = scalefactor_prev; + } + } + else { + /* no scalefactors */ + memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME); + } + } + + if (ch->type == STEREO_SECONDARY) { + /* read intensity */ + unsigned char intensity_value = bitreader_peek(br, 4); + + ch->intensity[0] = intensity_value; + if (intensity_value < 15) { + for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { + ch->intensity[i] = bitreader_read(br, 4); + } + } + /* 15 may be an invalid value? */ + //else { + // return -1; + //} + } + else { + /* read hfr scalefactors */ + for (i = 0; i < hfr_group_count; i++) { + ch->hfr_scales[i] = bitreader_read(br, 6); + } + } + + /* calculate resolutions */ + { + /* resolution determines the range of values per encoded spectra, + * using codebooks for lower resolutions during dequantization */ + for (i = 0; i < csf_count; i++) { + unsigned char new_resolution = 0; + unsigned char scalefactor = ch->scalefactors[i]; + if (scalefactor > 0) { + int noise_level = ath_curve[i] + ((packed_noise_level + i) >> 8); + int curve_position = noise_level - ((5 * scalefactor) >> 1) + 1; + + /* curve values can be simplified by clamping position to (0,58) and making + * scale_table[0] = 15, table[58] = 1 (like VGAudio does) */ + if (curve_position < 0) + new_resolution = 15; + else if (curve_position >= 57) + new_resolution = 1; + else + new_resolution = decode1_scale_to_resolution_curve[curve_position]; + } + ch->resolution[i] = new_resolution; + } + memset(&ch->resolution[csf_count], 0, sizeof(ch->resolution[0]) * (HCA_SAMPLES_PER_SUBFRAME - csf_count)); + } + + /* calculate gain */ + { + /* get actual scales to dequantize */ + for (i = 0; i < csf_count; i++) { + float scalefactor_scale = decode1_dequantizer_scaling_table[ ch->scalefactors[i] ]; + float resolution_scale = decode1_quantizer_step_size[ ch->resolution[i] ]; + ch->gain[i] = scalefactor_scale * resolution_scale; + } + } + + return 0; } //-------------------------------------------------- -// デコード第三段階 -// ブロックデータ修正その1 ※v2.0から追加 +// Decode 2nd step //-------------------------------------------------- -static const unsigned int stChannel_Decode3_listInt[2][0x40]={ - { - 0x00000000,0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5, - 0x33E0CCDF,0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF, - 0x358B95C2,0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF, - 0x372D583F,0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF, - 0x38D744FD,0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8, - 0x3A85AAC3,0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A, - 0x3C25FED7,0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B, - 0x3DCE248C,0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B, - },{ - 0x3F800000,0x3FAA8D26,0x3FE33F89,0x4017657D,0x4049B9BE,0x40866491,0x40B311C4,0x40EE9910, - 0x411EF532,0x4153CCF1,0x418D1ADF,0x41BC034A,0x41FA83B3,0x4226E595,0x425E60F5,0x429426FF, - 0x42C5672A,0x43038359,0x432F3B79,0x43697C38,0x439B8D3A,0x43CF4319,0x440A14D5,0x4437FBF0, - 0x4475257D,0x44A3520F,0x44D99D16,0x4510FA4D,0x45412C4D,0x4580B1ED,0x45AB7A3A,0x45E47B6D, - 0x461837F0,0x464AD226,0x46871F62,0x46B40AAF,0x46EFE4BA,0x471FD228,0x4754F35B,0x478DDF04, - 0x47BD08A4,0x47FBDFED,0x4827CD94,0x485F9613,0x4894F4F0,0x48C67991,0x49043A29,0x49302F0E, - 0x496AC0C7,0x499C6573,0x49D06334,0x4A0AD4C6,0x4A38FBAF,0x4A767A41,0x4AA43516,0x4ADACB94, - 0x4B11C3D3,0x4B4238D2,0x4B8164D2,0x4BAC6897,0x4BE5B907,0x4C190B88,0x4C4BEC15,0x00000000, - } +static const unsigned char decode2_quantized_spectrum_max_bits[16] = { + 0,2,3,3,4,4,4,4,5,6,7,8,9,10,11,12 +}; +static const unsigned char decode2_quantized_spectrum_bits[128] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,3,3,0,0,0,0,0,0,0,0, + 2,2,3,3,3,3,3,3,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4, + 3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4, + 3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4, + 3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +}; +static const float decode2_quantized_spectrum_value[128] = { + +0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,-1,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,+1,-1,-1,+2,-2,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,-1,+2,-2,+3,-3,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,+1,-1,-1,+2,+2,-2,-2,+3,+3,-3,-3,+4,-4, + +0,+0,+1,+1,-1,-1,+2,+2,-2,-2,+3,-3,+4,-4,+5,-5, + +0,+0,+1,+1,-1,-1,+2,-2,+3,-3,+4,-4,+5,-5,+6,-6, + +0,+0,+1,-1,+2,-2,+3,-3,+4,-4,+5,-5,+6,-6,+7,-7, }; -static const float *stChannel_Decode3_listFloat=(float *)stChannel_Decode3_listInt[1]; -void stChannel_Decode3(stChannel *ch,unsigned int a,unsigned int b,unsigned int c,unsigned int d){ - if(ch->type!=2&&b){ - unsigned int i, j, k, l; - const char *value=ch->value; - const char *value3=ch->value3; - float *block=ch->block; - for(i=0,k=c,l=c-1;icoded_scalefactor_count; + + + for (i = 0; i < csf_count; i++) { + float qc; + unsigned char resolution = ch->resolution[i]; + unsigned char bits = decode2_quantized_spectrum_max_bits[resolution]; + unsigned int code = bitreader_read(br, bits); + + /* read spectral coefficients */ + if (resolution < 8) { + /* use prefix codebooks for lower resolutions */ + code += resolution << 4; + bitreader_skip(br, decode2_quantized_spectrum_bits[code] - bits); + qc = decode2_quantized_spectrum_value[code]; + } + else { + /* parse values in sign-magnitude form (lowest bit = sign) */ + int signed_code = (1 - ((code & 1) << 1)) * (code >> 1); /* move sign */ + if (signed_code == 0) + bitreader_skip(br, -1); /* zero uses one less bit since it has no sign */ + qc = (float)signed_code; + } + + /* dequantize coef with gain */ + ch->spectra[i] = ch->gain[i] * qc; + } + + /* clean rest of spectra */ + memset(&ch->spectra[csf_count], 0, sizeof(ch->spectra[0]) * (HCA_SAMPLES_PER_SUBFRAME - csf_count)); } //-------------------------------------------------- -// デコード第四段階 -// ブロックデータ修正その2 +// Decode 3rd step //-------------------------------------------------- -static const unsigned int stChannel_Decode4_listInt[]={ - // v2.0 - 0x40000000,0x3FEDB6DB,0x3FDB6DB7,0x3FC92492,0x3FB6DB6E,0x3FA49249,0x3F924925,0x3F800000, - 0x3F5B6DB7,0x3F36DB6E,0x3F124925,0x3EDB6DB7,0x3E924925,0x3E124925,0x00000000,0x00000000, - 0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5,0x33E0CCDF, - 0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF,0x358B95C2, - 0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF,0x372D583F, - 0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF,0x38D744FD, - 0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8,0x3A85AAC3, - 0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A,0x3C25FED7, - //↓この2行要らない? - 0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B,0x3DCE248C, - 0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B,0x00000000, - // v1.3 - //0x40000000,0x3FEDB6DB,0x3FDB6DB7,0x3FC92492,0x3FB6DB6E,0x3FA49249,0x3F924925,0x3F800000, - //0x3F5B6DB7,0x3F36DB6E,0x3F124925,0x3EDB6DB7,0x3E924925,0x3E124925,0x00000000,0x00000000, -}; +static const unsigned int decode3_scale_conversion_table_int[128] = { + 0x00000000,0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5, + 0x33E0CCDF,0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF, + 0x358B95C2,0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF, + 0x372D583F,0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF, + 0x38D744FD,0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8, + 0x3A85AAC3,0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A, + 0x3C25FED7,0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B, + 0x3DCE248C,0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B, -void stChannel_Decode4(stChannel *ch,int index,unsigned int a,unsigned int b,unsigned int c){ - if(ch->type==1&&c){ - float f1=((float *)stChannel_Decode4_listInt)[ch[1].value2[index]]; - float f2=f1-2.0f; - float *s=&ch->block[b]; - float *d=&ch[1].block[b]; - unsigned int i; - for(i=0;itype == STEREO_SECONDARY) + return; + if (bands_per_hfr_group == 0) /* not in v1.x */ + return; + + { + unsigned int group, i; + unsigned int start_band = stereo_band_count + base_band_count; + unsigned int highband = start_band; + unsigned int lowband = start_band - 1; + + for (group = 0; group < hfr_group_count; group++) { + for (i = 0; i < bands_per_hfr_group && highband < total_band_count; i++) { + unsigned int sc_index = ch->hfr_scales[group] - ch->scalefactors[lowband] + 64; + ch->spectra[highband] = decode3_scale_conversion_table[sc_index] * ch->spectra[lowband]; + highband++; + lowband--; + } + } + + ch->spectra[HCA_SAMPLES_PER_SUBFRAME - 1] = 0; /* last spectral coefficient should be 0 */ + } } //-------------------------------------------------- -// デコード第五段階 -// 波形データを生成 +// Decode 4th step //-------------------------------------------------- -static const unsigned int stChannel_Decode5_list1Int[7][0x40]={ - { - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - },{ - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - },{ - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - },{ - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - },{ - 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, - 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, - 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, - 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, - 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, - 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, - 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, - 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, - },{ - 0x3F7FFB11,0x3F7FD397,0x3F7F84AB,0x3F7F0E58,0x3F7E70B0,0x3F7DABCC,0x3F7CBFC9,0x3F7BACCD, - 0x3F7A7302,0x3F791298,0x3F778BC5,0x3F75DEC6,0x3F740BDD,0x3F721352,0x3F6FF573,0x3F6DB293, - 0x3F6B4B0C,0x3F68BF3C,0x3F660F88,0x3F633C5A,0x3F604621,0x3F5D2D53,0x3F59F26A,0x3F5695E5, - 0x3F531849,0x3F4F7A1F,0x3F4BBBF8,0x3F47DE65,0x3F43E200,0x3F3FC767,0x3F3B8F3B,0x3F373A23, - 0x3F7FFB11,0x3F7FD397,0x3F7F84AB,0x3F7F0E58,0x3F7E70B0,0x3F7DABCC,0x3F7CBFC9,0x3F7BACCD, - 0x3F7A7302,0x3F791298,0x3F778BC5,0x3F75DEC6,0x3F740BDD,0x3F721352,0x3F6FF573,0x3F6DB293, - 0x3F6B4B0C,0x3F68BF3C,0x3F660F88,0x3F633C5A,0x3F604621,0x3F5D2D53,0x3F59F26A,0x3F5695E5, - 0x3F531849,0x3F4F7A1F,0x3F4BBBF8,0x3F47DE65,0x3F43E200,0x3F3FC767,0x3F3B8F3B,0x3F373A23, - },{ - 0x3F7FFEC4,0x3F7FF4E6,0x3F7FE129,0x3F7FC38F,0x3F7F9C18,0x3F7F6AC7,0x3F7F2F9D,0x3F7EEA9D, - 0x3F7E9BC9,0x3F7E4323,0x3F7DE0B1,0x3F7D7474,0x3F7CFE73,0x3F7C7EB0,0x3F7BF531,0x3F7B61FC, - 0x3F7AC516,0x3F7A1E84,0x3F796E4E,0x3F78B47B,0x3F77F110,0x3F772417,0x3F764D97,0x3F756D97, - 0x3F748422,0x3F73913F,0x3F7294F8,0x3F718F57,0x3F708066,0x3F6F6830,0x3F6E46BE,0x3F6D1C1D, - 0x3F6BE858,0x3F6AAB7B,0x3F696591,0x3F6816A8,0x3F66BECC,0x3F655E0B,0x3F63F473,0x3F628210, - 0x3F6106F2,0x3F5F8327,0x3F5DF6BE,0x3F5C61C7,0x3F5AC450,0x3F591E6A,0x3F577026,0x3F55B993, - 0x3F53FAC3,0x3F5233C6,0x3F5064AF,0x3F4E8D90,0x3F4CAE79,0x3F4AC77F,0x3F48D8B3,0x3F46E22A, - 0x3F44E3F5,0x3F42DE29,0x3F40D0DA,0x3F3EBC1B,0x3F3CA003,0x3F3A7CA4,0x3F385216,0x3F36206C, - } +static const unsigned int decode4_intensity_ratio_table_int[80] = { + 0x40000000,0x3FEDB6DB,0x3FDB6DB7,0x3FC92492,0x3FB6DB6E,0x3FA49249,0x3F924925,0x3F800000, + 0x3F5B6DB7,0x3F36DB6E,0x3F124925,0x3EDB6DB7,0x3E924925,0x3E124925,0x00000000,0x00000000, + /* v2.0 seems to define indexes over 15, but intensity is packed in 4b thus unused */ + 0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5,0x33E0CCDF, + 0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF,0x358B95C2, + 0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF,0x372D583F, + 0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF,0x38D744FD, + 0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8,0x3A85AAC3, + 0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A,0x3C25FED7, + 0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B,0x3DCE248C, + 0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B,0x00000000, }; -static const unsigned int stChannel_Decode5_list2Int[7][0x40]={ - { - 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, - 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, - 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, - 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, - 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, - 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, - 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, - 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, - },{ - 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, - 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, - 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, - 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, - 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, - 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, - 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, - 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, - },{ - 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, - 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, - 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, - 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, - 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, - 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, - 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, - 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, - },{ - 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, - 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, - 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, - 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, - 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, - 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, - 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, - 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, - },{ - 0xBCC90AB0,0xBD96A905,0xBDFAB273,0xBE2F10A2,0xBE605C13,0xBE888E93,0xBEA09AE5,0xBEB8442A, - 0xBECF7BCA,0xBEE63375,0xBEFC5D27,0xBF08F59B,0xBF13682A,0xBF1D7FD1,0xBF273656,0xBF3085BB, - 0x3CC90AB0,0x3D96A905,0x3DFAB273,0x3E2F10A2,0x3E605C13,0x3E888E93,0x3EA09AE5,0x3EB8442A, - 0x3ECF7BCA,0x3EE63375,0x3EFC5D27,0x3F08F59B,0x3F13682A,0x3F1D7FD1,0x3F273656,0x3F3085BB, - 0x3CC90AB0,0x3D96A905,0x3DFAB273,0x3E2F10A2,0x3E605C13,0x3E888E93,0x3EA09AE5,0x3EB8442A, - 0x3ECF7BCA,0x3EE63375,0x3EFC5D27,0x3F08F59B,0x3F13682A,0x3F1D7FD1,0x3F273656,0x3F3085BB, - 0xBCC90AB0,0xBD96A905,0xBDFAB273,0xBE2F10A2,0xBE605C13,0xBE888E93,0xBEA09AE5,0xBEB8442A, - 0xBECF7BCA,0xBEE63375,0xBEFC5D27,0xBF08F59B,0xBF13682A,0xBF1D7FD1,0xBF273656,0xBF3085BB, - },{ - 0xBC490E90,0xBD16C32C,0xBD7B2B74,0xBDAFB680,0xBDE1BC2E,0xBE09CF86,0xBE22ABB6,0xBE3B6ECF, - 0xBE541501,0xBE6C9A7F,0xBE827DC0,0xBE8E9A22,0xBE9AA086,0xBEA68F12,0xBEB263EF,0xBEBE1D4A, - 0xBEC9B953,0xBED53641,0xBEE0924F,0xBEEBCBBB,0xBEF6E0CB,0xBF00E7E4,0xBF064B82,0xBF0B9A6B, - 0xBF10D3CD,0xBF15F6D9,0xBF1B02C6,0xBF1FF6CB,0xBF24D225,0xBF299415,0xBF2E3BDE,0xBF32C8C9, - 0x3C490E90,0x3D16C32C,0x3D7B2B74,0x3DAFB680,0x3DE1BC2E,0x3E09CF86,0x3E22ABB6,0x3E3B6ECF, - 0x3E541501,0x3E6C9A7F,0x3E827DC0,0x3E8E9A22,0x3E9AA086,0x3EA68F12,0x3EB263EF,0x3EBE1D4A, - 0x3EC9B953,0x3ED53641,0x3EE0924F,0x3EEBCBBB,0x3EF6E0CB,0x3F00E7E4,0x3F064B82,0x3F0B9A6B, - 0x3F10D3CD,0x3F15F6D9,0x3F1B02C6,0x3F1FF6CB,0x3F24D225,0x3F299415,0x3F2E3BDE,0x3F32C8C9, - },{ - 0xBBC90F88,0xBC96C9B6,0xBCFB49BA,0xBD2FE007,0xBD621469,0xBD8A200A,0xBDA3308C,0xBDBC3AC3, - 0xBDD53DB9,0xBDEE3876,0xBE039502,0xBE1008B7,0xBE1C76DE,0xBE28DEFC,0xBE354098,0xBE419B37, - 0xBE4DEE60,0xBE5A3997,0xBE667C66,0xBE72B651,0xBE7EE6E1,0xBE8586CE,0xBE8B9507,0xBE919DDD, - 0xBE97A117,0xBE9D9E78,0xBEA395C5,0xBEA986C4,0xBEAF713A,0xBEB554EC,0xBEBB31A0,0xBEC1071E, - 0xBEC6D529,0xBECC9B8B,0xBED25A09,0xBED8106B,0xBEDDBE79,0xBEE363FA,0xBEE900B7,0xBEEE9479, - 0xBEF41F07,0xBEF9A02D,0xBEFF17B2,0xBF0242B1,0xBF04F484,0xBF07A136,0xBF0A48AD,0xBF0CEAD0, - 0xBF0F8784,0xBF121EB0,0xBF14B039,0xBF173C07,0xBF19C200,0xBF1C420C,0xBF1EBC12,0xBF212FF9, - 0xBF239DA9,0xBF26050A,0xBF286605,0xBF2AC082,0xBF2D1469,0xBF2F61A5,0xBF31A81D,0xBF33E7BC, - } -}; -static const unsigned int stChannel_Decode5_list3Int[2][0x40]={ - { - 0x3A3504F0,0x3B0183B8,0x3B70C538,0x3BBB9268,0x3C04A809,0x3C308200,0x3C61284C,0x3C8B3F17, - 0x3CA83992,0x3CC77FBD,0x3CE91110,0x3D0677CD,0x3D198FC4,0x3D2DD35C,0x3D434643,0x3D59ECC1, - 0x3D71CBA8,0x3D85741E,0x3D92A413,0x3DA078B4,0x3DAEF522,0x3DBE1C9E,0x3DCDF27B,0x3DDE7A1D, - 0x3DEFB6ED,0x3E00D62B,0x3E0A2EDA,0x3E13E72A,0x3E1E00B1,0x3E287CF2,0x3E335D55,0x3E3EA321, - 0x3E4A4F75,0x3E56633F,0x3E62DF37,0x3E6FC3D1,0x3E7D1138,0x3E8563A2,0x3E8C72B7,0x3E93B561, - 0x3E9B2AEF,0x3EA2D26F,0x3EAAAAAB,0x3EB2B222,0x3EBAE706,0x3EC34737,0x3ECBD03D,0x3ED47F46, - 0x3EDD5128,0x3EE6425C,0x3EEF4EFF,0x3EF872D7,0x3F00D4A9,0x3F0576CA,0x3F0A1D3B,0x3F0EC548, - 0x3F136C25,0x3F180EF2,0x3F1CAAC2,0x3F213CA2,0x3F25C1A5,0x3F2A36E7,0x3F2E9998,0x3F32E705, - },{ - 0xBF371C9E,0xBF3B37FE,0xBF3F36F2,0xBF431780,0xBF46D7E6,0xBF4A76A4,0xBF4DF27C,0xBF514A6F, - 0xBF547DC5,0xBF578C03,0xBF5A74EE,0xBF5D3887,0xBF5FD707,0xBF6250DA,0xBF64A699,0xBF66D908, - 0xBF68E90E,0xBF6AD7B1,0xBF6CA611,0xBF6E5562,0xBF6FE6E7,0xBF715BEF,0xBF72B5D1,0xBF73F5E6, - 0xBF751D89,0xBF762E13,0xBF7728D7,0xBF780F20,0xBF78E234,0xBF79A34C,0xBF7A5397,0xBF7AF439, - 0xBF7B8648,0xBF7C0ACE,0xBF7C82C8,0xBF7CEF26,0xBF7D50CB,0xBF7DA88E,0xBF7DF737,0xBF7E3D86, - 0xBF7E7C2A,0xBF7EB3CC,0xBF7EE507,0xBF7F106C,0xBF7F3683,0xBF7F57CA,0xBF7F74B6,0xBF7F8DB6, - 0xBF7FA32E,0xBF7FB57B,0xBF7FC4F6,0xBF7FD1ED,0xBF7FDCAD,0xBF7FE579,0xBF7FEC90,0xBF7FF22E, - 0xBF7FF688,0xBF7FF9D0,0xBF7FFC32,0xBF7FFDDA,0xBF7FFEED,0xBF7FFF8F,0xBF7FFFDF,0xBF7FFFFC, - } +static const float *decode4_intensity_ratio_table = (const float *)decode4_intensity_ratio_table_int; + +static void decode4_apply_intensity_stereo(stChannel *ch_pair, int subframe, + unsigned int total_band_count, unsigned int base_band_count, unsigned int stereo_band_count) { + if (ch_pair[0].type != STEREO_PRIMARY) + return; + if (stereo_band_count == 0) + return; + + { + float ratio_l = decode4_intensity_ratio_table[ ch_pair[1].intensity[subframe] ]; + float ratio_r = ratio_l - 2.0f; + float *sp_l = ch_pair[0].spectra; + float *sp_r = ch_pair[1].spectra; + unsigned int band; + + for (band = base_band_count; band < total_band_count; band++) { + sp_r[band] = sp_l[band] * ratio_r; + sp_l[band] = sp_l[band] * ratio_l; + } + } +} + +//-------------------------------------------------- +// Decode 5th step +//-------------------------------------------------- +static const unsigned int decode5_sin_tables_int[7][64] = { + { + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + },{ + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + },{ + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + },{ + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + },{ + 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, + 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, + 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, + 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, + 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, + 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, + 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, + 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, + },{ + 0x3F7FFB11,0x3F7FD397,0x3F7F84AB,0x3F7F0E58,0x3F7E70B0,0x3F7DABCC,0x3F7CBFC9,0x3F7BACCD, + 0x3F7A7302,0x3F791298,0x3F778BC5,0x3F75DEC6,0x3F740BDD,0x3F721352,0x3F6FF573,0x3F6DB293, + 0x3F6B4B0C,0x3F68BF3C,0x3F660F88,0x3F633C5A,0x3F604621,0x3F5D2D53,0x3F59F26A,0x3F5695E5, + 0x3F531849,0x3F4F7A1F,0x3F4BBBF8,0x3F47DE65,0x3F43E200,0x3F3FC767,0x3F3B8F3B,0x3F373A23, + 0x3F7FFB11,0x3F7FD397,0x3F7F84AB,0x3F7F0E58,0x3F7E70B0,0x3F7DABCC,0x3F7CBFC9,0x3F7BACCD, + 0x3F7A7302,0x3F791298,0x3F778BC5,0x3F75DEC6,0x3F740BDD,0x3F721352,0x3F6FF573,0x3F6DB293, + 0x3F6B4B0C,0x3F68BF3C,0x3F660F88,0x3F633C5A,0x3F604621,0x3F5D2D53,0x3F59F26A,0x3F5695E5, + 0x3F531849,0x3F4F7A1F,0x3F4BBBF8,0x3F47DE65,0x3F43E200,0x3F3FC767,0x3F3B8F3B,0x3F373A23, + },{ + 0x3F7FFEC4,0x3F7FF4E6,0x3F7FE129,0x3F7FC38F,0x3F7F9C18,0x3F7F6AC7,0x3F7F2F9D,0x3F7EEA9D, + 0x3F7E9BC9,0x3F7E4323,0x3F7DE0B1,0x3F7D7474,0x3F7CFE73,0x3F7C7EB0,0x3F7BF531,0x3F7B61FC, + 0x3F7AC516,0x3F7A1E84,0x3F796E4E,0x3F78B47B,0x3F77F110,0x3F772417,0x3F764D97,0x3F756D97, + 0x3F748422,0x3F73913F,0x3F7294F8,0x3F718F57,0x3F708066,0x3F6F6830,0x3F6E46BE,0x3F6D1C1D, + 0x3F6BE858,0x3F6AAB7B,0x3F696591,0x3F6816A8,0x3F66BECC,0x3F655E0B,0x3F63F473,0x3F628210, + 0x3F6106F2,0x3F5F8327,0x3F5DF6BE,0x3F5C61C7,0x3F5AC450,0x3F591E6A,0x3F577026,0x3F55B993, + 0x3F53FAC3,0x3F5233C6,0x3F5064AF,0x3F4E8D90,0x3F4CAE79,0x3F4AC77F,0x3F48D8B3,0x3F46E22A, + 0x3F44E3F5,0x3F42DE29,0x3F40D0DA,0x3F3EBC1B,0x3F3CA003,0x3F3A7CA4,0x3F385216,0x3F36206C, + } }; -void stChannel_Decode5(stChannel *ch,int index){ - const float *s, *s1, *s2; - float *d; - unsigned int i, count1, count2, j, k; - s=ch->block;d=ch->wav1; - for(i=0,count1=1,count2=0x40;i<7;i++,count1<<=1,count2>>=1){ - float *d1=d; - float *d2=&d[count2]; - float *w; - for(j=0;jwav1;d=ch->block; - for(i=0,count1=0x40,count2=1;i<7;i++,count1>>=1,count2<<=1){ - const float *list1Float=(const float *)stChannel_Decode5_list1Int[i]; - const float *list2Float=(const float *)stChannel_Decode5_list2Int[i]; - float *d1, *d2, *w; - s1=s; - s2=&s1[count2]; - d1=d; - d2=&d1[count2*2-1]; - for(j=0;jwav2; - for(i=0;i<0x80;i++)*(d++)=*(s++); - s=(const float *)stChannel_Decode5_list3Int;d=ch->wave[index]; - s1=&ch->wav2[0x40];s2=ch->wav3; - for(i=0;i<0x40;i++)*(d++)=*(s1++)**(s++)+*(s2++); - for(i=0;i<0x40;i++)*(d++)=*(s++)**(--s1)-*(s2++); - s1=&ch->wav2[0x40-1];d=ch->wav3; - for(i=0;i<0x40;i++)*(d++)=*(s1--)**(--s); - for(i=0;i<0x40;i++)*(d++)=*(--s)**(++s1); +static const unsigned int decode5_cos_tables_int[7][64]={ + { + 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, + 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, + 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, + 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, + 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, + 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, + 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, + 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, + },{ + 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, + 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, + 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, + 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, + 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, + 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, + 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, + 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, + },{ + 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, + 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, + 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, + 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, + 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, + 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, + 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, + 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, + },{ + 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, + 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, + 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, + 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, + 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, + 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, + 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, + 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, + },{ + 0xBCC90AB0,0xBD96A905,0xBDFAB273,0xBE2F10A2,0xBE605C13,0xBE888E93,0xBEA09AE5,0xBEB8442A, + 0xBECF7BCA,0xBEE63375,0xBEFC5D27,0xBF08F59B,0xBF13682A,0xBF1D7FD1,0xBF273656,0xBF3085BB, + 0x3CC90AB0,0x3D96A905,0x3DFAB273,0x3E2F10A2,0x3E605C13,0x3E888E93,0x3EA09AE5,0x3EB8442A, + 0x3ECF7BCA,0x3EE63375,0x3EFC5D27,0x3F08F59B,0x3F13682A,0x3F1D7FD1,0x3F273656,0x3F3085BB, + 0x3CC90AB0,0x3D96A905,0x3DFAB273,0x3E2F10A2,0x3E605C13,0x3E888E93,0x3EA09AE5,0x3EB8442A, + 0x3ECF7BCA,0x3EE63375,0x3EFC5D27,0x3F08F59B,0x3F13682A,0x3F1D7FD1,0x3F273656,0x3F3085BB, + 0xBCC90AB0,0xBD96A905,0xBDFAB273,0xBE2F10A2,0xBE605C13,0xBE888E93,0xBEA09AE5,0xBEB8442A, + 0xBECF7BCA,0xBEE63375,0xBEFC5D27,0xBF08F59B,0xBF13682A,0xBF1D7FD1,0xBF273656,0xBF3085BB, + },{ + 0xBC490E90,0xBD16C32C,0xBD7B2B74,0xBDAFB680,0xBDE1BC2E,0xBE09CF86,0xBE22ABB6,0xBE3B6ECF, + 0xBE541501,0xBE6C9A7F,0xBE827DC0,0xBE8E9A22,0xBE9AA086,0xBEA68F12,0xBEB263EF,0xBEBE1D4A, + 0xBEC9B953,0xBED53641,0xBEE0924F,0xBEEBCBBB,0xBEF6E0CB,0xBF00E7E4,0xBF064B82,0xBF0B9A6B, + 0xBF10D3CD,0xBF15F6D9,0xBF1B02C6,0xBF1FF6CB,0xBF24D225,0xBF299415,0xBF2E3BDE,0xBF32C8C9, + 0x3C490E90,0x3D16C32C,0x3D7B2B74,0x3DAFB680,0x3DE1BC2E,0x3E09CF86,0x3E22ABB6,0x3E3B6ECF, + 0x3E541501,0x3E6C9A7F,0x3E827DC0,0x3E8E9A22,0x3E9AA086,0x3EA68F12,0x3EB263EF,0x3EBE1D4A, + 0x3EC9B953,0x3ED53641,0x3EE0924F,0x3EEBCBBB,0x3EF6E0CB,0x3F00E7E4,0x3F064B82,0x3F0B9A6B, + 0x3F10D3CD,0x3F15F6D9,0x3F1B02C6,0x3F1FF6CB,0x3F24D225,0x3F299415,0x3F2E3BDE,0x3F32C8C9, + },{ + 0xBBC90F88,0xBC96C9B6,0xBCFB49BA,0xBD2FE007,0xBD621469,0xBD8A200A,0xBDA3308C,0xBDBC3AC3, + 0xBDD53DB9,0xBDEE3876,0xBE039502,0xBE1008B7,0xBE1C76DE,0xBE28DEFC,0xBE354098,0xBE419B37, + 0xBE4DEE60,0xBE5A3997,0xBE667C66,0xBE72B651,0xBE7EE6E1,0xBE8586CE,0xBE8B9507,0xBE919DDD, + 0xBE97A117,0xBE9D9E78,0xBEA395C5,0xBEA986C4,0xBEAF713A,0xBEB554EC,0xBEBB31A0,0xBEC1071E, + 0xBEC6D529,0xBECC9B8B,0xBED25A09,0xBED8106B,0xBEDDBE79,0xBEE363FA,0xBEE900B7,0xBEEE9479, + 0xBEF41F07,0xBEF9A02D,0xBEFF17B2,0xBF0242B1,0xBF04F484,0xBF07A136,0xBF0A48AD,0xBF0CEAD0, + 0xBF0F8784,0xBF121EB0,0xBF14B039,0xBF173C07,0xBF19C200,0xBF1C420C,0xBF1EBC12,0xBF212FF9, + 0xBF239DA9,0xBF26050A,0xBF286605,0xBF2AC082,0xBF2D1469,0xBF2F61A5,0xBF31A81D,0xBF33E7BC, + } +}; + +/* HCA window function, close to a KBD window with an alpha of around 3.82 (similar to AAC/Vorbis) */ +static const unsigned int decode5_imdct_window_int[128] = { + 0x3A3504F0,0x3B0183B8,0x3B70C538,0x3BBB9268,0x3C04A809,0x3C308200,0x3C61284C,0x3C8B3F17, + 0x3CA83992,0x3CC77FBD,0x3CE91110,0x3D0677CD,0x3D198FC4,0x3D2DD35C,0x3D434643,0x3D59ECC1, + 0x3D71CBA8,0x3D85741E,0x3D92A413,0x3DA078B4,0x3DAEF522,0x3DBE1C9E,0x3DCDF27B,0x3DDE7A1D, + 0x3DEFB6ED,0x3E00D62B,0x3E0A2EDA,0x3E13E72A,0x3E1E00B1,0x3E287CF2,0x3E335D55,0x3E3EA321, + 0x3E4A4F75,0x3E56633F,0x3E62DF37,0x3E6FC3D1,0x3E7D1138,0x3E8563A2,0x3E8C72B7,0x3E93B561, + 0x3E9B2AEF,0x3EA2D26F,0x3EAAAAAB,0x3EB2B222,0x3EBAE706,0x3EC34737,0x3ECBD03D,0x3ED47F46, + 0x3EDD5128,0x3EE6425C,0x3EEF4EFF,0x3EF872D7,0x3F00D4A9,0x3F0576CA,0x3F0A1D3B,0x3F0EC548, + 0x3F136C25,0x3F180EF2,0x3F1CAAC2,0x3F213CA2,0x3F25C1A5,0x3F2A36E7,0x3F2E9998,0x3F32E705, + + 0xBF371C9E,0xBF3B37FE,0xBF3F36F2,0xBF431780,0xBF46D7E6,0xBF4A76A4,0xBF4DF27C,0xBF514A6F, + 0xBF547DC5,0xBF578C03,0xBF5A74EE,0xBF5D3887,0xBF5FD707,0xBF6250DA,0xBF64A699,0xBF66D908, + 0xBF68E90E,0xBF6AD7B1,0xBF6CA611,0xBF6E5562,0xBF6FE6E7,0xBF715BEF,0xBF72B5D1,0xBF73F5E6, + 0xBF751D89,0xBF762E13,0xBF7728D7,0xBF780F20,0xBF78E234,0xBF79A34C,0xBF7A5397,0xBF7AF439, + 0xBF7B8648,0xBF7C0ACE,0xBF7C82C8,0xBF7CEF26,0xBF7D50CB,0xBF7DA88E,0xBF7DF737,0xBF7E3D86, + 0xBF7E7C2A,0xBF7EB3CC,0xBF7EE507,0xBF7F106C,0xBF7F3683,0xBF7F57CA,0xBF7F74B6,0xBF7F8DB6, + 0xBF7FA32E,0xBF7FB57B,0xBF7FC4F6,0xBF7FD1ED,0xBF7FDCAD,0xBF7FE579,0xBF7FEC90,0xBF7FF22E, + 0xBF7FF688,0xBF7FF9D0,0xBF7FFC32,0xBF7FFDDA,0xBF7FFEED,0xBF7FFF8F,0xBF7FFFDF,0xBF7FFFFC, +}; +static const float *decode5_imdct_window = (const float *)decode5_imdct_window_int; + +static void decoder5_run_imdct(stChannel *ch, int subframe) { + const static unsigned int size = HCA_SAMPLES_PER_SUBFRAME; + const static unsigned int half = HCA_SAMPLES_PER_SUBFRAME / 2; + const static unsigned int mdct_bits = HCA_MDCT_BITS; + + + /* apply DCT-IV to dequantized spectra */ + { + unsigned int i, j, k; + unsigned int count1a, count2a, count1b, count2b; + const float *temp1a, *temp1b; + float *temp2a, *temp2b; + + /* this is all too crafty for me to simplify, see VGAudio (Mdct.Dct4) */ + + temp1a = ch->spectra; + temp2a = ch->temp; + count1a = 1; + count2a = half; + for (i = 0; i < mdct_bits; i++) { + float *swap; + float *d1 = &temp2a[0]; + float *d2 = &temp2a[count2a]; + + for (j = 0; j < count1a; j++) { + for (k = 0; k < count2a; k++) { + float a = *(temp1a++); + float b = *(temp1a++); + *(d1++) = b + a; + *(d2++) = a - b; + } + d1 += count2a; + d2 += count2a; + } + swap = (float*) temp1a - HCA_SAMPLES_PER_SUBFRAME; /* move spectra/temp to beginning */ + temp1a = temp2a; + temp2a = swap; + + count1a = count1a << 1; + count2a = count2a >> 1; + } + + temp1b = ch->temp; + temp2b = ch->spectra; + count1b = half; + count2b = 1; + for (i = 0; i < mdct_bits; i++) { + const float *sin_table = (const float *) decode5_sin_tables_int[i]; + const float *cos_table = (const float *) decode5_cos_tables_int[i]; + float *swap; + float *d1 = temp2b; + float *d2 = &temp2b[count2b * 2 - 1]; + const float *s1 = &temp1b[0]; + const float *s2 = &temp1b[count2b]; + + for (j = 0; j < count1b; j++) { + for (k = 0; k < count2b; k++) { + float a = *(s1++); + float b = *(s2++); + float sin = *(sin_table++); + float cos = *(cos_table++); + *(d1++) = a * sin - b * cos; + *(d2--) = a * cos + b * sin; + } + s1 += count2b; + s2 += count2b; + d1 += count2b; + d2 += count2b * 3; + } + swap = (float*) temp1b; + temp1b = temp2b; + temp2b = swap; + + count1b = count1b >> 1; + count2b = count2b << 1; + } + + /* copy dct */ + /* (with the above optimization spectra is already modified, so this is redundant) */ + for (i = 0; i < size; i++) { + ch->dct[i] = ch->spectra[i]; + } + } + + /* update output/imdct */ + { + unsigned int i; + + for (i = 0; i < half; i++) { + ch->wave[subframe][i] = decode5_imdct_window[i] * ch->dct[i + half] + ch->imdct_previous[i]; + ch->wave[subframe][i + half] = decode5_imdct_window[i + half] * ch->dct[size - 1 - i] - ch->imdct_previous[i + half]; + ch->imdct_previous[i] = decode5_imdct_window[size - 1 - i] * ch->dct[half - i - 1]; + ch->imdct_previous[i + half] = decode5_imdct_window[half - i - 1] * ch->dct[i]; + } +#if 0 + /* over-optimized IMDCT (for reference), barely noticeable even when decoding hundred of files */ + const float *imdct_window = decode5_imdct_window; + const float *dct; + float *imdct_previous; + float *wave = ch->wave[subframe]; + + dct = &ch->dct[half]; + imdct_previous = ch->imdct_previous; + for (i = 0; i < half; i++) { + *(wave++) = *(dct++) * *(imdct_window++) + *(imdct_previous++); + } + for (i = 0; i < half; i++) { + *(wave++) = *(imdct_window++) * *(--dct) - *(imdct_previous++); + } + /* implicit: imdct_window pointer is now at end */ + dct = &ch->dct[half - 1]; + imdct_previous = ch->imdct_previous; + for (i = 0; i < half; i++) { + *(imdct_previous++) = *(--imdct_window) * *(dct--); + } + for (i = 0; i < half; i++) { + *(imdct_previous++) = *(--imdct_window) * *(++dct) ; + } +#endif + } } diff --git a/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.h b/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.h index 8961f86dd..071e3a2b4 100644 --- a/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.h +++ b/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.h @@ -5,61 +5,82 @@ extern "C" { #endif -enum { clHCA_samplesPerBlock = 0x80 * 8 }; -/* Must pass at least 8 bytes of data to this function. Returns -1 on non-match, or - * positive byte count on success. */ -int clHCA_isOurFile0(const void *data); - -/* Must pass a full header block for success. Returns 0 on success, -1 on failure. */ -int clHCA_isOurFile1(const void *data, unsigned int size); +/* Must pass at least 8 bytes of data to this function. + * Returns <0 on non-match, or header size on success. */ +int clHCA_isOurFile(const void *data, unsigned int size); /* The opaque state structure. */ typedef struct clHCA clHCA; -/* In case you wish to allocate the structure on your own. */ +/* In case you wish to allocate and reset the structure on your own. */ int clHCA_sizeof(); -void clHCA_clear(clHCA *, unsigned int ciphKey1, unsigned int ciphKey2); +void clHCA_clear(clHCA *); void clHCA_done(clHCA *); /* Or you could let the library allocate it. */ -clHCA * clHCA_new(unsigned int ciphKey1, unsigned int ciphKey2); +clHCA * clHCA_new(); void clHCA_delete(clHCA *); -/* Requires a pre-allocated data structure. - * Before any decoding may be performed, the header block must be passed in. - * The recommended way of doing this is to detect the header length with - * clHCA_isOurFile0, validate the header with clHCA_isOurFile1, then pass - * it to this function, with the address of 0. - * Subsequent decodes with non-zero address are assumed to be sample blocks, - * and should be of the blockSize returned by the clHCA_getInfo function. - * Returns 0 on success, -1 on failure. */ -int clHCA_Decode(clHCA *, void *data, unsigned int size, unsigned int address); - -/* This is the simplest decode function, for signed and clipped 16 bit samples. - * May be called after clHCA_Decode, and will return the same data until the next - * block of sample data is passed to clHCA_Decode. */ -void clHCA_DecodeSamples16(clHCA *, signed short * outSamples); +/* Parses the HCA header. Must be called before any decoding may be performed, + * and size must be at least headerSize long. The recommended way is to detect + * the header length with clHCA_isOurFile, then read data and call this. + * May be called multiple times to reset decoder state. + * Returns 0 on success, <0 on failure. */ +int clHCA_DecodeHeader(clHCA *, void *data, unsigned int size); typedef struct clHCA_stInfo { unsigned int version; - unsigned int dataOffset; + unsigned int headerSize; unsigned int samplingRate; unsigned int channelCount; unsigned int blockSize; unsigned int blockCount; + unsigned int encoderDelay; /* samples appended to the beginning */ + unsigned int encoderPadding; /* samples appended to the end */ unsigned int loopEnabled; - unsigned int loopStart; - unsigned int loopEnd; + unsigned int loopStartBlock; + unsigned int loopEndBlock; + unsigned int loopStartDelay; /* samples in block before loop starts */ + unsigned int loopEndPadding; /* samples in block after loop ends */ + unsigned int samplesPerBlock; /* should be 1024 */ const char *comment; + unsigned int encryptionEnabled; /* requires keycode */ + + /* Derived sample formulas: + * - sample count: blockCount*samplesPerBlock - encoderDelay - encoderPadding; + * - loop start sample = loopStartBlock*samplesPerBlock - encoderDelay + loopStartDelay + * - loop end sample = loopEndBlock*samplesPerBlock - encoderDelay + (samplesPerBlock - info.loopEndPadding) + */ } clHCA_stInfo; -/* Retrieve information relevant for decoding and playback with this function. - * Must be called after successfully decoding a header block with clHCA_Decode, - * or else it will fail. - * Returns 0 on success, -1 on failure. */ +/* Retrieves header information for decoding and playback (it's the caller's responsability + * to apply looping, encoder delay/skip samples, etc). May be called after clHCA_DecodeHeader. + * Returns 0 on success, <0 on failure. */ int clHCA_getInfo(clHCA *, clHCA_stInfo *out); +/* Decodes a single frame, from data after headerSize. Should be called after + * clHCA_DecodeHeader and size must be at least blockSize long. + * Returns 0 on success, <0 on failure. */ +int clHCA_DecodeBlock(clHCA *, void *data, unsigned int size); + +/* Extracts signed and clipped 16 bit samples into sample buffer. + * May be called after clHCA_DecodeBlock, and will return the same data until + * next decode. Buffer must be at least (samplesPerBlock*channels) long. */ +void clHCA_ReadSamples16(clHCA *, signed short * outSamples); + +/* Sets a 64 bit encryption key, to properly decode blocks. This may be called + * multiple times to change the key, before or after clHCA_DecodeHeader. + * Key is ignored if the file is not encrypted. */ +void clHCA_SetKey(clHCA *, unsigned long long keycode); + +/* Tests a single frame for validity, mainly to test if current key is correct. + * Returns <0 on incorrect block (wrong key), 0 on silent block (not useful to determine) + * and >0 if block is correct (the closer to 1 the more likely). + * Incorrect keys may give a few valid frames, so it's best to test a number of them + * and select the key with scores closer to 1. */ +int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size); + #ifdef __cplusplus } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/SASSC_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/SASSC_decoder.c index f3912286e..59917bfd8 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/SASSC_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/SASSC_decoder.c @@ -63,7 +63,7 @@ int32_t SASSC_steps[256] = -53261, -54797, -56333, -57870, -59406, -60942, -62479, 64015, }; -void decode_SASSC(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i; int32_t sample_count; int32_t hist = stream->adpcm_history1_32; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c index 2b12c3836..9814752ab 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c @@ -1,840 +1,57 @@ -/* - * ACM decoder. - * - * Copyright (c) 2004-2008, Marko Kreen - * Copyright (c) 2008, Adam Gashlin - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - #include "coding.h" -#include "../vgmstream.h" -#include "../streamtypes.h" -#include "acm_decoder.h" +#include "acm_decoder_libacm.h" +#include -#define ACM_EXPECTED_EOF -99 +/* libacm 1.2 (despite what libacm.h says) from: https://github.com/markokr/libacm */ -typedef int (*filler_t)(ACMStream *acm, unsigned ind, unsigned col); +typedef struct { + STREAMFILE *streamfile; + int offset; +} acm_io_config; -/************************************** - * Stream processing - **************************************/ +static int acm_read_streamfile(void *ptr, int size, int n, void *arg); +static int acm_seek_streamfile(void *arg, int offset, int whence); +static int acm_get_length_streamfile(void *arg); -/* NB: bits <= 31! Thus less checks in code. */ - -static int get_bits_reload(ACMStream *acm, unsigned bits) -{ - int got; - unsigned data, b_data, b_avail; - - data = acm->bit_data; - got = acm->bit_avail; - bits -= got; - - switch (acm->data_len - acm->buf_start_ofs) { - case 0: - b_data = 0; - b_avail = 8; - break; - case 1: - b_data = (uint8_t)read_8bit(acm->buf_start_ofs,acm->streamfile); - b_avail = 8; - acm->buf_start_ofs += 1; - break; - case 2: - b_data = (uint16_t)read_16bitLE(acm->buf_start_ofs,acm->streamfile); - b_avail = 16; - acm->buf_start_ofs += 2; - break; - case 3: - b_data = (uint8_t)read_8bit(acm->buf_start_ofs,acm->streamfile); - b_data |= (int32_t)(uint16_t)read_16bitLE(acm->buf_start_ofs+1,acm->streamfile)<<8; - b_avail = 24; - acm->buf_start_ofs += 3; - break; - case 4: - default: - if (acm->data_len - acm->buf_start_ofs <= 0) { - b_data = 0; - b_avail = 8; - break; - } - - b_data = read_32bitLE(acm->buf_start_ofs,acm->streamfile); - b_avail = 32; - acm->buf_start_ofs += 4; - break; - } - - data |= (b_data & ((1 << bits) - 1)) << got; - acm->bit_data = b_data >> bits; - acm->bit_avail = b_avail - bits; - return data; -} - -#define GET_BITS_NOERR(tmpval, acm, bits) do { \ - if (acm->bit_avail >= bits) { \ - tmpval = acm->bit_data & ((1 << bits) - 1); \ - acm->bit_data >>= bits; \ - acm->bit_avail -= bits; \ - } else \ - tmpval = get_bits_reload(acm, bits); \ - } while (0) - -#define GET_BITS(res, acm, bits) do { \ - int tmpval; \ - GET_BITS_NOERR(tmpval, acm, bits); \ - if (tmpval < 0) \ - return tmpval; \ - res = tmpval; \ - } while (0) - -#define GET_BITS_EXPECT_EOF(res, acm, bits) do { \ - int tmpval; \ - GET_BITS_NOERR(tmpval, acm, bits); \ - if (tmpval < 0) { \ - if (tmpval == ACM_ERR_UNEXPECTED_EOF) \ - return ACM_EXPECTED_EOF; \ - return tmpval; \ - } \ - res = tmpval; \ - } while (0) - -/************************************************* - * Table filling - *************************************************/ -static const int map_1bit[] = { -1, +1 }; -static const int map_2bit_near[] = { -2, -1, +1, +2 }; -static const int map_2bit_far[] = { -3, -2, +2, +3 }; -static const int map_3bit[] = { -4, -3, -2, -1, +1, +2, +3, +4 }; -static int mul_3x3[3*3*3]; -static int mul_3x5[5*5*5]; -static int mul_2x11[11*11]; -static int tables_generated; - -static void generate_tables(void) -{ - int x1, x2, x3; - if (tables_generated) - return; - for (x3 = 0; x3 < 3; x3++) - for (x2 = 0; x2 < 3; x2++) - for (x1 = 0; x1 < 3; x1++) - mul_3x3[x1 + x2*3 + x3*3*3] = - x1 + (x2 << 4) + (x3 << 8); - for (x3 = 0; x3 < 5; x3++) - for (x2 = 0; x2 < 5; x2++) - for (x1 = 0; x1 < 5; x1++) - mul_3x5[x1 + x2*5 + x3*5*5] = - x1 + (x2 << 4) + (x3 << 8); - for (x2 = 0; x2 < 11; x2++) - for (x1 = 0; x1 < 11; x1++) - mul_2x11[x1 + x2*11] = x1 + (x2 << 4); - - tables_generated = 1; -} - -/* IOW: (r * acm->subblock_len) + c */ -#define set_pos(acm, r, c, idx) do { \ - unsigned _pos = ((r) << acm->info.acm_level) + (c); \ - acm->block[_pos] = acm->midbuf[idx]; \ - } while (0) - -/************ Fillers **********/ - -static int f_zero(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i; - for (i = 0; i < acm->info.acm_rows; i++) - set_pos(acm, i, col, 0); - - return 1; -} - -static int f_bad(ACMStream *acm, unsigned ind, unsigned col) -{ - /* corrupt block? */ - return ACM_ERR_CORRUPT; -} - -static int f_linear(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned int i; - int b, middle = 1 << (ind - 1); - - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, ind); - set_pos(acm, i, col, b - middle); - } - return 1; -} - -static int f_k13(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, 1); - if (b == 0) { - /* 0 */ - set_pos(acm, i++, col, 0); - if (i >= acm->info.acm_rows) - break; - set_pos(acm, i, col, 0); - continue; - } - GET_BITS(b, acm, 1); - if (b == 0) { - /* 1, 0 */ - set_pos(acm, i, col, 0); - continue; - } - /* 1, 1, ? */ - GET_BITS(b, acm, 1); - set_pos(acm, i, col, map_1bit[b]); - } - return 1; -} - -static int f_k12(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, 1); - if (b == 0) { - /* 0 */ - set_pos(acm, i, col, 0); - continue; - } - - /* 1, ? */ - GET_BITS(b, acm, 1); - set_pos(acm, i, col, map_1bit[b]); - } - return 1; -} - -static int f_k24(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, 1); - if (b == 0) { - /* 0 */ - set_pos(acm, i++, col, 0); - if (i >= acm->info.acm_rows) break; - set_pos(acm, i, col, 0); - continue; - } - - GET_BITS(b, acm, 1); - if (b == 0) { - /* 1, 0 */ - set_pos(acm, i, col, 0); - continue; - } - - /* 1, 1, ?, ? */ - GET_BITS(b, acm, 2); - set_pos(acm, i, col, map_2bit_near[b]); - } - return 1; -} - -static int f_k23(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, 1); - if (b == 0) { - /* 0 */ - set_pos(acm, i, col, 0); - continue; - } - - /* 1, ?, ? */ - GET_BITS(b, acm, 2); - set_pos(acm, i, col, map_2bit_near[b]); - } - return 1; -} - -static int f_k35(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, 1); - if (b == 0) { - /* 0 */ - set_pos(acm, i++, col, 0); - if (i >= acm->info.acm_rows) - break; - set_pos(acm, i, col, 0); - continue; - } - - GET_BITS(b, acm, 1); - if (b == 0) { - /* 1, 0 */ - set_pos(acm, i, col, 0); - continue; - } - - GET_BITS(b, acm, 1); - if (b == 0) { - /* 1, 1, 0, ? */ - GET_BITS(b, acm, 1); - set_pos(acm, i, col, map_1bit[b]); - continue; - } - - /* 1, 1, 1, ?, ? */ - GET_BITS(b, acm, 2); - set_pos(acm, i, col, map_2bit_far[b]); - } - return 1; -} - -static int f_k34(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, 1); - if (b == 0) { - /* 0 */ - set_pos(acm, i, col, 0); - continue; - } - - GET_BITS(b, acm, 1); - if (b == 0) { - /* 1, 0, ? */ - GET_BITS(b, acm, 1); - set_pos(acm, i, col, map_1bit[b]); - continue; - } - - /* 1, 1, ?, ? */ - GET_BITS(b, acm, 2); - set_pos(acm, i, col, map_2bit_far[b]); - } - return 1; -} - -static int f_k45(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, 1); - if (b == 0) { - /* 0 */ - set_pos(acm, i, col, 0); i++; - if (i >= acm->info.acm_rows) - break; - set_pos(acm, i, col, 0); - continue; - } - - GET_BITS(b, acm, 1); - if (b == 0) { - /* 1, 0 */ - set_pos(acm, i, col, 0); - continue; - } - - /* 1, 1, ?, ?, ? */ - GET_BITS(b, acm, 3); - set_pos(acm, i, col, map_3bit[b]); - } - return 1; -} - -static int f_k44(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - for (i = 0; i < acm->info.acm_rows; i++) { - GET_BITS(b, acm, 1); - if (b == 0) { - /* 0 */ - set_pos(acm, i, col, 0); - continue; - } - - /* 1, ?, ?, ? */ - GET_BITS(b, acm, 3); - set_pos(acm, i, col, map_3bit[b]); - } - return 1; -} - -static int f_t15(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - int n1, n2, n3; - for (i = 0; i < acm->info.acm_rows; i++) { - /* b = (x1) + (x2 * 3) + (x3 * 9) */ - GET_BITS(b, acm, 5); - - n1 = (mul_3x3[b] & 0x0F) - 1; - n2 = ((mul_3x3[b] >> 4) & 0x0F) - 1; - n3 = ((mul_3x3[b] >> 8) & 0x0F) - 1; - - set_pos(acm, i++, col, n1); - if (i >= acm->info.acm_rows) - break; - set_pos(acm, i++, col, n2); - if (i >= acm->info.acm_rows) - break; - set_pos(acm, i, col, n3); - } - return 1; -} - -static int f_t27(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - int n1, n2, n3; - for (i = 0; i < acm->info.acm_rows; i++) { - /* b = (x1) + (x2 * 5) + (x3 * 25) */ - GET_BITS(b, acm, 7); - - n1 = (mul_3x5[b] & 0x0F) - 2; - n2 = ((mul_3x5[b] >> 4) & 0x0F) - 2; - n3 = ((mul_3x5[b] >> 8) & 0x0F) - 2; - - set_pos(acm, i++, col, n1); - if (i >= acm->info.acm_rows) - break; - set_pos(acm, i++, col, n2); - if (i >= acm->info.acm_rows) - break; - set_pos(acm, i, col, n3); - } - return 1; -} - -static int f_t37(ACMStream *acm, unsigned ind, unsigned col) -{ - unsigned i, b; - int n1, n2; - for (i = 0; i < acm->info.acm_rows; i++) { - /* b = (x1) + (x2 * 11) */ - GET_BITS(b, acm, 7); - - n1 = (mul_2x11[b] & 0x0F) - 5; - n2 = ((mul_2x11[b] >> 4) & 0x0F) - 5; - - set_pos(acm, i++, col, n1); - if (i >= acm->info.acm_rows) - break; - set_pos(acm, i, col, n2); - } - return 1; -} - -/****************/ - -static const filler_t filler_list[] = { - f_zero, f_bad, f_bad, f_linear, /* 0..3 */ - f_linear, f_linear, f_linear, f_linear, /* 4..7 */ - f_linear, f_linear, f_linear, f_linear, /* 8..11 */ - f_linear, f_linear, f_linear, f_linear, /* 12..15 */ - f_linear, f_k13, f_k12, f_t15, /* 16..19 */ - f_k24, f_k23, f_t27, f_k35, /* 20..23 */ - f_k34, f_bad, f_k45, f_k44, /* 24..27 */ - f_bad, f_t37, f_bad, f_bad /* 28..31 */ -}; - -static int fill_block(ACMStream *acm) -{ - unsigned i, ind; - int err; - for (i = 0; i < acm->info.acm_cols; i++) { - GET_BITS_EXPECT_EOF(ind, acm, 5); - err = filler_list[ind](acm, ind, i); - if (err < 0) - return err; - } - return 1; -} - -/********************************************** - * Decompress code - **********************************************/ - -static void juggle(int *wrap_p, int *block_p, unsigned sub_len, unsigned sub_count) -{ - unsigned int i, j; - int *p, r0, r1, r2, r3; - for (i = 0; i < sub_len; i++) { - p = block_p; - r0 = wrap_p[0]; - r1 = wrap_p[1]; - for (j = 0; j < sub_count/2; j++) { - r2 = *p; *p = r1*2 + (r0 + r2); p += sub_len; - r3 = *p; *p = r2*2 - (r1 + r3); p += sub_len; - r0 = r2; r1 = r3; - } - *wrap_p++ = r0; - *wrap_p++ = r1; - block_p++; - } -} - -static void juggle_block(ACMStream *acm) -{ - unsigned sub_count, sub_len, todo_count, step_subcount, i; - int *wrap_p, *block_p, *p; - - /* juggle only if subblock_len > 1 */ - if (acm->info.acm_level == 0) - return; - - /* 2048 / subblock_len */ - if (acm->info.acm_level > 9) - step_subcount = 1; - else - step_subcount = (2048 >> acm->info.acm_level) - 2; - - /* Apply juggle() (rows)x(cols) - * from (step_subcount * 2) x (subblock_len/2) - * to (step_subcount * subblock_len) x (1) - */ - todo_count = acm->info.acm_rows; - block_p = acm->block; - while (1) { - wrap_p = acm->wrapbuf; - sub_count = step_subcount; - if (sub_count > todo_count) - sub_count = todo_count; - - sub_len = acm->info.acm_cols / 2; - sub_count *= 2; - - juggle(wrap_p, block_p, sub_len, sub_count); - wrap_p += sub_len*2; - - for (i = 0, p = block_p; i < sub_count; i++) { - p[0]++; - p += sub_len; - } - - while (sub_len > 1) { - sub_len /= 2; - sub_count *= 2; - juggle(wrap_p, block_p, sub_len, sub_count); - wrap_p += sub_len*2; - } - if (todo_count <= step_subcount) - break; - todo_count -= step_subcount; - block_p += step_subcount << acm->info.acm_level; - } -} - -/***************************************************************/ -static int decode_block(ACMStream *acm) -{ - int pwr, count, val, i, x, err; - - acm->block_ready = 0; - acm->block_pos = 0; - - /* read header */ - GET_BITS_EXPECT_EOF(pwr, acm, 4); - GET_BITS_EXPECT_EOF(val, acm, 16); - - /* generate tables */ - count = 1 << pwr; - for (i = 0, x = 0; i < count; i++) { - acm->midbuf[i] = x; - x += val; - } - for (i = 1, x = -val; i <= count; i++) { - acm->midbuf[-i] = x; - x -= val; - } - - /* to_check? */ - if ((err = fill_block(acm)) <= 0) - return err; - - juggle_block(acm); - - acm->block_ready = 1; - - return 1; -} - -/****************************** - * Output formats - ******************************/ - -static unsigned char *out_s16le(int *src, unsigned char *dst, unsigned n, unsigned shift) -{ - while (n--) { - int val = *src++ >> shift; - *dst++ = val & 0xFF; - *dst++ = (val >> 8) & 0xFF; - } - return dst; -} - -static unsigned char *out_s16be(int *src, unsigned char *dst, unsigned n, unsigned shift) -{ - while (n--) { - int val = *src++ >> shift; - *dst++ = (val >> 8) & 0xFF; - *dst++ = val & 0xFF; - } - return dst; -} - -static unsigned char *out_u16le(int *src, unsigned char *dst, unsigned n, unsigned shift) -{ - while (n--) { - int val = (*src++ >> shift) + 0x8000; - *dst++ = val & 0xFF; - *dst++ = (val >> 8) & 0xFF; - } - return dst; -} - -static unsigned char *out_u16be(int *src, unsigned char *dst, unsigned n, unsigned shift) -{ - while (n--) { - int val = (*src++ >> shift) + 0x8000; - *dst++ = (val >> 8) & 0xFF; - *dst++ = val & 0xFF; - } - return dst; -} - -static int output_values(int *src, unsigned char *dst, int n, - int acm_level, int bigendianp, int wordlen, int sgned) -{ - unsigned char *res = NULL; - if (wordlen == 2) { - if (bigendianp == 0) { - if (sgned) - res = out_s16le(src, dst, n, acm_level); - else - res = out_u16le(src, dst, n, acm_level); - } else { - if (sgned) - res = out_s16be(src, dst, n, acm_level); - else - res = out_u16be(src, dst, n, acm_level); - } - } - if (res != NULL) - return res - dst; - return ACM_ERR_BADFMT; -} - -/* - * Header parsing. - */ - -static int read_header(ACMStream *acm) -{ - int tmp; - /* read header */ - GET_BITS(acm->info.acm_id, acm, 24); - if (acm->info.acm_id != ACM_ID) - return ACM_ERR_NOT_ACM; - GET_BITS(acm->info.acm_version, acm, 8); - if (acm->info.acm_version != 1) - return ACM_ERR_NOT_ACM; - GET_BITS(acm->total_values, acm, 16); - GET_BITS(tmp, acm, 16); - acm->total_values += tmp << 16; - if (acm->total_values == 0) - return ACM_ERR_NOT_ACM; - GET_BITS(acm->info.channels, acm, 16); - if (acm->info.channels < 1) - return ACM_ERR_NOT_ACM; - /* we play music, music is stereo, though not all headers agree */ - acm->info.channels = 2; - GET_BITS(acm->info.rate, acm, 16); - - GET_BITS(acm->info.acm_level, acm, 4); - GET_BITS(acm->info.acm_rows, acm, 12); - return 0; -} - -/*********************************************** - * Public functions - ***********************************************/ - -int acm_open_decoder(ACMStream **res, STREAMFILE *facilitator_file, - const char *const filename) -{ - int err = ACM_ERR_OTHER; - ACMStream *acm; - - acm = malloc(sizeof(*acm)); - if (!acm) - return err; - memset(acm, 0, sizeof(*acm)); - - acm->streamfile = facilitator_file->open(facilitator_file,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!acm->streamfile) - { - err = ACM_ERR_OPEN; - goto err_out; - } - - acm->data_len = get_streamfile_size(acm->streamfile); - - /* read header data */ - err = ACM_ERR_NOT_ACM; - if (read_header(acm) < 0) - goto err_out; - - /* calculate blocks */ - acm->info.acm_cols = 1 << acm->info.acm_level; - acm->wrapbuf_len = 2 * acm->info.acm_cols - 2; - acm->block_len = acm->info.acm_rows * acm->info.acm_cols; - - /* allocate */ - acm->block = malloc(acm->block_len * sizeof(int)); - acm->wrapbuf = malloc(acm->wrapbuf_len * sizeof(int)); - acm->ampbuf = malloc(0x10000 * sizeof(int)); - acm->midbuf = acm->ampbuf + 0x8000; - - memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int)); - - generate_tables(); - - *res = acm; - return ACM_OK; - -err_out: - - acm_close(acm); - return err; -} - -int acm_read(ACMStream *acm, void *dst, unsigned numbytes, - int bigendianp, int wordlen, int sgned) -{ - int avail, gotbytes = 0, err; - int *src, numwords; - - if (wordlen == 2) - numwords = numbytes / 2; - else - return ACM_ERR_BADFMT; - - if (acm->stream_pos >= acm->total_values) - return 0; - - if (!acm->block_ready) { - err = decode_block(acm); - if (err == ACM_EXPECTED_EOF) - return 0; - if (err < 0) - return err; - } - - /* check how many words can be read */ - avail = acm->block_len - acm->block_pos; - if (avail < numwords) - numwords = avail; - - if (acm->stream_pos + numwords > acm->total_values) - numwords = acm->total_values - acm->stream_pos; - - if (acm->info.channels > 1) - numwords -= numwords % acm->info.channels; - - /* convert, but if dst == NULL, simulate */ - if (dst != NULL) { - src = acm->block + acm->block_pos; - gotbytes = output_values(src, dst, numwords, - acm->info.acm_level, - bigendianp, wordlen, sgned); - } else - gotbytes = numwords * wordlen; - - if (gotbytes >= 0) { - acm->stream_pos += numwords; - acm->block_pos += numwords; - if (acm->block_pos == acm->block_len) - acm->block_ready = 0; - } - - return gotbytes; -} - -void acm_close(ACMStream *acm) -{ - if (acm == NULL) - return; - if (acm->streamfile) { - close_streamfile(acm->streamfile); - acm->streamfile = NULL; - } - if (acm->block) - free(acm->block); - if (acm->wrapbuf) - free(acm->wrapbuf); - if (acm->ampbuf) - free(acm->ampbuf); - free(acm); -} - -void acm_reset(ACMStream *acm) -{ - acm->bit_avail = 0; - acm->bit_data = 0; - - acm->stream_pos = 0; - acm->block_pos = 0; - acm->block_ready = 0; - acm->buf_start_ofs = ACM_HEADER_LEN; - - memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int)); -} - - -/*********************************************** - * interface to vgmstream - ***********************************************/ - -acm_codec_data *init_acm(STREAMFILE *streamFile) { +acm_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number) { 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->io_config = calloc(1,sizeof(acm_io_config)); + if (!data->io_config) goto fail; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + data->streamfile = open_streamfile(streamFile,filename); + if (!data->streamfile) goto fail; + + /* Setup libacm decoder, needs read callbacks and a parameter for said callbacks */ + { + ACMStream *handle = NULL; + int res; + acm_io_config *io_config = data->io_config; + acm_io_callbacks io_callbacks = {0}; + + io_config->offset = 0; + io_config->streamfile = data->streamfile; + + io_callbacks.read_func = acm_read_streamfile; + io_callbacks.seek_func = acm_seek_streamfile; + io_callbacks.close_func = NULL; /* managed in free_acm */ + io_callbacks.get_length_func = acm_get_length_streamfile; + + res = acm_open_decoder(&handle, io_config, io_callbacks, force_channel_number); + if (res < 0) { + VGM_LOG("ACM: failed opening libacm, error=%i\n", res); + goto fail; + } + + data->handle = handle; + } - data->file = acm_stream; return data; @@ -844,7 +61,7 @@ fail: } void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing) { - ACMStream * acm = data->file; + ACMStream * acm = data->handle; int32_t samples_read = 0; while (samples_read < samples_to_do) { @@ -862,20 +79,65 @@ void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, in } } -void reset_acm(VGMSTREAM *vgmstream) { - acm_codec_data *data = vgmstream->codec_data; +void reset_acm(acm_codec_data *data) { + if (!data || !data->handle) + return; - if (data && data->file) { - acm_reset(data->file); - } + acm_seek_pcm(data->handle, 0); } void free_acm(acm_codec_data *data) { - if (data) { - if (data->file) { - acm_close(data->file); - } - free(data); - } + if (!data) + return; + + acm_close(data->handle); + close_streamfile(data->streamfile); + free(data->io_config); + free(data); } +/* ******************************* */ + +static int acm_read_streamfile(void *ptr, int size, int n, void *arg) { + acm_io_config* config = arg; + int bytes_read, items_read; + + bytes_read = read_streamfile(ptr,config->offset,size*n,config->streamfile); + items_read = bytes_read / size; + config->offset += bytes_read; + + return items_read; + +} +static int acm_seek_streamfile(void *arg, int offset, int whence) { + acm_io_config* config = arg; + int base_offset, new_offset; + + switch (whence) { + case SEEK_SET: + base_offset = 0; + break; + case SEEK_CUR: + base_offset = config->offset; + break; + case SEEK_END: + base_offset = get_streamfile_size(config->streamfile); + break; + default: + return -1; + break; + } + + new_offset = base_offset + offset; + if (new_offset < 0 || new_offset > get_streamfile_size(config->streamfile)) { + return -1; /* unseekable */ + } else { + config->offset = new_offset; + return 0; + } +} +static int acm_get_length_streamfile(void *arg) { + acm_io_config* config = arg; + + return get_streamfile_size(config->streamfile); +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_decode.c b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_decode.c new file mode 100644 index 000000000..0ee8447a1 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_decode.c @@ -0,0 +1,912 @@ +/* + * ACM decoder. + * + * Copyright (c) 2004-2010, Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "acm_decoder_libacm.h" //"libacm.h"//vgmstream mod + +#define ACM_BUFLEN (64*1024) + +#define ACM_EXPECTED_EOF -99 + +typedef int (*filler_t)(ACMStream *acm, unsigned ind, unsigned col); + +/************************************** + * Stream processing + **************************************/ + +/* NB: bits <= 31! Thus less checks in code. */ + +static int load_buf(ACMStream *acm) +{ + int res = 0; + + if (acm->file_eof) + return 0; + + acm->buf_start_ofs += acm->buf_size; + + if (acm->io.read_func != NULL) + res = acm->io.read_func(acm->buf, 1, acm->buf_max, + acm->io_arg); + + if (res < 0) + return ACM_ERR_READ_ERR; + + if (res == 0) { + acm->file_eof = 1; + /* add single zero byte */ + acm->buf[0] = 0; + acm->buf_size = 1; + } else { + acm->buf_size = res; + } + acm->buf_pos = 0; + return 0; +} + +static int load_bits(ACMStream *acm) +{ + int err; + unsigned data, got; + unsigned char *p = acm->buf + acm->buf_pos; + switch (acm->buf_size - acm->buf_pos) { + default: + data = 0; + got = 0; + break; + case 1: + data = p[0]; + got = 8; + break; + case 2: + data = p[0] + (p[1] << 8); + got = 16; + break; + case 3: + data = p[0] + (p[1] << 8) + (p[2] << 16); + got = 24; + break; + } + + if ((err = load_buf(acm)) < 0) + return err; + + while (got < 32) { + if (acm->buf_size - acm->buf_pos == 0) + break; + data |= acm->buf[acm->buf_pos] << got; + got += 8; + acm->buf_pos++; + } + acm->bit_data = data; + acm->bit_avail = got; + return 0; +} + +static int get_bits_reload(ACMStream *acm, unsigned bits) +{ + int got, err; + unsigned data, b_data, b_avail; + + data = acm->bit_data; + got = acm->bit_avail; + bits -= got; + + if (acm->buf_size - acm->buf_pos >= 4) { + unsigned char *p = acm->buf + acm->buf_pos; + acm->buf_pos += 4; + b_data = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); + b_avail = 32; + } else { + if ((err = load_bits(acm)) < 0) + return err; + if (acm->bit_avail < bits) + return ACM_ERR_UNEXPECTED_EOF; + b_data = acm->bit_data; + b_avail = acm->bit_avail; + } + + data |= (b_data & ((1 << bits) - 1)) << got; + acm->bit_data = b_data >> bits; + acm->bit_avail = b_avail - bits; + return data; +} + +#define GET_BITS_NOERR(tmpval, acm, bits) do { \ + if (acm->bit_avail >= bits) { \ + tmpval = acm->bit_data & ((1 << bits) - 1); \ + acm->bit_data >>= bits; \ + acm->bit_avail -= bits; \ + } else \ + tmpval = get_bits_reload(acm, bits); \ + } while (0) + +#define GET_BITS(res, acm, bits) do { \ + int tmpval; \ + GET_BITS_NOERR(tmpval, acm, bits); \ + if (tmpval < 0) \ + return tmpval; \ + res = tmpval; \ + } while (0) + +#define GET_BITS_EXPECT_EOF(res, acm, bits) do { \ + int tmpval; \ + GET_BITS_NOERR(tmpval, acm, bits); \ + if (tmpval < 0) { \ + if (tmpval == ACM_ERR_UNEXPECTED_EOF) \ + return ACM_EXPECTED_EOF; \ + return tmpval; \ + } \ + res = tmpval; \ + } while (0) + +/************************************************* + * Table filling + *************************************************/ +static const int map_1bit[] = { -1, +1 }; +static const int map_2bit_near[] = { -2, -1, +1, +2 }; +static const int map_2bit_far[] = { -3, -2, +2, +3 }; +static const int map_3bit[] = { -4, -3, -2, -1, +1, +2, +3, +4 }; +static int mul_3x3[3*3*3]; +static int mul_3x5[5*5*5]; +static int mul_2x11[11*11]; +static int tables_generated; + +static void generate_tables(void) +{ + int x1, x2, x3; + if (tables_generated) + return; + for (x3 = 0; x3 < 3; x3++) + for (x2 = 0; x2 < 3; x2++) + for (x1 = 0; x1 < 3; x1++) + mul_3x3[x1 + x2*3 + x3*3*3] = + x1 + (x2 << 4) + (x3 << 8); + for (x3 = 0; x3 < 5; x3++) + for (x2 = 0; x2 < 5; x2++) + for (x1 = 0; x1 < 5; x1++) + mul_3x5[x1 + x2*5 + x3*5*5] = + x1 + (x2 << 4) + (x3 << 8); + for (x2 = 0; x2 < 11; x2++) + for (x1 = 0; x1 < 11; x1++) + mul_2x11[x1 + x2*11] = x1 + (x2 << 4); + + tables_generated = 1; +} + +/* IOW: (r * acm->subblock_len) + c */ +#define set_pos(acm, r, c, idx) do { \ + unsigned _pos = ((r) << acm->info.acm_level) + (c); \ + acm->block[_pos] = acm->midbuf[idx]; \ + } while (0) + +/************ Fillers **********/ + +static int f_zero(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i; + for (i = 0; i < acm->info.acm_rows; i++) + set_pos(acm, i, col, 0); + + return 1; +} + +static int f_bad(ACMStream *acm, unsigned ind, unsigned col) +{ + /* corrupt block? */ + return ACM_ERR_CORRUPT; +} + +static int f_linear(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned int i; + int b, middle = 1 << (ind - 1); + + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, ind); + set_pos(acm, i, col, b - middle); + } + return 1; +} + +static int f_k13(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, 1); + if (b == 0) { + /* 0 */ + set_pos(acm, i++, col, 0); + if (i >= acm->info.acm_rows) + break; + set_pos(acm, i, col, 0); + continue; + } + GET_BITS(b, acm, 1); + if (b == 0) { + /* 1, 0 */ + set_pos(acm, i, col, 0); + continue; + } + /* 1, 1, ? */ + GET_BITS(b, acm, 1); + set_pos(acm, i, col, map_1bit[b]); + } + return 1; +} + +static int f_k12(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, 1); + if (b == 0) { + /* 0 */ + set_pos(acm, i, col, 0); + continue; + } + + /* 1, ? */ + GET_BITS(b, acm, 1); + set_pos(acm, i, col, map_1bit[b]); + } + return 1; +} + +static int f_k24(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, 1); + if (b == 0) { + /* 0 */ + set_pos(acm, i++, col, 0); + if (i >= acm->info.acm_rows) break; + set_pos(acm, i, col, 0); + continue; + } + + GET_BITS(b, acm, 1); + if (b == 0) { + /* 1, 0 */ + set_pos(acm, i, col, 0); + continue; + } + + /* 1, 1, ?, ? */ + GET_BITS(b, acm, 2); + set_pos(acm, i, col, map_2bit_near[b]); + } + return 1; +} + +static int f_k23(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, 1); + if (b == 0) { + /* 0 */ + set_pos(acm, i, col, 0); + continue; + } + + /* 1, ?, ? */ + GET_BITS(b, acm, 2); + set_pos(acm, i, col, map_2bit_near[b]); + } + return 1; +} + +static int f_k35(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, 1); + if (b == 0) { + /* 0 */ + set_pos(acm, i++, col, 0); + if (i >= acm->info.acm_rows) + break; + set_pos(acm, i, col, 0); + continue; + } + + GET_BITS(b, acm, 1); + if (b == 0) { + /* 1, 0 */ + set_pos(acm, i, col, 0); + continue; + } + + GET_BITS(b, acm, 1); + if (b == 0) { + /* 1, 1, 0, ? */ + GET_BITS(b, acm, 1); + set_pos(acm, i, col, map_1bit[b]); + continue; + } + + /* 1, 1, 1, ?, ? */ + GET_BITS(b, acm, 2); + set_pos(acm, i, col, map_2bit_far[b]); + } + return 1; +} + +static int f_k34(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, 1); + if (b == 0) { + /* 0 */ + set_pos(acm, i, col, 0); + continue; + } + + GET_BITS(b, acm, 1); + if (b == 0) { + /* 1, 0, ? */ + GET_BITS(b, acm, 1); + set_pos(acm, i, col, map_1bit[b]); + continue; + } + + /* 1, 1, ?, ? */ + GET_BITS(b, acm, 2); + set_pos(acm, i, col, map_2bit_far[b]); + } + return 1; +} + +static int f_k45(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, 1); + if (b == 0) { + /* 0 */ + set_pos(acm, i, col, 0); i++; + if (i >= acm->info.acm_rows) + break; + set_pos(acm, i, col, 0); + continue; + } + + GET_BITS(b, acm, 1); + if (b == 0) { + /* 1, 0 */ + set_pos(acm, i, col, 0); + continue; + } + + /* 1, 1, ?, ?, ? */ + GET_BITS(b, acm, 3); + set_pos(acm, i, col, map_3bit[b]); + } + return 1; +} + +static int f_k44(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + for (i = 0; i < acm->info.acm_rows; i++) { + GET_BITS(b, acm, 1); + if (b == 0) { + /* 0 */ + set_pos(acm, i, col, 0); + continue; + } + + /* 1, ?, ?, ? */ + GET_BITS(b, acm, 3); + set_pos(acm, i, col, map_3bit[b]); + } + return 1; +} + +static int f_t15(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + int n1, n2, n3; + for (i = 0; i < acm->info.acm_rows; i++) { + /* b = (x1) + (x2 * 3) + (x3 * 9) */ + GET_BITS(b, acm, 5); + + n1 = (mul_3x3[b] & 0x0F) - 1; + n2 = ((mul_3x3[b] >> 4) & 0x0F) - 1; + n3 = ((mul_3x3[b] >> 8) & 0x0F) - 1; + + set_pos(acm, i++, col, n1); + if (i >= acm->info.acm_rows) + break; + set_pos(acm, i++, col, n2); + if (i >= acm->info.acm_rows) + break; + set_pos(acm, i, col, n3); + } + return 1; +} + +static int f_t27(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + int n1, n2, n3; + for (i = 0; i < acm->info.acm_rows; i++) { + /* b = (x1) + (x2 * 5) + (x3 * 25) */ + GET_BITS(b, acm, 7); + + n1 = (mul_3x5[b] & 0x0F) - 2; + n2 = ((mul_3x5[b] >> 4) & 0x0F) - 2; + n3 = ((mul_3x5[b] >> 8) & 0x0F) - 2; + + set_pos(acm, i++, col, n1); + if (i >= acm->info.acm_rows) + break; + set_pos(acm, i++, col, n2); + if (i >= acm->info.acm_rows) + break; + set_pos(acm, i, col, n3); + } + return 1; +} + +static int f_t37(ACMStream *acm, unsigned ind, unsigned col) +{ + unsigned i, b; + int n1, n2; + for (i = 0; i < acm->info.acm_rows; i++) { + /* b = (x1) + (x2 * 11) */ + GET_BITS(b, acm, 7); + + n1 = (mul_2x11[b] & 0x0F) - 5; + n2 = ((mul_2x11[b] >> 4) & 0x0F) - 5; + + set_pos(acm, i++, col, n1); + if (i >= acm->info.acm_rows) + break; + set_pos(acm, i, col, n2); + } + return 1; +} + +/****************/ + +static const filler_t filler_list[] = { + f_zero, f_bad, f_bad, f_linear, /* 0..3 */ + f_linear, f_linear, f_linear, f_linear, /* 4..7 */ + f_linear, f_linear, f_linear, f_linear, /* 8..11 */ + f_linear, f_linear, f_linear, f_linear, /* 12..15 */ + f_linear, f_k13, f_k12, f_t15, /* 16..19 */ + f_k24, f_k23, f_t27, f_k35, /* 20..23 */ + f_k34, f_bad, f_k45, f_k44, /* 24..27 */ + f_bad, f_t37, f_bad, f_bad /* 28..31 */ +}; + +static int fill_block(ACMStream *acm) +{ + unsigned i, ind; + int err; + for (i = 0; i < acm->info.acm_cols; i++) { + GET_BITS_EXPECT_EOF(ind, acm, 5); + err = filler_list[ind](acm, ind, i); + if (err < 0) + return err; + } + return 1; +} + +/********************************************** + * Decompress code + **********************************************/ + +static void juggle(int *wrap_p, int *block_p, unsigned sub_len, unsigned sub_count) +{ + unsigned int i, j; + int *p, r0, r1, r2, r3; + for (i = 0; i < sub_len; i++) { + p = block_p; + r0 = wrap_p[0]; + r1 = wrap_p[1]; + for (j = 0; j < sub_count/2; j++) { + r2 = *p; *p = r1*2 + (r0 + r2); p += sub_len; + r3 = *p; *p = r2*2 - (r1 + r3); p += sub_len; + r0 = r2; r1 = r3; + } + *wrap_p++ = r0; + *wrap_p++ = r1; + block_p++; + } +} + +static void juggle_block(ACMStream *acm) +{ + unsigned sub_count, sub_len, todo_count, step_subcount, i; + int *wrap_p, *block_p, *p; + + /* juggle only if subblock_len > 1 */ + if (acm->info.acm_level == 0) + return; + + /* 2048 / subblock_len */ + if (acm->info.acm_level > 9) + step_subcount = 1; + else + step_subcount = (2048 >> acm->info.acm_level) - 2; + + /* Apply juggle() (rows)x(cols) + * from (step_subcount * 2) x (subblock_len/2) + * to (step_subcount * subblock_len) x (1) + */ + todo_count = acm->info.acm_rows; + block_p = acm->block; + while (1) { + wrap_p = acm->wrapbuf; + sub_count = step_subcount; + if (sub_count > todo_count) + sub_count = todo_count; + + sub_len = acm->info.acm_cols / 2; + sub_count *= 2; + + juggle(wrap_p, block_p, sub_len, sub_count); + wrap_p += sub_len*2; + + for (i = 0, p = block_p; i < sub_count; i++) { + p[0]++; + p += sub_len; + } + + while (sub_len > 1) { + sub_len /= 2; + sub_count *= 2; + juggle(wrap_p, block_p, sub_len, sub_count); + wrap_p += sub_len*2; + } + if (todo_count <= step_subcount) + break; + todo_count -= step_subcount; + block_p += step_subcount << acm->info.acm_level; + } +} + +/***************************************************************/ +static int decode_block(ACMStream *acm) +{ + int pwr, count, val, i, x, err; + + acm->block_ready = 0; + acm->block_pos = 0; + + /* read header */ + GET_BITS_EXPECT_EOF(pwr, acm, 4); + GET_BITS_EXPECT_EOF(val, acm, 16); + + /* generate tables */ + count = 1 << pwr; + for (i = 0, x = 0; i < count; i++) { + acm->midbuf[i] = x; + x += val; + } + for (i = 1, x = -val; i <= count; i++) { + acm->midbuf[-i] = x; + x -= val; + } + + /* to_check? */ + if ((err = fill_block(acm)) <= 0) + return err; + + juggle_block(acm); + + acm->block_ready = 1; + + return 1; +} + +/****************************** + * Output formats + ******************************/ + +static unsigned char *out_s16le(int *src, unsigned char *dst, unsigned n, unsigned shift) +{ + while (n--) { + int val = *src++ >> shift; + *dst++ = val & 0xFF; + *dst++ = (val >> 8) & 0xFF; + } + return dst; +} + +static unsigned char *out_s16be(int *src, unsigned char *dst, unsigned n, unsigned shift) +{ + while (n--) { + int val = *src++ >> shift; + *dst++ = (val >> 8) & 0xFF; + *dst++ = val & 0xFF; + } + return dst; +} + +static unsigned char *out_u16le(int *src, unsigned char *dst, unsigned n, unsigned shift) +{ + while (n--) { + int val = (*src++ >> shift) + 0x8000; + *dst++ = val & 0xFF; + *dst++ = (val >> 8) & 0xFF; + } + return dst; +} + +static unsigned char *out_u16be(int *src, unsigned char *dst, unsigned n, unsigned shift) +{ + while (n--) { + int val = (*src++ >> shift) + 0x8000; + *dst++ = (val >> 8) & 0xFF; + *dst++ = val & 0xFF; + } + return dst; +} + +static int output_values(int *src, unsigned char *dst, int n, + int acm_level, int bigendianp, int wordlen, int sgned) +{ + unsigned char *res = NULL; + if (wordlen == 2) { + if (bigendianp == 0) { + if (sgned) + res = out_s16le(src, dst, n, acm_level); + else + res = out_u16le(src, dst, n, acm_level); + } else { + if (sgned) + res = out_s16be(src, dst, n, acm_level); + else + res = out_u16be(src, dst, n, acm_level); + } + } + if (res != NULL) + return res - dst; + return ACM_ERR_BADFMT; +} + +/* + * WAVC (compressed WAV) files are ACM files with additional header. + * + * 'WAVC' + 'V1.00' + uncompr(4b) + compr(4b) + 12b + */ + +#define WAVC_ID 0x564157 /* 'WAV' */ + +static int read_wavc_header(ACMStream *acm) +{ + static const unsigned short expect[12] = { + /* 'V1.0', raw_size, acm_size */ + 0x3156, 0x302E, 0,0, 0,0, + /* hdrlen?, chans?, bits?, hz */ + 28,0, 1, 16, 22050, 0 + }; + unsigned short i, buf[12]; + + for (i = 0; i < 12; i++) + GET_BITS(buf[i], acm, 16); + if (memcmp(buf, expect, 4) != 0) + return -1; + /* full comparision is too strict */ + if (0 && memcmp(buf + 6, expect + 6, 12) != 0) + return -1; + /* just make sure the magic 28 is there */ + if (expect[6] != buf[6]) + return -1; + + acm->wavc_file = 1; + return 0; +} + +static int read_header(ACMStream *acm) +{ + unsigned int tmp; + + /* read header */ + + GET_BITS(tmp, acm, 24); + if (tmp == WAVC_ID) { + GET_BITS(tmp, acm, 8); + if (tmp != 'C') + return ACM_ERR_NOT_ACM; + if (read_wavc_header(acm) < 0) + return ACM_ERR_NOT_ACM; + GET_BITS(tmp, acm, 24); + } + if (tmp != ACM_ID) + return ACM_ERR_NOT_ACM; + acm->info.acm_id = tmp; + + GET_BITS(acm->info.acm_version, acm, 8); + if (acm->info.acm_version != 1) + return ACM_ERR_NOT_ACM; + GET_BITS(acm->total_values, acm, 16); + GET_BITS(tmp, acm, 16); + acm->total_values += tmp << 16; + if (acm->total_values == 0) + return ACM_ERR_NOT_ACM; + GET_BITS(acm->info.channels, acm, 16); + if (acm->info.channels < 1 || acm->info.channels > 2) + return ACM_ERR_NOT_ACM; + acm->info.acm_channels = acm->info.channels; + GET_BITS(acm->info.rate, acm, 16); + if (acm->info.rate < 4096) + return ACM_ERR_NOT_ACM; + + GET_BITS(acm->info.acm_level, acm, 4); + GET_BITS(acm->info.acm_rows, acm, 12); + if (!acm->info.acm_rows) + return ACM_ERR_NOT_ACM; + return 0; +} + +/*********************************************** + * Public functions + ***********************************************/ + +int acm_open_decoder(ACMStream **res, void *arg, acm_io_callbacks io_cb, int force_chans) +{ + int err = ACM_ERR_OTHER; + ACMStream *acm; + + acm = malloc(sizeof(*acm)); + if (!acm) + return err; + memset(acm, 0, sizeof(*acm)); + + acm->io_arg = arg; + acm->io = io_cb; + + if (acm->io.get_length_func) { + acm->data_len = acm->io.get_length_func(acm->io_arg); + } else { + acm->data_len = 0; + } + + acm->buf_max = ACM_BUFLEN; + acm->buf = malloc(acm->buf_max); + if (!acm->buf) + goto err_out; + + /* read header data */ + err = ACM_ERR_NOT_ACM; + if (read_header(acm) < 0) + goto err_out; + + /* + * Overwrite channel info if requested, otherwise + * ignore the channel count on plain ACM files, + * it is frequently wrong, and actual 1-channel + * files are not interising to listen to anyway (samples). + * + * Trust WAVC files, as they seem to be correct? + */ + if (force_chans > 0) + acm->info.channels = force_chans; + else if (!acm->wavc_file && acm->info.channels < 2) + acm->info.channels = 2; + + /* calculate blocks */ + acm->info.acm_cols = 1 << acm->info.acm_level; + acm->wrapbuf_len = 2 * acm->info.acm_cols - 2; + acm->block_len = acm->info.acm_rows * acm->info.acm_cols; + + /* allocate */ + acm->block = malloc(acm->block_len * sizeof(int)); + acm->wrapbuf = malloc(acm->wrapbuf_len * sizeof(int)); + acm->ampbuf = malloc(0x10000 * sizeof(int)); + acm->midbuf = acm->ampbuf + 0x8000; + + memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int)); + + generate_tables(); + + *res = acm; + return ACM_OK; + +err_out: + /* disable callbacks */ + memset(&acm->io, 0, sizeof(acm->io)); + acm->io_arg = NULL; + + acm_close(acm); + return err; +} + +int acm_read(ACMStream *acm, void *dst, unsigned numbytes, + int bigendianp, int wordlen, int sgned) +{ + int avail, gotbytes = 0, err; + int *src, numwords; + + if (wordlen == 2) + numwords = numbytes / 2; + else + return ACM_ERR_BADFMT; + + if (acm->stream_pos >= acm->total_values) + return 0; + + if (!acm->block_ready) { + err = decode_block(acm); + if (err == ACM_EXPECTED_EOF) + return 0; + if (err < 0) + return err; + } + + /* check how many words can be read */ + avail = acm->block_len - acm->block_pos; + if (avail < numwords) + numwords = avail; + + if (acm->stream_pos + numwords > acm->total_values) + numwords = acm->total_values - acm->stream_pos; + + if (acm->info.channels > 1) + numwords -= numwords % acm->info.channels; + + /* convert, but if dst == NULL, simulate */ + if (dst != NULL) { + src = acm->block + acm->block_pos; + gotbytes = output_values(src, dst, numwords, + acm->info.acm_level, + bigendianp, wordlen, sgned); + } else + gotbytes = numwords * wordlen; + + if (gotbytes >= 0) { + acm->stream_pos += numwords; + acm->block_pos += numwords; + if (acm->block_pos == acm->block_len) + acm->block_ready = 0; + } + + return gotbytes; +} + +void acm_close(ACMStream *acm) +{ + if (acm == NULL) + return; + if (acm->io.close_func) + acm->io.close_func(acm->io_arg); + if (acm->buf) + free(acm->buf); + if (acm->block) + free(acm->block); + if (acm->wrapbuf) + free(acm->wrapbuf); + if (acm->ampbuf) + free(acm->ampbuf); + free(acm); +} + diff --git a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_libacm.h similarity index 55% rename from Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.h rename to Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_libacm.h index 917c748c2..a51573c2b 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_libacm.h @@ -1,10 +1,9 @@ /* * libacm - Interplay ACM audio decoder. * - * Copyright (c) 2004-2008, Marko Kreen - * Copyright (c) 2008, Adam Gashlin + * Copyright (c) 2004-2010, Marko Kreen * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * @@ -20,18 +19,11 @@ #ifndef __LIBACM_H #define __LIBACM_H -#ifdef BUILD_VGMSTREAM -#include "../streamfile.h" -#else -#include "streamfile.h" -#endif - -#define LIBACM_VERSION "1.0-svn" +#define LIBACM_VERSION "1.1" #define ACM_ID 0x032897 #define ACM_WORD 2 -#define ACM_HEADER_LEN 14 #define ACM_OK 0 #define ACM_ERR_OTHER -1 #define ACM_ERR_OPEN -2 @@ -47,21 +39,35 @@ typedef struct ACMInfo { unsigned rate; unsigned acm_id; unsigned acm_version; + unsigned acm_channels; /* channels from header (usually wrong) */ unsigned acm_level; unsigned acm_cols; /* 1 << acm_level */ unsigned acm_rows; } ACMInfo; +typedef struct { + /* read bytes */ + int (*read_func)(void *ptr, int size, int n, void *datasrc); + /* optional, must support seeking into start*/ + int (*seek_func)(void *datasrc, int offset, int whence); + /* optional, called on acm_close */ + int (*close_func)(void *datasrc); + /* returns size in bytes*/ + int (*get_length_func)(void *datasrc); +} acm_io_callbacks; + struct ACMStream { ACMInfo info; unsigned total_values; /* acm data stream */ - STREAMFILE *streamfile; + void *io_arg; + acm_io_callbacks io; unsigned data_len; /* acm stream buffer */ - unsigned bit_avail; + unsigned char *buf; + unsigned buf_max, buf_size, buf_pos, bit_avail; unsigned bit_data; unsigned buf_start_ofs; @@ -76,16 +82,36 @@ struct ACMStream { /* result */ unsigned block_ready:1; unsigned file_eof:1; + unsigned wavc_file:1; unsigned stream_pos; /* in words. absolute */ unsigned block_pos; /* in words, relative */ }; typedef struct ACMStream ACMStream; /* decode.c */ -int acm_open_decoder(ACMStream **res, STREAMFILE *facilitator_file, const char *const filename); +int acm_open_decoder(ACMStream **res, void *io_arg, acm_io_callbacks io, int force_chans); int acm_read(ACMStream *acm, void *buf, unsigned nbytes, int bigendianp, int wordlen, int sgned); void acm_close(ACMStream *acm); -void acm_reset(ACMStream *acm); + +/* util.c */ +int acm_open_file(ACMStream **acm, const char *filename, int force_chans); +const ACMInfo *acm_info(ACMStream *acm); +int acm_seekable(ACMStream *acm); +unsigned acm_bitrate(ACMStream *acm); +unsigned acm_rate(ACMStream *acm); +unsigned acm_channels(ACMStream *acm); +unsigned acm_raw_total(ACMStream *acm); +unsigned acm_raw_tell(ACMStream *acm); +unsigned acm_pcm_total(ACMStream *acm); +unsigned acm_pcm_tell(ACMStream *acm); +unsigned acm_time_total(ACMStream *acm); +unsigned acm_time_tell(ACMStream *acm); +int acm_read_loop(ACMStream *acm, void *dst, unsigned len, + int bigendianp, int wordlen, int sgned); +int acm_seek_pcm(ACMStream *acm, unsigned pcm_pos); +int acm_seek_time(ACMStream *acm, unsigned pos_ms); +const char *acm_strerror(int err); #endif + diff --git a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_util.c b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_util.c new file mode 100644 index 000000000..bdc423ea2 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_util.c @@ -0,0 +1,278 @@ +/* + * Utility functions for libacm. + * + * Copyright (c) 2004-2010, Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "acm_decoder_libacm.h" //"libacm.h"//vgmstream mod + +#define WAVC_HEADER_LEN 28 +#define ACM_HEADER_LEN 14 + +/* + * error strings + */ +static const char *_errlist[] = { + "No error", + "ACM error", + "Cannot open file", + "Not an ACM file", + "Read error", + "Bad format", + "Corrupt file", + "Unexcpected EOF", + "Stream not seekable" +}; + +const char *acm_strerror(int err) +{ + int nerr = sizeof(_errlist) / sizeof(char *); + if ((-err) < 0 || (-err) >= nerr) + return "Unknown error"; + return _errlist[-err]; +} + +/* + * File IO using stdio + */ + +static int _read_file(void *ptr, int size, int n, void *arg) +{ + FILE *f = (FILE *)arg; + return fread(ptr, size, n, f); +} + +static int _close_file(void *arg) +{ + FILE *f = (FILE *)arg; + return fclose(f); +} + +static int _seek_file(void *arg, int offset, int whence) +{ + FILE *f = (FILE *)arg; + return fseek(f, offset, whence); +} + +static int _get_length_file(void *arg) +{ + FILE *f = (FILE *)arg; + int res, pos, len = -1; + + pos = ftell(f); + if (pos < 0) + return -1; + + res = fseek(f, 0, SEEK_END); + if (res >= 0) { + len = ftell(f); + fseek(f, pos, SEEK_SET); + } + return len; +} + +int acm_open_file(ACMStream **res, const char *filename, int force_chans) +{ + int err; + FILE *f; + acm_io_callbacks io; + ACMStream *acm; + + if ((f = fopen(filename, "rb")) == NULL) + return ACM_ERR_OPEN; + + memset(&io, 0, sizeof(io)); + io.read_func = _read_file; + io.seek_func = _seek_file; + io.close_func = _close_file; + io.get_length_func = _get_length_file; + + if ((err = acm_open_decoder(&acm, f, io, force_chans)) < 0) { + fclose(f); + return err; + } + *res = acm; + return 0; +} + +/* + * utility functions + */ + +static unsigned pcm2time(ACMStream *acm, unsigned long long pcm) +{ + return pcm * 1000 / acm->info.rate; + /* return ((10 * pcm) / acm->info.rate) * 100; */ +} + +static unsigned time2pcm(ACMStream *acm, unsigned long long time_ms) +{ + return time_ms * acm->info.rate / 1000; + /* return (time_ms / 100) * (acm->info.rate / 10); */ +} + +/* + * info functions + */ + +const ACMInfo *acm_info(ACMStream *acm) +{ + return &acm->info; +} + +unsigned acm_rate(ACMStream *acm) +{ + return acm->info.rate; +} + +unsigned acm_channels(ACMStream *acm) +{ + return acm->info.channels; +} + +int acm_seekable(ACMStream *acm) +{ + return acm->data_len > 0; +} + +unsigned acm_bitrate(ACMStream *acm) +{ + unsigned long long bits, time, bitrate = 0; + + if (acm_raw_total(acm) == 0) + return 13000; + + time = acm_time_total(acm); + if (time > 0) { + bits = 8 * acm_raw_total(acm); + bitrate = 1000 * bits / time; + } + return bitrate; +} + +unsigned acm_pcm_tell(ACMStream *acm) +{ + return acm->stream_pos / acm->info.channels; +} + +unsigned acm_pcm_total(ACMStream *acm) +{ + return acm->total_values / acm->info.channels; +} + +unsigned acm_time_tell(ACMStream *acm) +{ + return pcm2time(acm, acm_pcm_tell(acm)); +} + +unsigned acm_time_total(ACMStream *acm) +{ + return pcm2time(acm, acm_pcm_total(acm)); +} + +unsigned acm_raw_tell(ACMStream *acm) +{ + return acm->buf_start_ofs + acm->buf_pos; +} + +unsigned acm_raw_total(ACMStream *acm) +{ + return acm->data_len; +} + +/* + * seeking + */ + +int acm_seek_time(ACMStream *acm, unsigned time_ms) +{ + int res = acm_seek_pcm(acm, time2pcm(acm, time_ms)); + if (res <= 0) + return res; + return pcm2time(acm, res); +} + +int acm_seek_pcm(ACMStream *acm, unsigned pcm_pos) +{ + unsigned word_pos = pcm_pos * acm->info.channels; + unsigned start_ofs; + + if (word_pos < acm->stream_pos) { + if (acm->io.seek_func == NULL) + return ACM_ERR_NOT_SEEKABLE; + + start_ofs = ACM_HEADER_LEN; + if (acm->wavc_file) + start_ofs += WAVC_HEADER_LEN; + + if (acm->io.seek_func(acm->io_arg, start_ofs, SEEK_SET) < 0) + return ACM_ERR_NOT_SEEKABLE; + + acm->file_eof = 0; + acm->buf_pos = 0; + acm->buf_size = 0; + acm->bit_avail = 0; + acm->bit_data = 0; + + acm->stream_pos = 0; + acm->block_pos = 0; + acm->block_ready = 0; + acm->buf_start_ofs = ACM_HEADER_LEN; + + memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int)); + } + while (acm->stream_pos < word_pos) { + int step = 2048, res; + if (acm->stream_pos + step > word_pos) + step = word_pos - acm->stream_pos; + + res = acm_read(acm, NULL, step*2, 0,2,1); + if (res < 1) + break; + } + return acm->stream_pos / acm->info.channels; +} + +/* + * read loop - full block reading + */ +int acm_read_loop(ACMStream *acm, void *dst, unsigned bytes, + int bigendianp, int wordlen, int sgned) +{ + unsigned char *dstp = dst; + int res, got = 0; + while (bytes > 0) { + res = acm_read(acm, dstp, bytes, bigendianp, wordlen, sgned); + if (res > 0) { + if (dstp) + dstp += res; + got += res; + bytes -= res; + } else { + if (res < 0 && got == 0) + return res; + break; + } + } + return got; +} + diff --git a/Frameworks/vgmstream/vgmstream/src/coding/asf_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/asf_decoder.c index c97d7434e..c0db63018 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/asf_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/asf_decoder.c @@ -1,13 +1,13 @@ #include "coding.h" -/* Decodec Argonaut's ASF ADPCM codec. Algorithm follows Croc2_asf2raw.exe, and the waveform - * looks almost correct, but should reverse engineer asfcodec.adl (DLL) for accuracy. */ +/* Decodes Argonaut's ASF ADPCM codec, used in some of their PC games. + * Algorithm should be accurate (reverse engineered from asfcodec.adl DLL). */ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { off_t frame_offset; int i, frames_in, sample_count = 0; size_t bytes_per_frame, samples_per_frame; - uint32_t shift, mode; + uint8_t shift, mode; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; @@ -17,12 +17,12 @@ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; - /* parse header */ + /* parse frame header */ frame_offset = stream->offset + bytes_per_frame*frames_in; - shift = (read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; - mode = (read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; + shift = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; + mode = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; - /* decoder nibbles */ + /* decode nibbles */ for (i = first_sample; i < first_sample + samples_to_do; i++) { int32_t new_sample; uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile); @@ -30,29 +30,20 @@ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, new_sample = i&1 ? /* high nibble first */ get_low_nibble_signed(nibbles): get_high_nibble_signed(nibbles); - new_sample = new_sample << shift; + /* move sample to upper nibble, then shift + 2 (IOW: shift + 6) */ + new_sample = (new_sample << 4) << (shift + 2); - switch(mode) { - case 0x00: - new_sample = new_sample + hist1; - //new_sample = (new_sample + (hist1 << 6)) >> 6; /* maybe? */ - break; - - case 0x04: - new_sample = new_sample + hist1*2 - hist2; - //new_sample = (new_sample + (hist1 << 7) - (hist2 << 6)) >> 6; /* maybe? */ - break; - - default: /* other modes (ex 0x02/09) seem only at last frame as 0 */ - //VGM_LOG("ASF: unknown mode %x at %lx\n", mode,frame_offset); - //new_sample = 0; /* maybe? */ - break; + /* mode is checked as a flag, so there are 2 modes only, but lower nibble + * may have other values at last frame (ex 0x02/09), could be control flags (loop related?) */ + if (mode & 0x4) { /* ~filters: 2, -1 */ + new_sample = (new_sample + (hist1 << 7) - (hist2 << 6)) >> 6; + } + else { /* ~filters: 1, 0 */ + new_sample = (new_sample + (hist1 << 6)) >> 6; } //new_sample = clamp16(new_sample); /* must not */ - new_sample = new_sample & 0xFFFF; /* probably unnecessary */ - - outbuf[sample_count] = new_sample; + outbuf[sample_count] = (int16_t)new_sample; sample_count += channelspacing; hist2 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index fad4c0532..420c450ab 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -4,13 +4,31 @@ #include +/* opaque struct */ +struct atrac9_codec_data { + uint8_t *data_buffer; + size_t data_buffer_size; + + sample *sample_buffer; + size_t samples_filled; /* number of samples in the buffer */ + size_t samples_used; /* number of samples extracted from the buffer */ + + int samples_to_discard; + + atrac9_config config; + + void *handle; /* decoder handle */ + Atrac9CodecInfo info; /* decoder info */ +}; + + atrac9_codec_data *init_atrac9(atrac9_config *cfg) { int status; uint8_t config_data[4]; - Atrac9CodecInfo info = {0}; atrac9_codec_data *data = NULL; data = calloc(1, sizeof(atrac9_codec_data)); + if (!data) goto fail; data->handle = Atrac9GetHandle(); if (!data->handle) goto fail; @@ -19,20 +37,20 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) { status = Atrac9InitDecoder(data->handle, config_data); if (status < 0) goto fail; - status = Atrac9GetCodecInfo(data->handle, &info); + status = Atrac9GetCodecInfo(data->handle, &data->info); if (status < 0) goto fail; //;VGM_LOG("ATRAC9: config=%x, sf-size=%x, sub-frames=%i x %i samples\n", cfg->config_data, info.superframeSize, info.framesInSuperframe, info.frameSamples); - if (cfg->channels && cfg->channels != info.channels) { - VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, info.channels); + if (cfg->channels && cfg->channels != data->info.channels) { + VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, data->info.channels); goto fail; /* unknown multichannel layout */ } /* must hold at least one superframe and its samples */ - data->data_buffer_size = info.superframeSize; + data->data_buffer_size = data->info.superframeSize; data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size); - data->sample_buffer = calloc(sizeof(sample), info.channels * info.frameSamples * info.framesInSuperframe); + data->sample_buffer = calloc(sizeof(sample), data->info.channels * data->info.frameSamples * data->info.framesInSuperframe); data->samples_to_discard = cfg->encoder_delay; @@ -41,6 +59,7 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) { return data; fail: + free_atrac9(data); return NULL; } @@ -82,62 +101,25 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int bytes_used = 0; uint8_t *buffer = data->data_buffer; size_t bytes; - Atrac9CodecInfo info = {0}; data->samples_used = 0; /* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives * superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */ - status = Atrac9GetCodecInfo(data->handle, &info); - if (status < 0) goto decode_fail; - - - /* preadjust */ //todo improve - switch(data->config.type) { - case ATRAC9_XVAG: - /* PS4 (ex. The Last of Us) has a RIFF AT9 (can be ignored) instead of the first superframe. - * As subsongs do too, needs to be skipped here instead of adjusting start_offset */ - if (stream->offset == stream->channel_start_offset) { - if (read_32bitBE(stream->offset, stream->streamfile) == 0x00000000 /* padding before RIFF */ - && read_32bitBE(stream->offset + info.superframeSize - 0x08,stream->streamfile) == 0x64617461) { /* RIFF's "data" */ - stream->offset += info.superframeSize; - } - } - break; - default: - break; - } /* read one raw block (superframe) and advance offsets */ - bytes = read_streamfile(data->data_buffer,stream->offset, info.superframeSize,stream->streamfile); - if (bytes != data->data_buffer_size) { - VGM_LOG("ATRAC9: read %x vs expected %x bytes at %lx\n", bytes, info.superframeSize, stream->offset); - goto decode_fail; - } + bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile); + if (bytes != data->data_buffer_size) goto decode_fail; stream->offset += bytes; - /* postadjust */ //todo improve - switch(data->config.type) { - case ATRAC9_XVAG: - case ATRAC9_KMA9: - /* skip other subsong blocks */ - if (data->config.interleave_skip && ((stream->offset - stream->channel_start_offset) % data->config.interleave_skip == 0)) { - stream->offset += data->config.interleave_skip * (data->config.subsong_skip - 1); - } - break; - default: - break; - } - - /* decode all frames in the superframe block */ - for (iframe = 0; iframe < info.framesInSuperframe; iframe++) { + for (iframe = 0; iframe < data->info.framesInSuperframe; iframe++) { status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used); if (status < 0) goto decode_fail; buffer += bytes_used; - data->samples_filled += info.frameSamples; + data->samples_filled += data->info.frameSamples; } } } @@ -146,7 +128,7 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, decode_fail: /* on error just put some 0 samples */ - VGM_LOG("ATRAC9: decode fail at %lx, missing %i samples\n", stream->offset, (samples_to_do - samples_done)); + VGM_LOG("ATRAC9: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done)); memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); } @@ -175,7 +157,7 @@ void reset_atrac9(VGMSTREAM *vgmstream) { data->samples_used = 0; data->samples_filled = 0; - data->samples_to_discard = 0; + data->samples_to_discard = data->config.encoder_delay; return; @@ -189,12 +171,43 @@ void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { reset_atrac9(vgmstream); - data->samples_to_discard = num_sample; - data->samples_to_discard += data->config.encoder_delay; + /* find closest offset to desired sample, and samples to discard after that offset to reach loop */ + { + int32_t seek_sample = data->config.encoder_delay + num_sample; + off_t seek_offset; + int32_t seek_discard; + int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe; + size_t superframe_number, superframe_back; + + superframe_number = (seek_sample / superframe_samples); /* closest */ + + /* decoded frames affect each other slightly, so move offset back to make PCM stable + * and equivalent to a full discard loop */ + superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */ + if (superframe_back > superframe_number) + superframe_back = superframe_number; + + seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples); + seek_offset = (superframe_number - superframe_back) * data->info.superframeSize; + + data->samples_to_discard = seek_discard; /* already includes encoder delay */ + + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset; + } + +#if 0 + //old full discard loop + { + data->samples_to_discard = num_sample; + data->samples_to_discard += data->config.encoder_delay; + + /* loop offsets are set during decode; force them to stream start so discard works */ + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; + } +#endif - /* loop offsets are set during decode; force them to stream start so discard works */ - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; } void free_atrac9(atrac9_codec_data *data) { @@ -208,15 +221,39 @@ void free_atrac9(atrac9_codec_data *data) { size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) { - Atrac9CodecInfo info = {0}; - int status; + return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe); +} - status = Atrac9GetCodecInfo(data->handle, &info); - if (status < 0) goto fail; +#if 0 //not needed (for now) +int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_channels, size_t *out_frame_size) { + static const int sample_rate_table[16] = { + 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 44100, 48000, 64000, 88200, 96000,128000,176400,192000 + }; + static const int channel_table[8] = { + 1, 2, 2, 6, 8, 4, 0, 0 + }; - return bytes / info.superframeSize * (info.frameSamples * info.framesInSuperframe); + uint32_t sync = (atrac9_config >> 24) & 0xff; /* 8b */ + uint8_t sample_rate_index = (atrac9_config >> 20) & 0x0f; /* 4b */ + uint8_t channels_index = (atrac9_config >> 17) & 0x07; /* 3b */ + /* uint8_t validation bit = (atrac9_config >> 16) & 0x01; */ /* 1b */ + size_t frame_size = (atrac9_config >> 5) & 0x7FF; /* 11b */ + size_t superframe_index = (atrac9_config >> 3) & 0x3; /* 2b */ + /* uint8_t unused = (atrac9_config >> 0) & 0x7);*/ /* 3b */ + if (sync != 0xFE) + goto fail; + if (out_sample_rate) + *out_sample_rate = sample_rate_table[sample_rate_index]; + if (out_channels) + *out_channels = channel_table[channels_index]; + if (out_frame_size) + *out_frame_size = (frame_size+1) * (1 << superframe_index); + + return 1; fail: return 0; } #endif +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c.orig b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c.orig new file mode 100644 index 000000000..d46a7c433 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c.orig @@ -0,0 +1,259 @@ +#include "coding.h" + +#ifdef VGM_USE_ATRAC9 +#include "libatrac9.h" + + +/* opaque struct */ +struct atrac9_codec_data { + uint8_t *data_buffer; + size_t data_buffer_size; + + sample *sample_buffer; + size_t samples_filled; /* number of samples in the buffer */ + size_t samples_used; /* number of samples extracted from the buffer */ + + int samples_to_discard; + + atrac9_config config; + + void *handle; /* decoder handle */ + Atrac9CodecInfo info; /* decoder info */ +}; + + +atrac9_codec_data *init_atrac9(atrac9_config *cfg) { + int status; + uint8_t config_data[4]; + atrac9_codec_data *data = NULL; + + data = calloc(1, sizeof(atrac9_codec_data)); + if (!data) goto fail; + + data->handle = Atrac9GetHandle(); + if (!data->handle) goto fail; + + put_32bitBE(config_data, cfg->config_data); + status = Atrac9InitDecoder(data->handle, config_data); + if (status < 0) goto fail; + + status = Atrac9GetCodecInfo(data->handle, &data->info); + if (status < 0) goto fail; + //;VGM_LOG("ATRAC9: config=%x, sf-size=%x, sub-frames=%i x %i samples\n", cfg->config_data, info.superframeSize, info.framesInSuperframe, info.frameSamples); + + if (cfg->channels && cfg->channels != data->info.channels) { + VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, data->info.channels); + goto fail; /* unknown multichannel layout */ + } + + + /* must hold at least one superframe and its samples */ + data->data_buffer_size = data->info.superframeSize; + data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size); + data->sample_buffer = calloc(sizeof(sample), data->info.channels * data->info.frameSamples * data->info.framesInSuperframe); + + data->samples_to_discard = cfg->encoder_delay; + + memcpy(&data->config, cfg, sizeof(atrac9_config)); + + return data; + +fail: + free_atrac9(data); + return NULL; +} + +void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; + atrac9_codec_data * data = vgmstream->codec_data; + int samples_done = 0; + + + while (samples_done < samples_to_do) { + + if (data->samples_filled) { /* consume samples */ + int samples_to_get = data->samples_filled; + + if (data->samples_to_discard) { + /* discard samples for looping */ + if (samples_to_get > data->samples_to_discard) + samples_to_get = data->samples_to_discard; + data->samples_to_discard -= samples_to_get; + } + else { + /* get max samples and copy */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + + memcpy(outbuf + samples_done*channels, + data->sample_buffer + data->samples_used*channels, + samples_to_get*channels * sizeof(sample)); + + samples_done += samples_to_get; + } + + /* mark consumed samples */ + data->samples_used += samples_to_get; + data->samples_filled -= samples_to_get; + } + else { /* decode data */ + int iframe, status; + int bytes_used = 0; + uint8_t *buffer = data->data_buffer; + size_t bytes; + + data->samples_used = 0; + + /* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives + * superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */ + + /* read one raw block (superframe) and advance offsets */ + bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile); + if (bytes != data->data_buffer_size) goto decode_fail; + + stream->offset += bytes; + + /* decode all frames in the superframe block */ + for (iframe = 0; iframe < data->info.framesInSuperframe; iframe++) { + status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used); + if (status < 0) goto decode_fail; + + buffer += bytes_used; + data->samples_filled += data->info.frameSamples; + } + } + } + + return; + +decode_fail: + /* on error just put some 0 samples */ + VGM_LOG("ATRAC9: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done)); + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); +} + +void reset_atrac9(VGMSTREAM *vgmstream) { + atrac9_codec_data *data = vgmstream->codec_data; + if (!data) return; + + if (!data->handle) + goto fail; + +#if 0 + /* reopen/flush, not needed as superframes decode separatedly and there is no carried state */ + { + int status; + uint8_t config_data[4]; + + Atrac9ReleaseHandle(data->handle); + data->handle = Atrac9GetHandle(); + if (!data->handle) goto fail; + + put_32bitBE(config_data, data->config.config_data); + status = Atrac9InitDecoder(data->handle, config_data); + if (status < 0) goto fail; + } +#endif + + data->samples_used = 0; + data->samples_filled = 0; + data->samples_to_discard = data->config.encoder_delay; + + return; + +fail: + return; /* decode calls should fail... */ +} + +void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { + atrac9_codec_data *data = vgmstream->codec_data; + if (!data) return; + + reset_atrac9(vgmstream); + + /* find closest offset to desired sample, and samples to discard after that offset to reach loop */ + { + int32_t seek_sample = data->config.encoder_delay + num_sample; + off_t seek_offset; + int32_t seek_discard; + int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe; + size_t superframe_number, superframe_back; + + superframe_number = (seek_sample / superframe_samples); /* closest */ + + /* decoded frames affect each other slightly, so move offset back to make PCM stable + * and equivalent to a full discard loop */ + superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */ + if (superframe_back > superframe_number) + superframe_back = superframe_number; + + seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples); + seek_offset = (superframe_number - superframe_back) * data->info.superframeSize; + + data->samples_to_discard = seek_discard; /* already includes encoder delay */ + + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset; + } + +#if 0 + //old full discard loop + { + data->samples_to_discard = num_sample; + data->samples_to_discard += data->config.encoder_delay; + + /* loop offsets are set during decode; force them to stream start so discard works */ + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; + } +#endif + +} + +void free_atrac9(atrac9_codec_data *data) { + if (!data) return; + + if (data->handle) Atrac9ReleaseHandle(data->handle); + free(data->data_buffer); + free(data->sample_buffer); + free(data); +} + + +size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) { + return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe); +} + +#if 0 //not needed (for now) +int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_channels, size_t *out_frame_size) { + static const int sample_rate_table[16] = { + 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 44100, 48000, 64000, 88200, 96000,128000,176400,192000 + }; + static const int channel_table[8] = { + 1, 2, 2, 6, 8, 4, 0, 0 + }; + + uint32_t sync = (atrac9_config >> 24) & 0xff; /* 8b */ + uint8_t sample_rate_index = (atrac9_config >> 20) & 0x0f; /* 4b */ + uint8_t channels_index = (atrac9_config >> 17) & 0x07; /* 3b */ + /* uint8_t validation bit = (atrac9_config >> 16) & 0x01; */ /* 1b */ + size_t frame_size = (atrac9_config >> 5) & 0x7FF; /* 11b */ + size_t superframe_index = (atrac9_config >> 3) & 0x3; /* 2b */ + /* uint8_t unused = (atrac9_config >> 0) & 0x7);*/ /* 3b */ + + if (sync != 0xFE) + goto fail; + if (out_sample_rate) + *out_sample_rate = sample_rate_table[sample_rate_index]; + if (out_channels) + *out_channels = channel_table[channels_index]; + if (out_frame_size) + *out_frame_size = (frame_size+1) * (1 << superframe_index); + + return 1; +fail: + return 0; +} +#endif +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c new file mode 100644 index 000000000..dbf3ba2fc --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c @@ -0,0 +1,231 @@ +#include "coding.h" + +#ifdef VGM_USE_CELT +#include "celt/celt_fsb.h" + +#define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */ +#define FSB_CELT_0_11_0_VERSION 0x80000010 /* libcelt-0.6.1 */ +#define FSB_CELT_SAMPLES_PER_FRAME 512 +#define FSB_CELT_INTERNAL_SAMPLE_RATE 44100 +#define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */ + +/* opaque struct */ +struct celt_codec_data { + sample *buffer; + + sample *sample_buffer; + size_t samples_filled; /* number of samples in the buffer */ + size_t samples_used; /* number of samples extracted from the buffer */ + + int samples_to_discard; + + int channel_mode; + celt_lib_t version; + void *mode_handle; + void *decoder_handle; +}; + + +/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs). + * FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */ + +celt_codec_data *init_celt_fsb(int channels, celt_lib_t version) { + int error = 0, lib_version = 0; + celt_codec_data *data = NULL; + + + data = calloc(1, sizeof(celt_codec_data)); + if (!data) goto fail; + + data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */ + data->version = version; + + switch(data->version) { + case CELT_0_06_1: /* older FSB4 (FMOD ~4.33) */ + data->mode_handle = celt_0061_mode_create(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error); + if (!data->mode_handle || error != CELT_OK) goto fail; + + error = celt_0061_mode_info(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); + if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail; + + data->decoder_handle = celt_0061_decoder_create(data->mode_handle); + if (!data->decoder_handle) goto fail; + break; + + case CELT_0_11_0: /* newer FSB4 (FMOD ~4.34), FSB5 */ + data->mode_handle = celt_0110_mode_create(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */ + if (!data->mode_handle || error != CELT_OK) goto fail; + + error = celt_0110_mode_info(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); + if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail; + + data->decoder_handle = celt_0110_decoder_create_custom(data->mode_handle, data->channel_mode, &error); + if (!data->decoder_handle || error != CELT_OK) goto fail; + break; + + default: + goto fail; + } + + data->sample_buffer = calloc(sizeof(sample), data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME); + if (!data->sample_buffer) goto fail; + /* there is ~128 samples of encoder delay, but FMOD DLLs don't discard it? */ + + return data; + +fail: + free_celt_fsb(data); + return NULL; +} + + +void decode_celt_fsb(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; + celt_codec_data * data = vgmstream->codec_data; + int samples_done = 0; + + + while (samples_done < samples_to_do) { + + if (data->samples_filled) { /* consume samples */ + int samples_to_get = data->samples_filled; + + if (data->samples_to_discard) { + /* discard samples for looping */ + if (samples_to_get > data->samples_to_discard) + samples_to_get = data->samples_to_discard; + data->samples_to_discard -= samples_to_get; + } + else { + /* get max samples and copy */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + + memcpy(outbuf + samples_done*channels, + data->sample_buffer + data->samples_used*channels, + samples_to_get*channels * sizeof(sample)); + + samples_done += samples_to_get; + } + + /* mark consumed samples */ + data->samples_used += samples_to_get; + data->samples_filled -= samples_to_get; + } + else { /* decode data */ + int status; + uint8_t data_buffer[FSB_CELT_MAX_DATA_SIZE] = {0}; + size_t bytes, frame_size; + + + data->samples_used = 0; + + /* FSB DLLs do seem to check this fixed value */ + if (read_32bitBE(stream->offset+0x00,stream->streamfile) != 0x17C30DF3) { + goto decode_fail; + } + + frame_size = read_32bitLE(stream->offset+0x04,stream->streamfile); + if (frame_size > FSB_CELT_MAX_DATA_SIZE) { + goto decode_fail; + } + + /* read and decode one raw block and advance offsets */ + bytes = read_streamfile(data_buffer,stream->offset+0x08, frame_size,stream->streamfile); + if (bytes != frame_size) goto decode_fail; + + switch(data->version) { + case CELT_0_06_1: + status = celt_0061_decode(data->decoder_handle, data_buffer,bytes, data->sample_buffer); + break; + + case CELT_0_11_0: + status = celt_0110_decode(data->decoder_handle, data_buffer,bytes, data->sample_buffer, FSB_CELT_SAMPLES_PER_FRAME); + break; + + default: + goto decode_fail; + } + if (status != CELT_OK) goto decode_fail; + + stream->offset += 0x04+0x04+frame_size; + data->samples_filled += FSB_CELT_SAMPLES_PER_FRAME; + } + } + + return; + +decode_fail: + /* on error just put some 0 samples */ + VGM_LOG("CELT: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done)); + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); +} + +void reset_celt_fsb(VGMSTREAM *vgmstream) { + celt_codec_data *data = vgmstream->codec_data; + if (!data) return; + + /* recreate decoder (mode should not change) */ + switch(data->version) { + case CELT_0_06_1: + if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle); + + data->decoder_handle = celt_0061_decoder_create(data->mode_handle); + if (!data->decoder_handle) goto fail; + break; + + case CELT_0_11_0: + if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle); + + data->decoder_handle = celt_0110_decoder_create_custom(data->mode_handle, data->channel_mode, NULL); + if (!data->decoder_handle) goto fail; + break; + + default: + goto fail; + } + + data->samples_used = 0; + data->samples_filled = 0; + data->samples_to_discard = 0; + + return; +fail: + return; /* decode calls should fail... */ +} + +void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample) { + celt_codec_data *data = vgmstream->codec_data; + if (!data) return; + + reset_celt_fsb(vgmstream); + + data->samples_to_discard = num_sample; + + /* loop offsets are set during decode; force them to stream start so discard works */ + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; +} + +void free_celt_fsb(celt_codec_data *data) { + if (!data) return; + + switch(data->version) { + case CELT_0_06_1: + if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle); + if (data->mode_handle) celt_0061_mode_destroy(data->mode_handle); + break; + + case CELT_0_11_0: + if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle); + if (data->mode_handle) celt_0110_mode_destroy(data->mode_handle); + break; + + default: + break; + } + + free(data->sample_buffer); + free(data); +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index b9fb13d1e..977cfdf0a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -15,24 +15,28 @@ void decode_g721(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, 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(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); +void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_alp_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + +void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); + +void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_xbox_ima_mch(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +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_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); 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); @@ -58,14 +62,14 @@ void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* pcm_decoder */ -void decode_pcm16LE(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_pcm16le(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); void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -73,15 +77,18 @@ void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac 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(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags); 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); +int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); +int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); 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); +/* psv_decoder */ +void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* xa_decoder */ -void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked); /* ea_xa_decoder */ @@ -103,9 +110,9 @@ 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 */ -acm_codec_data *init_acm(); +acm_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number); void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing); -void reset_acm(VGMSTREAM *vgmstream); +void reset_acm(acm_codec_data *data); void free_acm(acm_codec_data *data); /* nwa_decoder */ @@ -113,7 +120,8 @@ void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do); /* msadpcm_decoder */ void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do); -void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do); +void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); long msadpcm_bytes_to_samples(long bytes, int block_size, int channels); /* yamaha_decoder */ @@ -129,14 +137,14 @@ void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels /* l5_555_decoder */ void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -/* SASSC_decoder */ -void decode_SASSC(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +/* sassc_decoder */ +void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* lsf_decode */ void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* mtaf_decoder */ -void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int channels); +void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* mta2_decoder */ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); @@ -150,19 +158,29 @@ void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin /* asf_decoder */ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +/* xmd_decoder */ +void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); + +/* derf_decoder */ +void decode_derf(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); +ea_mt_codec_data *init_ea_mt(int channels, int type); +ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets); +void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); void reset_ea_mt(VGMSTREAM * vgmstream); void flush_ea_mt(VGMSTREAM *vgmstream); void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample); -void free_ea_mt(ea_mt_codec_data *data); +void free_ea_mt(ea_mt_codec_data *data, int channels); /* hca_decoder */ -void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); -void reset_hca(VGMSTREAM *vgmstream); -void loop_hca(VGMSTREAM *vgmstream); +hca_codec_data *init_hca(STREAMFILE *streamFile); +void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do); +void reset_hca(hca_codec_data * data); +void loop_hca(hca_codec_data * data); void free_hca(hca_codec_data * data); +int test_hca_key(hca_codec_data * data, unsigned long long keycode); #ifdef VGM_USE_VORBIS /* ogg_vorbis_decoder */ @@ -204,8 +222,8 @@ void free_g7221(VGMSTREAM *vgmstream); /* g719_decoder */ g719_codec_data *init_g719(int channel_count, int frame_size); void decode_g719(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); -void reset_g719(VGMSTREAM *vgmstream); -void free_g719(VGMSTREAM *vgmstream); +void reset_g719(g719_codec_data * data, int channels); +void free_g719(g719_codec_data * data, int channels); #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) @@ -233,13 +251,22 @@ void reset_atrac9(VGMSTREAM *vgmstream); void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample); void free_atrac9(atrac9_codec_data *data); size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data); +//int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_channels, size_t *out_frame_size); +#endif + +#ifdef VGM_USE_CELT +/* celt_fsb_decoder */ +celt_codec_data *init_celt_fsb(int channels, celt_lib_t version); +void decode_celt_fsb(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels); +void reset_celt_fsb(VGMSTREAM *vgmstream); +void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample); +void free_celt_fsb(celt_codec_data *data); #endif #ifdef VGM_USE_FFMPEG /* ffmpeg_decoder */ ffmpeg_codec_data *init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); ffmpeg_codec_data *init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size); -ffmpeg_codec_data *init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, ffmpeg_custom_config * config); void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels); void reset_ffmpeg(VGMSTREAM *vgmstream); @@ -248,14 +275,16 @@ void free_ffmpeg(ffmpeg_codec_data *data); void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); - +/* ffmpeg_decoder_custom_opus.c (helper-things) */ +ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); size_t ffmpeg_make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate); -size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t real_size, STREAMFILE *streamFile); - size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile); +size_t ue4_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile); #endif + /* coding_utils */ int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, size_t chunk_size, uint16_t codec); int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay); @@ -296,6 +325,7 @@ void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_ali void xma1_parse_fmt_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * loop_start_b, int32_t * loop_end_b, int32_t * loop_subframe, int be); void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int * loop_flag, int32_t * out_num_samples, int32_t * out_loop_start_sample, int32_t * out_loop_end_sample, int be); void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample); +int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset); size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align); size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c index dcdacfd44..aa5bb57c8 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c @@ -467,19 +467,19 @@ static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, i /* full packet skip, no new frames start in this packet (prev frames can end here) */ if (packet_skip_count == 0x7FF) { /* XMA1, 11b */ - VGM_LOG("MS_SAMPLES: XMA1 full packet_skip %i at 0x%lx\n", packet_skip_count, (off_t)offset_b/8); + VGM_LOG("MS_SAMPLES: XMA1 full packet_skip %i at 0x%"PRIx64"\n", packet_skip_count, (off64_t)offset_b/8); packet_skip_count = 0; continue; } else if (packet_skip_count == 0xFF) { /* XMA2, 8b*/ - VGM_LOG("MS_SAMPLES: XMA2 full packet_skip %i at 0x%lx\n", packet_skip_count, (off_t)offset_b/8); + VGM_LOG("MS_SAMPLES: XMA2 full packet_skip %i at 0x%"PRIx64"\n", packet_skip_count, (off64_t)offset_b/8); packet_skip_count = 0; continue; } offset += packet_size * (packet_skip_count); /* skip packets not owned by the first stream, since we only need samples from it */ /* unusual but not impossible, as the encoder can interleave packets in any way */ - VGM_ASSERT(packet_skip_count > 10, "MS_SAMPLES: found big packet skip %i at 0x%lx\n", packet_skip_count, (off_t)offset_b/8); + VGM_ASSERT(packet_skip_count > 10, "MS_SAMPLES: found big packet skip %i at 0x%"PRIx64"\n", packet_skip_count, (off64_t)offset_b/8); packet_offset_b = header_size_b + first_frame_b; @@ -535,7 +535,7 @@ static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, i frame_offset_b += 1; if (flag) { int new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile); - VGM_LOG("MS_SAMPLES: start_skip %i at 0x%lx (bit 0x%lx)\n", new_skip, (off_t)frame_offset_b/8, (off_t)frame_offset_b); + VGM_LOG("MS_SAMPLES: start_skip %i at 0x%"PRIx64" (bit 0x%"PRIx64")\n", new_skip, (off64_t)frame_offset_b/8, (off64_t)frame_offset_b); VGM_ASSERT(start_skip, "MS_SAMPLES: more than one start_skip (%i)\n", new_skip); //ignore, happens due to incorrect tilehdr_size frame_offset_b += 10; @@ -551,7 +551,7 @@ static void ms_audio_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, i frame_offset_b += 1; if (flag) { int new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile); - VGM_LOG("MS_SAMPLES: end_skip %i at 0x%lx (bit 0x%lx)\n", new_skip, (off_t)frame_offset_b/8, (off_t)frame_offset_b); + VGM_LOG("MS_SAMPLES: end_skip %i at 0x%"PRIx64" (bit 0x%"PRIx64")\n", new_skip, (off64_t)frame_offset_b/8, (off64_t)frame_offset_b); VGM_ASSERT(end_skip, "MS_SAMPLES: more than one end_skip (%i)\n", new_skip);//ignore, happens due to incorrect tilehdr_size frame_offset_b += 10; @@ -804,6 +804,23 @@ void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * cha if (channels) *channels = total_channels; } +/* manually read from "fact" chunk */ +int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset) { + off_t chunk_offset; + size_t chunk_size, fact_skip_samples = 0; + if (!find_chunk_le(streamFile, 0x66616374, start_offset + 0x0c, 0, &chunk_offset, &chunk_size)) /* find "fact" */ + goto fail; + if (chunk_size == 0x8) { + fact_skip_samples = read_32bitLE(chunk_offset + 0x4, streamFile); + } + else if (chunk_size == 0xc) { + fact_skip_samples = read_32bitLE(chunk_offset + 0x8, streamFile); + } + + return fact_skip_samples; +fail: + return 0; /* meh */ +} /* ******************************************** */ /* OTHER STUFF */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/derf_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/derf_decoder.c new file mode 100644 index 000000000..08cc55d3d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/derf_decoder.c @@ -0,0 +1,43 @@ +#include "coding.h" + + +/* IMA table plus seven extra steps at the beginning */ +static const int derf_steps[96] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 16, + 17, 19, 21, 23, 25, 28, 31, 34, + 37, 41, 45, 50, 55, 60, 66, 73, + 80, 88, 97, 107, 118, 130, 143, 157, + 173, 190, 209, 230, 253, 279, 307, 337, + 371, 408, 449, 494, 544, 598, 658, 724, + 796, 876, 963, 1060, 1166, 1282, 1411, 1552, + 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, + 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, + 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, + 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767, +}; + +/* Xilam DERF DPCM for Stupid Invaders (PC), decompiled from the exe */ +void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i, sample_pos = 0, index; + int32_t hist = stream->adpcm_history1_32; + off_t frame_offset = stream->offset; /* frame size is 1 */ + + for(i = first_sample; i < first_sample + samples_to_do; i++) { + uint8_t code = (uint8_t)read_8bit(frame_offset+i,stream->streamfile); + + /* original exe doesn't clamp the index, so presumably codes can't over it */ + index = code & 0x7f; + if (index > 95) index = 95; + + if (code & 0x80) + hist -= derf_steps[index]; + else + hist += derf_steps[index]; + + outbuf[sample_pos] = clamp16(hist); + sample_pos += channelspacing; + } + + stream->adpcm_history1_32 = hist; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c index 1ecc4106d..23ecbc68b 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder.c @@ -1,17 +1,15 @@ #include "coding.h" -/* Decodes EA MicroTalk (speech codec) using a copied utkencode lib. +#include "ea_mt_decoder_utk.h" + +/* Decodes EA MicroTalk (speech codec) using utkencode lib (slightly modified for vgmstream). * EA separates MT10:1 and MT5:1 (bigger frames), but apparently are the same * with different encoding parameters. Later revisions may have PCM blocks (rare). * * Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode * Info: http://wiki.niotso.org/UTK - * - * The following tries to follow the original code as close as possible, with minimal changes for vgmstream */ -/* ************************************************************************************************* */ -#define UTK_BUFFER_SIZE 0x4000 //#define UTK_MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24)) #define UTK_ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f)) @@ -19,561 +17,190 @@ #define UTK_MAX(x,y) ((x)>(y)?(x):(y)) #define UTK_CLAMP(x,min,max) UTK_MIN(UTK_MAX(x,min),max) +#define UTK_BUFFER_SIZE 0x1000 -/* Note: This struct assumes a member alignment of 4 bytes. -** This matters when pitch_lag > 216 on the first subframe of any given frame. */ -typedef struct UTKContext { - uint8_t buffer[UTK_BUFFER_SIZE]; //vgmstream extra - STREAMFILE * streamfile; //vgmstream extra - off_t offset; //vgmstream extra - int samples_filled; //vgmstream extra - //FILE *fp; //vgmstream extra - const uint8_t *ptr, *end; - int parsed_header; - unsigned int bits_value; - int bits_count; - int reduced_bw; - int multipulse_thresh; - float fixed_gains[64]; - float rc[12]; - float synth_history[12]; - float adapt_cb[324]; - float decompressed_frame[432]; -} UTKContext; +struct ea_mt_codec_data { + STREAMFILE *streamfile; + uint8_t buffer[UTK_BUFFER_SIZE]; + off_t offset; + off_t loop_offset; + int loop_sample; -enum { - MDL_NORMAL = 0, - MDL_LARGEPULSE = 1 + int pcm_blocks; + int samples_filled; + int samples_used; + int samples_done; + int samples_discard; + void* utk_context; }; -static const float utk_rc_table[64] = { - 0.0f, - -.99677598476409912109375f, -.99032700061798095703125f, -.983879029750823974609375f, -.977430999279022216796875f, - -.970982015132904052734375f, -.964533984661102294921875f, -.958085000514984130859375f, -.9516370296478271484375f, - -.930754005908966064453125f, -.904959976673126220703125f, -.879167020320892333984375f, -.853372991085052490234375f, - -.827579021453857421875f, -.801786005496978759765625f, -.775991976261138916015625f, -.75019800662994384765625f, - -.724404990673065185546875f, -.6986110210418701171875f, -.6706349849700927734375f, -.61904799938201904296875f, - -.567460000514984130859375f, -.515873014926910400390625f, -.4642859995365142822265625f, -.4126980006694793701171875f, - -.361110985279083251953125f, -.309523999691009521484375f, -.257937014102935791015625f, -.20634900033473968505859375f, - -.1547619998455047607421875f, -.10317499935626983642578125f, -.05158700048923492431640625f, - 0.0f, - +.05158700048923492431640625f, +.10317499935626983642578125f, +.1547619998455047607421875f, +.20634900033473968505859375f, - +.257937014102935791015625f, +.309523999691009521484375f, +.361110985279083251953125f, +.4126980006694793701171875f, - +.4642859995365142822265625f, +.515873014926910400390625f, +.567460000514984130859375f, +.61904799938201904296875f, - +.6706349849700927734375f, +.6986110210418701171875f, +.724404990673065185546875f, +.75019800662994384765625f, - +.775991976261138916015625f, +.801786005496978759765625f, +.827579021453857421875f, +.853372991085052490234375f, - +.879167020320892333984375f, +.904959976673126220703125f, +.930754005908966064453125f, +.9516370296478271484375f, - +.958085000514984130859375f, +.964533984661102294921875f, +.970982015132904052734375f, +.977430999279022216796875f, - +.983879029750823974609375f, +.99032700061798095703125f, +.99677598476409912109375 -}; +static size_t ea_mt_read_callback(void *dest, int size, void *arg); -static const uint8_t utk_codebooks[2][256] = { - { /* normal model */ - 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, - 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21, - 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, - 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 25, - 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, - 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22, - 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, - 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 0, - 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, - 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21, - 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, - 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 26, - 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, - 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22, - 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, - 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 2 - }, { /* large-pulse model */ - 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, - 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27, - 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, - 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1, - 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, - 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28, - 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, - 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3, - 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, - 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27, - 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, - 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1, - 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, - 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28, - 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, - 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3 - } -}; - -static const struct { - int next_model; - int code_size; - float pulse_value; -} utk_commands[29] = { - {MDL_LARGEPULSE, 8, 0.0f}, - {MDL_LARGEPULSE, 7, 0.0f}, - {MDL_NORMAL, 8, 0.0f}, - {MDL_NORMAL, 7, 0.0f}, - {MDL_NORMAL, 2, 0.0f}, - {MDL_NORMAL, 2, -1.0f}, - {MDL_NORMAL, 2, +1.0f}, - {MDL_NORMAL, 3, -1.0f}, - {MDL_NORMAL, 3, +1.0f}, - {MDL_LARGEPULSE, 4, -2.0f}, - {MDL_LARGEPULSE, 4, +2.0f}, - {MDL_LARGEPULSE, 3, -2.0f}, - {MDL_LARGEPULSE, 3, +2.0f}, - {MDL_LARGEPULSE, 5, -3.0f}, - {MDL_LARGEPULSE, 5, +3.0f}, - {MDL_LARGEPULSE, 4, -3.0f}, - {MDL_LARGEPULSE, 4, +3.0f}, - {MDL_LARGEPULSE, 6, -4.0f}, - {MDL_LARGEPULSE, 6, +4.0f}, - {MDL_LARGEPULSE, 5, -4.0f}, - {MDL_LARGEPULSE, 5, +4.0f}, - {MDL_LARGEPULSE, 7, -5.0f}, - {MDL_LARGEPULSE, 7, +5.0f}, - {MDL_LARGEPULSE, 6, -5.0f}, - {MDL_LARGEPULSE, 6, +5.0f}, - {MDL_LARGEPULSE, 8, -6.0f}, - {MDL_LARGEPULSE, 8, +6.0f}, - {MDL_LARGEPULSE, 7, -6.0f}, - {MDL_LARGEPULSE, 7, +6.0f} -}; - -static int utk_read_byte(UTKContext *ctx) -{ - if (ctx->ptr < ctx->end) - return *ctx->ptr++; - - //vgmstream extra: this reads from FILE if static buffer was exhausted, now from a context buffer and STREAMFILE instead - if (ctx->streamfile) { //if (ctx->fp) { - //static uint8_t buffer[4096]; - //size_t bytes_copied = fread(buffer, 1, sizeof(buffer), ctx->fp); - size_t bytes_copied = read_streamfile(ctx->buffer, ctx->offset, sizeof(ctx->buffer), ctx->streamfile); - - ctx->offset += bytes_copied; - if (bytes_copied > 0 && bytes_copied <= sizeof(ctx->buffer)) { - ctx->ptr = ctx->buffer; - ctx->end = ctx->buffer + bytes_copied; - return *ctx->ptr++; - } - } - - return 0; +ea_mt_codec_data *init_ea_mt(int channels, int pcm_blocks) { + return init_ea_mt_loops(channels, pcm_blocks, 0, NULL); } -static int16_t utk_read_i16(UTKContext *ctx) -{ - int x = utk_read_byte(ctx); - x = (x << 8) | utk_read_byte(ctx); - return x; -} - -static int utk_read_bits(UTKContext *ctx, int count) -{ - int ret = ctx->bits_value & ((1 << count) - 1); - ctx->bits_value >>= count; - ctx->bits_count -= count; - - if (ctx->bits_count < 8) { - /* read another byte */ - ctx->bits_value |= utk_read_byte(ctx) << ctx->bits_count; - ctx->bits_count += 8; - } - - return ret; -} - -static void utk_parse_header(UTKContext *ctx) -{ - int i; - float multiplier; - - ctx->reduced_bw = utk_read_bits(ctx, 1); - ctx->multipulse_thresh = 32 - utk_read_bits(ctx, 4); - ctx->fixed_gains[0] = 8.0f * (1 + utk_read_bits(ctx, 4)); - multiplier = 1.04f + utk_read_bits(ctx, 6)*0.001f; - - for (i = 1; i < 64; i++) - ctx->fixed_gains[i] = ctx->fixed_gains[i-1] * multiplier; -} - -static void utk_decode_excitation(UTKContext *ctx, int use_multipulse, float *out, int stride) -{ - int i; - - if (use_multipulse) { - /* multi-pulse model: n pulses are coded explicitly; the rest are zero */ - int model, cmd; - model = 0; - i = 0; - while (i < 108) { - cmd = utk_codebooks[model][ctx->bits_value & 0xff]; - model = utk_commands[cmd].next_model; - utk_read_bits(ctx, utk_commands[cmd].code_size); - - if (cmd > 3) { - /* insert a pulse with magnitude <= 6.0f */ - out[i] = utk_commands[cmd].pulse_value; - i += stride; - } else if (cmd > 1) { - /* insert between 7 and 70 zeros */ - int count = 7 + utk_read_bits(ctx, 6); - if (i + count * stride > 108) - count = (108 - i)/stride; - - while (count > 0) { - out[i] = 0.0f; - i += stride; - count--; - } - } else { - /* insert a pulse with magnitude >= 7.0f */ - int x = 7; - - while (utk_read_bits(ctx, 1)) - x++; - - if (!utk_read_bits(ctx, 1)) - x *= -1; - - out[i] = (float)x; - i += stride; - } - } - } else { - /* RELP model: entire residual (excitation) signal is coded explicitly */ - i = 0; - while (i < 108) { - if (!utk_read_bits(ctx, 1)) - out[i] = 0.0f; - else if (!utk_read_bits(ctx, 1)) - out[i] = -2.0f; - else - out[i] = 2.0f; - - i += stride; - } - } -} - -static void rc_to_lpc(const float *rc, float *lpc) -{ - int i, j; - float tmp1[12]; - float tmp2[12]; - - for (i = 10; i >= 0; i--) - tmp2[1+i] = rc[i]; - - tmp2[0] = 1.0f; - - for (i = 0; i < 12; i++) { - float x = -tmp2[11] * rc[11]; - - for (j = 10; j >= 0; j--) { - x -= tmp2[j] * rc[j]; - tmp2[j+1] = x * rc[j] + tmp2[j]; - } - - tmp1[i] = tmp2[0] = x; - - for (j = 0; j < i; j++) - x -= tmp1[i-1-j] * lpc[j]; - - lpc[i] = x; - } -} - -static void utk_lp_synthesis_filter(UTKContext *ctx, int offset, int num_blocks) -{ - int i, j, k; - float lpc[12]; - float *ptr = &ctx->decompressed_frame[offset]; - - rc_to_lpc(ctx->rc, lpc); - - for (i = 0; i < num_blocks; i++) { - for (j = 0; j < 12; j++) { - float x = *ptr; - - for (k = 0; k < j; k++) - x += lpc[k] * ctx->synth_history[k-j+12]; - for (; k < 12; k++) - x += lpc[k] * ctx->synth_history[k-j]; - - ctx->synth_history[11-j] = x; - *ptr++ = x; - } - } -} - -/* -** Public functions. -*/ - -static void utk_decode_frame(UTKContext *ctx) -{ - int i, j; - int use_multipulse = 0; - float excitation[5+108+5]; - float rc_delta[12]; - - if (!ctx->bits_count) { - ctx->bits_value = utk_read_byte(ctx); - ctx->bits_count = 8; - } - - if (!ctx->parsed_header) { - utk_parse_header(ctx); - ctx->parsed_header = 1; - } - - memset(&excitation[0], 0, 5*sizeof(float)); - memset(&excitation[5+108], 0, 5*sizeof(float)); - - /* read the reflection coefficients */ - for (i = 0; i < 12; i++) { - int idx; - if (i == 0) { - idx = utk_read_bits(ctx, 6); - if (idx < ctx->multipulse_thresh) - use_multipulse = 1; - } else if (i < 4) { - idx = utk_read_bits(ctx, 6); - } else { - idx = 16 + utk_read_bits(ctx, 5); - } - - rc_delta[i] = (utk_rc_table[idx] - ctx->rc[i])*0.25f; - } - - /* decode four subframes */ - for (i = 0; i < 4; i++) { - int pitch_lag = utk_read_bits(ctx, 8); - float pitch_gain = (float)utk_read_bits(ctx, 4)/15.0f; - float fixed_gain = ctx->fixed_gains[utk_read_bits(ctx, 6)]; - - if (!ctx->reduced_bw) { - utk_decode_excitation(ctx, use_multipulse, &excitation[5], 1); - } else { - /* residual (excitation) signal is encoded at reduced bandwidth */ - int align = utk_read_bits(ctx, 1); - int zero = utk_read_bits(ctx, 1); - - utk_decode_excitation(ctx, use_multipulse, &excitation[5+align], 2); - - if (zero) { - /* fill the remaining samples with zero - ** (spectrum is duplicated into high frequencies) */ - for (j = 0; j < 54; j++) - excitation[5+(1-align)+2*j] = 0.0f; - } else { - /* interpolate the remaining samples - ** (spectrum is low-pass filtered) */ - float *ptr = &excitation[5+(1-align)]; - for (j = 0; j < 108; j += 2) - ptr[j] = ptr[j-5] * 0.01803267933428287506103515625f - - ptr[j-3] * 0.114591561257839202880859375f - + ptr[j-1] * 0.597385942935943603515625f - + ptr[j+1] * 0.597385942935943603515625f - - ptr[j+3] * 0.114591561257839202880859375f - + ptr[j+5] * 0.01803267933428287506103515625f; - - /* scale by 0.5f to give the sinc impulse response unit energy */ - fixed_gain *= 0.5f; - } - } - - for (j = 0; j < 108; j++) - ctx->decompressed_frame[108*i+j] = fixed_gain * excitation[5+j] - + pitch_gain * ctx->adapt_cb[108*i+216-pitch_lag+j]; - } - - for (i = 0; i < 324; i++) - ctx->adapt_cb[i] = ctx->decompressed_frame[108+i]; - - for (i = 0; i < 4; i++) { - for (j = 0; j < 12; j++) - ctx->rc[j] += rc_delta[j]; - - utk_lp_synthesis_filter(ctx, 12*i, i < 3 ? 1 : 33); - } -} - -static void utk_init(UTKContext *ctx) -{ - memset(ctx, 0, sizeof(*ctx)); -} - -#if 0 //vgmstream extra: see flush_ea_mt -static void utk_set_fp(UTKContext *ctx, FILE *fp) -{ - ctx->fp = fp; - - /* reset the bit reader */ - ctx->bits_count = 0; -} - -static void utk_set_ptr(UTKContext *ctx, const uint8_t *ptr, const uint8_t *end) -{ - ctx->ptr = ptr; - ctx->end = end; - - /* reset the bit reader */ - ctx->bits_count = 0; -} -#endif - -/* -** MicroTalk Revision 3 decoding function. -*/ - -static void utk_rev3_decode_frame(UTKContext *ctx) -{ - int pcm_data_present = (utk_read_byte(ctx) == 0xee); - int i; - - utk_decode_frame(ctx); - - /* unread the last 8 bits and reset the bit reader */ - ctx->ptr--; - ctx->bits_count = 0; - - if (pcm_data_present) { - /* Overwrite n samples at a given offset in the decoded frame with - ** raw PCM data. */ - int offset = utk_read_i16(ctx); - int count = utk_read_i16(ctx); - - /* sx.exe does not do any bounds checking or clamping of these two - ** fields (see 004274D1 in sx.exe v3.01.01), which means a specially - ** crafted MT5:1 file can crash sx.exe. - ** We will throw an error instead. */ - if (offset < 0 || offset > 432) { - //fprintf(stderr, "error: invalid PCM offset %d\n", offset); - //exit(EXIT_FAILURE); - return; //vgmstream extra - } - if (count < 0 || count > 432 - offset) { - //fprintf(stderr, "error: invalid PCM count %d\n", count); - //exit(EXIT_FAILURE); - return; //vgmstream extra - } - - for (i = 0; i < count; i++) - ctx->decompressed_frame[offset+i] = (float)utk_read_i16(ctx); - } -} - -/* ************************************************************************************************* */ - -ea_mt_codec_data *init_ea_mt(int channel_count, int pcm_blocks) { +ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) { ea_mt_codec_data *data = NULL; int i; - data = calloc(channel_count, sizeof(ea_mt_codec_data)); + data = calloc(channels, sizeof(ea_mt_codec_data)); /* one decoder per channel */ if (!data) goto fail; - data->pcm_blocks = pcm_blocks; - data->utk_context_size = channel_count; + for (i = 0; i < channels; i++) { + data[i].utk_context = calloc(1, sizeof(UTKContext)); + if (!data[i].utk_context) goto fail; + utk_init(data[i].utk_context); - data->utk_context = calloc(channel_count, sizeof(UTKContext*)); - if (!data->utk_context) goto fail; + data[i].pcm_blocks = pcm_blocks; + data[i].loop_sample = loop_sample; + if (loop_offsets) + data[i].loop_offset = loop_offsets[i]; - for (i = 0; i < channel_count; i++) { - data->utk_context[i] = calloc(1, sizeof(UTKContext)); - if (!data->utk_context[i]) goto fail; - utk_init(data->utk_context[i]); + utk_set_callback(data[i].utk_context, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback); } return data; fail: - free_ea_mt(data); + free_ea_mt(data, channels); return NULL; } -void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) { + int i; ea_mt_codec_data *data = vgmstream->codec_data; - int i, sample_count = 0, frame_samples; - UTKContext* ctx = data->utk_context[channel]; + ea_mt_codec_data *ch_data = &data[channel]; + UTKContext* ctx = ch_data->utk_context; + int samples_done = 0; - /* Use the above decoder, which expects pointers to read data. Since EA-MT frames aren't - * byte-aligned, reading new buffer data is decided by the decoder. When decoding starts - * or a SCHl block changes flush_ea_mt must be called to reset the state. - * A bit hacky but would need some restructuring otherwise. */ - frame_samples = 432; - first_sample = first_sample % frame_samples; + while (samples_done < samples_to_do) { - /* don't decode again if we didn't consume the current frame. - * UTKContext saves the sample buffer, and can't re-decode a frame */ - if (!ctx->samples_filled) { - if (data->pcm_blocks) - utk_rev3_decode_frame(ctx); - else - utk_decode_frame(ctx); - ctx->samples_filled = 1; + if (ch_data->samples_filled) { + /* consume current frame */ + int samples_to_get = ch_data->samples_filled; + + /* don't go past loop, to reset decoder */ + if (ch_data->loop_sample > 0 && ch_data->samples_done < ch_data->loop_sample && + ch_data->samples_done + samples_to_get > ch_data->loop_sample) + samples_to_get = ch_data->loop_sample - ch_data->samples_done; + + if (ch_data->samples_discard) { + /* discard samples for looping */ + if (samples_to_get > ch_data->samples_discard) + samples_to_get = ch_data->samples_discard; + ch_data->samples_discard -= samples_to_get; + } + else { + /* get max samples and copy */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + + for (i = ch_data->samples_used; i < ch_data->samples_used + samples_to_get; i++) { + int pcm = UTK_ROUND(ctx->decompressed_frame[i]); + outbuf[0] = (int16_t)UTK_CLAMP(pcm, -32768, 32767); + outbuf += channelspacing; + } + + samples_done += samples_to_get; + } + + /* mark consumed samples */ + ch_data->samples_used += samples_to_get; + ch_data->samples_filled -= samples_to_get; + ch_data->samples_done += samples_to_get; + + /* Loops in EA-MT are done with fully separate intro/loop substreams. We must + * notify the decoder when a new substream begins (even with looping disabled). */ + if (ch_data->loop_sample > 0 && ch_data->samples_done == ch_data->loop_sample) { + ch_data->samples_filled = 0; + ch_data->samples_discard = 0; + + /* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */ + ch_data->offset = ch_data->loop_offset; + utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */ + utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */ + } + } + else { + /* new frame */ + if (ch_data->pcm_blocks) + utk_rev3_decode_frame(ctx); + else + utk_decode_frame(ctx); + + ch_data->samples_used = 0; + ch_data->samples_filled = 432; + } } - - /* copy samples */ - for (i = first_sample; i < first_sample+samples_to_do; i++) { - int x = UTK_ROUND(ctx->decompressed_frame[i]); - outbuf[sample_count] = (int16_t)UTK_CLAMP(x, -32768, 32767); - sample_count += channelspacing; - } - - if (i == frame_samples) - ctx->samples_filled = 0; } -static void flush_ea_mt_internal(VGMSTREAM *vgmstream, int is_start) { +static void flush_ea_mt_offsets(VGMSTREAM *vgmstream, int is_start, int samples_discard) { ea_mt_codec_data *data = vgmstream->codec_data; int i; - size_t bytes; if (!data) return; - /* the decoder needs to be notified when offsets change */ + + /* EA-MT frames are VBR (not byte-aligned?), so utk_decoder reads new buffer data automatically. + * When decoding starts or a SCHl block changes, flush_ea_mt must be called to reset the state. + * A bit hacky but would need some restructuring otherwise. */ + for (i = 0; i < vgmstream->channels; i++) { - UTKContext *ctx = data->utk_context[i]; + UTKContext* ctx = data[i].utk_context; - ctx->streamfile = vgmstream->ch[i].streamfile; - ctx->offset = is_start ? vgmstream->ch[i].channel_start_offset : vgmstream->ch[i].offset; - ctx->samples_filled = 0; + data[i].streamfile = vgmstream->ch[i].streamfile; /* maybe should keep its own STREAMFILE? */ + if (is_start) + data[i].offset = vgmstream->ch[i].channel_start_offset; + else + data[i].offset = vgmstream->ch[i].offset; + utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */ - bytes = read_streamfile(ctx->buffer,ctx->offset,sizeof(ctx->buffer),ctx->streamfile); - ctx->offset += sizeof(ctx->buffer); - ctx->ptr = ctx->buffer; - ctx->end = ctx->buffer + bytes; - ctx->bits_count = 0; + if (is_start) { + utk_reset(ctx); + ctx->parsed_header = 0; + data[i].samples_done = 0; + } + + data[i].samples_filled = 0; + data[i].samples_discard = samples_discard; } } void flush_ea_mt(VGMSTREAM *vgmstream) { - flush_ea_mt_internal(vgmstream, 0); + flush_ea_mt_offsets(vgmstream, 0, 0); } void reset_ea_mt(VGMSTREAM *vgmstream) { - flush_ea_mt_internal(vgmstream, 1); + flush_ea_mt_offsets(vgmstream, 1, 0); } void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample) { - flush_ea_mt_internal(vgmstream, 1); - //todo discard loop (though this should be adecuate as probably only uses full loops, if at all) + flush_ea_mt_offsets(vgmstream, 1, num_sample); } -void free_ea_mt(ea_mt_codec_data *data) { +void free_ea_mt(ea_mt_codec_data *data, int channels) { int i; if (!data) return; - for (i = 0; i < data->utk_context_size; i++) { - free(data->utk_context[i]); + for (i = 0; i < channels; i++) { + free(data[i].utk_context); } - free(data->utk_context); free(data); } + +/* ********************** */ + +static size_t ea_mt_read_callback(void *dest, int size, void *arg) { + ea_mt_codec_data *ch_data = arg; + int bytes_read; + + bytes_read = read_streamfile(dest,ch_data->offset,size,ch_data->streamfile); + ch_data->offset += bytes_read; + + return bytes_read; + +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder_utk.h b/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder_utk.h new file mode 100644 index 000000000..3034bc944 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder_utk.h @@ -0,0 +1,469 @@ +#ifndef _EA_MT_DECODER_UTK_H_ +#define _EA_MT_DECODER_UTK_H_ + +#include +#include + +/* Note: This struct assumes a member alignment of 4 bytes. +** This matters when pitch_lag > 216 on the first subframe of any given frame. */ +typedef struct UTKContext { + uint8_t *buffer; + size_t buffer_size; + void *arg; + size_t (*read_callback)(void *dest, int size, void *arg); + const uint8_t *ptr, *end; + + int parsed_header; + unsigned int bits_value; + int bits_count; + int reduced_bw; + int multipulse_thresh; + float fixed_gains[64]; + float rc[12]; + float synth_history[12]; + float adapt_cb[324]; + float decompressed_frame[432]; +} UTKContext; + +enum { + MDL_NORMAL = 0, + MDL_LARGEPULSE = 1 +}; + +static const float utk_rc_table[64] = { + +0.0f, + -.99677598476409912109375f, -.99032700061798095703125f, -.983879029750823974609375f, -.977430999279022216796875f, + -.970982015132904052734375f, -.964533984661102294921875f, -.958085000514984130859375f, -.9516370296478271484375f, + -.930754005908966064453125f, -.904959976673126220703125f, -.879167020320892333984375f, -.853372991085052490234375f, + -.827579021453857421875f, -.801786005496978759765625f, -.775991976261138916015625f, -.75019800662994384765625f, + -.724404990673065185546875f, -.6986110210418701171875f, -.6706349849700927734375f, -.61904799938201904296875f, + -.567460000514984130859375f, -.515873014926910400390625f, -.4642859995365142822265625f, -.4126980006694793701171875f, + -.361110985279083251953125f, -.309523999691009521484375f, -.257937014102935791015625f, -.20634900033473968505859375f, + -.1547619998455047607421875f, -.10317499935626983642578125f, -.05158700048923492431640625f, + +0.0f, + +.05158700048923492431640625f, +.10317499935626983642578125f, +.1547619998455047607421875f, +.20634900033473968505859375f, + +.257937014102935791015625f, +.309523999691009521484375f, +.361110985279083251953125f, +.4126980006694793701171875f, + +.4642859995365142822265625f, +.515873014926910400390625f, +.567460000514984130859375f, +.61904799938201904296875f, + +.6706349849700927734375f, +.6986110210418701171875f, +.724404990673065185546875f, +.75019800662994384765625f, + +.775991976261138916015625f, +.801786005496978759765625f, +.827579021453857421875f, +.853372991085052490234375f, + +.879167020320892333984375f, +.904959976673126220703125f, +.930754005908966064453125f, +.9516370296478271484375f, + +.958085000514984130859375f, +.964533984661102294921875f, +.970982015132904052734375f, +.977430999279022216796875f, + +.983879029750823974609375f, +.99032700061798095703125f, +.99677598476409912109375f +}; + +static const uint8_t utk_codebooks[2][256] = { + { /* normal model */ + 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, + 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21, + 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, + 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 25, + 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, + 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22, + 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, + 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 0, + 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, + 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21, + 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, + 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 26, + 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, + 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22, + 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, + 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 2 + }, { /* large-pulse model */ + 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, + 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27, + 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, + 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1, + 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, + 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28, + 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, + 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3, + 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, + 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27, + 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, + 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1, + 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, + 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28, + 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, + 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3 + } +}; + +static const struct { + int next_model; + int code_size; + float pulse_value; +} utk_commands[29] = { + {MDL_LARGEPULSE, 8, 0.0f}, + {MDL_LARGEPULSE, 7, 0.0f}, + {MDL_NORMAL, 8, 0.0f}, + {MDL_NORMAL, 7, 0.0f}, + {MDL_NORMAL, 2, 0.0f}, + {MDL_NORMAL, 2, -1.0f}, + {MDL_NORMAL, 2, +1.0f}, + {MDL_NORMAL, 3, -1.0f}, + {MDL_NORMAL, 3, +1.0f}, + {MDL_LARGEPULSE, 4, -2.0f}, + {MDL_LARGEPULSE, 4, +2.0f}, + {MDL_LARGEPULSE, 3, -2.0f}, + {MDL_LARGEPULSE, 3, +2.0f}, + {MDL_LARGEPULSE, 5, -3.0f}, + {MDL_LARGEPULSE, 5, +3.0f}, + {MDL_LARGEPULSE, 4, -3.0f}, + {MDL_LARGEPULSE, 4, +3.0f}, + {MDL_LARGEPULSE, 6, -4.0f}, + {MDL_LARGEPULSE, 6, +4.0f}, + {MDL_LARGEPULSE, 5, -4.0f}, + {MDL_LARGEPULSE, 5, +4.0f}, + {MDL_LARGEPULSE, 7, -5.0f}, + {MDL_LARGEPULSE, 7, +5.0f}, + {MDL_LARGEPULSE, 6, -5.0f}, + {MDL_LARGEPULSE, 6, +5.0f}, + {MDL_LARGEPULSE, 8, -6.0f}, + {MDL_LARGEPULSE, 8, +6.0f}, + {MDL_LARGEPULSE, 7, -6.0f}, + {MDL_LARGEPULSE, 7, +6.0f} +}; + +static int utk_read_byte(UTKContext *ctx) +{ + if (ctx->ptr < ctx->end) + return *ctx->ptr++; + + if (ctx->read_callback) { + size_t bytes_copied = ctx->read_callback(ctx->buffer, ctx->buffer_size, ctx->arg); + if (bytes_copied > 0 && bytes_copied <= ctx->buffer_size) { + ctx->ptr = ctx->buffer; + ctx->end = ctx->buffer + bytes_copied; + return *ctx->ptr++; + } + } + + return 0; +} + +static int16_t utk_read_i16(UTKContext *ctx) +{ + int x = utk_read_byte(ctx); + x = (x << 8) | utk_read_byte(ctx); + return x; +} + +static int utk_read_bits(UTKContext *ctx, int count) +{ + int ret = ctx->bits_value & ((1 << count) - 1); + ctx->bits_value >>= count; + ctx->bits_count -= count; + + if (ctx->bits_count < 8) { + /* read another byte */ + ctx->bits_value |= utk_read_byte(ctx) << ctx->bits_count; + ctx->bits_count += 8; + } + + return ret; +} + +static void utk_parse_header(UTKContext *ctx) +{ + int i; + float multiplier; + + ctx->reduced_bw = utk_read_bits(ctx, 1); + ctx->multipulse_thresh = 32 - utk_read_bits(ctx, 4); + ctx->fixed_gains[0] = 8.0f * (1 + utk_read_bits(ctx, 4)); + multiplier = 1.04f + utk_read_bits(ctx, 6)*0.001f; + + for (i = 1; i < 64; i++) + ctx->fixed_gains[i] = ctx->fixed_gains[i-1] * multiplier; +} + +static void utk_decode_excitation(UTKContext *ctx, int use_multipulse, float *out, int stride) +{ + int i; + + if (use_multipulse) { + /* multi-pulse model: n pulses are coded explicitly; the rest are zero */ + int model, cmd; + model = 0; + i = 0; + while (i < 108) { + cmd = utk_codebooks[model][ctx->bits_value & 0xff]; + model = utk_commands[cmd].next_model; + utk_read_bits(ctx, utk_commands[cmd].code_size); + + if (cmd > 3) { + /* insert a pulse with magnitude <= 6.0f */ + out[i] = utk_commands[cmd].pulse_value; + i += stride; + } else if (cmd > 1) { + /* insert between 7 and 70 zeros */ + int count = 7 + utk_read_bits(ctx, 6); + if (i + count * stride > 108) + count = (108 - i)/stride; + + while (count > 0) { + out[i] = 0.0f; + i += stride; + count--; + } + } else { + /* insert a pulse with magnitude >= 7.0f */ + int x = 7; + + while (utk_read_bits(ctx, 1)) + x++; + + if (!utk_read_bits(ctx, 1)) + x *= -1; + + out[i] = (float)x; + i += stride; + } + } + } else { + /* RELP model: entire residual (excitation) signal is coded explicitly */ + i = 0; + while (i < 108) { + if (!utk_read_bits(ctx, 1)) + out[i] = 0.0f; + else if (!utk_read_bits(ctx, 1)) + out[i] = -2.0f; + else + out[i] = 2.0f; + + i += stride; + } + } +} + +static void rc_to_lpc(const float *rc, float *lpc) +{ + int i, j; + float tmp1[12]; + float tmp2[12]; + + for (i = 10; i >= 0; i--) + tmp2[1+i] = rc[i]; + + tmp2[0] = 1.0f; + + for (i = 0; i < 12; i++) { + float x = -tmp2[11] * rc[11]; + + for (j = 10; j >= 0; j--) { + x -= tmp2[j] * rc[j]; + tmp2[j+1] = x * rc[j] + tmp2[j]; + } + + tmp1[i] = tmp2[0] = x; + + for (j = 0; j < i; j++) + x -= tmp1[i-1-j] * lpc[j]; + + lpc[i] = x; + } +} + +static void utk_lp_synthesis_filter(UTKContext *ctx, int offset, int num_blocks) +{ + int i, j, k; + float lpc[12]; + float *ptr = &ctx->decompressed_frame[offset]; + + rc_to_lpc(ctx->rc, lpc); + + for (i = 0; i < num_blocks; i++) { + for (j = 0; j < 12; j++) { + float x = *ptr; + + for (k = 0; k < j; k++) + x += lpc[k] * ctx->synth_history[k-j+12]; + for (; k < 12; k++) + x += lpc[k] * ctx->synth_history[k-j]; + + ctx->synth_history[11-j] = x; + *ptr++ = x; + } + } +} + +/* +** Public functions. +*/ + +static int utk_decode_frame(UTKContext *ctx) +{ + int i, j; + int use_multipulse = 0; + float excitation[5+108+5]; + float rc_delta[12]; + + if (!ctx->bits_count) { + ctx->bits_value = utk_read_byte(ctx); + ctx->bits_count = 8; + } + + if (!ctx->parsed_header) { + utk_parse_header(ctx); + ctx->parsed_header = 1; + } + + memset(&excitation[0], 0, 5*sizeof(float)); + memset(&excitation[5+108], 0, 5*sizeof(float)); + + /* read the reflection coefficients */ + for (i = 0; i < 12; i++) { + int idx; + if (i == 0) { + idx = utk_read_bits(ctx, 6); + if (idx < ctx->multipulse_thresh) + use_multipulse = 1; + } else if (i < 4) { + idx = utk_read_bits(ctx, 6); + } else { + idx = 16 + utk_read_bits(ctx, 5); + } + + rc_delta[i] = (utk_rc_table[idx] - ctx->rc[i])*0.25f; + } + + /* decode four subframes */ + for (i = 0; i < 4; i++) { + int pitch_lag = utk_read_bits(ctx, 8); + float pitch_gain = (float)utk_read_bits(ctx, 4)/15.0f; + float fixed_gain = ctx->fixed_gains[utk_read_bits(ctx, 6)]; + + if (!ctx->reduced_bw) { + utk_decode_excitation(ctx, use_multipulse, &excitation[5], 1); + } else { + /* residual (excitation) signal is encoded at reduced bandwidth */ + int align = utk_read_bits(ctx, 1); + int zero = utk_read_bits(ctx, 1); + + utk_decode_excitation(ctx, use_multipulse, &excitation[5+align], 2); + + if (zero) { + /* fill the remaining samples with zero + ** (spectrum is duplicated into high frequencies) */ + for (j = 0; j < 54; j++) + excitation[5+(1-align)+2*j] = 0.0f; + } else { + /* interpolate the remaining samples + ** (spectrum is low-pass filtered) */ + float *ptr = &excitation[5+(1-align)]; + for (j = 0; j < 108; j += 2) + ptr[j] = ptr[j-5] * 0.01803267933428287506103515625f + - ptr[j-3] * 0.114591561257839202880859375f + + ptr[j-1] * 0.597385942935943603515625f + + ptr[j+1] * 0.597385942935943603515625f + - ptr[j+3] * 0.114591561257839202880859375f + + ptr[j+5] * 0.01803267933428287506103515625f; + + /* scale by 0.5f to give the sinc impulse response unit energy */ + fixed_gain *= 0.5f; + } + } + + for (j = 0; j < 108; j++) + ctx->decompressed_frame[108*i+j] = fixed_gain * excitation[5+j] + + pitch_gain * ctx->adapt_cb[108*i+216-pitch_lag+j]; + } + + for (i = 0; i < 324; i++) + ctx->adapt_cb[i] = ctx->decompressed_frame[108+i]; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 12; j++) + ctx->rc[j] += rc_delta[j]; + + utk_lp_synthesis_filter(ctx, 12*i, i < 3 ? 1 : 33); + } + + return 0; +} + +static void utk_init(UTKContext *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +static void utk_reset(UTKContext *ctx) +{ + /* resets the internal state, leaving the external config/buffers + * untouched (could be reset externally or using utk_set_x) */ + ctx->parsed_header = 0; + ctx->bits_value = 0; + ctx->bits_count = 0; + ctx->reduced_bw = 0; + ctx->multipulse_thresh = 0; + memset(ctx->fixed_gains, 0, sizeof(ctx->fixed_gains)); + memset(ctx->rc, 0, sizeof(ctx->rc)); + memset(ctx->synth_history, 0, sizeof(ctx->synth_history)); + memset(ctx->adapt_cb, 0, sizeof(ctx->adapt_cb)); + memset(ctx->decompressed_frame, 0, sizeof(ctx->decompressed_frame)); +} + +static void utk_set_callback(UTKContext *ctx, uint8_t *buffer, size_t buffer_size, void *arg, size_t (*read_callback)(void *, int , void *)) +{ + /* prepares for external reading */ + ctx->buffer = buffer; + ctx->buffer_size = buffer_size; + ctx->arg = arg; + ctx->read_callback = read_callback; + + /* reset the bit reader */ + ctx->bits_count = 0; +} + +static void utk_set_ptr(UTKContext *ctx, const uint8_t *ptr, const uint8_t *end) +{ + /* sets the pointer to an external data buffer (can also be used to + * reset the buffered data if set to ptr/end 0) */ + ctx->ptr = ptr; + ctx->end = end; + + /* reset the bit reader */ + ctx->bits_count = 0; +} + +/* +** MicroTalk Revision 3 decoding function. +*/ + +static int utk_rev3_decode_frame(UTKContext *ctx) +{ + int pcm_data_present = (utk_read_byte(ctx) == 0xee); + int i; + + utk_decode_frame(ctx); + + /* unread the last 8 bits and reset the bit reader */ + ctx->ptr--; + ctx->bits_count = 0; + + if (pcm_data_present) { + /* Overwrite n samples at a given offset in the decoded frame with + ** raw PCM data. */ + int offset = utk_read_i16(ctx); + int count = utk_read_i16(ctx); + + /* sx.exe does not do any bounds checking or clamping of these two + ** fields (see 004274D1 in sx.exe v3.01.01), which means a specially + ** crafted MT5:1 file can crash sx.exe. + ** We will throw an error instead. */ + if (offset < 0 || offset > 432) { + return -1; /* invalid PCM offset */ + } + if (count < 0 || count > 432 - offset) { + return -2; /* invalid PCM count */ + } + + for (i = 0; i < count; i++) + ctx->decompressed_frame[offset+i] = (float)utk_read_i16(ctx); + } + + return 0; +} + +#endif /* _EA_MT_DECODER_UTK_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index f9ef42f0c..2f6b833d5 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -1,10 +1,9 @@ #include "coding.h" -#include "ffmpeg_decoder_utils.h" #ifdef VGM_USE_FFMPEG /* internal sizes, can be any value */ -#define FFMPEG_DEFAULT_BUFFER_SIZE 2048 +#define FFMPEG_DEFAULT_SAMPLE_BUFFER_SIZE 2048 #define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024 @@ -24,7 +23,7 @@ static void g_init_ffmpeg() { g_ffmpeg_initialized = 1; av_log_set_flags(AV_LOG_SKIP_REPEATED); av_log_set_level(AV_LOG_ERROR); - av_register_all(); + //av_register_all(); /* not needed in newer versions */ g_ffmpeg_initialized = 2; } } @@ -185,53 +184,38 @@ fail: /* ******************************************** */ /* AVIO callback: read stream, handling custom data */ -static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { +static int ffmpeg_read(void *opaque, uint8_t *buf, int read_size) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; - int ret = 0; + int bytes = 0; int max_to_copy = 0; - //;VGM_LOG("AVIO read: r_off=%"PRIx64", v_off=%"PRIx64", b_size=%x\n", data->real_offset, data->virtual_offset, buf_size);fflush(stdout); - /* clamp reads */ - if (data->virtual_offset + buf_size > data->virtual_size) - buf_size = data->virtual_size - data->virtual_offset; - if (buf_size == 0) - return ret; + if (data->logical_offset + read_size > data->logical_size) + read_size = data->logical_size - data->logical_offset; + if (read_size == 0) + return bytes; /* handle reads on inserted header */ - if (data->header_size) { - if (data->virtual_offset < data->header_size) { - max_to_copy = (int)(data->header_size - data->virtual_offset); - if (max_to_copy > buf_size) { - max_to_copy = buf_size; - } + if (data->header_size && data->logical_offset < data->header_size) { + max_to_copy = (int)(data->header_size - data->logical_offset); + if (max_to_copy > read_size) + max_to_copy = read_size; - //;VGM_LOG("AVIO header: v_off=%lx, h_size=%lx, mtc=%x, b_size=%x\n", (off_t)data->virtual_offset, (off_t)data->header_size, max_to_copy, buf_size);fflush(stdout); - memcpy(buf, data->header_insert_block + data->virtual_offset, max_to_copy); - buf += max_to_copy; - buf_size -= max_to_copy; - data->virtual_offset += max_to_copy; /* adjust for reads below */ + memcpy(buf, data->header_insert_block + data->logical_offset, max_to_copy); + buf += max_to_copy; + read_size -= max_to_copy; + data->logical_offset += max_to_copy; - if (buf_size == 0) { - return max_to_copy; /* offset still in header */ - } + if (read_size == 0) { + return max_to_copy; /* offset still in header */ } } /* main read */ - switch(data->config.type) { - case FFMPEG_EA_XMA: ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break; - case FFMPEG_SWITCH_OPUS: ret = ffmpeg_custom_read_switch_opus(data, buf, buf_size); break; - //case FFMPEG_EA_SCHL: ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break; - //case FFMPEG_SFH: ret = ffmpeg_custom_read_sfh(data, buf, buf_size); break; - default: ret = ffmpeg_custom_read_standard(data, buf, buf_size); break; - } - data->virtual_offset += ret; - //data->real_offset = ; /* must be updated in function */ - - //;VGM_LOG("AVIO read done: ret=%x, r_off=%"PRIx64", v_off=%"PRIx64"\n", ret + max_to_copy, data->real_offset, data->virtual_offset);fflush(stdout); - //;VGM_LOGB((buf - max_to_copy),ret + max_to_copy,0); - return ret + max_to_copy; + bytes = read_streamfile(buf, data->offset, read_size, data->streamfile); + data->logical_offset += bytes; + data->offset += bytes; + return bytes + max_to_copy; } /* AVIO callback: write stream not needed */ @@ -246,87 +230,50 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { /* get cache'd size */ if (whence & AVSEEK_SIZE) { - //;VGM_LOG("AVIO size: v_size=%"PRIx64", h_size=%"PRIx64", r_size=%"PRIx64", r_start=%"PRIx64"\n", data->virtual_size, data->header_size, data->real_size, data->real_start);fflush(stdout); - return data->virtual_size; + return data->logical_size; } - //;VGM_LOG("AVIO seek: off=%"PRIx64" r_off=%"PRIx64", v_off=%"PRIx64"\n", offset, data->real_offset, data->virtual_offset);fflush(stdout); - whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE); - /* find the final offset FFmpeg sees (within fake header + virtual size) */ switch (whence) { case SEEK_SET: /* absolute */ break; case SEEK_CUR: /* relative to current */ - offset += data->virtual_offset; + offset += data->logical_offset; break; case SEEK_END: /* relative to file end (should be negative) */ - offset += data->virtual_size; + offset += data->logical_size; break; } /* clamp offset; fseek does this too */ - if (offset > data->virtual_size) - offset = data->virtual_size; + if (offset > data->logical_size) + offset = data->logical_size; else if (offset < 0) offset = 0; - /* no change */ - if (data->virtual_offset == offset) { - return ret; - } - /* seeks inside fake header */ if (offset < data->header_size) { - data->virtual_offset = offset; - data->real_offset = data->real_start; + data->logical_offset = offset; + data->offset = data->start; return ret; } /* main seek */ - switch(data->config.type) { - case FFMPEG_EA_XMA: offset = ffmpeg_custom_seek_eaxma(data, offset); break; - case FFMPEG_SWITCH_OPUS: offset = ffmpeg_custom_seek_switch_opus(data, offset); break; - //case FFMPEG_EA_SCHL: offset = ffmpeg_custom_seek_ea_schl(data, offset); break; - //case FFMPEG_SFH: offset = ffmpeg_custom_seek_sfh(data, offset); break; - default: offset = ffmpeg_custom_seek_standard(data, offset); break; - } - data->virtual_offset = offset; - //data->real_offset = ; /* must be updated in function */ - - //;VGM_LOG("AVIO seek done: r_off=%"PRIx64", v_off=%"PRIx64"\n", data->real_offset, data->virtual_offset);fflush(stdout); + data->logical_offset = offset; + data->offset = data->start + (offset - data->header_size); return ret; } -/* called on init, not a callback */ //todo rename to ffmpeg_init -static int64_t ffmpeg_size(ffmpeg_codec_data * data) { - int64_t bytes; - switch(data->config.type) { - case FFMPEG_EA_XMA: bytes = ffmpeg_custom_size_eaxma(data); break; - case FFMPEG_SWITCH_OPUS: bytes = ffmpeg_custom_size_switch_opus(data); break; - //case FFMPEG_EA_SCHL: bytes = ffmpeg_custom_size_ea_schl(data); break; - //case FFMPEG_SFH: bytes = ffmpeg_custom_size_sfh(data); break; - default: bytes = ffmpeg_custom_size_standard(data); break; - } - - return bytes; -} - - /* ******************************************** */ /* MAIN INIT/DECODER */ /* ******************************************** */ ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { - return init_ffmpeg_config(streamFile, NULL,0, start,size, NULL); + return init_ffmpeg_header_offset(streamFile, NULL,0, start,size); } -ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) { - return init_ffmpeg_config(streamFile, header,header_size, start,size, NULL); -} - /** * Manually init FFmpeg, from a fake header / offset. @@ -335,17 +282,17 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * * This header will be seamlessly inserted before 'start' offset, and total filesize will be 'header_size' + 'size'. * The header buffer will be copied and memory-managed internally. * NULL header can used given if the stream has internal data recognized by FFmpeg at offset. - * Stream index can be passed to FFmpeg, if the format has multiple streams (1=first). + * Stream index can be passed to FFmpeg in the streamFile, if the format has multiple streams (1=first). */ -ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, ffmpeg_custom_config * config) { +ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) { char filename[PATH_LIMIT]; ffmpeg_codec_data * data; int errcode, i; - + int targetSubsong = streamFile->stream_index; int streamIndex, streamCount; + AVStream *stream; AVCodecParameters *codecPar = NULL; - AVRational tb; @@ -356,14 +303,9 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, if (!data) return NULL; streamFile->get_name( streamFile, filename, sizeof(filename) ); - data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); if (!data->streamfile) goto fail; - if (config) { - memcpy(&data->config, config, sizeof(ffmpeg_custom_config)); - } - /* ignore bad combos */ if ((header && !header_size) || (!header && header_size)) goto fail; @@ -375,13 +317,15 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, if (!data->header_insert_block) goto fail; } - data->real_start = start; - data->real_offset = data->real_start; - data->real_size = size; - data->virtual_offset = 0; - data->virtual_size = ffmpeg_size(data); - data->virtual_base = 0; - if (data->virtual_size == 0) goto fail; + data->start = start; + data->offset = start; + data->size = size; + if (data->size == 0 || data->start + data->size > get_streamfile_size(streamFile)) { + VGM_LOG("FFmpeg: wrong start+size found\n"); + data->size = get_streamfile_size(streamFile) - data->start; + } + data->logical_offset = 0; + data->logical_size = data->header_size + data->size; /* setup IO, attempt to autodetect format and gather some info */ data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE); @@ -411,7 +355,7 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, streamCount++; /* select Nth audio stream if specified, or first one */ - if (streamIndex < 0 || (data->config.stream_index > 0 && streamCount == data->config.stream_index)) { + if (streamIndex < 0 || (targetSubsong > 0 && streamCount == targetSubsong)) { codecPar = stream->codecpar; streamIndex = i; } @@ -420,7 +364,7 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, if (i != streamIndex) stream->discard = AVDISCARD_ALL; /* disable demuxing for other streams */ } - if (streamCount < data->config.stream_index) goto fail; + if (streamCount < targetSubsong) goto fail; if (streamIndex < 0 || !codecPar) goto fail; data->streamIndex = streamIndex; @@ -434,7 +378,7 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, if ((errcode = avcodec_parameters_to_context(data->codecCtx, codecPar)) < 0) goto fail; - av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); + //av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); /* deprecated and seemingly not needed */ data->codec = avcodec_find_decoder(data->codecCtx->codec_id); if (!data->codec) goto fail; @@ -507,7 +451,7 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, data->frameSize = av_get_audio_frame_duration(data->codecCtx,0); /* setup decode buffer */ - data->sampleBufferBlock = FFMPEG_DEFAULT_BUFFER_SIZE; + data->sampleBufferBlock = FFMPEG_DEFAULT_SAMPLE_BUFFER_SIZE; data->sampleBuffer = av_malloc( data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels ); if (!data->sampleBuffer) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c new file mode 100644 index 000000000..0815ecfae --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c @@ -0,0 +1,537 @@ +#include "coding.h" +#include "../streamfile.h" +#include + +/** + * Transmogrifies custom Opus (no Ogg layer and custom packet headers) into is Xiph Opus, creating + * valid Ogg pages with single Opus packets. + * Uses an intermediate buffer to make full Ogg pages, since checksums are calculated with the whole page. + * + * Info, CRC and stuff: + * https://www.opus-codec.org/docs/ + * https://tools.ietf.org/html/rfc7845.html + * https://github.com/hcs64/ww2ogg + */ + +static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate); +static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule); +static size_t opus_get_packet_samples(const uint8_t * buf, int len); + +typedef enum { OPUS_SWITCH, OPUS_UE4 } opus_type_t; +typedef struct { + /* config */ + opus_type_t type; + off_t stream_offset; + size_t stream_size; + + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + + size_t block_size; /* current block size */ + size_t page_size; /* current OggS page size */ + uint8_t page_buffer[0x2000]; /* OggS page (observed max is ~0xc00) */ + size_t sequence; /* OggS sequence */ + size_t samples_done; /* OggS granule */ + + uint8_t head_buffer[0x100]; /* OggS head page */ + size_t head_size; /* OggS head page size */ + + size_t logical_size; +} opus_io_data; + + +/* Convers custom Opus packets to Ogg Opus, so the resulting data is larger than physical data. */ +static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, opus_io_data* data) { + size_t total_read = 0; + + /* ignore bad reads */ + if (offset < 0 || offset > data->logical_size) { + return total_read; + } + + /* previous offset: re-start as we can't map logical<>physical offsets */ + if (offset < data->logical_offset) { + data->physical_offset = data->stream_offset; + data->logical_offset = 0x00; + data->page_size = 0; + data->samples_done = 0; + data->sequence = 2; /* appended header is 0/1 */ + + if (offset >= data->head_size) + data->logical_offset = data->head_size; + } + + /* insert fake header */ + if (offset < data->head_size) { + size_t bytes_consumed, to_read; + + bytes_consumed = offset - data->logical_offset; + to_read = data->head_size - bytes_consumed; + if (to_read > length) + to_read = length; + memcpy(dest, data->head_buffer + bytes_consumed, to_read); + + total_read += to_read; + dest += to_read; + offset += to_read; + length -= to_read; + data->logical_offset += to_read; + } + + /* read blocks, one at a time */ + while (length > 0) { + + /* ignore EOF */ + if (data->logical_offset >= data->logical_size) { + break; + } + + /* process new block */ + if (data->page_size == 0) { + size_t data_size, skip_size, oggs_size; + + switch(data->type) { + case OPUS_SWITCH: /* format seem to come from opus_test and not Nintendo-specific */ + data_size = read_32bitBE(data->physical_offset, streamfile); + skip_size = 0x08; /* size + Opus state(?) */ + break; + case OPUS_UE4: + data_size = (uint16_t)read_16bitLE(data->physical_offset, streamfile); + skip_size = 0x02; + break; + default: + return 0; + } + + oggs_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */ + + data->block_size = data_size + skip_size; + data->page_size = oggs_size + data_size; + + if (data->page_size > sizeof(data->page_buffer)) { /* happens on bad reads/EOF too */ + VGM_LOG("OPUS: buffer can't hold OggS at %"PRIx64"\n", (off64_t)data->physical_offset); + data->page_size = 0; + break; + } + + /* create fake OggS page (full page for checksums) */ + read_streamfile(data->page_buffer+oggs_size, data->physical_offset + skip_size, data_size, streamfile); /* store page data */ + data->samples_done += opus_get_packet_samples(data->page_buffer+oggs_size, data_size); + make_oggs_page(data->page_buffer,sizeof(data->page_buffer), data_size, data->sequence, data->samples_done); + data->sequence++; + } + + /* move to next block */ + if (offset >= data->logical_offset + data->page_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->page_size; + data->page_size = 0; + continue; + } + + /* read data */ + { + size_t bytes_consumed, to_read; + + bytes_consumed = offset - data->logical_offset; + to_read = data->page_size - bytes_consumed; + if (to_read > length) + to_read = length; + memcpy(dest, data->page_buffer + bytes_consumed, to_read); + + total_read += to_read; + dest += to_read; + offset += to_read; + length -= to_read; + + if (to_read == 0) { + break; /* error/EOF */ + } + } + } + + return total_read; +} + + +static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { + off_t physical_offset, max_physical_offset; + size_t logical_size = 0; + + if (data->logical_size) + return data->logical_size; + + if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) { + VGM_LOG("OPUS: wrong streamsize %"PRIx64" + %x vs %x\n", (off64_t)data->stream_offset, data->stream_size, get_streamfile_size(streamfile)); + return 0; + } + + physical_offset = data->stream_offset; + max_physical_offset = data->stream_offset + data->stream_size; + logical_size = data->head_size; + + /* get size of the logical stream */ + while (physical_offset < max_physical_offset) { + size_t data_size, skip_size, oggs_size; + + switch(data->type) { + case OPUS_SWITCH: + data_size = read_32bitBE(physical_offset, streamfile); + skip_size = 0x08; + break; + case OPUS_UE4: + data_size = (uint16_t)read_16bitLE(physical_offset, streamfile); + skip_size = 0x02; + break; + default: + return 0; + } + oggs_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */ + + physical_offset += data_size + skip_size; + logical_size += oggs_size + data_size; + } + + /* logical size can be bigger though */ + if (physical_offset > get_streamfile_size(streamfile)) { + VGM_LOG("OPUS: wrong size\n"); + return 0; + } + + data->logical_size = logical_size; + return data->logical_size; +} + + +/* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */ +static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, int channels, int skip, int sample_rate, off_t stream_offset, size_t stream_size, opus_type_t type) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + opus_io_data io_data = {0}; + size_t io_data_size = sizeof(opus_io_data); + + io_data.type = type; + io_data.stream_offset = stream_offset; + io_data.stream_size = stream_size; + io_data.physical_offset = stream_offset; + io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), channels, skip, sample_rate); + if (!io_data.head_size) goto fail; + io_data.sequence = 2; + io_data.logical_size = opus_io_size(streamFile, &io_data); /* force init */ + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, opus_io_read,opus_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +/* ******************************** */ + +/* from ww2ogg - from Tremor (lowmem) */ +static uint32_t crc_lookup[256]={ + 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, + 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, + 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, + 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, + 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, + 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, + 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, + 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, + 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, + 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, + 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, + 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, + 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, + 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, + 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, + 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, + 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, + 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, + 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, + 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, + 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, + 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, + 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, + 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, + 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, + 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, + 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, + 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, + 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, + 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, + 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, + 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4 +}; + +/* from ww2ogg */ +static uint32_t get_oggs_checksum(uint8_t * data, int bytes) { + uint32_t crc_reg=0; + int i; + + for(i=0;i> 24)&0xff)^data[i]]; + + return crc_reg; +} + +/* from opus_decoder.c's opus_packet_get_samples_per_frame */ +static uint32_t opus_packet_get_samples_per_frame(const uint8_t * data, int Fs) { + int audiosize; + if (data[0]&0x80) + { + audiosize = ((data[0]>>3)&0x3); + audiosize = (Fs<>3)&0x3); + if (audiosize == 3) + audiosize = Fs*60/1000; + else + audiosize = (Fs< buf_size) { + VGM_LOG("OPUS: buffer can't hold OggS page\n"); + goto fail; + } + + segment_count = (int)(data_size / 0xFF + 1); + put_32bitBE(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */ + put_8bit (buf+0x04, 0); /* stream structure version, fixed */ + put_8bit (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */ + put_32bitLE(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */ + put_32bitLE(buf+0x0A, (uint32_t)(absolute_granule >> 32 & 0xFFFFFFFF)); /* upper */ + put_32bitLE(buf+0x0E, stream_serial_number); /* for interleaved multi-streams */ + put_32bitLE(buf+0x12, page_sequence); + put_32bitLE(buf+0x16, checksum); /* 0 for now, until all data is written */ + put_8bit (buf+0x1A, segment_count); /* count of all lacing values */ + + /* segment table: size N in "lacing values" (ex. 0x20E=0xFF+FF+10; 0xFF=0xFF+00) */ + page_done = 0x1B; + while (lacing_done < data_size) { + int bytes = data_size - lacing_done; + if (bytes > 0xFF) + bytes = 0xFF; + + put_8bit(buf+page_done, bytes); + page_done++; + lacing_done += bytes; + + if (lacing_done == data_size && bytes == 0xFF) { + put_8bit(buf+page_done, 0x00); + page_done++; + } + } + + /* data */ + //memcpy(buf+page_done, data_buf, data_size); /* data must be copied before this call */ + page_done += data_size; + + /* final checksum */ + checksum = get_oggs_checksum(buf, page_done); + put_32bitLE(buf+0x16, checksum); + + return page_done; +fail: + return 0; +} + +static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) { + size_t header_size = 0x13; + + if (header_size > buf_size) { + VGM_LOG("OPUS: buffer can't hold header\n"); + goto fail; + } + + put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ + put_32bitBE(buf+0x04, 0x48656164); /* "Head" header magic */ + put_8bit (buf+0x08, 1); /* version */ + put_8bit (buf+0x09, channels); + put_16bitLE(buf+0x0A, skip); + put_32bitLE(buf+0x0c, sample_rate); + put_16bitLE(buf+0x10, 0); /* output gain */ + put_8bit (buf+0x12, 0); /* channel mapping family */ + + return header_size; +fail: + return 0; +} + +static size_t make_opus_comment(uint8_t * buf, int buf_size) { + const char * vendor_string = "vgmstream"; + const char * user_comment_0_string = "vgmstream Opus converter"; + size_t comment_size; + int vendor_string_length, user_comment_0_length; + + vendor_string_length = strlen(vendor_string); + user_comment_0_length = strlen(user_comment_0_string); + comment_size = 0x14 + vendor_string_length + user_comment_0_length; + + if (comment_size > buf_size) { + VGM_LOG("OPUS: buffer can't hold comment\n"); + goto fail; + } + + put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ + put_32bitBE(buf+0x04, 0x54616773); /* "Tags" header magic */ + put_32bitLE(buf+0x08, vendor_string_length); + memcpy (buf+0x0c, vendor_string, vendor_string_length); + put_32bitLE(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */ + put_32bitLE(buf+0x0c + vendor_string_length+0x04, user_comment_0_length); + memcpy (buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length); + + return comment_size; +fail: + return 0; +} + +static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) { + int buf_done = 0; + size_t bytes; + + if (buf_size < 0x100) /* approx */ + goto fail; + + /* make header */ + bytes = make_opus_header(buf+buf_done + 0x1c,buf_size, channels, skip, sample_rate); + make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 0, 0); + buf_done += 0x1c + bytes; + + /* make comment */ + bytes = make_opus_comment(buf+buf_done + 0x1c,buf_size); + make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 1, 0); + buf_done += 0x1c + bytes; + + return buf_done; +fail: + return 0; +} + +/************************** */ + +#ifdef VGM_USE_FFMPEG + +static size_t opus_get_packet_samples(const uint8_t * buf, int len) { + return opus_packet_get_nb_frames(buf, len) * opus_packet_get_samples_per_frame(buf, 48000); +} + +static size_t custom_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile, opus_type_t type) { + size_t num_samples = 0; + off_t end_offset = offset + data_size; + + if (end_offset > get_streamfile_size(streamFile)) { + VGM_LOG("OPUS: wrong end offset found\n"); + end_offset = get_streamfile_size(streamFile); + } + + /* count by reading all frames */ + while (offset < end_offset) { + uint8_t buf[4]; + size_t data_size, skip_size; + + switch(type) { + case OPUS_SWITCH: + data_size = read_32bitBE(offset, streamFile); + skip_size = 0x08; + break; + case OPUS_UE4: + data_size = (uint16_t)read_16bitLE(offset, streamFile); + skip_size = 0x02; + break; + default: + return 0; + } + + read_streamfile(buf, offset+skip_size, 0x04, streamFile); /* at least 0x02 */ + num_samples += opus_get_packet_samples(buf, 0x04); + + offset += skip_size + data_size; + } + + return num_samples; +} + +size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile) { + return custom_opus_get_samples(offset, data_size, sample_rate, streamFile, OPUS_SWITCH); +} +size_t ue4_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile) { + return custom_opus_get_samples(offset, data_size, sample_rate, streamFile, OPUS_UE4); +} + + +static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { + ffmpeg_codec_data * ffmpeg_data = NULL; + STREAMFILE *temp_streamFile = NULL; + + temp_streamFile = setup_opus_streamfile(streamFile, channels, skip, sample_rate, start_offset, data_size, type); + if (!temp_streamFile) goto fail; + + ffmpeg_data = init_ffmpeg_offset(temp_streamFile, 0x00,get_streamfile_size(temp_streamFile)); + if (!ffmpeg_data) goto fail; + + if (ffmpeg_data->skipSamples <= 0) { + ffmpeg_set_skip_samples(ffmpeg_data, skip); + } + + close_streamfile(temp_streamFile); + return ffmpeg_data; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH); +} + +ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_UE4); +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c deleted file mode 100644 index 2f718ddf9..000000000 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "coding.h" -#include "ffmpeg_decoder_utils.h" - -#ifdef VGM_USE_FFMPEG - -/** - * Standard read mode: virtual values are 1:1 but inside a portion of the streamfile (between real_start and real_size). - */ - - -int ffmpeg_custom_read_standard(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) { - size_t bytes = read_streamfile(buf, data->real_offset, buf_size, data->streamfile); - data->real_offset += bytes; - - return bytes; -} - -int64_t ffmpeg_custom_seek_standard(ffmpeg_codec_data *data, int64_t virtual_offset) { - int64_t seek_virtual_offset = virtual_offset - data->header_size; - - data->real_offset = data->real_start + seek_virtual_offset; - return virtual_offset; -} - -int64_t ffmpeg_custom_size_standard(ffmpeg_codec_data *data) { - return data->real_size + data->header_size; -} - - -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h deleted file mode 100644 index 64461fe9f..000000000 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _FFMPEG_DECODER_UTILS_ -#define _FFMPEG_DECODER_UTILS_ - -#ifdef VGM_USE_FFMPEG -/* used by ffmpeg_decoder.c, but scattered in other .c files */ - -/** - * Custom read/seek for data transformation. Must handle seeks+reads from virtual offsets, ie.- - * reads "real/file" data, not decodable by FFmpeg, and transforms to decodable "virtual/buffer" data, - * block by block (must seek to closest file offset and adjust on reads). - * - * To simplify, functions won't be called in common cases (seek over filesize, no change in offset, etc), - * and fake header seeks/reads are handled externally. Real offset must be updated internally though. - * - * example (a 0x100 block transforms to a 0x150 block): - * - seek 0: file-offset=0, virtual-offset=0 - * - read 0x150: file-read=0x100 transforms to buffer=0x150 - * - new file-offset=0x100, virtual-offset=0x150 - * - seek 0x310: file-offset=0x200, virtual-offset=0x310 (closest virtual block is 0x150+0x150, + 0x10 adjusted on reads) - */ - -int ffmpeg_custom_read_standard(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -int64_t ffmpeg_custom_seek_standard(ffmpeg_codec_data *data, int64_t virtual_offset); -int64_t ffmpeg_custom_size_standard(ffmpeg_codec_data *data); - -int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset); -int64_t ffmpeg_custom_size_eaxma(ffmpeg_codec_data *data); - -int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset); -int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data); - -//int ffmpeg_custom_read_ea_schl(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -//int64_t ffmpeg_custom_seek_ea_schl(ffmpeg_codec_data *data, int64_t virtual_offset); -//int64_t ffmpeg_custom_size_ea_schl(ffmpeg_codec_data *data); - -//int ffmpeg_custom_read_sfh(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -//int64_t ffmpeg_custom_seek_sfh(ffmpeg_codec_data *data, int64_t virtual_offset); -//int64_t ffmpeg_custom_size_sfh(ffmpeg_codec_data *data); - -#endif - -#endif/*_FFMPEG_DECODER_UTILS_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_ea_xma.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_ea_xma.c deleted file mode 100644 index 559c6fe63..000000000 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_ea_xma.c +++ /dev/null @@ -1,268 +0,0 @@ -#include "coding.h" -#include "ffmpeg_decoder_utils.h" - -#ifdef VGM_USE_FFMPEG - -#define EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK 4 /* normally max 3 (Dante's Inferno), ~14 (1 stream) in Burnout Paradise */ -#define EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK 4 /* XMA2 max is 8ch = 4 * 2ch */ -#define EAXMA_XMA_PACKET_SIZE 0x800 -#define EAXMA_XMA_BUFFER_SIZE (EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK * EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK * EAXMA_XMA_PACKET_SIZE) - -/** - * EA-XMA is XMA2 with padding removed (so a real 0x450 block would be padded to a virtual 0x800 block). - * Each EA-XMA SNS block contains 1~3 packets per stream, and multistream uses fully separate streams - * (no packet_skip set). We'll pad and reinterleave packets so it resembles standard XMA2. - * - * XMA2 data layout (XMA1 is the same but doesn't use blocks, they are only for seeking): - * - frames (containing 1..4 subframes): decode into 128*4 samples - * - packets: size 0x800, containing N frames (last frame can spill into next packet), must be padded - * - blocks: fixed size, containing N packets (last packet's frames won't spill into next block) - * - stream: N interleaved packets (1/2ch) for multichannel (Nch) audio. Interleave is not fixed: - * at file start/new block has one packet per stream, then must follow the "packet_skip" value - * in the XMA packet header to find its next packet (skiping packets from other streams). - * ex.: s1_p1 skip1, s2_p1 skip2, s1_p2 skip0 s1_p3 skip1, s2_p2 skip1, s1_p4... - */ - -static int get_block_max_packets(int num_streams, off_t packets_offset, STREAMFILE * streamfile); - - -int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) { - uint8_t v_buf[EAXMA_XMA_BUFFER_SIZE]; /* intermediate buffer, could be simplified */ - int buf_done = 0; - uint64_t real_offset = data->real_offset; - uint64_t virtual_offset = data->virtual_offset - data->header_size; - uint64_t virtual_base = data->virtual_base; - /* EA-XMA always uses late XMA2 streams (2ch + ... + 1/2ch) */ - int num_streams = (data->config.channels / 2) + (data->config.channels % 2 ? 1 : 0); - - - /* read and transform SNS/EA-XMA blocks into XMA packets */ - while (buf_done < buf_size) { - int s, p, bytes_to_copy, max_packets; - size_t block_size, data_size = 0, gap_size = 0; - uint32_t block_flag; - off_t packets_offset; - - block_flag = (uint8_t)read_8bit(real_offset+0x00,data->streamfile); - block_size = read_32bitBE(real_offset+0x00,data->streamfile) & 0x00FFFFFF; - packets_offset = real_offset + 0x08; /* 0x04(4): decoded samples */ - - if (block_flag == 0x45) /* exit on last block just in case, though should reach real_size */ - break; - - max_packets = get_block_max_packets(num_streams, packets_offset, data->streamfile); - if (max_packets == 0) goto fail; - - if (max_packets * num_streams * EAXMA_XMA_PACKET_SIZE > EAXMA_XMA_BUFFER_SIZE) { - VGM_LOG("EA XMA: block too big (%i * %i * 0x%x = 0x%x vs max 0x%x) at %lx\n", - max_packets,num_streams,EAXMA_XMA_PACKET_SIZE, max_packets*num_streams*EAXMA_XMA_PACKET_SIZE, EAXMA_XMA_BUFFER_SIZE,(off_t)real_offset); - goto fail; - } - - /* data is divided into a sub-block per stream (N packets), can be smaller than block_size (= has padding) - * copy XMA data re-interleaving for multichannel. To simplify some calcs fills the same number of packets - * per stream and adjusts packet headers (see above for XMA2 multichannel layout). */ - //to-do this doesn't make correct blocks sizes (but blocks are not needed to decode) - for (s = 0; s < num_streams; s++) { - size_t packets_size; - size_t packets_size4 = read_32bitBE(packets_offset, data->streamfile); /* size * 4, no idea */ - - packets_size = (packets_size4 / 4) - 0x04; - - /* Re-interleave all packets in order, one per stream. If one stream has more packets than - * others we add empty packets to keep the same number for all, avoiding packet_skip calcs */ - for (p = 0; p < max_packets; p++) { - off_t packet_offset = packets_offset + 0x04 + p * EAXMA_XMA_PACKET_SIZE; /* can be off but will copy 0 */ - off_t v_buf_offset = p * EAXMA_XMA_PACKET_SIZE * num_streams + s * EAXMA_XMA_PACKET_SIZE; - size_t packet_to_do = packets_size - p * EAXMA_XMA_PACKET_SIZE; - size_t extra_size = 0; - uint32_t header; - - if (packets_size < p * EAXMA_XMA_PACKET_SIZE) - packet_to_do = 0; /* empty packet */ - else if (packet_to_do > EAXMA_XMA_PACKET_SIZE) - packet_to_do = EAXMA_XMA_PACKET_SIZE; - - /* padding will be full size if packet_to_do is 0 */ - if (packet_to_do < EAXMA_XMA_PACKET_SIZE) - extra_size = EAXMA_XMA_PACKET_SIZE - (packet_to_do % EAXMA_XMA_PACKET_SIZE); - - /* copy data (or fully pad if empty packet) */ - read_streamfile(v_buf + v_buf_offset, packet_offset, packet_to_do, data->streamfile); - memset(v_buf + v_buf_offset + packet_to_do, 0xFF, extra_size); /* add padding, typically 0xFF */ - - /* rewrite packet header to add packet skips for multichannel (EA XMA streams are fully separate and have none) - * header bits: 6=num_frames, 15=first_frame_bits_offset, 3=metadata, 8=packet_skip */ - if (packet_to_do == 0) - header = 0x3FFF800; /* new empty packet header (0 num_frames, first_frame_bits_offset set to max) */ - else - header = (uint32_t)read_32bitBE(packet_offset, data->streamfile); - - /* get base header + change packet_skip since we know interleave is always 1 packet per stream */ - header = (header & 0xFFFFFF00) | ((header & 0x000000FF) + num_streams - 1); - put_32bitBE(v_buf + v_buf_offset, header); - } - - packets_offset += (packets_size4 / 4); - } - - if (buf_done == 0) /* first read */ - gap_size = virtual_offset - virtual_base; /* might start a few bytes into the XMA */ - - data_size = max_packets * num_streams * EAXMA_XMA_PACKET_SIZE; - - bytes_to_copy = data_size - gap_size; - if (bytes_to_copy > buf_size - buf_done) - bytes_to_copy = buf_size - buf_done; - - /* pad + copy */ - memcpy(buf + buf_done, v_buf + gap_size, bytes_to_copy); - buf_done += bytes_to_copy; - - /* move when block is fully done */ - if (data_size == bytes_to_copy + gap_size) { - real_offset += block_size; - virtual_base += data_size; - } - - if (block_flag == 0x80) /* exit on last block just in case, though should reach real_size */ - break; - } - - - data->real_offset = real_offset; - data->virtual_base = virtual_base; - return buf_size; - -fail: - return 0; -} - -int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset) { - int64_t real_offset, virtual_base; - int64_t current_virtual_offset = data->virtual_offset - data->header_size; - int64_t seek_virtual_offset = virtual_offset - data->header_size; - - /* Find SNS block start closest to offset. ie. virtual_offset 0x1A10 could mean SNS blocks - * of 0x456+0x820 padded to 0x800+0x1000 (base) + 0x210 (extra for reads), thus real_offset = 0xC76 */ - - if (seek_virtual_offset > current_virtual_offset) { /* seek after current: start from current block */ - real_offset = data->real_offset; - virtual_base = data->virtual_base; - } - else { /* seek before current: start from the beginning */ - real_offset = data->real_start; - virtual_base = 0; - } - - - /* find target block */ - while (virtual_base < seek_virtual_offset) { - size_t block_size, data_size, extra_size = 0; - uint32_t block_flag; - - block_flag = (uint8_t)read_8bit(real_offset+0x00,data->streamfile); - block_size = read_32bitBE(real_offset+0x00,data->streamfile) & 0x00FFFFFF; - - if (block_flag == 0x45) /* exit on last block just in case (v1/SPS, empty) */ - break; - - data_size = block_size - 0x0c; - if (data_size % EAXMA_XMA_PACKET_SIZE) - extra_size = EAXMA_XMA_PACKET_SIZE - (data_size % EAXMA_XMA_PACKET_SIZE); - - /* stop if virtual_offset lands inside current block */ - if (data_size + extra_size > seek_virtual_offset) - break; - - real_offset += block_size; - virtual_base += data_size + extra_size; - - if (block_flag == 0x80) /* exit on last block just in case (v0/SNS, full) */ - break; - } - - /* closest we can use for reads */ - data->real_offset = real_offset; - data->virtual_base = virtual_base; - - return virtual_offset; -} - -int64_t ffmpeg_custom_size_eaxma(ffmpeg_codec_data *data) { - - uint64_t virtual_size = data->config.virtual_size; - if (!virtual_size) - return 0; - - return virtual_size + data->header_size; -} - -/* needed to know in meta for fake RIFF */ -size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t real_size, STREAMFILE *streamFile) { - size_t virtual_size = 0; - size_t real_end_offset = real_offset + real_size; - /* EA-XMA always uses late XMA2 streams (2ch + ... + 1/2ch) */ - int num_streams = (channels / 2) + (channels % 2 ? 1 : 0); - - - /* count all SNS/EAXMA blocks size + padding size */ - while (real_offset < real_end_offset) { - int max_packets; - uint32_t block_flag, block_size; - off_t packets_offset; - - block_flag = (uint8_t)read_8bit(real_offset+0x00,streamFile); - block_size = read_32bitBE(real_offset+0x00,streamFile) & 0x00FFFFFF; - packets_offset = real_offset + 0x08; /* 0x04(4): decoded samples */ - - if (block_flag == 0x45) /* exit on last block just in case (v1/SPS, empty) */ - break; - - - max_packets = get_block_max_packets(num_streams, packets_offset, streamFile); - if (max_packets == 0) goto fail; - - /* fixed data_size per block for multichannel, see reads */ - virtual_size += max_packets * num_streams * EAXMA_XMA_PACKET_SIZE; - - real_offset += block_size; - - if (block_flag == 0x80) /* exit on last block just in case (v0/SNS, full) */ - break; - } - - return virtual_size; - -fail: - return 0; -} - -/* a block can have N streams each with a varying number of packets, get max */ -static int get_block_max_packets(int num_streams, off_t packets_offset, STREAMFILE * streamfile) { - int s; - int max_packets = 0; - - for (s = 0; s < num_streams; s++) { - size_t packets_size; - size_t packets_size4 = read_32bitBE(packets_offset, streamfile); /* size * 4, no idea */ - int num_packets; - - if (packets_size4 == 0) { - VGM_LOG("EA XMA: null packets in stream %i at %lx\n", s, (off_t)packets_offset); - goto fail; - } - packets_size = (packets_size4 / 4) - 0x04; - - num_packets = (int)(packets_size / EAXMA_XMA_PACKET_SIZE) + 1; - if (num_packets > max_packets) - max_packets = num_packets; - } - - return max_packets; - -fail: - return 0; -} - -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_switch_opus.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_switch_opus.c deleted file mode 100644 index c44fd0afd..000000000 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_switch_opus.c +++ /dev/null @@ -1,362 +0,0 @@ -#include "coding.h" -#include "ffmpeg_decoder_utils.h" -#include - -#ifdef VGM_USE_FFMPEG - - -/** - * Xiph Opus without Ogg layer and custom packet headers. This creates valid Ogg pages with single Opus packets. - * Wwise opus looks encoded roughly like "opusenc --hard-cbr --framesize 40". The packets are Opus-compliant. - * - * Info, CRC and stuff: - * https://www.opus-codec.org/docs/ - * https://tools.ietf.org/html/rfc7845.html - * https://github.com/hcs64/ww2ogg - */ - -static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule); -static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate); -static size_t make_opus_comment(uint8_t * buf, int buf_size); -static uint32_t get_opus_samples_per_frame(const uint8_t * data, int Fs); - - -size_t ffmpeg_make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) { - int buf_done = 0; - size_t bytes; - - if (buf_size < 0x100) /* approx */ - goto fail; - - /* make header */ - bytes = make_opus_header(buf+buf_done + 0x1c,buf_size, channels, skip, sample_rate); - make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 0, 0); - buf_done += 0x1c + bytes; - - /* make comment */ - bytes = make_opus_comment(buf+buf_done + 0x1c,buf_size); - make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 1, 0); - buf_done += 0x1c + bytes; - - return buf_done; -fail: - return 0; -} - - -int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) { - uint8_t v_buf[0x8000]; /* intermediate buffer, could be simplified */ - int buf_done = 0; - uint64_t real_offset = data->real_offset; - uint64_t virtual_offset = data->virtual_offset - data->header_size; - uint64_t virtual_base = data->virtual_base; - - - if (data->config.sequence == 0) - data->config.sequence = 2; - - /* read and transform Wwise Opus block into Ogg Opus block by making Ogg pages */ - while (buf_done < buf_size) { - int bytes_to_copy, samples_per_frame; - size_t extra_size = 0, gap_size = 0; - size_t data_size = read_32bitBE(real_offset, data->streamfile); - /* 0x00: data size, 0x04: ?, 0x08+: data */ - - /* setup */ - extra_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */ - if (buf_done == 0) /* first read */ - gap_size = virtual_offset - virtual_base; /* might start a few bytes into the block */ - - if (data_size + extra_size > 0x8000) { - VGM_LOG("WW OPUS: total size bigger than buffer at %lx\n", (off_t)real_offset); - return 0; - } - - bytes_to_copy = data_size + extra_size - gap_size; - if (bytes_to_copy > buf_size - buf_done) - bytes_to_copy = buf_size - buf_done; - - /* transform */ - read_streamfile(v_buf + extra_size, real_offset + 0x08, data_size, data->streamfile); - samples_per_frame = get_opus_samples_per_frame(v_buf + extra_size, 48000); /* fixed? */ - make_oggs_page(v_buf,0x8000, data_size, data->config.sequence, data->config.samples_done + samples_per_frame); - memcpy(buf + buf_done, v_buf + gap_size, bytes_to_copy); - - /* move when block is fully done */ - if (data_size + extra_size == bytes_to_copy + gap_size) { - real_offset += 0x04 + 0x04 + data_size; - virtual_base += data_size + extra_size; - data->config.sequence++; - data->config.samples_done += samples_per_frame; - } - - buf_done += bytes_to_copy; - } - - data->real_offset = real_offset; - data->virtual_base = virtual_base; - return buf_size; -} - -int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset) { - int64_t real_offset, virtual_base; - int64_t current_virtual_offset = data->virtual_offset; - int64_t seek_virtual_offset = virtual_offset - data->header_size; - - /* find Wwise block start closest to offset; a 0x1E8 block expands to 0x1D + 0x1E0 (oggs + data) */ - - if (seek_virtual_offset > current_virtual_offset) { /* seek after current: start from current block */ - real_offset = data->real_offset; - virtual_base = data->virtual_base; - } - else { /* seek before current: start from the beginning */ - real_offset = data->real_start; - virtual_base = 0; - data->config.sequence = 0; - data->config.samples_done = 0; - } - - - /* find target block */ - while (virtual_base < seek_virtual_offset) { - size_t extra_size; - size_t data_size = read_32bitBE(real_offset, data->streamfile); - - extra_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */ - - /* stop if virtual_offset lands inside current block */ - if (data_size + extra_size > seek_virtual_offset) - break; - - real_offset += 0x04 + 0x04 + data_size; - virtual_base += data_size + extra_size; - } - - /* closest we can use for reads */ - data->real_offset = real_offset; - data->virtual_base = virtual_base; - - return virtual_offset; -} - -int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data) { - uint64_t real_offset = data->real_start; - uint64_t real_end_offset = data->real_start + data->real_size; - uint64_t virtual_size = data->header_size; - - /* count all Wwise Opus blocks size + OggS page size */ - while (real_offset < real_end_offset) { - size_t extra_size; - size_t data_size = read_32bitBE(real_offset, data->streamfile); - /* 0x00: data size, 0x04: ? (not a sequence or CRC), 0x08+: data */ - - extra_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */ - - real_offset += 0x04 + 0x04 + data_size; - virtual_size += extra_size + data_size; - } - - - return virtual_size; -} - -size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile) { - size_t num_samples = 0; - off_t end_offset = offset + data_size; - - /* count by reading all frames */ - while (offset < end_offset) { - uint8_t buf[4]; - size_t block_size = read_32bitBE(offset, streamFile); - - read_streamfile(buf, offset+8, 4, streamFile); - num_samples += get_opus_samples_per_frame(buf, sample_rate); - - offset += 0x08 + block_size; - } - - return num_samples; -} - -/* ************************************************** */ - -/* from ww2ogg - from Tremor (lowmem) */ -static uint32_t crc_lookup[256]={ - 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, - 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, - 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, - 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, - 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, - 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, - 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, - 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, - 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, - 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, - 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, - 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, - 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, - 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, - 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, - 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, - 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, - 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, - 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, - 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, - 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, - 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, - 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, - 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, - 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, - 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, - 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, - 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, - 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, - 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, - 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, - 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4 -}; - -/* from ww2ogg */ -static uint32_t get_oggs_checksum(uint8_t * data, int bytes) { - uint32_t crc_reg=0; - int i; - - for(i=0;i> 24)&0xff)^data[i]]; - - return crc_reg; -} - -/* from opus_decoder.c */ -static uint32_t get_opus_samples_per_frame(const uint8_t * data, int Fs) { - int audiosize; - if (data[0]&0x80) - { - audiosize = ((data[0]>>3)&0x3); - audiosize = (Fs<>3)&0x3); - if (audiosize == 3) - audiosize = Fs*60/1000; - else - audiosize = (Fs< buf_size) { - VGM_LOG("WW OPUS: buffer can't hold OggS page\n"); - goto fail; - } - - segment_count = (int)(data_size / 0xFF + 1); - - put_32bitBE(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */ - put_8bit (buf+0x04, 0); /* stream structure version, fixed */ - put_8bit (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */ - put_32bitLE(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */ - put_32bitLE(buf+0x0A, (uint32_t)(absolute_granule >> 32 & 0xFFFFFFFF)); /* upper */ - put_32bitLE(buf+0x0E, stream_serial_number); /* for interleaved multi-streams */ - put_32bitLE(buf+0x12, page_sequence); - put_32bitLE(buf+0x16, checksum); /* 0 for now, until all data is written */ - put_8bit (buf+0x1A, segment_count); /* count of all lacing values */ - - /* segment table: size N in "lacing values" (ex. 0x20E=0xFF+FF+10; 0xFF=0xFF+00) */ - page_done = 0x1B; - while (lacing_done < data_size) { - int bytes = data_size - lacing_done; - if (bytes > 0xFF) - bytes = 0xFF; - - put_8bit(buf+page_done, bytes); - page_done++; - lacing_done += bytes; - - if (lacing_done == data_size && bytes == 0xFF) { - put_8bit(buf+page_done, 0x00); - page_done++; - } - } - - /* data */ - //memcpy(buf+page_done, data_buf, data_size); /* data must be copied before this call */ - page_done += data_size; - - /* final checksum */ - checksum = get_oggs_checksum(buf, page_done); - put_32bitLE(buf+0x16, checksum); - - return page_done; - -fail: - return 0; -} - -static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) { - size_t header_size = 0x13; - int output_gain = 0; - int channel_papping_family = 0; - - if (header_size > buf_size) { - VGM_LOG("WW OPUS: buffer can't hold header\n"); - goto fail; - } - - put_32bitBE(buf+0x00, 0x4F707573); /* header magic ("Opus") */ - put_32bitBE(buf+0x04, 0x48656164); /* header magic ("Head") */ - put_8bit (buf+0x08, 1); /* version, fixed */ - put_8bit (buf+0x09, channels); - put_16bitLE(buf+0x0A, skip); - put_32bitLE(buf+0x0c, sample_rate); - put_16bitLE(buf+0x10, output_gain); - put_8bit (buf+0x12, channel_papping_family); - - - return header_size; -fail: - return 0; -} - -static size_t make_opus_comment(uint8_t * buf, int buf_size) { - size_t comment_size; - int vendor_string_length, user_comment_0_length; - const char * vendor_string = "vgmstream"; - const char * user_comment_0_string = "vgmstream Opus converter"; - vendor_string_length = strlen(vendor_string); - user_comment_0_length = strlen(user_comment_0_string); - - comment_size = 0x14 + vendor_string_length + user_comment_0_length; - - if (comment_size > buf_size) { - VGM_LOG("WW OPUS: buffer can't hold comment\n"); - goto fail; - } - - put_32bitBE(buf+0x00, 0x4F707573); /* header magic ("Opus") */ - put_32bitBE(buf+0x04, 0x54616773); /* header magic ("Tags") */ - put_32bitLE(buf+0x08, vendor_string_length); - memcpy(buf+0x0c, vendor_string, vendor_string_length); - put_32bitLE(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */ - put_32bitLE(buf+0x0c + vendor_string_length+0x04, user_comment_0_length); - memcpy(buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length); - - - return comment_size; -fail: - return 0; -} - -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c index ab3cb8293..1f7938925 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c @@ -1,19 +1,28 @@ #include "coding.h" -#include "../util.h" #ifdef VGM_USE_G719 -#include "../stack_alloc.h" +#include + +#define G719_MAX_FRAME_SIZE 0x1000 /* arbitrary max (samples per frame seems to always be 960) */ + g719_codec_data *init_g719(int channel_count, int frame_size) { int i; g719_codec_data *data = NULL; + if (frame_size / sizeof(int16_t) > G719_MAX_FRAME_SIZE) + goto fail; + data = calloc(channel_count, sizeof(g719_codec_data)); /* one decoder per channel */ if (!data) goto fail; for (i = 0; i < channel_count; i++) { data[i].handle = g719_init(frame_size); /* Siren 22 == 22khz bandwidth */ if (!data[i].handle) goto fail; + + /* known values: 0xF0=common (sizeof(int16) * 960/8), 0x140=rare (sizeof(int16) * 1280/8) */ + data[i].code_buffer = malloc(sizeof(int16_t) * frame_size); + if (!data[i].code_buffer) goto fail; } return data; @@ -29,51 +38,42 @@ fail: return NULL; } + void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) { VGMSTREAMCHANNEL *ch = &vgmstream->ch[channel]; g719_codec_data *data = vgmstream->codec_data; g719_codec_data *ch_data = &data[channel]; int i; - if (0 == vgmstream->samples_into_block) - { - VARDECL(int16_t,code_buffer); - ALLOC(code_buffer, vgmstream->interleave_block_size / 2, int16_t); - vgmstream->ch[channel].streamfile->read(ch->streamfile, (uint8_t*)code_buffer, ch->offset, vgmstream->interleave_block_size); - g719_decode_frame(ch_data->handle, code_buffer, ch_data->buffer); + if (0 == vgmstream->samples_into_block) { + read_streamfile((uint8_t*)ch_data->code_buffer, ch->offset, vgmstream->interleave_block_size, ch->streamfile); + g719_decode_frame(ch_data->handle, ch_data->code_buffer, ch_data->buffer); } - for (i = 0; i < samples_to_do; i++) - { + for (i = 0; i < samples_to_do; i++) { outbuf[i*channelspacing] = ch_data->buffer[vgmstream->samples_into_block+i]; } } -void reset_g719(VGMSTREAM *vgmstream) { - g719_codec_data *data = vgmstream->codec_data; +void reset_g719(g719_codec_data * data, int channels) { int i; if (!data) return; - for (i = 0; i < vgmstream->channels; i++) - { + for (i = 0; i < channels; i++) { g719_reset(data[i].handle); } } -void free_g719(VGMSTREAM *vgmstream) { - g719_codec_data *data = (g719_codec_data *) vgmstream->codec_data; +void free_g719(g719_codec_data * data, int channels) { + int i; + if (!data) return; - if (data) - { - int i; - - for (i = 0; i < vgmstream->channels; i++) - { - g719_free(data[i].handle); - } - free(data); + for (i = 0; i < channels; i++) { + g719_free(data[i].handle); + free(data[i].code_buffer); } + free(data); } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c index 648a8b575..6915d9950 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder.c @@ -1,12 +1,16 @@ #include "coding.h" -#include "../util.h" #ifdef VGM_USE_G7221 +#define G7221_MAX_CODES (960/8) /* max frame 0xF0 uint8s = 0xF0/2 uint16s = 960/8 */ + g7221_codec_data * init_g7221(int channel_count, int frame_size) { int i; g7221_codec_data *data = NULL; + if (frame_size / sizeof(int16_t) > G7221_MAX_CODES) + goto fail; + data = calloc(channel_count, sizeof(g7221_codec_data)); /* one decoder per channel */ if (!data) goto fail; @@ -35,15 +39,13 @@ void decode_g7221(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, in g7221_codec_data *ch_data = &data[channel]; int i; - if (0 == vgmstream->samples_into_block) - { - int16_t code_buffer[960/8]; + if (0 == vgmstream->samples_into_block) { + int16_t code_buffer[G7221_MAX_CODES]; vgmstream->ch[channel].streamfile->read(ch->streamfile, (uint8_t*)code_buffer, ch->offset, vgmstream->interleave_block_size); g7221_decode_frame(ch_data->handle, code_buffer, ch_data->buffer); } - for (i = 0; i < samples_to_do; i++) - { + for (i = 0; i < samples_to_do; i++) { outbuf[i*channelspacing] = ch_data->buffer[vgmstream->samples_into_block+i]; } } @@ -54,25 +56,20 @@ void reset_g7221(VGMSTREAM *vgmstream) { int i; if (!data) return; - for (i = 0; i < vgmstream->channels; i++) - { + for (i = 0; i < vgmstream->channels; i++) { g7221_reset(data[i].handle); } } void free_g7221(VGMSTREAM *vgmstream) { g7221_codec_data *data = (g7221_codec_data *) vgmstream->codec_data; + int i; + if (!data) return; - if (data) - { - int i; - - for (i = 0; i < vgmstream->channels; i++) - { - g7221_free(data[i].handle); - } - free(data); + for (i = 0; i < vgmstream->channels; i++) { + g7221_free(data[i].handle); } + free(data); } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c index 8288229f3..61c824fb7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c @@ -1,110 +1,208 @@ #include "coding.h" -void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { + +/* init a HCA stream; STREAMFILE will be duplicated for internal use. */ +hca_codec_data * init_hca(STREAMFILE *streamFile) { + char filename[PATH_LIMIT]; + uint8_t header_buffer[0x2000]; /* hca header buffer data (probable max ~0x400) */ + hca_codec_data * data = NULL; /* vgmstream HCA context */ + int header_size; + int status; + + /* test header */ + if (read_streamfile(header_buffer, 0x00, 0x08, streamFile) != 0x08) + goto fail; + header_size = clHCA_isOurFile(header_buffer, 0x08); + if (header_size < 0 || header_size > 0x1000) + goto fail; + if (read_streamfile(header_buffer, 0x00, header_size, streamFile) != header_size) + goto fail; + + /* init vgmstream context */ + data = calloc(1, sizeof(hca_codec_data)); + if (!data) goto fail; + + /* init library handle */ + data->handle = calloc(1, clHCA_sizeof()); + clHCA_clear(data->handle); + + status = clHCA_DecodeHeader(data->handle, header_buffer, header_size); /* parse header */ + if (status < 0) goto fail; + + status = clHCA_getInfo(data->handle, &data->info); /* extract header info */ + if (status < 0) goto fail; + + data->data_buffer = malloc(data->info.blockSize); + if (!data->data_buffer) goto fail; + + data->sample_buffer = malloc(sizeof(signed short) * data->info.channelCount * data->info.samplesPerBlock); + if (!data->sample_buffer) goto fail; + + /* load streamfile for reads */ + get_streamfile_name(streamFile,filename, sizeof(filename)); + data->streamfile = open_streamfile(streamFile,filename); + if (!data->streamfile) goto fail; + + /* set initial values */ + reset_hca(data); + + return data; + +fail: + free_hca(data); + return NULL; +} + +void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) { int samples_done = 0; - int32_t samples_remain = clHCA_samplesPerBlock - data->sample_ptr; - - void *hca_data = NULL; - - clHCA *hca; + const unsigned int channels = data->info.channelCount; + const unsigned int blockSize = data->info.blockSize; - if ( data->samples_discard ) { - if ( samples_remain <= data->samples_discard ) { - data->samples_discard -= samples_remain; - samples_remain = 0; - } - else { - samples_remain -= data->samples_discard; - data->sample_ptr += data->samples_discard; - data->samples_discard = 0; - } - } - if ( samples_remain > samples_to_do ) samples_remain = samples_to_do; + while (samples_done < samples_to_do) { - memcpy( outbuf, data->sample_buffer + data->sample_ptr * data->info.channelCount, samples_remain * data->info.channelCount * sizeof(sample) ); - - outbuf += samples_remain * data->info.channelCount; - - data->sample_ptr += samples_remain; - - samples_done += samples_remain; - - hca_data = malloc( data->info.blockSize ); - - if ( !hca_data ) return; - - hca = (clHCA *)(data + 1); + if (data->samples_filled) { + int samples_to_get = data->samples_filled; - while ( samples_done < samples_to_do ) { - const unsigned int blockSize = data->info.blockSize; - const unsigned int channelCount = data->info.channelCount; - const unsigned int address = data->info.dataOffset + data->curblock * blockSize; - - if (data->curblock >= data->info.blockCount) { - memset(outbuf, 0, (samples_to_do - samples_done) * channelCount * sizeof(sample)); - break; + if (data->samples_to_discard) { + /* discard samples for looping */ + if (samples_to_get > data->samples_to_discard) + samples_to_get = data->samples_to_discard; + data->samples_to_discard -= samples_to_get; + } + else { + /* get max samples and copy */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + + memcpy(outbuf + samples_done*channels, + data->sample_buffer + data->samples_consumed*channels, + samples_to_get*channels * sizeof(sample)); + samples_done += samples_to_get; + } + + /* mark consumed samples */ + data->samples_consumed += samples_to_get; + data->samples_filled -= samples_to_get; } - - if ( read_streamfile((uint8_t*) hca_data, data->start + address, blockSize, data->streamfile) != blockSize ) - break; - - if ( clHCA_Decode( hca, hca_data, blockSize, address ) < 0 ) - break; - - ++data->curblock; + else { + off_t offset = data->info.headerSize + data->current_block * blockSize; + int status; + size_t bytes; - clHCA_DecodeSamples16( hca, data->sample_buffer ); - - samples_remain = clHCA_samplesPerBlock; - data->sample_ptr = 0; - if ( data->samples_discard ) { - if ( samples_remain <= data->samples_discard ) { - data->samples_discard -= samples_remain; - samples_remain = 0; - } - else { - samples_remain -= data->samples_discard; - data->sample_ptr = data->samples_discard; - data->samples_discard = 0; - } - } + /* EOF/error */ + if (data->current_block >= data->info.blockCount) { + memset(outbuf, 0, (samples_to_do - samples_done) * channels * sizeof(sample)); + break; + } - if ( samples_remain > samples_to_do - samples_done ) samples_remain = samples_to_do - samples_done; - memcpy( outbuf, data->sample_buffer, samples_remain * channelCount * sizeof(sample) ); - samples_done += samples_remain; - outbuf += samples_remain * channelCount; - data->sample_ptr = samples_remain; - } - - free( hca_data ); + /* read frame */ + bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile); + if (bytes != blockSize) { + VGM_LOG("HCA: read %x vs expected %x bytes at %"PRIx64"\n", bytes, blockSize, (off64_t)offset); + break; + } + + /* decode frame */ + status = clHCA_DecodeBlock(data->handle, (void*)(data->data_buffer), blockSize); + if (status < 0) { + VGM_LOG("HCA: decode fail at %"PRIx64", code=%i\n", (off64_t)offset, status); + break; + } + + /* extract samples */ + clHCA_ReadSamples16(data->handle, data->sample_buffer); + + data->current_block++; + data->samples_consumed = 0; + data->samples_filled += data->info.samplesPerBlock; + } + } } - -void reset_hca(VGMSTREAM *vgmstream) { - hca_codec_data *data = vgmstream->codec_data; +void reset_hca(hca_codec_data * data) { if (!data) return; - data->curblock = 0; - data->sample_ptr = clHCA_samplesPerBlock; - data->samples_discard = 0; + data->current_block = 0; + data->samples_filled = 0; + data->samples_consumed = 0; + data->samples_to_discard = data->info.encoderDelay; } -void loop_hca(VGMSTREAM *vgmstream) { - hca_codec_data *data = (hca_codec_data *)(vgmstream->codec_data); +void loop_hca(hca_codec_data * data) { if (!data) return; - data->curblock = data->info.loopStart; - data->sample_ptr = clHCA_samplesPerBlock; - data->samples_discard = 0; + data->current_block = data->info.loopStartBlock; + data->samples_filled = 0; + data->samples_consumed = 0; + data->samples_to_discard = data->info.loopStartDelay; } void free_hca(hca_codec_data * data) { - if (data) { - clHCA *hca = (clHCA *)(data + 1); - clHCA_done(hca); - if (data->streamfile) - close_streamfile(data->streamfile); - free(data); - } + if (!data) return; + + close_streamfile(data->streamfile); + clHCA_done(data->handle); + free(data->handle); + free(data->data_buffer); + free(data->sample_buffer); + free(data); +} + + +#define HCA_KEY_MAX_BLANK_FRAMES 15 /* ignored up to N blank frames (not uncommon to have ~10, if more something is off) */ +#define HCA_KEY_MAX_TEST_FRAMES 10 /* 5~15 should be enough, but mostly silent or badly mastered files may need more */ +#define HCA_KEY_MAX_ACCEPTABLE_SCORE 300 /* unlikely to work correctly, 10~30 may be ok */ + +/* Test a number of frames if key decrypts correctly. + * Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better) */ +int test_hca_key(hca_codec_data * data, unsigned long long keycode) { + size_t test_frame = 0, current_frame = 0, blank_frames = 0; + int total_score = 0; + const unsigned int blockSize = data->info.blockSize; + + + clHCA_SetKey(data->handle, keycode); + + while (test_frame < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) { + off_t offset = data->info.headerSize + current_frame * blockSize; + int score; + size_t bytes; + + /* read frame */ + bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile); + if (bytes != blockSize) { + total_score = -1; + break; + } + + /* test frame */ + score = clHCA_TestBlock(data->handle, (void*)(data->data_buffer), blockSize); + if (score < 0) { + total_score = -1; + break; + } + + current_frame++; + + /* skip blank block at the beginning */ + if (score == 0 && blank_frames < HCA_KEY_MAX_BLANK_FRAMES) { + blank_frames++; + continue; + } + + test_frame++; + total_score += score; + + /* too far, don't bother checking more frames */ + if (total_score > HCA_KEY_MAX_ACCEPTABLE_SCORE) + break; + } + + /* signal best possible score */ + if (total_score > 0 && total_score <= HCA_KEY_MAX_TEST_FRAMES) { + total_score = 1; + } + + return total_score; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index 9fc8902d7..b7c3015b3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -147,7 +147,7 @@ static void snds_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, *hist1 = clamp16(sample_decoded); } -/* Omikron: The Nomad Soul, algorithm by aluigi */ +/* Omikron: The Nomad Soul, algorithm from the .exe */ static void otns_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; @@ -156,8 +156,8 @@ static void otns_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, step = ADPCMTable[*step_index]; delta = 0; - if(sample_nibble & 4) delta = step << 2; - if(sample_nibble & 2) delta += step << 1; + if(sample_nibble & 4) delta = step * 4; + if(sample_nibble & 2) delta += step * 2; if(sample_nibble & 1) delta += step; delta >>= 2; if (sample_nibble & 8) delta = -delta; @@ -169,6 +169,44 @@ static void otns_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, if (*step_index > 88) *step_index=88; } +/* Fairly OddParents (PC) .WV6: minor variation, reverse engineered from the .exe */ +static void wv6_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 & 0x7); + delta = ((delta * step) >> 3) + ((delta * step) >> 2); + 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; +} + +/* Lego Racers (PC) .TUN variation, reverse engineered from the .exe */ +static void alp_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 & 0x7); + delta = (delta * step) >> 2; + 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 */ /* ************************************ */ @@ -269,6 +307,50 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * stream->adpcm_step_index = step_index; } +/* WV6 IMA, DVI IMA with custom nibble expand */ +void decode_wv6_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 + + //no header + + for (i=first_sample,sample_count=0; ioffset + i/2; + int nibble_shift = (i&1?0:4); //high nibble first + + wv6_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* ALT IMA, DVI IMA with custom nibble expand */ +void decode_alp_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 + + //no header + + for (i=first_sample,sample_count=0; ioffset + i/2; + int nibble_shift = (i&1?0:4); //high nibble first + + alp_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + /* ************************************ */ /* MS-IMA */ /* ************************************ */ @@ -389,22 +471,26 @@ void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o /* ************************************ */ /* 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; + * Defined in Xbox's SDK. Usable in mono or stereo modes (both suitable for interleaved multichannel). */ +void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { + int i, frames_in, sample_pos = 0, block_samples, frame_size; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; + off_t frame_offset; - /* internal interleave (fixed size), mixed channels */ - int block_samples = (0x24-0x4) * 2; + /* external interleave (fixed size), stereo/mono */ + block_samples = (0x24 - 0x4) * 2; + frames_in = first_sample / block_samples; first_sample = first_sample % block_samples; + frame_size = is_stereo ? 0x24*2 : 0x24; - /* normal header (hist+step+reserved), per stereo/mono channel in blocks */ + frame_offset = stream->offset + frame_size*frames_in; + + /* normal header (hist+step+reserved), stereo/mono */ if (first_sample == 0) { - off_t header_offset = (channelspacing & 1) ? - stream->offset + 0x24*(channel) + 0x00: - stream->offset + 0x48*(channel/2) + 0x04*(channel%2); + off_t header_offset = is_stereo ? + frame_offset + 0x04*(channel % 2) : + frame_offset + 0x00; hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); step_index = read_8bit(header_offset+0x02,stream->streamfile); @@ -412,32 +498,27 @@ void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac 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; + outbuf[sample_pos] = (short)(hist1); + sample_pos += channelspacing; first_sample += 1; samples_to_do -= 1; } - /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel, in stereo blocks) */ + /* decode nibbles (layout: straight in mono or 4 bytes per channel in stereo) */ 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 */ + off_t byte_offset = is_stereo ? + frame_offset + 0x04*2 + 0x04*(channel % 2) + 0x04*2*((i-1)/8) + ((i-1)%8)/2 : + frame_offset + 0x04 + (i-1)/2; + int nibble_shift = (!((i-1)&1) ? 0:4); /* low first */ - /* must skip last nibble per official decoder, probably not needed though */ + /* 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; + outbuf[sample_pos] = (short)(hist1); + sample_pos += 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; } @@ -486,50 +567,6 @@ void decode_xbox_ima_mch(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel stream->adpcm_step_index = step_index; } -/* 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 (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) { - 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 */ @@ -894,6 +931,80 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci stream->adpcm_step_index = step_index; } +/* IMA with variable frame formats controlled by the block layout. The original code uses + * tables mapping all standard IMA combinations (to optimize calculations), but decodes the same. + * Based on HCS's and Nisto's reverse engineering in h4m_audio_decode. */ +void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format) { + int i, samples_done = 0; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + size_t header_size; + int is_stereo = (channelspacing > 1); + + /* external interleave (blocked, should call 1 frame) */ + + /* custom header, per channel */ + if (first_sample == 0) { + int channel_pos = is_stereo ? (1 - channel) : channel; /* R hist goes first */ + switch(frame_format) { + case 1: /* combined hist+index */ + hist1 = read_16bitBE(stream->offset + 0x02*channel_pos + 0x00,stream->streamfile) & 0xFFFFFF80; + step_index = (uint8_t)read_8bit(stream->offset + 0x02*channel_pos + 0x01,stream->streamfile) & 0x7f; + break; + case 3: /* separate hist+index */ + hist1 = read_16bitBE(stream->offset + 0x03*channel_pos + 0x00,stream->streamfile); + step_index = (uint8_t)read_8bit(stream->offset + 0x03*channel_pos + 0x02,stream->streamfile); + break; + case 2: /* no hist/index (continues from previous frame) */ + default: + break; + } + + /* write header sample (last nibble is skipped) */ + if (frame_format == 1 || frame_format == 3) { + outbuf[samples_done * channelspacing] = (short)hist1; + samples_done++; + samples_to_do--; + } + + /* clamp corrupted data just in case */ + if (step_index < 0) step_index = 0; + if (step_index > 88) step_index = 88; + } + else { + /* offset adjust for header sample */ + if (frame_format == 1 || frame_format == 3) { + first_sample--; + } + } + + /* offset adjust */ + switch(frame_format) { + case 1: header_size = (channelspacing*0x02); break; + case 3: header_size = (channelspacing*0x03); break; + default: header_size = 0; break; + } + + /* decode block nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = is_stereo ? + stream->offset + header_size + i : /* stereo: one nibble per channel */ + stream->offset + header_size + i/2; /* mono: consecutive nibbles */ + int nibble_shift = is_stereo ? + (!(channel&1) ? 0:4) : /* stereo: L=low, R=high */ + (!(i&1) ? 0:4); /* mono: low first */ + + std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + + outbuf[samples_done * channelspacing] = (short)(hist1); + samples_done++; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* ************************************************************* */ size_t ima_bytes_to_samples(size_t bytes, int channels) { /* 2 samples per byte (2 nibbles) in stereo or mono config */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index a7737ddf9..990c28732 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -127,16 +127,37 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d goto fail; current_data_size = info.frame_size; - /* get FSB padding for Layer III or multichannel Layer II (Layer I doesn't seem to be supported) + /* get FSB padding for Layer III or multichannel Layer II (Layer I isn't supported by FMOD). * Padding sometimes contains garbage like the next frame header so we can't feed it to mpg123 or it gets confused. */ if ((info.layer == 3 && data->config.fsb_padding) || data->config.fsb_padding == 16) { current_padding = (current_data_size % data->config.fsb_padding) ? data->config.fsb_padding - (current_data_size % data->config.fsb_padding) : 0; + + /* Rare Mafia II (PS3) bug (GP_0701_music multilang only): some frame paddings "4" are incorrect, + * calcs give 0xD0+0x00 but need 0xD0+0x04 (unlike all other fsbs, which never do that). + * FMOD tools decode fine, so they may be doing special detection too, since even + * re-encoding the same file and using the same FSB flags/modes won't trigger the bug. */ + if (info.layer == 3 && data->config.fsb_padding == 4 && current_data_size == 0xD0) { + uint32_t next_header; + off_t next_offset; + + next_offset = stream->offset + current_data_size + current_padding; + if (current_interleave && ((next_offset - stream->channel_start_offset + current_interleave_pre + current_interleave_post) % current_interleave == 0)) { + next_offset += current_interleave_pre + current_interleave_post; + } + + next_header = read_32bitBE(next_offset, stream->streamfile); + if ((next_header & 0xFFE00000) != 0xFFE00000) { /* doesn't land in a proper frame, fix sizes and hope */ + VGM_LOG_ONCE("MPEG FSB: stream with wrong padding found\n"); + current_padding = 0x04; + } + } + } VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding, - "MPEG FSB: %i streams with non-constant interleave found @ 0x%08lx\n", data->streams_size, stream->offset); + "MPEG FSB: %i streams with non-constant interleave found @ 0x%08"PRIx64"\n", data->streams_size, (off64_t)stream->offset); break; case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */ @@ -267,9 +288,9 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * /* calculate frame length (from hcs's fsb_mpeg) */ switch (info->frame_samples) { - case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; - case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; - case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; + case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; /* 384/32 = 12 */ + case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 576/8 = 72 */ + case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 1152/8 = 144 */ default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c index 4701975d5..24b2c5b83 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c @@ -59,7 +59,7 @@ static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, o } fail: - VGM_LOG("AWC: can't find repeat size, new=0x%08lx, last=0x%08lx\n", new_offset, last_offset); + VGM_LOG("AWC: can't find repeat size, new=0x%08"PRIx64", last=0x%08"PRIx64"\n", (off64_t)new_offset, (off64_t)last_offset); return 0; /* keep on truckin' */ } 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 6c1c6d76a..f21c09f5d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -103,7 +103,7 @@ int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, ok = ealayer3_parse_frame(data, &is, &eaf); if (!ok) goto fail; } - VGM_ASSERT(!eaf.mpeg1, "MPEG EAL3: mpeg2 found at 0x%lx\n", start_offset); /* untested */ + //;VGM_ASSERT(!eaf.mpeg1, "MPEG EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */ *coding_type = coding_MPEG_ealayer3; data->channels_per_frame = eaf.channels; @@ -419,7 +419,7 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream* is_0, ealayer3_frame_info* || eaf_0->version != eaf_1->version || eaf_0->granule_index == eaf_1->granule_index || !eaf_0->common_size || !eaf_1->common_size)) { - VGM_LOG("MPEG EAL3: EA-frames for MPEG1 don't match at 0x%lx\n", os->info_offset); + VGM_LOG("MPEG EAL3: EA-frames for MPEG1 don't match\n"); goto fail; } @@ -529,7 +529,7 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream* is_0, ealayer3_frame_info* if (os->b_off/8 > expected_frame_size) { /* bit reservoir! shouldn't happen with free bitrate, otherwise it's hard to fix as needs complex buffering/calcs */ - VGM_LOG("MPEG EAL3: written 0x%lx but expected less than 0x%x at 0x%lx\n", os->b_off/8, expected_frame_size, os->info_offset); + VGM_LOG("MPEG EAL3: written 0x%"PRIx64" but expected less than 0x%x at 0x%"PRIx64"\n", (off64_t)(os->b_off/8), expected_frame_size, (off64_t)os->info_offset); } else { /* fill ancillary data (should be ignored, but 0x00 seems to improve mpg123's free bitrate detection) */ @@ -564,8 +564,8 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d if (!eaf->pcm_size) return 1; - VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%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); + VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%"PRIx64"\n", eaf->v1_pcm_decode_discard, (off64_t)stream->offset); + VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%"PRIx64"\n", eaf->v1_pcm_number, (off64_t)stream->offset); /* read + write PCM block samples (always BE) */ for (i = 0; i < eaf->v1_pcm_number * data->channels_per_frame; i++) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index 55d805181..d006a7d8f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -55,11 +55,11 @@ mpeg_codec_data *init_mpeg(STREAMFILE *streamfile, off_t start_offset, coding_t rc = mpg123_decode(main_m, data->buffer,data->buffer_size, NULL,0, &bytes_done); if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) { - VGM_LOG("MPEG: unable to set up mpg123 @ 0x%08lx to 0x%08lx\n", start_offset, read_offset); + VGM_LOG("MPEG: unable to set up mpg123 at start offset\n"); goto fail; //handle MPG123_DONE? } if (read_offset > 0x5000) { /* don't hang in some incorrectly detected formats */ - VGM_LOG("MPEG: unable to find mpeg data @ 0x%08lx to 0x%08lx\n", start_offset, read_offset); + VGM_LOG("MPEG: unable to find mpeg data at start offset\n"); goto fail; } @@ -401,7 +401,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break; } if (!ok) { - VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset); + VGM_LOG("MPEG: cannot parse frame @ around %"PRIx64"\n",(off64_t)stream->offset); goto decode_fail; /* mpg123 could resync but custom MPEGs wouldn't need that */ } //;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", ms->bytes_in_buffer, stream->offset); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c index e3b7ef0a8..646027767 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c @@ -1,17 +1,15 @@ #include "../util.h" #include "coding.h" -/* used to compute next scale */ -static const int ADPCMTable[16] = -{ + +static const int msadpcm_steps[16] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; -static const int ADPCMCoeffs[7][2] = -{ +static const int msadpcm_coefs[7][2] = { { 256, 0 }, { 512, -256 }, { 0, 0 }, @@ -23,138 +21,203 @@ static const int ADPCMCoeffs[7][2] = void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) { VGMSTREAMCHANNEL *ch1,*ch2; - int i; - int framesin; STREAMFILE *streamfile; - off_t offset; - - framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream); - first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream); + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; ch1 = &vgmstream->ch[0]; ch2 = &vgmstream->ch[1]; streamfile = ch1->streamfile; - offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream); + /* external interleave (variable size), stereo */ + bytes_per_frame = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = ch1->offset + frames_in*bytes_per_frame; + + /* parse frame header */ + if (first_sample == 0) { + ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0]; + ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1]; + ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0]; + ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1]; + ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile); + ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile); + ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile); + ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile); + ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile); + ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile); + } + + /* write header samples (needed) */ if (first_sample==0) { - ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0]; - ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1]; - ch2->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][0]; - ch2->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][1]; - ch1->adpcm_scale = read_16bitLE(offset+2,streamfile); - ch2->adpcm_scale = read_16bitLE(offset+4,streamfile); - ch1->adpcm_history1_16 = read_16bitLE(offset+6,streamfile); - ch2->adpcm_history1_16 = read_16bitLE(offset+8,streamfile); - ch1->adpcm_history2_16 = read_16bitLE(offset+10,streamfile); - ch2->adpcm_history2_16 = read_16bitLE(offset+12,streamfile); - outbuf[0] = ch1->adpcm_history2_16; outbuf[1] = ch2->adpcm_history2_16; - - outbuf+=2; + outbuf += 2; first_sample++; samples_to_do--; } - if (first_sample==1 && samples_to_do > 0) { + if (first_sample == 1 && samples_to_do > 0) { outbuf[0] = ch1->adpcm_history1_16; outbuf[1] = ch2->adpcm_history1_16; - - outbuf+=2; + outbuf += 2; first_sample++; samples_to_do--; } - for (i=first_sample; ich[j]; - int sample_nibble = - (j == 0 ? - get_high_nibble_signed(read_8bit(offset+14+i-2,streamfile)) : - get_low_nibble_signed(read_8bit(offset+14+i-2,streamfile)) - ); - int32_t hist1,hist2; - int32_t predicted; + for (ch = 0; ch < 2; ch++) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch]; + int32_t hist1,hist2, predicted; + int sample_nibble = (ch == 0) ? /* L = high nibble first */ + get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) : + get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile)); - hist1 = ch->adpcm_history1_16; - hist2 = ch->adpcm_history2_16; - predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1]; - predicted /= 256; - predicted += sample_nibble*ch->adpcm_scale; + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted / 256; + predicted = predicted + sample_nibble*stream->adpcm_scale; outbuf[0] = clamp16(predicted); - ch->adpcm_history2_16 = ch->adpcm_history1_16; - ch->adpcm_history1_16 = outbuf[0]; - ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] * - ch->adpcm_scale) / 256; - if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10; + + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; outbuf++; } } } -void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) { - VGMSTREAMCHANNEL *ch1; - int i; - int framesin; - STREAMFILE *streamfile; - off_t offset; +void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; - framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream); - first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream); + /* external interleave (variable size), mono */ + bytes_per_frame = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; - ch1 = &vgmstream->ch[0]; - streamfile = ch1->streamfile; - offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream); + frame_offset = stream->offset + frames_in*bytes_per_frame; - if (first_sample==0) { - ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0]; - ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1]; - ch1->adpcm_scale = read_16bitLE(offset+1,streamfile); - ch1->adpcm_history1_16 = read_16bitLE(offset+3,streamfile); - ch1->adpcm_history2_16 = read_16bitLE(offset+5,streamfile); + /* parse frame header */ + if (first_sample == 0) { + stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; + stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; + stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); + stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); + stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); + } - outbuf[0] = ch1->adpcm_history2_16; - - outbuf++; + /* write header samples (needed) */ + if (first_sample == 0) { + outbuf[0] = stream->adpcm_history2_16; + outbuf += channelspacing; first_sample++; samples_to_do--; } - if (first_sample==1 && samples_to_do > 0) { - outbuf[0] = ch1->adpcm_history1_16; - - outbuf++; + if (first_sample == 1 && samples_to_do > 0) { + outbuf[0] = stream->adpcm_history1_16; + outbuf += channelspacing; first_sample++; samples_to_do--; } - for (i=first_sample; ich[0]; - int sample_nibble = - (i & 1 ? - get_low_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile)) : - get_high_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile)) - ); - int32_t hist1,hist2; - int32_t predicted; + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist1,hist2, predicted; + int sample_nibble = (i & 1) ? /* high nibble first */ + get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : + get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); - hist1 = ch->adpcm_history1_16; - hist2 = ch->adpcm_history2_16; - predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1]; - predicted /= 256; - predicted += sample_nibble*ch->adpcm_scale; - outbuf[0] = clamp16(predicted); - ch->adpcm_history2_16 = ch->adpcm_history1_16; - ch->adpcm_history1_16 = outbuf[0]; - ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] * - ch->adpcm_scale) / 256; - if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10; + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted / 256; + predicted = predicted + sample_nibble*stream->adpcm_scale; + outbuf[0] = clamp16(predicted); - outbuf++; - } + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; + + outbuf += channelspacing; + } +} + +/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order + * (their tools may convert to float/others but internally it's all PCM16, from debugging). */ +void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; + + /* external interleave (variable size), mono */ + bytes_per_frame = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = stream->offset + frames_in*bytes_per_frame; + + /* parse frame header */ + if (first_sample == 0) { + stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; + stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; + stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); + stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */ + stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); + } + + /* write header samples (needed) */ + if (first_sample == 0) { + outbuf[0] = stream->adpcm_history2_16; + outbuf += channelspacing; + first_sample++; + samples_to_do--; + } + if (first_sample == 1 && samples_to_do > 0) { + outbuf[0] = stream->adpcm_history1_16; + outbuf += channelspacing; + first_sample++; + samples_to_do--; + } + + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist1,hist2, predicted; + int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */ + get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : + get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); + + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted >> 8; /* probably no difference vs MSADPCM */ + predicted = predicted + sample_nibble*stream->adpcm_scale; + outbuf[0] = clamp16(predicted); + + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; + + outbuf += channelspacing; } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c index 21c950cd6..4f4ca57aa 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c @@ -79,7 +79,7 @@ static void mta2_block_update(VGMSTREAMCHANNEL * stream) { } if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */ - VGM_LOG("MTA2: bad block @ %08lx\n", stream->offset); + VGM_LOG("MTA2: bad block @ 0x%"PRIx64"\n", (off64_t)stream->offset); stream->offset += 0x10; repeat = 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c index 642aa34da..7b9778eff 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c @@ -109,7 +109,7 @@ static void mtaf_block_update(VGMSTREAMCHANNEL * stream) { } if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */ - VGM_LOG("MTAF: bad block @ %08lx\n", stream->offset); + VGM_LOG("MTAF: bad block @ %"PRIx64"\n", (off64_t)stream->offset); stream->offset += 0x10; repeat = 0; } @@ -125,7 +125,7 @@ static void mtaf_block_update(VGMSTREAMCHANNEL * stream) { } #endif -void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int channels) { +void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int32_t sample_count; int i; int c = channel%2; /* global channel to track channel */ @@ -146,7 +146,7 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t init_idx = read_16bitLE(stream->offset+4+0+c*2, stream->streamfile); /* step-L/R */ int32_t init_hist = read_16bitLE(stream->offset+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */ - VGM_ASSERT(init_idx < 0 || init_idx > 31, "MTAF: bad header idx @ 0x%lx\n", stream->offset); + VGM_ASSERT(init_idx < 0 || init_idx > 31, "MTAF: bad header idx @ 0x%"PRIx64"\n", (off64_t)stream->offset); /* avoid index out of range in corrupt files */ if (init_idx < 0) { init_idx = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ngc_dtk_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ngc_dtk_decoder.c index 21d189021..b9138c90d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ngc_dtk_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ngc_dtk_decoder.c @@ -1,51 +1,65 @@ #include "coding.h" #include "../util.h" + +/* Nintendo GC Disc TracK streaming ADPCM (similar to CD-XA) */ void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i=first_sample; - int32_t sample_count; - - int framesin = first_sample/28; - - uint8_t q = read_8bit(framesin*32+stream->offset+channel,stream->streamfile); + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t coef_index, shift_factor; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; - first_sample = first_sample%28; - for (i=first_sample,sample_count=0; ioffset+4+i,stream->streamfile); + /* external interleave (fixed size), stereo */ + bytes_per_frame = 0x20; + samples_per_frame = 28; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; - int32_t hist=0; + /* parse frame L/R header (repeated at 0x03/04) */ + frame_offset = stream->offset + bytes_per_frame*frames_in; + coef_index = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 0) & 0xf; + /* rare but happens, also repeated headers don't match (ex. Ikaruga (GC) SONG02.adp) */ + VGM_ASSERT_ONCE(coef_index > 4 || shift_factor > 12, "DTK: incorrect coefs/shift at %"PRIx64"\n", (off64_t)frame_offset); - switch (q>>4) - { + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist = 0, new_sample; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x04+i,stream->streamfile); + + /* apply XA filters << 6 */ + switch(coef_index) { case 0: - hist = 0; + hist = 0; // (hist1 * 0) - (hist2 * 0); break; case 1: - hist = (hist1 * 0x3c); + hist = (hist1 * 60); // - (hist2 * 0); break; case 2: - hist = (hist1 * 0x73) - (hist2 * 0x34); + hist = (hist1 * 115) - (hist2 * 52); break; case 3: - hist = (hist1 * 0x62) - (hist2 * 0x37); + hist = (hist1 * 98) - (hist2 * 55); break; } - - hist = (hist+0x20)>>6; + hist = (hist + 32) >> 6; if (hist > 0x1fffff) hist = 0x1fffff; if (hist < -0x200000) hist = -0x200000; + new_sample = (channel==0) ? /* L=low nibble first */ + get_low_nibble_signed(nibbles) : + get_high_nibble_signed(nibbles); + new_sample = (new_sample << 12) >> shift_factor; + new_sample = (new_sample << 6) + hist; + hist2 = hist1; + hist1 = new_sample; - hist1 = ((((channel==0? - get_low_nibble_signed(sample_byte): - get_high_nibble_signed(sample_byte) - ) << 12) >> (q & 0xf)) << 6) + hist; - - outbuf[sample_count] = clamp16(hist1 >> 6); + outbuf[sample_count] = clamp16(new_sample >> 6); + sample_count += channelspacing; } stream->adpcm_history1_32 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c index 406f0dff8..d958d4fd1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c @@ -2,7 +2,7 @@ #include "../util.h" #include -void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i; int32_t sample_count; @@ -11,7 +11,7 @@ void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci } } -void decode_pcm16BE(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) { int i; int32_t sample_count; @@ -20,6 +20,15 @@ void decode_pcm16BE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci } } +void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) { + int i, sample_count; + int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; + + for (i=first_sample,sample_count=0; ioffset+i*2*channelspacing,stream->streamfile); + } +} + void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i; int32_t sample_count; @@ -38,14 +47,13 @@ void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac } } -void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_pcm8_unsigned(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*channelspacing,stream->streamfile); - if (v&0x80) v = 0-(v&0x7f); - outbuf[sample_count] = v*0x100; + int16_t v = (uint8_t)read_8bit(stream->offset+i,stream->streamfile); + outbuf[sample_count] = v*0x100 - 0x8000; } } @@ -59,22 +67,14 @@ void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int ch } } -void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_pcm8_sb(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,stream->streamfile); - outbuf[sample_count] = v*0x100 - 0x8000; - } -} - -void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) { - int i, sample_count; - int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; - - for (i=first_sample,sample_count=0; ioffset+i*2*channelspacing,stream->streamfile); + if (v&0x80) v = 0-(v&0x7f); + outbuf[sample_count] = v*0x100; } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c index 0fcc3751b..057ced733 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c @@ -1,40 +1,24 @@ -#include #include "coding.h" -#include "../util.h" -/* for some algos, maybe closer to the real thing */ -#define VAG_USE_INTEGER_TABLE 0 -/* PS ADPCM table (precalculated divs) */ -static const double VAG_f[16][2] = { +/* PS-ADPCM table, defined as rational numbers (as in the spec) */ +static const double ps_adpcm_coefs_f[5][2] = { { 0.0 , 0.0 }, { 60.0 / 64.0 , 0.0 }, { 115.0 / 64.0 , -52.0 / 64.0 }, { 98.0 / 64.0 , -55.0 / 64.0 }, { 122.0 / 64.0 , -60.0 / 64.0 }, - /* extended table from PPSSPP (PSP emu), found by tests - * (only seen in inFamous PS3, very rare, possibly "SVAG" or "VAG-HE") */ - { 0.0 , 0.0 }, - { 0.0 , 0.0 }, - { 52.0 / 64.0 , 0.0 }, - { 55.0 / 64.0 , -2.0 / 64.0 }, - { 60.0 / 64.0 ,-125.0 / 64.0 }, - { 0.0 , 0.0 }, - { 0.0 , -91.0 / 64.0 }, - { 0.0 , 0.0 }, - { 2.0 / 64.0 ,-216.0 / 64.0 }, - { 125.0 / 64.0 , -6.0 / 64.0 }, - { 0.0 ,-151.0 / 64.0 }, }; -#if VAG_USE_INTEGER_TABLE -/* PS ADPCM table */ -static const int8_t VAG_coefs[5][2] = { + +/* PS-ADPCM table, defined as spec_coef*64 (for int implementations) */ +static const int ps_adpcm_coefs_i[5][2] = { { 0 , 0 }, { 60 , 0 }, { 115 , -52 }, { 98 , -55 }, { 122 , -60 }, - /* extended */ +#if 0 + /* extended table from PPSSPP (PSP emu), found by tests (unused?) */ { 0 , 0 }, { 0 , 0 }, { 52 , 0 }, @@ -46,163 +30,69 @@ static const int8_t VAG_coefs[5][2] = { { 2 ,-216 }, { 125 , -6 }, { 0 ,-151 }, -}; #endif +}; -/** - * Sony's PS ADPCM (sometimes called VAG), decodes 16 bytes into 28 samples. - * The first 2 bytes are a header (shift, predictor, optional flag). - * All variants are the same with minor differences. +/* Decodes Sony's PS-ADPCM (sometimes called SPU-ADPCM or VAG, just "ADPCM" in the SDK docs). + * Very similar to XA ADPCM (see xa_decoder for extended info). * - * Flags: - * 0x0: Nothing - * 0x1: End marker + decode - * 0x2: Loop region - * 0x3: Loop end - * 0x4: Start marker - * 0x5: ? - * 0x6: Loop start - * 0x7: End marker + don't decode - * 0x8+ Not valid + * Some official PC tools decode using float coefs (from the spec), as does this code, but + * consoles/games/libs would vary (PS1 could do it in hardware using BRR/XA's logic, FMOD/PS3 + * may use int math in software, etc). There are inaudible rounding diffs between implementations. */ -/* default */ -void decode_psx(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; - - predict_nr = read_8bit(stream->offset+framesin*16,stream->streamfile) >> 4; - shift_factor = read_8bit(stream->offset+framesin*16,stream->streamfile) & 0xf; - flag = read_8bit(stream->offset+framesin*16+1,stream->streamfile); /* only lower nibble needed */ - - first_sample = first_sample % 28; - - for (i=first_sample,sample_count=0; ioffset+(framesin*16)+2+i/2,stream->streamfile); - - scale = ((i&1 ? /* odd/even byte */ - 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) { - - 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; - - int framesin = first_sample/28; - - predict_nr = read_8bit(stream->offset+framesin*16,stream->streamfile) >> 4; - shift_factor = read_8bit(stream->offset+framesin*16,stream->streamfile) & 0xf; - first_sample = first_sample % 28; - - for (i=first_sample,sample_count=0; ioffset+(framesin*16)+2+i/2,stream->streamfile); - - 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; -} - - -/* configurable frame size, with no flag - * Found in PS3 Afrika (SGXD type 5) in size 4, FF XI in sizes 3/5/9/41, Blur and James Bond in size 33. */ -void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) { - uint8_t predict_nr, shift, byte; - int16_t scale = 0; - - int32_t sample; +/* standard PS-ADPCM (float math version) */ +void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags) { + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t coef_index, shift_factor, flag; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; - int i, sample_count, bytes_per_frame, samples_per_frame; - const int header_size = 1; - int framesin; - - bytes_per_frame = frame_size - header_size; - samples_per_frame = bytes_per_frame * 2; - - framesin = first_sample / samples_per_frame; - - /* 1 byte header: predictor = 1st, shift = 2nd */ - byte = (uint8_t)read_8bit(stream->offset+framesin*frame_size+0,stream->streamfile); - predict_nr = byte >> 4; - shift = byte & 0x0f; - + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x10; + samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */ + frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; - if (first_sample & 1) { /* if restarting on a high nibble, read byte first */ - byte = (uint8_t)read_8bit(stream->offset+(framesin*frame_size)+header_size+first_sample/2,stream->streamfile); - } + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame*frames_in; + coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; + flag = (uint8_t)read_8bit(frame_offset+0x01,stream->streamfile); /* only lower nibble needed */ - for (i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { - sample = 0; + VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %"PRIx64"\n", (off64_t)frame_offset); + if (coef_index > 5) /* needed by inFamous (PS3) (maybe it's supposed to use more filters?) */ + coef_index = 0; /* upper filters aren't used in PS1/PS2, maybe in PSP/PS3? */ + if (shift_factor > 12) + shift_factor = 9; /* supposedly, from Nocash PSX docs */ - if (predict_nr < 5) { - if (!(i&1)) { /* low nibble first */ - byte = (uint8_t)read_8bit(stream->offset+(framesin*frame_size)+header_size+i/2,stream->streamfile); - scale = (byte & 0x0f); - } else { /* high nibble last */ - scale = byte >> 4; - } - scale = scale << 12; /* shift + sign extend (only if scale is int16_t) */ - /*if (scale > 7) { - scale = scale - 16; - }*/ -#if VAG_USE_INTEGER_TABLE - sample = (scale >> shift) + - (hist1 * VAG_coefs[predict_nr][0] + - hist2 * VAG_coefs[predict_nr][1] ) / 64; -#else - sample = (int)( (scale >> shift) + - (hist1 * VAG_f[predict_nr][0] + - hist2 * VAG_f[predict_nr][1]) ); -#endif + if (is_badflags) /* some games store garbage or extra internal logic in the flags, must be ignored */ + flag = 0; + VGM_ASSERT_ONCE(flag > 7,"PS-ADPCM: unknown flag at %"PRIx64"\n", (off64_t)frame_offset); /* meta should use PSX-badflags */ + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t new_sample = 0; + + if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */ + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x02+i/2,stream->streamfile); + + new_sample = i&1 ? /* low nibble first */ + (nibbles >> 4) & 0x0f : + (nibbles >> 0) & 0x0f; + new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ + new_sample = (int)(new_sample + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2); + new_sample = clamp16(new_sample); } - outbuf[sample_count] = clamp16(sample); + outbuf[sample_count] = new_sample; + sample_count += channelspacing; + hist2 = hist1; - hist1 = sample; + hist1 = new_sample; } stream->adpcm_history1_32 = hist1; @@ -210,6 +100,174 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int cha } +/* PS-ADPCM with configurable frame size and no flag (int math version). + * Found in some PC/PS3 games (FF XI in sizes 3/5/9/41, Afrika in size 4, Blur/James Bond in size 33, etc). + * + * Uses int math to decode, which seems more likely (based on FF XI PC's code in Moogle Toolbox). */ +void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) { + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t coef_index, shift_factor; + int32_t hist1 = stream->adpcm_history1_32; + int32_t hist2 = stream->adpcm_history2_32; + + /* external interleave (variable size), mono */ + bytes_per_frame = frame_size; + samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 28 */ + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame*frames_in; + coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; + + VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %"PRIx64"\n", (off64_t)frame_offset); + if (coef_index > 5) /* needed by Afrika (PS3) (maybe it's supposed to use more filters?) */ + coef_index = 0; /* upper filters aren't used in PS1/PS2, maybe in PSP/PS3? */ + if (shift_factor > 12) + shift_factor = 9; /* supposedly, from Nocash PSX docs */ + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t new_sample = 0; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01+i/2,stream->streamfile); + + new_sample = i&1 ? /* low nibble first */ + (nibbles >> 4) & 0x0f : + (nibbles >> 0) & 0x0f; + new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ + new_sample = new_sample + ((ps_adpcm_coefs_i[coef_index][0]*hist1 + ps_adpcm_coefs_i[coef_index][1]*hist2) >> 6); + new_sample = clamp16(new_sample); + + outbuf[sample_count] = new_sample; + sample_count += channelspacing; + + hist2 = hist1; + hist1 = new_sample; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_history2_32 = hist2; +} + + +/* Find loop samples in PS-ADPCM data and return if the file loops. + * + * PS-ADPCM/VAG has optional bit flags that control looping in the SPU. + * Possible combinations (as usually defined in Sony's docs): + * - 0x0 (0000): Normal decode + * - 0x1 (0001): End marker (last frame) + * - 0x2 (0010): Loop region (marks files that *may* have loop flags somewhere) + * - 0x3 (0011): Loop end (jump to loop address) + * - 0x4 (0100): Start marker + * - 0x5 (0101): Same as 0x07? Extremely rare [Blood Omen: Legacy of Kain (PS1)] + * - 0x6 (0110): Loop start (save loop address) + * - 0x7 (0111): End marker and don't decode + * - 0x8+(1NNN): Not valid + */ +static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end, int config) { + int num_samples = 0, loop_start = 0, loop_end = 0; + int loop_start_found = 0, loop_end_found = 0; + off_t offset = start_offset; + off_t max_offset = start_offset + data_size; + size_t interleave_consumed = 0; + int detect_full_loops = config & 1; + + + while (offset < max_offset) { + uint8_t flag = (uint8_t)read_8bit(offset+0x01,streamFile) & 0x0F; /* lower nibble only (for HEVAG) */ + + /* theoretically possible and would use last 0x06 */ + VGM_ASSERT_ONCE(loop_start_found && flag == 0x06, "PS LOOPS: multiple loop start found at %"PRIx64"\n", (off64_t)offset); + + if (flag == 0x06 && !loop_start_found) { + loop_start = num_samples; /* loop start before this frame */ + loop_start_found = 1; + } + + if (flag == 0x03 && !loop_end) { + loop_end = num_samples + 28; /* loop end after this frame */ + loop_end_found = 1; + + /* ignore strange case in Commandos (PS2), has many loop starts and ends */ + if (channels == 1 + && offset + 0x10 < max_offset + && ((uint8_t)read_8bit(offset+0x11,streamFile) & 0x0F) == 0x06) { + loop_end = 0; + loop_end_found = 0; + } + + if (loop_start_found && loop_end_found) + break; + } + + /* hack for some games that don't have loop points but do full loops, + * if there is a "partial" 0x07 end flag pretend it wants to loop + * (sometimes this will loop non-looping tracks, and won't loop all repeating files) */ + if (flag == 0x01 && detect_full_loops) { + static const uint8_t eof1[0x10] = {0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; /* common */ + static const uint8_t eof2[0x10] = {0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + //static const uint8_t eofx[0x10] = {0x07,0x00,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; /* sometimes loops */ + //static const uint8_t eofx[0x10] = {0xNN,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; /* sometimes loops */ + uint8_t buf[0x10]; + + int read = read_streamfile(buf,offset+0x10,0x10,streamFile); + + if (read > 0 + /* also test some extra stuff */ + && buf[0] != 0x00 /* skip padding */ + && buf[0] != 0x0c + && buf[0] != 0x3c /* skip Ecco the Dolphin (PS2), Ratchet & Clank 2 (PS2), lame hack */ + ) { + + /* assume full loop if there isn't an EOF tag after current frame */ + if (memcmp(buf,eof1,0x10) != 0 && memcmp(buf,eof2,0x10) != 0) { + loop_start = 28; /* skip first frame as it's null in PS-ADPCM */ + loop_end = num_samples + 28; /* loop end after this frame */ + loop_start_found = 1; + loop_end_found = 1; + //;VGM_LOG("PS LOOPS: full loop found\n"); + break; + } + } + } + + + num_samples += 28; + offset += 0x10; + + /* skip other channels */ + interleave_consumed += 0x10; + if (interleave_consumed == interleave) { + interleave_consumed = 0; + offset += interleave*(channels - 1); + } + } + + VGM_ASSERT(loop_start_found && !loop_end_found, "PS LOOPS: found loop start but not loop end\n"); + VGM_ASSERT(loop_end_found && !loop_start_found, "PS LOOPS: found loop end but not loop start\n"); + //;VGM_LOG("PS LOOPS: start=%i, end=%i\n", loop_start, loop_end); + + /* From Sony's docs: if only loop_end is set loop back to "phoneme region start", but in practice doesn't */ + if (loop_start_found && loop_end_found) { + *out_loop_start = loop_start; + *out_loop_end = loop_end; + return 1; + } + + return 0; /* no loop */ +} + +int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end) { + return ps_find_loop_offsets_internal(streamFile, start_offset, data_size, channels, interleave, out_loop_start, out_loop_end, 0); +} + +int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end) { + return ps_find_loop_offsets_internal(streamFile, start_offset, data_size, channels, interleave, out_loop_start, out_loop_end, 1); +} + size_t ps_bytes_to_samples(size_t bytes, int channels) { return bytes / channels / 0x10 * 28; } @@ -217,3 +275,4 @@ 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) { 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 b41dbd226..2a0ba6c69 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c @@ -69,6 +69,7 @@ vorbis_custom_codec_data * init_vorbis_custom(STREAMFILE *streamFile, off_t star return data; fail: + VGM_LOG("VORBIS: init fail at around 0x%"PRIx64"\n", (off64_t)start_offset); free_vorbis_custom(data); return NULL; } @@ -136,7 +137,6 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample default: goto decode_fail; } if(!ok) { - VGM_LOG("Vorbis: cannot parse packet @ around %lx\n",stream->offset); goto decode_fail; } @@ -144,20 +144,15 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample /* parse the fake ogg packet into a logical vorbis block */ rc = vorbis_synthesis(&data->vb,&data->op); if (rc == OV_ENOTAUDIO) { - VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %lx\n",(size_t)data->op.bytes,stream->offset); + VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %"PRIx64"\n",(size_t)data->op.bytes,(off64_t)stream->offset); //VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0); - continue; /* seems ok? */ - } else if (rc != 0) { - VGM_LOG("Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset); - goto decode_fail; - } + continue; /* rarely happens, seems ok? */ + } else if (rc != 0) goto decode_fail; /* finally decode the logical block into samples */ rc = vorbis_synthesis_blockin(&data->vd,&data->vb); - if (rc != 0) { - VGM_LOG("Vorbis: cannot decode Vorbis block @ %lx\n",stream->offset); - goto decode_fail; /* ? */ - } + if (rc != 0) goto decode_fail; /* ? */ + data->samples_full = 1; } @@ -167,6 +162,7 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample decode_fail: /* on error just put some 0 samples */ + VGM_LOG("VORBIS: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done)); memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(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 a8fd60f63..309283260 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c @@ -60,18 +60,12 @@ int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec /* get next packet size from the FSB 16b header (doesn't count this 16b) */ data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile); stream->offset += 2; - if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) { - VGM_LOG("FSB Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2); - goto fail; /* EOF or end padding */ - } + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ /* read raw block */ bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); - stream->offset += bytes; - if (bytes != data->op.bytes) { - VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes); - goto fail; /* wrong packet? */ - } + stream->offset += data->op.bytes; + if (bytes != data->op.bytes) goto fail; /* wrong packet? */ return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c index f75a912ff..8e746362a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c @@ -55,18 +55,12 @@ int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec /* get next packet size from the OGL 16b header (upper 14b) */ data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2; stream->offset += 2; - if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) { - VGM_LOG("OGL Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2); - goto fail; /* EOF or end padding */ - } + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ /* read raw block */ bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); stream->offset += data->op.bytes; - if (bytes != data->op.bytes) { - VGM_LOG("OGL Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes); - goto fail; /* wrong packet? */ - } + if (bytes != data->op.bytes) goto fail; /* wrong packet? */ return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c index b730f537b..408a7e200 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c @@ -61,7 +61,6 @@ int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorb return 1; fail: - VGM_LOG("SK Vorbis: failed to setup init packets @ around 0x%08lx\n", offset); return 0; } @@ -70,31 +69,27 @@ int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_ off_t packet_offset = 0; size_t packet_size = 0; int page_packets; + int res; /* read OggS/SK page and get current packet */ - if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, data->current_packet)) goto fail; + res = get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, data->current_packet); data->current_packet++; + if (!res || packet_size > data->buffer_size) goto fail; /* read raw block */ data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile); - if (data->op.bytes != packet_size) { - VGM_LOG("SK Vorbis: read error, 0x%x packet size vs 0x%lx bytes @ %lx\n", packet_size, data->op.bytes, packet_offset); - goto fail; /* wrong packet? */ - } - + if (data->op.bytes != packet_size) goto fail; /* wrong packet? */ + /* go next page when processed all packets in page */ if (data->current_packet >= page_packets) { - /* processed all packets in page, go to next */ if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, -1)) goto fail; stream->offset = packet_offset + packet_size; data->current_packet = 0; } - return 1; fail: - VGM_LOG("SK Vorbis: failed to parse packet @ around 0x%08lx\n", stream->offset); return 0; } @@ -170,7 +165,7 @@ static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_p return 1; fail: - VGM_LOG("SK Vorbis: failed to read page @ 0x%08lx\n", page_offset); + //VGM_LOG("SK Vorbis: failed to read page @ 0x%08lx\n", page_offset); return 0; } @@ -183,10 +178,8 @@ static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, o put_8bit (buf+0x00, read_8bit(packet_offset,streamFile)); /* packet_type */ memcpy (buf+0x01, "vorbis", 6); /* id */ bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,streamFile); /* copy rest (all except id+"SK") */ - if (packet_size-0x03 != bytes) { - VGM_LOG("SK Vorbis: packet (size 0x%x) not copied correctly (bytes=%x) @ 0x%lx\n", packet_size, bytes, packet_offset); + if (packet_size-0x03 != bytes) return 0; - } return 0x07+packet_size-0x03; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c index 1f5adad10..8dff6fdce 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c @@ -68,27 +68,20 @@ int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_code /* get packet info the VID1 header */ get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes); - if (data->op.bytes == 0) { - VGM_LOG("VID1 Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset); - goto fail; /* EOF or end padding */ - } + if (data->op.bytes == 0 || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ /* read raw block */ bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); stream->offset += data->op.bytes; - if (bytes != data->op.bytes) { - VGM_LOG("VID1 Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes); - goto fail; /* wrong packet? */ - } - //todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea + if (bytes != data->op.bytes) goto fail; /* wrong packet? */ + //todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea /* test block end (weird size calc but seems ok) */ if ((stream->offset - (data->block_offset + 0x34)) >= (data->block_size - 0x06)) { stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile); } - return 1; fail: 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 8ddafb51c..eff24352b 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c @@ -103,17 +103,11 @@ int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_cod /* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size so we get the header offsets here */ header_size = get_packet_header(stream->streamfile, stream->offset, data->config.header_type, (int*)&data->op.granulepos, &packet_size, data->config.big_endian); - if (!header_size || packet_size > data->buffer_size) { - VGM_LOG("Wwise Vorbis: wrong packet (0x%x) @ %lx\n", packet_size, stream->offset); - goto fail; - } + if (!header_size || packet_size > data->buffer_size) goto fail; data->op.bytes = rebuild_packet(data->buffer, data->buffer_size, stream->streamfile,stream->offset, data, data->config.big_endian); stream->offset += header_size + packet_size; - if (!data->op.bytes || data->op.bytes >= 0xFFFF) { - VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-header_size-packet_size); - goto fail; - } + if (!data->op.bytes || data->op.bytes >= 0xFFFF) goto fail; return 1; @@ -184,7 +178,7 @@ static size_t rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *stream if (!rc) goto fail; if (ow.b_off % 8 != 0) { - VGM_LOG("Wwise Vorbis: didn't write exactly audio packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); + //VGM_LOG("Wwise Vorbis: didn't write exactly audio packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); goto fail; } @@ -228,7 +222,7 @@ static size_t rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamF if (!rc) goto fail; if (ow.b_off % 8 != 0) { - VGM_LOG("Wwise Vorbis: didn't write exactly setup packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); + //VGM_LOG("Wwise Vorbis: didn't write exactly setup packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); goto fail; } @@ -1024,7 +1018,7 @@ static int ww2ogg_codebook_library_rebuild(vgm_bitstream * ow, vgm_bitstream * i /* check that we used exactly all bytes */ /* note: if all bits are used in the last byte there will be one extra 0 byte */ if ( 0 != cb_size && iw->b_off/8+1 != cb_size ) { - VGM_LOG("Wwise Vorbis: codebook size mistach (expected 0x%x, wrote 0x%lx)\n", cb_size, iw->b_off/8+1); + //VGM_LOG("Wwise Vorbis: codebook size mistach (expected 0x%x, wrote 0x%lx)\n", cb_size, iw->b_off/8+1); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c index 088c24bf5..d0c694594 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c @@ -2,14 +2,15 @@ #include "../util.h" // todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do -// XA coefs int math in different ways (see comments below), not be 100% accurate. -// May be implemented like the SNES/SPC700 BRR (see BSNES' brr.cpp, hardware-tested). +// XA coefs int math in different ways (see comments below), not 100% accurate. +// May be implemented like the SNES/SPC700 BRR. /* XA ADPCM gain values */ static const double K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 }; static const double K1[4] = { 0.0, 0.0, -0.8125,-0.859375}; -static int IK0(int fid) { return ((int)((-K0[fid]) * (1 << 10))); } /* K0/1 floats to int, K*2^10 = K*(1<<10) = K*1024 */ -static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); } +/* K0/1 floats to int, K*2^10 = K*(1<<10) = K*1024 */ +static int get_IK0(int fid) { return ((int)((-K0[fid]) * (1 << 10))); } +static int get_IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); } /* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs. * The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new. @@ -29,66 +30,114 @@ static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); } * (rounding differences should be inaudible, so public implementations may be approximations) * * Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly - * differently, using one of the above methods in software/CPU, but in XA's case may be done like - * the SNES/SPC700 BRR, with specific per-filter ops. + * differently, maybe using one of the above methods in software/CPU, but in XA's case may be done + * like the SNES/SPC700 BRR, with specific per-filter ops. * int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240 * PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this. * * Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf + * BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples + * (bsnes): https://gitlab.com/higan/higan/blob/master/higan/sfc/dsp/brr.cpp */ -void decode_xa(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - static int head_table[8] = {0,2,8,10}; - VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]); - off_t sp_offset; - int i; - int frames_in, sample_count = 0; - int32_t coef1, coef2, coef_index, shift_factor; + +void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + off_t frame_offset, sp_offset; + int i,j, frames_in, samples_done = 0, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; /* external interleave (fixed size), mono/stereo */ - frames_in = first_sample / (28*2 / channelspacing); - first_sample = first_sample % 28; + bytes_per_frame = 0x80; + samples_per_frame = 28*8 / channelspacing; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; - /* hack for mono/stereo handling */ - vgmstream->xa_get_high_nibble = !vgmstream->xa_get_high_nibble; - if (first_sample && channelspacing==1) - vgmstream->xa_get_high_nibble = !vgmstream->xa_get_high_nibble; + /* data layout (mono): + * - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames + * (handled externally, this decoder only gets frames) + * - a frame ("sound group") is divided into 8 subframes ("sound unit"), with + * subframe headers ("sound parameters") first then subframe nibbles ("sound data") + * - headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header) + * (repeats may be for error correction, though probably unused) + * - nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc + * (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc) + * + * stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R + * + * example: + * subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c + * subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c + * subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d + * ... + * subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f + */ + frame_offset = stream->offset + bytes_per_frame*frames_in; - /* parse current sound unit (subframe) sound parameters */ - sp_offset = stream->offset+head_table[frames_in]+vgmstream->xa_get_high_nibble; - coef_index = (read_8bit(sp_offset,stream->streamfile) >> 4) & 0xf; - shift_factor = (read_8bit(sp_offset,stream->streamfile) ) & 0xf; - - VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %lx\n", sp_offset); - if (coef_index > 4) - coef_index = 0; /* only 4 filters are used, rest is apparently 0 */ - if (shift_factor > 12) - shift_factor = 9; /* supposedly, from Nocash PSX docs */ - - coef1 = IK0(coef_index); - coef2 = IK1(coef_index); + if (read_32bitBE(frame_offset+0x00,stream->streamfile) != read_32bitBE(frame_offset+0x04,stream->streamfile) || + read_32bitBE(frame_offset+0x08,stream->streamfile) != read_32bitBE(frame_offset+0x0c,stream->streamfile)) { + VGM_LOG("bad frames at %"PRIx64"\n", (off64_t)frame_offset); + } - /* decode nibbles */ - for (i = first_sample; i < first_sample + samples_to_do; i++) { - int32_t new_sample; - uint8_t nibbles = (uint8_t)read_8bit(stream->offset+0x10+frames_in+(i*0x04),stream->streamfile); + /* decode subframes */ + for (i = 0; i < 8 / channelspacing; i++) { + int32_t coef1, coef2; + uint8_t coef_index, shift_factor; - new_sample = vgmstream->xa_get_high_nibble ? - (nibbles >> 4) & 0x0f : - (nibbles ) & 0x0f; - new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ - new_sample = new_sample << 4; - new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10); + /* parse current subframe (sound unit)'s header (sound parameters) */ + sp_offset = frame_offset + 0x04 + i*channelspacing + channel; + coef_index = ((uint8_t)read_8bit(sp_offset,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(sp_offset,stream->streamfile) >> 0) & 0xf; - hist2 = hist1; - hist1 = new_sample; /* must go before clamp, somehow */ - new_sample = new_sample >> 4; - new_sample = clamp16(new_sample); + VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %"PRIx64"\n", (off64_t)sp_offset); + if (coef_index > 4) + coef_index = 0; /* only 4 filters are used, rest is apparently 0 */ + if (shift_factor > 12) + shift_factor = 9; /* supposedly, from Nocash PSX docs */ - outbuf[sample_count] = new_sample; - sample_count += channelspacing; + coef1 = get_IK0(coef_index); + coef2 = get_IK1(coef_index); + + + /* decode subframe nibbles */ + for(j = 0; j < 28; j++) { + uint8_t nibbles; + int32_t new_sample; + + off_t su_offset = (channelspacing==1) ? + frame_offset + 0x10 + j*0x04 + (i/2) : /* mono */ + frame_offset + 0x10 + j*0x04 + i; /* stereo */ + int get_high_nibble = (channelspacing==1) ? + (i&1) : /* mono (even subframes = low, off subframes = high) */ + (channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */ + + /* skip half decodes to make sure hist isn't touched (kinda hack-ish) */ + if (!(sample_count >= first_sample && samples_done < samples_to_do)) { + sample_count++; + continue; + } + + nibbles = (uint8_t)read_8bit(su_offset,stream->streamfile); + + new_sample = get_high_nibble ? + (nibbles >> 4) & 0x0f : + (nibbles ) & 0x0f; + + new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ + new_sample = new_sample << 4; + new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10); + + hist2 = hist1; + hist1 = new_sample; /* must go before clamp, somehow */ + new_sample = new_sample >> 4; + new_sample = clamp16(new_sample); + + outbuf[samples_done * channelspacing] = new_sample; + samples_done++; + + sample_count++; + } } stream->adpcm_history1_32 = hist1; @@ -97,10 +146,9 @@ void decode_xa(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32 size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked) { if (is_blocked) { - //todo with -0x10 misses the last sector, not sure if bug or feature - return ((bytes - 0x10) / 0x930) * (0x900 - 18*0x10) * 2 / channels; + return (bytes / 0x930) * (28*8/ channels) * 18; } else { - return ((bytes / 0x80)*0xE0) / 2; + return (bytes / 0x80) * (28*8 / channels); } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c new file mode 100644 index 000000000..a438cea5e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c @@ -0,0 +1,63 @@ +#include "coding.h" + + +/* Decodes Konami XMD from Xbox games. + * Algorithm reverse engineered from SH4/CV:CoD's xbe (byte-accurate). */ +void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) { + off_t frame_offset; + int i, frames_in, sample_count = 0, samples_done = 0; + size_t bytes_per_frame, samples_per_frame; + int16_t hist1, hist2; + uint16_t scale; + + /* external interleave (variable size), mono */ + bytes_per_frame = frame_size; + samples_per_frame = 2 + (frame_size - 0x06) * 2; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame*frames_in; + hist2 = read_16bitLE(frame_offset+0x00,stream->streamfile); + hist1 = read_16bitLE(frame_offset+0x02,stream->streamfile); + scale = (uint16_t)read_16bitLE(frame_offset+0x04,stream->streamfile); /* scale doesn't go too high though */ + + + /* write header samples (needed) */ + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist2; + samples_done++; + } + sample_count++; + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist1; + samples_done++; + } + sample_count++; + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t new_sample; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x06 + i/2,stream->streamfile); + + new_sample = i&1 ? /* low nibble first */ + get_high_nibble_signed(nibbles): + get_low_nibble_signed(nibbles); + /* Coefs are based on XA's filter 2 (using those creates hissing in some songs though) + * ex. 1.796875 * (1 << 14) = 0x7300, -0.8125 * (1 << 14) = -0x3400 */ + new_sample = (new_sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14; + + //new_sample = clamp16(new_sample); /* not needed */ + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = (int16_t)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/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 74490213f..da48aff86 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -4,16 +4,17 @@ /* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here * to inform plugins that need it. Common extensions are commented out to avoid stealing them. */ -/* some extensions require external libraries and could be #ifdef, no really needed */ +/* some extensions require external libraries and could be #ifdef, not really needed */ /* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */ static const char* extension_list[] = { - //"", /* vgmstream can plays extensionless files too, but plugins must accept them manually */ + //"", /* vgmstream can play extensionless files too, but plugins must accept them manually */ "04sw", "2dx9", "2pfs", + "800", //"aac", //common, also tri-Ace's "aa3", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) @@ -32,6 +33,8 @@ static const char* extension_list[] = { "afc", "agsc", "ahx", + "ahv", + "ai", //"aif", //common "aifc", //common? "aifcl", //fake extension, for AIF??? @@ -41,8 +44,8 @@ static const char* extension_list[] = { "akb", "al2", "amts", //fake extension/header id for .stm (to be removed) - "ao", //txth/reserved [Cloudphobia (PC)] - "apc", //txth/reserved [MegaRace 3 (PC)] + "ao", + "apc", "as4", "asd", "asf", @@ -65,7 +68,7 @@ static const char* extension_list[] = { "bar", "bcstm", "bcwav", - "bd3", //txth/reserved [Elevator Action Deluxe (PS3)] + "bd3", "bdsp", "bfstm", "bfwav", @@ -77,9 +80,7 @@ static const char* extension_list[] = { "bik", "bika", "bik2", - "bik2a", "bk2", - "bk2a", "bmdx", "bms", "bnk", @@ -97,13 +98,17 @@ static const char* extension_list[] = { "ccc", "cd", "cfn", //fake extension/header id for .caf (to be removed) + "ckb", "ckd", + "cks", "cnk", "cps", + "csmp", "cvs", "cxs", "da", + "dax", "dbm", "dcs", "ddsp", @@ -158,15 +163,17 @@ static const char* extension_list[] = { "iadp", "idsp", "idvi", //fake extension for .pcm (to be removed) + "idx", "ikm", "ild", "int", "isd", "isws", - "itl", //txth/reserved [Charinko Hero (GC)] + "itl", "ivaud", "ivag", "ivb", + "ivs", //txth/reserved [Burnout 2 (PS2)] "joe", "jstm", @@ -185,6 +192,7 @@ static const char* extension_list[] = { "laac", //fake extension, for AAC (tri-Ace/FFmpeg) "lac3", //fake extension, for AC3 "leg", + "lflac", //fake extension, FFmpeg, not parsed, use with .pos pair for fun "lmp4", //fake extension, for MP4s "logg", //fake extension, for OGGs "lopus", //fake extension, for OPUS @@ -210,6 +218,7 @@ static const char* extension_list[] = { "mihb", "mnstr", "mogg", + //"mp3", //common //"mp4", //common //"mpc", //FFmpeg, not parsed (musepack) //common "mpdsp", @@ -221,7 +230,7 @@ static const char* extension_list[] = { "msd", "msf", "mss", - "msv", //txh/reserved [Fight Club (PS2)] + "msv", "msvp", "mta2", "mtaf", @@ -241,6 +250,7 @@ static const char* extension_list[] = { "npsf", //fake extension/header id for .nps (to be removed) "nus3bank", "nwa", + "nxa", //"ogg", //common "ogl", @@ -306,7 +316,7 @@ static const char* extension_list[] = { "scd", "sck", "sd9", - "sdf", //txth/reserved [Gummy Bears Mini Golf (3DS), Agent Hugo - Lemoon Twist (PS2)] + "sdf", "sdt", "seg", "sf0", @@ -330,6 +340,7 @@ static const char* extension_list[] = { "snr", "sns", "snu", + "sod", "son", "spd", "spm", @@ -350,7 +361,7 @@ static const char* extension_list[] = { "stx", "svag", "svs", - "svg", //txth/reserved [Hunter: The Reckoning - Wayward (PS2)] + "svg", "swag", "swav", "swd", @@ -360,6 +371,7 @@ static const char* extension_list[] = { "sxd2", "tec", + "tgq", "thp", "tk5", "tra", @@ -369,13 +381,16 @@ static const char* extension_list[] = { "txtp", "tydsp", + "ue4opus", "ulw", "um3", + "utk", + "uv", "v0", //"v1", //dual channel with v0 "vag", - "vai", //txth/reserved [Ratatouille (GC)] + "vai", "vas", "vawx", "vb", @@ -386,7 +401,7 @@ static const char* extension_list[] = { "vgs", "vgv", "vig", - "vis", //txth/reserved [AirForce Delta (PS2)] + "vis", "vms", "voi", "vpk", @@ -402,7 +417,9 @@ static const char* extension_list[] = { "wam", "was", //"wav", //common + "wavc", "wave", + "wavebatch", "wavm", "wb", "wem", @@ -413,10 +430,13 @@ static const char* extension_list[] = { "wpd", "wsd", "wsi", - "wv2", //txth/reserved [Slave Zero (PC)] + "wua", + "wv2", + "wv6", "wve", "wvs", + "x", "xa", "xa2", "xa30", @@ -427,11 +447,13 @@ static const char* extension_list[] = { "xmu", "xnb", "xsf", + "xsew", "xss", "xvag", "xvas", "xwav",//fake, to be removed "xwb", + "xmd", "xwc", "xwm", //FFmpeg, not parsed (XWMA) "xwma", //FFmpeg, not parsed (XWMA) @@ -482,7 +504,7 @@ static const coding_info coding_info_list[] = { {coding_PCM8_int, "8-bit PCM with 1 byte interleave (block)"}, {coding_PCM8_U, "8-bit unsigned PCM"}, {coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"}, - {coding_PCM8_SB_int, "8-bit PCM with sign bit, 1 byte interleave (block)"}, + {coding_PCM8_SB, "8-bit PCM with sign bit"}, {coding_ULAW, "8-bit u-Law"}, {coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"}, {coding_ALAW, "8-bit a-Law"}, @@ -518,6 +540,11 @@ static const coding_info coding_info_list[] = { {coding_DVI_IMA, "Intel DVI 4-bit IMA ADPCM"}, {coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (mono/interleave)"}, {coding_3DS_IMA, "3DS IMA 4-bit ADPCM"}, + {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, + {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, + {coding_WV6_IMA, "Gorilla Systems WV6 4-bit IMA ADPCM"}, + {coding_ALP_IMA, "High Voltage ALP 4-bit IMA ADPCM"}, + {coding_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)"}, @@ -527,15 +554,16 @@ static const coding_info coding_info_list[] = { {coding_RAD_IMA, "Radical 4-bit IMA ADPCM"}, {coding_RAD_IMA_mono, "Radical 4-bit IMA ADPCM (mono/interleave)"}, {coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"}, - {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, - {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, {coding_FSB_IMA, "FSB 4-bit IMA ADPCM"}, {coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"}, {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, {coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"}, + {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, + {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, + {coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"}, {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, {coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"}, @@ -543,18 +571,20 @@ static const coding_info coding_info_list[] = { {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"}, {coding_LSF, "lsf 4-bit ADPCM"}, {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_ASF, "Argonaut ASF 4-bit ADPCM"}, + {coding_XMD, "Konami XMD 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_SASSC, "Activision / EXAKT SASSC 8-bit DPCM"}, + {coding_DERF, "Xilam DERF 8-bit DPCM"}, {coding_ACM, "InterPlay ACM"}, {coding_NWA, "VisualArt's NWA DPCM"}, @@ -574,7 +604,6 @@ static const coding_info coding_info_list[] = { {coding_MPEG_layer3, "MPEG Layer III Audio (MP3)"}, #endif #ifdef VGM_USE_G7221 - {coding_G7221, "ITU G.722.1 (Polycom Siren 7)"}, {coding_G7221C, "ITU G.722.1 annex C (Polycom Siren 14)"}, #endif #ifdef VGM_USE_G719 @@ -586,13 +615,16 @@ static const coding_info coding_info_list[] = { #ifdef VGM_USE_ATRAC9 {coding_ATRAC9, "ATRAC9"}, #endif +#ifdef VGM_USE_CELT + {coding_CELT_FSB, "Custom CELT"}, +#endif #ifdef VGM_USE_FFMPEG {coding_FFmpeg, "FFmpeg"}, #endif }; static const layout_info layout_info_list[] = { - {layout_none, "flat (no layout)"}, + {layout_none, "flat"}, {layout_interleave, "interleave"}, {layout_segmented, "segmented"}, @@ -636,6 +668,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_ea_wve_ad10, "blocked (EA WVE Ad10)"}, {layout_blocked_sthd, "blocked (STHD)"}, {layout_blocked_h4m, "blocked (H4M)"}, + {layout_blocked_xa_aiff, "blocked (XA AIFF)"}, }; static const meta_info meta_info_list[] = { @@ -647,15 +680,16 @@ static const meta_info meta_info_list[] = { {meta_AIX, "CRI AIX header"}, {meta_AAX, "CRI AAX header"}, {meta_UTF_DSP, "CRI ADPCM_WII header"}, - {meta_DSP_AGSC, "Retro Studios AGSC header"}, - {meta_DSP_CSMP, "Retro Studios CSMP header"}, + {meta_AGSC, "Retro Studios AGSC header"}, + {meta_CSMP, "Retro Studios CSMP header"}, + {meta_RFRM, "Retro Studios RFRM header"}, {meta_NGC_ADPDTK, "Nintendo ADP raw header"}, {meta_RSF, "Retro Studios RSF raw header"}, {meta_AFC, "Nintendo AFC header"}, {meta_AST, "Nintendo AST header"}, {meta_HALPST, "HAL Laboratory HALPST header"}, {meta_DSP_RS03, "Retro Studios RS03 header"}, - {meta_DSP_STD, "Standard Nintendo DSP header"}, + {meta_DSP_STD, "Nintendo DSP header"}, {meta_DSP_CSTR, "Namco Cstr header"}, {meta_GCSW, "GCSW header"}, {meta_PS2_SShd, "Sony ADS header"}, @@ -667,28 +701,26 @@ static const meta_info meta_info_list[] = { {meta_FWAV, "Nintendo FWAV header"}, {meta_PSX_XA, "RIFF/CDXA header"}, {meta_PS2_RXWS, "Sony RXWS header"}, - {meta_PS2_RAW, "assumed RAW Interleaved PCM by .int extension"}, + {meta_PS2_RAW, ".int PCM raw header"}, {meta_PS2_OMU, "Alter Echo OMU Header"}, - {meta_DSP_STM, "Nintendo STM header"}, - {meta_PS2_EXST, "EXST header"}, + {meta_DSP_STM, "Intelligent Systems STM header"}, + {meta_PS2_EXST, "Sony EXST header"}, {meta_PS2_SVAG, "Konami SVAG header"}, - {meta_PS2_MIB, "Headerless/MIB PS-ADPCM raw header"}, + {meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"}, {meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, {meta_PS2_MIC, "assume KOEI MIC file by .mic extension"}, {meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"}, {meta_DSP_MSS, "Double DSP header stereo by .mss extension"}, {meta_DSP_GCM, "Double DSP header stereo by .gcm extension"}, - {meta_DSP_WII_IDSP, "Wii IDSP Double DSP header"}, - {meta_RSTM_SPM, "Nintendo RSTM header and .brstmspm extension"}, + {meta_IDSP_TT, "Traveller's Tales IDSP header"}, + {meta_RSTM_SPM, "Nintendo RSTM header (brstmspm)"}, {meta_RAW, "assumed RAW PCM file by .raw extension"}, - {meta_PS2_VAGi, "Sony VAG Interleaved header (VAGi)"}, - {meta_PS2_VAGp, "Sony VAG Mono header (VAGp)"}, - {meta_PS2_VAGs, "Sony VAG Stereo header (VAGp)"}, - {meta_PS2_VAGm, "Sony VAG Mono header (VAGm)"}, - {meta_PS2_pGAV, "Sony VAG Stereo Little Endian header (pGAV)"}, + {meta_PS2_VAGi, "Sony VAGi header"}, + {meta_PS2_VAGp, "Sony VAGp header"}, + {meta_PS2_pGAV, "Sony pGAV header"}, {meta_PSX_GMS, "assumed Grandia GMS file by .gms extension"}, - {meta_PS2_STR, "assumed STR + STH File by .str & .sth extension"}, + {meta_STR_WAV, "Blitz Games STR+WAV header"}, {meta_PS2_ILD, "ILD header"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, {meta_XBOX_WAVM, "Xbox WAVM raw header"}, @@ -696,7 +728,7 @@ static const meta_info meta_info_list[] = { {meta_EA_SCHL, "Electronic Arts SCHl header (variable)"}, {meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"}, {meta_CAF, "tri-Crescendo CAF Header"}, - {meta_PS2_VPK, "VPK Header"}, + {meta_VPK, "SCE America VPK Header"}, {meta_GENH, "GENH generic header"}, {meta_DSP_SADB, "Procyon Studio SADB header"}, {meta_SADL, "Procyon Studio SADL header"}, @@ -798,8 +830,10 @@ static const meta_info meta_info_list[] = { {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_PS2_ASS, "SystemSoft .ASS header"}, + {meta_NUB_IDSP, "Namco NUB IDSP header"}, + {meta_IDSP_NL, "Next Level IDSP header"}, + {meta_IDSP_IE, "Inevitable Entertainment IDSP Header"}, {meta_UBI_JADE, "Ubisoft Jade RIFF header"}, {meta_PS2_SEG, "SEG (PS2) Header"}, {meta_XBOX_SEG, "SEG (XBOX) Header"}, @@ -835,7 +869,6 @@ static const meta_info meta_info_list[] = { {meta_PS2_SND, "Might and Magic SSND Header"}, {meta_PS2_VSF_TTA, "VSF with SMSS Header"}, {meta_ADS, "dhSS Header"}, - {meta_WII_STR, "HOTD Overkill - STR+STH WII Header"}, {meta_PS2_MCG, "Gunvari MCG Header"}, {meta_ZSD, "ZSD Header"}, {meta_RedSpark, "RedSpark Header"}, @@ -879,7 +912,7 @@ static const meta_info meta_info_list[] = { {meta_SSS, "Namco .SSS raw header"}, {meta_PS2_GCM, "GCM 'MCG' Header"}, {meta_PS2_SMPL, "Homura SMPL header"}, - {meta_PS2_MSA, "Psyvariar -Complete Edition- MSA header"}, + {meta_PS2_MSA, "Success .MSA header"}, {meta_PC_SMP, "Ghostbusters .smp Header"}, {meta_NGC_PDT, "Hudson .PDT header"}, {meta_NGC_RKV, "Legacy of Kain - Blood Omen 2 RKV GC header"}, @@ -892,7 +925,6 @@ static const meta_info meta_info_list[] = { {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"}, @@ -928,10 +960,9 @@ static const meta_info meta_info_list[] = { {meta_OTNS_ADP, "Omikron: The Nomad Soul ADP header"}, {meta_EB_SFX, "Excitebots .sfx header"}, {meta_EB_SF0, "assumed Excitebots .sf0 by extension"}, - {meta_PS3_KLBS, "klBS Header"}, {meta_PS2_MTAF, "Konami MTAF header"}, - {meta_PS2_VAG1, "Konami VAG Mono header (VAG1)"}, - {meta_PS2_VAG2, "Konami VAG Stereo header (VAG2)"}, + {meta_PS2_VAG1, "Konami VAG1 header"}, + {meta_PS2_VAG2, "Konami VAG2 header"}, {meta_TUN, "Lego Racers ALP header"}, {meta_WPD, "WPD 'DPW' header"}, {meta_MN_STR, "Mini Ninjas 'STR' header"}, @@ -943,17 +974,18 @@ static const meta_info meta_info_list[] = { {meta_UBI_CKD, "Ubisoft CKD RIFF header"}, {meta_PS2_VBK, "PS2 VBK Header"}, {meta_OTM, "Otomedius OTM Header"}, - {meta_CSTM, "Nintendo 3DS CSTM Header"}, - {meta_FSTM, "Nintendo Wii U FSTM Header"}, + {meta_CSTM, "Nintendo CSTM Header"}, + {meta_FSTM, "Nintendo FSTM Header"}, {meta_KT_WIIBGM, "Koei Tecmo WiiBGM Header"}, {meta_KTSS, "Koei Tecmo Nintendo Stream KTSS Header"}, - {meta_3DS_IDSP, "Nintendo IDSP Header"}, + {meta_IDSP_NUS3, "Namco NUS3 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_HCA, "CRI HCA header"}, {meta_PS2_SVAG_SNK, "SNK SVAG header"}, {meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"}, + {meta_FFMPEG, "FFmpeg supported file format"}, {meta_X360_CXS, "tri-Crescendo CXS header"}, {meta_AKB, "Square-Enix AKB header"}, {meta_NUB_XMA, "Namco NUB XMA header"}, @@ -1035,10 +1067,35 @@ static const meta_info meta_info_list[] = { {meta_H4M, "Hudson HVQM4 header"}, {meta_OGG_MUS, "Ogg Vorbis (MUS header)"}, {meta_ASF, "Argonaut ASF header"}, + {meta_XMD, "Konami XMD header"}, + {meta_CKS, "Cricket Audio CKS header"}, + {meta_CKB, "Cricket Audio CKB header"}, + {meta_WV6, "Gorilla Systems WV6 header"}, + {meta_WAVEBATCH, "Firebrand Games WBAT header"}, + {meta_HD3_BD3, "Sony HD3+BD3 header"}, + {meta_BNK_SONY, "Sony BNK header"}, + {meta_SCD_SSCF, "Square-Enix SCD (SSCF) header"}, + {meta_DSP_VAG, ".VAG DSP header"}, + {meta_DSP_ITL, ".ITL DSP header"}, + {meta_A2M, "Artificial Mind & Movement A2M header"}, + {meta_AHV, "Amuze AHV header"}, + {meta_MSV, "Sony MultiStream MSV header"}, + {meta_SDF_PS2, "Beyond Reality PS2 SDF header"}, + {meta_SVG, "High Voltage SVG header"}, + {meta_VIS, "Konami VIS header"}, + {meta_SDF_3DS, "Beyond Reality 3DS SDF header"}, + {meta_VAI, "Asobo Studio .VAI header"}, + {meta_AIF_ASOBO, "Asobo Studio .AIF header"}, + {meta_AO, "AlphaOgg .AO header"}, + {meta_APC, "Cryo APC header"}, + {meta_WV2, "Infogrames North America WAV2 header"}, + {meta_XAU_KONAMI, "Konami XAU header"}, + {meta_DERF, "Xilam DERF header"}, + {meta_UTK, "Maxis UTK header"}, + {meta_NXA, "Entergram NXA header"}, + {meta_ADPCM_CAPCOM, "Capcom .ADPCM header"}, + {meta_UE4OPUS, "Epic Games UE4OPUS header"}, -#ifdef VGM_USE_FFMPEG - {meta_FFmpeg, "FFmpeg supported file format"}, -#endif }; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index ded88c8f6..fe88a5cfb 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -1,16 +1,18 @@ #include "layout.h" #include "../vgmstream.h" -static void block_update(VGMSTREAM * vgmstream); +/* Decodes samples for blocked streams. + * Data is divided into headered blocks with a bunch of data. The layout calls external helper functions + * when a block is decoded, and those must parse the new block and move offsets accordingly. */ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; + int samples_written = 0; + int frame_size, samples_per_frame, samples_this_block; - int frame_size = get_vgmstream_frame_size(vgmstream); - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - int samples_this_block; + frame_size = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + samples_this_block = 0; - /* get samples in the current block */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -19,12 +21,13 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; } - /* decode all samples */ + while (samples_written < sample_count) { int samples_to_do; + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - /* on loop those values are changed */ + /* handle looping, readjust back to loop start values */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -35,28 +38,27 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * continue; } - /* probably block bug or EOF, next calcs would give wrong values and buffer segfaults */ if (samples_this_block < 0) { - VGM_LOG("layout_blocked: wrong block at 0x%lx\n", vgmstream->current_block_offset); + /* probably block bug or EOF, next calcs would give wrong values/segfaults/infinite loop */ + VGM_LOG("layout_blocked: wrong block samples at 0x%"PRIx64"\n", (off64_t)vgmstream->current_block_offset); memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); - break; /* probable infinite loop otherwise */ + break; + } + + if (vgmstream->current_block_offset < 0 || vgmstream->current_block_offset == 0xFFFFFFFF) { + /* probably block bug or EOF, block functions won't be able to read anything useful/infinite loop */ + VGM_LOG("layout_blocked: wrong block offset found\n"); + memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); + break; } samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - if (samples_written + samples_to_do > sample_count) + if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; - if (vgmstream->current_block_offset >= 0) { - /* samples_this_block = 0 is allowed (empty block): do nothing then move to next block */ - if (samples_to_do > 0) - decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); - } - else { - /* 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; - } + if (samples_to_do > 0) { + /* samples_this_block = 0 is allowed (empty block, do nothing then move to next block) */ + decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); } samples_written += samples_to_do; @@ -65,15 +67,13 @@ 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 */ - block_update(vgmstream); + if (vgmstream->samples_into_block == samples_this_block + /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ //todo + block_update(vgmstream->next_block_offset,vgmstream); - /* for VBR these may change */ + /* update since these may change each block */ frame_size = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - - /* get samples in the current block */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -88,124 +88,124 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * } } - -static void block_update(VGMSTREAM * vgmstream) { +/* helper functions to parse new block */ +void block_update(off_t block_offset, VGMSTREAM * vgmstream) { switch (vgmstream->layout_type) { case layout_blocked_ast: - block_update_ast(vgmstream->next_block_offset,vgmstream); + block_update_ast(block_offset,vgmstream); break; case layout_blocked_mxch: - block_update_mxch(vgmstream->next_block_offset,vgmstream); + block_update_mxch(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; + block_update_halpst(block_offset,vgmstream); break; case layout_blocked_xa: - block_update_xa(vgmstream->next_block_offset,vgmstream); + block_update_xa(block_offset,vgmstream); break; case layout_blocked_ea_schl: - block_update_ea_schl(vgmstream->next_block_offset,vgmstream); + block_update_ea_schl(block_offset,vgmstream); break; case layout_blocked_ea_1snh: - block_update_ea_1snh(vgmstream->next_block_offset,vgmstream); + block_update_ea_1snh(block_offset,vgmstream); break; case layout_blocked_caf: - block_update_caf(vgmstream->next_block_offset,vgmstream); + block_update_caf(block_offset,vgmstream); break; case layout_blocked_wsi: - block_update_wsi(vgmstream->next_block_offset,vgmstream); + block_update_wsi(block_offset,vgmstream); break; case layout_blocked_str_snds: - block_update_str_snds(vgmstream->next_block_offset,vgmstream); + block_update_str_snds(block_offset,vgmstream); break; case layout_blocked_ws_aud: - block_update_ws_aud(vgmstream->next_block_offset,vgmstream); + block_update_ws_aud(block_offset,vgmstream); break; case layout_blocked_matx: - block_update_matx(vgmstream->next_block_offset,vgmstream); + block_update_matx(block_offset,vgmstream); break; case layout_blocked_dec: - block_update_dec(vgmstream->next_block_offset,vgmstream); + block_update_dec(block_offset,vgmstream); break; case layout_blocked_emff_ps2: - block_update_emff_ps2(vgmstream->next_block_offset,vgmstream); + block_update_emff_ps2(block_offset,vgmstream); break; case layout_blocked_emff_ngc: - block_update_emff_ngc(vgmstream->next_block_offset,vgmstream); + block_update_emff_ngc(block_offset,vgmstream); break; case layout_blocked_gsb: - block_update_gsb(vgmstream->next_block_offset,vgmstream); + block_update_gsb(block_offset,vgmstream); break; case layout_blocked_vs: - block_update_vs(vgmstream->next_block_offset,vgmstream); + block_update_vs(block_offset,vgmstream); break; case layout_blocked_xvas: - block_update_xvas(vgmstream->next_block_offset,vgmstream); + block_update_xvas(block_offset,vgmstream); break; case layout_blocked_thp: - block_update_thp(vgmstream->next_block_offset,vgmstream); + block_update_thp(block_offset,vgmstream); break; case layout_blocked_filp: - block_update_filp(vgmstream->next_block_offset,vgmstream); + block_update_filp(block_offset,vgmstream); break; case layout_blocked_ivaud: - block_update_ivaud(vgmstream->next_block_offset,vgmstream); + block_update_ivaud(block_offset,vgmstream); break; case layout_blocked_ea_swvr: - block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); + block_update_ea_swvr(block_offset,vgmstream); break; case layout_blocked_adm: - block_update_adm(vgmstream->next_block_offset,vgmstream); + block_update_adm(block_offset,vgmstream); break; case layout_blocked_bdsp: - block_update_bdsp(vgmstream->next_block_offset,vgmstream); + block_update_bdsp(block_offset,vgmstream); break; case layout_blocked_tra: - block_update_tra(vgmstream->next_block_offset,vgmstream); + block_update_tra(block_offset,vgmstream); break; case layout_blocked_ps2_iab: - block_update_ps2_iab(vgmstream->next_block_offset,vgmstream); + block_update_ps2_iab(block_offset,vgmstream); break; case layout_blocked_ps2_strlr: - block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream); + block_update_ps2_strlr(block_offset,vgmstream); break; case layout_blocked_rws: - block_update_rws(vgmstream->next_block_offset,vgmstream); + block_update_rws(block_offset,vgmstream); break; case layout_blocked_hwas: - block_update_hwas(vgmstream->next_block_offset,vgmstream); + block_update_hwas(block_offset,vgmstream); break; case layout_blocked_ea_sns: - block_update_ea_sns(vgmstream->next_block_offset,vgmstream); + block_update_ea_sns(block_offset,vgmstream); break; case layout_blocked_awc: - block_update_awc(vgmstream->next_block_offset,vgmstream); + block_update_awc(block_offset,vgmstream); break; case layout_blocked_vgs: - block_update_vgs(vgmstream->next_block_offset,vgmstream); + block_update_vgs(block_offset,vgmstream); break; case layout_blocked_vawx: - block_update_vawx(vgmstream->next_block_offset,vgmstream); + block_update_vawx(block_offset,vgmstream); break; case layout_blocked_xvag_subsong: - block_update_xvag_subsong(vgmstream->next_block_offset,vgmstream); + block_update_xvag_subsong(block_offset,vgmstream); break; case layout_blocked_ea_wve_au00: - block_update_ea_wve_au00(vgmstream->next_block_offset,vgmstream); + block_update_ea_wve_au00(block_offset,vgmstream); break; case layout_blocked_ea_wve_ad10: - block_update_ea_wve_ad10(vgmstream->next_block_offset,vgmstream); + block_update_ea_wve_ad10(block_offset,vgmstream); break; case layout_blocked_sthd: - block_update_sthd(vgmstream->next_block_offset,vgmstream); + block_update_sthd(block_offset,vgmstream); break; case layout_blocked_h4m: - block_update_h4m(vgmstream->next_block_offset,vgmstream); + block_update_h4m(block_offset,vgmstream); break; - default: + case layout_blocked_xa_aiff: + block_update_xa_aiff(block_offset,vgmstream); + break; + default: /* not a blocked layout */ break; } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c index 8ff32ba08..3aab1668a 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c @@ -1,18 +1,21 @@ #include "layout.h" #include "../vgmstream.h" -/* set up for the block at the given offset */ +/* simple headered blocks */ void block_update_ast(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = read_32bitBE( - vgmstream->current_block_offset+4, - vgmstream->ch[0].streamfile); - vgmstream->next_block_offset = vgmstream->current_block_offset + - vgmstream->current_block_size*vgmstream->channels + 0x20; + size_t block_data, header_size; - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset + - 0x20 + vgmstream->current_block_size*i; + /* 0x00: "BLCK", rest: null */ + block_data = read_32bitBE(block_offset+0x04,streamFile); + header_size = 0x20; + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = block_data; + vgmstream->next_block_offset = block_offset + block_data*vgmstream->channels + header_size; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + header_size + block_data*i; } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c index b9d921b8b..d784c2fc0 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c @@ -11,12 +11,23 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { size_t file_size = get_streamfile_size(streamFile); + /* EOF reads: signal we have nothing and let the layout fail */ + if (block_offset >= file_size) { + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset; + vgmstream->current_block_samples = -1; + return; + } + + while (block_offset < file_size) { uint32_t id = read_32bitBE(block_offset+0x00,streamFile); - block_size = read_32bitLE(block_offset+0x04,streamFile); - if (block_size > 0x00F00000) /* BE in SAT, but one file may have both BE and LE chunks */ + /* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */ + if (guess_endianness32bit(block_offset+0x04,streamFile)) block_size = read_32bitBE(block_offset+0x04,streamFile); + else + block_size = read_32bitLE(block_offset+0x04,streamFile); block_header = 0; @@ -31,7 +42,8 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ block_header = 0x08; } - else if (id == 0x00000000) { + else if (id == 0x00000000 || id == 0xFFFFFFFF || id == 0x31534E65) { /* EOF or "1SNe" */ + vgmstream->current_block_samples = -1; break; } @@ -45,6 +57,8 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { vgmstream->current_block_offset = block_offset; vgmstream->next_block_offset = block_offset + block_size; vgmstream->current_block_size = block_size - block_header; + if (vgmstream->current_block_samples == -1) + return; /* set new channel offsets and block sizes */ @@ -72,7 +86,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { break; case coding_DVI_IMA: - if (vgmstream->codec_version == 1) { /* ADPCM hist */ + if (vgmstream->codec_config == 1) { /* ADPCM hist */ vgmstream->current_block_samples = read_32bit(block_offset + block_header, streamFile); vgmstream->current_block_size = 0; // - (0x04 + 0x08*vgmstream->channels); /* should be equivalent */ @@ -83,8 +97,8 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { vgmstream->ch[i].offset = adpcm_offset + 0x08*vgmstream->channels; } - VGM_ASSERT(vgmstream->current_block_samples != (block_size - block_header - 0x04 - 0x08*vgmstream->channels) * 2 / vgmstream->channels, - "EA 1SHN blocked: different expected vs block num samples at %lx\n", block_offset); + //VGM_ASSERT(vgmstream->current_block_samples != (block_size - block_header - 0x04 - 0x08*vgmstream->channels) * 2 / vgmstream->channels, + // "EA 1SHN blocked: different expected vs block num samples at %lx\n", block_offset); } else { for(i = 0; i < vgmstream->channels; i++) { diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index aa21e2065..bfa074124 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -23,7 +23,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { { uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile); - if (guess_endianness32bit(block_offset + 0x04,streamFile)) /* size is always LE, except in early SS/MAC */ + if (vgmstream->codec_config & 0x02) /* size is always LE, except in early SS/MAC */ block_size = read_32bitBE(block_offset + 0x04,streamFile); else block_size = read_32bitLE(block_offset + 0x04,streamFile); @@ -180,7 +180,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { } /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ - if (vgmstream->codec_version == 1) { + if (vgmstream->codec_config & 0x01) { for (i = 0; i < vgmstream->channels; i++) { //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_h4m.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_h4m.c index 14f64cc8d..5c5c4082b 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_h4m.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_h4m.c @@ -34,7 +34,7 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) { uint32_t frame_samples = read_32bitBE(block_offset+0x08, streamFile); size_t block_skip; - if (vgmstream->codec_version & 0x80) { + if (vgmstream->codec_config & 0x80) { frame_samples /= 2; /* ??? */ } @@ -42,38 +42,19 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) { block_size = 0x08 + frame_size; block_samples = frame_samples; - /* skip data from other audio tracks */ - if (vgmstream->num_streams) { + if (vgmstream->num_streams > 1 && vgmstream->stream_index > 1) { uint32_t audio_bytes = frame_size - 0x04; - block_skip += (audio_bytes / vgmstream->num_streams) * vgmstream->stream_index; + block_skip += (audio_bytes / vgmstream->num_streams) * (vgmstream->stream_index-1); } - //VGM_ASSERT(frame_format < 1 && frame_format > 3, "H4M: unknown frame_format %x at %lx\n", frame_format, block_offset); - VGM_ASSERT(frame_format == 1, "H4M: unknown frame_format %x at %lx\n", frame_format, block_offset); + VGM_ASSERT(frame_format == 1, "H4M: unknown frame_format %x at %"PRIx64"\n", frame_format, (off64_t)block_offset); + + /* pass current mode to the decoder */ + vgmstream->codec_config = (frame_format << 8) | (vgmstream->codec_config & 0xFF); - //todo handle in the decoder? - //todo right channel first? - /* get ADPCM hist (usually every new block) */ for (i = 0; i < vgmstream->channels; i++) { - if (frame_format == 1) { /* combined hist+index */ - vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(block_offset + block_skip + 0x02*i + 0x00,streamFile) & 0xFFFFFF80; - vgmstream->ch[i].adpcm_step_index = read_8bit(block_offset + block_skip + 0x02*i + 0x01,streamFile) & 0x7f; - vgmstream->ch[i].offset = block_offset + block_skip + 0x02*vgmstream->channels; - } - else if (frame_format == 3) { /* separate hist+index */ - vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(block_offset + block_skip + 0x03*i + 0x00,streamFile); - vgmstream->ch[i].adpcm_step_index = read_8bit(block_offset + block_skip + 0x03*i + 0x02,streamFile); - vgmstream->ch[i].offset = block_offset + block_skip + 0x03*vgmstream->channels; - } - else if (frame_format == 2) { /* no hist/index */ - vgmstream->ch[i].offset = block_offset + block_skip; - } - } - - //todo temp hack, at it must write header sample and ignore the last nibble to get fully correct output - if (frame_format == 1 || frame_format == 3) { - block_samples--; + vgmstream->ch[i].offset = block_offset + block_skip; } } else { diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_wsi.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_wsi.c index 572c6cc12..6be576fb4 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_wsi.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_wsi.c @@ -6,6 +6,7 @@ void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; off_t channel_block_size; + //int is_first_offset = /* assume that all channels have the same size for this block */ @@ -18,4 +19,14 @@ void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream) { for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = block_offset + channel_block_size*i + 0x10; } + + /* first block has DSP header, remove */ + if (block_offset == vgmstream->ch[0].channel_start_offset) { + int i; + + vgmstream->current_block_size -= 0x60; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset += 0x60; + } + } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c index e7829b68a..329e4475b 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c @@ -2,72 +2,66 @@ #include "../coding/coding.h" #include "../vgmstream.h" -/* set up for the block at the given offset */ +/* parse a CD-XA raw mode2/form2 sector */ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; - int8_t currentChannel=0; - int8_t subAudio=0; + size_t block_samples; + uint8_t xa_submode; - vgmstream->xa_get_high_nibble = 1; /* reset nibble order */ - - /* don't change this variable in the init process */ - if (vgmstream->samples_into_block != 0) - vgmstream->xa_sector_length += 0x80; /* XA mode2/form2 sector, size 0x930 * 0x00: sync word * 0x0c: header = minute, second, sector, mode (always 0x02) - * 0x10: subheader = file, channel (marker), submode flags, xa header - * 0x14: subheader again + * 0x10: subheader = file, channel (substream marker), submode flags, xa header + * 0x14: subheader again (for error correction) * 0x18: data * 0x918: unused * 0x92c: EDC/checksum or null * 0x930: end - * (in non-blocked ISO 2048 mode1/data chunks are 0x800) */ - /* submode flag bits (typical audio value = 0x64) - * - 7: end of file - * - 6: real time mode - * - 5: sector form (0=form1, 1=form2) - * - 4: trigger (for application) - * - 3: data sector - * - 2: audio sector - * - 1: video sector - * - 0: end of audio + /* channel markers supposedly could be used to interleave streams, ex. audio languages within video + * (extractors may split .XA using channels?) */ + VGM_ASSERT(block_offset + 0x930 < get_streamfile_size(streamFile) && + (uint8_t)read_8bit(block_offset + 0x000 + 0x11,streamFile) != + (uint8_t)read_8bit(block_offset + 0x930 + 0x11,streamFile), + "XA block: subchannel change at %"PRIx64"\n", (off64_t)block_offset); + + /* submode flag bits (typical audio value = 0x64 01100100) + * - 7 (0x80 10000000): end of file + * - 6 (0x40 01000000): real time mode + * - 5 (0x20 00100000): sector form (0=form1, 1=form2) + * - 4 (0x10 00010000): trigger (for application) + * - 3 (0x08 00001000): data sector + * - 2 (0x04 00000100): audio sector + * - 1 (0x02 00000010): video sector + * - 0 (0x01 00000001): end of audio */ + xa_submode = (uint8_t)read_8bit(block_offset + 0x12,streamFile); - // We get to the end of a sector ? - if (vgmstream->xa_sector_length == (18*0x80)) { - vgmstream->xa_sector_length = 0; - - // 0x30 of unused bytes/sector :( - if (!vgmstream->xa_headerless) { - block_offset += 0x30; -begin: - // Search for selected channel & valid audio - currentChannel = read_8bit(block_offset-0x07,vgmstream->ch[0].streamfile); - subAudio = read_8bit(block_offset-0x06,vgmstream->ch[0].streamfile); - - // audio is coded as 0x64 - if (!((subAudio==0x64) && (currentChannel==vgmstream->xa_channel))) { - // go to next sector - block_offset += 0x930; - if (currentChannel!=-1) goto begin; - } + /* audio sector must set/not set certain flags, as per spec */ + if (!(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02)) { + if (xa_submode & 0x20) { + /* form2 audio: size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */ + block_samples = (28*8 / vgmstream->channels) * 18; } + else { /* rare, found with empty audio [Glint Glitters (PS1), Dance! Dance! Dance! (PS1)] */ + /* form1 audio: size 0x800, 16 frames of size 0x80 with 8 subframes of 28 samples (rest is garbage/other data) */ + block_samples = (28*8 / vgmstream->channels) * 16; + } + } + else { + ;VGM_ASSERT_ONCE(block_offset < get_streamfile_size(streamFile), + "XA block: non audio block found at %"PRIx64"\n", (off64_t)block_offset); + block_samples = 0; /* not an audio sector */ } vgmstream->current_block_offset = block_offset; + vgmstream->current_block_samples = block_samples; + vgmstream->next_block_offset = block_offset + 0x930; - // Quid : how to stop the current channel ??? - // i set up 0 to current_block_size to make vgmstream not playing bad samples - // another way to do it ??? - // (as the number of samples can be false in cd-xa due to multi-channels) - vgmstream->current_block_size = (currentChannel==-1 ? 0 : 0x70); - - vgmstream->next_block_offset = vgmstream->current_block_offset + 0x80; - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x18; } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa_aiff.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa_aiff.c new file mode 100644 index 000000000..ab4811f68 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa_aiff.c @@ -0,0 +1,19 @@ +#include "layout.h" +#include "../coding/coding.h" +#include "../vgmstream.h" + +/* parse a XA AIFF block (CD sector without the 0x18 subheader) */ +void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream) { + int i; + size_t block_samples; + + block_samples = (28*8 / vgmstream->channels) * 18; /* size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */ + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_samples = block_samples; + vgmstream->next_block_offset = block_offset + 0x914; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/flat.c b/Frameworks/vgmstream/vgmstream/src/layout/flat.c new file mode 100644 index 000000000..59f3ce034 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/flat.c @@ -0,0 +1,39 @@ +#include "layout.h" +#include "../vgmstream.h" + + +/* Decodes samples for flat streams. + * Data forms a single stream, and the decoder may internally skip chunks and move offsets as needed. */ +void render_vgmstream_flat(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { + int samples_written = 0; + int samples_per_frame, samples_this_block; + + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + samples_this_block = vgmstream->num_samples; /* do all samples if possible */ + + + while (samples_written < sample_count) { + int samples_to_do; + + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { + /* handle looping */ + continue; + } + + samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; + + if (samples_to_do == 0) { + VGM_LOG("layout_flat: wrong samples_to_do found\n"); + memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); + break; + } + + decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); + + 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/interleave.c b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c index 6090d197d..9e64fdfd2 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c @@ -1,64 +1,92 @@ #include "layout.h" #include "../vgmstream.h" + +/* Decodes samples for interleaved streams. + * Data has interleaved chunks per channel, and once one is decoded the layout moves offsets, + * skipping other chunks (essentially a simplified variety of blocked layout). + * Incompatible with decoders that move offsets. */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; - - int frame_size = get_vgmstream_frame_size(vgmstream); - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - int samples_this_block; + int samples_written = 0; + int frame_size, samples_per_frame, samples_this_block; + int has_interleave_last = 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; - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && - vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block> vgmstream->num_samples) { + if (has_interleave_last && + vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block > vgmstream->num_samples) { + /* adjust values again if inside last interleave */ frame_size = get_vgmstream_shortframe_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; } - while (samples_writtenchannels == 1) + samples_this_block = vgmstream->num_samples; + + + while (samples_written < sample_count) { int samples_to_do; if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - /* we assume that the loop is not back into a short block */ - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1) { + /* handle looping, restore standard interleave sizes */ + if (has_interleave_last) { /* assumes that won't loop back into a interleave_last */ 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; + if (samples_this_block == 0 && vgmstream->channels == 1) + samples_this_block = vgmstream->num_samples; } continue; } samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - /*printf("vgmstream_samples_to_do(samples_this_block=%d,samples_per_frame=%d,vgmstream) returns %d\n",samples_this_block,samples_per_frame,samples_to_do);*/ + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; + if (samples_to_do == 0) { /* happens when interleave is not set */ + VGM_LOG("layout_interleave: wrong samples_to_do found\n"); + memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); + break; + } decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; + vgmstream->samples_into_block += samples_to_do; - if (vgmstream->samples_into_block==samples_this_block) { - int chan; - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && - vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { + + /* move to next interleaved block when all samples are consumed */ + if (vgmstream->samples_into_block == samples_this_block) { + int ch; + + if (has_interleave_last && + vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { + /* adjust values again if inside last interleave */ frame_size = get_vgmstream_shortframe_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - 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_last_block_size*chan; - } else { + if (samples_this_block == 0 && vgmstream->channels == 1) + samples_this_block = vgmstream->num_samples; - for (chan=0;chanchannels;chan++) - vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*vgmstream->channels; + for (ch = 0; ch < vgmstream->channels; ch++) { + off_t skip = vgmstream->interleave_block_size*(vgmstream->channels-ch) + + vgmstream->interleave_last_block_size*ch;; + vgmstream->ch[ch].offset += skip; + } } - vgmstream->samples_into_block=0; + else { + for (ch = 0; ch < vgmstream->channels; ch++) { + off_t skip = vgmstream->interleave_block_size*vgmstream->channels; + vgmstream->ch[ch].offset += skip; + } + } + + vgmstream->samples_into_block = 0; } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layered.c b/Frameworks/vgmstream/vgmstream/src/layout/layered.c index bfaaec4b8..2dcae87bb 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layered.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/layered.c @@ -1,45 +1,51 @@ #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. */ +/* NOTE: if loop settings change the layered vgmstreams must be notified (preferably using vgmstream_force_loop) */ #define LAYER_BUF_SIZE 512 #define LAYER_MAX_CHANNELS 6 /* at least 2, but let's be generous */ - +/* Decodes samples for layered streams. + * Similar to interleave layout, but decodec samples are mixed from complete vgmstreams, each + * with custom codecs and different number of channels, creating a single super-vgmstream. + * Usually combined with custom streamfiles to handle data interleaved in weird ways. */ 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; + int samples_written = 0; layered_layout_data *data = vgmstream->layout_data; + sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS]; - 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; + while (samples_written < sample_count) { + int samples_to_do = LAYER_BUF_SIZE; + int layer, ch = 0; + + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; for (layer = 0; layer < data->layer_count; layer++) { - int s,l_ch; + int s, layer_ch; int layer_channels = data->layers[layer]->channels; + /* each layer will handle its own looping internally */ + render_vgmstream(interleave_buf, samples_to_do, data->layers[layer]); - for (l_ch = 0; l_ch < layer_channels; l_ch++) { + /* mix layer samples to main samples */ + for (layer_ch = 0; layer_ch < layer_channels; layer_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); + size_t layer_sample = s*layer_channels + layer_ch; + size_t buffer_sample = (samples_written+s)*vgmstream->channels + ch; buffer[buffer_sample] = interleave_buf[layer_sample]; } + ch++; } - } - samples_done += samples_to_do; + samples_written += samples_to_do; + vgmstream->current_sample = data->layers[0]->current_sample; /* just in case it's used for info */ + //vgmstream->samples_into_block = 0; /* handled in each layer */ } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 980904766..f547d6771 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -6,6 +6,7 @@ /* blocked layouts */ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void 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); @@ -44,11 +45,12 @@ 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); void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_xa_aiff(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_flat(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/layout/nolayout.c b/Frameworks/vgmstream/vgmstream/src/layout/nolayout.c deleted file mode 100644 index f481a034e..000000000 --- a/Frameworks/vgmstream/vgmstream/src/layout/nolayout.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; - - const int samples_this_block = vgmstream->num_samples; - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - - while (samples_writtenloop_flag && vgmstream_do_loop(vgmstream)) { - continue; - } - - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; - - if (!samples_to_do) { - memset(buffer + samples_written * vgmstream->channels, 0, sizeof(sample) * vgmstream->channels * (sample_count - samples_written)); - return; - } - - decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); - - 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/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 4f2b70737..ff87c68e3 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -2,35 +2,54 @@ #include "../vgmstream.h" +/* Decodes samples for segmented streams. + * Chains together sequential vgmstreams, for data divided into separate sections or files + * (like one part for intro and other for loop segments, which may even use different codecs). */ void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; + 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; + /* handle looping, finding loop segment */ + int loop_segment = 0, samples = 0, loop_samples_skip = 0; + while (samples < vgmstream->num_samples) { + int32_t segment_samples = data->segments[loop_segment]->num_samples; + if (vgmstream->loop_start_sample >= samples && vgmstream->loop_start_sample < samples + segment_samples) { + loop_samples_skip = vgmstream->loop_start_sample - samples; + break; /* loop_start falls within loop_segment's samples */ + } + samples += segment_samples; + loop_segment++; + } + if (loop_segment == data->segment_count) { + VGM_LOG("segmented_layout: can't find loop segment\n"); + loop_segment = 0; + } + if (loop_samples_skip > 0) { + VGM_LOG("segmented_layout: loop starts after %i samples\n", loop_samples_skip); + //todo skip/fix, but probably won't happen + } + data->current_segment = 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; + samples_to_do = vgmstream_samples_to_do(samples_this_block, sample_count, vgmstream); + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; + /* detect segment change and restart */ if (samples_to_do == 0) { data->current_segment++; reset_vgmstream(data->segments[data->current_segment]); - vgmstream->samples_into_block = 0; continue; } @@ -40,7 +59,7 @@ void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; + vgmstream->samples_into_block += samples_to_do; } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/Cstr.c b/Frameworks/vgmstream/vgmstream/src/meta/Cstr.c index 54218e20b..c625de84a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/Cstr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/Cstr.c @@ -280,7 +280,7 @@ VGMSTREAM * init_vgmstream_cstr(STREAMFILE *streamFile) { { int i; for (i=0;i<2;i++) { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,interleave); + 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/a2m.c b/Frameworks/vgmstream/vgmstream/src/meta/a2m.c new file mode 100644 index 000000000..93d41832b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/a2m.c @@ -0,0 +1,45 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* A2M - from Artificial Mind & Movement games [Scooby-Doo! Unmasked (PS2)] */ +VGMSTREAM * init_vgmstream_a2m(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"int") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41324D00) /* "A2M\0" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x50533200) /* "PS2\0" */ + goto fail; + + start_offset = 0x30; + data_size = get_streamfile_size(streamFile) - start_offset; + channel_count = 2; + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_A2M; + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x6000; + + 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/aax.c b/Frameworks/vgmstream/vgmstream/src/meta/aax.c index 15df8edec..1ad4dbaa7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aax.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aax.c @@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { 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; + int segment_count; segmented_layout_data *data = NULL; int table_error = 0; @@ -102,7 +102,6 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { if (!loop_flag && segment_loop_flag) { loop_start_sample = sample_count; - loop_segment = i; } sample_count += data->segments[i]->num_samples; @@ -130,7 +129,6 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { vgmstream->layout_type = layout_segmented; vgmstream->layout_data = data; - data->loop_segment = loop_segment; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acm.c b/Frameworks/vgmstream/vgmstream/src/meta/acm.c index 4f1cf6fa7..e35879c8c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/acm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/acm.c @@ -1,29 +1,48 @@ #include "meta.h" #include "../coding/coding.h" -#include "../coding/acm_decoder.h" +#include "../coding/acm_decoder_libacm.h" /* ACM - InterPlay infinity engine games [Planescape: Torment (PC), Baldur's Gate (PC)] */ VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int loop_flag = 0, channel_count, sample_rate, num_samples; + int force_channel_number = 0; acm_codec_data *data = NULL; /* checks */ - if (!check_extensions(streamFile, "acm")) + /* .acm: plain ACM extension (often but not always paired with .mus, parsed elsewhere) + * .wavc: header id for WAVC sfx (from bigfiles, extensionless) */ + if (!check_extensions(streamFile, "acm,wavc")) goto fail; - if (read_32bitBE(0x0,streamFile) != 0x97280301) /* header id */ + if (read_32bitBE(0x00,streamFile) != 0x97280301 && /* header id (music) */ + read_32bitBE(0x00,streamFile) != 0x57415643) /* "WAVC" (sfx) */ goto fail; + /* Plain ACM "channels" in the header may be set to 2 for mono voices or 1 for music, + * but actually seem related to ACM rows/cols and have nothing to do with channels. + * + * libacm will set plain ACM (not WAVC) to 2ch unless changes unless changed, but + * only Fallout (PC) seems to use plain ACM for sfx, others are WAVC (which do have channels). + * + * Doesn't look like there is any way to detect mono/stereo, so as a quick hack if + * we have a plain ACM (not WAVC) named .wavc we will force 1ch. */ + if (check_extensions(streamFile, "wavc") + && read_32bitBE(0x00,streamFile) == 0x97280301) { + force_channel_number = 1; + } + /* init decoder */ { - data = init_acm(streamFile); + ACMStream *handle; + data = init_acm(streamFile, force_channel_number); 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; + handle = data->handle; + channel_count = handle->info.channels; + sample_rate = handle->info.rate; + num_samples = handle->total_values / handle->info.channels; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c b/Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c new file mode 100644 index 000000000..e55ee8d2d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c @@ -0,0 +1,49 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util.h" + + +/* .ADX - from Capcom games [Resident Evil: Revelations (Switch), Monster Hunter XX (Switch)] */ +VGMSTREAM * init_vgmstream_adpcm_capcom(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile,"adpcm")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x02000000) + goto fail; + + channel_count = read_16bitLE(0x04, streamFile); + if (channel_count > 2) goto fail; /* header size seems fixed for mono/stereo */ + /* 0x08: channel size */ + /* 0x0c-14: some config/id? (may be shared between files) */ + loop_flag = read_16bitLE(0x68, streamFile); + + start_offset = 0xd8; /* also fixed for mono/stereo */ + + + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_ADPCM_CAPCOM; + vgmstream->sample_rate = read_32bitLE(0x64,streamFile); /* from first headerm repeated at +0x60 */ + vgmstream->num_samples = read_32bitLE(0x60, streamFile); + vgmstream->loop_start_sample = read_32bitLE(0x6c, streamFile); + vgmstream->loop_end_sample = read_32bitLE(0x70, streamFile) + 1; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_16bitLE(0x06, streamFile); + dsp_read_coefs_le(vgmstream,streamFile, 0x18, 0x60); + + 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/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 0dc09918c..290ef15c1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -10,7 +10,7 @@ #define MAX_TEST_FRAMES (INT_MAX/0x8000) -static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add); +static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add); /* ADX - CRI Middleware format */ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { @@ -75,13 +75,13 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* encryption */ if (version_signature == 0x0408) { - if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) { + if (find_adx_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) { coding_type = coding_CRI_ADX_enc_8; version_signature = 0x0400; } } else if (version_signature == 0x0409) { - if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) { + if (find_adx_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) { coding_type = coding_CRI_ADX_enc_9; version_signature = 0x0400; } @@ -140,6 +140,14 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { * 0x00 (4): "AINF"; 0x04 (4): size; 0x08 (10): str_id * 0x18 (2): volume (0=base/max?, negative=reduce) * 0x1c (2): pan l; 0x1e (2): pan r (0=base, max +-128) */ + + /* CINF header info (very rare, found after loops) [Sakura Taisen 3 (PS2)] + * 0x00 (4): "CINF" + * 0x04 (4): size + * 0x08 (4): "ASO ", unknown + * 0x28 (4): "SND ", unknown + * 0x48 (-): file name, null terminated + */ } else if (version_signature == 0x0500) { /* found in some SFD: Buggy Heat, appears to have no loop */ header_type = meta_ADX_05; @@ -238,20 +246,19 @@ fail: /* return 0 if not found, 1 if found and set parameters */ -static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) -{ +static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) { uint16_t * scales = NULL; uint16_t * prescales = NULL; - int bruteframe=0,bruteframecount=-1; + int bruteframe = 0, bruteframe_count = -1; int startoff, endoff; - int rc = 0; + int i, rc = 0; /* try to find key in external file first */ { uint8_t keybuf[6]; - if (read_key_file(keybuf, 6, file) == 6) { + if (read_key_file(keybuf, 6, streamFile) == 6) { *xor_start = get_16bitBE(keybuf+0); *xor_mult = get_16bitBE(keybuf+2); *xor_add = get_16bitBE(keybuf+4); @@ -259,86 +266,80 @@ static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_ } } - - /* guess key from the tables */ - startoff=read_16bitBE(2, file)+4; - endoff=(read_32bitBE(12, file)+31)/32*18*read_8bit(7, file)+startoff; - - /* how many scales? */ + /* setup totals */ { - int framecount=(endoff-startoff)/18; - if (framecount longest_length) { - longest_length=length; - longest=i-length+1; - if (longest_length >= 0x8000) break; + longest_length = length; + longest = i - length + 1; + if (longest_length >= 0x8000) + break; } } - if (longest==-1) { + if (longest == -1) { goto find_key_cleanup; } - bruteframecount = longest_length; + bruteframe_count = longest_length; bruteframe = longest; } + /* try to guess key */ { - /* try to guess key */ const adxkey_info * keys = NULL; int keycount = 0, keymask = 0; int scales_to_do; int key_id; /* allocate storage for scales */ - scales_to_do = (bruteframecount > MAX_TEST_FRAMES ? MAX_TEST_FRAMES : bruteframecount); + scales_to_do = (bruteframe_count > MAX_TEST_FRAMES ? MAX_TEST_FRAMES : bruteframe_count); scales = malloc(scales_to_do*sizeof(uint16_t)); - if (!scales) { - goto find_key_cleanup; - } + if (!scales) goto find_key_cleanup; + /* prescales are those scales before the first frame we test * against, we use these to compute the actual start */ if (bruteframe > 0) { - int i; /* allocate memory for the prescales */ prescales = malloc(bruteframe*sizeof(uint16_t)); - if (!prescales) { - goto find_key_cleanup; - } + if (!prescales) goto find_key_cleanup; + /* read the prescales */ for (i=0; iopen(streamFile,filename,9*channel_count*0x100); + chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!chstreamfile) goto fail; for (i=0;icoding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_DSP_AGSC; + vgmstream->meta_type = meta_AGSC; + vgmstream->allow_dual_stereo = 1; for (i=0;i<16;i++) { vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(header_offset+0xf6+i*2,streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ahv.c b/Frameworks/vgmstream/vgmstream/src/meta/ahv.c new file mode 100644 index 000000000..6de9ad70c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ahv.c @@ -0,0 +1,48 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* AHV - from Amuze games [Headhunter (PS2)] */ +VGMSTREAM * init_vgmstream_ahv(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size, channel_size, interleave; + int loop_flag, channel_count; + + + /* checks (.ahv: from names in bigfile) */ + if ( !check_extensions(streamFile,"ahv") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41485600) /* "AHV\0" */ + goto fail; + + start_offset = 0x800; + data_size = get_streamfile_size(streamFile) - start_offset; + interleave = read_32bitLE(0x10,streamFile); + channel_count = (interleave != 0) ? 2 : 1; + channel_size = read_32bitLE(0x08,streamFile); + loop_flag = 0; + /* VAGp header after 0x14 */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_AHV; + vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (interleave) + vgmstream->interleave_last_block_size = (data_size % (interleave*channel_count)) / channel_count; + + 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/aif_asobo.c b/Frameworks/vgmstream/vgmstream/src/meta/aif_asobo.c new file mode 100644 index 000000000..77cef8bee --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/aif_asobo.c @@ -0,0 +1,51 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .AIF - from Asobo Studio games [Ratatouille (PC), WALL-E (PC), Up (PC)] */ +VGMSTREAM * init_vgmstream_aif_asobo(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + /* aif: standard, aiffl: for plugins? */ + if ( !check_extensions(streamFile,"aif,aiffl") ) + goto fail; + if ((uint16_t)read_16bitLE(0x00,streamFile) != 0x69) /* Xbox codec */ + goto fail; + + channel_count = read_16bitLE(0x02,streamFile); /* assumed, only stereo is known */ + if (channel_count != 2) goto fail; + + /* 0x08: ? */ + if ((uint16_t)read_16bitLE(0x0c,streamFile) != 0x24*channel_count) /* Xbox block */ + goto fail; + if ((uint16_t)read_16bitLE(0x0e,streamFile) != 0x04) /* Xbox bps */ + goto fail; + loop_flag = 0; + + start_offset = 0x14; + 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_AIF_ASOBO; + vgmstream->sample_rate = read_32bitLE(0x04,streamFile); + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size,channel_count); + + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + 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/aifc.c b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c index 5ae7d61ce..daf2663c2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c @@ -2,7 +2,7 @@ #include "../layout/layout.h" -/* for reading integers inexplicably packed into 80 bit floats */ +/* for reading integers inexplicably packed into 80-bit ('double extended') floats */ static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) { uint8_t buf[10]; int32_t exponent; @@ -26,8 +26,7 @@ static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) { return mantissa*((buf[0]&0x80)?-1:1); } -static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset, - int marker_id) { +static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset, int marker_id) { uint16_t marker_count; int i; off_t marker_offset; @@ -52,72 +51,59 @@ static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset, /* Audio Interchange File Format AIFF/AIFF-C - from Mac/3DO games */ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - - off_t file_size = -1; - int channel_count = 0; - int sample_count = 0; - int sample_size = 0; - int sample_rate = 0; - int coding_type = -1; - off_t start_offset = -1; - int interleave = -1; + off_t start_offset = 0; + size_t file_size; + coding_t coding_type = 0; + int channel_count = 0, sample_count = 0, sample_size = 0, sample_rate = 0; + int interleave = 0; int loop_flag = 0; - int32_t loop_start = -1; - int32_t loop_end = -1; + int32_t loop_start = 0, loop_end = 0; - int AIFFext = 0; - int AIFCext = 0; - int AIFF = 0; - int AIFC = 0; - int FormatVersionChunkFound = 0; - int CommonChunkFound = 0; - int SoundDataChunkFound = 0; - int MarkerChunkFound = 0; - off_t MarkerChunkOffset = -1; - int InstrumentChunkFound =0; - off_t InstrumentChunkOffset = -1; + int is_aiff_ext = 0, is_aifc_ext = 0, is_aiff = 0, is_aifc = 0; + int FormatVersionChunkFound = 0, CommonChunkFound = 0, SoundDataChunkFound = 0, MarkerChunkFound = 0, InstrumentChunkFound = 0; + off_t MarkerChunkOffset = -1, InstrumentChunkOffset = -1; /* checks */ /* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC - * .cbd2: M2 games, .bgm: Super Street Fighter II Turbo (3DO), aifcl/aiffl: for plugins? */ + * .cbd2: M2 games + * .bgm: Super Street Fighter II Turbo (3DO) + * .acm: Crusader - No Remorse (SAT) + * .adp: Sonic Jam (SAT) + * .ai: Dragon Force (SAT) + * .aifcl/aiffl: for plugins? */ if (check_extensions(streamFile, "aif")) { - AIFCext = 1; - AIFFext = 1; + is_aifc_ext = 1; + is_aiff_ext = 1; } else if (check_extensions(streamFile, "aifc,aifcl,afc,cbd2,bgm")) { - AIFCext = 1; + is_aifc_ext = 1; } - else if (check_extensions(streamFile, "aiff,aiffl")) { - AIFFext = 1; - } - else { - goto fail; - } - - /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)==0x464F524D && /* "FORM" */ - /* check that file = header (8) + data */ - (uint32_t)read_32bitBE(4,streamFile)+8==get_streamfile_size(streamFile)) - { - if ((uint32_t)read_32bitBE(8,streamFile)==0x41494643) /* "AIFC" */ - { - if (!AIFCext) goto fail; - AIFC = 1; - } - else if ((uint32_t)read_32bitBE(8,streamFile)==0x41494646) /* "AIFF" */ - { - if (!AIFFext) goto fail; - AIFF = 1; - } - else goto fail; + else if (check_extensions(streamFile, "aiff,acm,adp,ai,aiffl")) { + is_aiff_ext = 1; } else { goto fail; } file_size = get_streamfile_size(streamFile); + if ((uint32_t)read_32bitBE(0x00,streamFile) != 0x464F524D && /* "FORM" */ + (uint32_t)read_32bitBE(0x04,streamFile)+0x08 != file_size) + goto fail; + + if ((uint32_t)read_32bitBE(0x08,streamFile) == 0x41494643) { /* "AIFC" */ + if (!is_aifc_ext) goto fail; + is_aifc = 1; + } + else if ((uint32_t)read_32bitBE(0x08,streamFile) == 0x41494646) { /* "AIFF" */ + if (!is_aiff_ext) goto fail; + is_aiff = 1; + } + else { + goto fail; + } + /* read through chunks to verify format and find metadata */ { @@ -125,7 +111,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { while (current_chunk < file_size) { uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitBE(current_chunk+4,streamFile); + off_t chunk_size = read_32bitBE(current_chunk+0x04,streamFile); /* chunks must be padded to an even number of bytes but chunk * size does not include that padding */ @@ -136,14 +122,14 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { switch(chunk_type) { case 0x46564552: /* "FVER" (version info) */ if (FormatVersionChunkFound) goto fail; - if (AIFF) goto fail; /* plain AIFF shouldn't have */ + if (is_aiff) goto fail; /* plain AIFF shouldn't have */ FormatVersionChunkFound = 1; /* specific size */ if (chunk_size != 4) goto fail; /* Version 1 of AIFF-C spec timestamp */ - if ((uint32_t)read_32bitBE(current_chunk+8,streamFile) != 0xA2805140) goto fail; + if ((uint32_t)read_32bitBE(current_chunk+0x08,streamFile) != 0xA2805140) goto fail; break; case 0x434F4D4D: /* "COMM" (main header) */ @@ -153,11 +139,11 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { channel_count = read_16bitBE(current_chunk+8,streamFile); if (channel_count <= 0) goto fail; - sample_count = (uint32_t)read_32bitBE(current_chunk+0x0a,streamFile); /* number of blocks, actually */ + sample_count = (uint32_t)read_32bitBE(current_chunk+0x0a,streamFile); /* sometimes number of blocks */ sample_size = read_16bitBE(current_chunk+0x0e,streamFile); sample_rate = read80bitSANE(current_chunk+0x10,streamFile); - if (AIFC) { + if (is_aifc) { switch (read_32bitBE(current_chunk+0x1a,streamFile)) { case 0x53445832: /* "SDX2" [3DO games: Super Street Fighter II Turbo (3DO), etc] */ coding_type = coding_SDX2; @@ -171,7 +157,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { coding_type = coding_DVI_IMA_int; if (channel_count != 1) break; /* don't know how stereo DVI is laid out */ break; - case 0x696D6134: /* "ima4" [Alida (PC) Lunar SSS (iOS)] */ + case 0x696D6134: /* "ima4" [Alida (PC), Lunar SSS (iOS)] */ coding_type = coding_APPLE_IMA4; interleave = 0x22; sample_count = sample_count * ((interleave-0x2)*2); @@ -182,7 +168,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { } /* string size and human-readable AIFF-C codec follows */ } - else if (AIFF) { + else if (is_aiff) { switch (sample_size) { case 8: coding_type = coding_PCM8; @@ -192,6 +178,9 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { coding_type = coding_PCM16BE; interleave = 2; break; + case 4: /* Crusader: No Remorse (SAT), Road Rash (3DO) */ + coding_type = coding_XA; + break; default: VGM_LOG("AIFF: unknown codec\n"); goto fail; @@ -200,10 +189,12 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { break; case 0x53534E44: /* "SSND" (main data) */ + case 0x4150434D: /* "APCM" (main data for XA) */ if (SoundDataChunkFound) goto fail; SoundDataChunkFound = 1; - start_offset = current_chunk + 16 + read_32bitBE(current_chunk+8,streamFile); + start_offset = current_chunk + 0x10 + read_32bitBE(current_chunk+0x08,streamFile); + /* when "APCM" XA frame size is at 0x0c, fixed to 0x914 */ break; case 0x4D41524B: /* "MARK" (loops) */ @@ -229,10 +220,10 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { } } - if (AIFC) { + if (is_aifc) { if (!FormatVersionChunkFound || !CommonChunkFound || !SoundDataChunkFound) goto fail; - } else if (AIFF) { + } else if (is_aiff) { if (!CommonChunkFound || !SoundDataChunkFound) goto fail; } @@ -260,7 +251,8 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { * We shouldn't have a loop point that overflows an int32_t * anyway. */ loop_flag = 1; - if (loop_start==loop_end) loop_flag = 0; + if (loop_start==loop_end) + loop_flag = 0; } } } @@ -276,12 +268,19 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { vgmstream->loop_end_sample = loop_end; vgmstream->coding_type = coding_type; - vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none; - vgmstream->interleave_block_size = interleave; + if (coding_type == coding_XA) { + vgmstream->layout_type = layout_blocked_xa_aiff; + /* AIFF XA can use sample rates other than 37800/18900 */ + /* some Crusader: No Remorse tracks have XA headers with incorrect 0xFF, rip bug/encoder feature? */ + } + else { + vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none; + vgmstream->interleave_block_size = interleave; + } - if (AIFC) + if (is_aifc) vgmstream->meta_type = meta_AIFC; - else if (AIFF) + else if (is_aiff) vgmstream->meta_type = meta_AIFF; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aix.c b/Frameworks/vgmstream/vgmstream/src/meta/aix.c index 682dced53..57b429eb8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aix.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aix.c @@ -1,167 +1,150 @@ #include "meta.h" #include "aix_streamfile.h" -/* AIX - interleaved AAX, N segments per channels [SoulCalibur IV (PS3)] */ +/* AIX - interleaved AAX, N segments with M layers (1/2ch) inside [SoulCalibur IV (PS3), Dragon Ball Z: Burst Limit (PS3)] */ VGMSTREAM * init_vgmstream_aix(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; + VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileAIX = NULL; - STREAMFILE * streamFileADX = NULL; - char filename[PATH_LIMIT]; + int loop_flag = 0, channel_count, sample_rate; + int32_t sample_count, loop_start_sample = 0, loop_end_sample = 0; + off_t *segment_offset = NULL; - int32_t *samples_in_segment = NULL; - int32_t sample_count; - - int loop_flag = 0; - int32_t loop_start_sample=0; - int32_t loop_end_sample=0; - + int32_t *segment_samples = NULL; aix_codec_data *data = NULL; - - off_t first_AIXP; - off_t stream_list_offset; - off_t stream_list_end; - const int segment_list_entry_size = 0x10; + off_t data_offset; + off_t layer_list_offset; + off_t layer_list_end; const off_t segment_list_offset = 0x20; + const size_t segment_list_entry_size = 0x10; + const size_t layer_list_entry_size = 0x08; - int stream_count,channel_count,segment_count; - int sample_rate; + int segment_count, layer_count; + int i, j; - int i; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("aix",filename_extension(filename))) goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x41495846 || /* "AIXF" */ - read_32bitBE(0x08,streamFile) != 0x01000014 || - read_32bitBE(0x0c,streamFile) != 0x00000800) + /* checks */ + if (!check_extensions(streamFile, "aix")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41495846 || /* "AIXF" */ + read_32bitBE(0x08,streamFile) != 0x01000014 || /* version? */ + read_32bitBE(0x0c,streamFile) != 0x00000800) goto fail; - first_AIXP = read_32bitBE(0x4,streamFile)+8; + /* base segment header */ + data_offset = read_32bitBE(0x04,streamFile)+0x08; + segment_count = (uint16_t)read_16bitBE(0x18,streamFile); - stream_list_offset = segment_list_offset+segment_list_entry_size*segment_count+0x10; + if (segment_count < 1) goto fail; - if (stream_list_offset >= first_AIXP) - goto fail; - if (segment_count < 1) - goto fail; + layer_list_offset = segment_list_offset + segment_count*segment_list_entry_size + 0x10; + if (layer_list_offset >= data_offset) goto fail; - sample_rate = read_32bitBE(stream_list_offset+8,streamFile); - if (sample_rate < 300 || sample_rate > 96000) - goto fail; - - samples_in_segment = calloc(segment_count,sizeof(int32_t)); - if (!samples_in_segment) - goto fail; + segment_samples = calloc(segment_count,sizeof(int32_t)); + if (!segment_samples) goto fail; segment_offset = calloc(segment_count,sizeof(off_t)); - if (!segment_offset) - goto fail; + if (!segment_offset) goto fail; - for (i = 0; i < segment_count; i++) + /* parse segments table */ { - segment_offset[i] = read_32bitBE(segment_list_offset+segment_list_entry_size*i+0,streamFile); - samples_in_segment[i] = read_32bitBE(segment_list_offset+segment_list_entry_size*i+0x08,streamFile); - /*printf("samples_in_segment[%d]=%d\n",i,samples_in_segment[i]);*/ - /* all segments must have equal sample rate */ - if (read_32bitBE(segment_list_offset+segment_list_entry_size*i+0x0c,streamFile) != sample_rate) - { - /* segments > 0 can have 0 sample rate (Ryu ga gotoku: Kenzan! tenkei_sng1.aix), - seems to indicate same sample rate as first */ - if (!(i > 0 && read_32bitBE(segment_list_offset+segment_list_entry_size*i+0x0c,streamFile) == 0)) - goto fail; + sample_rate = read_32bitBE(layer_list_offset+0x08,streamFile); /* first layer's sample rate */ + + for (i = 0; i < segment_count; i++) { + segment_offset[i] = read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x00,streamFile); + /* 0x04: segment size */ + segment_samples[i] = read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x08,streamFile); + + /* all segments must have equal sample rate */ + if (read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x0c,streamFile) != sample_rate) { + /* segments > 0 can have 0 sample rate (Ryu ga gotoku: Kenzan! tenkei_sng1.aix), + seems to indicate same sample rate as first */ + if (!(i > 0 && read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x0c,streamFile) == 0)) + goto fail; + } } + + if (segment_offset[0] != data_offset) + goto fail; } - if (segment_offset[0] != first_AIXP) - goto fail; + /* base layer header */ + layer_count = (uint8_t)read_8bit(layer_list_offset,streamFile); + if (layer_count < 1) goto fail; - stream_count = (uint8_t)read_8bit(stream_list_offset,streamFile); - if (stream_count < 1) - goto fail; - stream_list_end = stream_list_offset + 0x8 + stream_count * 8; - - if (stream_list_end >= first_AIXP) - goto fail; + layer_list_end = layer_list_offset + 0x08 + layer_count*layer_list_entry_size; + if (layer_list_end >= data_offset) goto fail; + /* parse layers table */ channel_count = 0; - for (i = 0; i < stream_count; i++) - { + for (i = 0; i < layer_count; i++) { /* all streams must have same samplerate as segments */ - if (read_32bitBE(stream_list_offset+8+i*8,streamFile)!=sample_rate) + if (read_32bitBE(layer_list_offset + 0x08 + i*layer_list_entry_size + 0x00,streamFile) != sample_rate) goto fail; - channel_count += read_8bit(stream_list_offset+8+i*8+4,streamFile); + channel_count += read_8bit(layer_list_offset + 0x08 + i*layer_list_entry_size + 0x04,streamFile); } /* check for existence of segments */ - for (i = 0; i < segment_count; i++) - { - int j; - off_t AIXP_offset = segment_offset[i]; - for (j = 0; j < stream_count; j++) - { - if (read_32bitBE(AIXP_offset,streamFile)!=0x41495850) /* "AIXP" */ + for (i = 0; i < segment_count; i++) { + off_t aixp_offset = segment_offset[i]; + for (j = 0; j < layer_count; j++) { + if (read_32bitBE(aixp_offset,streamFile) != 0x41495850) /* "AIXP" */ goto fail; - if (read_8bit(AIXP_offset+8,streamFile)!=j) + if (read_8bit(aixp_offset+0x08,streamFile) != j) goto fail; - AIXP_offset += read_32bitBE(AIXP_offset+4,streamFile)+8; + aixp_offset += read_32bitBE(aixp_offset+0x04,streamFile) + 0x08; } } - /*streamFileAIX = streamFile->open(streamFile,filename,sample_rate*0.0375*2/32*18segment_count);*/ - streamFileAIX = streamFile->open(streamFile,filename,sample_rate*0.1*segment_count); - if (!streamFileAIX) goto fail; - - data = malloc(sizeof(aix_codec_data)); - if (!data) goto fail; - data->segment_count = segment_count; - data->stream_count = stream_count; - data->adxs = malloc(sizeof(STREAMFILE *)*segment_count*stream_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; - memcpy(data->sample_counts,samples_in_segment,segment_count*sizeof(int32_t)); - - /* for each segment */ - for (i = 0; i < segment_count; i++) + /* open base streamfile, that will be shared by all open_aix_with_STREAMFILE */ { - int j; - /* for each stream */ - for (j = 0; j < stream_count; j++) - { - VGMSTREAM *adx; - /*printf("try opening segment %d/%d stream %d/%d %x\n",i,segment_count,j,stream_count,segment_offset[i]);*/ - streamFileADX = open_aix_with_STREAMFILE(streamFileAIX,segment_offset[i],j); - if (!streamFileADX) goto fail; - adx = data->adxs[i*stream_count+j] = init_vgmstream_adx(streamFileADX); - if (!adx) + char filename[PATH_LIMIT]; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + streamFileAIX = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); //todo simplify + if (!streamFileAIX) goto fail; + } + + /* init layout */ + { + data = malloc(sizeof(aix_codec_data)); + if (!data) goto fail; + + data->segment_count = segment_count; + data->stream_count = layer_count; + data->adxs = calloc(segment_count * layer_count, sizeof(VGMSTREAM*)); + if (!data->adxs) goto fail; + + data->sample_counts = calloc(segment_count,sizeof(int32_t)); + if (!data->sample_counts) goto fail; + + memcpy(data->sample_counts,segment_samples,segment_count*sizeof(int32_t)); + } + + /* open each segment / layer subfile */ + for (i = 0; i < segment_count; i++) { + for (j = 0; j < layer_count; j++) { + //;VGM_LOG("AIX: opening segment %d/%d stream %d/%d %x\n",i,segment_count,j,stream_count,segment_offset[i]); + VGMSTREAM *temp_vgmstream; + STREAMFILE * temp_streamFile = open_aix_with_STREAMFILE(streamFileAIX,segment_offset[i],j); + if (!temp_streamFile) goto fail; + + temp_vgmstream = data->adxs[i*layer_count+j] = init_vgmstream_adx(temp_streamFile); + + close_streamfile(temp_streamFile); + + if (!temp_vgmstream) goto fail; + + /* setup layers */ + if (temp_vgmstream->num_samples != data->sample_counts[i] || temp_vgmstream->loop_flag != 0) goto fail; - close_streamfile(streamFileADX); streamFileADX = NULL; - - if (adx->num_samples != data->sample_counts[i] || - 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)); - + memcpy(temp_vgmstream->start_ch,temp_vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*temp_vgmstream->channels); + memcpy(temp_vgmstream->start_vgmstream,temp_vgmstream,sizeof(VGMSTREAM)); } } - if (segment_count > 1) - { - loop_flag = 1; - } - + /* get looping and samples */ sample_count = 0; - for (i = 0; i < segment_count; i++) - { + loop_flag = (segment_count > 1); + for (i = 0; i < segment_count; i++) { sample_count += data->sample_counts[i]; if (i == 0) @@ -170,45 +153,45 @@ VGMSTREAM * init_vgmstream_aix(STREAMFILE *streamFile) { loop_end_sample = sample_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->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; + vgmstream->meta_type = meta_AIX; vgmstream->coding_type = data->adxs[0]->coding_type; vgmstream->layout_type = layout_aix; - vgmstream->meta_type = meta_AIX; vgmstream->ch[0].streamfile = streamFileAIX; data->current_segment = 0; vgmstream->codec_data = data; free(segment_offset); - free(samples_in_segment); + free(segment_samples); return vgmstream; - /* clean up anything we may have opened */ fail: - if (streamFileAIX) close_streamfile(streamFileAIX); - if (streamFileADX) close_streamfile(streamFileADX); - if (vgmstream) close_vgmstream(vgmstream); - if (samples_in_segment) free(samples_in_segment); - if (segment_offset) free(segment_offset); + close_streamfile(streamFileAIX); + close_vgmstream(vgmstream); + free(segment_samples); + free(segment_offset); + + /* free aix layout */ if (data) { - if (data->adxs) - { + if (data->adxs) { int i; - for (i=0;isegment_count*data->stream_count;i++) - if (data->adxs) - close_vgmstream(data->adxs[i]); + for (i = 0; i < data->segment_count*data->stream_count; i++) { + close_vgmstream(data->adxs[i]); + } free(data->adxs); } - if (data->sample_counts) - { + if (data->sample_counts) { free(data->sample_counts); } free(data); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/akb.c b/Frameworks/vgmstream/vgmstream/src/meta/akb.c index 0f65d66a8..a42d091aa 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/akb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/akb.c @@ -188,7 +188,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, material_offset, extradata_offset; size_t material_size, extradata_size, stream_size; - int loop_flag = 0, channel_count, encryption_flag, codec, sample_rate, num_samples, loop_start, loop_end; + int loop_flag = 0, channel_count, encryption_flag, codec, sample_rate, /*num_samples, loop_start,*/ loop_end; int total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ @@ -232,9 +232,9 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *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); + //num_samples = read_32bitLE(material_offset+0x0c,streamFile); - loop_start = read_32bitLE(material_offset+0x10,streamFile); + //loop_start = read_32bitLE(material_offset+0x10,streamFile); loop_end = read_32bitLE(material_offset+0x14,streamFile); extradata_size = read_32bitLE(material_offset+0x18,streamFile); /* rest: ? (empty or 0x3f80) */ @@ -309,8 +309,8 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *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 = num_samples; - vgmstream->loop_start_sample = loop_start; + vgmstream->num_samples = read_32bitLE(material_offset+0x0c,streamFile);//num_samples; + vgmstream->loop_start_sample = read_32bitLE(material_offset+0x10,streamFile);//loop_start; vgmstream->loop_end_sample = loop_end; break; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ao.c b/Frameworks/vgmstream/vgmstream/src/meta/ao.c new file mode 100644 index 000000000..1a6f2a063 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ao.c @@ -0,0 +1,36 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .AO - from AlphaOgg lib [Cloudphobia (PC)] */ +VGMSTREAM * init_vgmstream_ao(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + + + /* checks */ + if ( !check_extensions(streamFile,"ao") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x414C5048) /* "ALPH" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x414F4747) /* "AOGG" */ + goto fail; + +#ifdef VGM_USE_VORBIS + { + ogg_vorbis_meta_info_t ovmi = {0}; + + ovmi.meta_type = meta_AO; + /* values at 0x08/0x0c/0x10 may be related to looping? */ + start_offset = 0xc8; + vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); + } +#else + goto fail; +#endif + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/apc.c b/Frameworks/vgmstream/vgmstream/src/meta/apc.c new file mode 100644 index 000000000..25239be82 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/apc.c @@ -0,0 +1,48 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* APC - from Cryo games [MegaRace 3 (PC)] */ +VGMSTREAM * init_vgmstream_apc(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"apc") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4352594F) /* "CRYO" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x5F415043) /* "_APC" */ + goto fail; + //if (read_32bitBE(0x08,streamFile) != 0x312E3230) /* "1.20" */ + // goto fail; + + /* 0x14/18: L/R hist sample? */ + + start_offset = 0x20; + data_size = get_streamfile_size(streamFile) - start_offset; + channel_count = read_32bitLE(0x1c,streamFile) == 0 ? 1 : 2; + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_APC; + vgmstream->sample_rate = read_32bitLE(0x10,streamFile); + vgmstream->num_samples = ima_bytes_to_samples(data_size,channel_count); + + vgmstream->coding_type = coding_IMA; + vgmstream->layout_type = layout_none; + + 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/ast.c b/Frameworks/vgmstream/vgmstream/src/meta/ast.c index 4ed5162c2..ede135947 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ast.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ast.c @@ -2,90 +2,60 @@ #include "../layout/layout.h" #include "../util.h" +/* .AST - from Nintendo games [Super Mario Galaxy (Wii), Pac-Man Vs (GC)] */ VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + off_t start_offset; + int loop_flag, channel_count, codec; - coding_t coding_type; - int codec_number; - int channel_count; - int loop_flag; - - size_t max_block; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ast",filename_extension(filename))) goto fail; - - /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)!=0x5354524D || /* "STRM" */ - read_16bitBE(0xa,streamFile)!=0x10 || - /* check that file = header (0x40) + data */ - read_32bitBE(4,streamFile)+0x40!=get_streamfile_size(streamFile)) + /* checks */ + if (!check_extensions(streamFile, "ast")) goto fail; - - /* check for a first block */ - if (read_32bitBE(0x40,streamFile)!=0x424C434B) /* "BLCK" */ + if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ + goto fail; + if (read_16bitBE(0x0a,streamFile) != 0x10) /* ? */ goto fail; - /* check type details */ - codec_number = read_16bitBE(8,streamFile); - loop_flag = read_16bitBE(0xe,streamFile); - channel_count = read_16bitBE(0xc,streamFile); - max_block = read_32bitBE(0x20,streamFile); + if (read_32bitBE(0x04,streamFile)+0x40 != get_streamfile_size(streamFile)) + goto fail; + codec = read_16bitBE(0x08,streamFile); + channel_count = read_16bitBE(0x0c,streamFile); + loop_flag = read_16bitBE(0x0e,streamFile); + //max_block = read_32bitBE(0x20,streamFile); - switch (codec_number) { - case 0: - coding_type = coding_NGC_AFC; + start_offset = 0x40; + if (read_32bitBE(start_offset,streamFile) != 0x424C434B) /* "BLCK" */ + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_AST; + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + vgmstream->num_samples = read_32bitBE(0x14,streamFile); + vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile); + vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile); + + vgmstream->layout_type = layout_blocked_ast; + switch (codec) { + case 0x00: /* , Pikmin 2 (GC) */ + vgmstream->coding_type = coding_NGC_AFC; break; - case 1: - coding_type = coding_PCM16BE; + case 0x01: /* Mario Kart: Double Dash!! (GC) */ + vgmstream->coding_type = coding_PCM16BE; break; default: 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_32bitBE(0x14,streamFile); - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile); - - vgmstream->coding_type = coding_type; - vgmstream->layout_type = layout_blocked_ast; - vgmstream->meta_type = meta_AST; - - /* open the file for reading by each channel */ - { - int i; - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename, - (i==0? - max_block+0x20-4: /* first buffer a bit bigger to - read block header without - inefficiency */ - max_block - ) - ); - - if (!vgmstream->ch[i].streamfile) goto fail; - } - } - - /* start me up */ - block_update_ast(0x40,vgmstream); - + 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/atsl.c b/Frameworks/vgmstream/vgmstream/src/meta/atsl.c index bb19f62ff..7ec190248 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/atsl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/atsl.c @@ -120,10 +120,12 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) { vgmstream = init_vgmstream_riff(temp_streamFile); if (!vgmstream) goto fail; break; +#ifdef VGM_USE_VORBIS case KOVS: vgmstream = init_vgmstream_ogg_vorbis(temp_streamFile); if (!vgmstream) goto fail; break; +#endif case KTSS: vgmstream = init_vgmstream_ktss(temp_streamFile); if (!vgmstream) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index 2990c772c..0b75c44c9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -156,13 +156,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { } - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset)) goto fail; - - if (vgmstream->layout_type == layout_blocked_awc) - block_update_awc(awc.stream_offset, vgmstream); - return vgmstream; fail: @@ -338,7 +333,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { /* If music, data is divided into blocks of block_chunk size with padding. * Each block has a header/seek table and interleaved data for all channels */ if (awc->is_music && read_32bit(awc->stream_offset, streamFile) != 0) { - VGM_LOG("AWC: music found, but block doesn't start with seek table at %lx\n", awc->stream_offset); + VGM_LOG("AWC: music found, but block doesn't start with seek table at %"PRIx64"\n", (off64_t)awc->stream_offset); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h index f4c231c7a..541066247 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h @@ -5,26 +5,25 @@ typedef struct { /* config */ + int channel; + int channel_count; + size_t block_size; 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; + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ - size_t total_size; /* size of the resulting XMA data */ + size_t skip_size; /* size to skip from a block start to reach data start */ + size_t data_size; /* logical size of the block */ + + size_t logical_size; } 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_repeated_data_size(STREAMFILE *streamFile, off_t next_offset, size_t repeat_samples); 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. @@ -35,7 +34,7 @@ static size_t awc_xma_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offse size_t frame_size = 0x800; /* ignore bad reads */ - if (offset < 0 || offset > data->total_size) { + if (offset < 0 || offset > data->logical_size) { return 0; } @@ -44,97 +43,84 @@ static size_t awc_xma_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offse 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; + data->data_size = 0; } - /* read from block, moving to next when all data is consumed */ + /* read blocks, one at a time */ while (length > 0) { - size_t to_read, bytes_read; - /* new block */ - if (data->current_data_size == 0) { + /* ignore EOF */ + if (data->logical_offset >= data->logical_size) { + break; + } + + /* process new block */ + if (data->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 others_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); + if (repeat_samples) { + off_t data_offset = data->physical_offset + header_size + others_size; + repeat_size = get_repeated_data_size(streamfile, data_offset, repeat_samples); } - 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; + data->skip_size = header_size + others_size + repeat_size; + data->data_size = data_size - repeat_size; + } + + /* move to next block */ + if (offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_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; + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_offset; + + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + + offset += bytes_done; + total_read += bytes_done; + length -= bytes_done; + dest += bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } } - /* 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; + off_t physical_offset, max_physical_offset; size_t frame_size = 0x800; - size_t total_size = 0; + size_t logical_size = 0; - if (data->total_size) - return data->total_size; + if (data->logical_size) + return data->logical_size; physical_offset = data->stream_offset; max_physical_offset = data->stream_offset + data->stream_size; - last_offset = 0; - /* read blocks and sum final size */ + /* get size of the logical stream */ while (physical_offset < max_physical_offset) { size_t header_size = get_block_header_size(streamfile, physical_offset, data); /* header table entries = frames... I hope */ @@ -145,33 +131,38 @@ static size_t awc_xma_io_size(STREAMFILE *streamfile, awc_xma_io_data* data) { 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) { + if (repeat_samples) { off_t data_offset = physical_offset + header_size + skip_size; - repeat_size = get_repeated_data_size(streamfile, data_offset, last_offset); + repeat_size = get_repeated_data_size(streamfile, data_offset, repeat_samples); } - last_offset = physical_offset + header_size + skip_size + data_size; - total_size += data_size - repeat_size; - physical_offset += data->chunk_size; + logical_size += data_size - repeat_size; + physical_offset += data->block_size; } - data->total_size = total_size; - return data->total_size; + data->logical_size = logical_size; + return data->logical_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) { +static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, size_t block_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.channel = channel; + io_data.channel_count = channel_count; 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.block_size = block_size; io_data.physical_offset = stream_offset; + io_data.logical_size = awc_xma_io_size(streamFile, &io_data); /* force init */ + + if (io_data.logical_size > io_data.stream_size) { + VGM_LOG("AWC XMA: wrong logical size\n"); + goto fail; + } /* setup subfile */ new_streamFile = open_wrap_streamfile(streamFile); @@ -182,7 +173,10 @@ static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *streamFile, off_t stream if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; - //todo maybe should force to read filesize once + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + return temp_streamFile; fail: @@ -209,35 +203,29 @@ static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xm /* 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; +static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t next_offset, size_t repeat_samples) { + const size_t frame_size = 0x800; + const size_t samples_per_subframe = 512; + size_t samples_this_frame; + uint8_t subframes; - /* read block first frame */ - if (read_streamfile(new_frame,new_offset, frame_size,streamFile) != frame_size) - goto fail; + //todo: fix this + /* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that. + * Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples. + * We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though + * output will be slightly off since subframes are related. + * + * For now just skip a full frame depending on the number of subframes vs repeat samples. + * Most files work ok-ish but channels may desync slightly. */ - /* 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; + subframes = ((uint8_t)read_8bit(next_offset,streamFile) >> 2) & 0x3F; /* peek into frame header */ + samples_this_frame = subframes*samples_per_subframe; + if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */ + return frame_size; + } + else { + return 0; } - -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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c b/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c index 0b5d89ef4..87603d296 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bcstm.c @@ -1,165 +1,133 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" + /* BCSTM - Nintendo 3DS format */ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - coding_t coding_type; - - off_t info_offset = 0, seek_offset = 0, data_offset = 0; - uint16_t temp_id; - int codec_number; - int channel_count, loop_flag; - int i, ima = 0; off_t start_offset; - int section_count; + off_t info_offset = 0, seek_offset = 0, data_offset = 0; + int channel_count, loop_flag, codec; + int is_camelot_ima = 0; - /* check extension, case insensitive */ + + /* checks */ if ( !check_extensions(streamFile,"bcstm") ) goto fail; - /* check header */ - if ((uint32_t)read_32bitBE(0, streamFile) != 0x4353544D) /* "CSTM" */ + /* CSTM header */ + if (read_32bitBE(0x00, streamFile) != 0x4353544D) /* "CSTM" */ + goto fail; + /* 0x06(2): header size (0x40), 0x08: version (0x00000400), 0x0c: file size (not accurate for Camelot IMA) */ + + /* check BOM */ + if ((uint16_t)read_16bitLE(0x04, streamFile) != 0xFEFF) goto fail; - if ((uint16_t)read_16bitLE(4, streamFile) != 0xFEFF) - goto fail; - - section_count = read_16bitLE(0x10, streamFile); - for (i = 0; i < section_count; i++) { - temp_id = read_16bitLE(0x14 + i * 0xc, streamFile); - switch(temp_id) { - case 0x4000: - info_offset = read_32bitLE(0x18 + i * 0xc, streamFile); - /* size_t info_size = read_32bitLE(0x1c + i * 0xc, streamFile); */ - break; - case 0x4001: - seek_offset = read_32bitLE(0x18 + i * 0xc, streamFile); - /* size_t seek_size = read_32bitLE(0x1c + i * 0xc, streamFile); */ - break; - case 0x4002: - data_offset = read_32bitLE(0x18 + i * 0xc, streamFile); - /* size_t data_size = read_32bitLE(0x1c + i * 0xc, streamFile); */ - break; - case 0x4003: - /* off_t regn_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ - /* size_t regn_size = read_32bitLE(0x1c + i * 0xc, streamFile); */ - break; - case 0x4004: - /* off_t pdat_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ - /* size_t pdat_size = read_32bitLE(0x1c + i * 0xc, streamFile); */ - break; - default: - break; + /* get sections (should always appear in the same order) */ + { + int i; + int section_count = read_16bitLE(0x10, streamFile); + for (i = 0; i < section_count; i++) { + /* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */ + uint16_t section_id = read_16bitLE(0x14 + i*0x0c+0x00, streamFile); + switch(section_id) { + case 0x4000: info_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break; + case 0x4001: seek_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break; + case 0x4002: data_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break; + //case 0x4003: /* off_t regn_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ /* ? */ + //case 0x4004: /* off_t pdat_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ /* ? */ + default: + break; + } } } - if (info_offset == 0) goto fail; - if ((uint32_t)read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ + /* INFO section */ + if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ goto fail; - - - /* check type details */ - codec_number = read_8bit(info_offset + 0x20, streamFile); + codec = read_8bit(info_offset + 0x20, streamFile); loop_flag = read_8bit(info_offset + 0x21, streamFile); channel_count = read_8bit(info_offset + 0x22, streamFile); - switch (codec_number) { - case 0: - coding_type = coding_PCM8; - break; - case 1: - coding_type = coding_PCM16LE; - break; - case 2: - if (seek_offset == 0) goto fail; - if ((uint32_t)read_32bitBE(seek_offset, streamFile) != 0x5345454B) { /* "SEEK" If this header doesn't exist, assuming that the file is IMA */ - ima = 1; - coding_type = coding_3DS_IMA; - } - else - 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_32bitLE(info_offset + 0x2c, streamFile); - vgmstream->sample_rate = read_32bitLE(info_offset + 0x24, streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - if (ima) //Shift the loop points back slightly to avoid stupid pops in some IMA streams due to DC offsetting - { - vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile); - if (vgmstream->loop_start_sample > 10000) - { - vgmstream->loop_start_sample -= 5000; - vgmstream->loop_end_sample = vgmstream->num_samples - 5000; - } - else - vgmstream->loop_end_sample = vgmstream->num_samples; - } - else - { - vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile); - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - vgmstream->coding_type = coding_type; - vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; - vgmstream->meta_type = meta_CSTM; - - if (ima) { - vgmstream->interleave_block_size = 0x200; - } else { - vgmstream->interleave_block_size = read_32bitLE(info_offset + 0x34, streamFile); - vgmstream->interleave_last_block_size = read_32bitLE(info_offset + 0x44, streamFile); - } - - if (vgmstream->coding_type == coding_NGC_DSP) { - off_t coef_offset; - off_t tempoffset = info_offset; - int foundcoef = 0; - int i, j; - int coef_spacing = 0x2E; - - while (!(foundcoef)) - { - if ((uint32_t)read_32bitLE(tempoffset, streamFile) == 0x00004102) - { - coef_offset = read_32bitLE(tempoffset + 4, streamFile) + tempoffset + (channel_count * 8) - 4 - info_offset; - foundcoef++; - break; - } - tempoffset++; - } - - for (j = 0; jchannels; j++) { - for (i = 0; i<16; i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitLE(info_offset + coef_offset + j*coef_spacing + i * 2, streamFile); - } - } - } - - if (ima) { // No SEEK (ADPC) header, so just start where the SEEK header is supposed to be. + /* Camelot games have some weird codec hijack [Mario Tennis Open (3DS), Mario Golf: World Tour (3DS)] */ + if (codec == 0x02 && read_32bitBE(seek_offset, streamFile) != 0x5345454B) { /* "SEEK" */ if (seek_offset == 0) goto fail; start_offset = seek_offset; - } else { + is_camelot_ima = 1; + } + else { if (data_offset == 0) goto fail; start_offset = data_offset + 0x20; } - /* open the file for reading by each channel */ + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(info_offset + 0x24, streamFile); + vgmstream->num_samples = read_32bitLE(info_offset + 0x2c, streamFile); + vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile); + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_CSTM; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(info_offset + 0x34, streamFile); + vgmstream->interleave_last_block_size = read_32bitLE(info_offset + 0x44, streamFile); + + /* Camelot doesn't follow header values */ + if (is_camelot_ima) { + size_t block_samples, last_samples; + size_t data_size = get_streamfile_size(streamFile) - start_offset; + vgmstream->interleave_block_size = 0x200; + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*channel_count)) / channel_count; + + /* align the loop points back to avoid pops in some IMA streams, seems to help some Mario Golf songs but not all */ + block_samples = ima_bytes_to_samples(vgmstream->interleave_block_size*channel_count,channel_count); // 5000? + last_samples = ima_bytes_to_samples(vgmstream->interleave_last_block_size*channel_count,channel_count); + if (vgmstream->loop_start_sample > block_samples) { + vgmstream->loop_start_sample -= last_samples; + vgmstream->loop_end_sample -= last_samples; + } + } + + switch(codec) { + case 0x00: + vgmstream->coding_type = coding_PCM8; + break; + case 0x01: + vgmstream->coding_type = coding_PCM16LE; + break; + case 0x02: + vgmstream->coding_type = coding_NGC_DSP; + + if (is_camelot_ima) { + vgmstream->coding_type = coding_3DS_IMA; + } + else { + int i, c; + off_t channel_indexes, channel_info_offset, coefs_offset; + + channel_indexes = info_offset+0x08 + read_32bitLE(info_offset + 0x1C, streamFile); + for (i = 0; i < vgmstream->channels; i++) { + channel_info_offset = channel_indexes + read_32bitLE(channel_indexes+0x04+(i*0x08)+0x04, streamFile); + coefs_offset = channel_info_offset + read_32bitLE(channel_info_offset+0x04, streamFile); + + for (c = 0; c < 16; c++) { + vgmstream->ch[i].adpcm_coef[c] = read_16bitLE(coefs_offset + c*2, streamFile); + } + } + } + break; + + default: /* 0x03: IMA? */ + goto fail; + } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c b/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c index 19a2bb902..c7fb775f3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c @@ -1,171 +1,235 @@ #include "meta.h" -#include "../util.h" -#include "../stack_alloc.h" +#include "../coding/coding.h" + + +/* Regions seem mostly for in-game purposes and are not very listenable on its own. + * Also, sample start is slightly off since vgmstream can't start in the middle of block ATM. + * Otherwise this kinda works, but for now it's just a test. */ +#define BFSTM_ENABLE_REGION_SUBSONGS 0 +#define BFSTM_ENABLE_REGION_FORCE_LOOPS 0 /* this makes sense in SM3D World, but not in Zelda BotW) */ + +#if BFSTM_ENABLE_REGION_SUBSONGS +static off_t bfstm_set_regions(STREAMFILE *streamFile, VGMSTREAM *vgmstream, int region_count, off_t regn_offset, int codec, int big_endian); +#endif + /* BFSTM - Nintendo Wii U format */ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - coding_t coding_type; - coding_t coding_PCM16; + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + off_t info_offset = 0, data_offset = 0; + int channel_count, loop_flag, codec; + int big_endian; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; +#if BFSTM_ENABLE_REGION_SUBSONGS + off_t regn_offset = 0; + int region_count; +#endif - off_t info_offset = 0, seek_offset = 0, data_offset = 0; - uint16_t temp_id; - int codec_number; - int channel_count, loop_flag; - int i, j; - int ima = 0; - off_t start_offset; - off_t tempoffset1; - int section_count; - /* check extension, case insensitive */ - if ( !check_extensions(streamFile,"bfstm") ) - goto fail; + /* checks */ + if ( !check_extensions(streamFile,"bfstm") ) + goto fail; - /* check header */ - if ((uint32_t)read_32bitBE(0, streamFile) != 0x4653544D) /* "FSTM" */ - goto fail; + /* FSTM header */ + if (read_32bitBE(0x00, streamFile) != 0x4653544D) /* "FSTM" */ + goto fail; + /* 0x06(2): header size (0x40), 0x08: version (0x00000400), 0x0c: file size */ - if ((uint16_t)read_16bitBE(4, streamFile) == 0xFEFF) { /* endian marker (BE most common) */ + /* check BOM */ + if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFEFF) { /* Wii U games */ read_32bit = read_32bitBE; read_16bit = read_16bitBE; - coding_PCM16 = coding_PCM16BE; - } else if ((uint16_t)read_16bitBE(4, streamFile) == 0xFFFE) { /* Blaster Master Zero 3DS */ + big_endian = 1; + } else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* Switch games */ read_32bit = read_32bitLE; read_16bit = read_16bitLE; - coding_PCM16 = coding_PCM16LE; - } else { - goto fail; - } + big_endian = 0; + } else { + goto fail; + } - section_count = read_16bit(0x10, streamFile); - for (i = 0; i < section_count; i++) { - temp_id = read_16bit(0x14 + i * 0xc, streamFile); - switch(temp_id) { - case 0x4000: - info_offset = read_32bit(0x18 + i * 0xc, streamFile); - /* size_t info_size = read_32bit(0x1c + i * 0xc, streamFile); */ - break; - case 0x4001: - seek_offset = read_32bit(0x18 + i * 0xc, streamFile); - /* size_t seek_size = read_32bit(0x1c + i * 0xc, streamFile); */ - break; - case 0x4002: - data_offset = read_32bit(0x18 + i * 0xc, streamFile); - /* size_t data_size = read_32bit(0x1c + i * 0xc, streamFile); */ - break; - case 0x4003: - /* off_t regn_offset = read_32bit(0x18 + i * 0xc, streamFile); */ - /* size_t regn_size = read_32bit(0x1c + i * 0xc, streamFile); */ - break; - case 0x4004: - /* off_t pdat_offset = read_32bit(0x18 + i * 0xc, streamFile); */ - /* size_t pdat_size = read_32bit(0x1c + i * 0xc, streamFile); */ - break; - default: - break; - } - } - - if (info_offset == 0) goto fail; - if ((uint32_t)read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ - goto fail; + /* get sections (should always appear in the same order) */ + { + int i; + int section_count = read_16bit(0x10, streamFile); + for (i = 0; i < section_count; i++) { + /* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */ + uint16_t section_id = read_16bit(0x14 + i*0xc+0x00, streamFile); + switch(section_id) { + case 0x4000: info_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break; + case 0x4001: /* seek_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); */ break; + case 0x4002: data_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break; +#if BFSTM_ENABLE_REGION_SUBSONGS + case 0x4003: regn_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break; +#endif + case 0x4004: /* pdat_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); */ break; /* prefetch data */ + default: + break; + } + } + + if (info_offset == 0 || data_offset == 0) + goto fail; + } + + /* INFO section */ + if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ + goto fail; + codec = read_8bit(info_offset + 0x20, streamFile); + loop_flag = read_8bit(info_offset + 0x21, streamFile); + channel_count = read_8bit(info_offset + 0x22, streamFile); +#if BFSTM_ENABLE_REGION_SUBSONGS + region_count = read_8bit(info_offset + 0x23, streamFile); +#endif - /* check type details */ - codec_number = read_8bit(info_offset + 0x20, streamFile); - loop_flag = read_8bit(info_offset + 0x21, streamFile); - channel_count = read_8bit(info_offset + 0x22, streamFile); + start_offset = data_offset + 0x20; - 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; - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; + vgmstream->sample_rate = read_32bit(info_offset + 0x24, streamFile); + vgmstream->num_samples = read_32bit(info_offset + 0x2c, streamFile); + vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, streamFile); + vgmstream->loop_end_sample = vgmstream->num_samples; - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bit(info_offset + 0x2c, streamFile); - vgmstream->sample_rate = read_32bit(info_offset + 0x24, streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - if (ima) //Shift the loop points back slightly to avoid stupid pops in some IMA streams due to DC offsetting - { - vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, streamFile); - if (vgmstream->loop_start_sample > 10000) - { - vgmstream->loop_start_sample -= 5000; - vgmstream->loop_end_sample = vgmstream->num_samples - 5000; - } - else - vgmstream->loop_end_sample = vgmstream->num_samples; - } - else - { - vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, streamFile); - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - vgmstream->coding_type = coding_type; + vgmstream->meta_type = meta_FSTM; vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; - vgmstream->meta_type = meta_FSTM; + vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile); + vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, streamFile); - if (ima) - vgmstream->interleave_block_size = 0x200; - else { - vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile); - vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, streamFile); - } + 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; - if (vgmstream->coding_type == coding_NGC_DSP) { - off_t coeff_ptr_table; - VARDECL(off_t, coef_offset); - ALLOC(coef_offset, channel_count, off_t); - coeff_ptr_table = read_32bit(info_offset + 0x1c, streamFile) + info_offset + 8; // Getting pointer for coefficient pointer table - - for (i = 0; i < channel_count; i++) { - tempoffset1 = read_32bit(coeff_ptr_table + 8 + i * 8, streamFile); - coef_offset[i] = tempoffset1 + coeff_ptr_table; - coef_offset[i] += read_32bit(coef_offset[i] + 4, streamFile); - } - - for (j = 0; jchannels; j++) { - for (i = 0; i<16; i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bit(coef_offset[j] + i * 2, streamFile); - } - } - } + { + int i, c; + off_t channel_indexes, channel_info_offset, coefs_offset; - if (ima) { // No SEEK (ADPC) header, so just start where the SEEK header is supposed to be. - if (seek_offset == 0) goto fail; - start_offset = seek_offset; - } else { - if (data_offset == 0) goto fail; - start_offset = data_offset + 0x20; - } + channel_indexes = info_offset+0x08 + read_32bit(info_offset + 0x1C, streamFile); + for (i = 0; i < vgmstream->channels; i++) { + channel_info_offset = channel_indexes + read_32bit(channel_indexes+0x04+(i*0x08)+0x04, streamFile); + coefs_offset = channel_info_offset + read_32bit(channel_info_offset+0x04, streamFile); + + for (c = 0; c < 16; c++) { + vgmstream->ch[i].adpcm_coef[c] = read_16bit(coefs_offset + c*2, streamFile); + } + } + } + break; + + default: /* 0x03: IMA? */ + goto fail; + } - /* open the file for reading by each channel */ - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; +#if BFSTM_ENABLE_REGION_SUBSONGS + start_offset += bfstm_set_regions(streamFile, vgmstream, region_count, regn_offset, codec, big_endian); +#endif - return vgmstream; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; fail: - close_vgmstream(vgmstream); - return NULL; + close_vgmstream(vgmstream); + return NULL; } + + +#if BFSTM_ENABLE_REGION_SUBSONGS +/* Newer .bfstm may have multiple regions, that are sample sections of some meaning, + * like loop parts (Super Mario 3D World), or dynamic subsongs (Zelda: BotW) + * We'll hack them in as subsongs (though seem mostly activated by game events) */ +static off_t bfstm_set_regions(STREAMFILE *streamFile, VGMSTREAM *vgmstream, int region_count, off_t regn_offset, int codec, int big_endian) { + off_t start_offset; + size_t stream_size; + int total_subsongs, target_subsong = streamFile->stream_index; + int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitLE : read_16bitLE; + + + if (region_count <= 0 && regn_offset == 0 && codec != 0x02) + goto fail; + if (read_32bitBE(regn_offset, streamFile) != 0x5245474E) /* "REGN" */ + goto fail; + + /* pretend each region is a subsong, but use first subsong as the whole file, + * since regions may not map all samples */ + total_subsongs = region_count + 1; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + if (target_subsong > 1) { + int sample_aligned = 0, sample_skip = 0; + int i; + off_t region_start, region_end; + size_t block_size; + size_t sample_start = read_32bit(regn_offset + 0x20 + (target_subsong-2)*0x100+0x00, streamFile); + size_t sample_end = read_32bit(regn_offset + 0x20 + (target_subsong-2)*0x100+0x04, streamFile) + 1; + off_t adpcm_offset = regn_offset + 0x20 + (target_subsong-2)*0x100+0x08; + /* rest is padding up to 0x100 */ + + /* samples-to-bytes, approximate since samples could land in the middle of a 0x08 frame */ + region_start = sample_start / 14 * vgmstream->channels * 0x08; + region_end = sample_end / 14 * vgmstream->channels * 0x08; + stream_size = region_end - region_start; + //;VGM_LOG("BFSTM: region offset start=%lx, end=%lx\n", region_start, region_end); + + /* align to block start or interleave causes funny sounds, but the bigger the interleave + * the less accurate this is (with 0x2000 align can be off by ~4600 samples per channel) */ + //todo could be fixed with interleave_first_block + block_size = (vgmstream->interleave_block_size*vgmstream->channels); + if (region_start % block_size) { + region_start -= region_start % block_size; /* now aligned */ + //;VGM_LOG("BFSTM: new region start=%lx\n", region_start); + + /* get position of our block (close but smaller than sample_start) */ + sample_aligned = dsp_bytes_to_samples(region_start, vgmstream->channels); + /* and how many samples to skip until actual sample_start */ + sample_skip = (sample_start - sample_aligned); + } + + //;VGM_LOG("BFSTM: region align=%i, skip=%i, start=%i, end=%i\n", sample_aligned, sample_skip, sample_start, sample_end); + start_offset = region_start; + + if (sample_end != vgmstream->num_samples) /* not exact but... */ + vgmstream->interleave_last_block_size = 0; + + vgmstream->num_samples = sample_skip + (sample_end - sample_start); + vgmstream->loop_start_sample = sample_skip; + vgmstream->loop_end_sample = vgmstream->num_samples; +#if BFSTM_ENABLE_REGION_FORCE_LOOPS + vgmstream_force_loop(vgmstream, 1, vgmstream->loop_start_sample, vgmstream->loop_end_sample); +#endif + /* maybe loops should be disabled with some regions? */ + + /* this won't make sense after aligning, whatevs, doesn't sound too bad */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].adpcm_history1_16 = read_16bit(adpcm_offset+0x02+0x00, streamFile); + vgmstream->ch[i].adpcm_history2_16 = read_16bit(adpcm_offset+0x02+0x02, streamFile); + } + } + else { + start_offset = 0; + stream_size = get_streamfile_size(streamFile); + } + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + + return start_offset; +fail: + return 0; +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c index 601d7cbdd..4ba1acbff 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c @@ -35,7 +35,7 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { /*0x2c: unk (vol?) */ /*0x2d: unk (0x10?) */ channel_count = read_8bit(0x2e,streamFile); - block_align = read_8bit(0x2f,streamFile); + block_align = (uint8_t)read_8bit(0x2f,streamFile); if (file_size != get_streamfile_size(streamFile)) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bik.c b/Frameworks/vgmstream/vgmstream/src/meta/bik.c index 18a3996d3..a80875fb1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bik.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bik.c @@ -2,25 +2,27 @@ #include "../coding/coding.h" #include "../util.h" -static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples); +static int bink_get_info(STREAMFILE *streamFile, int target_subsong, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples); /* BINK 1/2 - RAD Game Tools movies (audio/video format) */ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0; - int total_subsongs = 0, stream_index = streamFile->stream_index; + int total_subsongs = 0, target_subsong = streamFile->stream_index; size_t stream_size; - - /* check extension, case insensitive (bika = manually demuxed audio) */ - if (!check_extensions(streamFile,"bik,bika,bik2,bik2a,bk2,bk2a")) goto fail; + /* checks */ + /* .bik/bik2/bk2: standard + * .bika = fake extension for demuxed audio */ + if (!check_extensions(streamFile,"bik,bika,bik2,bk2")) + goto fail; /* check header "BIK" (bink 1) or "KB2" (bink 2), followed by version-char (audio is the same for both) */ if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 && (read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail; /* find target stream info and samples */ - if (!bink_get_info(streamFile, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples)) + if (!bink_get_info(streamFile, target_subsong, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples)) goto fail; /* build the VGMSTREAM */ @@ -36,11 +38,9 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG { - ffmpeg_custom_config cfg = {0}; + /* target_subsong should be passed with the streamFile */ - cfg.stream_index = stream_index; - - vgmstream->codec_data = init_ffmpeg_config(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), &cfg); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile)); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; } @@ -60,12 +60,12 @@ fail: * as they are not in the main header. The header for BINK1 and 2 is the same. * (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough) */ -static int bink_get_info(STREAMFILE *streamFile, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { +static int bink_get_info(STREAMFILE *streamFile, int target_subsong, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { uint32_t *offsets = NULL; uint32_t num_frames, num_samples_b = 0; off_t cur_offset; int i, j, sample_rate, channel_count; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs; size_t stream_size = 0; size_t filesize = get_streamfile_size(streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c new file mode 100644 index 000000000..10e8f7a66 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c @@ -0,0 +1,393 @@ +#include "meta.h" +#include "../coding/coding.h" + +typedef enum { PSX, PCM16, ATRAC9 } bnk_codec; + +/* BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */ +VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { +#if 1 + VGMSTREAM * vgmstream = NULL; + off_t start_offset, stream_offset, name_offset = 0; + size_t stream_size, interleave = 0; + off_t sblk_offset, data_offset; + size_t data_size; + int channel_count = 0, loop_flag, sample_rate, version; + int loop_start = 0, loop_end = 0; + uint32_t atrac9_info = 0; + + int total_subsongs, target_subsong = streamFile->stream_index; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + bnk_codec codec; + + + /* checks */ + if (!check_extensions(streamFile, "bnk")) + goto fail; + + if (read_32bitBE(0x00,streamFile) == 0x00000003 && read_32bitBE(0x04,streamFile) == 0x00000002) { /* PS3 */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } + else if (read_32bitBE(0x00,streamFile) == 0x03000000 && read_32bitBE(0x04,streamFile) == 0x02000000) { /* Vita/PS4 */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + else { + goto fail; + } + + sblk_offset = read_32bit(0x08,streamFile); + /* 0x0c: sklb size */ + data_offset = read_32bit(0x10,streamFile); + data_size = read_32bit(0x14,streamFile); + + /* SE banks, also used for music. Most table fields seems reserved/defaults and + * don't change much between subsongs or files, so they aren't described in detail */ + + + /* SBlk part: parse header */ + if (read_32bit(sblk_offset+0x00,streamFile) != 0x6B6C4253) /* "SBlk" (sample block?) */ + goto fail; + version = read_32bit(sblk_offset+0x04,streamFile); + /* 0x08: possibly when version=0x0d, 0x03=Vita, 0x06=PS4 */ + //;VGM_LOG("BNK: sblk_offset=%lx, data_offset=%lx, version %x\n", sblk_offset, data_offset, version); + + { + int i; + off_t table1_offset, table2_offset, table3_offset, table4_offset; + size_t section_entries, material_entries, stream_entries; + size_t table1_entry_size; + off_t table1_suboffset, table2_suboffset, table3_suboffset; + off_t table2_entry_offset = 0, table3_entry_offset = 0; + int table4_entry_id = -1; + off_t table4_entries_offset, table4_names_offset; + + + switch(version) { + case 0x03: /* L@ove Once - Mermaid's Tears (PS3) */ + case 0x04: /* Test banks */ + case 0x09: /* Puyo Puyo Tetris (PS4) */ + section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */ + material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x08 */ + stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: ~0x60 */ + table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile); + table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,streamFile); + table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,streamFile); + table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,streamFile); + + table1_entry_size = 0x0c; + table1_suboffset = 0x08; + table2_suboffset = 0x00; + table3_suboffset = 0x10; + break; + + case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */ + table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,streamFile); + table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile); + table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,streamFile); + table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,streamFile); + section_entries = (uint16_t)read_16bit(sblk_offset+0x38,streamFile); /* entry size: ~0x24 */ + material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,streamFile); /* entry size: ~0x08 */ + stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,streamFile); /* entry size: ~0x90 + variable (sometimes) */ + + table1_entry_size = 0x24; + table1_suboffset = 0x0c; + table2_suboffset = 0x00; + table3_suboffset = 0x44; + break; + + default: + VGM_LOG("BNK: unknown version %x\n", version); + goto fail; + } + + //;VGM_LOG("BNK: table offsets=%lx, %lx, %lx, %lx\n", table1_offset,table2_offset,table3_offset,table4_offset); + //;VGM_LOG("BNK: table entries=%i, %i, %i\n", section_entries,material_entries,stream_entries); + + + /* table defs: + * - table1: sections, point to some materials (may be less than streams/materials) + * - table2: materials, point to all sounds or others subtypes (may be more than sounds) + * - table3: sounds, point to streams (multiple sounds can repeat stream) + * - table4: names define section names (not all sounds may have a name) + * + * approximate table parsing + * - check materials and skip non-sounds to get table3 offsets (since table3 entry size isn't always constant) + * - get stream offsets + * - find if one section points to the selected material, and get section name = stream name */ + + + /* parse materials */ + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + for (i = 0; i < material_entries; i++) { + uint32_t table2_value, table2_subinfo, table2_subtype; + + table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,streamFile); + table2_subinfo = (table2_value >> 0) & 0xFFFF; + table2_subtype = (table2_value >> 16) & 0xFFFF; + if (table2_subtype != 0x100) + continue; /* not sounds */ + + total_subsongs++; + if (total_subsongs == target_subsong) { + table2_entry_offset = (i*0x08); + table3_entry_offset = table2_subinfo; + /* continue to count all subsongs*/ + } + } + + //;VGM_LOG("BNK: subsongs %i, table2_entry=%lx, table3_entry=%lx\n", total_subsongs,table2_entry_offset,table3_entry_offset); + + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + /* this means some subsongs repeat streams, that can happen in some sfx banks, whatevs */ + if (total_subsongs != stream_entries) { + //;VGM_LOG("BNK: subsongs %i vs table3 %i don't match\n", total_subsongs, stream_entries); + /* find_dupes...? */ + } + + + /* parse sounds */ + stream_offset = read_32bit(table3_offset+table3_entry_offset+table3_suboffset+0x00,streamFile); + stream_size = read_32bit(table3_offset+table3_entry_offset+table3_suboffset+0x04,streamFile); + + /* parse names */ + switch(version) { + //case 0x03: /* different format? */ + //case 0x04: /* different format? */ + case 0x09: + case 0x0d: + /* find if this sound has an assigned name in table1 */ + for (i = 0; i < section_entries; i++) { + off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,streamFile); + + /* rarely (ex. Polara sfx) one name applies to multiple materials, + * from current entry_offset to next entry_offset (section offsets should be in order) */ + if (entry_offset <= table2_entry_offset ) { + table4_entry_id = i; + //break; + } + } + + /* table4: */ + /* 0x00: bank name (optional) */ + /* 0x08: header size */ + /* 0x0c: table4 size */ + /* variable: entries */ + /* variable: names (null terminated) */ + table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, streamFile); + table4_names_offset = table4_entries_offset + (0x10*section_entries); + //;VGM_LOG("BNK: t4_entries=%lx, t4_names=%lx\n", table4_entries_offset, table4_names_offset); + + /* get assigned name from table4 names */ + for (i = 0; i < section_entries; i++) { + int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, streamFile); + if (entry_id == table4_entry_id) { + name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, streamFile); + break; + } + } + + break; + default: + break; + } + + //;VGM_LOG("BNK: stream_offset=%lx, stream_size=%x, name_offset=%lx\n", stream_offset, stream_size, name_offset); + } + + + /* data part: parse extradata before the codec, if needed */ + { + int type, loop_length; + size_t extradata_size = 0, postdata_size = 0; + start_offset = data_offset + stream_offset; + + switch(version) { + case 0x03: + case 0x04: + sample_rate = 48000; /* seems ok */ + channel_count = 1; + + /* hack for PS3 files that use dual subsongs as stereo */ + if (total_subsongs == 2 && stream_size * 2 == data_size) { + channel_count = 2; + stream_size = stream_size*channel_count; + total_subsongs = 1; + } + + interleave = stream_size / channel_count; + //postdata_size = 0x10; /* last frame may be garbage */ + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end); + loop_flag = (loop_start > 28); /* ignore full loops since they just fadeout + repeat */ + + codec = PSX; + break; + + case 0x09: + type = read_16bit(start_offset+0x00,streamFile); + extradata_size = 0x08 + read_32bit(start_offset+0x04,streamFile); /* 0x14 for AT9 */ + + switch(type) { + case 0x02: /* ATRAC9 mono */ + case 0x05: /* ATRAC9 stereo */ + if (read_32bit(start_offset+0x08,streamFile) + 0x08 != extradata_size) /* repeat? */ + goto fail; + sample_rate = 48000; /* seems ok */ + channel_count = (type == 0x02) ? 1 : 2; + + atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,streamFile); + /* 0x10: null? */ + loop_length = read_32bit(start_offset+0x14,streamFile); + loop_start = read_32bit(start_offset+0x18,streamFile); + loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ + + codec = ATRAC9; + break; + + default: + VGM_LOG("BNK: unknown type %x\n", type); + goto fail; + } + break; + + case 0x0d: + type = read_16bit(start_offset+0x00,streamFile); + if (read_32bit(start_offset+0x04,streamFile) != 0x01) /* type? */ + goto fail; + extradata_size = 0x10 + read_32bit(start_offset+0x08,streamFile); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */ + /* 0x0c: null? */ + + switch(type) { + case 0x02: /* ATRAC9 mono */ + case 0x05: /* ATRAC9 stereo */ + if (read_32bit(start_offset+0x10,streamFile) + 0x10 != extradata_size) /* repeat? */ + goto fail; + sample_rate = 48000; /* seems ok */ + channel_count = (type == 0x02) ? 1 : 2; + + atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,streamFile); + /* 0x18: null? */ + /* 0x1c: channels? */ + /* 0x20: null? */ + + loop_length = read_32bit(start_offset+0x24,streamFile); + loop_start = read_32bit(start_offset+0x28,streamFile); + loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ + + codec = ATRAC9; + break; + + case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */ + case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */ + sample_rate = 48000; /* seems ok */ + /* 0x10: null? */ + channel_count = read_32bit(start_offset+0x14,streamFile); + interleave = 0x02; + + loop_start = read_32bit(start_offset+0x18,streamFile); + loop_length = read_32bit(start_offset+0x1c,streamFile); + loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ + + codec = PCM16; + break; + + case 0x00: /* PS-ADPCM (test banks) */ + sample_rate = 48000; /* seems ok */ + /* 0x10: null? */ + channel_count = read_32bit(start_offset+0x14,streamFile); + interleave = 0x02; + + loop_start = read_32bit(start_offset+0x18,streamFile); + loop_length = read_32bit(start_offset+0x1c,streamFile); + loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ + + codec = PSX; + break; + + default: + VGM_LOG("BNK: unknown type %x\n", type); + goto fail; + } + break; + + default: + goto fail; + } + + start_offset += extradata_size; + stream_size -= extradata_size; + stream_size -= postdata_size; + //;VGM_LOG("BNK: offset=%lx, size=%x\n", start_offset, stream_size); + } + + loop_flag = (loop_start >= 0) && (loop_end > 0); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + + vgmstream->meta_type = meta_BNK_SONY; + + switch(codec) { +#ifdef VGM_USE_ATRAC9 + case ATRAC9: { + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = atrac9_info; + //cfg.encoder_delay = 0x00; //todo + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = atrac9_bytes_to_samples(stream_size, vgmstream->codec_data); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; + } +#endif + case PCM16: + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + 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 PSX: + vgmstream->sample_rate = 48000; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; + } + + if (name_offset) + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; +fail: + close_vgmstream(vgmstream); +#endif + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c b/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c index 1dfdb1197..f8e7599de 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c @@ -2,17 +2,18 @@ #include "../coding/coding.h" #include "../util.h" -/* BNSF - Namco Bandai's Bandai Namco Sound Format/File */ +/* BNSF - Namco Bandai's Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset = 0, first_offset = 0x0C; - int loop_flag = 0, channel_count = 0; - int sample_rate, num_samples, loop_start = 0, loop_end = 0, block_size; + int loop_flag = 0, channel_count = 0, sample_rate; + int num_samples, loop_start = 0, loop_end = 0, loop_adjust, block_samples; uint32_t codec, subcodec = 0; - size_t sdat_size; + size_t bnsf_size, sdat_size, block_size; off_t loop_chunk = 0, sfmt_chunk, sdat_chunk; + /* checks */ if (!check_extensions(streamFile,"bnsf")) goto fail; if (read_32bitBE(0,streamFile) != 0x424E5346) /* "BNSF" */ @@ -20,8 +21,9 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { codec = read_32bitBE(0x08,streamFile); - /* check RIFF size (for siren22 it's full size) */ - if (read_32bitBE(0x04,streamFile) + (codec == 0x49533232 ? 0 : 8) != get_streamfile_size(streamFile)) + /* check file size (siren22 uses full size) */ + bnsf_size = read_32bitBE(0x04,streamFile); + if (bnsf_size + (codec == 0x49533232 ? 0x00 : 0x08) != get_streamfile_size(streamFile)) goto fail; if (!find_chunk_be(streamFile, 0x73666d74,first_offset,0, &sfmt_chunk,NULL)) /* "sfmt" */ @@ -30,21 +32,25 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { goto fail; if ( find_chunk_be(streamFile, 0x6C6F6F70,first_offset,0, &loop_chunk,NULL)) { /* "loop" */ loop_flag = 1; - loop_start = read_32bitBE(loop_chunk+0x00, streamFile); - loop_end = read_32bitBE(loop_chunk+0x04,streamFile); + loop_start = read_32bitBE(loop_chunk+0x00, streamFile); /* block-aligned */ + loop_end = read_32bitBE(loop_chunk+0x04,streamFile) + 1; } - subcodec = read_16bitBE(sfmt_chunk+0x00,streamFile); + subcodec = read_16bitBE(sfmt_chunk+0x00,streamFile); channel_count = read_16bitBE(sfmt_chunk+0x02,streamFile); - sample_rate = read_32bitBE(sfmt_chunk+0x04,streamFile); - num_samples = read_32bitBE(sfmt_chunk+0x08,streamFile); - /* 0x0c: some kind of sample count (skip?), can be 0 when no loops */ - block_size = read_16bitBE(sfmt_chunk+0x10,streamFile); - //block_samples = read_16bitBE(sfmt_chunk+0x12,streamFile); - /* max_samples = sdat_size/block_size*block_samples //num_samples is smaller */ + sample_rate = read_32bitBE(sfmt_chunk+0x04,streamFile); + num_samples = read_32bitBE(sfmt_chunk+0x08,streamFile); + loop_adjust = read_32bitBE(sfmt_chunk+0x0c,streamFile); /* 0 if no looping */ + block_size = read_16bitBE(sfmt_chunk+0x10,streamFile); + block_samples = read_16bitBE(sfmt_chunk+0x12,streamFile); + //max_samples = sdat_size / block_size * block_samples /* num_samples is smaller */ start_offset = sdat_chunk; + /* without adjust some files have a small pop when looping */ + if (loop_adjust >= block_samples) + goto fail; /* shouldn't happen, plus decoder can't handle it */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); @@ -52,15 +58,15 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start; + vgmstream->loop_start_sample = loop_start + loop_adjust; vgmstream->loop_end_sample = loop_end; vgmstream->meta_type = meta_BNSF; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = block_size/channel_count; - /* some IS14 voice files have 0x02 (Tales of Zestiria, The Idolm@ster 2); data looks normal and silent - * frames decode ok but rest fails, must be some subtle decoding variation (not encryption) */ + /* Late IS14 voice/ambient files use subcodec 0x02 [Tales of Zestiria (PS3/PC), The Idolm@ster 2 (PS3)]. + * Bitstream looks modified (most noticeable in silent frames), probably not encrypted, still 1ch frames */ if (subcodec != 0) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/caf.c b/Frameworks/vgmstream/vgmstream/src/meta/caf.c index 8180fe8ea..ca1b1134b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/caf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/caf.c @@ -54,9 +54,6 @@ VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile) { if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) goto fail; - - block_update_caf(start_offset,vgmstream); - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ck.c b/Frameworks/vgmstream/vgmstream/src/meta/ck.c new file mode 100644 index 000000000..d01f62a42 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ck.c @@ -0,0 +1,177 @@ +#include "meta.h" + + +/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */ +VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, codec, sample_rate; + int32_t num_samples, loop_start, loop_end; + size_t block_size; + + + /* checks */ + if (!check_extensions(streamFile, "cks")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */ + goto fail; + /* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */ + if (read_32bitLE(0x08,streamFile) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */ + goto fail; + if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */ + goto fail; + + codec = read_8bit(0x10,streamFile); + channel_count = read_8bit(0x11,streamFile); + sample_rate = (uint16_t)read_16bitLE(0x12,streamFile); + num_samples = read_32bitLE(0x14,streamFile) * read_16bitLE(0x1a,streamFile); /* number_of_blocks * samples_per_frame */ + block_size = read_16bitLE(0x18,streamFile); + /* 0x1c(2): volume */ + /* 0x1e(2): pan */ + loop_start = read_32bitLE(0x20,streamFile); + loop_end = read_32bitLE(0x24,streamFile); + loop_flag = read_16bitLE(0x28,streamFile) != 0; /* loop count (-1 = forever) */ + /* 0x2a(2): unused? */ + + start_offset = 0x2c; + + + /* 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_CKS; + + switch(codec) { + case 0x00: /* pcm16 [from tests] */ + vgmstream->coding_type = coding_PCM16LE; + break; + case 0x01: /* pcm8 [from tests] */ + vgmstream->coding_type = coding_PCM8; + break; + case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */ + vgmstream->coding_type = coding_MSADPCM_ck; + /* frame_size is always 0x18 */ + break; + default: + goto fail; + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channel_count; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */ +VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, name_offset = 0; + int loop_flag, channel_count, codec, sample_rate; + int32_t num_samples, loop_start, loop_end; + size_t block_size, stream_size; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "ckb")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */ + goto fail; + /* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */ + if (read_32bitLE(0x08,streamFile) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */ + goto fail; + if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */ + goto fail; + + /* 0x10: bank name (size 0x1c+1) */ + /* 0x30/34: reserved? */ + total_subsongs = read_32bitLE(0x38,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + /* 0x3c: total_subsongs again? (ignored) */ + /* 0x40/44: unknown (ignored) */ + + /* get subsong (stream offset isn't given so must calc manually) */ + { + int i; + off_t header_offset = 0x48; + off_t stream_offset = 0x48 + total_subsongs*0x48; + + for (i = 0; i < total_subsongs; i++) { + name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */ + codec = read_8bit(header_offset+0x20,streamFile); + channel_count = read_8bit(header_offset+0x21,streamFile); + sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,streamFile); + num_samples = read_32bitLE(header_offset+0x24,streamFile) * read_16bitLE(header_offset+0x2a,streamFile); /* number_of_blocks * samples_per_frame */ + block_size = read_16bitLE(header_offset+0x28,streamFile); + /* 0x2c(2): volume */ + /* 0x2e(2): pan */ + loop_start = read_32bitLE(header_offset+0x30,streamFile); + loop_end = read_32bitLE(header_offset+0x34,streamFile); + loop_flag = read_16bitLE(header_offset+0x38,streamFile) != 0; /* loop count (-1 = forever) */ + /* 0x3a(2): unused? */ + stream_size = read_32bitLE(header_offset+0x3c,streamFile); + /* 0x40/44(4): unused? */ + + if (target_subsong == (i+1)) + break; + header_offset += 0x48; + stream_offset += stream_size; + } + + start_offset = stream_offset; + } + + + /* 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->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + read_string(vgmstream->stream_name,0x1c+1, name_offset,streamFile); + + vgmstream->meta_type = meta_CKB; + + switch(codec) { + case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */ + vgmstream->coding_type = coding_PCM16LE; + break; + case 0x01: /* pcm8 */ + vgmstream->coding_type = coding_PCM8; + break; + case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */ + vgmstream->coding_type = coding_MSADPCM_ck; + /* frame_size is always 0x18 */ + break; + default: + goto fail; + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channel_count; + + 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/csmp.c b/Frameworks/vgmstream/vgmstream/src/meta/csmp.c new file mode 100644 index 000000000..30915b290 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/csmp.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* CSMP - Retro Studios sample [Metroid Prime 3 (Wii), Donkey Kong Country Returns (Wii)] */ +VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, first_offset = 0x08, chunk_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "csmp")) + goto fail; + if (read_32bitBE(0x00, streamFile) != 0x43534D50) /* "CSMP" */ + goto fail; + if (read_32bitBE(0x04, streamFile) != 1) /* version? */ + goto fail; + + if (!find_chunk(streamFile, 0x44415441,first_offset,0, &chunk_offset,NULL, 1, 0)) /*"DATA"*/ + goto fail; + + /* contains standard DSP header, but somehow some validations (start/loop ps) + * don't seem to work, so no point to handle as standard DSP */ + + channel_count = 1; + loop_flag = read_16bitBE(chunk_offset+0x0c,streamFile); + start_offset = chunk_offset + 0x60; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_CSMP; + vgmstream->sample_rate = read_32bitBE(chunk_offset+0x08,streamFile); + vgmstream->num_samples = read_32bitBE(chunk_offset+0x00,streamFile); + vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bitBE(chunk_offset+0x10,streamFile)); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bitBE(chunk_offset+0x14,streamFile))+1; + 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; + dsp_read_coefs_be(vgmstream, streamFile, chunk_offset+0x1c, 0x00); + dsp_read_hist_be(vgmstream, streamFile, chunk_offset+0x40, 0x00); + + 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/dc_dcsw_dcs.c b/Frameworks/vgmstream/vgmstream/src/meta/dc_dcsw_dcs.c index d1710560a..d8e98de67 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dc_dcsw_dcs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dc_dcsw_dcs.c @@ -98,7 +98,7 @@ VGMSTREAM * init_vgmstream_dc_dcsw_dcs(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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= diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dec.c b/Frameworks/vgmstream/vgmstream/src/meta/dec.c index 43a3fe18e..69247e150 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dec.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dec.c @@ -13,14 +13,16 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) { int loop_flag, channel_count, sample_rate, loop_start = 0, loop_end = 0; - /* check extension (.dec: main, .de2: Gurumin) */ + /* checks + * .dec: main, + * .de2: Gurumin (PC) */ if ( !check_extensions(streamFile,"dec,de2") ) goto fail; /* Gurumin has extra data, maybe related to rhythm (~0x50000) */ if (check_extensions(streamFile,"de2")) { /* still not sure what this is for, but consistently 0xb */ - if (read_32bitLE(0x04,streamFile) != 0xb) goto fail; + if (read_32bitLE(0x04,streamFile) != 0x0b) goto fail; /* legitimate! really! */ riff_off = 0x10 + (read_32bitLE(0x0c,streamFile) ^ read_32bitLE(0x04,streamFile)); @@ -67,6 +69,7 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_DEC; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = pcm_size / 2 / channel_count; vgmstream->loop_start_sample = loop_start; @@ -76,14 +79,8 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x800; vgmstream->layout_type = layout_blocked_dec; - vgmstream->meta_type = meta_DEC; - - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - - block_update_dec(start_offset, vgmstream); - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/derf.c b/Frameworks/vgmstream/vgmstream/src/meta/derf.c new file mode 100644 index 000000000..3312f008f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/derf.c @@ -0,0 +1,48 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* DERF - from Stupid Invaders (PC) */ +VGMSTREAM * init_vgmstream_derf(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + size_t data_size; + + + /* checks */ + if (!check_extensions(streamFile, "adp")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x44455246) /* "DERF" */ + goto fail; + + channel_count = read_32bitLE(0x04,streamFile); + if (channel_count > 2) goto fail; + /* movie DERF also exist with slightly different header */ + + start_offset = 0x0c; + data_size = read_32bitLE(0x08,streamFile); + if (data_size + start_offset != get_streamfile_size(streamFile)) + goto fail; + + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = 22050; + vgmstream->meta_type = meta_DERF; + vgmstream->coding_type = coding_DERF; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + vgmstream->num_samples = data_size / channel_count; /* bytes-to-samples */ + + 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/dsp_bdsp.c b/Frameworks/vgmstream/vgmstream/src/meta/dsp_bdsp.c index cd39bf622..a72dd7820 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/dsp_bdsp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/dsp_bdsp.c @@ -42,7 +42,7 @@ VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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= diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dsp_sth_str.c b/Frameworks/vgmstream/vgmstream/src/meta/dsp_sth_str.c deleted file mode 100644 index 4db018c13..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/dsp_sth_str.c +++ /dev/null @@ -1,320 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* - STH+STR - found in SpongebobSquarepants: Creature From The Krusty Krab (NGC) -*/ - -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str1(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileSTR = NULL; - char filename[PATH_LIMIT]; - char filenameSTR[PATH_LIMIT]; - int i, j; - int channel_count; - int loop_flag; - off_t coef_table[8] = {0x12C,0x18C,0x1EC,0x24C,0x2AC,0x30C,0x36C,0x3CC}; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sth",filename_extension(filename))) goto fail; - - strcpy(filenameSTR,filename); - strcpy(filenameSTR+strlen(filenameSTR)-3,"str"); - streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileSTR) goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x0) - { - goto fail; - } - - if (read_32bitBE(0x4,streamFile) != 0x800) - { - goto fail; - } - - /* Not really channel_count, just 'included tracks * channels per track */ - loop_flag = (read_32bitBE(0xD8,streamFile) != 0xFFFFFFFF); - channel_count = (read_32bitBE(0x70,streamFile)) * (read_32bitBE(0x88,streamFile)); - - if (channel_count > 8) - { - goto fail; - } - - /* 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(0x24,streamFile); - vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14; - vgmstream->coding_type = coding_NGC_DSP; - - if(loop_flag) - { - vgmstream->loop_start_sample = read_32bitBE(0xD8,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0xDC,streamFile); - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else - { - vgmstream->layout_type = layout_interleave; - if (channel_count == 2) - { - vgmstream->interleave_block_size=0x10000; - } - else - { - vgmstream->interleave_block_size=0x8000; - } - } - - vgmstream->meta_type = meta_NGC_DSP_STH_STR; - - /* open the file for reading */ - for (i=0;ich[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000); - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; - } - - // COEFFS - 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); - } - } - - close_streamfile(streamFileSTR); streamFileSTR=NULL; - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (streamFileSTR) close_streamfile(streamFileSTR); - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -/* - STH+STR - found in Taz Wanted (NGC), Cubix Robots for Everyone: Showdown (NGC) -*/ - -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str2(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileSTR = NULL; - char filename[PATH_LIMIT]; - char filenameSTR[PATH_LIMIT]; - int i, j; - int channel_count; - int loop_flag; - off_t coef_table[8] = {0xDC,0x13C,0x19C,0x1FC,0x25C,0x2BC,0x31C,0x37C}; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sth",filename_extension(filename))) goto fail; - - strcpy(filenameSTR,filename); - strcpy(filenameSTR+strlen(filenameSTR)-3,"str"); - streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileSTR) goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x0) - { - goto fail; - } - - if (read_32bitBE(0x4,streamFile) != 0x900) - { - goto fail; - } - - /* Not really channel_count, just 'included tracks * channels per track */ - loop_flag = (read_32bitBE(0xB8,streamFile) != 0xFFFFFFFF); - channel_count = read_32bitBE(0x50,streamFile)*2; - - if (channel_count > 8) - { - goto fail; - } - - /* 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(0x24,streamFile); - vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14; - vgmstream->coding_type = coding_NGC_DSP; - - if(loop_flag) - { - vgmstream->loop_start_sample = read_32bitBE(0xB8,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0xBC,streamFile); - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else - { - vgmstream->layout_type = layout_interleave; - if (channel_count == 2) - { - vgmstream->interleave_block_size=0x10000; - } - else - { - vgmstream->interleave_block_size=0x8000; - } - } - - vgmstream->meta_type = meta_NGC_DSP_STH_STR; - - /* open the file for reading */ - for (i=0;ich[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000); - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; - } - - // COEFFS - 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); - } - } - - close_streamfile(streamFileSTR); streamFileSTR=NULL; - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (streamFileSTR) close_streamfile(streamFileSTR); - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -/* - STH+STR - found in Tak and the Guardians of Gross (WII) -*/ - -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str3(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileSTR = NULL; - char filename[PATH_LIMIT]; - char filenameSTR[PATH_LIMIT]; - int i, j; - int channel_count; - int loop_flag; - off_t coef_table[8] = {read_32bitBE(0x7C,streamFile),read_32bitBE(0x80,streamFile),read_32bitBE(0x84,streamFile),read_32bitBE(0x88,streamFile),read_32bitBE(0x8C,streamFile),read_32bitBE(0x90,streamFile),read_32bitBE(0x94,streamFile),read_32bitBE(0x98,streamFile)}; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sth",filename_extension(filename))) goto fail; - - strcpy(filenameSTR,filename); - strcpy(filenameSTR+strlen(filenameSTR)-3,"str"); - streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileSTR) goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x0) - { - goto fail; - } - - if ((read_32bitBE(0x4,streamFile) != 0x700) && - (read_32bitBE(0x4,streamFile) != 0x800)) - { - goto fail; - } - - /* Not really channel_count, just 'included tracks * channels per track */ - loop_flag = 0; - channel_count = read_32bitBE(0x70,streamFile); - - if (channel_count > 8) - { - goto fail; - } - - /* 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(0x38,streamFile); - vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14; - vgmstream->coding_type = coding_NGC_DSP; - - if(loop_flag) - { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = 0; - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else - { - vgmstream->layout_type = layout_interleave; - if (channel_count == 2 || channel_count == 4) - { - vgmstream->interleave_block_size=0x8000; - } - else - { - vgmstream->interleave_block_size=0x4000; - } - } - - vgmstream->meta_type = meta_NGC_DSP_STH_STR; - - /* open the file for reading */ - for (i=0;ich[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000); - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; - } - - // COEFFS - 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); - } - } - - close_streamfile(streamFileSTR); streamFileSTR=NULL; - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (streamFileSTR) close_streamfile(streamFileSTR); - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index 6c5ace308..b7b4881de 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -21,7 +21,7 @@ typedef struct { int big_endian; int loop_flag; int is_sead; - int codec_version; + int codec_config; } ea_header; static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); @@ -37,7 +37,8 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { /* checks */ /* .asf/as4: common, cnk: some PS games, .sng: fake for plugins (to mimic EA SCHl's common extension) */ - if (!check_extensions(streamFile,"asf,as4,cnk,sng")) + /* .uv, .tgq: some SAT games */ + if (!check_extensions(streamFile,"asf,as4,cnk,sng,uv,tgq")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */ @@ -84,7 +85,7 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { case EA_CODEC_IMA: /* Need for Speed II (PC) */ if (ea.bits && ea.bits!=2) goto fail; /* only in EACS */ vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ - vgmstream->codec_version = ea.codec_version; + vgmstream->codec_config = ea.codec_config; break; case EA_CODEC_PSX: /* Need for Speed (PS) */ @@ -96,12 +97,9 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { goto fail; } - /* open files; channel offsets are updated below */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - block_update_ea_1snh(start_offset,vgmstream); - return vgmstream; fail: @@ -126,7 +124,7 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { VGM_ASSERT(ea->type != 0, "EA EACS: unknown type\n"); /* block type? */ if (ea->codec == EA_CODEC_IMA) - ea->codec_version = get_ea_1snh_ima_version(streamFile, 0x00, ea); + ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea); } else if (ea->is_sead) { /* alt subheader (found in some PC videos) */ @@ -135,7 +133,7 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { ea->codec = read_32bit(offset+0x08, streamFile); if (ea->codec == EA_CODEC_IMA) - ea->codec_version = get_ea_1snh_ima_version(streamFile, 0x00, ea); + ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea); set_ea_1snh_num_samples(streamFile, 0x00, ea); if (ea->loop_start_offset) /* offset found, now find actual start sample */ @@ -183,7 +181,7 @@ static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ block_header = 0x08; } - else if (id == 0x00000000) { + else if (id == 0x00000000 || id == 0xFFFFFFFF || id == 0x31534E65) { /* EOF or "1SNe" */ break; } else if (id == 0x31534E6C) { /* "1SNl" loop point found */ @@ -197,7 +195,7 @@ static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, block_samples = ps_bytes_to_samples(block_size - block_header, ea->channels); break; case EA_CODEC_IMA: - if (ea->codec_version == 1) + if (ea->codec_config == 1) block_samples = read_32bit(block_offset + block_header, streamFile); else block_samples = ima_bytes_to_samples(block_size - block_header, ea->channels); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 99ff7ae49..e56c55056 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -5,7 +5,38 @@ /* EAAudioCore formats, EA's current audio middleware */ +#define EAAC_VERSION_V0 0x00 /* SNR/SNS */ +#define EAAC_VERSION_V1 0x01 /* SPS */ + +#define EAAC_CODEC_NONE 0x00 /* internal 'codec not set' */ +#define EAAC_CODEC_RESERVED 0x01 /* not used/reserved? /MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */ +#define EAAC_CODEC_PCM 0x02 +#define EAAC_CODEC_EAXMA 0x03 +#define EAAC_CODEC_XAS 0x04 +#define EAAC_CODEC_EALAYER3_V1 0x05 +#define EAAC_CODEC_EALAYER3_V2_PCM 0x06 +#define EAAC_CODEC_EALAYER3_V2_SPIKE 0x07 +#define EAAC_CODEC_DSP 0x08 +#define EAAC_CODEC_EASPEEX 0x09 +#define EAAC_CODEC_EATRAX 0x0a +#define EAAC_CODEC_EAOPUS 0x0c + +#define EAAC_FLAG_NONE 0x00 +#define EAAC_FLAG_LOOPED 0x02 +#define EAAC_FLAG_STREAMED 0x04 + +#define EAAC_BLOCKID0_DATA 0x00 +#define EAAC_BLOCKID0_END 0x80 /* maybe meant to be a bitflag? */ + +#define EAAC_BLOCKID1_HEADER 0x48 /* 'H' */ +#define EAAC_BLOCKID1_DATA 0x44 /* 'D' */ +#define EAAC_BLOCKID1_END 0x45 /* 'E' */ + static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type); +static size_t get_snr_size(STREAMFILE *streamFile, off_t offset); +static VGMSTREAM *parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16_t target_index, off_t ast_offset); + + /* .SNR+SNS - from EA latest games (~2008-2013), v0 header */ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) { @@ -18,14 +49,7 @@ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) { /* SNR headers normally need an external SNS file, but some have data [Burnout Paradise, NFL2013 (iOS)] */ if (get_streamfile_size(streamFile) > 0x10) { - off_t start_offset; - - switch(read_8bit(0x04,streamFile)) { /* flags */ - case 0x60: start_offset = 0x10; break; - case 0x20: start_offset = 0x0c; break; - default: start_offset = 0x08; break; - } - + off_t start_offset = get_snr_size(streamFile, 0x00); vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS); if (!vgmstream) goto fail; } @@ -55,7 +79,7 @@ VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile) { goto fail; /* SPS block start: 0x00(1): block flag (header=0x48); 0x01(3): block size (usually 0x0c-0x14) */ - if (read_8bit(0x00, streamFile) != 0x48) + if (read_8bit(0x00, streamFile) != EAAC_BLOCKID1_HEADER) goto fail; start_offset = read_32bitBE(0x00, streamFile) & 0x00FFFFFF; @@ -109,7 +133,7 @@ fail: } /* .SPS - from Frostbite engine games, v1 header */ -VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile) { //todo remove in the future, use better extractors VGMSTREAM * vgmstream = NULL; off_t start_offset = 0, header_offset = 0, sps_offset, max_offset; @@ -155,6 +179,313 @@ fail: return NULL; } +/* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */ +VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { + int is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; + off_t bnk_offset, header_table_offset, base_offset, unk_struct_offset, table_offset, snd_entry_offset, ast_offset; + off_t num_entries_off, base_offset_off, entries_off, sound_table_offset_off; + uint32_t i, j, k, num_sounds, total_sound_tables; + uint16_t num_tables, bnk_index, bnk_target_index; + uint8_t num_entries, extra_entries; + off_t sound_table_offsets[0x2000]; + VGMSTREAM *vgmstream; + int32_t (*read_32bit)(off_t,STREAMFILE*); + int16_t (*read_16bit)(off_t,STREAMFILE*); + + /* check extension */ + if (!check_extensions(streamFile, "abk")) + goto fail; + + if (read_32bitBE(0x00, streamFile) != 0x41424B43) /* "ABKC" */ + goto fail; + + /* use table offset to check endianness */ + if (guess_endianness32bit(0x1C,streamFile)) { + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0) + goto fail; + + num_tables = read_16bit(0x0A, streamFile); + header_table_offset = read_32bit(0x1C, streamFile); + bnk_offset = read_32bit(0x20, streamFile); + total_sound_tables = 0; + bnk_target_index = 0xFFFF; + ast_offset = 0; + + if (!bnk_offset || read_32bitBE(bnk_offset, streamFile) != 0x53313041) /* "S10A" */ + goto fail; + + /* set up some common values */ + if (header_table_offset == 0x5C) { + /* the usual variant */ + num_entries_off = 0x24; + base_offset_off = 0x2C; + entries_off = 0x3C; + sound_table_offset_off = 0x04; + } + else if (header_table_offset == 0x78) { + /* FIFA 08 has a bunch of extra zeroes all over the place, don't know what's up with that */ + num_entries_off = 0x40; + base_offset_off = 0x54; + entries_off = 0x68; + sound_table_offset_off = 0x0C; + } + else { + goto fail; + } + + for (i = 0; i < num_tables; i++) { + num_entries = read_8bit(header_table_offset + num_entries_off, streamFile); + extra_entries = read_8bit(header_table_offset + num_entries_off + 0x03, streamFile); + base_offset = read_32bit(header_table_offset + base_offset_off, streamFile); + if (num_entries == 0xff) goto fail; /* EOF read */ + + for (j = 0; j < num_entries; j++) { + unk_struct_offset = read_32bit(header_table_offset + entries_off + 0x04 * j, streamFile); + table_offset = read_32bit(base_offset + unk_struct_offset + sound_table_offset_off, streamFile); + + /* For some reason, there are duplicate entries pointing at the same sound tables */ + is_dupe = 0; + for (k = 0; k < total_sound_tables; k++) + { + if (table_offset==sound_table_offsets[k]) + { + is_dupe = 1; + break; + } + } + + if (is_dupe) + continue; + + sound_table_offsets[total_sound_tables++] = table_offset; + num_sounds = read_32bit(table_offset, streamFile); + if (num_sounds == 0xffffffff) goto fail; /* EOF read */ + + for (k = 0; k < num_sounds; k++) { + /* 0x00: sound index */ + /* 0x02: ??? */ + /* 0x04: ??? */ + /* 0x08: streamed data offset */ + snd_entry_offset = table_offset + 0x04 + 0x0C * k; + bnk_index = read_16bit(snd_entry_offset + 0x00, streamFile); + + /* some of these are dummies */ + if (bnk_index == 0xFFFF) + continue; + + total_sounds++; + if (target_stream == total_sounds) { + bnk_target_index = bnk_index; + ast_offset = read_32bit(snd_entry_offset + 0x08, streamFile); + } + } + } + + header_table_offset += entries_off + num_entries * 0x04 + extra_entries * 0x04; + } + + if (bnk_target_index == 0xFFFF || ast_offset == 0) + goto fail; + + vgmstream = parse_s10a_header(streamFile, bnk_offset, bnk_target_index, ast_offset); + if (!vgmstream) + goto fail; + + vgmstream->num_streams = total_sounds; + return vgmstream; + +fail: + return NULL; +} + +/* EA S10A header - seen inside new ABK files. Putting it here in case it's encountered stand-alone. */ +static VGMSTREAM * parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16_t target_index, off_t ast_offset) { + uint32_t num_sounds; + off_t snr_offset, sns_offset; + STREAMFILE *astFile = NULL; + VGMSTREAM *vgmstream; + + /* header is always big endian */ + /* 0x00: header magic */ + /* 0x04: zero */ + /* 0x08: number of files */ + /* 0x0C: offsets table */ + if (read_32bitBE(offset + 0x00, streamFile) != 0x53313041) /* "S10A" */ + goto fail; + + num_sounds = read_32bitBE(offset + 0x08, streamFile); + if (num_sounds == 0 || target_index > num_sounds) + goto fail; + + snr_offset = offset + read_32bitBE(offset + 0x0C + 0x04 * target_index, streamFile); + + if (ast_offset == 0xFFFFFFFF) { + /* RAM asset */ + sns_offset = snr_offset + get_snr_size(streamFile, snr_offset); + //;VGM_LOG("EA S10A: RAM at sns=%lx, sns=%lx\n", snr_offset, sns_offset); + vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, snr_offset, sns_offset, meta_EA_SNR_SNS); + if (!vgmstream) + goto fail; + } + else { + /* streamed asset */ + astFile = open_streamfile_by_ext(streamFile, "ast"); + if (!astFile) + goto fail; + + if (read_32bitBE(0x00, astFile) != 0x53313053) /* "S10S" */ + goto fail; + + sns_offset = ast_offset; + //;VGM_LOG("EA S10A: stream at sns=%lx, sns=%lx\n", snr_offset, sns_offset); + vgmstream = init_vgmstream_eaaudiocore_header(streamFile, astFile, snr_offset, sns_offset, meta_EA_SNR_SNS); + if (!vgmstream) + goto fail; + + close_streamfile(astFile); + } + + return vgmstream; + +fail: + close_streamfile(astFile); + return NULL; +} + +/* EA HDR/STH/DAT - seen in early 7th-gen games, used for storing speech */ +VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { + int target_stream = streamFile->stream_index; + uint32_t i; + uint8_t userdata_size, total_sounds, block_id; + off_t snr_offset, sns_offset; + size_t file_size, block_size; + STREAMFILE *datFile = NULL, *sthFile = NULL; + VGMSTREAM *vgmstream; + + /* 0x00: ID */ + /* 0x02: userdata size */ + /* 0x03: number of files */ + /* 0x04: sub-ID (used for different police voices in NFS games) */ + /* 0x08: alt number of files? */ + /* 0x09: zero */ + /* 0x0A: ??? */ + /* 0x0C: zero */ + /* 0x10: table start */ + + sthFile = open_streamfile_by_ext(streamFile, "sth"); + if (!sthFile) + goto fail; + + datFile = open_streamfile_by_ext(streamFile, "dat"); + if (!datFile) + goto fail; + + /* STH always starts with the first offset of zero */ + sns_offset = read_32bitLE(0x00, sthFile); + if (sns_offset != 0) + goto fail; + + /* check if DAT starts with a correct SNS block */ + block_id = read_8bit(0x00, datFile); + if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END) + goto fail; + + userdata_size = read_8bit(0x02, streamFile); + total_sounds = read_8bit(0x03, streamFile); + if (read_8bit(0x08, streamFile) > total_sounds) + goto fail; + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) + goto fail; + + /* offsets in HDR are always big endian */ + //snr_offset = (off_t)read_16bitBE(0x10 + (0x02+userdata_size) * (target_stream-1), streamFile) + 0x04; + //sns_offset = read_32bit(snr_offset, sthFile); + + /* we can't reliably detect byte endianness so we're going to find the sound the hacky way */ + /* go through blocks until we reach the goal sound */ + file_size = get_streamfile_size(datFile); + snr_offset = 0; + sns_offset = 0; + + for (i = 0; i < total_sounds; i++) { + snr_offset = (off_t)read_16bitBE(0x10 + (0x02+userdata_size) * i, streamFile) + 0x04; + + if (i == target_stream - 1) + break; + + while (1) { + if (sns_offset >= file_size) + goto fail; + + block_id = read_8bit(sns_offset, datFile); + block_size = read_32bitBE(sns_offset, datFile) & 0x00FFFFFF; + if (block_size == 0) + goto fail; + + if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END) + goto fail; + + sns_offset += block_size; + + if (block_id == EAAC_BLOCKID0_END) + break; + } + } + + block_id = read_8bit(sns_offset, datFile); + if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END) + goto fail; + + vgmstream = init_vgmstream_eaaudiocore_header(sthFile, datFile, snr_offset, sns_offset, meta_EA_SNR_SNS); + if (!vgmstream) + goto fail; + + vgmstream->num_streams = total_sounds; + close_streamfile(sthFile); + close_streamfile(datFile); + return vgmstream; + +fail: + close_streamfile(sthFile); + close_streamfile(datFile); + return NULL; +} + +/* ************************************************************************* */ + +typedef struct { + int version; + int codec; + int channel_config; + int sample_rate; + int flags; + + int streamed; + int channels; + + int num_samples; + int loop_start; + int loop_end; + int loop_flag; + + off_t stream_offset; + off_t loop_offset; +} eaac_header; + +static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac); +static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamFile, eaac_header *eaac); + + /* 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), * or together in pseudoformats (.SNU, .SBR+.SBS banks, .AEMS, .MUS, etc). @@ -162,177 +493,189 @@ fail: 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; + uint32_t header1, header2; + eaac_header eaac = {0}; + /* EA SNR/SPH header */ - 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) */ + header1 = (uint32_t)read_32bitBE(header_offset + 0x00, streamHead); + header2 = (uint32_t)read_32bitBE(header_offset + 0x04, streamHead); + eaac.version = (header1 >> 28) & 0x0F; /* 4 bits */ + eaac.codec = (header1 >> 24) & 0x0F; /* 4 bits */ + eaac.channel_config = (header1 >> 18) & 0x3F; /* 6 bits */ + eaac.sample_rate = (header1 & 0x03FFFF); /* 18 bits (some Dead Space 2 (PC) do use 96000) */ + eaac.flags = (header2 >> 28) & 0x0F; /* 4 bits *//* TODO: maybe even 3 bits and not 4? */ + eaac.num_samples = (header2 & 0x0FFFFFFF); /* 28 bits */ + /* rest is optional, depends on used flags and codec (handled below) */ + eaac.stream_offset = start_offset; - /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than the block flags used) */ - if (version != 0 && version != 1) { - VGM_LOG("EA SNS/SPS: unknown version\n"); + /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than block flags) */ + if (eaac.version != EAAC_VERSION_V0 && eaac.version != EAAC_VERSION_V1) { + VGM_LOG("EA EAAC: unknown version\n"); goto fail; } - /* 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); + /* catch unknown/garbage values just in case */ + if (eaac.flags != EAAC_FLAG_NONE && !(eaac.flags & (EAAC_FLAG_LOOPED | EAAC_FLAG_STREAMED))) { + VGM_LOG("EA EAAC: unknown flags 0x%02x\n", eaac.flags); goto fail; } - /* seen in sfx and Dead Space ambient tracks */ - if (flags & 0x20) { - loop_flag = 1; - loop_start = 0; - loop_end = num_samples; - } + /* Non-streamed sounds are stored as a single block (may not set block end flags) */ + eaac.streamed = (eaac.flags & EAAC_FLAG_STREAMED) != 0; - /* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */ - //channel_count = ((channel_config >> 2) & 0xf) + 1; /* likely, but better fail with unknown values */ - switch(channel_config) { - case 0x00: channel_count = 1; break; - case 0x04: channel_count = 2; break; - case 0x0c: channel_count = 4; break; - case 0x14: channel_count = 6; break; - case 0x1c: channel_count = 8; break; - default: - VGM_LOG("EA SNS/SPS: unknown channel config 0x%02x\n", channel_config); + /* get loops (fairly involved due to the multiple layouts and mutant streamfiles) + * full loops aren't too uncommon [Dead Space (PC) stream sfx/ambiance, FIFA 98 (PS3) RAM sfx], + * while actual looping is very rare [Need for Speed: World (PC)] */ + if (eaac.flags & EAAC_FLAG_LOOPED) { + eaac.loop_flag = 1; + eaac.loop_start = read_32bitBE(header_offset+0x08, streamHead); + eaac.loop_end = eaac.num_samples; + + /* RAM assets only have one block, even if they (rarely) set loop_start > 0 */ + if (eaac.streamed) + eaac.loop_offset = read_32bitBE(header_offset+0x0c, streamHead); + else + eaac.loop_offset = eaac.stream_offset; /* implicit */ + + //todo EATrax has extra values in header, which would coexist with loop values + if (eaac.codec == EAAC_CODEC_EATRAX) { + VGM_LOG("EA EAAC: unknown loop header for EATrax\n"); goto fail; + } + + //todo need more cases to test how layout/streamfiles react + if (eaac.loop_start > 0 && !(eaac.codec == EAAC_CODEC_EALAYER3_V1 || + eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM || eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE)) { + VGM_LOG("EA EAAC: unknown actual looping for non-EALayer3\n"); + goto fail; + } + } + + /* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1, from debug strings */ + switch(eaac.channel_config) { + case 0x00: eaac.channels = 1; break; + case 0x01: eaac.channels = 2; break; + case 0x03: eaac.channels = 4; break; + case 0x05: eaac.channels = 6; break; + case 0x07: eaac.channels = 8; break; + default: + VGM_LOG("EA EAAC: unknown channel config 0x%02x\n", eaac.channel_config); + goto fail; /* fail with unknown values just in case */ } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(eaac.channels,eaac.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->sample_rate = eaac.sample_rate; + vgmstream->num_samples = eaac.num_samples; + vgmstream->loop_start_sample = eaac.loop_start; + vgmstream->loop_end_sample = eaac.loop_end; vgmstream->meta_type = meta_type; /* EA decoder list and known internal FourCCs */ - switch(codec) { + switch(eaac.codec) { - case 0x02: /* "P6B0": PCM16BE [NBA Jam (Wii)] */ + case EAAC_CODEC_PCM: /* "P6B0": PCM16BE [NBA Jam (Wii)] */ vgmstream->coding_type = coding_PCM16_int; vgmstream->codec_endian = 1; vgmstream->layout_type = layout_blocked_ea_sns; break; #ifdef VGM_USE_FFMPEG - case 0x03: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */ - uint8_t buf[0x100]; - int bytes, block_size, block_count; - size_t stream_size, virtual_size; - ffmpeg_custom_config cfg = {0}; - - stream_size = get_streamfile_size(streamData) - start_offset; - virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, start_offset,stream_size, streamData); - block_size = 0x10000; /* todo unused and not correctly done by the parser */ - block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0); - - bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, virtual_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; - - cfg.type = FFMPEG_EA_XMA; - cfg.virtual_size = virtual_size; - cfg.channels = vgmstream->channels; - - vgmstream->codec_data = init_ffmpeg_config(streamData, buf,bytes, start_offset,stream_size, &cfg); - if (!vgmstream->codec_data) goto fail; - + case EAAC_CODEC_EAXMA: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */ + vgmstream->layout_data = build_layered_eaaudiocore_eaxma(streamData, &eaac); + if (!vgmstream->layout_data) goto fail; vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; + vgmstream->layout_type = layout_layered; break; } #endif - case 0x04: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */ + case EAAC_CODEC_XAS: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */ vgmstream->coding_type = coding_EA_XAS; vgmstream->layout_type = layout_blocked_ea_sns; break; #ifdef VGM_USE_MPEG - case 0x05: /* "EL31": EALayer3 v1 [Need for Speed: Hot Pursuit (PS3)] */ - case 0x06: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */ - case 0x07: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */ + case EAAC_CODEC_EALAYER3_V1: /* "EL31": EALayer3 v1 [Need for Speed: Hot Pursuit (PS3)] */ + case EAAC_CODEC_EALAYER3_V2_PCM: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */ + case EAAC_CODEC_EALAYER3_V2_SPIKE: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */ mpeg_custom_config cfg = {0}; - mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S); + mpeg_custom_t type = (eaac.codec == 0x05 ? MPEG_EAL31b : (eaac.codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S); - /* 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; + /* EALayer3 needs custom IO that removes blocks on reads to fix some edge cases in L32P + * and to properly apply discard modes (see ealayer3 decoder) + * (otherwise, and after removing discard code, it'd work with layout_blocked_ea_sns) */ 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; + if (eaac.streamed && eaac.loop_start > 0) { /* special (if hacky) loop handling, see comments */ + segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac); + if (!data) goto fail; + vgmstream->layout_data = data; + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + } + else { + temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset); + if (!temp_streamFile) goto fail; + + vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + } break; } #endif - case 0x08: /* "Gca0"?: DSP [Need for Speed: Nitro sfx (Wii)] */ + case EAAC_CODEC_DSP: /* "Gca0"?: DSP [Need for Speed: Nitro (Wii) sfx] */ vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_blocked_ea_sns; /* DSP coefs are read in the blocks */ break; #ifdef VGM_USE_ATRAC9 - case 0x0a: { /* EATrax */ + case EAAC_CODEC_EATRAX: { /* EATrax (unknown FourCC) [Need for Speed: Most Wanted (Vita)] */ atrac9_config cfg = {0}; - size_t total_size; - cfg.channels = vgmstream->channels; + /* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */ + + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + + cfg.channels = eaac.channels; cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead); /* 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 */ + /* actual data size without blocks, LE b/c why make sense (but don't use it in case of truncated files) */ + //total_size = read_32bitLE(header_offset + 0x0c,streamHead); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; 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); + temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset); if (!temp_streamFile) goto fail; - start_offset = 0x00; /* must point to the custom streamfile's beginning */ break; } #endif - case 0x00: /* "NONE" (internal 'codec not set' flag) */ - case 0x01: /* not used/reserved? /MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */ - case 0x09: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ - case 0x0b: /* ? */ - case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */ - case 0x0d: /* ? */ - case 0x0e: /* ? */ - case 0x0f: /* ? */ + case EAAC_CODEC_EASPEEX: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ + /* TODO */ + case EAAC_CODEC_EAOPUS: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */ + /* TODO */ default: - VGM_LOG("EA SNS/SPS: unknown codec 0x%02x\n", codec); + VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec); goto fail; } 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; @@ -341,3 +684,144 @@ fail: close_vgmstream(vgmstream); return NULL; } + +static size_t get_snr_size(STREAMFILE *streamFile, off_t offset) { + switch (read_8bit(offset + 0x04, streamFile) >> 4 & 0x0F) { /* flags */ + case EAAC_FLAG_LOOPED | EAAC_FLAG_STREAMED: return 0x10; + case EAAC_FLAG_LOOPED: return 0x0C; + default: return 0x08; + } +} + + +/* Actual looping uses 2 block sections, separated by a block end flag *and* padded. + * + * We use the segmented layout, since the eaac_streamfile doesn't handle padding properly ATM + * (getting EALayer3 frame sizes + skip sizes can be fairly involved), plus seems likely + * that after a block end the decoder needs to be reset (not possible from a streamfile). + * + * Or could fix the blocked_layout+L32P bug, though that involves a lot of rewrites. + * So this is the simplest, surest way ATM (if very ugly). */ +// todo consider better ways to handle this once more looped files for other codecs are found +static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac) { + segmented_layout_data *data = NULL; + STREAMFILE* temp_streamFile[2] = {0}; + off_t offsets[2] = { eaac->stream_offset, eaac->loop_offset }; + int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start}; + int segment_count = 2; /* intro/loop */ + int i; + + + /* init layout */ + data = init_layout_segmented(segment_count); + if (!data) goto fail; + + for (i = 0; i < segment_count; i++) { + temp_streamFile[i] = setup_eaac_streamfile(streamData, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]); + if (!temp_streamFile[i]) goto fail; + + data->segments[i] = allocate_vgmstream(eaac->channels, 0); + if (!data->segments[i]) goto fail; + data->segments[i]->sample_rate = eaac->sample_rate; + data->segments[i]->num_samples = num_samples[i]; + //data->segments[i]->meta_type = eaac->meta_type; /* bleh */ + + switch(eaac->codec) { +#ifdef VGM_USE_MPEG + case EAAC_CODEC_EALAYER3_V1: + case EAAC_CODEC_EALAYER3_V2_PCM: + case EAAC_CODEC_EALAYER3_V2_SPIKE: { + mpeg_custom_config cfg = {0}; + mpeg_custom_t type = (eaac->codec == 0x05 ? MPEG_EAL31b : (eaac->codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S); + + data->segments[i]->codec_data = init_mpeg_custom(temp_streamFile[i], 0x00, &data->segments[i]->coding_type, eaac->channels, type, &cfg); + if (!data->segments[i]->codec_data) goto fail; + data->segments[i]->layout_type = layout_none; + break; + } +#endif + default: + goto fail; + } + + if (!vgmstream_open_stream(data->segments[i],temp_streamFile[i],0x00)) + goto fail; + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + return data; + +fail: + for (i = 0; i < segment_count; i++) + close_streamfile(temp_streamFile[i]); + free_layout_segmented(data); + return NULL; +} + +static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamData, eaac_header *eaac) { + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + int i, layers = (eaac->channels+1) / 2; + + + /* init layout */ + data = init_layout_layered(layers); + if (!data) goto fail; + + /* open each layer subfile (1/2ch streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch). + * EA-XMA uses completely separate 1/2ch streams, unlike standard XMA that interleaves 1/2ch streams + * with a skip counter to reinterleave (so EA-XMA streams don't have skips set) */ + for (i = 0; i < layers; i++) { + int layer_channels = (i+1 == layers && eaac->channels % 2 == 1) ? 1 : 2; /* last layer can be 1/2ch */ + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, eaac->loop_flag); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = eaac->sample_rate; + data->layers[i]->num_samples = eaac->num_samples; + data->layers[i]->loop_start_sample = eaac->loop_start; + data->layers[i]->loop_end_sample = eaac->loop_end; + +#ifdef VGM_USE_FFMPEG + { + uint8_t buf[0x100]; + int bytes, block_size, block_count; + size_t stream_size; + + temp_streamFile = setup_eaac_streamfile(streamData, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset); + if (!temp_streamFile) goto fail; + + stream_size = get_streamfile_size(temp_streamFile); + block_size = 0x10000; /* unused */ + block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0); + + bytes = ffmpeg_make_riff_xma2(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, block_count, block_size); + data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00, stream_size); + if (!data->layers[i]->codec_data) goto fail; + + data->layers[i]->coding_type = coding_FFmpeg; + data->layers[i]->layout_type = layout_none; + } +#else + goto fail; +#endif + + if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) { + goto fail; + } + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + close_streamfile(temp_streamFile); + return data; + +fail: + close_streamfile(temp_streamFile); + free_layout_layered(data); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h index 9436af00d..4d08a0ef2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h @@ -2,140 +2,184 @@ #define _EA_EAAC_STREAMFILE_H_ #include "../streamfile.h" +#define XMA_FRAME_SIZE 0x800 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 */ + int streamed; + int stream_number; + int stream_count; + off_t stream_offset; + + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + + uint32_t block_flag; /* current block flags */ + size_t block_size; /* current block size */ + size_t skip_size; /* size to skip from a block start to reach data start */ + size_t data_size; /* logical size of the block */ + size_t extra_size; /* extra padding/etc size of the block */ + + size_t logical_size; } 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 */ + * physical/logical_offset will be at the start of a block and only advance when a block is 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) { + if (offset < 0 || offset > data->logical_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->physical_offset = data->stream_offset; data->logical_offset = 0x00; + data->data_size = 0; + data->extra_size = 0; } - /* read doing one EA block at a time */ + /* read blocks, one 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 = (uint8_t)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 */ + /* ignore EOF (implicitly handles block end flags) */ + if (data->logical_offset >= data->logical_size) { + break; } - if (data->version == 1 && block_flag == 0x45) - break; /* stop on last block (always empty) */ - switch(data->codec) { + /* process new block */ + if (data->data_size == 0) { + data->block_flag = (uint8_t)read_8bit(data->physical_offset+0x00,streamfile); + data->block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF; + + /* ignore header block */ + if (data->version == 1 && data->block_flag == 0x48) { + data->physical_offset += data->block_size; + continue; + } + + switch(data->codec) { + case 0x03: { /* EA-XMA */ + /* block format: 0x04=num-samples, (size*4 + N XMA packets) per stream (with 1/2ch XMA headers) */ + int i; + + data->skip_size = 0x04 + 0x04; + for (i = 0; i < data->stream_number; i++) { + data->skip_size += read_32bitBE(data->physical_offset+data->skip_size, streamfile) / 4; + } + data->data_size = read_32bitBE(data->physical_offset+data->skip_size, streamfile) / 4; /* why size*4...? */ + data->skip_size += 0x04; /* skip mini header */ + data->data_size -= 0x04; /* remove mini header */ + if (data->data_size % XMA_FRAME_SIZE) + data->extra_size = XMA_FRAME_SIZE - (data->data_size % XMA_FRAME_SIZE); + break; + } + + case 0x05: /* EALayer3 v1 */ + case 0x06: /* EALayer3 v2 "PCM" */ + case 0x07: /* EALayer3 v2 "Spike" */ + data->skip_size = 0x08; + data->data_size = data->block_size - data->skip_size; + break; + + case 0x0a: /* EATrax */ + data->skip_size = 0x08; + data->data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* also block_size - 0x08 */ + break; #if 0 - case 0x03: - data_size = block_size - ???; - extra_size = (data_size % 0x800); /* deflated padding */ - - - skip_size = 0x08 + 0x04*data->stream_count; - break; + case 0x0c: /* EA Opus */ + data->skip_size = 0x08; + data->data_size = data->block_size - data->skip_size; + 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; + 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; + /* move to next block */ + if (offset >= data->logical_offset + data->data_size + data->extra_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->data_size + data->extra_size; + data->data_size = 0; + data->extra_size = 0; continue; } - /* reads could fall in the middle of the block */ - intradata_offset = offset - data->logical_offset; - intrablock_offset = skip_size + intradata_offset; + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; - /* 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) - break; /* should never happen... */ + bytes_consumed = offset - data->logical_offset; - /* 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) - break; /* couldn't read fully */ + switch(data->codec) { + case 0x03: { /* EA-XMA */ + if (bytes_consumed < data->data_size) { /* offset falls within actual data */ + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + } + else { /* offset falls within logical padded data */ + to_read = data->data_size + data->extra_size - bytes_consumed; + if (to_read > length) + to_read = length; + memset(dest, 0xFF, to_read); /* no real need though, padding is ignored */ + bytes_done = to_read; + } + break; + } - dest += bytes_read; - offset += bytes_read; - length -= bytes_read; + default: + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + break; + } - /* block fully read, go next */ - if (intradata_offset + bytes_read == data_size) { - data->physical_offset += block_size; - data->logical_offset += data_size; + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } } - - 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; + size_t logical_size = 0; - if (data->total_size) - return data->total_size; + if (data->logical_size) + return data->logical_size; - physical_offset = data->start_offset; - max_physical_offset = get_streamfile_size(streamfile) - data->start_offset; + physical_offset = data->stream_offset; + max_physical_offset = get_streamfile_size(streamfile); - /* get size of the underlying, non-blocked data */ + /* get size of the logical stream */ while (physical_offset < max_physical_offset) { - uint32_t block_flag, block_size, data_size; + uint32_t block_flag, block_size, data_size, skip_size; + int i; block_flag = (uint8_t)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 */ + break; /* unknown block */ if (data->version == 1 && block_flag == 0x48) { physical_offset += block_size; @@ -144,15 +188,21 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { 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 */ + break; /* unknown block */ switch(data->codec) { -#if 0 - case 0x03: - data_size = block_size - ???; - data_size += (data_size % 0x800); /* deflated padding */ + case 0x03: /* EA-XMA */ + skip_size = 0x04 + 0x04; + for (i = 0; i < data->stream_number; i++) { + skip_size += read_32bitBE(physical_offset + skip_size, streamfile) / 4; /* why size*4...? */ + } + data_size = read_32bitBE(physical_offset + skip_size, streamfile) / 4; + skip_size += 0x04; /* skip mini header */ + data_size -= 0x04; /* remove mini header */ + if (data_size % XMA_FRAME_SIZE) + data_size += XMA_FRAME_SIZE - (data_size % XMA_FRAME_SIZE); /* extra padding */ break; -#endif + case 0x05: /* EALayer3 v1 */ case 0x06: /* EALayer3 v2 "PCM" */ case 0x07: /* EALayer3 v2 "Spike" */ @@ -160,40 +210,62 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { break; case 0x0a: /* EATrax */ - data_size = read_32bitBE(physical_offset+0x04,streamfile); /* should be block_size - 0x08 */ + data_size = read_32bitBE(physical_offset+0x04,streamfile); /* also block_size - 0x08 */ break; +#if 0 + case 0x0c: { /* EAOpus */ + size_t done; + data_size = 0; + while (done < block_size - 0x08) { + size_t packet_size = read_16bitBE(physical_offset+0x08+done,streamfile); + done += 0x02 + packet_size; + data_size = 0x1a + packet_size; /* OggS page per Opus packet */ + } + break; + } +#endif default: return 0; } physical_offset += block_size; - total_size += data_size; + logical_size += data_size; - if (data->version == 0 && block_flag == 0x80) + if (data->version == 0 && (!data->streamed || block_flag == 0x80)) break; /* stop on last block */ } - data->total_size = total_size; - return data->total_size; + /* logical size can be bigger in EA-XMA though */ + if (physical_offset > get_streamfile_size(streamfile)) { + VGM_LOG("EA EAAC: wrong size\n"); + return 0; + } + + data->logical_size = logical_size; + return data->logical_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) + * - EA-XMA: deflated XMA in multistreams (separate 1/2ch packets) * - EALayer3: MPEG granule 1 can go in the next block (in V2"P" mainly, others could use layout blocked_sns) * - EATrax: ATRAC9 frames can be split between blooks + * - EAOpus: */ -static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, off_t start_offset, size_t total_size) { +static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, int streamed, int stream_number, int stream_count, off_t stream_offset) { STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; 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; + io_data.streamed = streamed; + io_data.stream_number = stream_number; + io_data.stream_count = stream_count; + io_data.stream_offset = stream_offset; + io_data.physical_offset = stream_offset; + io_data.logical_size = eaac_io_size(streamFile, &io_data); /* force init */ /* setup subfile */ new_streamFile = open_wrap_streamfile(streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 06bf53a25..d1611eeeb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../layout/layout.h" #include "../coding/coding.h" +#include "ea_schl_streamfile.h" /* header version */ #define EA_VERSION_NONE -1 @@ -29,7 +30,7 @@ #define EA_CODEC1_NONE -1 #define EA_CODEC1_PCM 0x00 #define EA_CODEC1_VAG 0x01 // unsure -#define EA_CODEC1_EAXA 0x07 // Need for Speed 2 PC, FIFA 98 SAT +#define EA_CODEC1_EAXA 0x07 #define EA_CODEC1_MT10 0x09 //#define EA_CODEC1_N64 ? @@ -46,10 +47,33 @@ #define EA_CODEC2_XBOXADPCM 0x14 #define EA_CODEC2_MT5 0x16 #define EA_CODEC2_EALAYER3 0x17 -#define EA_CODEC2_ATRAC3PLUS 0x1B /* Medal of Honor Heroes 2 (PSP) */ -//todo #define EA_CODEC2_ATRAC9 0x-- /* supposedly exists */ +#define EA_CODEC2_ATRAC3PLUS 0x1B -#define EA_MAX_CHANNELS 6 +/* Block headers, SCxy - where x is block ID and y is endianness flag (always 'l'?) */ +#define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */ +#define EA_BLOCKID_COUNT 0x5343436C /* "SCCl" */ +#define EA_BLOCKID_DATA 0x5343446C /* "SCDl" */ +#define EA_BLOCKID_LOOP 0x53434C6C /* "SCLl */ +#define EA_BLOCKID_END 0x5343456C /* "SCEl" */ + +/* Localized block headers, Sxyy - where x is block ID and yy is lang code (e.g. "SHEN"), used in videos */ +#define EA_BLOCKID_LOC_HEADER 0x53480000 /* "SH" */ +#define EA_BLOCKID_LOC_COUNT 0x53430000 /* "SC" */ +#define EA_BLOCKID_LOC_DATA 0x53440000 /* "SD" */ +#define EA_BLOCKID_LOC_END 0x53450000 /* "SE" */ + +#define EA_BLOCKID_LOC_EN 0x0000454E +#define EA_BLOCKID_LOC_FR 0x00004652 +#define EA_BLOCKID_LOC_GE 0x00004745 +#define EA_BLOCKID_LOC_IT 0x00004954 +#define EA_BLOCKID_LOC_SP 0x00005350 +#define EA_BLOCKID_LOC_RU 0x00005255 +#define EA_BLOCKID_LOC_JA 0x00004A41 + +#define EA_BNK_HEADER_LE 0x424E4B6C /* "BNKl" */ +#define EA_BNK_HEADER_BE 0x424E4B62 /* "BNKb" */ + +#define EA_MAX_CHANNELS 6 typedef struct { int32_t num_samples; @@ -66,15 +90,16 @@ typedef struct { off_t offsets[EA_MAX_CHANNELS]; off_t coefs[EA_MAX_CHANNELS]; + off_t loops[EA_MAX_CHANNELS]; int big_endian; int loop_flag; - int codec_version; + int codec_config; } ea_header; static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams); static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int total_streams); -static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length); +static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length, int bnk_version); static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset); static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream); static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); @@ -84,18 +109,18 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* check extension; exts don't seem enforced by EA's tools, but usually: * STR/ASF/MUS ~early, EAM ~mid, SNG/AUD ~late, rest uncommon/one game (ex. STRM: MySims Kingdom Wii) */ - if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm,ast,trj,trm")) + if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm,trj,trm")) goto fail; /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x5343486C && /* "SCHl" */ - read_32bitBE(0x00,streamFile) != 0x5348454E && /* "SHEN" */ - read_32bitBE(0x00,streamFile) != 0x53484652 && /* "SHFR" */ - read_32bitBE(0x00,streamFile) != 0x53484745 && /* "SHGE" */ - read_32bitBE(0x00,streamFile) != 0x53484954 && /* "SHIT" */ - read_32bitBE(0x00,streamFile) != 0x53485350 && /* "SHSP" */ - read_32bitBE(0x00,streamFile) != 0x53485255 && /* "SHRU" */ - read_32bitBE(0x00,streamFile) != 0x53484A41) /* "SHJA" */ + if (read_32bitBE(0x00,streamFile) != EA_BLOCKID_HEADER && /* "SCHl" */ + read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */ + read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */ + read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */ + read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */ + read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */ + read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */ + read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA)) /* "SHJA" */ goto fail; /* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. @@ -117,13 +142,10 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { goto fail; /* check header (doesn't use EA blocks, otherwise very similar to SCHl) */ - if (read_32bitBE(0x00,streamFile) == 0x424E4B6C || /* "BNKl" (common) */ - read_32bitBE(0x00,streamFile) == 0x424E4B62) /* "BNKb" (FIFA 98 SS) */ - offset = 0; - else if (read_32bitBE(0x100,streamFile) == 0x424E4B6C) /* "BNKl" (common) */ + if (read_32bitBE(0x100,streamFile) == EA_BNK_HEADER_LE) offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */ else - goto fail; + offset = 0x00; return parse_bnk_header(streamFile, offset, streamFile->stream_index, 0); @@ -131,13 +153,15 @@ fail: return NULL; } -/* EA ABK - contains embedded BNK file or references streams in AST file */ +/* EA ABK - common soundbank format in 6th-gen games, can reference RAM and streamed assets */ +/* RAM assets are stored in embedded BNK file */ +/* streamed assets are stored externally in AST file (mostly seen in earlier 6th-gen games) */ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset; uint32_t i, j, k, num_sounds, total_sound_tables; uint16_t num_tables; - uint8_t version, sound_type, num_entries; + uint8_t sound_type, num_entries; off_t sound_table_offsets[0x2000]; STREAMFILE * astData = NULL; VGMSTREAM * vgmstream; @@ -151,10 +175,6 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { if (read_32bitBE(0x00, streamFile) != 0x41424B43) /* "ABKC" */ goto fail; - version = read_8bit(0x06, streamFile); - if (version > 0x01) - goto fail; - /* use table offset to check endianness */ if (guess_endianness32bit(0x1C,streamFile)) { read_32bit = read_32bitBE; @@ -165,7 +185,8 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { } if (target_stream == 0) target_stream = 1; - if (target_stream < 0) goto fail; + if (target_stream < 0) + goto fail; num_tables = read_16bit(0x0A, streamFile); header_table_offset = read_32bit(0x1C, streamFile); @@ -173,9 +194,16 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { target_entry_offset = 0; total_sound_tables = 0; + /* check to avoid clashing with the newer ABK format */ + if (bnk_offset && + read_32bitBE(bnk_offset, streamFile) != EA_BNK_HEADER_LE && + read_32bitBE(bnk_offset, streamFile) != EA_BNK_HEADER_BE) + goto fail; + for (i = 0; i < num_tables; i++) { num_entries = read_8bit(header_table_offset + 0x24, streamFile); base_offset = read_32bit(header_table_offset + 0x2C, streamFile); + if (num_entries == 0xff) goto fail; /* EOF read */ for (j = 0; j < num_entries; j++) { value_offset = read_32bit(header_table_offset + 0x3C + 0x04 * j, streamFile); @@ -192,14 +220,16 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { } } - if (is_dupe) continue; + if (is_dupe) + continue; sound_table_offsets[total_sound_tables++] = table_offset; num_sounds = read_32bit(table_offset, streamFile); + if (num_sounds == 0xffffffff) goto fail; /* EOF read */ for (k = 0; k < num_sounds; k++) { entry_offset = table_offset + 0x04 + 0x0C * k; - sound_type = read_8bit(entry_offset, streamFile); + sound_type = read_8bit(entry_offset + 0x00, streamFile); /* some of these dummies pointing at sound 0 in BNK */ if (sound_type == 0x00 && read_32bit(entry_offset + 0x04, streamFile) == 0) @@ -211,45 +241,48 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { } } + /* there can be another set of values, don't know what they mean */ + num_entries += read_8bit(header_table_offset + 0x27, streamFile); header_table_offset += 0x3C + num_entries * 0x04; } - if (target_entry_offset == 0) goto fail; + if (target_entry_offset == 0) + goto fail; /* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed and prefetched(?) */ /* 0x01: ??? */ /* 0x04: index for normal sounds, offset for streamed sounds */ /* 0x08: offset for prefetched sounds */ - sound_type = read_8bit(target_entry_offset, streamFile); + sound_type = read_8bit(target_entry_offset + 0x00, streamFile); switch (sound_type) { case 0x00: - if (!bnk_offset) goto fail; - - if (read_32bitBE(bnk_offset, streamFile) != 0x424E4B6C && /* "BNKl" */ - read_32bitBE(bnk_offset,streamFile) != 0x424E4B62) /* BNKb */ + if (!bnk_offset) goto fail; bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1; vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, total_sounds); - if (!vgmstream) goto fail; + if (!vgmstream) + goto fail; break; case 0x01: case 0x02: astData = open_streamfile_by_ext(streamFile, "ast"); - if (!astData) goto fail; + if (!astData) + goto fail; if (sound_type == 0x01) schl_offset = read_32bit(target_entry_offset + 0x04, streamFile); else schl_offset = read_32bit(target_entry_offset + 0x08, streamFile); - if (read_32bitBE(schl_offset, astData) != 0x5343486C) /* "SCHl */ + if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER) goto fail; vgmstream = parse_schl_block(astData, schl_offset, total_sounds); - if (!vgmstream) goto fail; + if (!vgmstream) + goto fail; break; default: @@ -265,38 +298,87 @@ fail: return NULL; } -/* EA HDR/DAT combo - frequently used for storing speech */ -VGMSTREAM * init_vgmstream_ea_hdr(STREAMFILE *streamFile) { +/* EA HDR/DAT combo - seen in late 6th-gen games, used for storing speech and other streamed sounds (except for music) */ +VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { int target_stream = streamFile->stream_index; uint8_t userdata_size, total_sounds; - size_t total_size; - off_t schl_offset; - STREAMFILE *datFile = NULL, *sthFile = NULL; + off_t schl_offset, offset_mult; + STREAMFILE *datFile = NULL; VGMSTREAM *vgmstream; + + /* main header's endianness is platform-native but we only care about one byte values */ + /* 0x00: ID */ + /* 0x02: sub-ID (used for different police voices in NFS games) */ + /* 0x04: (low nibble) userdata size */ + /* 0x04: (high nibble) ??? */ + /* 0x05: number of files */ + /* 0x06: ??? */ + /* 0x07: offset multiplier flag */ + /* 0x08: combined size of all sounds without padding divided by 0x0100 */ + /* 0x0C: table start */ + + /* no nice way to validate these so we do what we can */ + /* must be accompanied by DAT file with SCHl sounds */ + datFile = open_streamfile_by_ext(streamFile, "dat"); + if (!datFile) + goto fail; + + if (read_32bitBE(0x00, datFile) != EA_BLOCKID_HEADER) + goto fail; + + userdata_size = read_8bit(0x04, streamFile) & 0x0F; + total_sounds = read_8bit(0x05, streamFile); + offset_mult = (off_t)read_8bit(0x07, streamFile) * 0x0100 + 0x0100; + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) + goto fail; + + /* offsets are always big endian */ + schl_offset = (off_t)read_16bitBE(0x0C + (0x02+userdata_size) * (target_stream-1), streamFile) * offset_mult; + if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER) + goto fail; + + vgmstream = parse_schl_block(datFile, schl_offset, total_sounds); + if (!vgmstream) + goto fail; + + close_streamfile(datFile); + return vgmstream; + +fail: + close_streamfile(datFile); + return NULL; +} + +/* EA IDX/BIG combo - basically a set of HDR/DAT compiled into one file */ +VGMSTREAM * init_vgmstream_ea_idx_big(STREAMFILE *streamFile) { + int target_stream = streamFile->stream_index, total_sounds, subsound_index; + uint32_t i, num_hdr; + uint16_t hdr_id, hdr_subid; + uint8_t userdata_size, hdr_sounds; + off_t entry_offset, hdr_offset, base_offset, schl_offset, offset_mult; + //size_t hdr_size; + char stream_name[STREAM_NAME_SIZE]; + STREAMFILE *bigFile = NULL; + VGMSTREAM *vgmstream = NULL; int32_t (*read_32bit)(off_t,STREAMFILE*); int16_t (*read_16bit)(off_t,STREAMFILE*); - /* No nice way to validate these so we do what we can */ - sthFile = open_streamfile_by_ext(streamFile, "sth"); - if (sthFile) goto fail; /* newer version using SNR/SNS */ + /* seems to always start with 0x00000001 */ + if (read_32bitLE(0x00, streamFile) != 0x00000001 && + read_32bitBE(0x00, streamFile) != 0x00000001) + goto fail; - datFile = open_streamfile_by_ext(streamFile, "dat"); - if (!datFile) goto fail; + bigFile = open_streamfile_by_ext(streamFile, "big"); + if (!bigFile) + goto fail; - /* 0x00: hash */ - /* 0x02: sub-ID (used for different police voices in NFS games) */ - /* 0x04: userdata size (low nibble) */ - /* 0x05: number of files */ - /* 0x06: ??? */ - /* 0x08: combined size of all sounds without padding divided by 0x0100 */ - /* 0x0C: table start */ - userdata_size = read_8bit(0x04, streamFile) & 0x0F; - total_sounds = read_8bit(0x05, streamFile); + if (read_32bitBE(0x00, bigFile) != EA_BLOCKID_HEADER) + goto fail; - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) goto fail; - - if (guess_endianness16bit(0x08,streamFile)) { + /* use number of files for endianness check */ + if (guess_endianness32bit(0x04,streamFile)) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { @@ -304,24 +386,57 @@ VGMSTREAM * init_vgmstream_ea_hdr(STREAMFILE *streamFile) { read_16bit = read_16bitLE; } - total_size = (size_t)read_16bit(0x08, streamFile) * 0x0100; - if (total_size > get_streamfile_size(datFile)) goto fail; - - /* offsets are always big endian */ - schl_offset = (off_t)read_16bitBE(0x0C + (0x02+userdata_size) * (target_stream-1), streamFile) * 0x0100; - if (read_32bitBE(schl_offset, datFile) != 0x5343486C) /* "SCHl */ + num_hdr = read_32bit(0x04, streamFile); + if (read_32bit(0x54,streamFile) != num_hdr) goto fail; - vgmstream = parse_schl_block(datFile, schl_offset, total_sounds); - if (!vgmstream) goto fail; + if (target_stream == 0) target_stream = 1; + schl_offset = 0; + total_sounds = 0; + schl_offset = 0xFFFFFFFF; - close_streamfile(datFile); + for (i = 0; i < num_hdr; i++) { + entry_offset = 0x58 + 0x10 * i; + //hdr_size = read_32bit(entry_offset + 0x04, streamFile); + hdr_offset = read_32bit(entry_offset + 0x08, streamFile); + base_offset = read_32bit(entry_offset + 0x0C, streamFile); + + hdr_id = read_16bit(hdr_offset + 0x00, streamFile); + hdr_subid = read_16bit(hdr_offset + 0x02, streamFile); + userdata_size = read_8bit(hdr_offset + 0x04, streamFile) & 0x0F; + hdr_sounds = read_8bit(hdr_offset + 0x05, streamFile); + offset_mult = (off_t)read_8bit(hdr_offset + 0x07, streamFile) * 0x0100 + 0x0100; + + if (target_stream > total_sounds && target_stream <= total_sounds + hdr_sounds) { + schl_offset = base_offset + (off_t)read_16bitBE(hdr_offset + 0x0C + (0x02+userdata_size) * (target_stream-total_sounds-1), streamFile) * offset_mult; + subsound_index = target_stream - total_sounds; + + /* There are no filenames but we can add IDs to stream name for better organization */ + if (hdr_subid != 0xFFFF) + snprintf(stream_name, STREAM_NAME_SIZE, "%03d_%02d_%d", hdr_id, hdr_subid, subsound_index); + else + snprintf(stream_name, STREAM_NAME_SIZE, "%03d_%d", hdr_id, subsound_index); + } + + total_sounds += hdr_sounds; + } + + if (schl_offset == 0xFFFFFFFF) + goto fail; + + if (read_32bitBE(schl_offset, bigFile) != EA_BLOCKID_HEADER) + goto fail; + + vgmstream = parse_schl_block(bigFile, schl_offset, total_sounds); + if (!vgmstream) + goto fail; + + strncpy(vgmstream->stream_name, stream_name, STREAM_NAME_SIZE); + close_streamfile(bigFile); return vgmstream; fail: - close_streamfile(datFile); - close_streamfile(sthFile); - + close_streamfile(bigFile); return NULL; } @@ -331,19 +446,22 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int to size_t header_size; ea_header ea = { 0 }; - if (guess_endianness32bit(offset + 0x04, streamFile)) /* size is always LE, except in early SS/MAC */ + if (guess_endianness32bit(offset + 0x04, streamFile)) { /* size is always LE, except in early SS/MAC */ header_size = read_32bitBE(offset + 0x04, streamFile); - else + ea.codec_config |= 0x02; + } + else { header_size = read_32bitLE(offset + 0x04, streamFile); + } header_offset = offset + 0x08; - if (!parse_variable_header(streamFile, &ea, header_offset, header_size)) + if (!parse_variable_header(streamFile, &ea, header_offset, header_size - 0x08, 0)) goto fail; start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ - /* rest is common */ + /* rest is common */ return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, total_streams); fail: @@ -360,21 +478,26 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta int i, bnk_version; int total_bnk_sounds, real_bnk_sounds = 0; - /* use header size as endianness flag */ - if (guess_endianness32bit(offset + 0x08,streamFile)) { + /* check header */ + /* BNK header endianness is platform-native */ + if (read_32bitBE(offset + 0x00, streamFile) == EA_BNK_HEADER_BE) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; - } else { + } + else if (read_32bitBE(offset + 0x00, streamFile) == EA_BNK_HEADER_LE) { read_32bit = read_32bitLE; read_16bit = read_16bitLE; } + else { + goto fail; + } bnk_version = read_8bit(offset + 0x04,streamFile); total_bnk_sounds = read_16bit(offset + 0x06,streamFile); /* check multi-streams */ switch(bnk_version) { - case 0x02: /* early (Need For Speed PC, Fifa 98 SS) */ + case 0x02: /* early [Need For Speed II (PC/PS1), FIFA 98 (PC/PS1/SAT)] */ table_offset = 0x0c; header_size = read_32bit(offset + 0x08,streamFile); /* full size */ break; @@ -416,7 +539,7 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta if (target_stream < 0 || header_offset == 0 || real_bnk_sounds < 1) goto fail; - if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset)) + if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset, bnk_version)) goto fail; /* fix absolute offsets so it works in next funcs */ @@ -428,13 +551,6 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */ - /* special case found in some tests (pcstream had hist, pcbnk no hist, no patch diffs) - * I think this works but what decides if hist is used or not a secret to everybody */ - if (ea.codec2 == EA_CODEC2_EAXA && ea.codec1 == EA_CODEC1_NONE && ea.version >= EA_VERSION_V1) { - ea.codec_version = 0; - } - - /* rest is common */ return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams ? total_streams : real_bnk_sounds); @@ -458,7 +574,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ vgmstream->loop_end_sample = ea->loop_end; vgmstream->codec_endian = ea->big_endian; - vgmstream->codec_version = ea->codec_version; + vgmstream->codec_config = ea->codec_config; vgmstream->meta_type = is_bnk ? meta_EA_BNK : meta_EA_SCHL; @@ -479,7 +595,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ } } else if (vgmstream->channels > 1 && ea->codec2 == EA_CODEC2_GCADPCM && ea->offsets[0] == ea->offsets[1]) { - /* pcstream+gcadpcm with sx.exe v2, this is probably an bug (even with this parts of the wave are off) */ + /* pcstream+gcadpcm with sx.exe v2, this is probably a bug (even with this parts of the wave are off) */ int interleave = (vgmstream->num_samples / 14 * 8); /* full interleave */ for (i = 0; i < vgmstream->channels; i++) { ea->offsets[i] = ea->offsets[0] + interleave*i; @@ -491,6 +607,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ } vgmstream->num_streams = total_streams; + //vgmstream->stream_size = ; //todo needed for kbps info /* EA usually implements their codecs in all platforms (PS2/WII do EAXA/MT/EALAYER3) and * favors them over platform's natives (ex. EAXA vs VAG/DSP). @@ -498,7 +615,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ switch (ea->codec2) { case EA_CODEC2_EAXA: /* EA-XA, CDXA ADPCM variant */ - if (ea->codec1 == EA_CODEC1_EAXA) { + if (ea->version == EA_VERSION_V0) { if (ea->platform != EA_PLATFORM_SAT && ea->channels > 1) vgmstream->coding_type = coding_EA_XA; /* original version, stereo stream */ else @@ -586,19 +703,57 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ use_pcm_blocks = 1; } + /* make relative loops absolute for the decoder */ + if (ea->loop_flag) { + for (i = 0; i < vgmstream->channels; i++) { + ea->loops[i] += ea->offsets[0]; + } + } + vgmstream->coding_type = coding_EA_MT; - vgmstream->codec_data = init_ea_mt(vgmstream->channels, use_pcm_blocks); + vgmstream->codec_data = init_ea_mt_loops(vgmstream->channels, use_pcm_blocks, ea->loop_start, ea->loops); if (!vgmstream->codec_data) goto fail; break; } - case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */ +#ifdef VGM_USE_FFMPEG + case EA_CODEC2_ATRAC3PLUS: { + ffmpeg_codec_data *ffmpeg_data; + + /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header [Medal of Honor Heroes 2 (PSP)] */ + if (!is_bnk) { + STREAMFILE* temp_streamFile = NULL; + /* remove blocks on reads to feed FFmpeg a clean .at3 */ + temp_streamFile = setup_schl_streamfile(streamFile, ea->codec2, ea->channels, start_offset, 0); + if (!temp_streamFile) goto fail; + + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + + ffmpeg_data = init_ffmpeg_offset(temp_streamFile, start_offset, get_streamfile_size(temp_streamFile)); + close_streamfile(temp_streamFile); + if (!ffmpeg_data) goto fail; + } + else { + size_t riff_size = read_32bitLE(start_offset + 0x04, streamFile) + 0x08; + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, riff_size); + 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) /* in case FFmpeg didn't get them */ + ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); + break; + } +#endif + default: VGM_LOG("EA SCHl: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform); goto fail; } - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; @@ -606,54 +761,44 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ if (is_bnk) { /* setup channel offsets */ - if (vgmstream->coding_type == coding_EA_XA) { /* shared */ + if (vgmstream->coding_type == coding_EA_XA) { + /* shared (stereo/mono codec) */ for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = ea->offsets[0]; } - //} else if (vgmstream->layout_type == layout_interleave) { /* interleaved */ + } + //else if (vgmstream->layout_type == layout_interleave) { /* interleaved */ // for (i = 0; i < vgmstream->channels; i++) { // vgmstream->ch[i].offset = ea->offsets[0] + vgmstream->interleave_block_size*i; // } - } else { /* absolute */ + //} + else if (vgmstream->coding_type == coding_PCM16_int && ea->version == 0) { + /* Need for Speed 2 (PC) bad offsets */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + 0x02*i; + } + } + else if (vgmstream->coding_type == coding_PCM8 && ea->platform == EA_PLATFORM_PS2 && ea->version == 3) { + /* SSX3 (PS2) weird 0x10 mini header (codec/loop start/loop end/samples) */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + 0x10; + } + } + else { + /* absolute */ for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = ea->offsets[i]; } } - - /* setup ADPCM hist */ - switch(vgmstream->coding_type) { - /* id, size, samples, hists-per-channel, stereo/interleaved data */ - case coding_EA_XA: - /* read ADPCM history from all channels before data (not actually read in sx.exe) */ - for (i = 0; i < vgmstream->channels; i++) { - //vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile); - //vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile); - vgmstream->ch[i].offset += vgmstream->channels*0x04; - } - break; - - default: - /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ - if (vgmstream->codec_version == 1) { - for (i = 0; i < vgmstream->channels; i++) { - //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); - //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); - vgmstream->ch[i].offset += 4; - } - } - break; - } } - else { + else if (vgmstream->layout_type == layout_blocked_ea_schl) { + /* regular SCHls, except ATRAC3plus */ if (total_streams == 0) { /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this */ int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream); if (total_samples > vgmstream->num_samples) vgmstream->num_samples = total_samples; } - - /* setup first block to update offsets */ - block_update_ea_schl(start_offset,vgmstream); } return vgmstream; @@ -689,11 +834,11 @@ static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset) { } /* decodes EA's GSTR/PT header (mostly cross-referenced with sx.exe) */ -static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length) { +static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length, int bnk_version) { off_t offset = begin_offset; uint32_t platform_id; int is_header_end = 0; - + int is_bnk = bnk_version; /* null defaults as 0 can be valid */ ea->version = EA_VERSION_NONE; @@ -724,6 +869,8 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be uint8_t patch_type = read_8bit(offset,streamFile); offset++; + //;off_t test_offset = offset; + //;VGM_LOG("EA SCHl: patch=%02x at %lx, value=%x\n", patch_type, offset-1, read_patch(streamFile, &test_offset)); switch(patch_type) { case 0x00: /* signals non-default block rate and maybe other stuff; or padding after 0xFF */ if (!is_header_end) @@ -732,38 +879,37 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be case 0x05: /* unknown (usually 0x50 except Madden NFL 3DS: 0x3e800) */ case 0x06: /* priority (0..100, always 0x65 for streams, others for BNKs; rarely ommited) */ - case 0x07: /* unknown (BNK only: 36|3A) */ + case 0x07: /* unknown (BNK only: 36|3A|40) */ case 0x08: /* release envelope (BNK only) */ case 0x09: /* related to playback envelope (BNK only) */ case 0x0A: /* bend range (BNK only) */ - case 0x0B: /* unknown (always 0x02) */ + case 0x0B: /* bank channels (or, offsets[] size; defaults to 1 if not present, removed in sx.exe v3) */ case 0x0C: /* pan offset (BNK only) */ case 0x0D: /* random pan offset range (BNK only) */ case 0x0E: /* volume (BNK only) */ case 0x0F: /* random volume range (BNK only) */ case 0x10: /* detune (BNK only) */ case 0x11: /* random detune range (BNK only) */ + case 0x12: /* unknown, rare (BNK only) [Need for Speed III: Hot Pursuit (PS1)] */ case 0x13: /* effect bus (0..127) */ case 0x14: /* emdedded user data (free size/value) */ + case 0x15: /* unknown, rare (BNK only) [Need for Speed: High Stakes (PS1)] */ case 0x19: /* related to playback envelope (BNK only) */ - case 0x1A: /* unknown and very rare, size 0 (BNK only) [SSX3 (PS2)] */ case 0x1B: /* unknown (movie only?) */ case 0x1C: /* initial envelope volume (BNK only) */ case 0x1D: /* unknown, rare [NASCAR 06 (Xbox)] */ - case 0x1E: + case 0x1E: /* related to ch1? (BNK only) */ case 0x1F: case 0x20: - case 0x21: + case 0x21: /* related to ch2? (BNK only) */ case 0x22: case 0x23: case 0x24: /* master random detune range (BNK only) */ case 0x25: /* unknown */ - case 0x26: /* unknown, rare [FIFA 07 (Xbox)] */ read_patch(streamFile, &offset); break; case 0xFC: /* padding for alignment between patches */ - case 0xFE: /* padding? (actually exists?) */ case 0xFD: /* info section start marker */ break; @@ -795,7 +941,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be ea->loop_start = read_patch(streamFile, &offset); break; case 0x87: /* loop end sample */ - ea->loop_end = read_patch(streamFile, &offset); + ea->loop_end = read_patch(streamFile, &offset) + 1; /* sx.exe does +1 */ break; /* channel offsets (BNK only), can be the equal for all channels or interleaved; not necessarily contiguous */ @@ -843,12 +989,33 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be read_patch(streamFile, &offset); break; + case 0x1A: /* EA-MT/EA-XA relative loop offset of ch1 */ + ea->loops[0] = read_patch(streamFile, &offset); + break; + case 0x26: /* EA-MT/EA-XA relative loop offset of ch2 */ + ea->loops[1] = read_patch(streamFile, &offset); + break; + case 0x27: /* EA-MT/EA-XA relative loop offset of ch3 */ + ea->loops[2] = read_patch(streamFile, &offset); + break; + case 0x28: /* EA-MT/EA-XA relative loop offset of ch4 */ + ea->loops[3] = read_patch(streamFile, &offset); + break; + case 0x29: /* EA-MT/EA-XA relative loop offset of ch5 */ + ea->loops[4] = read_patch(streamFile, &offset); + break; + case 0x2a: /* EA-MT/EA-XA relative loop offset of ch6 */ + ea->loops[5] = read_patch(streamFile, &offset); + break; + case 0x8A: /* long padding (always 0x00000000) */ + case 0x8B: /* also padding? [Need for Speed: Hot Pursuit 2 (PC)] */ case 0x8C: /* flags (ex. play type = 01=static/02=dynamic | spatialize = 20=pan/etc) */ /* (ex. PS1 VAG=0, PS2 PCM/LAYER2=4, GC EAXA=4, 3DS DSP=512, Xbox EAXA=36, N64 BLK=05E800, N64 MT10=01588805E800) */ case 0x8D: /* unknown, rare [FIFA 07 (GC)] */ case 0x8E: case 0x92: /* bytes per sample? */ + case 0x93: /* unknown (BNK only) [Need for Speed III: Hot Pursuit (PC)] */ case 0x98: /* embedded time stretch 1 (long data for who-knows-what) */ case 0x99: /* embedded time stretch 2 */ case 0x9C: /* azimuth ch1 */ @@ -864,11 +1031,16 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be case 0xFF: /* header end (then 0-padded so it's 32b aligned) */ is_header_end = 1; break; + case 0xFE: /* info subsection start marker (rare [SSX3 (PS2)]) */ + is_header_end = 1; + /* Signals that another info section starts, redefining codec/samples/offsets/etc + * (previous header values should be cleared first as not everything is overwritten). + * This subsection seems the same as a next or prev PT subsong, so it's ignored. */ + break; default: - VGM_LOG("EA SCHl: unknown patch 0x%02x at 0x%04lx\n", patch_type, (offset-1)); + VGM_LOG("EA SCHl: unknown patch 0x%02x\n", patch_type); goto fail; - break; } } @@ -982,27 +1154,34 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be case EA_PLATFORM_X360: ea->sample_rate = 44100; break; case EA_PLATFORM_PSP: ea->sample_rate = 22050; break; case EA_PLATFORM_PS3: ea->sample_rate = 44100; break; - //case EA_PLATFORM_3DS: ea->sample_rate = 44100; break;//todo (not 22050/16000) + case EA_PLATFORM_3DS: ea->sample_rate = 32000; break; default: VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform); goto fail; } } - /* special flag: 1=has ADPCM history per block, 0=doesn't */ - if (ea->codec2 == EA_CODEC2_GCADPCM && ea->platform == EA_PLATFORM_3DS) { - ea->codec_version = 1; - } - else if (ea->codec2 == EA_CODEC2_EAXA && ea->codec1 == EA_CODEC1_NONE) { - /* console V2 uses hist, as does PC/MAC V1 (but not later versions) */ - if (ea->version <= EA_VERSION_V1 || - ((ea->platform == EA_PLATFORM_PS2 || ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX) - && ea->version == EA_VERSION_V2)) { - ea->codec_version = 1; + /* some codecs have ADPCM hist at the start of every block in streams (but not BNKs) */ + if (!is_bnk) { + if (ea->codec2 == EA_CODEC2_GCADPCM) { + if (ea->platform == EA_PLATFORM_3DS) + ea->codec_config |= 0x01; + } + else if (ea->codec2 == EA_CODEC2_EAXA) { + /* EA-XA has ADPCM hist in earlier versions */ + /* V0, V1: always */ + /* V2: consoles only */ + /* V3: never */ + if (ea->version <= EA_VERSION_V1) { + ea->codec_config |= 0x01; + } + else if (ea->version == EA_VERSION_V2) { + if (ea->platform == EA_PLATFORM_PS2 || ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX) + ea->codec_config |= 0x01; + } } } - return offset; fail: @@ -1014,24 +1193,27 @@ fail: * music (.map/lin). Subfiles always share header, except num_samples. */ static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream) { int num_samples = 0; - int new_schl = 0; + int multiple_schl = 0; /* calc num_samples as playable data size varies between files/blocks */ { vgmstream->next_block_offset = start_offset; do { uint32_t block_id = read_32bitBE(vgmstream->next_block_offset+0x00,streamFile); - if (block_id == 0x5343486C) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ - new_schl = 1; + if (block_id == EA_BLOCKID_HEADER) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ + multiple_schl = 1; block_update_ea_schl(vgmstream->next_block_offset,vgmstream); num_samples += vgmstream->current_block_samples; } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + + /* reset after getting samples */ + block_update(start_offset,vgmstream); } /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */ - if (new_schl) { + if (multiple_schl) { ;VGM_LOG("EA SCHl: multiple SCHl found\n"); return num_samples; } @@ -1057,14 +1239,14 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start block_size = read_32bitBE(block_offset+0x04,streamFile); switch(block_id) { - case 0x5343446C: /* "SCDl" */ - case 0x5344454E: /* "SDEN" */ - case 0x53444652: /* "SDFR" */ - case 0x53444745: /* "SDGE" */ - case 0x53444954: /* "SDIT" */ - case 0x53445350: /* "SDSP" */ - case 0x53445255: /* "SDRU" */ - case 0x53444A41: /* "SDJA" */ + case EA_BLOCKID_DATA: /* "SCDl" */ + case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_EN: /* "SDEN" */ + case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_FR: /* "SDFR" */ + case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_GE: /* "SDGE" */ + case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_IT: /* "SDIT" */ + case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_SP: /* "SDSP" */ + case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_RU: /* "SDRU" */ + case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_JA: /* "SDJA" */ offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */ return block_offset + 0x0c + ea->channels*0x04 + offset; case 0x00000000: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c index 1703c0adf..c374f874c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c @@ -75,14 +75,8 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) { } - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - /* setup first block to update offsets */ - block_update_ea_schl(start_offset,vgmstream); - - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_streamfile.h new file mode 100644 index 000000000..97eb0c123 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_streamfile.h @@ -0,0 +1,182 @@ +#ifndef _EA_SCHL_STREAMFILE_H_ +#define _EA_SCHL_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 codec; + int channels; + off_t start_offset; + size_t total_size; /* size of the resulting substream */ +} schl_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 schl_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, schl_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_id, block_size, data_size, skip_size; + + block_id = (uint32_t)read_32bitBE(data->physical_offset+0x00,streamfile); + block_size = read_32bitLE(data->physical_offset+0x04,streamfile); /* always LE, hopefully */ + + if (block_id == 0x5343456C) /* "SCEl" */ + break; /* end block (no need to look for more SCHl for codecs needed this custom IO) */ + + if (block_id != 0x5343446C) { /* "SCDl" */ + data->physical_offset += block_size; + continue; /* skip non-data blocks */ + } + + switch(data->codec) { + case 0x1b: /* ATRAC3plus */ + data_size = read_32bitLE(data->physical_offset+0x0c+0x04*data->channels,streamfile); + skip_size = 0x0c+0x04*data->channels+0x04; + 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) + break; /* 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) + break; /* 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; + } + } + + return total_read; +} + +static size_t schl_io_size(STREAMFILE *streamfile, schl_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); + + /* get size of the underlying, non-blocked data */ + while (physical_offset < max_physical_offset) { + uint32_t block_id, block_size, data_size; + + block_id = (uint32_t)read_32bitBE(physical_offset+0x00,streamfile); + block_size = read_32bitLE(physical_offset+0x04,streamfile); /* always LE, hopefully */ + + if (block_id == 0x5343456C) /* "SCEl" */ + break; /* end block (no need to look for more SCHl for codecs needed this custom IO) */ + + if (block_id != 0x5343446C) { /* "SCDl" */ + physical_offset += block_size; + continue; /* skip non-data blocks */ + } + + switch(data->codec) { + case 0x1b: /* ATRAC3plus */ + data_size = read_32bitLE(physical_offset+0x0c+0x04*data->channels,streamfile); + break; + default: + return 0; + } + + physical_offset += block_size; + total_size += data_size; + } + + + if (total_size > get_streamfile_size(streamfile)) { + VGM_LOG("EA SCHL: wrong streamfile total_size\n"); + total_size = 0; + } + + data->total_size = total_size; + return data->total_size; +} + + +/* Prepares custom IO for some blocked SCHl formats, that need clean reads without block headers. + * Basically done to feed FFmpeg clean ATRAC3plus. + */ +static STREAMFILE* setup_schl_streamfile(STREAMFILE *streamFile, int codec, int channels, off_t start_offset, size_t total_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + schl_io_data io_data = {0}; + size_t io_data_size = sizeof(schl_io_data); + + io_data.codec = codec; + io_data.channels = channels; + 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, schl_io_read,schl_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_SCHL_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c index 7321d2538..55ed54d3a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c @@ -61,7 +61,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { total_subsongs = 1; block_id = read_32bit(start_offset, streamFile); - /* files are basically headerless so we inspect blocks the first block + /* files are basically headerless so we inspect the first block * Freekstyle uses multiblocks/subsongs (though some subsongs may be clones?) */ switch(block_id) { case 0x5641474D: /* "VAGM" */ @@ -121,12 +121,12 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_EA_SWVR; 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; /* DSP coefs are loaded per block */ @@ -141,7 +141,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { 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); + block_update(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; @@ -151,6 +151,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { vgmstream->num_samples += num_samples; } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); } if (loop_flag) { @@ -158,7 +159,6 @@ 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 index 494a6b461..53a2a6ac1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_ad10.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_ad10.c @@ -39,14 +39,13 @@ VGMSTREAM * init_vgmstream_ea_wve_ad10(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_ea_wve_ad10(vgmstream->next_block_offset,vgmstream); + block_update(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(start_offset, vgmstream); } - block_update_ea_wve_ad10(start_offset, vgmstream); - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_au00.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_au00.c index a4aea7a4d..72d328496 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_au00.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_wve_au00.c @@ -28,8 +28,8 @@ VGMSTREAM * init_vgmstream_ea_wve_au00(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = 22050; vgmstream->meta_type = meta_EA_WVE_AU00; + vgmstream->sample_rate = 22050; /* 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 */ @@ -44,13 +44,13 @@ VGMSTREAM * init_vgmstream_ea_wve_au00(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_ea_wve_au00(vgmstream->next_block_offset,vgmstream); + block_update(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(start_offset, vgmstream); } - block_update_ea_wve_au00(start_offset, vgmstream); return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/excitebots.c b/Frameworks/vgmstream/vgmstream/src/meta/excitebots.c index 831145a04..2f5de05bb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/excitebots.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/excitebots.c @@ -82,6 +82,7 @@ VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_EB_SFX; + vgmstream->allow_dual_stereo = 1; /* open the file for reading */ { @@ -148,7 +149,7 @@ VGMSTREAM * init_vgmstream_eb_sf0(STREAMFILE *streamFile) { { int i; for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index c93e60713..b0e9004ab 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -46,13 +46,11 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, vgmstream = allocate_vgmstream(data->channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->loop_flag = loop_flag; - vgmstream->codec_data = data; - vgmstream->channels = data->channels; vgmstream->sample_rate = data->sampleRate; + vgmstream->meta_type = meta_FFMPEG; vgmstream->coding_type = coding_FFmpeg; + vgmstream->codec_data = data; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_FFmpeg; if (!num_samples) { num_samples = data->totalSamples; @@ -64,11 +62,10 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, vgmstream->loop_end_sample = loop_end; } - /* this may happen for some streams if FFmpeg can't determine it */ + /* this may happen for some streams if FFmpeg can't determine it (ex. AAC) */ if (vgmstream->num_samples <= 0) goto fail; - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/flx.c b/Frameworks/vgmstream/vgmstream/src/meta/flx.c index d37762e86..832f4cc24 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/flx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/flx.c @@ -85,6 +85,7 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { case 0x02: /* EA-MT (voices) */ vgmstream->coding_type = coding_EA_MT; + vgmstream->layout_type = layout_none; vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0); if (!vgmstream->codec_data) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 306765649..a78c9af01 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -1,13 +1,13 @@ #include "meta.h" #include "../coding/coding.h" +#include "../layout/layout.h" +#include "fsb_interleave_streamfile.h" -/* ************************************************************************************************************ +/* ************************************************************************************************ * FSB defines, copied from the public spec (https://www.fmod.org/questions/question/forum-4928/) - * The format is mostly compatible for FSB1/2/3/4, but not FSB5. Headers always use LE. A FSB contains - * main header + sample header(s) + raw data. In multistreams N sample headers are stored (and - * if the BASICHEADERS flag is set, all headers but the first use HEADER_BASIC = numsamples + datasize) - * ************************************************************************************************************ */ + * for reference. The format is mostly compatible for FSB1/2/3/4, but not FSB5. + * ************************************************************************************************ */ /* These flags are used for FMOD_FSB_HEADER::mode */ #define FMOD_FSB_SOURCE_FORMAT 0x00000001 /* all samples stored in their original compressed format */ #define FMOD_FSB_SOURCE_BASICHEADERS 0x00000002 /* samples should use the basic header structure */ @@ -67,12 +67,13 @@ /* simplified struct based on the original definitions */ +typedef enum { MPEG, IMA, PSX, XMA2, DSP, CELT, PCM8, PCM16 } fsb_codec_t; typedef struct { /* main header */ uint32_t id; int32_t total_subsongs; - uint32_t sample_header_size; /* all of the sample headers including extended information */ - uint32_t data_size; + uint32_t sample_headers_size; /* all of them including extended information */ + uint32_t sample_data_size; uint32_t version; /* extended fsb version (in FSB 3/3.1/4) */ uint32_t flags; /* flags common to all streams (in FSB 3/3.1/4)*/ /* sample header */ @@ -86,25 +87,34 @@ typedef struct { /* extra */ uint32_t base_header_size; uint32_t sample_header_min; + off_t extradata_offset; + off_t first_extradata_offset; meta_t meta_type; off_t name_offset; size_t name_size; + + int loop_flag; + + off_t stream_offset; + + fsb_codec_t codec; } fsb_header; /* ********************************************************************************** */ -/* FSB4 */ +static layered_layout_data* build_layered_fsb_celt(STREAMFILE *streamFile, fsb_header* fsb, int is_new_lib); + +/* FSB1~4 - from games using FMOD audio middleware */ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset; - size_t custom_data_offset; - int loop_flag = 0; int target_subsong = streamFile->stream_index; fsb_header fsb = {0}; - /* check extensions (.bnk = Hard Corps Uprising PS3) */ + /* checks + * .fsb: standard + * .bnk: Hard Corps Uprising (PS3) */ if ( !check_extensions(streamFile, "fsb,bnk") ) goto fail; @@ -117,8 +127,8 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { /* main header */ fsb.total_subsongs = read_32bitLE(0x04,streamFile); - fsb.data_size = read_32bitLE(0x08,streamFile); - fsb.sample_header_size = 0x40; + fsb.sample_data_size = read_32bitLE(0x08,streamFile); + fsb.sample_headers_size = 0x40; fsb.version = 0; fsb.flags = 0; @@ -141,9 +151,11 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1; if (fsb.loop_end > fsb.num_samples) /* this seems common... */ fsb.num_samples = fsb.loop_end; - - start_offset = fsb.base_header_size + fsb.sample_header_size; - custom_data_offset = fsb.base_header_size + fsb.sample_header_min; /* DSP coefs, seek tables, etc */ + + /* DSP coefs, seek tables, etc */ + fsb.extradata_offset = header_offset+fsb.sample_header_min; + + fsb.stream_offset = fsb.base_header_size + fsb.sample_headers_size; } } else { /* other FSBs (common/extended format) */ @@ -165,12 +177,12 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { /* main header */ fsb.total_subsongs = read_32bitLE(0x04,streamFile); - fsb.sample_header_size = read_32bitLE(0x08,streamFile); - fsb.data_size = read_32bitLE(0x0c,streamFile); + fsb.sample_headers_size = read_32bitLE(0x08,streamFile); + fsb.sample_data_size = read_32bitLE(0x0c,streamFile); if (fsb.base_header_size > 0x10) { fsb.version = read_32bitLE(0x10,streamFile); fsb.flags = read_32bitLE(0x14,streamFile); - /* FSB4: 0x18:hash 0x20:guid */ + /* FSB4: 0x18(8):hash 0x20(10):guid */ } else { fsb.version = 0; fsb.flags = 0; @@ -184,7 +196,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { goto fail; } - if (fsb.sample_header_size < fsb.sample_header_min) goto fail; + if (fsb.sample_headers_size < fsb.sample_header_min) goto fail; if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail; @@ -192,7 +204,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { { int i; off_t header_offset = fsb.base_header_size; - off_t data_offset = fsb.base_header_size + fsb.sample_header_size; + off_t data_offset = fsb.base_header_size + fsb.sample_headers_size; /* find target_stream header (variable sized) */ for (i = 0; i < fsb.total_subsongs; i++) { @@ -217,10 +229,18 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { fsb.loop_end = read_32bitLE(header_offset+0x2c,streamFile); fsb.mode = read_32bitLE(header_offset+0x30,streamFile); fsb.sample_rate = read_32bitLE(header_offset+0x34,streamFile); - /* 0x38:defvol 0x3a:defpan 0x3c:defpri */ + /* 0x38: defvol, 0x3a: defpan, 0x3c: defpri */ fsb.channels = read_16bitLE(header_offset+0x3e,streamFile); - /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */ - /* FSB3/4: 0x50:extended_data size_32bits (not always given) */ + /* FSB3.1/4: + * 0x40: mindistance, 0x44: maxdistance, 0x48: varfreq/size_32bits + * 0x4c: varvol, 0x4e: fsb.varpan */ + + /* DSP coefs, seek tables, etc */ + if (stream_header_size > fsb.sample_header_min) { + fsb.extradata_offset = header_offset+fsb.sample_header_min; + if (fsb.first_extradata_offset == 0) + fsb.first_extradata_offset = fsb.extradata_offset; + } } if (i+1 == target_subsong) /* final data_offset found */ @@ -230,42 +250,52 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { data_offset += fsb.stream_size; /* there is no offset so manually count */ /* some subsongs offsets need padding (most FSOUND_IMAADPCM, few MPEG too [Hard Reset (PC) subsong 5]) - * other PADDED4 may set it (ex. XMA) but don't seem to use it and work fine */ + * other codecs may set PADDED4 (ex. XMA) but don't seem to need it and work fine */ if (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4) { if (data_offset % 0x20) data_offset += 0x20 - (data_offset % 0x20); } } - if (i > fsb.total_subsongs) goto fail; /* not found */ + if (i > fsb.total_subsongs) + goto fail; /* not found */ - start_offset = data_offset; - custom_data_offset = header_offset + fsb.sample_header_min; /* DSP coefs, seek tables, etc */ + fsb.stream_offset = data_offset; } } /* XOR encryption for some FSB4, though the flag is only seen after decrypting */ - //VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n"); + //;VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n"); - /* sometimes there is garbage at the end or missing bytes due to improper demuxing */ - VGM_ASSERT(fsb.base_header_size + fsb.sample_header_size + fsb.data_size != streamFile->get_size(streamFile), + /* sometimes there is garbage at the end or missing bytes due to improper ripping */ + VGM_ASSERT(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != streamFile->get_size(streamFile), "FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n", - fsb.base_header_size + fsb.sample_header_size + fsb.data_size, streamFile->get_size(streamFile)); + fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, streamFile->get_size(streamFile)); - /* Loops unless disabled. FMOD default seems full loops (0/num_samples-1) without flags, for repeating tracks + /* Loops unless disabled. FMOD default seems to be full loops (0/num_samples-1) without flags, for repeating tracks * that should loop and jingles/sfx that shouldn't. We'll try to disable looping if it looks jingly enough. */ - loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); + fsb.loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); if(!(fsb.mode & FSOUND_LOOP_NORMAL) /* rarely set */ - && fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */ - && fsb.num_samples < 20*fsb.sample_rate) /* seconds, lame but no other way to know */ - loop_flag = 0; + && fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */ + && fsb.num_samples < 20*fsb.sample_rate) /* in seconds (lame but no other way to know) */ + fsb.loop_flag = 0; /* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */ VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n"); + /* convert to clean some code */ + if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG; + else if (fsb.mode & FSOUND_IMAADPCM) fsb.codec = IMA; + else if (fsb.mode & FSOUND_VAG) fsb.codec = PSX; + else if (fsb.mode & FSOUND_XMA) fsb.codec = XMA2; + else if (fsb.mode & FSOUND_GCADPCM) fsb.codec = DSP; + else if (fsb.mode & FSOUND_CELT) fsb.codec = CELT; + else if (fsb.mode & FSOUND_8BITS) fsb.codec = PCM8; + else fsb.codec = PCM16; + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(fsb.channels,loop_flag); + vgmstream = allocate_vgmstream(fsb.channels,fsb.loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = fsb.sample_rate; @@ -278,98 +308,142 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { if (fsb.name_offset) read_string(vgmstream->stream_name,fsb.name_size+1, fsb.name_offset,streamFile); + switch(fsb.codec) { +#ifdef VGM_USE_MPEG + case MPEG: { /* FSB4: Shatter (PS3), Way of the Samurai 3/4 (PS3) */ + mpeg_custom_config cfg = {0}; - /* parse codec */ - if (fsb.mode & FSOUND_MPEG) { /* FSB4: Shatter, Way of the Samurai 3/4 (PS3) */ -#if defined(VGM_USE_MPEG) - mpeg_custom_config cfg = {0}; + cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : + (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 : + (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0))); - cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : - (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 : - (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0))); + vgmstream->codec_data = init_mpeg_custom(streamFile, fsb.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; - //VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ - VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); - - vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->layout_type = layout_none; -#else - goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */ + //VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ + VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); /* not seen */ + break; + } #endif - } - else if (fsb.mode & FSOUND_IMAADPCM) { /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ - /* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different - * (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */ - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_none; - /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch) - * or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1 */ - if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL)) - vgmstream->coding_type = coding_FSB_IMA; - } - else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - } - else if (fsb.mode & FSOUND_XMA) { /* FSB4: Armored Core V (X360), Hard Corps (X360) */ -#if defined(VGM_USE_FFMPEG) - uint8_t buf[0x100]; - size_t bytes, block_size, block_count; + case IMA: /* FSB3: Bioshock (PC), FSB4: Blade Kitten (PC) */ + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch) + * or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1) */ + if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL)) + vgmstream->coding_type = coding_FSB_IMA; - block_size = 0x8000; /* FSB default */ - block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */ + /* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different + * (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */ + break; - bytes = ffmpeg_make_riff_xma2(buf, 0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; + case PSX: /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) { + vgmstream->interleave_block_size = fsb.stream_size / fsb.channels; + } + else { + vgmstream->interleave_block_size = 0x10; + } + break; - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,fsb.stream_size); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; -#else - goto fail; +#ifdef VGM_USE_FFMPEG + case XMA2: { /* FSB3: The Bourne Conspiracy 2008 (X360), FSB4: Armored Core V (X360), Hard Corps (X360) */ + uint8_t buf[0x100]; + size_t bytes, block_size, block_count; + + block_size = 0x8000; /* FSB default */ + block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, fsb.stream_offset,fsb.stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } #endif - } - else if (fsb.mode & FSOUND_GCADPCM) { - /* FSB3: ?; FSB4: de Blob (Wii), Night at the Museum, M. Night Shyamalan Avatar: The Last Airbender */ - vgmstream->coding_type = coding_NGC_DSP_subint; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x2; - dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e); - } - else if (fsb.mode & FSOUND_CELT) { /* FSB4: War Thunder (PC), The Witcher 2 (PC) */ - VGM_LOG("FSB4 FSOUND_CELT found\n"); - goto fail; - } - else { /* PCM */ - if (fsb.mode & FSOUND_8BITS) { + + case DSP: /* FSB3: Metroid Prime 3 (GC), FSB4: de Blob (Wii) */ + if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) { /* [de Blob (Wii) sfx)] */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = fsb.stream_size / fsb.channels; + } + else { + vgmstream->coding_type = coding_NGC_DSP_subint; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x2; + } + dsp_read_coefs_be(vgmstream, streamFile, fsb.extradata_offset, 0x2e); + break; + +#ifdef VGM_USE_CELT + case CELT: { /* FSB4: War Thunder (PC), The Witcher 2 (PC), Vessel (PC) */ + int is_new_lib; + + /* get libcelt version (set in the first subsong only, but try all extradata just in case) */ + if (fsb.first_extradata_offset || fsb.extradata_offset) { + uint32_t lib = fsb.first_extradata_offset ? + (uint32_t)read_32bitLE(fsb.first_extradata_offset, streamFile) : + (uint32_t)read_32bitLE(fsb.extradata_offset, streamFile);; + switch(lib) { + case 0x80000009: is_new_lib = 0; break; /* War Thunder (PC) */ + case 0x80000010: is_new_lib = 1; break; /* Vessel (PC) */ + default: VGM_LOG("FSB: unknown CELT lib 0x%x\n", lib); goto fail; + } + } + else { + /* split FSBs? try to guess from observed bitstreams */ + uint16_t frame = (uint16_t)read_16bitBE(fsb.stream_offset+0x04+0x04,streamFile); + if ((frame & 0xF000) == 0x6000 || frame == 0xFFFE) { + is_new_lib = 1; + } else { + is_new_lib = 0; + } + } + + if (fsb.channels > 2) { /* multistreams */ + vgmstream->layout_data = build_layered_fsb_celt(streamFile, &fsb, is_new_lib); + if (!vgmstream->layout_data) goto fail; + vgmstream->coding_type = coding_CELT_FSB; + vgmstream->layout_type = layout_layered; + } + else { + vgmstream->codec_data = init_celt_fsb(vgmstream->channels, is_new_lib ? CELT_0_11_0 : CELT_0_06_1); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_CELT_FSB; + vgmstream->layout_type = layout_none; + } + + break; + } +#endif + + case PCM8: /* assumed, no games known */ vgmstream->coding_type = (fsb.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x1; - } - else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */ - /* sometimes FSOUND_STEREO/FSOUND_MONO is not set (ex. Dead Space iOS), - * or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */ + break; + + case PCM16: /* (PCM16) FSB4: Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */ vgmstream->coding_type = (fsb.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x2; - } - } - /* full channel interleave, used in short streams (ex. de Blob Wii SFXs) */ - if (fsb.channels > 1 && (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED)) { - if (vgmstream->coding_type == coding_NGC_DSP_subint) - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = fsb.stream_size / fsb.channels; + /* sometimes FSOUND_MONO/FSOUND_STEREO is not set (ex. Dead Space iOS), + * or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */ + break; + + default: + goto fail; } - /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if ( !vgmstream_open_stream(vgmstream, streamFile, fsb.stream_offset) ) goto fail; return vgmstream; @@ -378,6 +452,62 @@ fail: return NULL; } +#ifdef VGM_USE_CELT +static layered_layout_data* build_layered_fsb_celt(STREAMFILE *streamFile, fsb_header* fsb, int is_new_lib) { + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + int i, layers = (fsb->channels+1) / 2; + + + /* init layout */ + data = init_layout_layered(layers); + if (!data) goto fail; + + /* open each layer subfile (1/2ch CELT streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch) */ + for (i = 0; i < layers; i++) { + int layer_channels = (i+1 == layers && fsb->channels % 2 == 1) + ? 1 : 2; /* last layer can be 1/2ch */ + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, fsb->loop_flag); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = fsb->sample_rate; + data->layers[i]->num_samples = fsb->num_samples; + data->layers[i]->loop_start_sample = fsb->loop_start; + data->layers[i]->loop_end_sample = fsb->loop_end; + +#ifdef VGM_USE_CELT + data->layers[i]->codec_data = init_celt_fsb(layer_channels, is_new_lib ? CELT_0_11_0 : CELT_0_06_1); + if (!data->layers[i]->codec_data) goto fail; + data->layers[i]->coding_type = coding_CELT_FSB; + data->layers[i]->layout_type = layout_none; +#else + goto fail; +#endif + + temp_streamFile = setup_fsb_interleave_streamfile(streamFile, fsb->stream_offset, fsb->stream_size, layers, i, FSB_INT_CELT); + if (!temp_streamFile) goto fail; + + if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) { + goto fail; + } + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + close_streamfile(temp_streamFile); + return data; + +fail: + close_streamfile(temp_streamFile); + free_layout_layered(data); + return NULL; +} +#endif + +/* ****************************************** */ static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size); @@ -387,7 +517,7 @@ VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE *test_streamFile = NULL; off_t subfile_start = 0x10; - size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo + size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10; /* check extensions */ if ( !check_extensions(streamFile, "fsb,wii") ) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 0a0526ac0..d06717ec6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -1,19 +1,49 @@ #include "meta.h" #include "../coding/coding.h" -#include "../util.h" +#include "../layout/layout.h" +#include "fsb5_interleave_streamfile.h" + + +typedef struct { + int total_subsongs; + int version; + int codec; + int flags; + + int channels; + int sample_rate; + int32_t num_samples; + int32_t loop_start; + int32_t loop_end; + int loop_flag; + + off_t sample_header_offset; + size_t sample_header_size; + size_t name_table_size; + size_t sample_data_size; + size_t base_header_size; + + off_t extradata_offset; + size_t extradata_size; + + off_t stream_offset; + size_t stream_size; + off_t name_offset; +} fsb5_header; + +/* ********************************************************************************** */ + +static layered_layout_data* build_layered_fsb5_celt(STREAMFILE *streamFile, fsb5_header* fsb5); +static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE *streamFile, fsb5_header* fsb5, off_t configs_offset, size_t configs_size); /* FSB5 - FMOD Studio multiplatform format */ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t StartOffset = 0, NameOffset = 0; - off_t SampleHeaderStart = 0, ExtraInfoStart = 0; - size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0, ExtraInfoSize = 0; - - uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0; - int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID; - int TotalSubsongs, TargetSubsong = streamFile->stream_index; + fsb5_header fsb5 = {0}; + int target_subsong = streamFile->stream_index; int i; + /* check extension, case insensitive */ if (!check_extensions(streamFile,"fsb")) goto fail; @@ -22,121 +52,125 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { goto fail; /* 0x00 is rare (seen in Tales from Space Vita) */ - Version = read_32bitLE(0x04,streamFile); - if (Version != 0x00 && Version != 0x01) goto fail; + fsb5.version = read_32bitLE(0x04,streamFile); + if (fsb5.version != 0x00 && fsb5.version != 0x01) goto fail; - TotalSubsongs = read_32bitLE(0x08,streamFile); - SampleHeaderLength = read_32bitLE(0x0C,streamFile); - NameTableLength = read_32bitLE(0x10,streamFile); - SampleDataLength = read_32bitLE(0x14,streamFile); - CodingID = read_32bitLE(0x18,streamFile); - /* type 0x01 - 0x1c(8): zero, 0x24(16): hash, 0x34(8): unk - * type 0x00 has an extra field (always 0?) at 0x1c */ - BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C; + fsb5.total_subsongs = read_32bitLE(0x08,streamFile); + fsb5.sample_header_size = read_32bitLE(0x0C,streamFile); + fsb5.name_table_size = read_32bitLE(0x10,streamFile); + fsb5.sample_data_size = read_32bitLE(0x14,streamFile); + fsb5.codec = read_32bitLE(0x18,streamFile); + /* version 0x01 - 0x1c(4): zero, 0x24(16): hash, 0x34(8): unk + * version 0x00 has an extra field (always 0?) at 0x1c */ + if (fsb5.version == 0x01) { + /* found by tests and assumed to be flags, no games known */ + fsb5.flags = read_32bitLE(0x20,streamFile); + } + fsb5.base_header_size = (fsb5.version==0x00) ? 0x40 : 0x3C; - if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) { - VGM_LOG("FSB5: bad size (%x + %x + %x + %x != %x)\n", SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, get_streamfile_size(streamFile)); + if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(streamFile)) { + VGM_LOG("FSB5: bad size (%x + %x + %x + %x != %x)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, get_streamfile_size(streamFile)); goto fail; } - if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */ - if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > fsb5.total_subsongs || fsb5.total_subsongs <= 0) goto fail; - SampleHeaderStart = BaseHeaderLength; + fsb5.sample_header_offset = fsb5.base_header_size; /* find target stream header and data offset, and read all needed values for later use * (reads one by one as the size of a single stream header is variable) */ - for (i = 1; i <= TotalSubsongs; i++) { - off_t DataStart = 0; - size_t StreamHeaderLength = 0; - uint32_t SampleMode1, SampleMode2; + for (i = 1; i <= fsb5.total_subsongs; i++) { + size_t stream_header_size = 0; + off_t data_offset = 0; + uint32_t sample_mode1, sample_mode2; /* maybe one uint64? */ - - /* seems ok but could use some testing against FMOD's SDK */ - SampleMode1 = (uint32_t)read_32bitLE(SampleHeaderStart+0x00,streamFile); - SampleMode2 = (uint32_t)read_32bitLE(SampleHeaderStart+0x04,streamFile); - StreamHeaderLength += 0x08; + sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x00,streamFile); + sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x04,streamFile); + stream_header_size += 0x08; /* get samples */ - NumSamples = ((SampleMode2 >> 2) & 0x3FFFFFFF); /* bits 31..2 (30) */ + fsb5.num_samples = ((sample_mode2 >> 2) & 0x3FFFFFFF); /* bits2: 31..2 (30) */ /* get offset inside data section */ - DataStart = ((SampleMode1 >> 7) & 0x1FFFFFF) << 5; /* bits 31..8 (25) * 0x20 */ - //SampleMode2 bits 1..0 part of DataStart for files larger than 0x3FFFFFE0? + /* up to 0x07FFFFFF * 0x20 = full 32b offset 0xFFFFFFE0 */ + data_offset = (((sample_mode2 & 0x03) << 25) | ((sample_mode1 >> 7) & 0x1FFFFFF)) << 5; /* bits2: 1..0 (2) | bits1: 31..8 (25) */ - /* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */ - switch ((SampleMode1 >> 5) & 0x03) { /* bits 7..6 (2) */ - case 0: ChannelCount = 1; break; - case 1: ChannelCount = 2; break; - case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */ - case 3: ChannelCount = 8; break;/* some IMA ADPCM */ - default: /* other values (ex. 10ch) are specified in the extra flags, using 0 here */ + /* get channels */ + switch ((sample_mode1 >> 5) & 0x03) { /* bits1: 7..6 (2) */ + case 0: fsb5.channels = 1; break; + case 1: fsb5.channels = 2; break; + case 2: fsb5.channels = 6; break; /* some Dark Souls 2 MPEG; some IMA ADPCM */ + case 3: fsb5.channels = 8; break; /* some IMA ADPCM */ + /* other channels (ex. 4/10/12ch) use 0 here + set extra flags */ + default: /* not possible */ goto fail; } /* get sample rate */ - switch ((SampleMode1 >> 1) & 0x0f) { /* bits 5..1 (4) */ - case 0: SampleRate = 4000; break; //??? - case 1: SampleRate = 8000; break; - case 2: SampleRate = 11000; break; - case 3: SampleRate = 11025; break; - case 4: SampleRate = 16000; break; - case 5: SampleRate = 22050; break; - case 6: SampleRate = 24000; break; - case 7: SampleRate = 32000; break; - case 8: SampleRate = 44100; break; - case 9: SampleRate = 48000; break; - case 10: SampleRate = 96000; break; //??? - default: /* probably specified in the extra flags */ - SampleRate = 44100; - break; + switch ((sample_mode1 >> 1) & 0x0f) { /* bits1: 5..1 (4) */ + case 0: fsb5.sample_rate = 4000; break; + case 1: fsb5.sample_rate = 8000; break; + case 2: fsb5.sample_rate = 11000; break; + case 3: fsb5.sample_rate = 11025; break; + case 4: fsb5.sample_rate = 16000; break; + case 5: fsb5.sample_rate = 22050; break; + case 6: fsb5.sample_rate = 24000; break; + case 7: fsb5.sample_rate = 32000; break; + case 8: fsb5.sample_rate = 44100; break; + case 9: fsb5.sample_rate = 48000; break; + case 10: fsb5.sample_rate = 96000; break; + /* other sample rates (ex. 3000/64000/192000) use 0 here + set extra flags */ + default: /* 11-15: rejected (FMOD error) */ + goto fail; } /* get extra flags */ - if (SampleMode1 & 0x01) { /* bit 0 (1) */ - uint32_t ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd; + if (sample_mode1 & 0x01) { /* bits1: 0 (1) */ + off_t extraflag_offset = fsb5.sample_header_offset+0x08; + uint32_t extraflag, extraflag_type, extraflag_size, extraflag_end; - ExtraFlagStart = SampleHeaderStart+0x08; do { - ExtraFlag = read_32bitLE(ExtraFlagStart,streamFile); - ExtraFlagType = (ExtraFlag >> 25) & 0x7F; /* bits 32..26 (7) */ - ExtraFlagSize = (ExtraFlag >> 1) & 0xFFFFFF; /* bits 25..1 (24)*/ - ExtraFlagEnd = (ExtraFlag & 0x01); /* bit 0 (1) */ + extraflag = read_32bitLE(extraflag_offset,streamFile); + extraflag_type = (extraflag >> 25) & 0x7F; /* bits 32..26 (7) */ + extraflag_size = (extraflag >> 1) & 0xFFFFFF; /* bits 25..1 (24)*/ + extraflag_end = (extraflag & 0x01); /* bit 0 (1) */ - switch(ExtraFlagType) { - case 0x01: /* Channel Info */ - ChannelCount = read_8bit(ExtraFlagStart+0x04,streamFile); + switch(extraflag_type) { + case 0x01: /* channels */ + fsb5.channels = read_8bit(extraflag_offset+0x04,streamFile); break; - case 0x02: /* Sample Rate Info */ - SampleRate = read_32bitLE(ExtraFlagStart+0x04,streamFile); + case 0x02: /* sample rate */ + fsb5.sample_rate = read_32bitLE(extraflag_offset+0x04,streamFile); break; - case 0x03: /* Loop Info */ - LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile); - if (ExtraFlagSize > 0x04) /* probably not needed */ - LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile); + case 0x03: /* loop info */ + fsb5.loop_start = read_32bitLE(extraflag_offset+0x04,streamFile); + if (extraflag_size > 0x04) /* probably not needed */ + fsb5.loop_end = read_32bitLE(extraflag_offset+0x08,streamFile); /* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ - LoopFlag = (LoopStart != 0x00); + fsb5.loop_flag = (fsb5.loop_start != 0x00); break; case 0x04: /* free comment, or maybe SFX info */ break; - //case 0x05: /* Unknown (32b) */ + //case 0x05: /* Unknown (32b) */ //todo multistream marker? // /* found in Tearaway Vita, value 0, first stream only */ // break; case 0x06: /* XMA seek table */ /* no need for it */ break; - case 0x07: /* DSP coeffs */ - ExtraInfoStart = ExtraFlagStart + 0x04; + case 0x07: /* DSP coefs */ + fsb5.extradata_offset = extraflag_offset + 0x04; break; case 0x09: /* ATRAC9 config */ - ExtraInfoStart = ExtraFlagStart + 0x04; - ExtraInfoSize = ExtraFlagSize; + fsb5.extradata_offset = extraflag_offset + 0x04; + fsb5.extradata_size = extraflag_size; break; - case 0x0a: /* XWMA data */ + case 0x0a: /* XWMA config */ + fsb5.extradata_offset = extraflag_offset + 0x04; break; case 0x0b: /* Vorbis setup ID and seek table */ - ExtraInfoStart = ExtraFlagStart + 0x04; + fsb5.extradata_offset = extraflag_offset + 0x04; /* seek table format: * 0x08: table_size (total_entries = seek_table_size / (4+4)), not counting this value; can be 0 * 0x0C: sample number (only some samples are saved in the table) @@ -148,122 +182,141 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { // /* found in some XMA2/Vorbis/FADPCM */ // break; default: - VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x + 0x04 (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); + VGM_LOG("FSB5: unknown extraflag 0x%x at %"PRIx64" + 0x04 (size 0x%x)\n", extraflag_type, (off64_t)extraflag_offset, extraflag_size); break; } - ExtraFlagStart += 0x04 + ExtraFlagSize; - StreamHeaderLength += 0x04 + ExtraFlagSize; - } while (ExtraFlagEnd != 0x00); + extraflag_offset += 0x04 + extraflag_size; + stream_header_size += 0x04 + extraflag_size; + } while (extraflag_end != 0x00); } /* stream found */ - if (i == TargetSubsong) { - StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart; + if (i == target_subsong) { + fsb5.stream_offset = fsb5.base_header_size + fsb5.sample_header_size + fsb5.name_table_size + data_offset; - /* get stream size from next stream or datasize if there is only one */ - if (i == TotalSubsongs) { - StreamSize = SampleDataLength - DataStart; - } else { - uint32_t NextSampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile); - StreamSize = (((NextSampleMode >> 7) & 0x00FFFFFF) << 5) - DataStart; + /* get stream size from next stream offset or full size if there is only one */ + if (i == fsb5.total_subsongs) { + fsb5.stream_size = fsb5.sample_data_size - data_offset; + } + else { + off_t next_data_offset; + uint32_t next_sample_mode1, next_sample_mode2; + next_sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x00,streamFile); + next_sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x04,streamFile); + next_data_offset = (((next_sample_mode2 & 0x03) << 25) | ((next_sample_mode1 >> 7) & 0x1FFFFFF)) << 5; + + fsb5.stream_size = next_data_offset - data_offset; } break; } /* continue searching */ - SampleHeaderStart += StreamHeaderLength; + fsb5.sample_header_offset += stream_header_size; } /* target stream not found*/ - if (!StartOffset || !StreamSize) goto fail; + if (!fsb5.stream_offset || !fsb5.stream_size) goto fail; /* get stream name */ - if (NameTableLength) { - NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetSubsong-1),streamFile); + if (fsb5.name_table_size) { + off_t name_suboffset = fsb5.base_header_size + fsb5.sample_header_size + 0x04*(target_subsong-1); + fsb5.name_offset = fsb5.base_header_size + fsb5.sample_header_size + read_32bitLE(name_suboffset,streamFile); } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(ChannelCount,LoopFlag); + vgmstream = allocate_vgmstream(fsb5.channels,fsb5.loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = SampleRate; - vgmstream->num_samples = NumSamples; - if (LoopFlag) { - vgmstream->loop_start_sample = LoopStart; - vgmstream->loop_end_sample = LoopEnd; + vgmstream->sample_rate = fsb5.sample_rate; + vgmstream->num_samples = fsb5.num_samples; + if (fsb5.loop_flag) { + vgmstream->loop_start_sample = fsb5.loop_start; + vgmstream->loop_end_sample = fsb5.loop_end; } - vgmstream->num_streams = TotalSubsongs; - vgmstream->stream_size = StreamSize; + vgmstream->num_streams = fsb5.total_subsongs; + vgmstream->stream_size = fsb5.stream_size; vgmstream->meta_type = meta_FSB5; - if (NameOffset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile); + if (fsb5.name_offset) + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, fsb5.name_offset,streamFile); - - /* parse codec */ - switch (CodingID) { + switch (fsb5.codec) { case 0x00: /* FMOD_SOUND_FORMAT_NONE */ goto fail; case 0x01: /* FMOD_SOUND_FORMAT_PCM8 [Anima - Gate of Memories (PC)] */ vgmstream->coding_type = coding_PCM8_U; - vgmstream->layout_type = ChannelCount == 1 ? layout_none : layout_interleave; + vgmstream->layout_type = fsb5.channels == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x01; break; - case 0x02: /* FMOD_SOUND_FORMAT_PCM16 */ - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = ChannelCount == 1 ? layout_none : layout_interleave; + case 0x02: /* FMOD_SOUND_FORMAT_PCM16 [Shantae Risky's Revenge (PC)] */ + vgmstream->coding_type = (fsb5.flags & 0x01) ? coding_PCM16BE : coding_PCM16LE; + vgmstream->layout_type = fsb5.channels == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x02; break; case 0x03: /* FMOD_SOUND_FORMAT_PCM24 */ - goto fail; /* not used */ + VGM_LOG("FSB5: FMOD_SOUND_FORMAT_PCM24 found\n"); + goto fail; case 0x04: /* FMOD_SOUND_FORMAT_PCM32 */ - goto fail; /* not used */ + VGM_LOG("FSB5: FMOD_SOUND_FORMAT_PCM32 found\n"); + goto fail; - case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT [Anima - Gate of Memories (PC)] */ + case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT [Anima: Gate of Memories (PC)] */ vgmstream->coding_type = coding_PCMFLOAT; - vgmstream->layout_type = (ChannelCount == 1) ? layout_none : layout_interleave; + vgmstream->layout_type = (fsb5.channels == 1) ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x04; break; - case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM [Sonic Boom - Fire and Ice (3DS)] */ - vgmstream->coding_type = coding_NGC_DSP_subint; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x02; - - dsp_read_coefs_be(vgmstream,streamFile,ExtraInfoStart,0x2E); + case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM [Sonic Boom: Fire and Ice (3DS)] */ + if (fsb5.flags & 0x02) { /* non-interleaved mode */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = (fsb5.stream_size / fsb5.channels); + } + else { + vgmstream->coding_type = coding_NGC_DSP_subint; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x02; + } + dsp_read_coefs_be(vgmstream,streamFile,fsb5.extradata_offset,0x2E); break; - case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */ + case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM [Skylanders] */ vgmstream->coding_type = (vgmstream->channels > 2) ? coding_FSB_IMA : coding_XBOX_IMA; vgmstream->layout_type = layout_none; break; - case 0x08: /* FMOD_SOUND_FORMAT_VAG */ - goto fail; /* not used */ + case 0x08: /* FMOD_SOUND_FORMAT_VAG [from fsbankex tests, no known games] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + if (fsb5.flags & 0x02) { /* non-interleaved mode */ + vgmstream->interleave_block_size = (fsb5.stream_size / fsb5.channels); + } + else { + vgmstream->interleave_block_size = 0x10; + } + break; - case 0x09: /* FMOD_SOUND_FORMAT_HEVAG */ + case 0x09: /* FMOD_SOUND_FORMAT_HEVAG [Guacamelee (Vita)] */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; break; #ifdef VGM_USE_FFMPEG - case 0x0A: {/* FMOD_SOUND_FORMAT_XMA */ + case 0x0A: {/* FMOD_SOUND_FORMAT_XMA [Dark Souls 2 (X360)] */ uint8_t buf[0x100]; int bytes, block_size, block_count; block_size = 0x8000; /* FSB default */ - block_count = StreamSize / block_size + (StreamSize % block_size ? 1 : 0); + block_count = fsb5.stream_size / block_size + (fsb5.stream_size % block_size ? 1 : 0); - bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, StreamSize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; - - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize); + bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, fsb5.stream_offset,fsb5.stream_size); if ( !vgmstream->codec_data ) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -272,82 +325,126 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { #endif #ifdef VGM_USE_MPEG - case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG */ + case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG [Final Fantasy X HD (PS3), Shantae Risky's Revenge (PC)] */ mpeg_custom_config cfg = {0}; cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */ - vgmstream->codec_data = init_mpeg_custom(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, fsb5.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; break; } #endif - case 0x0C: /* FMOD_SOUND_FORMAT_CELT */ - goto fail; + +#ifdef VGM_USE_CELT + case 0x0C: { /* FMOD_SOUND_FORMAT_CELT [BIT.TRIP Presents Runner2 (PC), Full Bore (PC)] */ + int is_multistream = fsb5.channels > 2; + + if (is_multistream) { + vgmstream->layout_data = build_layered_fsb5_celt(streamFile, &fsb5); + if (!vgmstream->layout_data) goto fail; + vgmstream->coding_type = coding_CELT_FSB; + vgmstream->layout_type = layout_layered; + } + else { + vgmstream->codec_data = init_celt_fsb(vgmstream->channels, CELT_0_11_0); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_CELT_FSB; + vgmstream->layout_type = layout_none; + } + break; + } +#endif #ifdef VGM_USE_ATRAC9 case 0x0D: {/* FMOD_SOUND_FORMAT_AT9 */ - atrac9_config cfg = {0}; + int is_multistream; + off_t configs_offset = fsb5.extradata_offset; + size_t configs_size = fsb5.extradata_size; - cfg.channels = vgmstream->channels; - switch(ExtraInfoSize) { - case 0x04: /* Little Big Planet 2ch (Vita), Guacamelee (Vita) */ - cfg.config_data = read_32bitBE(ExtraInfoStart,streamFile); - break; - case 0x08: /* Day of the Tentacle Remastered (Vita) */ - /* 0x00: superframe size (also in config_data) */ - cfg.config_data = read_32bitBE(ExtraInfoStart+0x04,streamFile); - break; - //case 0x0c: /* Little Big Planet 6ch (Vita) */ - // //todo: this is just 0x04 x3, in case of 4ch would be 0x08 --must improve detection - // //each stream has its own config_data (but seem to be the same), interleaves 1 super frame per stream - // break; - default: - VGM_LOG("FSB5: unknown extra info size 0x%x\n", ExtraInfoSize); - goto fail; + + /* skip frame size in newer FSBs [Day of the Tentacle Remastered (Vita), Tearaway Unfolded (PS4)] */ + if (configs_size >= 0x08 && (uint8_t)read_8bit(configs_offset, streamFile) != 0xFE) { /* ATRAC9 sync */ + configs_offset += 0x04; + configs_size -= 0x04; } - //cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; + is_multistream = (configs_size / 0x04) > 1; + + if (is_multistream) { + /* multichannel made of various streams [Little Big Planet (Vita)] */ + vgmstream->layout_data = build_layered_fsb5_atrac9(streamFile, &fsb5, configs_offset, configs_size); + if (!vgmstream->layout_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_layered; + } + else { + /* standard ATRAC9, can be multichannel [Tearaway Unfolded (PS4)] */ + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(configs_offset,streamFile); + //cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + } + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x0E: { /* FMOD_SOUND_FORMAT_XWMA [from fsbankex tests, no known games] */ + uint8_t buf[0x100]; + int bytes, format, average_bps, block_align; + + format = read_16bitBE(fsb5.extradata_offset+0x00,streamFile); + block_align = (uint16_t)read_16bitBE(fsb5.extradata_offset+0x02,streamFile); + average_bps = (uint32_t)read_32bitBE(fsb5.extradata_offset+0x04,streamFile); + /* rest: seek entries + mini seek table? */ + /* XWMA encoder only does up to 6ch (doesn't use FSB multistreams for more) */ + + bytes = ffmpeg_make_riff_xwma(buf,0x100, format, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, average_bps, block_align); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, fsb5.stream_offset,fsb5.stream_size); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } #endif - case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */ - goto fail; - #ifdef VGM_USE_VORBIS - case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS */ + case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS [Shantae Half Genie Hero (PC), Pokemon Go (iOS)] */ vorbis_custom_config cfg = {0}; cfg.channels = vgmstream->channels; cfg.sample_rate = vgmstream->sample_rate; - cfg.setup_id = read_32bitLE(ExtraInfoStart,streamFile); + cfg.setup_id = read_32bitLE(fsb5.extradata_offset,streamFile); vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_VORBIS_custom; - vgmstream->codec_data = init_vorbis_custom(streamFile, StartOffset, VORBIS_FSB, &cfg); + vgmstream->codec_data = init_vorbis_custom(streamFile, fsb5.stream_offset, VORBIS_FSB, &cfg); if (!vgmstream->codec_data) goto fail; break; } #endif - case 0x10: /* FMOD_SOUND_FORMAT_FADPCM */ + case 0x10: /* FMOD_SOUND_FORMAT_FADPCM [Dead Rising 4 (PC), Sine Mora Ex (Switch)] */ vgmstream->coding_type = coding_FADPCM; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x8c; break; default: + VGM_LOG("FSB5: unknown codec %x found\n", fsb5.codec); goto fail; } - if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset)) + if (!vgmstream_open_stream(vgmstream,streamFile,fsb5.stream_offset)) goto fail; return vgmstream; @@ -356,3 +453,144 @@ fail: close_vgmstream(vgmstream); return NULL; } + + +static layered_layout_data* build_layered_fsb5_celt(STREAMFILE *streamFile, fsb5_header* fsb5) { + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + int i, layers = (fsb5->channels+1) / 2; + size_t interleave; + + if (read_32bitBE(fsb5->stream_offset+0x00,streamFile) != 0x17C30DF3) /* FSB CELT frame ID */ + goto fail; + interleave = 0x04+0x04+read_32bitLE(fsb5->stream_offset+0x04,streamFile); /* frame size */ + + //todo unknown interleave for max quality odd channel streams (found in test files) + /* FSB5 odd channels use 2ch+2ch...+1ch streams, and the last only goes up to 0x17a, and other + * streams only use that max (doesn't happen for smaller frames, even channels, or FSB4) + * however streams other than the last seem to be padded with 0s somehow and wont work */ + if (interleave > 0x17a && (fsb5->channels % 2 == 1)) + interleave = 0x17a; + + + /* init layout */ + data = init_layout_layered(layers); + if (!data) goto fail; + + /* open each layer subfile (1/2ch CELT streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch) */ + for (i = 0; i < layers; i++) { + int layer_channels = (i+1 == layers && fsb5->channels % 2 == 1) + ? 1 : 2; /* last layer can be 1/2ch */ + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, fsb5->loop_flag); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = fsb5->sample_rate; + data->layers[i]->num_samples = fsb5->num_samples; + data->layers[i]->loop_start_sample = fsb5->loop_start; + data->layers[i]->loop_end_sample = fsb5->loop_end; + +#ifdef VGM_USE_CELT + data->layers[i]->codec_data = init_celt_fsb(layer_channels, CELT_0_11_0); + if (!data->layers[i]->codec_data) goto fail; + data->layers[i]->coding_type = coding_CELT_FSB; + data->layers[i]->layout_type = layout_none; +#else + goto fail; +#endif + + temp_streamFile = setup_fsb5_interleave_streamfile(streamFile, fsb5->stream_offset, fsb5->stream_size, layers, i, FSB5_INT_CELT, interleave); + if (!temp_streamFile) goto fail; + + if (!vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00)) + goto fail; + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + close_streamfile(temp_streamFile); + return data; + +fail: + close_streamfile(temp_streamFile); + free_layout_layered(data); + return NULL; +} + + +static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE *streamFile, fsb5_header* fsb5, off_t configs_offset, size_t configs_size) { + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + int i, layers = (configs_size / 0x04); + size_t interleave = 0; + + + /* init layout */ + data = init_layout_layered(layers); + if (!data) goto fail; + + /* open each layer subfile (2ch+2ch..+1/2ch) */ + for (i = 0; i < layers; i++) { + uint32_t config = read_32bitBE(configs_offset + 0x04*i, streamFile); + int channel_index, layer_channels; + size_t frame_size; + + + /* parse ATRAC9 config (see VGAudio docs) */ + channel_index = ((config >> 17) & 0x7); + frame_size = (((config >> 5) & 0x7FF) + 1) * (1 << ((config >> 3) & 0x2)); /* frame size * superframe index */ + if (channel_index > 2) + goto fail; /* only 1/2ch expected */ + + layer_channels = (channel_index==0) ? 1 : 2; + if (interleave == 0) + interleave = frame_size; + //todo in test files with 2ch+..+1ch interleave is off (uses some strange padding) + + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, fsb5->loop_flag); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = fsb5->sample_rate; + data->layers[i]->num_samples = fsb5->num_samples; + data->layers[i]->loop_start_sample = fsb5->loop_start; + data->layers[i]->loop_end_sample = fsb5->loop_end; + +#ifdef VGM_USE_ATRAC9 + { + atrac9_config cfg = {0}; + + cfg.channels = layer_channels; + cfg.config_data = config; + //cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data + + data->layers[i]->codec_data = init_atrac9(&cfg); + if (!data->layers[i]->codec_data) goto fail; + data->layers[i]->coding_type = coding_ATRAC9; + data->layers[i]->layout_type = layout_none; + } +#else + goto fail; +#endif + + temp_streamFile = setup_fsb5_interleave_streamfile(streamFile, fsb5->stream_offset, fsb5->stream_size, layers, i, FSB5_INT_ATRAC9, interleave); + if (!temp_streamFile) goto fail; + + if (!vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00)) + goto fail; + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + close_streamfile(temp_streamFile); + return data; + +fail: + close_streamfile(temp_streamFile); + free_layout_layered(data); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5_interleave_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb5_interleave_streamfile.h new file mode 100644 index 000000000..d9cb14c2b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5_interleave_streamfile.h @@ -0,0 +1,201 @@ +#ifndef _FSB5_INTERLEAVE_STREAMFILE_H_ +#define _FSB5_INTERLEAVE_STREAMFILE_H_ +#include "../streamfile.h" + +typedef enum { FSB5_INT_CELT, FSB5_INT_ATRAC9 } fsb_interleave_codec_t; +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 */ + fsb_interleave_codec_t codec; + size_t interleave; + int stream_count; + int stream_number; + size_t stream_size; + off_t start_offset; /* pointing to the stream's beginning */ + size_t total_size; /* size of the resulting substream */ +} fsb_interleave_io_data; + + + +/* Reads skipping other streams */ +static size_t fsb_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_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 + * (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; + data->skip_frames = data->stream_number; + } + + /* 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; + + if (offset >= data->total_size) + break; + + /* get current data */ + switch (data->codec) { + case FSB5_INT_CELT: + case FSB5_INT_ATRAC9: + data_size = data->interleave; + break; + + default: + return 0; + } + + /* 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->stream_count - 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) + break; /* 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) + break; /* 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->stream_count - 1; + } + } + + return total_read; +} + +static size_t fsb_interleave_io_size(STREAMFILE *streamfile, fsb_interleave_io_data* data) { + off_t physical_offset, max_physical_offset; + size_t total_size = 0; + int skip_frames = 0; + + if (data->total_size) + return data->total_size; + + physical_offset = data->start_offset; + max_physical_offset = physical_offset + data->stream_size; + skip_frames = data->stream_number; + + /* get size of the underlying stream, skipping other streams + * (all streams should have the same frame count) */ + while (physical_offset < max_physical_offset) { + size_t data_size; + + data_size = data->interleave; + switch(data->codec) { + case FSB5_INT_CELT: + case FSB5_INT_ATRAC9: + data_size = data->interleave; + break; + + default: + return 0; + } + + /* there may be padding at the end, so this doubles as EOF marker */ + if (data_size == 0) + break; + if (physical_offset+data_size > max_physical_offset) + break; + + /* skip frames from other streams */ + if (skip_frames) { + physical_offset += data_size; + skip_frames--; + continue; + } + + physical_offset += data_size; + total_size += data_size; + skip_frames = data->stream_count - 1; + } + + data->total_size = total_size; + return data->total_size; +} + + +/* Prepares custom IO for multistreams, interleaves 1 packet per stream */ +static STREAMFILE* setup_fsb5_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size, int stream_count, int stream_number, fsb_interleave_codec_t codec, size_t interleave) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + fsb_interleave_io_data io_data = {0}; + size_t io_data_size = sizeof(fsb_interleave_io_data); + + io_data.start_offset = start_offset; + io_data.physical_offset = start_offset; + io_data.skip_frames = stream_number; /* adjust since start_offset points to the first */ + io_data.codec = codec; + io_data.interleave = interleave; + io_data.stream_count = stream_count; + io_data.stream_number = stream_number; + io_data.stream_size = stream_size; /* full size for all streams */ + + io_data.total_size = fsb_interleave_io_size(streamFile, &io_data); /* force init, size of a single stream */ + + if (io_data.total_size == 0 || io_data.total_size > io_data.stream_size) { + VGM_LOG("FSB5 INTERLEAVE: wrong total_size %x vs %x\n", io_data.total_size,io_data.stream_size); + goto fail; + } + + /* 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, fsb_interleave_io_read,fsb_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 /* _FSB_INTERLEAVE_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_interleave_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_interleave_streamfile.h new file mode 100644 index 000000000..86db85900 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_interleave_streamfile.h @@ -0,0 +1,198 @@ +#ifndef _FSB_INTERLEAVE_STREAMFILE_H_ +#define _FSB_INTERLEAVE_STREAMFILE_H_ +#include "../streamfile.h" + +typedef enum { FSB_INT_CELT } fsb_interleave_codec_t; +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 */ + fsb_interleave_codec_t codec; + int stream_count; + int stream_number; + size_t stream_size; + off_t start_offset; /* pointing to the stream's beginning */ + size_t total_size; /* size of the resulting substream */ +} fsb_interleave_io_data; + + + +/* Reads skipping other streams */ +static size_t fsb_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_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 + * (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; + data->skip_frames = data->stream_number; + } + + /* 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; + + if (offset >= data->total_size) + break; + + /* get current data */ + switch (data->codec) { + case FSB_INT_CELT: + data_size = 0x04+0x04+read_32bitLE(data->physical_offset+0x04,streamfile); + break; + + default: + return 0; + } + + /* 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->stream_count - 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) + break; /* 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) + break; /* 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->stream_count - 1; + } + } + + return total_read; +} + +static size_t fsb_interleave_io_size(STREAMFILE *streamfile, fsb_interleave_io_data* data) { + off_t physical_offset, max_physical_offset; + size_t total_size = 0; + int skip_frames = 0; + + if (data->total_size) + return data->total_size; + + physical_offset = data->start_offset; + max_physical_offset = physical_offset + data->stream_size; + skip_frames = data->stream_number; + + /* get size of the underlying stream, skipping other streams + * (all streams should have the same frame count) */ + while (physical_offset < max_physical_offset) { + size_t data_size; + uint32_t id; + + switch(data->codec) { + case FSB_INT_CELT: + id = read_32bitBE(physical_offset+0x00,streamfile); + if (id != 0x17C30DF3) /* incorrect FSB CELT frame sync */ + data_size = 0; + else + data_size = 0x04+0x04+read_32bitLE(physical_offset+0x04,streamfile); + break; + + default: + return 0; + } + + /* there may be padding at the end, so this doubles as EOF marker */ + if (data_size == 0) + break; + + /* skip frames from other streams */ + if (skip_frames) { + physical_offset += data_size; + skip_frames--; + continue; + } + + physical_offset += data_size; + total_size += data_size; + skip_frames = data->stream_count - 1; + } + + data->total_size = total_size; + return data->total_size; +} + + +/* Prepares custom IO for multistreams, interleaves 1 packet per stream */ +static STREAMFILE* setup_fsb_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size, int stream_count, int stream_number, fsb_interleave_codec_t codec) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + fsb_interleave_io_data io_data = {0}; + size_t io_data_size = sizeof(fsb_interleave_io_data); + + io_data.start_offset = start_offset; + io_data.physical_offset = start_offset; + io_data.skip_frames = stream_number; /* adjust since start_offset points to the first */ + io_data.codec = codec; + io_data.stream_count = stream_count; + io_data.stream_number = stream_number; + io_data.stream_size = stream_size; + io_data.total_size = fsb_interleave_io_size(streamFile, &io_data); /* force init */ + + if (io_data.total_size == 0 || io_data.total_size > io_data.stream_size) { + VGM_LOG("FSB INTERLEAVE: wrong total_size %x vs %x\n", io_data.total_size,io_data.stream_size); + goto fail; + } + + /* 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, fsb_interleave_io_read,fsb_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 /* _FSB_INTERLEAVE_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gcsw.c b/Frameworks/vgmstream/vgmstream/src/meta/gcsw.c index 92690ec64..c46954d10 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gcsw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gcsw.c @@ -43,7 +43,7 @@ VGMSTREAM * init_vgmstream_gcsw(STREAMFILE *streamFile) { { int i; for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x8000); + 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/genh.c b/Frameworks/vgmstream/vgmstream/src/meta/genh.c index 70cac1066..bdadd6c8b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/genh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/genh.c @@ -197,14 +197,16 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; break; case coding_XBOX_IMA: - if (genh.codec_mode == 1) { - if (!genh.interleave) goto fail; /* creates garbage */ + if (genh.codec_mode == 1) { /* mono interleave */ coding = coding_XBOX_IMA_int; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = genh.interleave; } - else { - vgmstream->layout_type = layout_none; + else { /* 1ch mono, or stereo interleave */ + vgmstream->layout_type = genh.interleave ? layout_interleave : layout_none; + vgmstream->interleave_block_size = genh.interleave; + if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) + goto fail; /* only 2ch+..+2ch layout is known */ } break; case coding_NGC_DTK: @@ -325,6 +327,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { vgmstream->coding_type = coding; vgmstream->meta_type = meta_GENH; + vgmstream->allow_dual_stereo = 1; if ( !vgmstream_open_stream(vgmstream,streamFile,genh.start_offset) ) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c index d51ec2407..4163db820 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c @@ -2,10 +2,10 @@ #include "../layout/layout.h" #include "../coding/coding.h" -/* GSP+GSB - from Tecmo's Super Swing Golf 1 & 2 (WII), Quantum Theory (PS3/X360) */ +/* GSP+GSB - from Tecmo's Super Swing Golf 1 & 2 (Wii), Quantum Theory (PS3/X360) */ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileGSP = NULL; + STREAMFILE * streamHeader = NULL; int loop_flag, channel_count; off_t start_offset, chunk_offset, first_offset; size_t datasize; @@ -16,68 +16,70 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { if (!check_extensions(streamFile,"gsb")) goto fail; - streamFileGSP = open_streamfile_by_ext(streamFile, "gsp"); - if (!streamFileGSP) goto fail; + streamHeader = open_streamfile_by_ext(streamFile, "gsp"); + if (!streamHeader) goto fail; /* check header */ - if (read_32bitBE(0x00,streamFileGSP) != 0x47534E44) /* "GSND" */ + if (read_32bitBE(0x00,streamHeader) != 0x47534E44) /* "GSND" */ goto fail; /* 0x04: version?, 0x08: 1?, 0x0c: 0?, */ - first_offset = read_32bitBE(0x10,streamFileGSP); /* usually 0x14*/ + first_offset = read_32bitBE(0x10,streamHeader); /* usually 0x14*/ + + if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ + channel_count = read_16bitBE(chunk_offset+0x0e,streamHeader); + if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ + loop_flag = read_8bit(chunk_offset+0x0c,streamHeader); + + start_offset = 0x00; - if (!find_chunk_be(streamFileGSP, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ - channel_count = read_16bitBE(chunk_offset+0x0e,streamFileGSP); - if (!find_chunk_be(streamFileGSP, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ - loop_flag = read_8bit(chunk_offset+0x0c,streamFileGSP); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_GSP_GSB; - if (!find_chunk_be(streamFileGSP, 0x48454144,first_offset,1, &chunk_offset,NULL)) goto fail; /*"HEAD"*/ + + if (!find_chunk_be(streamHeader, 0x48454144,first_offset,1, &chunk_offset,NULL)) goto fail; /*"HEAD"*/ /* 0x00: header_size, 0x04: num_chunks */ - if (!find_chunk_be(streamFileGSP, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ + if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ /* 0x00: filesize, 0x0c: always 10?, 0x10: always 0?, 0x18: always 0? */ - datasize = read_32bitBE(chunk_offset+0x00,streamFileGSP); - codec_id = read_32bitBE(chunk_offset+0x04,streamFileGSP); - vgmstream->sample_rate = read_32bitBE(chunk_offset+0x08,streamFileGSP); - vgmstream->channels = channel_count; /* 0x0e */ - vgmstream->num_samples = read_32bitBE(chunk_offset+0x14,streamFileGSP); + datasize = read_32bitBE(chunk_offset+0x00,streamHeader); + codec_id = read_32bitBE(chunk_offset+0x04,streamHeader); + vgmstream->sample_rate = read_32bitBE(chunk_offset+0x08,streamHeader); + vgmstream->num_samples = read_32bitBE(chunk_offset+0x14,streamHeader); /* 0x1c: unk (varies with codec_id) */ - if (!find_chunk_be(streamFileGSP, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ + if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ /* 0x00+: probably volume/pan/etc */ - vgmstream->loop_start_sample = read_32bitBE(chunk_offset+0x10,streamFileGSP); - vgmstream->loop_end_sample = read_32bitBE(chunk_offset+0x14,streamFileGSP); + vgmstream->loop_start_sample = read_32bitBE(chunk_offset+0x10,streamHeader); + vgmstream->loop_end_sample = read_32bitBE(chunk_offset+0x14,streamHeader); - //if (!find_chunk_be(streamFileGSP, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) goto fail; /*"NAME"*/ + //if (!find_chunk_be(streamHeader, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) goto fail; /*"NAME"*/ /* 0x00: name_size, 0x04+: name*/ - start_offset = 0x0; - switch (codec_id) { - case 4: { /* DSP */ + case 0x04: { /* DSP [Super Swing Golf (Wii)] */ size_t block_header_size; size_t num_blocks; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_blocked_gsb; - if (!find_chunk_be(streamFileGSP, 0x47434558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"GCEX"*/ + if (!find_chunk_be(streamHeader, 0x47434558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"GCEX"*/ - //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamFileGSP); - block_header_size = read_32bitBE(chunk_offset+0x04,streamFileGSP); - num_blocks = read_32bitBE(chunk_offset+0x08,streamFileGSP); + //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamHeader); + block_header_size = read_32bitBE(chunk_offset+0x04,streamHeader); + num_blocks = read_32bitBE(chunk_offset+0x08,streamHeader); vgmstream->num_samples = (datasize - block_header_size * num_blocks) / 8 / vgmstream->channels * 14; /* 0x0c+: unk */ - dsp_read_coefs_be(vgmstream, streamFileGSP, chunk_offset+0x18, 0x30); + dsp_read_coefs_be(vgmstream, streamHeader, chunk_offset+0x18, 0x30); break; } #ifdef VGM_USE_FFMPEG - case 8: { /* ATRAC3 */ + case 0x08: { /* ATRAC3 [Quantum Theory (PS3)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[100]; int32_t bytes, block_size, encoder_delay, joint_stereo, max_samples; @@ -105,15 +107,15 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { break; } - case 9: { /* XMA2 */ + case 0x09: { /* XMA2 [Quantum Theory (PS3)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[200]; int32_t bytes; - if (!find_chunk_be(streamFileGSP, 0x584D4558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"XMEX"*/ + if (!find_chunk_be(streamHeader, 0x584D4558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"XMEX"*/ /* 0x00: fmt0x166 header (BE), 0x34: seek table */ - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, datasize, streamFileGSP, 1); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, datasize, streamHeader, 1); if (bytes <= 0) goto fail; ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize); @@ -128,18 +130,15 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { default: goto fail; } - vgmstream->meta_type = meta_GSP_GSB; - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - - close_streamfile(streamFileGSP); + close_streamfile(streamHeader); return vgmstream; fail: - if (streamFileGSP) close_streamfile(streamFileGSP); + close_streamfile(streamHeader); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/h4m.c b/Frameworks/vgmstream/vgmstream/src/meta/h4m.c index 47ec0ecec..805409776 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/h4m.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/h4m.c @@ -46,15 +46,16 @@ VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile) { /* 0x3a: unk3A (0 or 0x12) */ /* 0x3b: unk3B (0) */ channel_count = read_8bit(0x3c,streamFile); - if (read_8bit(0x3d,streamFile) != 16) /* bitdepth */ //todo Pikmin not working - goto fail; - format = read_8bit(0x3e,streamFile); /* flags? */ + if (read_8bit(0x3d,streamFile) != 16) /* bitdepth */ + goto fail; //todo Pikmin (GC) is using some kind of variable blocks + format = (uint8_t)read_8bit(0x3e,streamFile); /* flags? */ extra_tracks = read_8bit(0x3f,streamFile); sample_rate = read_32bitBE(0x40,streamFile); loop_flag = 0; - total_subsongs = extra_tracks + 1; /* tracks for languages [Pokemon Channel], or sometimes used to fake multichannel [Tales of Symphonia] */ + /* tracks for languages [Pokemon Channel], or sometimes used to fake multichannel [Tales of Symphonia] */ + total_subsongs = extra_tracks + 1; if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; @@ -66,15 +67,16 @@ VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = get_streamfile_size(streamFile) / total_subsongs; /* approx... */ - vgmstream->codec_version = format; /* for blocks */ + vgmstream->codec_config = format; /* for blocks */ vgmstream->meta_type = meta_H4M; vgmstream->layout_type = layout_blocked_h4m; switch(format & 0x7F) { case 0x00: - vgmstream->coding_type = coding_DVI_IMA; //todo H4M_IMA + vgmstream->coding_type = coding_H4M_IMA; break; - /* no games known to use this, h4m_audio_decode may decode them */ + + /* no games known to use these, h4m_audio_decode may decode them */ case 0x01: /* Uncompressed PCM */ case 0x04: /* 8-bit (A)DPCM */ default: @@ -87,16 +89,18 @@ VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile) { /* calc num_samples manually */ { + vgmstream->stream_index = target_subsong; /* extra setup for H4M */ + vgmstream->full_block_size = 0; /* extra setup for H4M */ vgmstream->next_block_offset = start_offset; do { - block_update_h4m(vgmstream->next_block_offset,vgmstream); + block_update(vgmstream->next_block_offset,vgmstream); vgmstream->num_samples += vgmstream->current_block_samples; } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + vgmstream->full_block_size = 0; /* extra cleanup for H4M */ + block_update(start_offset, vgmstream); } - block_update_h4m(start_offset, vgmstream); - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/halpst.c b/Frameworks/vgmstream/vgmstream/src/meta/halpst.c index 24e6e23cb..6b6368266 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/halpst.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/halpst.c @@ -14,8 +14,6 @@ VGMSTREAM * init_vgmstream_halpst(STREAMFILE *streamFile) { int32_t samples_l,samples_r; int32_t start_sample = 0; - size_t max_block; - /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("hps",filename_extension(filename))) goto fail; @@ -27,7 +25,7 @@ VGMSTREAM * init_vgmstream_halpst(STREAMFILE *streamFile) { /* details */ channel_count = read_32bitBE(0xc,streamFile); - max_block = read_32bitBE(0x10,streamFile)/channel_count; + /*max_block = read_32bitBE(0x10,streamFile)/channel_count;*/ if (channel_count > 2) { /* align the header length needed for the extra channels */ @@ -106,14 +104,7 @@ VGMSTREAM * init_vgmstream_halpst(STREAMFILE *streamFile) { { int i; for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename, - (i==0? - max_block+0x20: /* first buffer a bit bigger to - read block header without - inefficiency */ - max_block - ) - ); + 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/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index fcd599be5..90efd4f9b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -2,181 +2,107 @@ #include "hca_keys.h" #include "../coding/coding.h" -#define HCA_KEY_MAX_TEST_CLIPS 400 /* hopefully nobody masters files with more that a handful... */ -#define HCA_KEY_MAX_TEST_FRAMES 100 /* ~102400 samples */ -#define HCA_KEY_MAX_TEST_SAMPLES 10240 /* ~10 frames of non-blank samples */ - -static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2); +static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode); VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - uint8_t buffer[0x8000]; /* hca header buffer data (probably max ~0x400) */ - char filename[PATH_LIMIT]; - off_t start = 0; - size_t file_size = streamFile->get_size(streamFile); + hca_codec_data * hca_data = NULL; + unsigned long long keycode = 0; - int header_size; - hca_codec_data * hca_data = NULL; /* vgmstream HCA context */ - clHCA * hca; /* HCA_Decoder context */ - unsigned int ciphKey1, ciphKey2; + /* checks */ + if ( !check_extensions(streamFile, "hca")) + return NULL; + if (((uint32_t)read_32bitBE(0x00,streamFile) & 0x7f7f7f7f) != 0x48434100) /* "HCA\0", possibly masked */ + goto fail; - /* check extension, case insensitive */ - if ( !check_extensions(streamFile, "hca")) return NULL; - - - /* test/init header (find real header size first) */ - if ( file_size < 8 ) goto fail; - if ( read_streamfile(buffer, start, 8, streamFile) != 8 ) goto fail; - - header_size = clHCA_isOurFile0(buffer); - if ( header_size < 0 || header_size > 0x8000 ) goto fail; - - if ( read_streamfile(buffer, start, header_size, streamFile) != header_size ) goto fail; - if ( clHCA_isOurFile1(buffer, header_size) < 0 ) goto fail; - - - /* init vgmstream context */ - hca_data = (hca_codec_data *) calloc(1, sizeof(hca_codec_data) + clHCA_sizeof()); + /* init vgmstream and library's context, will validate the HCA */ + hca_data = init_hca(streamFile); if (!hca_data) goto fail; - //hca_data->size = file_size; - hca_data->start = 0; - hca_data->sample_ptr = clHCA_samplesPerBlock; - - /* HCA_Decoder context memory goes right after our codec data (reserved in alloc'ed) */ - hca = (clHCA *)(hca_data + 1); - - /* pre-load streamfile so the hca_data is ready before key detection */ - streamFile->get_name( streamFile, filename, sizeof(filename) ); - hca_data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!hca_data->streamfile) goto fail; - /* find decryption key in external file or preloaded list */ - { + if (hca_data->info.encryptionEnabled) { uint8_t keybuf[8]; if (read_key_file(keybuf, 8, streamFile) == 8) { - ciphKey2 = get_32bitBE(keybuf+0); - ciphKey1 = get_32bitBE(keybuf+4); + keycode = (uint64_t)get_64bitBE(keybuf+0x00); } else { - find_hca_key(hca_data, hca, buffer, header_size, &ciphKey1, &ciphKey2); + find_hca_key(hca_data, &keycode); } - } - /* init decoder with key */ - clHCA_clear(hca, ciphKey1, ciphKey2); - if ( clHCA_Decode(hca, buffer, header_size, 0) < 0 ) goto fail; - if ( clHCA_getInfo(hca, &hca_data->info) < 0 ) goto fail; + clHCA_SetKey(hca_data->handle, keycode); //maybe should be done through hca_decoder.c? + } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(hca_data->info.channelCount, hca_data->info.loopEnabled); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_HCA; vgmstream->sample_rate = hca_data->info.samplingRate; - vgmstream->num_samples = hca_data->info.blockCount * clHCA_samplesPerBlock; - vgmstream->loop_start_sample = hca_data->info.loopStart * clHCA_samplesPerBlock; - vgmstream->loop_end_sample = hca_data->info.loopEnd * clHCA_samplesPerBlock; + + vgmstream->num_samples = hca_data->info.blockCount * hca_data->info.samplesPerBlock - + hca_data->info.encoderDelay - hca_data->info.encoderPadding; + vgmstream->loop_start_sample = hca_data->info.loopStartBlock * hca_data->info.samplesPerBlock - + hca_data->info.encoderDelay + hca_data->info.loopStartDelay; + vgmstream->loop_end_sample = hca_data->info.loopEndBlock * hca_data->info.samplesPerBlock - + hca_data->info.encoderDelay + (hca_data->info.samplesPerBlock - hca_data->info.loopEndPadding); + /* After loop end CRI's encoder removes the rest of the original samples and puts some + * garbage in the last frame that should be ignored. Optionally it can encode fully preserving + * the file too, but it isn't detectable, so we'll allow the whole thing just in case */ + //if (vgmstream->loop_end_sample && vgmstream->num_samples > vgmstream->loop_end_sample) + // vgmstream->num_samples = vgmstream->loop_end_sample; vgmstream->coding_type = coding_CRI_HCA; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_HCA; - vgmstream->codec_data = hca_data; return vgmstream; fail: - free(hca_data); + free_hca(hca_data); return NULL; } -/* Tries to find the decryption key from a list. Simply decodes a few frames and checks if there aren't too many - * clipped samples, as it's common for invalid keys (though possible with valid keys in poorly mastered files). */ -static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2) { - sample *testbuf = NULL, *temp; - int i, j, bufsize = 0, tempsize; - size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info); +/* Try to find the decryption key from a list. */ +static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode) { + const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info); + unsigned long long best_keycode; + int best_score = -1; + int i; - int min_clip_count = -1; - /* defaults to PSO2 key, most common */ - unsigned int best_key2 = 0xCC554639; - unsigned int best_key1 = 0x30DBE1AB; + best_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */ /* find a candidate key */ for (i = 0; i < keys_length; i++) { - int clip_count = 0, sample_count = 0; - int f = 0, s; + int score; + unsigned long long keycode = (unsigned long long)hcakey_list[i].key; - unsigned int key1, key2; - uint64_t key = hcakey_list[i].key; - key2 = (key >> 32) & 0xFFFFFFFF; - key1 = (key >> 0) & 0xFFFFFFFF; + score = test_hca_key(hca_data, keycode); + //;VGM_LOG("HCA: test key=%08x%08x, score=%i\n", + // (uint32_t)((keycode >> 32) & 0xFFFFFFFF), (uint32_t)(keycode & 0xFFFFFFFF), score); - /* re-init HCA with the current key as buffer becomes invalid (probably can be simplified) */ - hca_data->curblock = 0; - hca_data->sample_ptr = clHCA_samplesPerBlock; - if ( read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile) != header_size ) continue; + /* wrong key */ + if (score < 0) + continue; - clHCA_clear(hca, key1, key2); - if (clHCA_Decode(hca, buffer, header_size, 0) < 0) continue; - if (clHCA_getInfo(hca, &hca_data->info) < 0) continue; - if (hca_data->info.channelCount > 32) continue; /* nonsense don't alloc too much */ - - tempsize = sizeof(sample) * clHCA_samplesPerBlock * hca_data->info.channelCount; - if (tempsize > bufsize) { /* should happen once */ - temp = (sample *)realloc(testbuf, tempsize); - if (!temp) goto end; - testbuf = temp; - bufsize = tempsize; + /* score 0 is not trustable, update too if something better is found */ + if (best_score < 0 || score < best_score || (best_score == 0 && score == 1)) { + best_score = score; + best_keycode = keycode; } - /* test enough frames, but not too many */ - while (f < HCA_KEY_MAX_TEST_FRAMES && f < hca_data->info.blockCount) { - j = clHCA_samplesPerBlock; - decode_hca(hca_data, testbuf, j, hca_data->info.channelCount); - - j *= hca_data->info.channelCount; - for (s = 0; s < j; s++) { - if (testbuf[s] != 0x0000 && testbuf[s] != 0xFFFF) - sample_count++; /* ignore upper/lower blank samples */ - - if (testbuf[s] == 0x7FFF || testbuf[s] == 0x8000) - clip_count++; /* upper/lower clip */ - } - - if (clip_count > HCA_KEY_MAX_TEST_CLIPS) - break; /* too many, don't bother */ - if (sample_count >= HCA_KEY_MAX_TEST_SAMPLES) - break; /* enough non-blank samples tested */ - - f++; + /* best possible score */ + if (score == 1) { + break; } - - if (min_clip_count < 0 || clip_count < min_clip_count) { - min_clip_count = clip_count; - best_key2 = key2; - best_key1 = key1; - } - - if (min_clip_count == 0) - break; /* can't get better than this */ - - /* a few clips is normal, but some invalid keys may give low numbers too */ - //if (clip_count < 10) - // break; } - /* reset HCA */ - hca_data->curblock = 0; - hca_data->sample_ptr = clHCA_samplesPerBlock; - read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile); + //;VGM_LOG("HCA: best key=%08x%08x (score=%i)\n", + // (uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score); -end: - VGM_ASSERT(min_clip_count > 0, "HCA: best key=%08x%08x (clips=%i)\n", best_key2,best_key1, min_clip_count); - *out_key2 = best_key2; - *out_key1 = best_key1; - free(testbuf);//free(temp); + VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n", + (uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score); + *out_keycode = best_keycode; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index f80c82243..811302c67 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -235,10 +235,10 @@ static const hcakey_info hcakey_list[] = { {7335633962698440504}, // 65CD683924EE7F38 // Princess Connect Re:Dive (iOS/Android/PC) - {3201512}, // 000000000030D9E8 + {3201512}, // 000000000030D9E8 // PriPara: All Idol Perfect Stage (Takara Tomy) [Switch] - {217735759}, // 000000000CFA624F + {217735759}, // 000000000CFA624F // Space Invaders Extreme (Taito Corporation, Backbone Entertainment) [PC] {91380310056}, // 0000001546B0E028 @@ -246,6 +246,9 @@ static const hcakey_info hcakey_list[] = { // CR Another God Hades Advent (Universal Entertainment Corporation) [iOS/Android] {64813795}, // 0000000003DCFAE3 + // Onsen Musume: Yunohana Kore Kushon (Android) voices + {6667}, // 0000000000001A0B + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hd3_bd3.c b/Frameworks/vgmstream/vgmstream/src/meta/hd3_bd3.c new file mode 100644 index 000000000..1fbad671f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/hd3_bd3.c @@ -0,0 +1,115 @@ +#include "meta.h" +#include "../coding/coding.h" + + /* HD3+BD3 - Sony PS3 bank format [Elevator Action Deluxe (PS3), R-Type Dimensions (PS3)] */ +VGMSTREAM * init_vgmstream_hd3_bd3(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + off_t start_offset; + int channel_count, loop_flag, sample_rate; + size_t interleave, stream_size; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "bd3")) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile,"hd3"); + if (!streamHeader) goto fail; + + if (read_32bitBE(0x00,streamHeader) != 0x50334844) /* "P3HD" */ + goto fail; + + /* 0x04: section size (not including first 0x08) */ + /* 0x08: version? 0x00020000 */ + /* 0x10: "P3PG" offset (seems mostly empty and contains number of subsongs towards the end) */ + /* 0x14: "P3TN" offset (some kind of config?) */ + /* 0x18: "P3VA" offset (VAG headers) */ + { + off_t section_offset = read_32bitBE(0x18,streamHeader); + off_t header_offset; + size_t section_size; + int i, entries, is_bgm = 0; + + if (read_32bitBE(section_offset+0x00,streamHeader) != 0x50335641) /* "P3VA" */ + goto fail; + section_size = read_32bitBE(section_offset+0x04,streamHeader); /* (not including first 0x08) */ + /* 0x08 always 0x10? */ + entries = read_32bitBE(section_offset+0x14,streamHeader) + 1; + if (entries != (section_size-0x18) / 0x10) + goto fail; + + /* autodetect use of N bank entries as channels [Elevator Action Deluxe (PS3)] */ + if (entries > 1) { + size_t channel_size = read_32bitBE(section_offset+0x20+0x08,streamHeader); + is_bgm = 1; + + for (i = 1; i < entries; i++) { + size_t next_size = read_32bitBE(section_offset+0x20+(i*0x10)+0x08,streamHeader); + if (channel_size != next_size) { + is_bgm = 0; + break; + } + } + } + + if (is_bgm) { + total_subsongs = 1; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + header_offset = section_offset+0x20+(0)*0x10; + start_offset = read_32bitBE(header_offset+0x00,streamHeader); /* 0x00 */ + sample_rate = read_32bitBE(header_offset+0x04,streamHeader); + stream_size = get_streamfile_size(streamFile); + if (read_32bitBE(header_offset+0x0c,streamHeader) != -1) + goto fail; + + channel_count = entries; + interleave = stream_size / channel_count; + } + else { + total_subsongs = entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + header_offset = section_offset+0x20+(target_subsong-1)*0x10; + start_offset = read_32bitBE(header_offset+0x00,streamHeader); + sample_rate = read_32bitBE(header_offset+0x04,streamHeader); + stream_size = read_32bitBE(header_offset+0x08,streamHeader); + if (read_32bitBE(header_offset+0x0c,streamHeader) != -1) + goto fail; + + channel_count = 1; + interleave = 0; + } + } + + 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_HD3_BD3; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = interleave; + + 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/idsp.c b/Frameworks/vgmstream/vgmstream/src/meta/idsp.c deleted file mode 100644 index 82f79a5d3..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/idsp.c +++ /dev/null @@ -1,249 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* "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; - off_t start_offset; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("idsp",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x69647370 || /* "idsp" */ - read_32bitBE(0xBC,streamFile) != 0x49445350) /* IDSP */ - goto fail; - - loop_flag = read_32bitBE(0x20,streamFile); - channel_count = read_32bitBE(0xC4,streamFile); - - if (channel_count > 8) - goto fail; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - 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; - } - 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); - } - } - - 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); - } - } - } - } - - /* 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; -} - -/* IDSP (Mario Strikers Charged) - - Single "IDSP" header... */ -VGMSTREAM * init_vgmstream_idsp3(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 1; - int channel_count; - off_t start_offset; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("idsp",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* IDSP */ - goto fail; - - channel_count = read_32bitBE(0x24,streamFile); - - if (channel_count > 8) - goto fail; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - start_offset = (channel_count*0x60)+0xC; - vgmstream->sample_rate = read_32bitBE(0x14,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitBE(0x0C,streamFile)); - } - - vgmstream->interleave_block_size = read_32bitBE(0x04,streamFile); - 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) { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x28+i*2,streamFile); - } - if (vgmstream->channels) { - for (i=0;i<16;i++) { - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x88+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; -} - -/* IDSP (Defender NGC) */ -VGMSTREAM * init_vgmstream_idsp4(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 0; - int channel_count; - off_t start_offset; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("idsp",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ - goto fail; - - channel_count = read_32bitBE(0x0C,streamFile); - - if (channel_count > 2) // Refuse everything else for now - goto fail; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - start_offset = 0x70; - vgmstream->sample_rate = read_32bitBE(0x08,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitBE(0x04,streamFile)/channel_count/8*14; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitBE(0x04,streamFile)/channel_count/8*14; - } - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } - 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); - } - } - - /* 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/idsp_ie.c b/Frameworks/vgmstream/vgmstream/src/meta/idsp_ie.c new file mode 100644 index 000000000..30fccc323 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/idsp_ie.c @@ -0,0 +1,44 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* IDSP - from Inevitable Entertainment games [Defender (GC)] */ +VGMSTREAM * init_vgmstream_idsp_ie(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"idsp") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + goto fail; + + loop_flag = 0; + channel_count = read_32bitBE(0x0C,streamFile); + if (channel_count > 2) goto fail; + start_offset = 0x70; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_IDSP_IE; + vgmstream->sample_rate = read_32bitBE(0x08,streamFile); + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->num_samples = dsp_bytes_to_samples(read_32bitBE(0x04,streamFile), channel_count); + + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0x10,streamFile); + dsp_read_coefs_be(vgmstream,streamFile,0x14,0x2E); + + 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/ivaud.c b/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c index 96efa6701..8a85a5567 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c @@ -73,10 +73,6 @@ VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,ivaud.stream_offset)) goto fail; - - if (vgmstream->layout_type == layout_blocked_ivaud) - block_update_ivaud(ivaud.stream_offset, vgmstream); - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kma9.c b/Frameworks/vgmstream/vgmstream/src/meta/kma9.c index 16f2e7835..e1049cd73 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/kma9.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/kma9.c @@ -1,20 +1,21 @@ #include "meta.h" #include "../coding/coding.h" +#include "kma9_streamfile.h" -/* KMA9 - Koei Tecmo's custom ATRAC9 [Nobunaga no Yabou - Souzou (Vita)] */ + +/* KMA9 - Koei Tecmo's interleaved ATRAC9 [Nobunaga no Yabou - Souzou (Vita)] */ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE* temp_streamFile = NULL; off_t start_offset; - size_t stream_size; + size_t stream_size, interleave; int loop_flag, channel_count; int total_subsongs = 0, target_subsong = streamFile->stream_index; - /* check extension */ + /* checks */ if ( !check_extensions(streamFile,"km9") ) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x4B4D4139) /* "KMA9" */ goto fail; @@ -26,6 +27,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* 0x0c: unknown */ + interleave = read_32bitLE(0x10,streamFile); /* 1 superframe */ stream_size = read_32bitLE(0x14,streamFile); /* per subsong */ @@ -45,14 +47,9 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { { atrac9_config cfg = {0}; - cfg.type = ATRAC9_KMA9; cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(0x5c,streamFile); cfg.encoder_delay = read_32bitLE(0x20,streamFile); - - cfg.interleave_skip = read_32bitLE(0x10,streamFile); /* 1 superframe */ - cfg.subsong_skip = total_subsongs; - start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); + cfg.config_data = read_32bitBE(0x5c,streamFile); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; @@ -63,17 +60,24 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { vgmstream->loop_start_sample -= cfg.encoder_delay; //vgmstream->loop_end_sample -= cfg.encoder_delay; } + + /* KMA9 interleaves one ATRAC9 frame per subsong */ + temp_streamFile = setup_kma9_streamfile(streamFile, start_offset, stream_size, interleave, (target_subsong-1), total_subsongs); + if (!temp_streamFile) goto fail; + start_offset = 0; } #else goto fail; #endif /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if ( !vgmstream_open_stream(vgmstream, temp_streamFile, start_offset) ) goto fail; + close_streamfile(temp_streamFile); return vgmstream; fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kma9_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/kma9_streamfile.h new file mode 100644 index 000000000..34f21dde3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/kma9_streamfile.h @@ -0,0 +1,148 @@ +#ifndef _KM9_STREAMFILE_H_ +#define _KM9_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + int stream_number; + int stream_count; + size_t interleave_size; + off_t stream_offset; + size_t stream_size; + + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + + size_t skip_size; /* size to skip from a block start to reach data start */ + size_t data_size; /* logical size of the block */ + + size_t logical_size; +} kma9_io_data; + + +static size_t kma9_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, kma9_io_data* data) { + size_t total_read = 0; + + /* ignore bad reads */ + if (offset < 0 || offset > data->logical_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->data_size = 0; + } + + /* read blocks, one at a time */ + while (length > 0) { + + /* ignore EOF */ + if (data->logical_offset >= data->logical_size) { + break; + } + + /* process new block */ + if (data->data_size == 0) { + data->skip_size = data->interleave_size * data->stream_number; + data->data_size = data->interleave_size; + } + + /* move to next block */ + if (offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->interleave_size*data->stream_count; + data->logical_offset += data->data_size; + data->data_size = 0; + continue; + } + + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_offset; + + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + + offset += bytes_done; + total_read += bytes_done; + length -= bytes_done; + dest += bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + + } + + return total_read; +} + +static size_t kma9_io_size(STREAMFILE *streamfile, kma9_io_data* data) { + off_t physical_offset = data->stream_offset; + off_t max_physical_offset = get_streamfile_size(streamfile); + size_t logical_size = 0; + + if (data->logical_size) + return data->logical_size; + + /* get size of the logical stream */ + while (physical_offset < max_physical_offset) { + logical_size += data->interleave_size; + physical_offset += data->interleave_size*data->stream_count; + } + + if (logical_size > max_physical_offset) + return 0; + if (logical_size != data->stream_size) + return 0; + data->logical_size = logical_size; + return data->logical_size; +} + + +/* Prepares custom IO for KMA9, which interleaves ATRAC9 frames */ +static STREAMFILE* setup_kma9_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, size_t interleave_size, int stream_number, int stream_count) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + kma9_io_data io_data = {0}; + size_t io_data_size = sizeof(kma9_io_data); + + io_data.stream_number = stream_number; + io_data.stream_count = stream_count; + io_data.stream_offset = stream_offset; + io_data.stream_size = stream_size; + io_data.interleave_size = interleave_size; + io_data.physical_offset = stream_offset; + io_data.logical_size = kma9_io_size(streamFile, &io_data); /* force init */ + + if (io_data.logical_size == 0) { + VGM_LOG("KMA9: wrong logical size\n"); + goto fail; + } + + /* 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, kma9_io_read,kma9_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + + +#endif /* _KM9_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kraw.c b/Frameworks/vgmstream/vgmstream/src/meta/kraw.c index 2fd4123ef..a14a2146c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/kraw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/kraw.c @@ -37,6 +37,7 @@ VGMSTREAM * init_vgmstream_kraw(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_KRAW; + vgmstream->allow_dual_stereo = 1; /* open the file for reading */ { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c index 2f2bccc6f..76339eb71 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c @@ -1,6 +1,4 @@ #include "meta.h" -#include "../util.h" -#include "../stack_alloc.h" #include "../coding/coding.h" VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index c92046e10..f8b084461 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -3,8 +3,6 @@ #include "../vgmstream.h" -VGMSTREAM * init_vgmstream_3ds_idsp(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile); @@ -26,16 +24,34 @@ VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_wii_was(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_dsp_vag(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_rfrm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile); @@ -59,18 +75,18 @@ VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_raw(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_str(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile); @@ -109,8 +125,6 @@ typedef struct { } ogg_vorbis_meta_info_t; 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 VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile); @@ -129,9 +143,9 @@ VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start VGMSTREAM * init_vgmstream_akb_mp4(STREAMFILE *streamFile); #endif -VGMSTREAM * init_vgmstream_sfl(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_sfl_ogg(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_bmdx(STREAMFILE * streamFile); @@ -150,7 +164,6 @@ VGMSTREAM * init_vgmstream_ivb(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_svs(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_riff(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_rifx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xnb(STREAMFILE * streamFile); @@ -234,8 +247,6 @@ VGMSTREAM * init_vgmstream_aix(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_tydsp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_capdsp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xbox_wvs(STREAMFILE *streamFile); @@ -262,17 +273,15 @@ VGMSTREAM * init_vgmstream_dc_idvi(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_rnd(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_wii_idsp(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_kraw(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_omu(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_xa2(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_idsp2(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_idsp3(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_idsp4(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_idsp_ie(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_ymf(STREAMFILE * streamFile); @@ -385,8 +394,6 @@ VGMSTREAM * init_vgmstream_ps2_vsf_tta(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ads(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_wii_str(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ps2_mcg(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_zsd(STREAMFILE *streamFile); @@ -397,10 +404,6 @@ VGMSTREAM * init_vgmstream_RedSpark(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile); - -VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ps2_sps(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_xa2_rrp(STREAMFILE *streamFile); @@ -437,8 +440,6 @@ VGMSTREAM * init_vgmstream_exakt_sc(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_wii_bns(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_wii_was(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_pona_3do(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_pona_psx(STREAMFILE* streamFile); @@ -454,8 +455,6 @@ VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_dmsg(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ngc_dsp_konami(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_ster(STREAMFILE* streamFile); @@ -480,28 +479,16 @@ VGMSTREAM * init_vgmstream_pc_smp(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ngc_rkv(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_p3d(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ngc_dsp_mpds(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str1(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str2(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str3(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ps2_b1s(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_wad(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE* streamFile); - -VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE* streamFile); @@ -516,8 +503,6 @@ VGMSTREAM * init_vgmstream_bar(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ffw(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ps2_jstm(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_xvag(STREAMFILE* streamFile); @@ -532,8 +517,6 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps3_past(STREAMFILE* streamFile); @@ -571,8 +554,6 @@ VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sf0(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps3_klbs(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_tun(STREAMFILE* streamFile); @@ -661,7 +642,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ea_hdr(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_idx_big(STREAMFILE * steeamFile); VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile); @@ -678,6 +660,7 @@ VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_nlsd(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile); @@ -694,6 +677,8 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE * streamFile); @@ -735,8 +720,6 @@ 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); @@ -754,12 +737,60 @@ VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile); - -VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_xmd(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_wv6(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_wavebatch(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_hd3_bd3(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_scd_sscf(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_a2m(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_ahv(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_msv(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_sdf_ps2(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_sdf_3ds(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_svg(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_vis(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_vai(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_aif_asobo(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_ao(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_apc(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_wv2(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_xau_konami(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_derf(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_utk(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_adpcm_capcom(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mib_mih.c b/Frameworks/vgmstream/vgmstream/src/meta/mib_mih.c new file mode 100644 index 000000000..b3f3f6383 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/mib_mih.c @@ -0,0 +1,60 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */ +VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + off_t start_offset; + size_t data_size, frame_size, frame_last, frame_count; + int channel_count, loop_flag; + + /* check extension */ + if (!check_extensions(streamFile, "mib")) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile,"mih"); + if (!streamHeader) goto fail; + + if (read_32bitBE(0x00,streamHeader) != 0x40000000) /* header size */ + goto fail; + + loop_flag = 0; /* MIB+MIH don't PS-ADPCM loop flags */ + channel_count = read_32bitLE(0x08,streamHeader); + start_offset = 0x00; + + /* 0x04: padding (0x20, MIH header must be multiple of 0x40) */ + frame_last = (uint16_t)read_16bitLE(0x05,streamHeader); + frame_size = read_32bitLE(0x10,streamHeader); + frame_count = read_32bitLE(0x14,streamHeader); + if (frame_count == 0) { /* rarely [Gladius (PS2)] */ + frame_count = get_streamfile_size(streamFile) / (frame_size * channel_count); + } + + data_size = frame_count * frame_size; + data_size -= frame_last ? (frame_size-frame_last) : 0; /* last frame has less usable data */ + data_size *= channel_count; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x0C,streamHeader); + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + + vgmstream->meta_type = meta_PS2_MIB_MIH; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = frame_size; + + 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 e3596daec..d04a30d2f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mss.c @@ -9,46 +9,48 @@ VGMSTREAM * init_vgmstream_mss(STREAMFILE *streamFile) { size_t data_size; int loop_flag = 0, channel_count; - /* check extension, case insensitive */ - if (!check_extensions(streamFile, "mss")) goto fail; - - /* check header */ + /* checks */ + if (!check_extensions(streamFile, "mss")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x4D435353) /* "MCSS" */ goto fail; loop_flag = 0; channel_count = read_16bitLE(0x16,streamFile); - if (read_32bitLE(0x18,streamFile) == 0x4800 && channel_count > 2) - channel_count = 2; //todo add support for interleave stereo streams + + /* 0x04: version? (always 0x00000100 LE) */ + start_offset = read_32bitLE(0x08,streamFile); + data_size = read_32bitLE(0x0c,streamFile); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - /* 0x04: version? (always 0x00000100 LE) */ - start_offset = read_32bitLE(0x08,streamFile); - data_size = read_32bitLE(0x0c,streamFile); vgmstream->sample_rate = read_32bitLE(0x10,streamFile); /* 0x14(1): 1/2/3/4 if 2/4/6/8ch, 0x15(1): 0/1?, 0x16: ch */ + vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = read_32bitLE(0x18,streamFile); vgmstream->num_samples = read_32bitLE(0x1C,streamFile); vgmstream->meta_type = meta_MSS; /* no other way to know */ if (vgmstream->interleave_block_size == 0x4800) { - /* interleaved stereo streams (2ch 0x4800 + 2ch 0x4800 = 4ch) */ vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_interleave; + + /* in stereo multichannel this value is distance between 2ch pair, but we need + * interleave*ch = full block (2ch 0x4800 + 2ch 0x4800 = 4ch, 0x4800+4800 / 4 = 0x2400) */ + vgmstream->interleave_block_size = vgmstream->interleave_block_size / 2; + if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) + goto fail; /* only 2ch+..+2ch layout is known */ /* header values are somehow off? */ - data_size = get_streamfile_size(streamFile); + data_size = get_streamfile_size(streamFile) - start_offset; vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); } else { /* 0x800 interleave */ vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; if (vgmstream->num_samples * vgmstream->channels <= data_size) vgmstream->num_samples = vgmstream->num_samples / 16 * 28; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msv.c b/Frameworks/vgmstream/vgmstream/src/meta/msv.c new file mode 100644 index 000000000..ff844b817 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/msv.c @@ -0,0 +1,43 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* MSV - from Sony MultiStream format [Fight Club (PS2)] */ +VGMSTREAM * init_vgmstream_msv(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t channel_size; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"msv") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4D535670) /* "MSVp" */ + goto fail; + + start_offset = 0x30; + channel_count = 1; + channel_size = read_32bitBE(0x0c,streamFile); + loop_flag = 0; /* no looping and last 16 bytes (end frame) are removed, from Sony's docs */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MSV; + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_none; + read_string(vgmstream->stream_name,0x10+1, 0x20,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/mus_acm.c b/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c index 84cfc4e21..f0a77278f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c @@ -1,7 +1,5 @@ #include "meta.h" #include "../layout/layout.h" -#include "../coding/coding.h" -#include "../coding/acm_decoder.h" #include #include #include @@ -50,9 +48,11 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { case 0x97280301: /* ACM header id [Planescape: Torment (PC)] */ data->segments[i] = init_vgmstream_acm(temp_streamFile); break; +#ifdef VGM_USE_VORBIS case 0x4F676753: /* "OggS" [Planescape: Torment Enhanced Edition (PC)] */ data->segments[i] = init_vgmstream_ogg_vorbis(temp_streamFile); break; +#endif default: data->segments[i] = NULL; break; @@ -93,9 +93,7 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { 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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c b/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c index 37d78ff22..963a8c748 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c @@ -12,8 +12,6 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) { /* .hwas: usually in archives but also found named (ex. Guitar Hero On Tour) */ if (!check_extensions(streamFile,"hwas")) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x73617768) /* "sawh" */ goto fail; @@ -27,23 +25,18 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_NDS_HWAS; vgmstream->sample_rate = read_32bitLE(0x08,streamFile); vgmstream->num_samples = ima_bytes_to_samples(read_32bitLE(0x14,streamFile), channel_count); vgmstream->loop_start_sample = ima_bytes_to_samples(read_32bitLE(0x10,streamFile), channel_count); //assumed, always 0 vgmstream->loop_end_sample = ima_bytes_to_samples(read_32bitLE(0x18,streamFile), channel_count); - vgmstream->meta_type = meta_NDS_HWAS; - vgmstream->coding_type = coding_IMA_int; 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; - - block_update_hwas(start_offset, vgmstream); - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c index ab3852c6f..c7e4d0b19 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c @@ -2,40 +2,45 @@ #include "meta.h" #include "../util.h" -/* DTK - headerless Nintendo DTK file [Harvest Moon - Another Wonderful Life (GC), XGRA (GC)] */ +/* DTK - headerless Nintendo GC 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 */ + off_t start_offset; + int channel_count, loop_flag; + /* checks */ - /* dtk: standard [XGRA (GC)], adp: standard [Harvest Moon AWL (GC)], wav/lwav: Alien Hominid (GC) */ + /* .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; - /* 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")) { + /* check valid frames as files have no header, and .adp/wav are common */ + { 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) || read_8bit(0x01 + i*0x20,streamFile) != read_8bit(0x03 + i*0x20,streamFile)) goto fail; + /* header 0x00/01 are repeated in 0x02/03 (for error correction?), + * could also test header values (upper nibble should be 0..3, and lower nibble 0..C) */ } } + /* always stereo, no loop (since it's hardware-decoded and streamed) */ + channel_count = 2; + loop_flag = 0; + start_offset = 0x00; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = get_streamfile_size(streamFile) / 32 * 28; - vgmstream->sample_rate = 48000; + vgmstream->num_samples = get_streamfile_size(streamFile) / 0x20 * 28; + vgmstream->sample_rate = 48000; /* due to a GC hardware defect this may be closer to 48043 */ vgmstream->coding_type = coding_NGC_DTK; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGC_ADPDTK; - - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; @@ -45,4 +50,3 @@ fail: 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 840812a7e..ddf4d34e6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -1,12 +1,10 @@ #include "meta.h" #include "../layout/layout.h" #include "../coding/coding.h" -#include "../util.h" -#include "../stack_alloc.h" + /* If these variables are packed properly in the struct (one after another) * then this is actually how they are laid out in the file, albeit big-endian */ - struct dsp_header { uint32_t sample_count; uint32_t nibble_count; @@ -67,179 +65,129 @@ static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFIL 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; -} - /* ********************************* */ -/* common parser config as most DSPs are basically the same with minor changes */ typedef struct { + /* basic config */ 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; + off_t header_offset; /* standard DSP header */ + size_t header_spacing; /* distance between DSP header of other channels */ + off_t start_offset; /* data start */ + size_t interleave; /* distance between data of other channels */ + size_t interleave_last; /* same, in the last block */ meta_t meta_type; + + /* hacks */ + int force_loop; /* force full loop */ + int force_loop_seconds; /* force loop, but must be longer than this (to catch jingles) */ + 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 */ } dsp_meta; #define COMMON_DSP_MAX_CHANNELS 6 + +/* Common parser for most DSPs that are basically the same with minor changes. + * Custom variants will just concatenate or interleave standard DSP headers and data, + * so we make sure to validate read vs expected values, based on dsp_meta config. */ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *dspm) { VGMSTREAM * vgmstream = NULL; + int i, j; 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; + /* load standard DSP header per channel */ + { + for (i = 0; i < dspm->channel_count; i++) { + if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, streamFile, !dspm->little_endian)) + goto fail; + } + } + + /* fix bad/fixed value in loop start */ + if (dspm->fix_loop_start) { 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; + /* check type==0 and gain==0 */ + { + for (i = 0; i < dspm->channel_count; i++) { + if (ch_header[i].format || ch_header[i].gain) + goto fail; + } + } - if (!dspm->ignore_header_agreement && !check_dsp_samples(ch_header, dspm->channel_count)) - goto fail; + /* check for agreement between channels */ + if (!dspm->ignore_header_agreement) { + for (i = 0; i < dspm->channel_count - 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; + } + } + } - 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; + /* check expected initial predictor/scale */ + { + int channels = dspm->channel_count; + if (dspm->single_header) + channels = 1; - 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; + for (i = 0; i < channels; i++) { + off_t channel_offset = dspm->start_offset + i*dspm->interleave; + if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, streamFile)) + goto fail; + } + } + + /* check expected loop predictor/scale */ + if (ch_header[0].loop_flag) { + int channels = dspm->channel_count; + if (dspm->single_header) + channels = 1; + + for (i = 0; i < channels; i++) { + off_t loop_offset = ch_header[i].loop_start_offset; + if (dspm->interleave) { + loop_offset = loop_offset / 16 * 8; + loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave); + } + + if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,streamFile)) + goto fail; + } } + /* all done, must be DSP */ + loop_flag = ch_header[0].loop_flag; - if (dspm->force_loop) + if (!loop_flag && dspm->force_loop) { loop_flag = 1; + if (dspm->force_loop_seconds && + ch_header[0].sample_count < dspm->force_loop_seconds*ch_header[0].sample_rate) { + loop_flag = 0; + } + } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(dspm->channel_count,loop_flag); @@ -258,8 +206,18 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d if (dspm->interleave == 0 || vgmstream->coding_type == coding_NGC_DSP_subint) vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = dspm->interleave; + vgmstream->interleave_last_block_size = dspm->interleave_last; - setup_vgmstream_dsp(vgmstream, ch_header); + { + /* set coefs 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; + } + } /* don't know why, but it does happen*/ if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples) @@ -292,7 +250,8 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { int i, channel_count; /* checks */ - /* .dsp: standard, .adp: Dr. Muto/Battalion Wars (GC) mono files */ + /* .dsp: standard + * .adp: Dr. Muto/Battalion Wars (GC) mono files */ if (!check_extensions(streamFile, "dsp,adp")) goto fail; @@ -349,6 +308,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_DSP_STD; + vgmstream->allow_dual_stereo = 1; /* very common in .dsp */ vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; @@ -519,9 +479,7 @@ fail: 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. */ +/* ********************************* */ /* .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) { @@ -597,75 +555,51 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { dspm.start_offset = 0xc0; streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strlen(filename) > 7 && !strcasecmp("_lr.dsp",filename+strlen(filename)-7)) { + if (strlen(filename) > 7 && !strcasecmp("_lr.dsp",filename+strlen(filename)-7)) { //todo improve dspm.interleave = 0x14180; dspm.meta_type = meta_DSP_JETTERS; /* Bomberman Jetters (GC) */ - } else if (!strcasecmp("mss",filename_extension(filename))) { + } else if (check_extensions(streamFile, "mss")) { dspm.interleave = 0x1000; dspm.meta_type = meta_DSP_MSS; /* Free Radical GC games */ /* Timesplitters 2 GC's ts2_atom_smasher_44_fx.mss differs slightly in samples but plays ok */ dspm.ignore_header_agreement = 1; - } else if (!strcasecmp("gcm",filename_extension(filename))) { + } else if (check_extensions(streamFile, "gcm")) { + /* older Traveller's Tales games [Lego Star Wars (GC), The Chronicles of Narnia (GC), Sonic R (GC)] */ dspm.interleave = 0x8000; - dspm.meta_type = meta_DSP_GCM; /* some of Traveller's Tales games */ + dspm.meta_type = meta_DSP_GCM; } else { goto fail; } - return init_vgmstream_dsp_common(streamFile, &dspm); fail: return NULL; } -/* IDSP - Namco header + interleaved dsp [SSB4 (3DS), Tekken Tag Tournament 2 (WiiU)] */ -VGMSTREAM * init_vgmstream_3ds_idsp(STREAMFILE *streamFile) { +/* IDSP - Namco header (from NUS3) + interleaved dsp [SSB4 (3DS), Tekken Tag Tournament 2 (WiiU)] */ +VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile) { dsp_meta dspm = {0}; - off_t offset; /* checks */ - if (!check_extensions(streamFile, "idsp,nus3bank")) + if (!check_extensions(streamFile, "idsp")) goto fail; - - /* try NUS3BANK container */ - if (read_32bitBE(0x00,streamFile) == 0x4E555333) { /* "NUS3" */ - int i, chunk_count; - - offset = 0x14 + read_32bitLE(0x10, streamFile); /* TOC size */ - chunk_count = read_32bitLE(0x14, streamFile); /* rarely not 7 (ex. SMB U's snd_bgm_CRS12_Simple_Result_Final) */ - - for (i = 0; i < chunk_count; i++) { - if (read_32bitBE(0x18 + i*0x08 + 0x00, streamFile) == 0x5041434B) { /* "PACK" */ - offset += 0x08; - break; /* contains "IDSP", should appear last anyway */ - } - else { - offset += 0x08 + read_32bitLE(0x18 + i*0x08 + 0x04, streamFile); - } - } - } - else { - offset = 0x00; - } - - - if (read_32bitBE(offset,streamFile) != 0x49445350) /* "IDSP" */ + if (read_32bitBE(0x00,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.channel_count = read_32bitBE(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 */ + dspm.header_offset = read_32bitBE(0x20,streamFile); + dspm.header_spacing = read_32bitBE(0x24,streamFile); + dspm.start_offset = read_32bitBE(0x28,streamFile); + dspm.interleave = read_32bitBE(0x1c,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.interleave = read_32bitBE(0x2c,streamFile); /* half interleave, use channel size */ - dspm.meta_type = meta_3DS_IDSP; + dspm.meta_type = meta_IDSP_NUS3; return init_vgmstream_dsp_common(streamFile, &dspm); fail: return NULL; @@ -696,7 +630,7 @@ fail: } /* sadf - Procyon Studio Header Variant [Xenoblade Chronicles 2 (Switch)] (sfx) */ -VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int channel_count, loop_flag; off_t start_offset; @@ -738,107 +672,6 @@ 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 start_offset, header_offset; - size_t header_spacing; - struct dsp_header ch_header[WSI_MAX_CHANNELS]; - int channel_count; - - /* checks */ - if (!check_extensions(streamFile, "wsi")) - 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); - if (channel_count != 2) goto fail; - - /* check for consistent block headers */ - { - off_t block_offset; - off_t block_size_has_been; - int i; - - block_offset = read_32bitBE(0x00,streamFile); - if (block_offset < 0x08) goto fail; - - block_size_has_been = block_offset; - - /* check 4 blocks, to get an idea */ - for (i = 0; i < 4*channel_count; i++) { - off_t block_size = read_32bitBE(block_offset,streamFile); - - 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 */ - - 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 */ - - block_offset += block_size; - } - } - - 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_header[0].loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = ch_header[0].sample_rate; - - 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 = layout_blocked_wsi; - - setup_vgmstream_dsp(vgmstream, ch_header); - - - 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: - close_vgmstream(vgmstream); - return NULL; -} - - /* SWD - PSF chunks + interleaved dsps [Conflict: Desert Storm 1 & 2] */ VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE *streamFile) { dsp_meta dspm = {0}; @@ -866,137 +699,98 @@ fail: return NULL; } -/* IDSP .gcm files, two standard DSP headers */ -/* found in: Lego Batman (Wii) - 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) - The Chronicles of Narnia - Prince Caspian (Wii) */ -VGMSTREAM * init_vgmstream_wii_idsp(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; +/* IDSP - Traveller's Tales header + interleaved dsps [Lego Batman (Wii), Lego Dimensions (Wii U)] */ +VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + int version_main, version_sub; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if ((strcasecmp("gcm",filename_extension(filename))) && - (strcasecmp("idsp",filename_extension(filename)))) - goto fail; - - /* check header magic */ - if (read_32bitBE(0x0,streamFile) != 0x49445350) goto fail; /* "IDSP" */ - - /* different versions? */ - if (read_32bitBE(0x4, streamFile) == 1 && - read_32bitBE(0x8, streamFile) == 0xc8) - { - if (read_dsp_header(&ch0_header, 0x10, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, 0x70, streamFile)) goto fail; - - start_offset = 0xd0; - } - else if (read_32bitBE(0x4, streamFile) == 2 && - read_32bitBE(0x8, streamFile) == 0xd2) - { - if (read_dsp_header(&ch0_header, 0x20, streamFile)) goto fail; - if (read_dsp_header(&ch1_header, 0x80, streamFile)) goto fail; - - 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; - - start_offset = 0xe0; - } - else goto fail; - - interleave = read_32bitBE(0xc, streamFile); - - /* 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)) + /* checks */ + /* .gcm: standard + * .idsp: header id? + * .wua: Lego Dimensions (Wii U) */ + if (!check_extensions(streamFile, "gcm,idsp,wua")) goto fail; - /* check type==0 and gain==0 */ - if (ch0_header.format || ch0_header.gain || - ch1_header.format || ch1_header.gain) + if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ 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; - 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; + version_main = read_32bitBE(0x04, streamFile); + version_sub = read_32bitBE(0x08, streamFile); /* extra check since there are other IDSPs */ + if (version_main == 0x01 && version_sub == 0xc8) { + /* Transformers: The Game (Wii) */ + dspm.channel_count = 2; + dspm.max_channels = 2; + dspm.header_offset = 0x10; + } + else if (version_main == 0x02 && version_sub == 0xd2) { + /* Lego Batman (Wii) + * The Chronicles of Narnia: Prince Caspian (Wii) + * Lego Indiana Jones 2 (Wii) + * Lego Star Wars: The Complete Saga (Wii) + * Lego Pirates of the Caribbean (Wii) + * Lego Harry Potter: Years 1-4 (Wii) */ + dspm.channel_count = 2; + dspm.max_channels = 2; + dspm.header_offset = 0x20; + /* 0x10+: null */ + } + else if (version_main == 0x03 && version_sub == 0x12c) { + /* Lego The Lord of the Rings (Wii) */ + /* Lego Dimensions (Wii U) */ + dspm.channel_count = read_32bitBE(0x10, streamFile); + dspm.max_channels = 2; + dspm.header_offset = 0x20; + /* 0x14+: "I_AM_PADDING" */ + } + else { + 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_DSP_WII_IDSP; - - /* 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_spacing = 0x60; + dspm.start_offset = dspm.header_offset + 0x60 * dspm.channel_count; + dspm.interleave = read_32bitBE(0x0c, streamFile); + dspm.meta_type = meta_IDSP_TT; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +/* IDSP - from Next Level games [Super Mario Strikers (GC), Mario Strikers: Charged (Wii)] */ +VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(streamFile, "idsp")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + goto fail; + + dspm.channel_count = 2; + dspm.max_channels = 2; + + dspm.header_offset = 0x0c; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; + dspm.interleave = read_32bitBE(0x04,streamFile); + /* 0x08: usable channel size */ + { + size_t stream_size = get_streamfile_size(streamFile); + if (read_32bitBE(stream_size - 0x04,streamFile) == 0x30303030) + stream_size -= 0x14; /* remove padding */ + stream_size -= dspm.start_offset; + + if (dspm.interleave) + dspm.interleave_last = (stream_size / dspm.channel_count) % dspm.interleave; + } + + dspm.fix_looping = 1; + dspm.force_loop = 1; + dspm.force_loop_seconds = 15; + + dspm.meta_type = meta_IDSP_NL; + return init_vgmstream_dsp_common(streamFile, &dspm); fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); return NULL; } @@ -1142,7 +936,8 @@ fail: return NULL; } -/* Cabela's series (Magic Wand dev?) - header + interleaved dsp [Cabela's Big Game Hunt 2005 Adventures (GC), Cabela's Outdoor Adventures (GC)] */ +/* 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) { dsp_meta dspm = {0}; @@ -1264,106 +1059,6 @@ 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; - int chanel_count, i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("csmp",filename_extension(filename))) goto fail; - - if (read_32bitBE(0x00, streamFile) != 0x43534D50) /* "CSMP" */ - goto fail; - if (read_32bitBE(0x04, streamFile) != 1) /* version? */ - goto fail; - - chanel_count = 1; - start_offset = 0x60; - - current_offset = 0x08; - tries = 0; - while (1) { - uint32_t chunk_id, chunk_size; - - if (tries > 4) - goto fail; - - 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; - } - - break; - } - - if (read_dsp_header(&header, current_offset, streamFile)) goto fail; - - - - /* check initial predictor/scale */ - /* Retro doesn't seem to abide by this */ - //if (header.initial_ps != (uint8_t)read_8bit(current_offset + start_offset,streamFile)) - // goto fail; - - /* check type==0 and gain==0 */ - 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 = header.loop_start_offset/16*8; -// if (header.loop_ps != (uint8_t)read_8bit(current_offset + start_offset+loop_off,streamFile)) -// goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(chanel_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; - - /* 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_CSMP; - - /* coeffs */ - 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; - - /* 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; - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - /* .mcadpcm - Custom header + full interleaved dsp [Skyrim (Switch)] */ VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile) { dsp_meta dspm = {0}; @@ -1418,3 +1113,56 @@ VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile) { fail: return NULL; } + +/* .vag - from Penny-Punching Princess (Switch) sfx */ +VGMSTREAM * init_vgmstream_dsp_vag(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(streamFile, "vag")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x08000000) /* file type? OPUSs had 09 */ + goto fail; + if (read_32bitLE(0x08,streamFile) != read_32bitLE(0x24,streamFile)) /* header has various repeated values */ + goto fail; + + dspm.channel_count = 1; + dspm.max_channels = 1; + dspm.little_endian = 1; + + dspm.header_offset = 0x1c; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; + dspm.interleave = 0; + + dspm.fix_loop_start = 1; + + dspm.meta_type = meta_DSP_VAG; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + +/* .itl - from Chanrinko Hero (GC) */ +VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(streamFile, "itl")) + goto fail; + + dspm.channel_count = 2; + dspm.max_channels = 2; + + dspm.header_offset = 0x00; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; + dspm.interleave = 0x23C0; + + dspm.fix_looping = 1; + + dspm.meta_type = meta_DSP_ITL; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_ygo.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_ygo.c index 776080188..e53905ef0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_ygo.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_ygo.c @@ -39,6 +39,7 @@ VGMSTREAM * init_vgmstream_dsp_ygo(STREAMFILE *streamFile) { vgmstream->num_samples = read_32bitBE(0x20,streamFile); vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_DSP_YGO; + vgmstream->allow_dual_stereo = 1; if (loop_flag) { vgmstream->loop_start_sample = (read_32bitBE(0x30,streamFile)*14/16); vgmstream->loop_end_sample = (read_32bitBE(0x34,streamFile)*14/16); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_lps.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_lps.c index 6957a782f..bafa5f904 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_lps.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_lps.c @@ -37,6 +37,7 @@ VGMSTREAM * init_vgmstream_ngc_lps(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGC_LPS; + vgmstream->allow_dual_stereo = 1; if (vgmstream->coding_type == coding_NGC_DSP) { int i; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_sck_dsp.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_sck_dsp.c index edf1ed678..f281bace8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_sck_dsp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_sck_dsp.c @@ -65,7 +65,7 @@ VGMSTREAM * init_vgmstream_ngc_sck_dsp(STREAMFILE *streamFile) { { for (i=0;ich[i].streamfile = streamFile->open(streamFileDSP,filenameDSP,0x8000); + vgmstream->ch[i].streamfile = streamFile->open(streamFileDSP,filenameDSP,STREAMFILE_DEFAULT_BUFFER_SIZE); vgmstream->ch[i].offset = 0; if (!vgmstream->ch[i].streamfile) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngca.c b/Frameworks/vgmstream/vgmstream/src/meta/ngca.c index 271be64ec..fb1352045 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngca.c @@ -34,6 +34,7 @@ VGMSTREAM * init_vgmstream_ngca(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGCA; + vgmstream->allow_dual_stereo = 1; if (vgmstream->coding_type == coding_NGC_DSP) { int i; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nub_idsp.c b/Frameworks/vgmstream/vgmstream/src/meta/nub_idsp.c new file mode 100644 index 000000000..8ece72d2f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/nub_idsp.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* "idsp" - from Namco's Wii NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */ +VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + /* checks */ + if ( !check_extensions(streamFile,"idsp") ) + goto fail; + + /* actual header starts at "IDSP", while "idsp" is mostly nub bank stuff */ + if (read_32bitBE(0x00,streamFile) != 0x69647370) /* "idsp" */ + goto fail; + if (read_32bitBE(0xBC,streamFile) != 0x49445350) /* "IDSP" */ + goto fail; + + loop_flag = read_32bitBE(0x20,streamFile); + channel_count = read_32bitBE(0xC4,streamFile); + if (channel_count > 8) goto fail; + + start_offset = 0x100 + (channel_count * 0x60); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_NUB_IDSP; + vgmstream->sample_rate = read_32bitBE(0xC8,streamFile); + vgmstream->num_samples = dsp_bytes_to_samples(read_32bitBE(0x14,streamFile),channel_count); + if (loop_flag) { + vgmstream->loop_start_sample = (read_32bitBE(0xD0,streamFile)); + vgmstream->loop_end_sample = (read_32bitBE(0xD4,streamFile)); + } + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0xD8,streamFile); + if (vgmstream->interleave_block_size == 0) + vgmstream->interleave_block_size = (get_streamfile_size(streamFile) - start_offset) / channel_count; + + dsp_read_coefs_be(vgmstream,streamFile,0x118,0x60); + + 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/nub_vag.c b/Frameworks/vgmstream/vgmstream/src/meta/nub_vag.c index c74393e9e..6d68746c0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nub_vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nub_vag.c @@ -32,6 +32,7 @@ VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NUB_VAG; + vgmstream->allow_dual_stereo = 1; start_offset = 0xC0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c b/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c new file mode 100644 index 000000000..f47919ae5 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c @@ -0,0 +1,208 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_nus3bank_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext); +typedef enum { /*XMA_RAW, ATRAC3,*/ IDSP, ATRAC9, OPUS, BNSF, /*PCM, XMA_RIFF*/ } nus3bank_codec; + +/* .nus3bank - Namco's newest audio container [Super Smash Bros (Wii U), idolmaster (PS4))] */ +VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t tone_offset = 0, pack_offset = 0, name_offset = 0, subfile_offset = 0; + size_t name_size = 0, subfile_size = 0; + nus3bank_codec codec; + const char* fake_ext; + int total_subsongs, target_subsong = streamFile->stream_index; + + /* checks */ + if (!check_extensions(streamFile, "nus3bank")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4E555333) /* "NUS3" */ + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x42414E4B) /* "BANK" */ + goto fail; + if (read_32bitBE(0x0c,streamFile) != 0x544F4320) /* "TOC\0" */ + goto fail; + + /* parse TOC with all existing chunks and sizes (offsets must be derived) */ + { + int i; + off_t offset = 0x14 + read_32bitLE(0x10, streamFile); /* TOC size */ + size_t chunk_count = read_32bitLE(0x14, streamFile); /* rarely not 7 (ex. SMB U's snd_bgm_CRS12_Simple_Result_Final) */ + + for (i = 0; i < chunk_count; i++) { + uint32_t chunk_id = (uint32_t)read_32bitBE(0x18+(i*0x08)+0x00, streamFile); + size_t chunk_size = (size_t)read_32bitLE(0x18+(i*0x08)+0x04, streamFile); + + switch(chunk_id) { + case 0x544F4E45: /* "TONE": stream info */ + tone_offset = 0x08 + offset; + break; + case 0x5041434B: /* "PACK": audio streams */ + pack_offset = 0x08 + offset; + break; + + case 0x50524F50: /* "PROP": project info */ + case 0x42494E46: /* "BINF": bank info (filename) */ + case 0x47525020: /* "GRP ": ? */ + case 0x44544F4E: /* "DTON": ? */ + case 0x4D41524B: /* "MARK": ? */ + case 0x4A554E4B: /* "JUNK": padding */ + default: + break; + } + + offset += 0x08 + chunk_size; + } + + if (tone_offset == 0 || pack_offset == 0) { + VGM_LOG("NUS3BANK: chunks found\n"); + goto fail; + } + } + + + /* parse tones */ + { + int i; + uint32_t codec_id = 0; + size_t entries = read_32bitLE(tone_offset+0x00, streamFile); + + /* get actual number of subsongs */ + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + for (i = 0; i < entries; i++) { + off_t header_offset, header_suboffset, stream_name_offset, stream_offset; + size_t stream_name_size, stream_size; + off_t tone_header_offset = read_32bitLE(tone_offset+0x04+(i*0x08)+0x00, streamFile); + size_t tone_header_size = read_32bitLE(tone_offset+0x04+(i*0x08)+0x04, streamFile); + + if (tone_header_size <= 0x0c) { + continue; /* ignore non-sounds */ + } + + header_offset = tone_offset + tone_header_offset; + + stream_name_size = read_8bit(header_offset+0x0c, streamFile); /* includes null */ + stream_name_offset = header_offset+0x0d; + header_suboffset = 0x0c + 0x01 + stream_name_size; + if (header_suboffset % 0x04) /* padded */ + header_suboffset += (0x04 - (header_suboffset % 0x04)); + if (read_32bitLE(header_offset+header_suboffset+0x04, streamFile) != 0x08) { + continue; /* ignore non-sounds, too */ + } + + stream_offset = read_32bitLE(header_offset+header_suboffset+0x08, streamFile) + pack_offset; + stream_size = read_32bitLE(header_offset+header_suboffset+0x0c, streamFile); + if (stream_size == 0) { + continue; /* happens in some sfx packs */ + } + + total_subsongs++; + if (total_subsongs == target_subsong) { + //;VGM_LOG("NUS3BANK: subsong header offset %lx\n", header_offset); + subfile_offset = stream_offset; + subfile_size = stream_size; + name_size = stream_name_size; + name_offset = stream_name_offset; + //todo improve, codec may be related to header values at 0x00/0x06/0x08 + codec_id = read_32bitBE(subfile_offset, streamFile); + } + /* continue counting subsongs */ + } + + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + if (subfile_offset == 0 || codec_id == 0) { + VGM_LOG("NUS3BANK: subsong not found\n"); + goto fail; + } + + switch(codec_id) { + case 0x49445350: /* "IDSP" [Super Smash Bros. for 3DS (3DS)] */ + codec = IDSP; + fake_ext = "idsp"; + break; + case 0x52494646: /* "RIFF" [idolm@ster: Platinum Stars (PS4)] */ + //todo this can be standard PCM RIFF too, ex. Mario Kart Arcade GP DX (PC) (works but should have better detection) + codec = ATRAC9; + fake_ext = "at9"; + break; + case 0x4F505553: /* "OPUS" [Taiko no Tatsujin (Switch)] */ + codec = OPUS; + fake_ext = "opus"; + break; + case 0x424E5346: /* "BNSF" [Naruto Shippuden Ultimate Ninja Storm 4 (PC)] */ + codec = BNSF; + fake_ext = "bnsf"; + break; + default: + VGM_LOG("NUS3BANK: unknown codec %x\n", codec_id); + goto fail; + } + } + + //;VGM_LOG("NUS3BANK: subfile=%lx, size=%x\n", subfile_offset, subfile_size); + + temp_streamFile = setup_nus3bank_streamfile(streamFile, subfile_offset,subfile_size, fake_ext); + if (!temp_streamFile) goto fail; + + /* init the VGMSTREAM */ + switch(codec) { + case IDSP: + vgmstream = init_vgmstream_idsp_nus3(temp_streamFile); + if (!vgmstream) goto fail; + break; + case OPUS: + vgmstream = init_vgmstream_opus_nus3(temp_streamFile); + if (!vgmstream) goto fail; + break; + case ATRAC9: + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + break; + case BNSF: + vgmstream = init_vgmstream_bnsf(temp_streamFile); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + if (name_offset) + read_string(vgmstream->stream_name,name_size, name_offset,streamFile); + + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +static STREAMFILE* setup_nus3bank_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nxa.c b/Frameworks/vgmstream/vgmstream/src/meta/nxa.c new file mode 100644 index 000000000..3c385ad2a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/nxa.c @@ -0,0 +1,58 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* Entergram NXA Opus [Higurashi no Naku Koro ni Hou (Switch)] */ +VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count; + size_t data_size, skip = 0; + + /* checks */ + if (!check_extensions(streamFile, "nxa")) + goto fail; + if (read_32bitBE(0x00, streamFile) != 0x4E584131) /* "NXA1" */ + goto fail; + + channel_count = read_16bitLE(0x10, streamFile); + skip = read_16bitLE(0x16, streamFile); + data_size = read_32bitLE(0x08, streamFile)-0x30; + start_offset = 0x30; + + /* TODO: Determine if loop points are stored externally. No visible loop points in header */ + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_NXA; + vgmstream->num_samples = read_32bitLE(0x20, streamFile); + vgmstream->sample_rate = read_32bitLE(0x0C, streamFile); + +#ifdef VGM_USE_FFMPEG + { + vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + 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/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index e4edf876e..76bd0aa5d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -265,11 +265,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { /* check extension */ - /* .ogg: standard/various, .logg: renamed for plugins, - * .adx: KID [Remember11 (PC)], + /* .ogg: standard/various, .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")) { + * .acm: Planescape Torment Enhanced Edition (PC) + * .sod: Zone 4 (PC) */ + if (check_extensions(streamFile,"ogg,logg,adx,rof,acm,sod")) { is_ogg = 1; } else if (check_extensions(streamFile,"um3")) { is_um3 = 1; @@ -285,7 +286,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { is_eno = 1; } else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */ is_gwm = 1; - } else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */ + } else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */ is_mus = 1; } else { goto fail; @@ -545,7 +546,25 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb loop_length = atol(strrchr(user_comment, '=') + 1) - loop_start; loop_length_found = 1; } - } + } + else if (strstr(user_comment, "omment=") == user_comment) { /* Air (Android) */ + sscanf(strstr(user_comment, "=LOOPSTART=") + 11, "%d,LOOPEND=%d", &loop_start, &loop_end); + loop_flag = 1; + loop_end_found = 1; + } + else if (strstr(user_comment,"MarkerNum=0002")==user_comment) { /* Megaman X Legacy Collection: MMX1/2/3 (PC) flag */ + /* uses LoopStart=-1 LoopEnd=-1, then 3 secuential comments: "MarkerNum" + "M=7F(start)" + "M=7F(end)" */ + loop_flag = 1; + } + else if (strstr(user_comment,"M=7F")==user_comment) { /* Megaman X Legacy Collection: MMX1/2/3 (PC) start/end */ + if (loop_flag && loop_start < 0) { /* LoopStart should set as -1 before */ + sscanf(user_comment,"M=7F%x", &loop_start); + } + else if (loop_flag && loop_start >= 0) { + sscanf(user_comment,"M=7F%x", &loop_end); + loop_end_found = 1; + } + } //;VGM_LOG("OGG: user_comment=%s\n", user_comment); } @@ -571,7 +590,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb vgmstream->loop_end_sample = loop_end; else vgmstream->loop_end_sample = vgmstream->num_samples; - vgmstream->loop_flag = loop_flag; if (vgmstream->loop_end_sample > vgmstream->num_samples) vgmstream->loop_end_sample = vgmstream->num_samples; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c index 06e07f6d7..125e1e9d1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -1,5 +1,4 @@ #include "meta.h" -#include "../util.h" #include "../coding/coding.h" #include "../layout/layout.h" #include "opus_interleave_streamfile.h" @@ -35,39 +34,23 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_type; 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; + vgmstream->stream_size = data_size; /* to avoid inflated sizes from fake OggS IO */ #ifdef VGM_USE_FFMPEG { - uint8_t buf[0x100]; - size_t bytes; - ffmpeg_custom_config cfg = {0}; - ffmpeg_codec_data *ffmpeg_data; - - bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate); - if (bytes <= 0) goto fail; - - 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->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - 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; + vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, vgmstream->sample_rate, streamFile) - skip; } } #else @@ -87,7 +70,7 @@ fail: /* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { - STREAMFILE * PSIFile = NULL; + STREAMFILE * PSIFile = NULL; off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0; @@ -95,23 +78,22 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { if (!check_extensions(streamFile,"opus,lopus")) goto fail; - /* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */ - /* Maybe future Arc System Works games will use this too? */ - PSIFile = open_streamfile_by_ext(streamFile, "psi"); + offset = 0x00; - offset = 0x00; - - if (PSIFile){ - num_samples = read_32bitLE(0x8C, PSIFile); - loop_start = read_32bitLE(0x84, PSIFile); - loop_end = read_32bitLE(0x88, PSIFile); - close_streamfile(PSIFile); - } - else { - num_samples = 0; - loop_start = 0; - loop_end = 0; - } + /* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */ + /* Maybe future Arc System Works games will use this too? */ + PSIFile = open_streamfile_by_ext(streamFile, "psi"); + if (PSIFile) { + num_samples = read_32bitLE(0x8C, PSIFile); + loop_start = read_32bitLE(0x84, PSIFile); + loop_end = read_32bitLE(0x88, PSIFile); + close_streamfile(PSIFile); + } + else { + num_samples = 0; + loop_start = 0; + loop_end = 0; + } return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: @@ -171,7 +153,6 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) { 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; @@ -278,17 +259,17 @@ VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE *streamFile) { int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ - if (!check_extensions(streamFile, "lopus")) + if (!check_extensions(streamFile, "opus,lopus")) goto fail; if (read_32bitBE(0x00, streamFile) != 0x4F505553) /* "OPUS" */ goto fail; - /* Here's an interesting quirk, OPUS header contains big endian values + /* Here's an interesting quirk, OPUS header contains big endian values while the Nintendo Opus header and data that follows remain little endian as usual */ offset = read_32bitBE(0x20, streamFile); num_samples = read_32bitBE(0x08, streamFile); - /* Check if there's a loop end value to determine loop_flag*/ + /* Check if there's a loop end value to determine loop_flag*/ loop_flag = read_32bitBE(0x18, streamFile); if (loop_flag) { loop_start = read_32bitBE(0x14, streamFile); @@ -314,7 +295,7 @@ VGMSTREAM * init_vgmstream_opus_nlsd(STREAMFILE *streamFile) { if (read_32bitBE(0x00, streamFile) != 0x09000000) goto fail; - offset = 0x1C; + offset = 0x1C; num_samples = read_32bitLE(0x0C, streamFile); /* Check if there's a loop_end "adjuster" value to determine loop_flag diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus_interleave_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/opus_interleave_streamfile.h index f4680f07e..503e83410 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus_interleave_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus_interleave_streamfile.h @@ -1,19 +1,19 @@ -#ifndef _OPUS_CAPCOM_STREAMFILE_H_ -#define _OPUS_CAPCOM_STREAMFILE_H_ +#ifndef _OPUS_INTERLEAVE_STREAMFILE_H_ +#define _OPUS_INTERLEAVE_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 */ + off_t stream_offset; + + /* 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 */ + + size_t logical_size; } opus_interleave_io_data; @@ -22,35 +22,33 @@ static size_t opus_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off size_t total_read = 0; /* ignore bad reads */ - if (offset < 0 || offset > data->total_size) { + if (offset < 0 || offset > data->logical_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) */ + /* previous offset: re-start as we can't map logical<>physical offsets (may be VBR) */ if (offset < data->logical_offset) { - data->physical_offset = data->start_offset; + data->physical_offset = data->stream_offset; data->logical_offset = 0x00; + data->skip_frames = 0; } /* 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; + size_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; + /* ignore EOF */ + if (data->logical_offset >= data->logical_size) { + break; } - else { - data_size += 0x08; + + /* process block (must be read every time since skip frame sizes may vary) */ + { + data_size = read_32bitBE(data->physical_offset,streamfile); + if ((uint32_t)data_size == 0x01000080) //todo not ok if offset between 0 and header_size + data_size = read_32bitLE(data->physical_offset+0x10,streamfile) + 0x08; + else + data_size += 0x08; } /* skip frames from other streams */ @@ -60,7 +58,7 @@ static size_t opus_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off continue; } - /* requested offset is outside current block, try next */ + /* move to next block */ if (offset >= data->logical_offset + data_size) { data->physical_offset += data_size; data->logical_offset += data_size; @@ -68,32 +66,24 @@ static size_t opus_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off continue; } - /* reads could fall in the middle of the block */ - intradata_offset = offset - data->logical_offset; - intrablock_offset = intradata_offset; + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; - /* 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... */ + bytes_consumed = offset - data->logical_offset; + to_read = data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + bytes_consumed, to_read, streamfile); - /* 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 */ + offset += bytes_done; + total_read += bytes_done; + length -= bytes_done; + dest += bytes_done; - 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; + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } } } @@ -103,25 +93,25 @@ static size_t opus_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off 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; + if (data->logical_size) + return data->logical_size; - info_offset = read_32bitLE(data->start_offset+0x10,streamfile); - return read_32bitLE(data->start_offset + info_offset+0x04,streamfile); + info_offset = read_32bitLE(data->stream_offset+0x10,streamfile); + data->logical_size = (0x08+info_offset) + read_32bitLE(data->stream_offset+info_offset+0x04,streamfile); + return data->logical_size; } -/* Prepares custom IO for multistream Opus, interleaves 1 packet per stream */ +/* Prepares custom IO for multistream, 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.stream_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 */ - + io_data.logical_size = opus_interleave_io_size(streamFile, &io_data); /* force init */ /* setup subfile */ new_streamFile = open_wrap_streamfile(streamFile); @@ -143,4 +133,4 @@ fail: return NULL; } -#endif /* _OPUS_CAPCOM_STREAMFILE_H_ */ +#endif /* _OPUS_INTERLEAVE_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c b/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c index 6c69b05b6..87906b9f8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c @@ -84,9 +84,7 @@ VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile) { 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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_smp.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_smp.c index 95f7ad5c9..52ab77623 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_smp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/pc_smp.c @@ -74,8 +74,7 @@ VGMSTREAM * init_vgmstream_pc_smp(STREAMFILE *streamFile) { { int i; - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, - channel_count*interleave*2); + vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[0].streamfile) goto fail; for (i=0;i #include "meta.h" -#include "../util.h" - -#ifdef WIN32 -#define DIRSEP '\\' -#else -#define DIRSEP '/' -#endif - -/* .pos is a tiny file with loop points, and the same base name as a .wav */ +/* .pos - loop points for .wav [Ys I Complete (PC); reused for manual looping] */ VGMSTREAM * init_vgmstream_pos(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileWAV = NULL; - char filename[PATH_LIMIT]; - char filenameWAV[PATH_LIMIT]; + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamData = NULL; + int32_t loop_start, loop_end; - int i; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("pos",filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "pos")) + goto fail; + if (get_streamfile_size(streamFile) != 0x08) + goto fail; - /* check for .WAV file */ - strcpy(filenameWAV,filename); - strcpy(filenameWAV+strlen(filenameWAV)-3,"wav"); - - streamFileWAV = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileWAV) { - /* try again, ucase */ - for (i=strlen(filenameWAV);i>=0&&filenameWAV[i]!=DIRSEP;i--) - filenameWAV[i]=toupper(filenameWAV[i]); - - streamFileWAV = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileWAV) goto fail; + streamData = open_streamfile_by_ext(streamFile, "wav"); + if (streamData) { + vgmstream = init_vgmstream_riff(streamData); + if (!vgmstream) goto fail; + vgmstream->meta_type = meta_RIFF_WAVE_POS; + } + else { + /* hack for Ogg with external loops */ + streamData = open_streamfile_by_ext(streamFile, "ogg"); + if (streamData) { + vgmstream = init_vgmstream_ogg_vorbis(streamData); + if (!vgmstream) goto fail; + } + else { + goto fail; + } } - /* let the real initer do the parsing */ - vgmstream = init_vgmstream_riff(streamFileWAV); - if (!vgmstream) goto fail; + close_streamfile(streamData); + streamData = NULL; - close_streamfile(streamFileWAV); - streamFileWAV = NULL; - - /* install loops */ - if (!vgmstream->loop_flag) { - vgmstream->loop_flag = 1; - vgmstream->loop_ch = calloc(vgmstream->channels, - sizeof(VGMSTREAMCHANNEL)); - if (!vgmstream->loop_ch) goto fail; + /* install loops (wrong values are validated later) */ + loop_start = read_32bitLE(0x00, streamFile); + loop_end = read_32bitLE(0x04, streamFile); + if (loop_end <= 0 || (loop_end > vgmstream->num_samples)) { + loop_end = vgmstream->num_samples; } - - vgmstream->loop_start_sample = read_32bitLE(0,streamFile); - vgmstream->loop_end_sample = read_32bitLE(4,streamFile); - vgmstream->meta_type = meta_RIFF_WAVE_POS; + vgmstream_force_loop(vgmstream, 1, loop_start, loop_end); return vgmstream; - /* clean up anything we may have opened */ fail: - if (streamFileWAV) close_streamfile(streamFileWAV); - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(streamData); + close_vgmstream(vgmstream); return NULL; -} +} \ No newline at end of file diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_adm.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_adm.c index b1c6d3dbc..b0c5b61cf 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_adm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_adm.c @@ -32,8 +32,8 @@ VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = 44100; vgmstream->meta_type = meta_PS2_ADM; + vgmstream->sample_rate = 44100; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_blocked_adm; @@ -44,19 +44,18 @@ VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_adm(vgmstream->next_block_offset,vgmstream); - + block_update(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)); + block_update(start_offset,vgmstream); if (loop_flag) vgmstream->loop_end_sample = vgmstream->num_samples; } - block_update_adm(start_offset,vgmstream); return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c index b9e6399d3..a20aedc7d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c @@ -17,8 +17,10 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) { /* .ads: actual extension * .ss2: demuxed videos (fake?) * .pcm: Taisho Mononoke Ibunroku (PS2) - * .adx: Armored Core 3 (PS2) */ - if (!check_extensions(streamFile, "ads,ss2,pcm,adx")) + * .adx: Armored Core 3 (PS2) + * [no actual extension]: MotoGP (PS2) + * .800: Mobile Suit Gundam: The One Year War (PS2) */ + if (!check_extensions(streamFile, "ads,ss2,pcm,adx,,800")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x53536864 && /* "SShd" */ @@ -196,7 +198,6 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) { loop_start_sample = loop_start; loop_end_sample = loop_end; is_loop_samples = 1; - VGM_LOG("1\n"); } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ass.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ass.c index 1253bc43b..6fe656bb8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ass.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ass.c @@ -1,92 +1,51 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* SVS (from Dai Senryaku VII - Exceed) */ +/* .ASS - from Dai Senryaku VII: Exceed (PS2) */ VGMSTREAM * init_vgmstream_ps2_ass(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - uint8_t testBuffer[0x10]; - off_t loopStart = 0; - off_t loopEnd = 0; - off_t readOffset = 0; - size_t fileLength; - int loop_flag; - int channel_count; + off_t start_offset; + size_t channel_size, interleave; + int loop_flag, channel_count, sample_rate; + int32_t num_samples, loop_start = 0, loop_end = 0; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ass",filename_extension(filename))) goto fail; - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x02000000) /* "0x02000000" */ + /* checks */ + if (!check_extensions(streamFile, "ass")) goto fail; - loop_flag = 1; - channel_count = 2; - - /* build the VGMSTREAM */ + start_offset = 0x800; + channel_count = read_32bitLE(0x00,streamFile); /* assumed */ + if (channel_count != 2) goto fail; + sample_rate = read_32bitLE(0x04,streamFile); + channel_size = read_32bitLE(0x08,streamFile); + interleave = read_32bitLE(0x0c,streamFile); + num_samples = ps_bytes_to_samples(channel_size,1); + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start, &loop_end); + loop_flag = loop_flag && (num_samples > 10*sample_rate); /* disable looping for smaller files (in seconds) */ + + + /* 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_32bitLE(0x04,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x08,streamFile)*2)*28/16/channel_count; - - fileLength = get_streamfile_size(streamFile); - - do { - - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - /* Loop Start */ - if(testBuffer[0x01]==0x06) { - if(loopStart == 0) loopStart = readOffset-0x10; - /* break; */ - } - /* Loop End */ - if(testBuffer[0x01]==0x03) { - if(loopEnd == 0) loopEnd = readOffset-0x10; - /* break; */ - } - } while (streamFile->get_offset(streamFile)<(int32_t)fileLength); - - if(loopStart == 0) { - loop_flag = 0; - vgmstream->num_samples = read_32bitLE(0x4,streamFile)*28/16/channel_count; - } else { - loop_flag = 1; - vgmstream->loop_start_sample = (loopStart-start_offset)*28/16/channel_count; - vgmstream->loop_end_sample = (loopEnd-start_offset)*28/16/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile); vgmstream->meta_type = meta_PS2_ASS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; - /* 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->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* 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_exst.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_exst.c index d3f5cea2f..68a3388e4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_exst.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_exst.c @@ -1,79 +1,68 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* EXST - - PS2 INT format is an interleaved format found in Shadow of the Colossus - The header start with a EXST id. - The headers and bgm datas was separated in the game, and joined in order - to add support for vgmstream - - The interleave value is allways 0x400 - known extensions : .STS - - 2008-05-13 - Fastelbja : First version ... -*/ +/* EXST - from Sony games [Shadow of the Colossus (PS2), Gacha Mecha Stadium Saru Battle (PS2)] */ VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + STREAMFILE * streamBody = NULL; + off_t start_offset; + int loop_flag, channel_count; + size_t block_size, num_blocks, loop_start_block; - int loop_flag=0; - int channel_count; - int i; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sts",filename_extension(filename))) goto fail; - - /* check EXST Header */ - if (read_32bitBE(0x00,streamFile) != 0x45585354) + /* checks */ + /* .sts+int: main [Shadow of the Colossus (PS2)] (some .sts have manually joined header+body) + * .x: header+body [Ape Escape 3 (PS2)] */ + if (!check_extensions(streamFile, "sts,x")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x45585354) /* "EXST" */ goto fail; - /* check loop */ - loop_flag = (read_32bitLE(0x0C,streamFile)==1); + streamBody = open_streamfile_by_ext(streamFile,"int"); + if (!streamBody) { + /* data+body joined */ + start_offset = 0x78; + if (get_streamfile_size(streamFile) < start_offset) + goto fail; + } + else { + /* body is separate */ + start_offset = 0x00; + } + + + channel_count = read_16bitLE(0x06,streamFile); + loop_flag = read_32bitLE(0x0C,streamFile) == 1; + loop_start_block = read_32bitLE(0x10,streamFile); + num_blocks = read_32bitLE(0x14,streamFile); - channel_count=read_16bitLE(0x06,streamFile); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = read_16bitLE(0x06,streamFile); vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - - /* Compression Scheme */ - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x14,streamFile)*0x400)/16*28; - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x10,streamFile)*0x400)/16*28; - vgmstream->loop_end_sample = (read_32bitLE(0x14,streamFile)*0x400)/16*28; - } - - vgmstream->interleave_block_size = 0x400; - vgmstream->layout_type = layout_interleave; vgmstream->meta_type = meta_PS2_EXST; - /* open the file for reading by each channel */ - { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x8000); + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x400; - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(0x78+vgmstream->interleave_block_size*i); - } + block_size = vgmstream->interleave_block_size * vgmstream->channels; + vgmstream->num_samples = ps_bytes_to_samples(num_blocks*block_size, channel_count); + if (vgmstream->loop_flag) { + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_block*block_size, channel_count);; + vgmstream->loop_end_sample = vgmstream->num_samples; } + if (!vgmstream_open_stream(vgmstream,streamBody ? streamBody : streamFile,start_offset)) + goto fail; + close_streamfile(streamBody); return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(streamBody); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_gbts.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_gbts.c index 0a04851be..8304b8c12 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_gbts.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_gbts.c @@ -73,7 +73,7 @@ VGMSTREAM * init_vgmstream_ps2_gbts(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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/ps2_iab.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_iab.c index 593cb4f89..edae3d814 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_iab.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_iab.c @@ -40,13 +40,13 @@ VGMSTREAM * init_vgmstream_ps2_iab(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_ps2_iab(vgmstream->next_block_offset, vgmstream); + block_update(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(start_offset, vgmstream); } - block_update_ps2_iab(start_offset, vgmstream); return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ild.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ild.c index 7ef439efa..761ea4ecb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ild.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ild.c @@ -50,7 +50,7 @@ VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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/ps2_int.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_int.c index fc623fa39..a89af591c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_int.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_int.c @@ -1,64 +1,52 @@ #include "meta.h" -#include "../util.h" - -/* INT - - PS2 INT format is a RAW 48khz PCM file without header - The only fact about those file, is that the raw is interleaved - - The interleave value is allways 0x200 - known extensions : INT - - 2008-05-11 - Fastelbja : First version ... -*/ +#include "../coding/coding.h" +/* raw PCM file assumed by extension [PaRappa The Rapper 2 (PS2)? , Amplitude (PS2)?] */ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int i,channel_count; + off_t start_offset; + int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("int",filename_extension(filename)) && - strcasecmp("wp2",filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "int,wp2")) + goto fail; - if(!strcasecmp("int",filename_extension(filename))) - channel_count = 2; - else - channel_count = 4; + if (check_extensions(streamFile, "int")) + channel_count = 2; + else + channel_count = 4; + + /* try to skip known .int (a horrible idea this parser exists) */ + { + /* ignore A2M .int */ + if (read_32bitBE(0x00,streamFile) == 0x41324D00) /* "A2M\0" */ + goto fail; + /* ignore EXST .int */ + if (read_32bitBE(0x10,streamFile) == 0x0C020000 && + read_32bitBE(0x20,streamFile) == 0x0C020000 && + read_32bitBE(0x30,streamFile) == 0x0C020000 && + read_32bitBE(0x40,streamFile) == 0x0C020000) /* check a few empty PS-ADPCM frames */ + goto fail; + } + + start_offset = 0x00; - /* ignore A2M .int */ - if (read_32bitBE(0x00,streamFile) == 0x41324D00) /* "A2M\0" */ - goto fail; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,0); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels=channel_count; vgmstream->sample_rate = 48000; - vgmstream->coding_type = coding_PCM16LE; - vgmstream->num_samples = (int32_t)(get_streamfile_size(streamFile)/(vgmstream->channels*2)); - vgmstream->interleave_block_size = 0x200; - vgmstream->layout_type = layout_interleave; vgmstream->meta_type = meta_PS2_RAW; + vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), vgmstream->channels, 16); + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x200; - /* open the file for reading by each channel */ - { - for (i=0;ichannels;i++) { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x8000); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].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); return NULL; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c index 02c51c954..4d7872d3f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c @@ -5,7 +5,8 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int channel_count, loop_flag, sample_rate, num_samples; + int channel_count, loop_flag, sample_rate; + int32_t num_samples, loop_start = 0, loop_end = 0; size_t file_size, data_size, unknown1, unknown2, interleave; @@ -20,37 +21,41 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { /* detect version */ if (data_size/2 == file_size - 0x10 - && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm */ + && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm (PS2) */ data_size = data_size / 2; interleave = 0x4000; + start_offset = 0x10; } else if (data_size/2 == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks */ + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks (PS2) */ data_size = data_size / 2; interleave = 0x8000; + start_offset = 0x10; } else if (data_size == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series */ + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series (PS2) */ interleave = 0x8000; + start_offset = 0x10; } - else if (data_size == file_size - 0x4020) { /* CT Special Forces (and all games beyond) */ + else if (data_size == file_size - 0x4020) { /* CT Special Forces (PS2), and all games beyond */ interleave = unknown1; /* always 0? */ if (!interleave) interleave = 0x10; - /* header padding contains garbage */ + start_offset = 0x4020; /* header padding contains garbage */ } else { goto fail; } - start_offset = file_size - data_size; - + //start_offset = file_size - data_size; /* also ok */ channel_count = 2; sample_rate = read_32bitLE(0x00,streamFile); num_samples = ps_bytes_to_samples(data_size, channel_count); + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, data_size, channel_count, interleave,&loop_start, &loop_end); /* most songs simply repeat except a few jingles (PS-ADPCM flags are always set) */ - loop_flag = (num_samples > 20*sample_rate); /* in seconds */ + loop_flag = loop_flag && (num_samples > 20*sample_rate); /* in seconds */ /* build the VGMSTREAM */ @@ -59,52 +64,8 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - - //todo improve, not working 100% with early .joe - { - uint8_t testBuffer[0x10]; - off_t blockOffset = 0; - off_t sampleOffset = 0; - off_t readOffset = 0; - off_t loopStart = 0, loopEnd = 0; - - readOffset = start_offset; - do { - off_t blockRead = (off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - readOffset += blockRead; - blockOffset += blockRead; - - if (blockOffset >= interleave) { - readOffset += interleave; - blockOffset -= interleave; - } - - /* Loop Start */ - if(testBuffer[0x01]==0x06) { - if(loopStart == 0) - loopStart = sampleOffset; - /* break; */ - } - - sampleOffset += 28; - - /* Loop End */ - if(testBuffer[0x01]==0x03) { - if(loopEnd == 0) - loopEnd = sampleOffset; - /* break; */ - } - - } while (streamFile->get_offset(streamFile)<(int32_t)file_size); - - if (loopStart == 0 && loopEnd == 0) { - vgmstream->loop_flag = 0; - } else { - vgmstream->loop_start_sample = loopStart; - vgmstream->loop_end_sample = loopEnd; - } - } + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mic.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mic.c index 3a1f52071..ee7c917d3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mic.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mic.c @@ -56,7 +56,7 @@ VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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/ps2_msa.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c index 22a184e11..c54146b5c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c @@ -1,37 +1,49 @@ #include "meta.h" #include "../coding/coding.h" -/* MSA - from Psyvariar -Complete Edition- (PS2) */ +/* MSA - from Sucess games [Psyvariar -Complete Edition- (PS2), Konohana Pack: 3tsu no Jikenbo (PS2)]*/ VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, data_size, file_size; + off_t start_offset; int loop_flag, channel_count; + size_t data_size, channel_size, file_size; + /* checks */ if (!check_extensions(streamFile, "msa")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x00000000) goto fail; + if (read_32bitBE(0x08,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); + data_size = read_32bitLE(0x04,streamFile); /* wrong, see below */ + channel_size = read_32bitLE(0x0c,streamFile); /* also wrong like data_size */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_PS2_MSA; vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - if (vgmstream->sample_rate == 0) /* ex. AME.MSA */ + if (vgmstream->sample_rate == 0) /* ex. Psyvariar's AME.MSA */ vgmstream->sample_rate = 44100; + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); - 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; + + if (channel_size) /* Konohana Pack */ + vgmstream->interleave_block_size = 0x6000; + else /* Psyvariar */ + vgmstream->interleave_block_size = 0x4000; + /* (could also test first frame in various interleaves, as it's always 0'ed) */ /* MSAs are strangely truncated, so manually calculate samples. * Data after last usable block is always silence or garbage. */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mtaf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mtaf.c index 0c99479a3..e760399bd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mtaf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mtaf.c @@ -79,7 +79,7 @@ VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) { streamFile->get_name(streamFile,filename,sizeof(filename)); for (i = 0; i < channel_count; i++) { - STREAMFILE * file = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + STREAMFILE * file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!file) goto fail; vgmstream->ch[i].streamfile = file; vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = start_offset + vgmstream->interleave_block_size*2*(i/2); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_p2bt.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_p2bt.c index 674177d15..a5c2d8e0c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_p2bt.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_p2bt.c @@ -51,7 +51,7 @@ VGMSTREAM * init_vgmstream_ps2_p2bt(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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/ps2_pnb.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_pnb.c index 86dc606e1..5d9d4696c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_pnb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_pnb.c @@ -47,7 +47,7 @@ VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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/ps2_psh.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh.c index cb9d3a1b7..e0b11df27 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh.c @@ -1,75 +1,72 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +#include "ps2_psh_streamfile.h" -/* PSH (from Dawn of Mana - Seiken Densetsu 4, Kingdom Hearts Re:Chain of Memories) */ -/* probably Square Vag Stream */ + +/* PSH/VSV - from Square Enix games [Dawn of Mana: Seiken Densetsu 4 (PS2), Kingdom Hearts Re:Chain of Memories (PS2)] */ VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; off_t start_offset; - uint8_t testBuffer[0x10]; - off_t loopEnd = 0; - off_t readOffset = 0; - size_t fileLength; + int loop_flag, channel_count; + size_t loop_start, adjust, data_size, interleave; - int loop_flag; - int channel_count; - /* check extension, case insensitive */ - if (!check_extensions(streamFile, "psh,vsv")) // vsv seems to be official extension + /* checks */ + /* .psh: assumed? [Romancing SaGa: Minstrel's Song (PS2)] + * .vsv: official? [Kingdom Hearts HD I.5 + II.5 ReMIX (PS4)] */ + if (!check_extensions(streamFile, "psh,vsv")) + goto fail; + /* 0x00(2): 0x0000 (RS:MS) / 0x6440 (KH:RCoM) / varies (DoM) */ + if ((uint16_t)read_16bitBE(0x02,streamFile) != 0x6400) goto fail; - /* check header */ - if (read_16bitBE(0x02,streamFile) != 0x6400) - goto fail; - - loop_flag = (read_16bitLE(0x06,streamFile)!=0); channel_count = 2; - + start_offset = 0x00; /* correct, but needs some tricks to fix sound (see below) */ + interleave = 0x800; + + adjust = (uint16_t)read_16bitLE(0x04,streamFile) & 0x7FF; /* upper bits = ??? */ + data_size = (uint16_t)read_16bitLE(0x0c,streamFile) * interleave; + /* 0x0e: ? (may be 0x0001, or a low-ish value, not related to looping?) */ + loop_start = ((uint16_t)read_16bitLE(0x06,streamFile) & 0x7FFF) * interleave; /* uper bit == loop flag? */ + loop_flag = (loop_start != 0); /* (no known files loop from beginning to end) */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0; - vgmstream->channels = channel_count; - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (uint16_t)read_16bitLE(0x0C,streamFile)*0x800*28/16/channel_count; - - // loop end is set by the loop marker which we need to find ... - // there's some extra data on unloop files, so we calculate - // the sample count with loop marker on this files - fileLength = get_streamfile_size(streamFile); - do { - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - // Loop End ... - if(testBuffer[0x01]==0x03) { - if(loopEnd==0) loopEnd = readOffset-0x10; - break; - } - } while (streamFile->get_offset(streamFile)<(int32_t)fileLength); - - if(loopEnd!=0) - vgmstream->num_samples = loopEnd*28/16/channel_count; - - if(loop_flag) { - vgmstream->loop_start_sample = - ((uint16_t)read_16bitLE(0x06,streamFile)-0x8000)*0x400*28/16; - vgmstream->loop_end_sample=vgmstream->num_samples; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x800; vgmstream->meta_type = meta_PS2_PSH; + vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count); - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start,channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; + + /* loops are odd, but comparing the audio wave with the OSTs these values seem correct */ + if (adjust == 0) { /* Romancing SaGa (PS2) */ + vgmstream->loop_start_sample -= ps_bytes_to_samples(channel_count*interleave,channel_count); /* maybe *before* loop block? */ + vgmstream->loop_start_sample -= ps_bytes_to_samples(0x200*channel_count,channel_count); /* maybe default adjust? */ + } + else { /* all others */ + vgmstream->loop_end_sample -= ps_bytes_to_samples((0x800 - adjust)*channel_count,channel_count); /* at last block + adjust is a 0x03 flag */ + } + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + temp_streamFile = setup_ps2_psh_streamfile(streamFile, start_offset, data_size); + if (!temp_streamFile) goto fail; + + if (!vgmstream_open_stream(vgmstream, temp_streamFile, start_offset)) goto fail; + + close_streamfile(temp_streamFile); return vgmstream; - /* clean up anything we may have opened */ fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh_streamfile.h new file mode 100644 index 000000000..105ce5660 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_psh_streamfile.h @@ -0,0 +1,48 @@ +#ifndef _PS2_PSH_STREAMFILE_H_ +#define _PS2_PSH_STREAMFILE_H_ +#include "../streamfile.h" + +typedef struct { + off_t null_offset; +} ps2_psh_io_data; + +static size_t ps2_psh_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ps2_psh_io_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* PSHs do start at 0x00, but first line is also the header; must null it to avoid clicks */ + if (offset < data->null_offset) { + for (i = 0; i < data->null_offset - offset; i++) { + dest[i] = 0; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_ps2_psh_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + ps2_psh_io_data io_data = {0}; + size_t io_data_size = sizeof(ps2_psh_io_data); + + io_data.null_offset = 0x10; + + /* 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, ps2_psh_io_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _PS2_PSH_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_smpl.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_smpl.c index e44bd7edd..b4a03712e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_smpl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_smpl.c @@ -8,11 +8,11 @@ VGMSTREAM * init_vgmstream_ps2_smpl(STREAMFILE *streamFile) { int loop_flag, channel_count; size_t channel_size; - /* check extension (.v0: left channel, .v1: right channel, .smpl: header id) */ + /* checks*/ + /* .v0: left channel, .v1: right channel + * .smpl: header id */ if ( !check_extensions(streamFile,"v0,v1,smpl") ) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x534D504C) /* "SMPL" */ goto fail; @@ -31,13 +31,12 @@ VGMSTREAM * init_vgmstream_ps2_smpl(STREAMFILE *streamFile) { vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_PS2_SMPL; + vgmstream->allow_dual_stereo = 1; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_none; - /* always, but can be null or used as special string */ read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); - /* 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_str.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_str.c deleted file mode 100644 index d02dcba18..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_str.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* STR - - 2008-05-19 - Fastelbja : Test version ... -*/ - -VGMSTREAM * init_vgmstream_ps2_str(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - STREAMFILE * infileSTH = NULL; - char filename[PATH_LIMIT]; - - char * filenameSTH = NULL; - - int i, channel_count, loop_flag; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("str",filename_extension(filename))) goto fail; - - /* check for .MIH file */ - filenameSTH=(char *)malloc(strlen(filename)+1); - - if (!filenameSTH) goto fail; - - strcpy(filenameSTH,filename); - strcpy(filenameSTH+strlen(filenameSTH)-3,"STH"); - - infileSTH = streamFile->open(streamFile,filenameSTH,STREAMFILE_DEFAULT_BUFFER_SIZE); - - /* STH File is necessary, so we can't confuse those file */ - /* with others .STR file as it is a very common extension */ - if (!infileSTH) goto fail; - - if(read_32bitLE(0x2C,infileSTH)==0) - goto fail; - - if((read_32bitLE(0x2C,infileSTH)==0x07) || - (read_32bitLE(0x2C,infileSTH)==0x06)) - channel_count=2; - if(read_32bitLE(0x2C,infileSTH)==0x05) - channel_count=1; - - loop_flag = read_32bitLE(0x2C,infileSTH) & 0x01; - - /* 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(0x24,infileSTH); - - vgmstream->interleave_block_size=0x4000; - - if(read_32bitLE(0x40,infileSTH)==0x01) - vgmstream->interleave_block_size = 0x8000; - - vgmstream->num_samples=read_32bitLE(0x20,infileSTH); - - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - - vgmstream->meta_type = meta_PS2_STR; - - if(loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitLE(0x20,infileSTH); - } - - close_streamfile(infileSTH); infileSTH=NULL; - - /* 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+=(off_t)(vgmstream->interleave_block_size*i); - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (infileSTH) close_streamfile(infileSTH); - if (filenameSTH) {free(filenameSTH); filenameSTH=NULL;} - if (vgmstream) 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 9e8a42c6f..83770fc47 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c @@ -2,7 +2,7 @@ #include "../layout/layout.h" #include "../coding/coding.h" -/* STR - The Bouncer (PS2) */ +/* .vs/STRx - from The Bouncer (PS2) */ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int channel_count, loop_flag; @@ -10,11 +10,11 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { /* checks */ - /* .vs: real extension (from .nam container) , .str: partial header id */ + /* .vs: real extension (from .nam container) + * .str: fake, partial header id */ if (!check_extensions(streamFile, "vs,str")) goto fail; - /* check header */ if (!(read_32bitBE(0x000,streamFile) == 0x5354524C && /* "STRL" */ read_32bitBE(0x800,streamFile) == 0x53545252) && /* "STRR" */ read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ @@ -29,11 +29,10 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_PS2_STRLR; vgmstream->sample_rate = 44100; vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_blocked_ps2_strlr; - vgmstream->meta_type = meta_PS2_STRLR; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; @@ -42,13 +41,13 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream); + block_update(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(start_offset, vgmstream); } - block_update_ps2_strlr(start_offset, vgmstream); return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c deleted file mode 100644 index 9d5303c8e..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c +++ /dev/null @@ -1,328 +0,0 @@ -#include "meta.h" - - -static int vag_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, off_t * loop_start, off_t * loop_end); - -/* VAGp - SDK format, created by various Sony's tools (like AIFF2VAG) */ -VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, loopStart = 0, loopEnd = 0; - - uint8_t vagID; - uint32_t version = 0; - - size_t filesize = 0, datasize = 0, interleave; - - int loop_flag = 0, loop_samples_found = 0; - int channel_count = 0; - int is_swag = 0; - - /* 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 */ - if (((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700) && /* "VAG" */ - ((read_32bitLE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700)) - goto fail; - - /* Frantix VAGp .swag: some (not all) fields in LE + 2 VAGp in the same file (full interleave) */ - is_swag = check_extensions(streamFile,"swag"); - - filesize = get_streamfile_size(streamFile); - - /* version used to create the file - * ex: 00000000 = v1.8 PC, 00000002 = v1.3 Mac, 00000003 = v1.6+ Mac, 00000020 = v2.0+ PC */ - version = read_32bitBE(0x04,streamFile); - /* 0x08-0c: reserved */ - if (is_swag) - datasize = read_32bitLE(0x0c,streamFile); - else - datasize = read_32bitBE(0x0c,streamFile); - /* 0x14-20 reserved */ - /* 0x20-30: name (optional) */ - /* 0x30: data start (first 0x10 usually 0s to init SPU) */ - - /* Check for correct channel count and loop flag */ - vagID=read_8bit(0x03,streamFile); - switch(vagID) { - case '1': /* "VAG1" (1 channel) [Metal Gear Solid 3] */ - channel_count=1; - break; - case '2': /* "VAG2" (2 channels) [Metal Gear Solid 3] */ - channel_count=2; - break; - case 'i': /* "VAGi" (interleaved) */ - channel_count=2; - break; - case 'V': /* pGAV (little endian / stereo) [Jak 3, Jak X] */ - if (read_32bitBE(0x20,streamFile)==0x53746572) /* "Ster" */ - channel_count=2; - else - channel_count=1; - break; - case 'p': /* "VAGp" (extended) [most common, ex Ratchet & Clank] */ - - if (check_extensions(streamFile,"vig")) { /* MX vs. ATV Untamed PS2 */ - channel_count = 2; /* normal interleave */ - loop_flag = 0; - } - else if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */ - channel_count = 2; /* The Simpsons Wrestling PSX interleave */ - loop_flag = 0; - } - else if ((version <= 0x00000004) && (datasize < filesize / 2)) { /* two VAGp in the same file */ - if (is_swag) - loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); - else - loop_flag = read_32bitBE(0x14,streamFile) != 0; - channel_count=2; - } - else if (version == 0x00020001) { /* HEVAG */ - loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); - - /* channels are usually at 0x1e, but not in Ukiyo no Roushi which has some kind - * of loop-like values instead (who designs this crap?) */ - if (read_32bitBE(0x18,streamFile) == 0 - && (read_32bitBE(0x1c,streamFile) & 0xFFFF00FF) == 0 - && read_8bit(0x1e,streamFile) < 16) { - channel_count = read_8bit(0x1e,streamFile); - if (channel_count == 0) - channel_count = 1; /* ex. early Vita vag (Lumines) */ - } - else { - channel_count = 1; - } - } - else { - loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); - channel_count = 1; - } - break; - default: - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->coding_type = coding_PSX; - if (is_swag) - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - else - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - - switch(vagID) { - case '1': // VAG1 - vgmstream->layout_type=layout_none; - vgmstream->num_samples = datasize / 16 * 28; - interleave = read_32bitLE(0x08,streamFile); - if (interleave != 0) goto fail; - vgmstream->meta_type=meta_PS2_VAG1; - start_offset=0x40; /* 0x30 is extra data in VAG1 */ - break; - case '2': // VAG2 - vgmstream->layout_type=layout_interleave; - vgmstream->num_samples = datasize / 16 * 28; /* datasize is for 1 channel only in VAG2 */ - interleave = 0x800; - vgmstream->meta_type=meta_PS2_VAG2; - start_offset=0x40; /* 0x30 is extra data in VAG2 */ - break; - case 'i': // VAGi - vgmstream->layout_type=layout_interleave; - vgmstream->num_samples = datasize / 16 * 28; - interleave = read_32bitLE(0x08,streamFile); - vgmstream->meta_type=meta_PS2_VAGi; - start_offset=0x800; - break; - case 'p': // VAGp - interleave=0x10; - - if (check_extensions(streamFile,"vig")) { /* MX vs. ATV Untamed PS2 */ - vgmstream->layout_type=layout_interleave; - vgmstream->meta_type=meta_PS2_VAGp; - - vgmstream->num_samples = (datasize - 0x10*channel_count) / 16 * 28; - start_offset = 0x800; - } - else if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* interleaved "VAGp" */ - interleave = 0x6000; /* The Simpsons Wrestling PSX interleave, includes header */ - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_PS2_VAGs; - - vgmstream->num_samples = datasize / 16 * 28; - start_offset = 0x30; - } - else if ((version == 0x00000004) && (datasize < filesize / 2)) { - vgmstream->channels=2; - vgmstream->layout_type=layout_interleave; - vgmstream->meta_type=meta_PS2_VAGs; - - if (is_swag) { - start_offset = 0x30; - interleave = datasize; - vgmstream->num_samples = datasize / 16 * 28; - vgmstream->loop_start_sample = (loopStart-start_offset) / 16 * 28; - vgmstream->loop_end_sample = (loopEnd-start_offset) / 16 * 28; - loop_samples_found = 1; - - } else { - start_offset=0x80; - vgmstream->num_samples = datasize; /* todo test if datasize/16*28? */ - if(loop_flag) { - vgmstream->loop_start_sample=read_32bitBE(0x14,streamFile); - vgmstream->loop_end_sample =read_32bitBE(0x18,streamFile); - loop_samples_found = 1; - // Double VAG Header @ 0x0000 & 0x1000 - if(read_32bitBE(0,streamFile)==read_32bitBE(0x1000,streamFile)) { - vgmstream->num_samples = datasize / 16 * 28; - interleave=0x1000; - start_offset=0; - } - } - } - } - else if (version == 0x40000000) { /* Guerilla VAG (little endian) */ - datasize = read_32bitLE(0x0c,streamFile); - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->layout_type=layout_none; - vgmstream->meta_type=meta_PS2_VAGp; - - vgmstream->num_samples = datasize / channel_count / 16 * 28; - start_offset = 0x30; - } - else if (version == 0x00020001) { /* HEVAG */ - vgmstream->coding_type = coding_HEVAG; - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_PS2_VAGs; - - vgmstream->num_samples = datasize / channel_count / 16 * 28; - start_offset = 0x30; - } - else { /* VAGp, usually separate L/R files */ - vgmstream->layout_type=layout_none; - vgmstream->meta_type=meta_PS2_VAGp; - - vgmstream->num_samples = datasize / channel_count / 16 * 28; - start_offset=0x30; - } - break; - case 'V': // pGAV - vgmstream->layout_type=layout_interleave; - interleave=0x2000; /* Jak 3 interleave, includes header */ - - if(read_32bitLE(0x1000,streamFile)==0x56414770) /* "pGAV" */ - interleave=0x1000; /* Jak X interleave, includes header */ - - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = read_32bitLE(0x0C,streamFile)/16*14; - vgmstream->meta_type=meta_PS2_pGAV; - start_offset=0; - break; - default: - goto fail; - } - - vgmstream->interleave_block_size=interleave; - - /* Don't add the header size to loop calc points */ - if(loop_flag && !loop_samples_found) { - loopStart-=start_offset; - loopEnd-=start_offset; - - vgmstream->loop_start_sample = (int32_t)((loopStart/(interleave*channel_count))*interleave)/16*28; - vgmstream->loop_start_sample += (int32_t)(loopStart%(interleave*channel_count))/16*28; - vgmstream->loop_end_sample = (int32_t)((loopEnd/(interleave*channel_count))*interleave)/16*28; - vgmstream->loop_end_sample += (int32_t)(loopEnd%(interleave*channel_count))/16*28; - } - - /* always, but can be null or used as special string */ - read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); - - - /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/** - * Finds loop points in VAG data using flag markers and updates loop_start and loop_end with the global offsets. - * - * returns 0 if not found - */ -static int vag_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, off_t * loop_start, off_t * loop_end) { - off_t loopStart = 0; - off_t loopEnd = 0; - - /* used for loop points (todo: variations: 0x0c0700..00, 0x070077..77 ) */ - /* 'used to prevent unnecessary SPU interrupts' (optional if no IRQ or no looping) */ - uint8_t eofVAG[16]={0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; - uint8_t eofVAG2[16]={0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t readbuf[16]; - uint8_t flag; - - /* Search for loop in VAG */ - size_t fileLength = get_streamfile_size(streamFile); - - - off_t readOffset = start_offset - 0x10; - do { - readOffset+=0x10; - - flag = read_8bit(readOffset+0x01,streamFile) & 0x0F; /* lower nibble (for HEVAG) */ - - // Loop Start ... - if (flag == 0x06 && !loopStart) { - loopStart = readOffset; - } - - // Loop End ... - if (flag == 0x03 && !loopEnd) { - loopEnd = readOffset; - - if (loopStart && loopEnd) - break; - } - - /* hack for some games that don't have loop points but play the same track on repeat - * (sometimes this will loop non-looping tracks incorrectly) - * if there is a "partial" 0x07 end flag pretend it wants to loop */ - if (flag == 0x01) { - // Check if we have a full eof tag after the loop point ... - // if so we don't loop, if not present, we loop from end to start ... - int read = read_streamfile(readbuf,readOffset+0x10,0x10,streamFile); - /* is there valid data after flag 0x1? */ - if (read > 0 - && readbuf[0] != 0x00 - && readbuf[0] != 0x0c - && readbuf[0] != 0x3c /* Ecco the Dolphin, Ratchet & Clank 2 */ - ) { - if (memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { /* full end flags */ - loopStart = start_offset + 0x10; /* todo proper start */ - loopEnd = readOffset; - break; - } - } - } - - } while (streamFile->get_offset(streamFile)<(off_t)fileLength); - - - if (loopStart && loopEnd) { - *loop_start = loopStart; - *loop_end = loopEnd; - return 1; - } - - return 0; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vbk.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vbk.c index 3bc44fe18..ea303ac83 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vbk.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vbk.c @@ -1,111 +1,66 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -//#include -//#include -/* VBK (from Disney's Stitch - Experiment 626) */ - -VGMSTREAM * init_vgmstream_ps2_vbk(STREAMFILE *streamFile) -{ +/* VBK - from Disney's Stitch - Experiment 626 (PS2) */ +VGMSTREAM * init_vgmstream_ps2_vbk(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - uint8_t testBuffer[0x10]; - off_t loopStart = 0; - off_t loopEnd = 0; - off_t readOffset = 0; - size_t fileLength; - int loop_flag; - int channel_count; + off_t start_offset, header_offset, stream_offset; + size_t stream_size, interleave; + int loop_flag, channel_count, sample_rate; + int32_t num_samples, loop_start = 0, loop_end = 0; + int total_subsongs, target_subsong = streamFile->stream_index; - //_TCHAR szBuffer[100]; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vbk",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x2E56424B) /* .VBK */ + /* checks */ + if (!check_extensions(streamFile, "vbk")) goto fail; + if (read_32bitBE(0x00,streamFile) != 0x2E56424B) /* ".VBK" */ + goto fail; + /* 0x04: version? always 0x02? */ + start_offset = read_32bitLE(0x0C, streamFile); + /* 0x10: file size */ - loop_flag = 1; - channel_count = read_32bitLE(0x28,streamFile) + 1; + total_subsongs = read_32bitLE(0x08,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - //_stprintf(szBuffer, _T("%x"), channel_count); - //MessageBox(NULL, szBuffer, _T("Foo"), MB_OK); + header_offset = 0x14 + (target_subsong-1)*0x18; - /* build the VGMSTREAM */ + stream_size = read_32bitLE(header_offset+0x00,streamFile); + /* 0x04: id? */ + stream_offset = read_32bitLE(header_offset+0x08,streamFile); + sample_rate = read_32bitLE(header_offset+0x0c,streamFile); + interleave = read_32bitLE(header_offset+0x10,streamFile); + channel_count = read_32bitLE(header_offset+0x14,streamFile) + 1; /* 4ch is common, 1ch sfx too */ + start_offset += stream_offset; + + num_samples = ps_bytes_to_samples(stream_size,channel_count); + loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end); + loop_flag = loop_flag && (num_samples > 10*sample_rate); /* disable looping for smaller files (in seconds) */ + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - fileLength = get_streamfile_size(streamFile); - start_offset = read_32bitLE(0x0C, streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x20,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (fileLength - start_offset)*28/16/channel_count; - - // get loop start - do { - - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - if(testBuffer[0x01]==0x06) - { - loopStart = readOffset-0x10; - break; - } - - } while (streamFile->get_offset(streamFile)<(int32_t)fileLength); - - - // get loop end - readOffset = fileLength - 0x10; - - do { - readOffset-=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - /* Loop End */ - if(testBuffer[0x01]==0x03) - { - loopEnd = readOffset-0x10; - break; - } - } while (readOffset > 0); - - loop_flag = 1; - vgmstream->loop_start_sample = (loopStart-start_offset)*28/16/channel_count; - vgmstream->loop_end_sample = (loopEnd-start_offset)*28/16/channel_count; - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); vgmstream->meta_type = meta_PS2_VBK; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; - /* 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->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; return vgmstream; - /* 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_vgs.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vgs.c index ea19558e3..9af59737f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vgs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vgs.c @@ -1,122 +1,51 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* VGS (Phantom of Inferno) - - This format is used in _many_ Princess Soft games. -*/ +/* VGS - from Princess Soft games [Gin no Eclipse (PS2), Metal Wolf REV (PS2)] */ VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - size_t fileLength; - off_t readOffset = 0; off_t start_offset; - off_t loop_start_offset = 0; - off_t loop_end_offset = 0; + size_t data_size, channel_size, interleave; + int loop_flag, channel_count; + int32_t loop_start = 0, loop_end = 0; - uint8_t testBuffer[0x10]; - int loop_flag = 0; - int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vgs",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x56475300) + /* check */ + if ( !check_extensions(streamFile,"vgs") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x56475300) /* "VGS\0" ('VAG stereo', presumably) */ goto fail; - // get file length - fileLength = get_streamfile_size(streamFile); - - // Find loop start - do { - readOffset += (off_t)read_streamfile(testBuffer, readOffset, 0x10, streamFile); - - // Loop Start ... - if(testBuffer[0x01] == 0x06) - { - loop_start_offset = readOffset - 0x10; - break; - } - - } while (streamFile->get_offset(streamFile)<((int32_t)fileLength)); - - // start at last line of file and move up - readOffset = (int32_t)fileLength - 0x10; - - // Find loop end - do { - readOffset -= (off_t)read_streamfile(testBuffer, readOffset, 0x10, streamFile); - - // Loop End ... - if((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) - { - loop_end_offset = readOffset + 0x20; - break; - } - } while (readOffset > 0); - - // setup loops - if (loop_start_offset > 0) - { - loop_flag = 1; - - // if we have a start loop, use EOF if end loop is not found - if (loop_end_offset == 0) - { - loop_end_offset = (int32_t)fileLength - 0x10; - } - } - else - { - loop_flag = 0; - } - + start_offset = 0x30; + data_size = get_streamfile_size(streamFile) - start_offset; + interleave = 0x20000; channel_count = 2; - + channel_size = read_32bitBE(0x0c,streamFile); + loop_flag = 0; /* all files have loop flags but simply fade out normally and repeat */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x30; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = ((get_streamfile_size(streamFile)-0x30)/16/channel_count*28); - - if (loop_flag) - { - vgmstream->loop_start_sample = loop_start_offset/16/channel_count*28; - vgmstream->loop_end_sample = loop_end_offset/16/channel_count*28; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x04,streamFile)*0x1000; vgmstream->meta_type = meta_PS2_VGS; + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; - /* 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->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); /* always, can be null */ + 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_vpk.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vpk.c deleted file mode 100644 index 8709809da..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vpk.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* VPK */ - -VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - int loop_flag=0; - int channel_count; - off_t start_offset; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vpk",filename_extension(filename))) goto fail; - - /* check VPK Header */ - if (read_32bitBE(0x00,streamFile) != 0x204B5056) - goto fail; - - /* check loop */ - loop_flag = (read_32bitLE(0x7FC,streamFile)!=0); - channel_count=read_32bitLE(0x14,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = read_32bitLE(0x14,streamFile); - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - - /* Check for Compression Scheme */ - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile),vgmstream->channels); - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x7FC,streamFile); - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile)/vgmstream->channels; - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_PS2_VPK; - - start_offset = (off_t)read_32bitLE(0x08,streamFile); - - /* 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= - (off_t)(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_wmus.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_wmus.c index 58f64d409..209b8e4c4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_wmus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_wmus.c @@ -82,7 +82,7 @@ VGMSTREAM * init_vgmstream_ps2_wmus(STREAMFILE *streamFile) /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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/ps3_klbs.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_klbs.c deleted file mode 100644 index 272c5c53d..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_klbs.c +++ /dev/null @@ -1,122 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* .KLBS (L@VE ONCE PS3 */ -VGMSTREAM * init_vgmstream_ps3_klbs(STREAMFILE *streamFile) -{ - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - size_t fileLength; - off_t readOffset = 0; - off_t start_offset; - off_t loop_start_offset = 0; - off_t loop_end_offset = 0; - - uint8_t testBuffer[0x10]; - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("bnk",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x20,streamFile) != 0x6B6C4253) - goto fail; - - // get file length - fileLength = get_streamfile_size(streamFile); - - // Find loop start - start_offset = read_32bitBE(0x10,streamFile); - readOffset = start_offset; - - do { - readOffset += (off_t)read_streamfile(testBuffer, readOffset, 0x10, streamFile); - - // Loop Start ... - if(testBuffer[0x01] == 0x06) - { - loop_start_offset = readOffset - 0x10; - break; - } - - } while (streamFile->get_offset(streamFile)<((int32_t)fileLength)); - - // start at last line of file and move up - readOffset = (int32_t)fileLength - 0x10; - - // Find loop end - do { - readOffset -= (off_t)read_streamfile(testBuffer, readOffset, 0x10, streamFile); - - // Loop End ... - if((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) - { - loop_end_offset = readOffset + 0x20; - break; - } - } while (readOffset > 0); - - // setup loops - if (loop_start_offset > 0) - { - loop_flag = 1; - - // if we have a start loop, use EOF if end loop is not found - if (loop_end_offset == 0) - { - loop_end_offset = (int32_t)fileLength - 0x10; - } - } - else - { - loop_flag = 0; - } - - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x90, streamFile); - vgmstream->meta_type = meta_PS3_KLBS; - - vgmstream->channels = channel_count; - vgmstream->sample_rate = 48000; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = ((vgmstream->interleave_block_size * channel_count)/16/channel_count*28); - - if (loop_flag) - { - vgmstream->loop_start_sample = loop_start_offset/16/channel_count*28; - vgmstream->loop_end_sample = loop_end_offset/16/channel_count*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; - - } - } - - 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_mib.c b/Frameworks/vgmstream/vgmstream/src/meta/ps_headerless.c similarity index 80% rename from Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c rename to Frameworks/vgmstream/vgmstream/src/meta/ps_headerless.c index b3de59893..8a4a7d974 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps_headerless.c @@ -4,13 +4,11 @@ static int check_psadpcm(STREAMFILE *streamFile); -/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [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 * init_vgmstream_ps_headerless(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileMIH = NULL; off_t start_offset = 0x00; char filename[PATH_LIMIT]; @@ -32,8 +30,6 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { int forceNoLoop=0; int gotEmptyLine=0; - uint8_t gotMIH=0; - int i, channel_count=0; @@ -59,14 +55,6 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { goto fail; - /* .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; - } - - fileLength = get_streamfile_size(streamFile); /* Search for interleave value (checking channel starts) and loop points (using PS-ADPCM flags). @@ -137,7 +125,7 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { } } - } while (streamFile->get_offset(streamFile)<((int32_t)fileLength)); + } while (readOffset<((int32_t)fileLength)); } if(testBuffer[0]==0x0c && testBuffer[1]==0) @@ -146,9 +134,6 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { 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; @@ -229,34 +214,25 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { 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; + vgmstream->interleave_block_size = interleave; - if(!strcasecmp("mib",filename_extension(filename))) - vgmstream->sample_rate = 44100; + if(!strcasecmp("mib",filename_extension(filename))) + vgmstream->sample_rate = 44100; - if(!strcasecmp("mi4",filename_extension(filename))) - vgmstream->sample_rate = 48000; + if(!strcasecmp("mi4",filename_extension(filename))) + vgmstream->sample_rate = 48000; - if(!strcasecmp("snds", 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("xag",filename_extension(filename))) + vgmstream->sample_rate = 44100; - if (!strcasecmp("cvs", filename_extension(filename)) || - !strcasecmp("vb",filename_extension(filename))) - vgmstream->sample_rate = 22050; + 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); - } + vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28); if(loopEnd!=0) { if(vgmstream->channels==1) { @@ -299,17 +275,14 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { vgmstream->loop_end_sample-=(emptySamples*channel_count); } - vgmstream->meta_type = gotMIH ? meta_PS2_MIB_MIH : meta_PS2_MIB; + vgmstream->meta_type = meta_PS_HEADERLESS; + vgmstream->allow_dual_stereo = 1; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - - close_streamfile(streamFileMIH); return vgmstream; fail: - close_streamfile(streamFileMIH); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c index efdb52f6e..612b5adf3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c @@ -7,12 +7,13 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag = 0, channel_count, sample_rate; - int xa_channel=0; int is_blocked; size_t file_size = get_streamfile_size(streamFile); - /* check extension (.xa: common, .str: sometimes used) */ - if ( !check_extensions(streamFile,"xa,str") ) + /* checks + * .xa: common, .str: sometimes (mainly videos) + * .adp: Phantasy Star Collection (SAT) raw XA */ + if ( !check_extensions(streamFile,"xa,str,adp") ) goto fail; /* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders. @@ -33,7 +34,7 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { is_blocked = 1; start_offset = 0x00; } - else { /* headerless */ + else { /* headerless and possibly incorrectly ripped */ is_blocked = 0; start_offset = 0x00; } @@ -50,12 +51,25 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { test_offset += (is_blocked ? 0x18 : 0x00); /* header */ for (i = 0; i < (sector_size/block_size); i++) { - /* first 0x10 ADPCM predictors should be 0..3 index */ + /* XA headers checks: filter indexes should be 0..3, and shifts 0..C */ for (j = 0; j < 16; j++) { - uint8_t header = read_8bit(test_offset + i, streamFile); - if (((header >> 4) & 0xF) > 3) + uint8_t header = (uint8_t)read_8bit(test_offset + i, streamFile); + if (((header >> 4) & 0xF) > 0x03) + goto fail; + if (((header >> 0) & 0xF) > 0x0c) goto fail; } + /* XA headers pairs are repeated */ + if (read_32bitBE(test_offset+0x00,streamFile) != read_32bitBE(test_offset+0x04,streamFile) || + read_32bitBE(test_offset+0x08,streamFile) != read_32bitBE(test_offset+0x0c,streamFile)) + goto fail; + /* blank frames should always use 0x0c0c0c0c (due to how shift works) */ + if (read_32bitBE(test_offset+0x00,streamFile) == 0 && + read_32bitBE(test_offset+0x04,streamFile) == 0 && + read_32bitBE(test_offset+0x08,streamFile) == 0 && + read_32bitBE(test_offset+0x0c,streamFile) == 0) + goto fail; + test_offset += 0x80; } @@ -66,13 +80,10 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { /* data is ok: parse header */ if (is_blocked) { - uint8_t xa_header; - /* parse 0x18 sector header (also see xa_blocked.c) */ - xa_channel = read_8bit(start_offset + 0x11,streamFile); - xa_header = read_8bit(start_offset + 0x13,streamFile); + uint8_t xa_header = (uint8_t)read_8bit(start_offset + 0x13,streamFile); - switch((xa_header >> 0) & 3) { /* 0..1: stereo */ + switch((xa_header >> 0) & 3) { /* 0..1: mono/stereo */ case 0: channel_count = 1; break; case 1: channel_count = 2; break; default: goto fail; @@ -82,16 +93,49 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { case 1: sample_rate = 18900; break; default: goto fail; } - VGM_ASSERT(((xa_header >> 4) & 3) == 1, /* 4..5: bits per sample (0=4, 1=8) */ - "XA: 8 bits per sample mode found\n"); /* spec only? */ - /* 6: emphasis (applies a filter but apparently not used by games) - * XA is also filtered when resampled to 44100 during output, differently from PS-ADPCM */ - /* 7: reserved */ + switch((xa_header >> 4) & 3) { /* 4..5: bits per sample (0=4, 1=8) */ + case 0: break; + default: /* PS1 games only do 4b */ + VGM_LOG("XA: unknown bits per sample found\n"); + goto fail; + } + switch((xa_header >> 6) & 1) { /* 6: emphasis (applies a filter) */ + case 0: break; + default: /* shouldn't be used by games */ + VGM_LOG("XA: unknown emphasis found\n"); + break; + } + switch((xa_header >> 7) & 1) { /* 7: reserved */ + case 0: break; + default: + VGM_LOG("XA: unknown reserved bit found\n"); + break; + } } else { - /* headerless, probably will go wrong */ - channel_count = 2; - sample_rate = 44100; /* not 37800? */ + /* headerless */ + if (check_extensions(streamFile,"adp")) { + /* Phantasy Star Collection (SAT) raw files */ + /* most are stereo, though a few (mainly sfx banks, sometimes using .bin) are mono */ + + char filename[PATH_LIMIT] = {0}; + get_streamfile_filename(streamFile, filename,PATH_LIMIT); + + /* detect PS1 mono files, very lame but whatevs, no way to detect XA mono/stereo */ + if (filename[0]=='P' && filename[1]=='S' && filename[2]=='1' && filename[3]=='S') { + channel_count = 1; + sample_rate = 22050; + } + else { + channel_count = 2; + sample_rate = 44100; + } + } + else { + /* incorrectly ripped standard XA */ + channel_count = 2; + sample_rate = 37800; + } } @@ -100,22 +144,29 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = xa_bytes_to_samples(file_size - start_offset, channel_count, is_blocked); - vgmstream->xa_headerless = !is_blocked; - vgmstream->xa_channel = xa_channel; - vgmstream->coding_type = coding_XA; - vgmstream->layout_type = layout_blocked_xa; vgmstream->meta_type = meta_PSX_XA; - - if (is_blocked) - start_offset += 0x18; /* move to first frame (hack for xa_blocked.c) */ + vgmstream->coding_type = coding_XA; + vgmstream->layout_type = is_blocked ? layout_blocked_xa : layout_none; /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - block_update_xa(start_offset,vgmstream); + + if (is_blocked) { + /* calc num_samples as blocks may be empty or smaller than usual depending on flags */ + vgmstream->next_block_offset = start_offset; + do { + block_update(vgmstream->next_block_offset,vgmstream); + vgmstream->num_samples += vgmstream->current_block_samples; + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset,vgmstream); + } + else { + vgmstream->num_samples = xa_bytes_to_samples(file_size - start_offset, channel_count, is_blocked); + } return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psx_gms.c b/Frameworks/vgmstream/vgmstream/src/meta/psx_gms.c index e1caaf77d..c48341484 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psx_gms.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psx_gms.c @@ -57,7 +57,7 @@ VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); + 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/rfrm.c b/Frameworks/vgmstream/vgmstream/src/meta/rfrm.c new file mode 100644 index 000000000..edf1a2ebf --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/rfrm.c @@ -0,0 +1,110 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* RFTM - Retro Studios format [Donkey Kong Country Tropical Freeze (WiiU/Switch)] */ +VGMSTREAM *init_vgmstream_rfrm(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + off_t fmta_offset = 0, data_offset = 0, header_offset, start_offset; + size_t data_size = 0, interleave; + int loop_flag, channel_count, version; + int big_endian; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + + /* checks */ + if (!check_extensions(streamFile, "csmp")) + goto fail; + + if (read_32bitBE(0x00, streamFile) != 0x5246524D) /* "RFRM" */ + goto fail; + /* 0x08: file size but not exact */ + if (read_32bitBE(0x14, streamFile) != 0x43534D50) /* "CSMP" */ + goto fail; + version = read_32bitBE(0x18,streamFile); /* assumed, also at 0x1c */ + + if (version == 0x0a) { /* Wii U */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + big_endian = 1; + } + else if (version == 0x12) { /* Switch */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + big_endian = 0; + } + else { + goto fail; + } + + + /* parse chunks (always BE) */ + { + off_t chunk_offset = 0x20; + off_t file_size = get_streamfile_size(streamFile); + + while (chunk_offset < file_size) { + uint32_t chunk_type = read_32bitBE(chunk_offset+0x00,streamFile); + size_t chunk_size = read_32bitBE(chunk_offset+0x08,streamFile); /* maybe 64b from 0x04? */ + + switch(chunk_type) { + case 0x464D5441: /* "FMTA" */ + fmta_offset = chunk_offset + 0x18; + break; + case 0x44415441: /* "DATA" */ + data_offset = chunk_offset + 0x18; + data_size = chunk_size; + break; + default: /* known: "LABL" (usually before "FMTA"), "META" (usually after "DATA") */ + break; + } + + chunk_offset += 0x18 + chunk_size; + } + + if (!fmta_offset || !data_offset || !data_size) + goto fail; + } + + + /* parse FMTA / DATA (fully interleaved standard DSPs) */ + channel_count = read_8bit(fmta_offset+0x00, streamFile); + /* FMTA 0x08: channel mapping */ + + header_offset = data_offset; + if (version == 0x0a) { + size_t align = 0x03; /* possibly 32b align */ + header_offset += align; + data_size -= align; + } + start_offset = header_offset + 0x60; + loop_flag = read_16bit(header_offset + 0x0C, streamFile); + interleave = data_size / channel_count; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_RFRM; + vgmstream->sample_rate = read_32bit(header_offset + 0x08, streamFile); + vgmstream->num_samples = read_32bit(header_offset + 0x00, streamFile); + vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bit(header_offset + 0x10, streamFile)); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bit(header_offset + 0x14, streamFile)) + 1; + 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_interleave; + vgmstream->interleave_block_size = interleave; + dsp_read_coefs(vgmstream, streamFile, header_offset + 0x1C, interleave, big_endian); + dsp_read_hist (vgmstream, streamFile, header_offset + 0x40, interleave, big_endian); + + 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/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index ad1353787..03b21ba3d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -13,6 +13,7 @@ static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, si /* return milliseconds */ static long parse_adtl_marker(unsigned char * marker) { long hh,mm,ss,ms; + if (memcmp("Marker ",marker,7)) return -1; if (4 != sscanf((char*)marker+7,"%ld:%ld:%ld.%ld",&hh,&mm,&ss,&ms)) @@ -25,31 +26,31 @@ static long parse_adtl_marker(unsigned char * marker) { static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag) { int loop_start_found = 0; int loop_end_found = 0; - off_t current_chunk = adtl_offset+4; + off_t current_chunk = adtl_offset+0x04; - while (current_chunk < adtl_offset+adtl_length) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); + while (current_chunk < adtl_offset + adtl_length) { + uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile); + off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile); - if (current_chunk+8+chunk_size > adtl_offset+adtl_length) return; + if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length) + return; switch(chunk_type) { - case 0x6c61626c: { /* labl */ - unsigned char *labelcontent; - labelcontent = malloc(chunk_size-4); + case 0x6c61626c: { /* "labl" */ + unsigned char *labelcontent = malloc(chunk_size-0x04); if (!labelcontent) return; - if (read_streamfile(labelcontent,current_chunk+0xc, chunk_size-4,streamFile)!=chunk_size-4) { + if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,streamFile) != chunk_size-0x04) { free(labelcontent); return; } switch (read_32bitLE(current_chunk+8,streamFile)) { case 1: - if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent))>=0) + if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent)) >= 0) loop_start_found = 1; break; case 2: - if (!loop_end_found && (*loop_end = parse_adtl_marker(labelcontent))>=0) + if (!loop_end_found && (*loop_end = parse_adtl_marker(labelcontent)) >= 0) loop_end_found = 1; break; default: @@ -95,15 +96,14 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; fmt->offset = current_chunk; - fmt->size = read_32bit(current_chunk+0x4,streamFile); + fmt->size = read_32bit(current_chunk+0x04,streamFile); + fmt->codec = (uint16_t)read_16bit(current_chunk+0x08,streamFile); fmt->sample_rate = read_32bit(current_chunk+0x0c,streamFile); fmt->channel_count = read_16bit(current_chunk+0x0a,streamFile); fmt->block_size = read_16bit(current_chunk+0x14,streamFile); - fmt->interleave = 0; - fmt->bps = read_16bit(current_chunk+0x16,streamFile); - fmt->codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); + fmt->interleave = 0; switch (fmt->codec) { case 0x00: /* Yamaha ADPCM (raw) [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */ @@ -129,8 +129,15 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk break; case 0x02: /* MS ADPCM */ - if (fmt->bps != 4) goto fail; - fmt->coding_type = coding_MSADPCM; + if (fmt->bps == 4) { + fmt->coding_type = coding_MSADPCM; + } + else if (fmt->bps == 16 && fmt->block_size == 0x02 * fmt->channel_count && fmt->size == 0x14) { + fmt->coding_type = coding_IMA; /* MX vs ATV Unleashed (PC) codec hijack */ + } + else { + goto fail; + } break; case 0x11: /* MS IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */ @@ -243,6 +250,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { 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; + int32_t loop_start_cue = -1; int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; @@ -254,18 +262,26 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* check extension */ - /* .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), + /* .lwav: to avoid hijacking .wav + * .xwav: fake for Xbox games (not needed anymore) + * .da: The Great Battle VI (PS1) + * .dax: Love Game's - Wai Wai Tennis (PS1) + * .cd: Exector (PS) + * .med: Psi Ops (PC) + * .snd: Layton Brothers (iOS/Android) * .adx: Remember11 (PC) sfx * .adp: Headhunter (DC) - * .xss: Spider-Man The Movie (Xbox) */ - if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp,xss") ) { + * .xss: Spider-Man The Movie (Xbox) + * .xsew: Mega Man X Legacy Collections (PC) */ + if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew") ) { ; } else if ( check_extensions(streamFile, "mwv") ) { mwv = 1; } - /* .rws: Climax games (Silent Hill Origins PSP, Oblivion PSP), .aud: EA Replay */ + /* .at3: standard + * .rws: Climax games (Silent Hill Origins PSP, Oblivion PSP) + * .aud: EA Replay */ else if ( check_extensions(streamFile, "at3,rws,aud") ) { at3 = 1; } @@ -285,16 +301,18 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { riff_size = read_32bitLE(0x04,streamFile); file_size = get_streamfile_size(streamFile); - /* for some of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ + /* some of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ 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; + /* some PC games do this [Halo 2 (PC)] */ + if (riff_size + 0x04 == file_size && read_16bitLE(0x14,streamFile)==0x0069) + riff_size -= 0x04; /* check for truncated RIFF */ @@ -408,6 +426,18 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { mwv_ctrl_offset = current_chunk; break; + case 0x63756520: /* "cue " (used in Source Engine for storing loop points) */ + if (fmt.coding_type == coding_PCM16LE || fmt.coding_type == coding_MSADPCM) { + uint32_t num_cues = read_32bitLE(current_chunk + 0x08, streamFile); + + if (num_cues == 1 || num_cues == 2) { + // The second cue sets loop end point but it's not actually used by the engine. + loop_flag = 1; + loop_start_cue = read_32bitLE(current_chunk + 0x20, streamFile); + } + } + break; + case 0x4A554E4B: /* "JUNK" */ JunkFound = 1; break; @@ -515,6 +545,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = fact_sample_count; /* some (converted?) Xbox games have bigger fact_samples */ break; + case coding_IMA: + vgmstream->num_samples = ima_bytes_to_samples(data_size, fmt.channel_count); + break; + #ifdef VGM_USE_FFMPEG case coding_FFmpeg: { ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, 0x00, streamFile->get_size(streamFile)); @@ -582,6 +616,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case coding_MS_IMA: case coding_AICA: case coding_XBOX_IMA: + case coding_IMA: #ifdef VGM_USE_FFMPEG case coding_FFmpeg: #endif @@ -600,6 +635,15 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { break; } + /* Dynasty Warriors 5 (Xbox) 6ch interleaves stereo frames, probably not official */ + if (vgmstream->coding_type == coding_XBOX_IMA && vgmstream->channels > 2) { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x24; /* block_size / channels */ + if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) + goto fail; /* only 2ch+..+2ch layout is known */ + } + + /* meta, loops */ vgmstream->meta_type = meta_RIFF_WAVE; if (loop_flag) { @@ -622,6 +666,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; } + else if (loop_start_cue != -1) { + vgmstream->loop_start_sample = loop_start_cue; + vgmstream->loop_end_sample = vgmstream->num_samples; + } } if (mwv) { vgmstream->meta_type = meta_RIFF_WAVE_MWV; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rs03.c b/Frameworks/vgmstream/vgmstream/src/meta/rs03.c index 6244e2eaa..127e783f5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rs03.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rs03.c @@ -27,11 +27,11 @@ VGMSTREAM * init_vgmstream_rs03(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitBE(0xc,streamFile); - vgmstream->num_samples = read_32bitBE(8,streamFile); + vgmstream->sample_rate = read_32bitBE(0x0c,streamFile); + vgmstream->num_samples = read_32bitBE(0x08,streamFile); if (loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile)/8*14; - vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile)/8*14; + vgmstream->loop_start_sample = dsp_bytes_to_samples(read_32bitBE(0x18,streamFile), 1); + vgmstream->loop_end_sample = dsp_bytes_to_samples(read_32bitBE(0x1c,streamFile), 1); } vgmstream->meta_type = meta_DSP_RS03; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c index c4af75e60..08fd535ba 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c @@ -1125,19 +1125,8 @@ VGMSTREAM * init_vgmstream_rsd6at3p(STREAMFILE *streamFile) { 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); - } + if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ + ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); } #else goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rws.c b/Frameworks/vgmstream/vgmstream/src/meta/rws.c index c40898752..563a70584 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rws.c @@ -3,190 +3,284 @@ #include "../layout/layout.h" -static off_t get_rws_string_size(off_t off, STREAMFILE *streamFile); +static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile); + + +typedef struct { + int channel_count; + int codec; + int sample_rate; + + int total_segments; + int target_segment; + off_t segment_offset; + size_t segment_size; + off_t segment_name_offset; + + int total_layers; + int target_layer; + off_t layer_offset; + size_t layer_size; + off_t layer_name_offset; + + size_t file_size; + size_t header_size; + size_t data_size; + off_t data_offset; + + //size_t stream_size; + size_t block_size; + size_t block_size_total; + size_t stream_size_full; + + off_t coefs_offset; + + int use_segment_subsongs; /* otherwise play the whole thing */ +} rws_header; /* RWS - RenderWare Stream (from games using RenderWare Audio middleware) */ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, off, coefs_offset = 0, stream_offset = 0, name_offset = 0; - int loop_flag = 0, channel_count = 0, codec = 0, sample_rate = 0; - size_t file_size, header_size, data_size, stream_size_full = 0, stream_size = 0, stream_size_expected = 0; - size_t block_size = 0, block_size_total = 0; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int i, total_segments; + off_t start_offset, offset; + off_t stream_offset, name_offset; + size_t stream_size; + int loop_flag; + int i; int total_subsongs, target_subsong = streamFile->stream_index; + rws_header rws = {0}; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + /* checks */ if (!check_extensions(streamFile,"rws")) goto fail; - /* check chunks (always LE) */ - /* Made of a file chunk with header and data chunks (other chunks exist for non-audio .RWS). + /* parse chunks (always LE) */ + /* RWS are made of a file chunk with header and data chunks (other chunks exist for non-audio .RWS). * A chunk is: id, size, RW version (no real diffs), data of size (version is repeated but same for all chunks). * Version: 16b main + 16b build (can vary between files) ex: 0c02, 1003, 1400 = 3.5, 1803 = 3.6, 1C02 = 3.7. */ + if (read_32bitLE(0x00,streamFile) != 0x0000080d) /* audio file chunk id */ goto fail; - file_size = read_32bitLE(0x04,streamFile); /* audio file chunk size */ - if (file_size + 0x0c != get_streamfile_size(streamFile)) goto fail; + rws.file_size = read_32bitLE(0x04,streamFile); /* audio file chunk size */ + if (rws.file_size + 0x0c != get_streamfile_size(streamFile)) + goto fail; if (read_32bitLE(0x0c,streamFile) != 0x0000080e) /* header chunk id */ goto fail; - header_size = read_32bitLE(0x10,streamFile); /* header chunk size */ + rws.header_size = read_32bitLE(0x10,streamFile); /* header chunk size */ - off = 0x0c + 0x0c + header_size; - if (read_32bitLE(off+0x00,streamFile) != 0x0000080f) /* data chunk id */ + rws.data_offset = 0x0c + 0x0c + rws.header_size; /* usually 0x800 but not always */ + if (read_32bitLE(rws.data_offset+0x00,streamFile) != 0x0000080f) /* data chunk id */ goto fail; - data_size = read_32bitLE(off+0x04,streamFile); /* data chunk size */ - if (data_size+0x0c + off != get_streamfile_size(streamFile)) + rws.data_size = read_32bitLE(rws.data_offset+0x04,streamFile); /* data chunk size */ + if (rws.data_size+0x0c + rws.data_offset != get_streamfile_size(streamFile)) goto fail; /* inside header chunk (many unknown fields are probably IDs/config, as two same-sized files vary a lot) */ - off = 0x0c + 0x0c; + offset = 0x0c + 0x0c; - /* get base header */ + + /* base header */ /* 0x00: actual header size (less than chunk size), 0x04/08/10: sizes of various sections?, 0x14/18/24/2C: commands? * 0x1c: null? 0x30: 0x800?, 0x34: block_size_total?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid */ - read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */ - total_segments = read_32bit(off+0x20,streamFile); - total_subsongs = read_32bit(off+0x28,streamFile); + read_32bit = (read_32bitLE(offset+0x00,streamFile) > rws.header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */ + rws.total_segments = read_32bit(offset+0x20,streamFile); + rws.total_layers = read_32bit(offset+0x28,streamFile); + if (rws.total_segments > 1 && rws.total_layers > 1) { + VGM_LOG("RWS: unknown segments+layers layout\n"); + } - /* skip audio file name */ - off += 0x50 + get_rws_string_size(off+0x50, streamFile); + /* audio file name */ + offset += 0x50 + get_rws_string_size(offset+0x50, streamFile); - /* Data is divided into "segments" (cues/divisions within data, ex. intro+main, voice1+2+..N) and "streams" - * of interleaved blocks (for multichannel?). last stream (only?) has padding. Segments divide all streams. - * ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN). */ + /* RWS data can be divided in two ways: + * - "segments": cues/divisions within data, like intro+main/loop [[Max Payne 2 (PS2), Nana (PS2)] + * or voice1+2+..N, song1+2+..N [Madagascar (PS2), The Legend of Spyro: Dawn of the Dragon (X360)] + * - "streams" (layers): interleaved blocks, like L/R+C/S+LS/RS [Burnout 2 (GC/Xbox)]. Last stream has padding: + * ex.- 1 block: 0x1800 data of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch. + * + * Layers seem only used to fake multichannel, but as they are given sample rate/channel/codec/coefs/etc + * they are treated as subsongs. Similarly segments can be treated as subsongs in some cases. + * They don't seem used at the same time, though could be possible. */ + + /* Use either layers or segments as subsongs (with layers having priority). This divides Nana (PS2) + * or Max Payne 2 (PS2) intro+main, so it could be adjusted to >2 (>4 for Max Payne 2) if undesired */ if (target_subsong == 0) target_subsong = 1; + + rws.use_segment_subsongs = (rws.total_layers == 1 && rws.total_segments > 1); + if (rws.use_segment_subsongs) { + rws.target_layer = 1; + rws.target_segment = target_subsong; + total_subsongs = rws.total_segments; + } + else { + rws.target_layer = target_subsong; + rws.target_segment = 0; /* none = play all */ + total_subsongs = rws.total_layers; + } + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - /* get segment info, for all streams */ - /* 0x00/04/0c: command?, 0x18: full segment size (including all streams), 0x1c: offset, others: ?) */ - for (i = 0; i < total_segments; i++) { - stream_size_full += read_32bit(off + 0x20*i + 0x18,streamFile); + + /* segment info, for all layers */ + /* 0x00/04/0c: command?, 0x18: full segment size (including padding), 0x1c: offset, others: ?) */ + for (i = 0; i < rws.total_segments; i++) { + if (i+1 == rws.target_segment) { + rws.segment_offset = read_32bit(offset + 0x20*i + 0x1c,streamFile); + } + rws.stream_size_full += read_32bit(offset + 0x20*i + 0x18,streamFile); } - off += 0x20 * total_segments; + offset += 0x20 * rws.total_segments; - /* get usable segment sizes (usually ok but sometimes > stream_size), per stream */ - for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */ - stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_subsong-1),streamFile); + /* usable segment/layer sizes (assumed either one, sometimes incorrect size?) */ + for (i = 0; i < (rws.total_segments * rws.total_layers); i++) { /* sum usable segment sizes (no padding) */ + size_t usable_size = read_32bit(offset + 0x04*i,streamFile); /* size without padding */ + if (i+1 == rws.target_segment) { + rws.segment_size = usable_size; + } + if (i+1 == rws.target_layer || rws.total_layers == 1) { + rws.layer_size += usable_size; + } } - off += 0x04 * (total_segments * total_subsongs); + offset += 0x04 * (rws.total_segments * rws.total_layers); - /* skip segment uuids */ - off += 0x10 * total_segments; + /* segment uuids */ + offset += 0x10 * rws.total_segments; - /* skip segment names */ - for (i = 0; i < total_segments; i++) { - off += get_rws_string_size(off, streamFile); + /* segment names */ + for (i = 0; i < rws.total_segments; i++) { + if (i+1 == rws.target_segment) { + rws.segment_name_offset = offset; + } + offset += get_rws_string_size(offset, streamFile); } - /* get stream layout */ - /* 0x00/04/14: command?, 0x08: null? 0x0c: spf related? (XADPCM=07, VAG=1C, DSP=0E, PCM=01) + + /* layer info */ + /* 0x00/04/14: command?, 0x08: null? 0x0c: related to samples per frame? (XADPCM=07, VAG=1C, DSP=0E, PCM=01) * 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */ - for (i = 0; i < total_subsongs; i++) { /* get block_sizes */ - block_size_total += read_32bit(off + 0x10 + 0x28*i, streamFile); /* for all streeams, to skip during decode */ - if (i+1 == target_subsong) { + for (i = 0; i < rws.total_layers; i++) { /* get block_sizes */ + rws.block_size_total += read_32bit(offset + 0x10 + 0x28*i, streamFile); /* for all layers, to skip during decode */ + if (i+1 == rws.target_layer) { //block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */ - block_size = read_32bit(off + 0x20 + 0x28*i, streamFile); /* without padding */ - stream_offset = read_32bit(off + 0x24 + 0x28*i, streamFile); /* within data */ + rws.block_size = read_32bit(offset + 0x20 + 0x28*i, streamFile); /* without padding */ + rws.layer_offset = read_32bit(offset + 0x24 + 0x28*i, streamFile); /* within data */ } } - off += 0x28 * total_subsongs; + offset += 0x28 * rws.total_layers; - /* get stream config */ + /* layer config */ /* 0x04: command?, 0x0c(1): bits per sample, others: null? */ - for (i = 0; i < total_subsongs; i++) { /* size depends on codec so we must parse it */ + for (i = 0; i < rws.total_layers; i++) { /* size depends on codec so we must parse it */ int prev_codec = 0; - if (i+1 == target_subsong) { - sample_rate = read_32bit(off+0x00, streamFile); - //unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */ - channel_count = read_8bit(off+0x0d, streamFile); - codec = read_32bitBE(off+0x1c, streamFile); /* uuid of 128b but first 32b is enough */ + if (i+1 == rws.target_layer) { + rws.sample_rate = read_32bit(offset+0x00, streamFile); + //unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */ + rws.channel_count = read_8bit(offset+0x0d, streamFile); + rws.codec = read_32bitBE(offset+0x1c, streamFile); /* uuid of 128b but first 32b is enough */ } - prev_codec = read_32bitBE(off+0x1c, streamFile); - off += 0x2c; + prev_codec = read_32bitBE(offset+0x1c, streamFile); + offset += 0x2c; - if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */ + if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per layer */ /* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */ - if (i+1 == target_subsong) { - coefs_offset = off + 0x1c; + if (i+1 == rws.target_layer) { + rws.coefs_offset = offset + 0x1c; } - off += 0x60; + offset += 0x60; } - off += 0x04; /* padding/garbage */ + offset += 0x04; /* padding/garbage */ } - /* skip stream uuids */ - off += 0x10 * total_subsongs; + /* layer uuids */ + offset += 0x10 * rws.total_layers; - /* get stream name */ - for (i = 0; i < total_subsongs; i++) { - if (i+1 == target_subsong) { - name_offset = off; + /* layer names */ + for (i = 0; i < rws.total_layers; i++) { + if (i+1 == rws.target_layer) { + rws.layer_name_offset = offset; } - off += get_rws_string_size(off, streamFile); + offset += get_rws_string_size(offset, streamFile); } + /* rest is padding/garbage until chunk end (may contain strings and weird stuff) */ // ... - /* usually 0x800 but not always */ - start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset; - /* sometimes it's wrong for no apparent reason (probably a bug in RWS) */ - stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_subsongs) / total_subsongs; - if (stream_size > stream_size_expected) { - VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected); - stream_size = stream_size_expected; + if (rws.use_segment_subsongs) { + stream_offset = rws.segment_offset; + stream_size = rws.segment_size; + name_offset = rws.segment_name_offset; + } + else { + stream_offset = rws.layer_offset; + stream_size = rws.layer_size; + name_offset = rws.layer_name_offset; + } + start_offset = rws.data_offset + 0x0c + stream_offset; + + + /* sometimes it's wrong in XBOX-IMA for no apparent reason (probably a bug in RWS) */ + if (!rws.use_segment_subsongs) { + size_t stream_size_expected = (rws.stream_size_full / rws.block_size_total) * (rws.block_size * rws.total_layers) / rws.total_layers; + if (stream_size > stream_size_expected) { + VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected); + stream_size = stream_size_expected; + } } + loop_flag = 0; /* RWX doesn't seem to include actual looping (so devs may fake it with segments) */ + + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(rws.channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_RWS; + vgmstream->sample_rate = rws.sample_rate; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; - vgmstream->meta_type = meta_RWS; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); vgmstream->layout_type = layout_blocked_rws; - vgmstream->current_block_size = block_size / vgmstream->channels; - vgmstream->full_block_size = block_size_total; + vgmstream->current_block_size = rws.block_size / vgmstream->channels; + vgmstream->full_block_size = rws.block_size_total; - switch(codec) { - case 0x17D21BD0: /* PCM PC (17D21BD0 8735ED4E B9D9B8E8 6EA9B995) */ + switch(rws.codec) { + case 0x17D21BD0: /* PCM PC (17D21BD0 8735ED4E B9D9B8E8 6EA9B995) */ case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */ /* ex. D.i.R.T. - Origin of the Species (PC), The Legend of Spyro (X360) */ vgmstream->coding_type = coding_PCM16_int; - vgmstream->codec_endian = (codec == 0xD01BD217); + vgmstream->codec_endian = (rws.codec == 0xD01BD217); /* X360: BE */ vgmstream->interleave_block_size = 0x02; /* only used to setup channels */ - vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, rws.channel_count, 16); break; case 0x9897EAD9: /* PS-ADPCM PS2 (9897EAD9 BCBB7B44 96B26547 59102E16) */ /* ex. Silent Hill Origins (PS2), Ghost Rider (PS2), Max Payne 2 (PS2), Nana (PS2) */ vgmstream->coding_type = coding_PSX; - vgmstream->interleave_block_size = block_size / 2; + vgmstream->interleave_block_size = rws.block_size / 2; - vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); + vgmstream->num_samples = ps_bytes_to_samples(stream_size, rws.channel_count); break; case 0xF86215B0: /* DSP GC/Wii (F86215B0 31D54C29 BD37CDBF 9BD10C53) */ /* ex. Burnout 2 (GC), Alice in Wonderland (Wii) */ vgmstream->coding_type = coding_NGC_DSP; - vgmstream->interleave_block_size = block_size / 2; + vgmstream->interleave_block_size = rws.block_size / 2; /* get coefs (all channels share them so 0 spacing; also seem fixed for all RWS) */ - dsp_read_coefs_be(vgmstream,streamFile,coefs_offset, 0); + dsp_read_coefs_be(vgmstream,streamFile,rws.coefs_offset, 0); - vgmstream->num_samples = dsp_bytes_to_samples(stream_size, channel_count); + vgmstream->num_samples = dsp_bytes_to_samples(stream_size, rws.channel_count); break; case 0x936538EF: /* XBOX-IMA PC (936538EF 11B62D43 957FA71A DE44227A) */ @@ -195,20 +289,17 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { vgmstream->coding_type = coding_XBOX_IMA; /* PC and Xbox share the same data */ vgmstream->interleave_block_size = 0; - vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, channel_count); + vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, rws.channel_count); break; default: - VGM_LOG("RSW: unknown codec 0x%08x\n", codec); + VGM_LOG("RWS: unknown codec 0x%08x\n", rws.codec); goto fail; } if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - block_update_rws(start_offset, vgmstream); /* block init */ - return vgmstream; fail: @@ -218,10 +309,10 @@ fail: /* rws-strings are null-terminated then padded to 0x10 (weirdly the padding contains garbage) */ -static off_t get_rws_string_size(off_t off, STREAMFILE *streamFile) { +static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile) { int i; for (i = 0; i < 0x800; i++) { /* 0x800=arbitrary max */ - if (read_8bit(off+i,streamFile) == 0) { /* null terminator */ + if (read_8bit(offset+i,streamFile) == 0) { /* null terminator */ return i + (0x10 - (i % 0x10)); /* size is padded */ } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c index 44cb6a508..7dd87f089 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c @@ -280,10 +280,13 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RWAR; else if (rwav) { - if (big_endian) + if (big_endian) { vgmstream->meta_type = meta_RWAV; - else + } + else { vgmstream->meta_type = meta_CWAV; + vgmstream->allow_dual_stereo = 1; /* LEGO 3DS games */ + } } else vgmstream->meta_type = meta_RWSD; @@ -365,8 +368,7 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { { int i; for (i=0;ich[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/scd_pcm.c b/Frameworks/vgmstream/vgmstream/src/meta/scd_pcm.c index 1e374beda..d28c669a1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/scd_pcm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/scd_pcm.c @@ -1,63 +1,65 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* PCM (from Lunar: Eternal Blue (Sega CD) */ + +/* .PCM - from Lunar: Eternal Blue (Sega CD) */ VGMSTREAM * init_vgmstream_scd_pcm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + int loop_flag, channel_count; - int loop_flag; - int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("pcm",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x00020000) + /* checks */ + if (!check_extensions(streamFile, "pcm")) goto fail; - loop_flag = (read_32bitLE(0x02,streamFile)!=0); - channel_count = 1; + if (read_16bitBE(0x00,streamFile) == 0x0002) { + channel_count = 1; + } + else if (read_16bitBE(0x00,streamFile) == 0x0001) { + channel_count = 2; /* RP025.PCM, RP039.PCM */ + } + else { + goto fail; + } + + start_offset = 0x800; + + + /* extra validations since .pcm it's kinda generic */ + { + off_t i; + /* should be empty up to start (~0x0a/~0x10 sometimes has unknown values) */ + for (i = 0x20; i < start_offset; i++) { + if (read_8bit(i, streamFile) != 0) + goto fail; + } + } + + /* loops start 0 is possible, plus all? files loops + * (even sfx/voices loop, but those set loop start in silence near the end) */ + loop_flag = (read_32bitBE(0x06,streamFile) != 0); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x200; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PCM8_SB_int; - vgmstream->num_samples = read_32bitBE(0x06,streamFile)*2; - if(loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x02,streamFile)*0x400*2; - vgmstream->loop_end_sample = read_32bitBE(0x06,streamFile)*2; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x1; vgmstream->meta_type = meta_SCD_PCM; + vgmstream->sample_rate = 32500; /* looks correct compared to emu/recordings */ + vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count, 8); + vgmstream->loop_start_sample = read_32bitBE(0x02,streamFile)*0x400*2; + vgmstream->loop_end_sample = read_32bitBE(0x06,streamFile)*2; - /* 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->coding_type = coding_PCM8_SB; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x800; + 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/sdf.c b/Frameworks/vgmstream/vgmstream/src/meta/sdf.c new file mode 100644 index 000000000..1b298015e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sdf.c @@ -0,0 +1,96 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* SDF - from Beyond Reality games [Agent Hugo - Lemoon Twist (PS2)] */ +VGMSTREAM * init_vgmstream_sdf_ps2(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"sdf") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x53444600) /* "SDF\0" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x03000000) /* version? */ + goto fail; + + start_offset = 0x18; + data_size = get_streamfile_size(streamFile) - start_offset; + if (read_32bitLE(0x08,streamFile) != data_size) + goto fail; + + channel_count = read_32bitLE(0x10,streamFile); + loop_flag = 0; /* all files have loop flags but simply fade out normally and repeat */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SDF_PS2; + vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* SDF - from Beyond Reality games [Gummy Bears Mini Golf (3DS)] */ +VGMSTREAM * init_vgmstream_sdf_3ds(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"sdf") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x53444600) /* "SDF\0" */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x03000000) /* version? */ + goto fail; + + start_offset = 0x78; /* assumed */ + data_size = get_streamfile_size(streamFile) - start_offset; + if (read_32bitLE(0x08,streamFile) != data_size) + goto fail; + + channel_count = read_32bitLE(0x14,streamFile); + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SDF_3DS; + vgmstream->sample_rate = read_32bitLE(0x10,streamFile); + vgmstream->num_samples = dsp_bytes_to_samples(data_size,channel_count); + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = data_size / channel_count; + dsp_read_coefs_le(vgmstream,streamFile,0x1c,0x2e); + //todo: there be hist around 0x3c + + 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/sfl.c b/Frameworks/vgmstream/vgmstream/src/meta/sfl.c index c656142b0..51c640927 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sfl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sfl.c @@ -1,12 +1,118 @@ -#include "../vgmstream.h" +#include "meta.h" + +static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag); + + +/* .sfl - odd RIFF-formatted files that go along with .ogg [Hanachirasu (PC), Touhou 10.5 (PC)] */ +VGMSTREAM * init_vgmstream_sfl_ogg(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamData = NULL; + int loop_flag = 0; + long loop_start_ms = -1; + long loop_end_ms = -1; + + + /* checks */ + if (!check_extensions(streamFile, "sfl")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */ + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x5346504C) /* "SFPL" */ + goto fail; + + { + /* try with file.ogg.sfl=header and file.ogg=data [Hanachirasu (PC)] */ + char basename[PATH_LIMIT]; + get_streamfile_basename(streamFile,basename,PATH_LIMIT); + streamData = open_streamfile_by_filename(streamFile, basename); + if (!streamData) { + /* try again with file.sfl=header + file.ogg=daba */ + streamData = open_streamfile_by_ext(streamFile,"ogg"); + if (!streamData) goto fail; + } + else { + if (!check_extensions(streamData, "ogg")) + goto fail; + } + } #ifdef VGM_USE_VORBIS + /* let the real initer do the parsing */ + vgmstream = init_vgmstream_ogg_vorbis(streamData); + if (!vgmstream) goto fail; + vgmstream->meta_type = meta_OGG_SFL; +#else + goto fail; +#endif -#include "meta.h" -#include "../layout/layout.h" -#include "../util.h" + /* read through chunks to verify format and find metadata */ + { + size_t riff_size, file_size; + off_t current_chunk = 0x0c; /* start with first chunk */ -/* .sfl, odd RIFF-formatted files that go along with oggs */ + riff_size = read_32bitLE(0x04,streamFile); + file_size = get_streamfile_size(streamFile); + if (file_size < riff_size+0x08) + goto fail; + + while (current_chunk < file_size) { + uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile); + off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile); + + /* There seem to be a few bytes left over, included in the + * RIFF but not enough to make a new chunk. */ + if (current_chunk+0x08 > file_size) break; + + if (current_chunk+0x08+chunk_size > file_size) + goto fail; + + switch(chunk_type) { + case 0x4C495354: /* "LIST" */ + switch (read_32bitBE(current_chunk+0x08, streamFile)) { + case 0x6164746C: /* "adtl" */ + /* yay, atdl is its own little world */ + parse_adtl(current_chunk + 0x08, chunk_size, streamFile, + &loop_start_ms,&loop_end_ms,&loop_flag); + break; + default: + break; + } + break; + default: + break; + } + current_chunk += 0x08+chunk_size; + } + } + + /* install loops */ + if (loop_flag) { + int loop_start = (long long)loop_start_ms * vgmstream->sample_rate / 1000; + int loop_end = (long long)loop_end_ms * vgmstream->sample_rate / 1000; + vgmstream_force_loop(vgmstream,loop_flag,loop_start, loop_end); + } + /* sfl .ogg often has song endings (use the base .ogg for those) */ + + close_streamfile(streamData); + return vgmstream; + +fail: + close_streamfile(streamData); + close_vgmstream(vgmstream); + return NULL; +} + +/* return milliseconds */ +static long parse_adtl_marker(unsigned char * marker) { + long hh,mm,ss,ms; + + if (memcmp("Marker ",marker,7)) return -1; + + if (4 != sscanf((char*)marker+7,"%ld:%ld:%ld.%ld",&hh,&mm,&ss,&ms)) + return -1; + + return ((hh*60+mm)*60+ss)*1000+ms; +} /* return milliseconds */ static int parse_region(unsigned char * region, long *start, long *end) { @@ -22,161 +128,67 @@ static int parse_region(unsigned char * region, long *start, long *end) { *start = ((start_hh*60+start_mm)*60+start_ss)*1000+start_ms; *end = ((end_hh*60+end_mm)*60+end_ss)*1000+end_ms; - return 0; } /* loop points have been found hiding here */ -static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, - long *loop_start, long *loop_end, int *loop_flag) { - int loop_found = 0; +static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag) { + int loop_start_found = 0; + int loop_end_found = 0; + off_t current_chunk = adtl_offset+0x04; - off_t current_chunk = adtl_offset+4; + while (current_chunk < adtl_offset + adtl_length) { + uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile); + off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile); - while (current_chunk < adtl_offset+adtl_length) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); - - if (current_chunk+8+chunk_size > adtl_offset+adtl_length) return; + if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length) + return; switch(chunk_type) { - case 0x6c61626c: /* labl */ - { - unsigned char *labelcontent; - labelcontent = malloc(chunk_size-4); - if (!labelcontent) return; - if (read_streamfile(labelcontent,current_chunk+0xc, - chunk_size-4,streamFile)!=chunk_size-4) { - free(labelcontent); - return; - } - - if (!loop_found && - parse_region(labelcontent,loop_start,loop_end)>=0) - { - loop_found = 1; - } - + case 0x6c61626c: { /* "labl" */ + unsigned char *labelcontent = malloc(chunk_size-0x04); + if (!labelcontent) return; + if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,streamFile) != chunk_size-0x04) { free(labelcontent); + return; } + + switch (read_32bitLE(current_chunk+8,streamFile)) { + case 1: + if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent)) >= 0) + loop_start_found = 1; + + if (!loop_start_found && !loop_end_found && parse_region(labelcontent,loop_start,loop_end) >= 0) { + loop_start_found = 1; + loop_end_found = 1; + } + + break; + case 2: + if (!loop_end_found && (*loop_end = parse_adtl_marker(labelcontent)) >= 0) + loop_end_found = 1; + break; + default: + break; + } + + free(labelcontent); break; + } default: break; } - current_chunk += 8 + chunk_size; + current_chunk += 0x08 + chunk_size; } - if (loop_found) *loop_flag = 1; + if (loop_start_found && loop_end_found) + *loop_flag = 1; + + /* labels don't seem to be consistently ordered */ + if (*loop_start > *loop_end) { + long temp = *loop_start; + *loop_start = *loop_end; + *loop_end = temp; + } } - -VGMSTREAM * init_vgmstream_sfl(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileOGG = NULL; - char filenameOGG[PATH_LIMIT]; - char filename[PATH_LIMIT]; - - off_t file_size = -1; - - int loop_flag = 0; - long loop_start_ms = -1; - long loop_end_ms = -1; - uint32_t riff_size; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sfl",filename_extension(filename))) goto fail; - - /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)!=0x52494646) /* "RIFF" */ - goto fail; - /* check for SFPL form */ - if ((uint32_t)read_32bitBE(8,streamFile)!=0x5346504C) /* "SFPL" */ - goto fail; - - /* check for .OGG file */ - strcpy(filenameOGG,filename); - strcpy(filenameOGG+strlen(filenameOGG)-3,"ogg"); - - streamFileOGG = streamFile->open(streamFile,filenameOGG,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileOGG) { - goto fail; - } - - /* let the real initer do the parsing */ - vgmstream = init_vgmstream_ogg_vorbis(streamFileOGG); - if (!vgmstream) goto fail; - - close_streamfile(streamFileOGG); - streamFileOGG = NULL; - - /* now that we have an ogg, proceed with parsing the .sfl */ - riff_size = read_32bitLE(4,streamFile); - file_size = get_streamfile_size(streamFile); - - /* check for tructated RIFF */ - if (file_size < riff_size+8) goto fail; - - /* read through chunks to verify format and find metadata */ - { - off_t current_chunk = 0xc; /* start with first chunk */ - - while (current_chunk < file_size) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); - - /* There seem to be a few bytes left over, included in the - * RIFF but not enough to make a new chunk. - */ - if (current_chunk+8 > file_size) break; - - if (current_chunk+8+chunk_size > file_size) goto fail; - - switch(chunk_type) { - case 0x4C495354: /* LIST */ - /* what lurks within?? */ - switch (read_32bitBE(current_chunk + 8, streamFile)) { - case 0x6164746C: /* adtl */ - /* yay, atdl is its own little world */ - parse_adtl(current_chunk + 8, chunk_size, - streamFile, - &loop_start_ms,&loop_end_ms,&loop_flag); - break; - default: - break; - } - break; - default: - /* ignorance is bliss */ - break; - } - - current_chunk += 8+chunk_size; - } - } - - if (loop_flag) { - /* install loops */ - if (!vgmstream->loop_flag) { - vgmstream->loop_flag = 1; - vgmstream->loop_ch = calloc(vgmstream->channels, - sizeof(VGMSTREAMCHANNEL)); - if (!vgmstream->loop_ch) goto fail; - } - - vgmstream->loop_start_sample = (long long)loop_start_ms*vgmstream->sample_rate/1000; - vgmstream->loop_end_sample = (long long)loop_end_ms*vgmstream->sample_rate/1000; - } - - vgmstream->meta_type = meta_OGG_SFL; - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (streamFileOGG) close_streamfile(streamFileOGG); - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index e37444632..9c43ecfef 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -139,21 +139,9 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { 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(streamFile, 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, streamFile); - } else if (chunk_size == 0xc) { - fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile); - } - ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples); - } + if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ + ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); /* SGXD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ - break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sli.c b/Frameworks/vgmstream/vgmstream/src/meta/sli.c index a2852667e..f4ed50242 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sli.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sli.c @@ -1,121 +1,99 @@ -#include "../vgmstream.h" +#include "meta.h" +#include + + +/* .sli - loop points associated with a similarly named .ogg [Fate/Stay Night (PC), World End Economica (PC)]*/ +VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamData = NULL; + int32_t loop_start = -1, loop_length = -1; + int32_t loop_from = -1, loop_to = -1; + + /* checks */ + if (!check_extensions(streamFile, "sli")) + goto fail; + + { + /* try with file.ogg.sli=header and file.ogg=data */ + char basename[PATH_LIMIT]; + get_streamfile_basename(streamFile,basename,PATH_LIMIT); + streamData = open_streamfile_by_filename(streamFile, basename); + if (!streamData) goto fail; + + if (!check_extensions(streamData, "ogg")) + goto fail; + } #ifdef VGM_USE_VORBIS - -#include -#include "meta.h" -#include "../util.h" - -#ifdef WIN32 -#define DIRSEP '\\' + /* let the real initer do the parsing */ + vgmstream = init_vgmstream_ogg_vorbis(streamData); + if (!vgmstream) goto fail; #else -#define DIRSEP '/' + goto fail; #endif -/* .sli is a file with loop points, associated with a similarly named .ogg */ + /* find loop text */ + { + char linebuffer[PATH_LIMIT]; + size_t bytes_read; + off_t sli_offset; + int done; -VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileOGG = NULL; - char filename[PATH_LIMIT]; - char filenameOGG[PATH_LIMIT]; - char linebuffer[PATH_LIMIT]; - off_t bytes_read; - off_t sli_offset; - int done; - int32_t loop_start = -1; - int32_t loop_length = -1; - int32_t loop_from = -1; - int32_t loop_to = -1; + sli_offset = 0; + while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(streamFile)) { + char *endptr, *foundptr; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sli",filename_extension(filename))) goto fail; + bytes_read = get_streamfile_text_line(sizeof(linebuffer),linebuffer,sli_offset,streamFile,&done); + if (!done) goto fail; - /* check for .OGG file */ - strcpy(filenameOGG,filename); - /* strip off .sli */ - filenameOGG[strlen(filenameOGG)-4]='\0'; + if (memcmp("LoopStart=",linebuffer,10)==0 && linebuffer[10] != '\0') { + loop_start = strtol(linebuffer+10,&endptr,10); + if (*endptr != '\0') { + loop_start = -1; /* if it didn't parse cleanly */ + } + } + else if (memcmp("LoopLength=",linebuffer,11)==0 && linebuffer[11] != '\0') { + loop_length = strtol(linebuffer+11,&endptr,10); + if (*endptr != '\0') { + loop_length = -1; /* if it didn't parse cleanly */ + } + } - streamFileOGG = streamFile->open(streamFile,filenameOGG,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileOGG) { - goto fail; + /* a completely different format (2.0?), also with .sli extension and can be handled similarly */ + if ((foundptr = strstr(linebuffer,"To=")) != NULL && isdigit(foundptr[3])) { + loop_to = strtol(foundptr+3,&endptr,10); + if (*endptr != ';') { + loop_to = -1; + } + } + if ((foundptr = strstr(linebuffer,"From=")) != NULL && isdigit(foundptr[5])) { + loop_from = strtol(foundptr+5,&endptr,10); + if (*endptr != ';') { + loop_from = -1; + } + } + + sli_offset += bytes_read; + } } - /* let the real initer do the parsing */ - vgmstream = init_vgmstream_ogg_vorbis(streamFileOGG); - if (!vgmstream) goto fail; - - close_streamfile(streamFileOGG); - streamFileOGG = NULL; - - sli_offset = 0; - while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(streamFile)) { - char *endptr; - char *foundptr; - bytes_read=get_streamfile_text_line(sizeof(linebuffer),linebuffer,sli_offset,streamFile,&done); - if (!done) goto fail; - - if (!memcmp("LoopStart=",linebuffer,10) && linebuffer[10]!='\0') { - loop_start = strtol(linebuffer+10,&endptr,10); - if (*endptr != '\0') { - /* if it didn't parse cleanly */ - loop_start = -1; - } - } - else if (!memcmp("LoopLength=",linebuffer,11) && linebuffer[11]!='\0') { - loop_length = strtol(linebuffer+11,&endptr,10); - if (*endptr != '\0') { - /* if it didn't parse cleanly */ - loop_length = -1; - } - } - - /* a completely different format, also with .sli extension and can be handled similarly */ - if ((foundptr=strstr(linebuffer,"To="))!=NULL && isdigit(foundptr[3])) { - loop_to = strtol(foundptr+3,&endptr,10); - if (*endptr != ';') { - loop_to = -1; - } - } - if ((foundptr=strstr(linebuffer,"From="))!=NULL && isdigit(foundptr[5])) { - loop_from = strtol(foundptr+5,&endptr,10); - if (*endptr != ';') { - loop_from = -1; - } - } - - sli_offset += bytes_read; + if (loop_start != -1 && loop_length != -1) { + vgmstream_force_loop(vgmstream,1,loop_start, loop_start+loop_length); + vgmstream->meta_type = meta_OGG_SLI; + } + else if (loop_from != -1 && loop_to != -1) { + vgmstream_force_loop(vgmstream,1,loop_to, loop_from); + vgmstream->meta_type = meta_OGG_SLI2; + } + else { + goto fail; /* if there's no loop points the .sli wasn't valid */ } - if ((loop_start != -1 && loop_length != -1) || - (loop_to != -1 && loop_from != -1)) { - /* install loops */ - if (!vgmstream->loop_flag) { - vgmstream->loop_flag = 1; - vgmstream->loop_ch = calloc(vgmstream->channels, - sizeof(VGMSTREAMCHANNEL)); - if (!vgmstream->loop_ch) goto fail; - } - - if (loop_to != -1 && loop_from != -1) { - vgmstream->loop_start_sample = loop_to; - vgmstream->loop_end_sample = loop_from; - vgmstream->meta_type = meta_OGG_SLI2; - } else { - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_start+loop_length; - vgmstream->meta_type = meta_OGG_SLI; - } - } else goto fail; /* if there's no loop points the .sli wasn't valid */ - + close_streamfile(streamData); return vgmstream; - /* clean up anything we may have opened */ fail: - if (streamFileOGG) close_streamfile(streamFileOGG); - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(streamData); + close_vgmstream(vgmstream); return NULL; } -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c index 6eca13000..ba0280fc7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c @@ -29,7 +29,7 @@ VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) { temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "vag"); if (!temp_streamFile) goto fail; - vgmstream = init_vgmstream_ps2_vag(temp_streamFile); + vgmstream = init_vgmstream_vag(temp_streamFile); if (!vgmstream) goto fail; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/spt_spd.c b/Frameworks/vgmstream/vgmstream/src/meta/spt_spd.c index 04b1a0a0f..5535a74b4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/spt_spd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/spt_spd.c @@ -71,12 +71,13 @@ VGMSTREAM * init_vgmstream_spt_spd(STREAMFILE *streamFile) { } vgmstream->meta_type = meta_SPT_SPD; - + vgmstream->allow_dual_stereo = 1; + /* open the file for reading */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x8000); + vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); vgmstream->ch[i].offset = 0; if (!vgmstream->ch[i].streamfile) goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index dd14dbf4a..900a32c8f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -257,11 +257,12 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); - //todo find if this actually helps - vgmstream->num_samples -= vgmstream->num_samples%576; - vgmstream->loop_start_sample -= vgmstream->loop_start_sample%576; - vgmstream->loop_end_sample -= vgmstream->loop_end_sample%576; - + /* somehow loops offsets aren't always frame-aligned, and the code below supposedly helped, + * but there isn't much difference since MPEG loops are rough (1152-aligned). Seems it + * would help more loop_start - ~1000, loop_end + ~1000 (ex. FFXIII-2 music_SunMizu.ps3.scd) */ + //vgmstream->num_samples -= vgmstream->num_samples % 576; + //vgmstream->loop_start_sample -= vgmstream->loop_start_sample % 576; + //vgmstream->loop_end_sample -= vgmstream->loop_end_sample % 576; break; } #endif @@ -367,21 +368,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; - /* 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 == 0x8) { - 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); - } + if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ + ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); /* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ - break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd_sscf.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd_sscf.c new file mode 100644 index 000000000..56b4377ee --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd_sscf.c @@ -0,0 +1,174 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* SSCF - Square-Enix games, older version of .scd [Crisis Core -Final Fantasy VII- (PSP), Dissidia 012 (PSP)] */ +VGMSTREAM * init_vgmstream_scd_sscf(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, meta_offset, stream_offset; + size_t stream_size; + int loop_flag, channel_count, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "scd")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x53534346) /* "SSCF" "*/ + goto fail; + if (!(read_32bitBE(0x04,streamFile) == 0x002070210 || /* version? [Crisis Core (PSP)] */ + read_32bitBE(0x04,streamFile) == 0x10020702)) /* inverted version? [Dissidia (PSP)] */ + goto fail; + /* 0x08: file size, except for a few files that with a weird value */ + /* 0x10: file id? */ + + + /* find total subsongs (entries can be dummy) and target subsong */ + { + int i,j, is_dupe; + int entries = read_32bitLE(0x0c,streamFile); + off_t stream_offsets[0x800]; + + if (entries > 0x800) /* meh */ + goto fail; + + + if (target_subsong == 0) target_subsong = 1; + + meta_offset = 0; + total_subsongs = 0; + for (i = 0; i < entries; i++) { + off_t entry_offset = 0x20 + (0x20*i); + off_t stream_offset; + + /* skip dummies */ + if (read_32bitLE(entry_offset+0x08,streamFile) == 0) /* size 0 */ + continue; + if (read_16bitLE(entry_offset+0x0c,streamFile) == 0) /* no sample rate */ + continue; + + /* skip repeated sounds */ + is_dupe = 0; + stream_offset = read_32bitLE(entry_offset+0x04,streamFile); + for (j = 0; j < total_subsongs; j++) { + if (stream_offset == stream_offsets[j]) { + is_dupe = 1; + break; + } + } + if (is_dupe) + continue; + stream_offsets[total_subsongs] = stream_offset; + + /* ok */ + total_subsongs++; + if (total_subsongs == target_subsong) { + meta_offset = entry_offset; + } + } + + if (meta_offset == 0) + goto fail; + } + + + /* 0x00(2): config? usually 0x00/0x01 */ + /* 0x02(2): loop config */ //todo when 1 uses PS-ADPCM uses SPU loop flags to set start/end + stream_offset = read_32bitLE(meta_offset+0x04,streamFile); /* absolute */ + stream_size = read_32bitLE(meta_offset+0x08,streamFile); + sample_rate = (uint16_t)read_16bitLE(meta_offset+0x0c,streamFile); + /* 0x0e: config? */ + /* 0x10: config? */ + /* 0x14: 0xCA5F or 0x5FCA */ + /* 0x18: config? */ + /* 0x1c: null / some id? */ + + loop_flag = 0; + channel_count = 1; + start_offset = stream_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); + if (loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + vgmstream->meta_type = meta_SCD_SSCF; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +#if 0 +/* SSCF X360 - updated SCD with encrypted data [Final Fantasy XI (360), PlayOnline Viewer (X360)] */ +VGMSTREAM * init_vgmstream_scd_sscf_x360(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, meta_offset, stream_offset; + size_t stream_size; + int loop_flag, channel_count, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "scd")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x53534346) /* "SSCF" "*/ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x20050300)/* version? */ + goto fail; + /* 0x08: file size, except for a few files that with a weird value */ + /* 0x0c: null */ + /* 0x10: file id? */ + /* 0x14: encryption key (different files with the same value encrypt the same) */ + + /* 0x1c: entry count */ + + /* ~0x20: entries start? */ + /* 0x40: num samples? */ + /* 0x44: loop start? */ + /* 0x50: channels */ + /* 0x54: sample rate */ + + /* 0x80: encrypted RIFF data */ + + + loop_flag = 0; + channel_count = 1; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ...; + + vgmstream->meta_type = meta_SCD_SSCF; + vgmstream->coding_type = coding_...; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sthd.c b/Frameworks/vgmstream/vgmstream/src/meta/sthd.c index 9c83a0134..5c025df65 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sthd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sthd.c @@ -28,8 +28,9 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *streamFile) { 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->sample_rate = read_32bitLE(0x20, streamFile); /* repeated ~8 times? */ + vgmstream->coding_type = coding_XBOX_IMA_int; vgmstream->layout_type = layout_blocked_sthd; @@ -45,8 +46,7 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *streamFile) { vgmstream->next_block_offset = start_offset; do { - block_update_sthd(vgmstream->next_block_offset,vgmstream); - + block_update(vgmstream->next_block_offset,vgmstream); if (block_count == loop_start_block) vgmstream->loop_start_sample = vgmstream->num_samples; if (block_count == loop_end_block) @@ -56,10 +56,9 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *streamFile) { block_count++; } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); } - block_update_sthd(start_offset, vgmstream); - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c new file mode 100644 index 000000000..ffb0c4032 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c @@ -0,0 +1,432 @@ +#include "meta.h" +#include "../coding/coding.h" + + +typedef enum { PSX, DSP, XBOX, WMA } strwav_codec; +typedef struct { + int32_t channels; + int32_t sample_rate; + int32_t num_samples; + int32_t loop_start; + int32_t loop_end; + int32_t loop_flag; + size_t interleave; + + off_t coefs_offset; + off_t dsps_table; + off_t coefs_table; + + uint32_t flags; + strwav_codec codec; +} strwav_header; + +static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav); + + +/* STR+WAV - Blitz Games streams+header */ +VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + strwav_header strwav = {0}; + off_t start_offset; + + + /* checks */ + if (!check_extensions(streamFile, "str")) + goto fail; + + /* get external header (extracted with filenames from bigfiles) */ + { + /* try with standard file.wav.str=body + file.wav=header (or file.wma.str + file.wma for Fuzion Frenzy (Xbox)) */ + char basename[PATH_LIMIT]; + get_streamfile_basename(streamFile,basename,PATH_LIMIT); + streamHeader = open_streamfile_by_filename(streamFile, basename); + if (!streamHeader) { + /* try again with file.str=body + file.wav=header [Bad Boys II (PS2), Zapper (PS2)] */ + streamHeader = open_streamfile_by_ext(streamFile, "wav"); + if (!streamHeader) { + /* try again with file.str=body + file.sth=header (renamed/fake extension) */ + streamHeader = open_streamfile_by_ext(streamFile, "sth"); + if (!streamHeader) goto fail; + } + } + else { + if (!check_extensions(streamHeader, "wav,wma")) + goto fail; + } + } + + /* detect version */ + if (!parse_header(streamHeader, &strwav)) + goto fail; + + if (strwav.flags == 0) + goto fail; + if (strwav.channels > 8) + goto fail; + + /* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */ + if (strwav.flags != 0x07 && strwav.flags != 0x06 && strwav.flags != 0x05 && strwav.flags != 0x04 && strwav.flags != 0x02) { + VGM_LOG("STR+WAV: unknown flags\n"); + goto fail; + } + + + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(strwav.channels,strwav.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = strwav.sample_rate; + vgmstream->num_samples = strwav.num_samples; + if (strwav.loop_flag) { + vgmstream->loop_start_sample = strwav.loop_start; + vgmstream->loop_end_sample = strwav.loop_end; + } + + vgmstream->meta_type = meta_STR_WAV; + + switch(strwav.codec) { + case PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = strwav.interleave; + break; + + case DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = strwav.interleave; + + /* get coefs */ + { + int i, ch; + for (ch = 0; ch < vgmstream->channels; ch++) { + for (i = 0; i < 16; i++) { + off_t coef_offset; + if (strwav.dsps_table) /* mini table with offsets to DSP headers */ + coef_offset = read_32bitBE(strwav.dsps_table+0x04*ch,streamHeader) + 0x1c; + else if (strwav.coefs_table) /* mini table with offsets to coefs then header */ + coef_offset = read_32bitBE(strwav.coefs_table+0x04*ch,streamHeader); + else + coef_offset = strwav.coefs_offset + 0x60*ch; + vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(coef_offset+i*0x02,streamHeader); + } + } + } + break; + + case XBOX: + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_interleave; /* interleaved stereo for >2ch*/ + vgmstream->interleave_block_size = strwav.interleave; + if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) + goto fail; /* only 2ch+..+2ch layout is known */ + break; + +#ifdef VGM_USE_FFMPEG + case WMA: { + ffmpeg_codec_data *ffmpeg_data = NULL; + + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,get_streamfile_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; + if (vgmstream->channels != ffmpeg_data->channels) + goto fail; + + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + close_streamfile(streamHeader); + return vgmstream; + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} + + + +/* Parse header versions. Almost every game uses its own variation (struct serialization?), + * so detection could be improved once enough versions are known. */ +static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) { + size_t header_size; + + if (read_32bitBE(0x00,streamHeader) != 0x00000000) + goto fail; + + header_size = get_streamfile_size(streamHeader); + + //todo loop start/end values may be off for some headers + + /* Fuzion Frenzy (Xbox)[2001] wma */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitLE(0x20,streamHeader) == 0 && /* no num_samples */ + read_32bitLE(0x24,streamHeader) == read_32bitLE(0x80,streamHeader) && /* sample rate repeat */ + read_32bitLE(0x28,streamHeader) == 0x10 && + header_size == 0x110 /* no value in header */ + ) { + strwav->num_samples = read_32bitLE(0x20,streamHeader); /* 0 but will be rectified later */ + strwav->sample_rate = read_32bitLE(0x24,streamHeader); + strwav->flags = read_32bitLE(0x2c,streamHeader); + strwav->loop_start = 0; + strwav->loop_end = 0; + + strwav->channels = read_32bitLE(0x60,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = 0; + + strwav->codec = WMA; + //;VGM_LOG("STR+WAV: header Fuzion Frenzy (Xbox)\n"); + return 1; + } + + /* Taz: Wanted (GC)[2002] */ + /* Cubix Robots for Everyone: Showdown (GC)[2003] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0x90,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitBE(0xa0,streamHeader) == header_size /* ~0x3C0 */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xb8,streamHeader); + strwav->loop_end = read_32bitBE(0xbc,streamHeader); + + strwav->channels = read_32bitBE(0x50,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->coefs_offset = 0xdc; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header Taz: Wanted (GC)\n"); + return 1; + } + + /* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitLE(0x24,streamHeader) == read_32bitLE(0xb0,streamHeader) && /* sample rate repeat */ + read_32bitLE(0x28,streamHeader) == 0x10 && + read_32bitLE(0xc0,streamHeader)*0x04 + read_32bitLE(0xc4,streamHeader) == header_size /* ~0xe0 + variable */ + ) { + strwav->num_samples = read_32bitLE(0x20,streamHeader); + strwav->sample_rate = read_32bitLE(0x24,streamHeader); + strwav->flags = read_32bitLE(0x2c,streamHeader); + strwav->loop_start = read_32bitLE(0x38,streamHeader); + strwav->loop_end = strwav->num_samples; + + strwav->channels = read_32bitLE(0x70,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = 0xD800/2; + + strwav->codec = XBOX; + //;VGM_LOG("STR+WAV: header The Fairly OddParents (Xbox)\n"); + return 1; + } + + /* Bad Boys II (GC)[2004] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xe0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == header_size /* variable + variable */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xd8,streamHeader); + strwav->loop_end = read_32bitBE(0xdc,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xe0; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header Bad Boys II (GC)\n"); + return 1; + } + + /* Bad Boys II (PS2)[2004] */ + /* Pac-Man World 3 (PS2)[2005] */ + if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || + read_32bitBE(0x04,streamHeader) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ + read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */ + read_32bitLE(0x28,streamHeader) == 0x10 && + read_32bitLE(0x78,streamHeader)*0x04 + read_32bitLE(0x7c,streamHeader) == header_size /* ~0xe0 + variable */ + ) { + strwav->num_samples = read_32bitLE(0x20,streamHeader); + strwav->sample_rate = read_32bitLE(0x24,streamHeader); + strwav->flags = read_32bitLE(0x2c,streamHeader); + strwav->loop_start = read_32bitLE(0x38,streamHeader); + strwav->interleave = read_32bitLE(0x40,streamHeader) == 1 ? 0x8000 : 0x4000; + strwav->loop_end = read_32bitLE(0x54,streamHeader); + + strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header Bad Boys II (PS2)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (PS2)[2005] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */ + read_32bitLE(0x28,streamHeader) == 0x10 && + read_32bitLE(0x7c,streamHeader) == header_size /* ~0xD0 */ + ) { + strwav->num_samples = read_32bitLE(0x20,streamHeader); + strwav->sample_rate = read_32bitLE(0x24,streamHeader); + strwav->flags = read_32bitLE(0x2c,streamHeader); + strwav->loop_start = read_32bitLE(0x38,streamHeader); + strwav->loop_end = read_32bitLE(0x54,streamHeader); + + strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header Zapper (PS2)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (GC)[2005] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0xB0,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitLE(0xc0,streamHeader) == header_size /* variable LE size */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xd8,streamHeader); + strwav->loop_end = read_32bitBE(0xdc,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xe0; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header Zapper (GC)\n"); + return 1; + } + + /* Pac-Man World 3 (GC)[2005] */ + /* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xf0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == read_32bitBE(0xe0,streamHeader) && /* main size */ + (read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */ + read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xd8,streamHeader); + strwav->loop_end = read_32bitBE(0xdc,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xf0; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header SpongeBob SquarePants (GC)\n"); + return 1; + } + + /* SpongeBob SquarePants: Creature from the Krusty Krab (PS2)[2006] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitLE(0x08,streamHeader) == 0x00000000 && + read_32bitLE(0x0c,streamHeader) != header_size && /* some ID */ + (header_size == 0x74 + read_32bitLE(0x64,streamHeader)*0x04 || + header_size == 0x78 + read_32bitLE(0x64,streamHeader)*0x04) + ) { + strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok? + strwav->num_samples = read_32bitLE(0x30,streamHeader); + strwav->loop_end = read_32bitLE(0x34,streamHeader); + strwav->sample_rate = read_32bitLE(0x38,streamHeader); + strwav->flags = read_32bitLE(0x3c,streamHeader); + + strwav->channels = read_32bitLE(0x64,streamHeader); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header SpongeBob SquarePants (PS2)\n"); + return 1; + } + + /* Tak and the Guardians of Gross (PS2)[2008] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitLE(0x08,streamHeader) != 0x00000000 && + read_32bitLE(0x0c,streamHeader) == header_size && /* ~0x80+0x04*ch */ + read_32bitLE(0x0c,streamHeader) == 0x80 + read_32bitLE(0x70,streamHeader)*0x04 + ) { + strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok? + strwav->num_samples = read_32bitLE(0x30,streamHeader); + strwav->loop_end = read_32bitLE(0x34,streamHeader); + strwav->sample_rate = read_32bitLE(0x38,streamHeader); + strwav->flags = read_32bitLE(0x3c,streamHeader); + + strwav->channels = read_32bitLE(0x70,streamHeader); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + //;VGM_LOG("STR+WAV: header Tak (PS2)\n"); + return 1; + } + + /* Tak and the Guardians of Gross (Wii)[2008] */ + /* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */ + if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || + read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */ + read_32bitLE(0x08,streamHeader) != 0x00000000 && + read_32bitBE(0x0c,streamHeader) == header_size && /* variable per DSP header */ + read_32bitBE(0x38,streamHeader) == read_32bitBE(read_32bitBE(0x7c,streamHeader)+0x38,streamHeader) /* sample rate vs 1st DSP header */ + ) { + strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok? + strwav->num_samples = read_32bitBE(0x30,streamHeader); + strwav->loop_end = read_32bitBE(0x34,streamHeader); + strwav->sample_rate = read_32bitBE(0x38,streamHeader); + strwav->flags = read_32bitBE(0x3c,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->coefs_table = 0x7c; + strwav->codec = DSP; + //;VGM_LOG("STR+WAV: header Tak (Wii)\n"); + return 1; + } + + + /* unknown */ + goto fail; + +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/stx.c b/Frameworks/vgmstream/vgmstream/src/meta/stx.c index ea8c845aa..3c8f3f922 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/stx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/stx.c @@ -49,7 +49,7 @@ VGMSTREAM * init_vgmstream_stx(STREAMFILE *streamFile) { int i; /* both channels use same buffer, as interleave is so small */ - chstreamfile = streamFile->open(streamFile,filename,9*channel_count*0x100); + chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!chstreamfile) goto fail; for (i=0;imeta_type = meta_SVG; + vgmstream->sample_rate = read_32bitBE(0x2c,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + read_string(vgmstream->stream_name,0x10+1, 0x04,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/sxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c index 8a8c85ac1..1e0420dfb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c @@ -9,7 +9,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0; size_t chunk_size, stream_size = 0; - int is_dual, is_external; + int is_dual = 0, is_external = 0; int loop_flag, channels, codec, location; int sample_rate, num_samples, loop_start_sample, loop_end_sample; uint32_t at9_config_data = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/tun.c b/Frameworks/vgmstream/vgmstream/src/meta/tun.c index cf270679a..b49e9ba92 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/tun.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/tun.c @@ -7,11 +7,9 @@ VGMSTREAM * init_vgmstream_tun(STREAMFILE *streamFile) { off_t start_offset; int loop_flag, channel_count; - /* check extension */ + /* checks */ if ( !check_extensions(streamFile,"tun") ) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x414C5020) /* "ALP " */ goto fail; @@ -28,12 +26,11 @@ VGMSTREAM * init_vgmstream_tun(STREAMFILE *streamFile) { vgmstream->sample_rate = 22050; vgmstream->num_samples = ima_bytes_to_samples(get_streamfile_size(streamFile) - 0x10, channel_count); - vgmstream->coding_type = coding_DVI_IMA_int; + vgmstream->coding_type = coding_ALP_IMA; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x01; vgmstream->meta_type = meta_TUN; - /* 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/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 235b9ddd8..cebdfea39 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -47,7 +47,7 @@ typedef struct { int data_size_set; uint32_t start_offset; - int sample_type_bytes; + int sample_type; uint32_t num_samples; uint32_t loop_start_sample; uint32_t loop_end_sample; @@ -57,6 +57,7 @@ typedef struct { uint32_t loop_flag; int loop_flag_set; + int loop_flag_auto; uint32_t coef_offset; uint32_t coef_spacing; @@ -132,6 +133,13 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { goto fail; } + /* try to autodetect PS-ADPCM loop data */ + if (txth.loop_flag_auto && coding == coding_PSX) { + size_t data_size = get_streamfile_size(streamFile) - txth.start_offset; + txth.loop_flag = ps_find_loop_offsets(streamFile, txth.start_offset, data_size, txth.channels, txth.interleave, + (int32_t*)&txth.loop_start_sample, (int32_t*)&txth.loop_end_sample); + } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(txth.channels,txth.loop_flag); @@ -215,14 +223,16 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; break; case coding_XBOX_IMA: - if (txth.codec_mode == 1) { - if (!txth.interleave) goto fail; /* creates garbage */ + if (txth.codec_mode == 1) { /* mono interleave */ coding = coding_XBOX_IMA_int; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = txth.interleave; } - else { - vgmstream->layout_type = layout_none; + else { /* 1ch mono, or stereo interleave */ + vgmstream->layout_type = txth.interleave ? layout_interleave : layout_none; + vgmstream->interleave_block_size = txth.interleave; + if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) + goto fail; /* only 2ch+..+2ch layout is known */ } break; case coding_NGC_DTK: @@ -315,8 +325,10 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode); } else if (txth.codec == XMA2) { - int block_size = txth.interleave ? txth.interleave : 2048; - int block_count = txth.data_size / block_size; + int block_count, block_size; + + block_size = txth.interleave ? txth.interleave : 2048; + block_count = txth.data_size / block_size; bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); } @@ -345,7 +357,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { } #ifdef VGM_USE_FFMPEG - if ((txth.sample_type_bytes || txth.num_samples_data_size) && (txth.codec == XMA1 || txth.codec == XMA2)) { + if ((txth.sample_type==1 || txth.num_samples_data_size) && (txth.codec == XMA1 || txth.codec == XMA2)) { /* manually find sample offsets */ ms_sample_data msd = {0}; @@ -353,7 +365,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { msd.channels = txth.channels; msd.data_offset = txth.start_offset; msd.data_size = txth.data_size; - if (txth.sample_type_bytes) { + if (txth.sample_type==1) { msd.loop_flag = txth.loop_flag; msd.loop_start_b = txth.loop_start_sample; msd.loop_end_b = txth.loop_end_sample; @@ -364,7 +376,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { xma_get_samples(&msd, streamFile); vgmstream->num_samples = msd.num_samples; - if (txth.sample_type_bytes) { + if (txth.sample_type==1) { vgmstream->loop_start_sample = msd.loop_start_sample; vgmstream->loop_end_sample = msd.loop_end_sample; } @@ -374,6 +386,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->coding_type = coding; vgmstream->meta_type = meta_TXTH; + vgmstream->allow_dual_stereo = 1; if ( !vgmstream_open_stream(vgmstream,streamFile,txth.start_offset) ) @@ -397,7 +410,7 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { /* try "(path/)(name.ext).txth" */ get_streamfile_name(streamFile,filename,PATH_LIMIT); strcat(filename, ".txth"); - streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + streamText = open_streamfile(streamFile,filename); if (streamText) return streamText; /* try "(path/)(.ext).txth" */ @@ -406,13 +419,13 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { strcat(filename,"."); strcat(filename, fileext); strcat(filename, ".txth"); - streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + streamText = open_streamfile(streamFile,filename); if (streamText) return streamText; /* try "(path/).txth" */ get_streamfile_path(streamFile,filename,PATH_LIMIT); strcat(filename, ".txth"); - streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + streamText = open_streamfile(streamFile,filename); if (streamText) return streamText; /* not found */ @@ -428,7 +441,7 @@ static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_hea txth->data_size = get_streamfile_size(streamFile); /* for later use */ /* skip BOM if needed */ - if (read_16bitLE(0x00, streamText) == 0xFFFE || read_16bitLE(0x00, streamText) == 0xFEFF) + if ((uint16_t)read_16bitLE(0x00, streamText) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamText) == 0xFEFF) txt_offset = 0x02; /* read lines */ @@ -493,6 +506,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h } else if (0==strcmp(key,"interleave")) { if (0==strcmp(val,"half_size")) { + if (txth->channels == 0) goto fail; txth->interleave = txth->data_size / txth->channels; } else { @@ -523,8 +537,9 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h txth->data_size_set = 1; } else if (0==strcmp(key,"sample_type")) { - if (0==strcmp(val,"bytes")) txth->sample_type_bytes = 1; - else if (0==strcmp(val,"samples")) txth->sample_type_bytes = 0; + if (0==strcmp(val,"samples")) txth->sample_type = 0; + else if (0==strcmp(val,"bytes")) txth->sample_type = 1; + else if (0==strcmp(val,"blocks")) txth->sample_type = 2; else goto fail; } else if (0==strcmp(key,"num_samples")) { @@ -534,14 +549,18 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h } else { if (!parse_num(streamFile,val, &txth->num_samples)) goto fail; - if (txth->sample_type_bytes) + if (txth->sample_type==1) txth->num_samples = get_bytes_to_samples(txth, txth->num_samples); + if (txth->sample_type==2) + txth->num_samples = get_bytes_to_samples(txth, txth->num_samples * (txth->interleave*txth->channels)); } } else if (0==strcmp(key,"loop_start_sample")) { if (!parse_num(streamFile,val, &txth->loop_start_sample)) goto fail; - if (txth->sample_type_bytes) + if (txth->sample_type==1) txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample); + if (txth->sample_type==2) + txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample * (txth->interleave*txth->channels)); if (txth->loop_adjust) txth->loop_start_sample += txth->loop_adjust; } @@ -551,8 +570,10 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h } else { if (!parse_num(streamFile,val, &txth->loop_end_sample)) goto fail; - if (txth->sample_type_bytes) + if (txth->sample_type==1) txth->loop_end_sample = get_bytes_to_samples(txth, txth->loop_end_sample); + if (txth->sample_type==2) + txth->loop_end_sample = get_bytes_to_samples(txth, txth->loop_end_sample * (txth->interleave*txth->channels)); } if (txth->loop_adjust) txth->loop_end_sample += txth->loop_adjust; @@ -560,17 +581,26 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h else if (0==strcmp(key,"skip_samples")) { if (!parse_num(streamFile,val, &txth->skip_samples)) goto fail; txth->skip_samples_set = 1; - if (txth->sample_type_bytes) + if (txth->sample_type==1) txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples); + if (txth->sample_type==2) + txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples * (txth->interleave*txth->channels)); } else if (0==strcmp(key,"loop_adjust")) { if (!parse_num(streamFile,val, &txth->loop_adjust)) goto fail; - if (txth->sample_type_bytes) + if (txth->sample_type==1) txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust); + if (txth->sample_type==2) + txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust * (txth->interleave*txth->channels)); } else if (0==strcmp(key,"loop_flag")) { - if (!parse_num(streamFile,val, &txth->loop_flag)) goto fail; - txth->loop_flag_set = 1; + if (0==strcmp(val,"auto")) { + txth->loop_flag_auto = 1; + } + else { + if (!parse_num(streamFile,val, &txth->loop_flag)) goto fail; + txth->loop_flag_set = 1; + } } else if (0==strcmp(key,"coef_offset")) { if (!parse_num(streamFile,val, &txth->coef_offset)) goto fail; @@ -588,6 +618,9 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h else if (0==strcmp(key,"coef_mode")) { if (!parse_num(streamFile,val, &txth->coef_mode)) goto fail; } + else if (0==strcmp(key,"psx_loops")) { + if (!parse_num(streamFile,val, &txth->coef_mode)) goto fail; + } else { VGM_LOG("TXTH: unknown key=%s, val=%s\n", key,val); goto fail; @@ -697,7 +730,7 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { case SDX2: return bytes; case NGC_DTK: - return bytes / 32 * 28; /* always stereo? */ + return bytes / 0x20 * 28; /* always stereo */ case APPLE_IMA4: if (!txth->interleave) return 0; return (bytes / txth->interleave) * (txth->interleave - 2) * 2; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index a0a8dcf22..a020c0d4b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -9,6 +9,15 @@ typedef struct { char filename[TXT_LINE_MAX]; int subsong; uint32_t channel_mask; + int channel_mappings_on; + int channel_mappings[32]; + + double config_loop_count; + double config_fade_time; + double config_fade_delay; + int config_ignore_loop; + int config_force_loop; + int config_ignore_fade; } txtp_entry; typedef struct { @@ -24,9 +33,10 @@ typedef struct { static txtp_header* parse_txtp(STREAMFILE* streamFile); static void clean_txtp(txtp_header* txtp); +static void set_config(VGMSTREAM *vgmstream, txtp_entry *current); -/* TXTP - an artificial playlist-like format to play segmented files with config */ +/* TXTP - an artificial playlist-like format to play files with segments/layers/config */ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; txtp_header* txtp = NULL; @@ -59,6 +69,13 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->channel_mask = txtp->entry[0].channel_mask; + + vgmstream->channel_mappings_on = txtp->entry[0].channel_mappings_on; + if(vgmstream->channel_mappings_on) { + for (i = 0; i < 32; i++) { + vgmstream->channel_mappings[i] = txtp->entry[0].channel_mappings[i]; + } + } } else if (txtp->is_layered) { /* layered multi file */ @@ -103,6 +120,13 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { vgmstream->channel_mask = txtp->entry[0].channel_mask; + vgmstream->channel_mappings_on = txtp->entry[0].channel_mappings_on; + if (vgmstream->channel_mappings_on) { + for (i = 0; i < 32; i++) { + vgmstream->channel_mappings[i] = txtp->entry[0].channel_mappings[i]; + } + } + vgmstream->layout_data = data_l; } else { @@ -164,13 +188,13 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { vgmstream->meta_type = meta_TXTP; vgmstream->coding_type = data_s->segments[0]->coding_type; vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data_s; - if (loop_flag) - data_s->loop_segment = txtp->loop_start_segment-1; } + /* loop settings apply to the resulting vgmstream, so config based on first entry */ + set_config(vgmstream, &txtp->entry[0]); + clean_txtp(txtp); return vgmstream; @@ -182,47 +206,68 @@ fail: return NULL; } +static void set_config(VGMSTREAM *vgmstream, txtp_entry *current) { + vgmstream->config_loop_count = current->config_loop_count; + vgmstream->config_fade_time = current->config_fade_time; + vgmstream->config_fade_delay = current->config_fade_delay; + vgmstream->config_ignore_loop = current->config_ignore_loop; + vgmstream->config_force_loop = current->config_force_loop; + vgmstream->config_ignore_fade = current->config_ignore_fade; +} + +/* ********************************** */ + +static void get_double(const char * config, double *value) { + int n; + if (sscanf(config, "%lf%n", value,&n) != 1) { + *value = 0; + } +} static int add_filename(txtp_header * txtp, char *filename) { - int i; - uint32_t channel_mask = 0; + int i, n; + txtp_entry cfg = {0}; size_t range_start, range_end; + const char separator = '#'; + //;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 */ + /* parse config: file.ext#(command) */ { char *config; - /* position in base extension */ - config = strrchr(filename,'.'); - if (!config) /* needed...? */ + /* find config start (filenames and config can contain multiple dots and #, + * so this may be fooled by certain patterns of . and #) */ + config = strchr(filename, '.'); /* first dot (may be a false positive) */ + if (!config) /* extensionless */ + config = filename; + config = strchr(config,separator); /* next should be config (hopefully right after extension) */ + if (!config) /* no config */ config = filename; range_start = 0; range_end = 1; do { /* get config pointer but remove config from filename */ - config = strchr(config, '#'); + config = strchr(config, separator); if (!config) continue; + //;VGM_LOG("TXTP: config=%s\n", config); config[0] = '\0'; config++; if (config[0] == 'c') { - /* mask channels */ - int n, ch; + /* channel mask: file.ext#c1,2 = play channels 1,2 and mutes rest */ + int ch; config++; - channel_mask = 0; + cfg.channel_mask = 0; while (sscanf(config, "%d%n", &ch,&n) == 1) { - if (ch > 0 && ch < 32) - channel_mask |= (1 << (ch-1)); + if (ch > 0 && ch <= 32) + cfg.channel_mask |= (1 << (ch-1)); config += n; if (config[0]== ',' || config[0]== '-') /* "-" for PowerShell, may have problems with "," */ @@ -231,10 +276,41 @@ static int add_filename(txtp_header * txtp, char *filename) { break; }; } - else { - /* subsong range */ + else if (config[0] == 'm') { + /* channel mappings: file.ext#m1-2,3-4 = swaps channels 1<>2 and 3<>4 */ + int ch_from = 0, ch_to = 0; + + config++; + cfg.channel_mappings_on = 1; + + while (config[0] != '\0') { + if (sscanf(config, "%d%n", &ch_from, &n) != 1) + break; + config += n; + if (config[0]== ',' || config[0]== '-') + config++; + else if (config[0] != '\0') + break; + + if (sscanf(config, "%d%n", &ch_to, &n) != 1) + break; + config += n; + if (config[0]== ',' || config[0]== '-') + config++; + else if (config[0] != '\0') + break; + + if (ch_from > 0 && ch_from <= 32 && ch_to > 0 && ch_to <= 32) { + cfg.channel_mappings[ch_from-1] = ch_to-1; + } + } + } + else if (config[0] == 's' || (config[0] >= '0' && config[0] <= '9')) { + /* subsongs: file.ext#s2 = play subsong 2, file.ext#2~10 = play subsong range */ int subsong_start = 0, subsong_end = 0; + if (config[0]== 's') + config++; if (sscanf(config, "%d~%d", &subsong_start, &subsong_end) == 2) { if (subsong_start > 0 && subsong_end > 0) { range_start = subsong_start-1; @@ -251,6 +327,37 @@ static int add_filename(txtp_header * txtp, char *filename) { config = NULL; /* wrong config, ignore */ } } + else if (config[0] == 'i') { + config++; + cfg.config_ignore_loop = 1; + } + else if (config[0] == 'E') { + config++; + cfg.config_force_loop = 1; + } + else if (config[0] == 'F') { + config++; + cfg.config_ignore_fade = 1; + } + else if (config[0] == 'l') { + config++; + get_double(config, &cfg.config_loop_count); + } + else if (config[0] == 'f') { + config++; + get_double(config, &cfg.config_fade_time); + } + else if (config[0] == 'd') { + config++; + get_double(config, &cfg.config_fade_delay); + } + else if (config[0] == ' ') { + continue; /* likely a comment, find next # */ + } + else { + //;VGM_LOG("TXTP: unknown command '%c'\n", config[0]); + break; /* also possibly a comment too */ + } } while (config != NULL); @@ -273,6 +380,8 @@ static int add_filename(txtp_header * txtp, char *filename) { /* add filesnames */ for (i = range_start; i < range_end; i++){ + txtp_entry *current; + /* resize in steps if not enough */ if (txtp->entry_count+1 > txtp->entry_max) { txtp_entry *temp_entry; @@ -284,11 +393,29 @@ static int add_filename(txtp_header * txtp, char *filename) { } /* new entry */ - memset(&txtp->entry[txtp->entry_count],0, sizeof(txtp_entry)); + current = &txtp->entry[txtp->entry_count]; + memset(current,0, sizeof(txtp_entry)); + strcpy(current->filename, filename); + + current->subsong = (i+1); + + current->channel_mask = cfg.channel_mask; + + if (cfg.channel_mappings_on) { + int ch; + current->channel_mappings_on = cfg.channel_mappings_on; + for (ch = 0; ch < 32; ch++) { + current->channel_mappings[ch] = cfg.channel_mappings[ch]; + } + } + + current->config_loop_count = cfg.config_loop_count; + current->config_fade_time = cfg.config_fade_time; + current->config_fade_delay = cfg.config_fade_delay; + current->config_ignore_loop = cfg.config_ignore_loop; + current->config_force_loop = cfg.config_force_loop; + current->config_ignore_fade = cfg.config_ignore_fade; - 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++; } @@ -363,7 +490,7 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { /* skip BOM if needed */ - if (read_16bitLE(0x00, streamFile) == 0xFFFE || read_16bitLE(0x00, streamFile) == 0xFEFF) + if ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF) txt_offset = 0x02; /* read lines */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index 1ce698ccf..329bc4bc4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -4,6 +4,7 @@ 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 { + int version; ubi_bao_codec codec; int big_endian; int total_subsongs; @@ -12,9 +13,14 @@ typedef struct { size_t header_size; size_t stream_size; off_t stream_offset; + size_t prefetch_size; + off_t prefetch_offset; + size_t main_size; + off_t main_offset; uint32_t stream_id; off_t extradata_offset; int is_external; + int is_prefetched; int header_codec; int num_samples; @@ -23,26 +29,28 @@ typedef struct { char resource_name[255]; int types_count[9]; + int subtypes_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); +static STREAMFILE * setup_bao_streamfile(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}; + 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+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) ) + /* main parse */ + if (!parse_pk_header(&bao, streamFile)) goto fail; return init_vgmstream_ubi_bao_main(&bao, streamFile); @@ -53,7 +61,7 @@ fail: #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}; + ubi_bao_header bao = { 0 }; /* checks */ if (!check_extensions(streamFile, "bao")) @@ -64,8 +72,8 @@ VGMSTREAM * init_vgmstream_ubi_bao_file(STREAMFILE *streamFile) { * 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) ) + /* main parse */ + if (!parse_bao_header(&bao, streamFile)) goto fail; return init_vgmstream_ubi_bao_main(&bao, streamFile); @@ -75,30 +83,17 @@ fail: #endif -static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE *streamFile) { +static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE * streamFile) { VGMSTREAM * vgmstream = NULL; - STREAMFILE *streamData = NULL; - off_t start_offset; + STREAMFILE * streamData = NULL; + off_t start_offset = 0x00; 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; - + streamData = setup_bao_streamfile(bao, streamFile); + if (!streamData) goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(bao->channels,loop_flag); + vgmstream = allocate_vgmstream(bao->channels, loop_flag); if (!vgmstream) goto fail; vgmstream->num_samples = bao->num_samples; @@ -107,14 +102,12 @@ static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE vgmstream->stream_size = bao->stream_size; vgmstream->meta_type = meta_UBI_BAO; - switch(bao->codec) { -#if 0 + switch (bao->codec) { 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 */ @@ -132,22 +125,71 @@ static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE 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); + 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; + uint32_t num_frames; + size_t bytes, chunk_size, frame_size, data_size; + int is_xma2_old; + STREAMFILE *header_data; + off_t header_offset; - chunk_size = (bao->codec == RAW_XMA1) ? 0x20 : 0x34; + if (bao->version == 0x00230008) { + is_xma2_old = 1; + chunk_size = 0x2c; + } + else { + is_xma2_old = 0; + 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; + if (bao->is_external) { + /* external XMA sounds have a custom header */ + /* first there's XMA2/FMT chunk, after that: */ + /* 0x00: some low number like 0x01 or 0x04 */ + /* 0x04: number of frames */ + /* 0x08: frame size (not always present?) */ + /* then there's a set of rising numbers followed by some weird data?.. */ + /* calculate true XMA size and use that get data start offset */ + num_frames = read_32bitBE(start_offset + chunk_size + 0x04, streamData); + //frame_size = read_32bitBE(start_offset + chunk_size + 0x08, streamData); + frame_size = 0x800; + + data_size = num_frames * frame_size; + start_offset = bao->stream_size - data_size; + } + else { + data_size = bao->stream_size; + start_offset = 0x00; + } + + /* XMA header is stored in 0x20 header for internal sounds and before audio data for external sounds */ + if (bao->is_external) { + header_data = streamData; + header_offset = 0x00; + } + else { + header_data = streamFile; + header_offset = bao->extradata_offset; + } + + if (is_xma2_old) { + bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf, 0x100, header_offset, chunk_size, data_size, header_data); + } + else { + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, data_size, header_data, 1); + } + + vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, data_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + + vgmstream->stream_size = data_size; break; } @@ -159,8 +201,8 @@ static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE 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); + bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, vgmstream->stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); + vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, bao->stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -171,24 +213,13 @@ static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE ffmpeg_codec_data *ffmpeg_data; ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, bao->stream_size); - if ( !ffmpeg_data ) goto fail; + 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); - } + if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ + ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamData, start_offset)); break; } @@ -196,7 +227,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE ffmpeg_codec_data *ffmpeg_data; ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, bao->stream_size); - if ( !ffmpeg_data ) goto fail; + if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -212,14 +243,14 @@ static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE } /* open the file for reading (can be an external stream, different from the current .pk) */ - if ( !vgmstream_open_stream(vgmstream, streamData, start_offset) ) + if (!vgmstream_open_stream(vgmstream, streamData, start_offset)) goto fail; - if (bao->is_external && streamData) close_streamfile(streamData); + close_streamfile(streamData); return vgmstream; fail: - if (bao->is_external && streamData) close_streamfile(streamData); + close_streamfile(streamData); close_vgmstream(vgmstream); return NULL; } @@ -269,6 +300,8 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { ;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]); + ;VGM_LOG("BAO 0x20 subtypes: 01=%i,02=%i,03=%i,04=%i,05=%i,06=%i,07=%i,08=%i\n", + bao->types_count[1],bao->subtypes_count[2],bao->subtypes_count[3],bao->subtypes_count[4],bao->subtypes_count[5],bao->subtypes_count[6],bao->subtypes_count[7],bao->subtypes_count[8]); if (bao->total_subsongs == 0) { VGM_LOG("UBI BAO: no streams\n"); @@ -279,10 +312,35 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { /* 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); + int resources_count; + size_t strings_size; + + /* some sounds have a prefetched bit stored internally with the remaining streamed part stored externally */ + 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->prefetch_offset = bao_offset + bao->header_size; + break; + } + + bao_offset += bao_size; + } + + if (bao->prefetch_size) { + if (bao->prefetch_offset == 0) goto fail; + bao->is_prefetched = 1; + } + else { + if (bao->prefetch_offset != 0) goto fail; + } + + /* parse resource table, LE (may be empty, or exist even with nothing in the file) */ + resources_count = read_32bitLE(resources_offset+0x00, streamFile); + strings_size = read_32bitLE(resources_offset+0x04, streamFile); offset = resources_offset + 0x04+0x04 + strings_size; for (i = 0; i < resources_count; i++) { @@ -295,37 +353,29 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { 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"); + if (bao->is_prefetched) { + bao->main_offset = resource_offset + bao->header_size; + bao->main_size = resource_size - bao->header_size; + VGM_ASSERT(bao->stream_size != bao->main_size + bao->prefetch_size, "UBI BAO: stream vs resource size mismatch\n"); + } + else { + 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) { + /* in some cases, stream size value from 0x20 header can be bigger than */ + /* the actual audio chunk o_O [Rayman Raving Rabbids: TV Party (Wii)] */ + bao->stream_size = bao_size - bao->header_size; bao->stream_offset = bao_offset + bao->header_size; /* relative, adjust to skip descriptor */ break; } @@ -339,7 +389,7 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { 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")); + ;VGM_LOG("BAO stream: id=%x, offset=%"PRIx64", size=%x, res=%s\n", bao->stream_id, (off64_t)bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal")); return 1; fail: @@ -349,7 +399,7 @@ fail: /* 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; + uint32_t bao_version, descriptor_type, descriptor_subtype; size_t header_size; int target_subsong = streamFile->stream_index; @@ -370,7 +420,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) /* 0x18: null */ /* 0x1c: null */ descriptor_type = read_32bit(offset+0x20, streamFile); - /* 0x28: subtype? usually 0x02/0x01, games may crash if changed */ + descriptor_subtype = read_32bit(offset+header_size+0x04, streamFile); /* for debugging purposes */ switch(descriptor_type) { @@ -379,18 +429,35 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) 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 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); + VGM_LOG("UBI BAO: unknown type %x at %"PRIx64" + %x\n", descriptor_type, (off64_t)offset, 0x20); 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) + + /* for debugging purposes */ + switch(descriptor_subtype) { + case 0x00000001: bao->subtypes_count[1]++; break; /* standard */ + case 0x00000002: bao->subtypes_count[2]++; break; /* multilayer??? related to other header BAOs? */ + case 0x00000003: bao->subtypes_count[3]++; break; /* related to other header BAOs? */ + case 0x00000004: bao->subtypes_count[4]++; break; /* related to other header BAOs? */ + case 0x00000005: bao->subtypes_count[5]++; break; /* related to other header BAOs? */ + case 0x00000006: bao->subtypes_count[6]++; break; /* some multilayer/table? may contain sounds??? */ + case 0x00000007: bao->subtypes_count[7]++; break; /* related to other header BAOs? */ + case 0x00000008: bao->subtypes_count[8]++; break; /* ? (almost empty with some unknown value) */ + default: + VGM_LOG("UBI BAO: unknown subtype %x at %"PRIx64" + %x\n", descriptor_subtype, (off64_t)offset, header_size+0x04); + goto fail; + } + //;VGM_ASSERT(descriptor_subtype != 0x01, "UBI BAO: subtype %x at %lx (%lx)\n", descriptor_subtype, offset, offset+header_size+0x04); + + /* ignore unknown subtypes */ + if (descriptor_subtype != 0x00000001) return 1; bao->total_subsongs++; @@ -401,18 +468,18 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) /* 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) + * - subtype (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) { + ;VGM_LOG("BAO header at %"PRIx64"\n", (off64_t)offset); + bao->version = bao_version; + switch(bao->version) { + case 0x001F0008: /* Rayman Raving Rabbids: TV Party (Wii)-pk */ case 0x001F0011: /* Naruto: The Broken Bond (X360)-pk */ case 0x0022000D: /* Just Dance (Wii)-pk */ bao->stream_size = read_32bit(offset+header_size+0x08, streamFile); @@ -420,24 +487,31 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) 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); + if (read_32bit(offset + header_size + 0x34, streamFile) & 0x01) { /* single flag? */ + bao->num_samples = read_32bit(offset + header_size + 0x5c, streamFile); + } + else { + 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 0x02: bao->codec = FMT_OGG; break; + case 0x03: bao->codec = UBI_ADPCM; 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; + default: VGM_LOG("UBI BAO: unknown codec at %"PRIx64"\n", (off64_t)offset); goto fail; } - //todo use flags? - if (bao->header_codec == 0x09) { + bao->prefetch_size = read_32bit(offset + header_size + 0x74, streamFile); + + if (bao->codec == RAW_DSP) { bao->extradata_offset = offset+header_size+0x80; /* mini DSP header */ } - if (bao->header_codec == 0x05 && !bao->is_external) { + if (bao->codec == RAW_XMA1 && !bao->is_external) { bao->extradata_offset = offset+header_size + 0x7c; /* XMA header */ } - //todo external XMA may use blocked layout + layered layout break; @@ -459,16 +533,46 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) 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; + default: VGM_LOG("UBI BAO: unknown codec at %"PRIx64"\n", (off64_t)offset); goto fail; } if (read_32bit(offset+header_size+0x20, streamFile) & 0x10) { - VGM_LOG("UBI BAO: possible full loop at %lx\n", offset); + VGM_LOG("UBI BAO: possible full loop at %"PRIx64"\n", (off64_t)offset); /* RIFFs may have "smpl" and this flag, even when data shouldn't loop... */ } break; + case 0x00230008: /* Splinter Cell: Conviction (X360/PC)-pk */ + 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+0x38, streamFile); + bao->channels = read_32bit(offset+header_size+0x54, streamFile); + bao->sample_rate = read_32bit(offset+header_size+0x5c, streamFile); + if (read_32bit(offset+header_size+0x44, streamFile) & 0x01) { /* single flag? */ + bao->num_samples = read_32bit(offset+header_size+0x6c, streamFile); + } + else { + bao->num_samples = read_32bit(offset+header_size+0x64, streamFile); + } + bao->header_codec = read_32bit(offset+header_size+0x74, streamFile); + + switch (bao->header_codec) { + case 0x01: bao->codec = RAW_PCM; break; + case 0x02: bao->codec = UBI_ADPCM; break; + case 0x03: bao->codec = FMT_OGG; break; + case 0x04: bao->codec = RAW_XMA2; break; + default: VGM_LOG("UBI BAO: unknown codec at %"PRIx64"\n", (off64_t)offset); goto fail; + } + + bao->prefetch_size = read_32bit(offset+header_size+0x84, streamFile); + + if (bao->codec == RAW_XMA2 && !bao->is_external) { + bao->extradata_offset = offset + header_size + 0x8c; /* XMA header */ + } + + 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); @@ -487,13 +591,17 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) switch(bao->header_codec) { case 0x01: bao->codec = RAW_PCM; break; + case 0x02: bao->codec = UBI_ADPCM; break; /* assumed */ + case 0x03: bao->codec = FMT_OGG; break; /* assumed */ 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; + default: VGM_LOG("UBI BAO: unknown codec at %"PRIx64"\n", (off64_t)offset); goto fail; } - if (bao->header_codec == 0x04 && !bao->is_external) { + bao->prefetch_size = read_32bit(offset + header_size + 0x78, streamFile); + + if (bao->codec == RAW_XMA2 && !bao->is_external) { bao->extradata_offset = offset+header_size + 0x8c; /* XMA header */ } @@ -504,8 +612,8 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) 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); + default: /* others possibly using BAO: Avatar X360/PS3/PC, Just Dance, Watch_Dogs, Far Cry Primal, Far Cry 4 */ + VGM_LOG("UBI BAO: unknown BAO version at %"PRIx64"\n", (off64_t)offset); goto fail; } @@ -515,3 +623,63 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) fail: return 0; } + +static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile) { + STREAMFILE *new_streamFile = NULL; + STREAMFILE *temp_streamFile = NULL; + STREAMFILE *stream_segments[2] = { 0 }; + + if (bao->is_prefetched) { + /* need to join prefetched and streamed part as they're stored separately */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, bao->prefetch_offset, bao->prefetch_size); + if (!new_streamFile) goto fail; + stream_segments[0] = new_streamFile; + temp_streamFile = NULL; + + /* open external file */ + new_streamFile = open_streamfile_by_filename(streamFile, bao->resource_name); + if (!new_streamFile) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, bao->main_offset, bao->main_size); + if (!new_streamFile) goto fail; + stream_segments[1] = new_streamFile; + temp_streamFile = NULL; + + new_streamFile = open_multifile_streamfile(stream_segments, 2); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + } + else if (bao->is_external) { + /* open external file */ + new_streamFile = open_streamfile_by_filename(streamFile, bao->resource_name); + if (!new_streamFile) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + } + else { + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + } + + return temp_streamFile; + +fail: + close_streamfile(stream_segments[0]); + close_streamfile(stream_segments[1]); + close_streamfile(temp_streamFile); + + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c index dbeefaef0..a3b48435b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c @@ -297,7 +297,7 @@ static int get_loop_points(STREAMFILE *streamFile, int *out_loop_start, int *out break; default: - VGM_LOG("Jade: unknown LIST chunk at %lx\n", offset); + VGM_LOG("Jade: unknown LIST chunk\n"); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn_ogg_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn_ogg_streamfile.h index aeec84f87..364718d34 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn_ogg_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn_ogg_streamfile.h @@ -12,7 +12,7 @@ typedef struct { /* 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) { +static size_t lyn_ogg_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) { @@ -53,7 +53,7 @@ static size_t scd_dsp_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offse return total_read; } -static size_t scd_dsp_io_size(STREAMFILE *streamfile, lyn_ogg_io_data* data) { +static size_t lyn_ogg_io_size(STREAMFILE *streamfile, lyn_ogg_io_data* data) { return data->total_size; } @@ -74,7 +74,7 @@ static STREAMFILE* setup_lyn_ogg_streamfile(STREAMFILE *streamFile, off_t start_ 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); + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, lyn_ogg_io_read,lyn_ogg_io_size); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c index c1d7e3c0d..7387205a2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c @@ -185,9 +185,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 0x4E7820204E782020: { /* "Nx Nx " */ /* chunks: "MARK" (optional seek table), "STRG" (optional description) */ - uint8_t buf[0x100]; - size_t bytes, skip, opus_size; - ffmpeg_custom_config cfg = {0}; + size_t skip, opus_size; /* a standard Switch Opus header */ skip = read_32bitLE(start_offset + 0x1c, streamFile); @@ -195,19 +193,11 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { start_offset += opus_size; data_size -= opus_size; - cfg.type = FFMPEG_SWITCH_OPUS; - - bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate); - if (bytes <= 0) goto fail; - - vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg); + vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - if (((ffmpeg_codec_data*)vgmstream->codec_data)->skipSamples <= 0) - ffmpeg_set_skip_samples(vgmstream->codec_data, skip); - { off_t chunk_offset = off + 0x20 + 0xc; /* after "fmt" */ while (chunk_offset < header_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index ec39684e9..2782a7cf8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -212,19 +212,8 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { goto fail; } - /* 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); - } + if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ + ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamData, start_offset)); break; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ue4opus.c b/Frameworks/vgmstream/vgmstream/src/meta/ue4opus.c new file mode 100644 index 000000000..6e13f5a72 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ue4opus.c @@ -0,0 +1,68 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* UE4OPUS - from Unreal Engine 4 games [ARK: Survival Evolved (PC), Fortnite (PC)] */ +VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count, sample_rate, num_samples, skip; + size_t data_size; + + + /* checks*/ + /* .opus/lopus: possible real extension + * .ue4opus: header id */ + if (!check_extensions(streamFile, "opus,lopus,ue4opus")) + goto fail; + if (read_32bitBE(0x00, streamFile) != 0x5545344F && /* "UE4O" */ + read_32bitBE(0x00, streamFile) != 0x50555300) /* "PUS\0" */ + goto fail; + + + sample_rate = (uint16_t)read_16bitLE(0x08, streamFile); + num_samples = read_32bitLE(0x0a, streamFile); /* may be less or equal to file num_samples */ + channel_count = read_8bit(0x0e, streamFile); + /* 0x0f(2): frame count */ + loop_flag = 0; + + /* UE4Opus encodes 60ms (music) or 120ms (voice) frames, and encoder delay seems 1/8 + * of samples per frame (may be more complex but wave looks ok-ish) */ + //todo does UE4 actually care about encoder delay? + skip = (60 * 48000 / 1000) / 8; /* 48000 is the internal/resampled rate */ + + start_offset = 0x11; + 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_UE4OPUS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples - skip; + +#ifdef VGM_USE_FFMPEG + { + vgmstream->codec_data = init_ffmpeg_ue4_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + if (vgmstream->num_samples == 0) { + vgmstream->num_samples = ue4_opus_get_samples(start_offset, data_size, vgmstream->sample_rate, streamFile) - skip; + } + } +#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/utk.c b/Frameworks/vgmstream/vgmstream/src/meta/utk.c new file mode 100644 index 000000000..edff0c81d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/utk.c @@ -0,0 +1,47 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* UTK - from Maxis games [The Sims Online (PC), SimCity 4 (PC)] */ +VGMSTREAM * init_vgmstream_utk(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "utk")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x55544D30) /* "UTM0" */ + goto fail; + if (read_32bitLE(0x08,streamFile) != 0x14) /* header size? */ + goto fail; + if (read_16bitLE(0x0c,streamFile) != 0x01) /* codec */ + goto fail; + /* 0x14: avg bitrate, 0x18: PCM block size, 0x1a: PCM bits, 0x1c: extra size? */ + + channel_count = read_16bitLE(0x0e,streamFile); + if (channel_count > 1) goto fail; /* unknown */ + loop_flag = 0; + start_offset = 0x20; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_UTK; + vgmstream->sample_rate = read_32bitLE(0x10, streamFile); + vgmstream->num_samples = read_32bitLE(0x04, streamFile) / 2; /* PCM size */ + vgmstream->coding_type = coding_EA_MT; + vgmstream->layout_type = layout_none; + vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0); + if (!vgmstream->codec_data) 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/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c new file mode 100644 index 000000000..e88a78a59 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -0,0 +1,233 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* VAGp - Sony SDK format, created by various tools */ +VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t file_size, channel_size, interleave; + meta_t meta_type; + int channel_count = 0, loop_flag, sample_rate; + uint32_t vag_id, version; + int32_t loop_start_sample = 0, loop_end_sample = 0; + int allow_dual_stereo = 0; + + + /* checks */ + /* .vag: standard + * .swag: Frantix (PSP) + * .str: Ben10 Galactic Racing + * .vig: MX vs. ATV Untamed (PS2) + * .l/r: Crash Nitro Kart (PS2), Gradius V (PS2) */ + if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r") ) + goto fail; + + /* check VAG Header */ + if (((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700) && /* "VAG" */ + ((read_32bitLE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700)) + goto fail; + + file_size = get_streamfile_size(streamFile); + + /* version used to create the file: + * - 00000000 = v1.8 PC, + * - 00000002 = v1.3 Mac (used?) + * - 00000003 = v1.6+ Mac + * - 00000020 = v2.0 PC (most common) + * - 00000004 = ? (later games) + * - 00000006 = ? (vagconv) + * - 00020001 = v2.1 (vagconv2) + * - 00030000 = v3.0 (vagconv2) */ + version = (uint32_t)read_32bitBE(0x04,streamFile); + /* 0x08-0c: reserved */ + channel_size = read_32bitBE(0x0c,streamFile); + sample_rate = read_32bitBE(0x10,streamFile); + /* 0x14-20 reserved */ + /* 0x20-30: name (optional) */ + /* 0x30: data start (first 0x10 usually 0s to init SPU) */ + + + /* check variation */ + vag_id = read_32bitBE(0x00,streamFile); + switch(vag_id) { + + case 0x56414731: /* "VAG1" (1 channel) [Metal Gear Solid 3 (PS2)] */ + meta_type = meta_PS2_VAG1; + start_offset = 0x40; /* 0x30 is extra data in VAG1 */ + channel_count = 1; + interleave = 0; + loop_flag = 0; + break; + + case 0x56414732: /* "VAG2" (2 channels) [Metal Gear Solid 3 (PS2)] */ + meta_type = meta_PS2_VAG2; + start_offset = 0x40; /* 0x30 is extra data in VAG2 */ + channel_count = 2; + interleave = 0x800; + loop_flag = 0; + break; + + case 0x56414769: /* "VAGi" (interleaved) */ + meta_type = meta_PS2_VAGi; + start_offset = 0x800; + channel_count = 2; + interleave = read_32bitLE(0x08,streamFile); + loop_flag = 0; + break; + + case 0x70474156: /* pGAV (little endian / stereo) [Jak 3 (PS2), Jak X (PS2)] */ + meta_type = meta_PS2_pGAV; + start_offset = 0x00; //todo 0x30, requires interleave_first + + if (read_32bitBE(0x20,streamFile) == 0x53746572) { /* "Ster" */ + channel_count = 2; + + if (read_32bitLE(0x2000,streamFile) == 0x56414770) /* "pGAV" */ + interleave = 0x2000; /* Jak 3 interleave, includes header */ + else if (read_32bitLE(0x1000,streamFile) == 0x56414770) /* "pGAV" */ + interleave = 0x1000; /* Jak X interleave, includes header */ + else + goto fail; + //todo interleave_first = interleave - start_offset; /* interleave includes header */ + } + else { + channel_count = 1; + interleave = 0; + } + + channel_size = read_32bitLE(0x0C,streamFile) / channel_count; + sample_rate = read_32bitLE(0x10,streamFile); + //todo adjust channel_size, includes part of header? + loop_flag = 0; + break; + + case 0x56414770: /* "VAGp" (standard and variations) */ + meta_type = meta_PS2_VAGp; + + if (check_extensions(streamFile,"vig")) { + /* MX vs. ATV Untamed (PS2) */ + start_offset = 0x800 - 0x20; + channel_count = 2; + interleave = 0x10; + loop_flag = 0; + } + else if (check_extensions(streamFile,"swag")) { /* algo "VAGp" at (file_size / channels) */ + /* Frantix (PSP) */ + start_offset = 0x40; /* channel_size ignores empty frame */ + channel_count = 2; + interleave = file_size / channel_count; + + channel_size = read_32bitLE(0x0c,streamFile); + sample_rate = read_32bitLE(0x10,streamFile); + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + } + else if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */ + /* The Simpsons Wrestling (PS1) */ + start_offset = 0x00; //todo 0x30, requires interleave_first + channel_count = 2; + interleave = 0x6000; + //todo interleave_first = interleave - start_offset; /* includes header */ + channel_size += 0x30; + + loop_flag = 0; + } + else if (read_32bitBE(0x1000,streamFile) == 0x56414770) { /* "VAGp" */ + /* Shikigami no Shiro (PS2) */ + start_offset = 0x00; //todo 0x30, requires interleave_first + channel_count = 2; + interleave = 0x1000; + //todo interleave_first = interleave - start_offset; /* includes header */ + channel_size += 0x30; + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + } + else if (read_32bitBE(0x30,streamFile) == 0x56414770) { /* "VAGp" */ + /* The Red Star (PS2) */ + start_offset = 0x60; /* two VAGp headers */ + channel_count = 2; + + if ((file_size - start_offset) % 0x4000 == 0) + interleave = 0x4000; + else if ((file_size - start_offset) % 0x4180 == 0) + interleave = 0x4180; + else + goto fail; + + loop_flag = 0; /* loop segments */ + } + else if (version == 0x40000000) { + /* Killzone (PS2) */ + start_offset = 0x30; + channel_count = 1; + interleave = 0; + + channel_size = read_32bitLE(0x0C,streamFile) / channel_count; + sample_rate = read_32bitLE(0x10,streamFile); + loop_flag = 0; + } + else if (version == 0x00020001 || version == 0x00030000) { + /* standard Vita/PS4 .vag [Chronovolt (Vita), Grand Kingdom (PS4)] */ + start_offset = 0x30; + interleave = 0x10; + + /* channels are at 0x1e, except Ukiyo no Roushi (Vita), which has + * loop start/end frame (but also uses PS-ADPCM flags) */ + if (read_32bitBE(0x18,streamFile) == 0 + && (read_32bitBE(0x1c,streamFile) & 0xFFFF00FF) == 0 + && read_8bit(0x1e,streamFile) < 16) { + channel_count = read_8bit(0x1e,streamFile); + if (channel_count == 0) + channel_count = 1; /* ex. early games [Lumines (Vita)] */ + } + else { + channel_count = 1; + } + + channel_size = channel_size / channel_count; + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + } + else { + /* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */ + start_offset = 0x30; + interleave = 0; + + channel_count = 1; + loop_flag = ps_find_loop_offsets_full(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + allow_dual_stereo = 1; /* often found with external L/R files */ + } + break; + + default: + goto fail; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_type; + vgmstream->allow_dual_stereo = allow_dual_stereo; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + vgmstream->coding_type = coding_PSX; + if (version == 0x00020001 || version == 0x00030000) + vgmstream->coding_type = coding_HEVAG; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = interleave; + + read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); /* always, can be null */ + + 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/vai.c b/Frameworks/vgmstream/vgmstream/src/meta/vai.c new file mode 100644 index 000000000..7539d7f3a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/vai.c @@ -0,0 +1,45 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .VAI - from Asobo Studio games [Ratatouille (GC)] */ +VGMSTREAM * init_vgmstream_vai(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"vai") ) + goto fail; + + start_offset = 0x4060; + data_size = get_streamfile_size(streamFile) - start_offset; + if (read_32bitBE(0x04,streamFile) != data_size) + goto fail; + + channel_count = 2; + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_VAI; + vgmstream->sample_rate = read_32bitBE(0x00,streamFile); + vgmstream->num_samples = dsp_bytes_to_samples(data_size,channel_count); + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x4000; + dsp_read_coefs_be(vgmstream,streamFile,0x0c,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/vawx.c b/Frameworks/vgmstream/vgmstream/src/meta/vawx.c index a7d3347b2..02a689ca8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vawx.c @@ -3,39 +3,39 @@ #include "../coding/coding.h" -/* VAWX - found in feelplus games (No More Heroes Heroes Paradise, Moon Diver) */ +/* VAWX - found in feelplus games [No More Heroes: Heroes Paradise (PS3/X360), Moon Diver (PS3/X360)] */ VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, data_size; + int loop_flag = 0, channel_count, codec; - int loop_flag = 0, channel_count, type; - /* check extensions */ - if ( !check_extensions(streamFile, "vawx,xwv") ) + /* checks */ + /* .xwv: actual extension [Moon Diver (PS3/X360)] + * .vawx: header id */ + if ( !check_extensions(streamFile, "xwv,vawx") ) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x56415758) /* "VAWX" */ goto fail; loop_flag = read_8bit(0x37,streamFile); - channel_count = read_8bit(0x39,streamFile);; - + channel_count = read_8bit(0x39,streamFile); + start_offset = 0x800; /* ? read_32bitLE(0x0c,streamFile); */ + codec = read_8bit(0x36,streamFile); /* could be at 0x38 too */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* 0x04: filesize */ - start_offset = 0x800; /* ? read_32bitLE(0x0c,streamFile); */ - vgmstream->channels = channel_count; /* 0x16: file id */ - type = read_8bit(0x36,streamFile); /* could be at 0x38 too */ vgmstream->num_samples = read_32bitBE(0x3c,streamFile); vgmstream->sample_rate = read_32bitBE(0x40,streamFile); vgmstream->meta_type = meta_VAWX; - switch(type) { + switch(codec) { case 2: /* VAG */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = channel_count == 6 ? layout_blocked_vawx : layout_interleave ; @@ -57,8 +57,6 @@ VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) { block_count = (uint16_t)read_16bitBE(0x3A, streamFile); /* also at 0x56 */ bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; @@ -84,9 +82,6 @@ VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) { /* make a fake riff so FFmpeg can parse the ATRAC3 */ bytes = ffmpeg_make_riff_atrac3(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); - if (bytes <= 0) - goto fail; - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; @@ -106,13 +101,8 @@ VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) { } - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - - if (vgmstream->layout_type == layout_blocked_vawx) - block_update_vawx(start_offset,vgmstream); - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vgs.c b/Frameworks/vgmstream/vgmstream/src/meta/vgs.c index ba2e66de4..db55f8fb0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vgs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vgs.c @@ -50,25 +50,22 @@ VGMSTREAM * init_vgmstream_vgs(STREAMFILE *streamFile) { channel_count++; } + start_offset = 0x80; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - start_offset = 0x80; + vgmstream->meta_type = meta_VGS; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = ps_bytes_to_samples(channel_size*channel_count, channel_count); vgmstream->coding_type = coding_PSX_badflags; /* flag = stream/channel number */ vgmstream->layout_type = layout_blocked_vgs; - vgmstream->meta_type = meta_VGS; - - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - block_update_vgs(start_offset, vgmstream); - return vgmstream; fail: close_vgmstream(vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vis.c b/Frameworks/vgmstream/vgmstream/src/meta/vis.c new file mode 100644 index 000000000..3ce35b713 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/vis.c @@ -0,0 +1,52 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* VIS - from Konami games [AirForce Delta Strike (PS2) (PS2)] */ +VGMSTREAM * init_vgmstream_vis(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"vis") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x56495341) /* "VISA" */ + goto fail; + + start_offset = 0x800; + data_size = get_streamfile_size(streamFile) - start_offset; + + loop_flag = read_32bitLE(0x18,streamFile); + channel_count = read_32bitLE(0x20,streamFile); /* assumed */ + /* 0x1c: always 0x10 */ + /* 0x24: always 0x01 */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_VIS; + vgmstream->sample_rate = read_32bitLE(0x08,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x0c,streamFile),channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x10,streamFile),channel_count); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); /* usually 0x10 or 0x4000 */ + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = + (data_size % (vgmstream->interleave_block_size*channel_count)) / channel_count; + read_string(vgmstream->stream_name,0x10+1, 0x28,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/vpk.c b/Frameworks/vgmstream/vgmstream/src/meta/vpk.c new file mode 100644 index 000000000..1dd68bed8 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/vpk.c @@ -0,0 +1,51 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* VPK - from SCE America second party devs [God of War (PS2), NBA 08 (PS3)] */ +VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + int loop_flag, channel_count; + off_t start_offset; + size_t channel_size; + + + /* checks */ + if (!check_extensions(streamFile, "vpk")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x204B5056) /* " KPV" */ + goto fail; + + /* seems this consistently has 0x10-0x20 extra bytes, landing in garbage frames at the end */ + channel_size = read_32bitLE(0x04,streamFile) - 0x20; /* remove for cleaner ends */ + + start_offset = read_32bitLE(0x08,streamFile); + channel_count = read_32bitLE(0x14,streamFile); + /* 0x18+(per channel): channel config? */ + loop_flag = (read_32bitLE(0x7FC,streamFile) != 0); /* used? */ + + + /* 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*vgmstream->channels,vgmstream->channels); + if (vgmstream->loop_flag) { + vgmstream->loop_start_sample = read_32bitLE(0x7FC,streamFile); + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + vgmstream->meta_type = meta_VPK; + vgmstream->coding_type = coding_PSX; + vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile) / 2; /* even in >2ch */ + vgmstream->layout_type = layout_interleave; + + 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/vs.c b/Frameworks/vgmstream/vgmstream/src/meta/vs.c index 0e7aa209a..d36aee6f3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vs.c @@ -49,7 +49,7 @@ VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile) { /* open the file for reading */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x2000); + 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/wave_segmented.c b/Frameworks/vgmstream/vgmstream/src/meta/wave_segmented.c index a4af548c3..263b069b4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wave_segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wave_segmented.c @@ -134,6 +134,7 @@ VGMSTREAM * init_vgmstream_wave_segmented(STREAMFILE *streamFile) { break; } +#ifdef VGM_USE_VORBIS case 0x04: { /* "vorbis" */ ogg_vorbis_meta_info_t ovmi = {0}; @@ -153,6 +154,7 @@ VGMSTREAM * init_vgmstream_wave_segmented(STREAMFILE *streamFile) { break; } +#endif default: /* others: s16be/s16le/mp3 as referenced in the exe? */ VGM_LOG("WAVE: unknown codec\n"); @@ -205,10 +207,7 @@ VGMSTREAM * init_vgmstream_wave_segmented(STREAMFILE *streamFile) { /* .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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wavebatch.c b/Frameworks/vgmstream/vgmstream/src/meta/wavebatch.c new file mode 100644 index 000000000..03cb3a505 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wavebatch.c @@ -0,0 +1,138 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* WBAT - Firebrand Games header [Need for Speed: The Run (3DS), Fast & Furious: Showdown (3DS)] */ +VGMSTREAM * init_vgmstream_wavebatch(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, name_offset, offset, stream_offset; + size_t names_size, stream_size; + int loop_flag, channel_count, sample_rate, num_samples; + int big_endian, version, codec; + int total_subsongs, target_subsong = streamFile->stream_index; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + + /* checks */ + if (!check_extensions(streamFile, "wavebatch")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x54414257) /* "TABW" */ + goto fail; + + /* section0: base header */ + big_endian = ((uint16_t)read_16bitBE(0x04,streamFile) == 0xFEFF); /* BOM (always LE on 3DS/Android) */ + if (big_endian) { + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + + version = read_16bit(0x06, streamFile); /* assumed */ + if (version != 0x06 && version != 0x07) /* v6 = NFS: The Run , v7 = F&F Showndown */ + goto fail; + + total_subsongs = read_32bit(0x08,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + names_size = read_32bit(0x0c,streamFile); + /* 0x10/14: see below */ + /* 0x18: data size (all subsongs) */ + offset = 0x1c + names_size; /* skip names table */ + + /* the following 2 sections (rarely) won't match total_subsongs */ + + /* section1: unknown */ + { + size_t unknown_size = read_32bit(0x10,streamFile); + /* 0x00: usually 0, rarely 0x20? */ + offset += unknown_size*0x04; + } + + /* section2: samples */ + { + size_t samples_size = read_32bit(0x14,streamFile); + /* 0x00: num_samples */ + offset += samples_size*0x04; + } + + /* section3: headers */ + { + off_t header_offset = offset+(target_subsong-1)*0x24; + + name_offset = read_32bit(header_offset+0x00, streamFile) + 0x1c; /* within name table */ + codec = read_32bit(header_offset+0x04, streamFile); + sample_rate = read_32bit(header_offset+0x08, streamFile); + channel_count = read_32bit(header_offset+0x0c, streamFile); + /* 0x10: index within section1/2? */ + /* 0x14: flags? 0x01 or (rarely) 0x02 */ + stream_offset = read_32bit(header_offset+0x18, streamFile); + stream_size = read_32bit(header_offset+0x1c, streamFile); /* including DSP config */ + num_samples = read_32bit(header_offset+0x20, streamFile) / channel_count; /* nibble/PCMs */ + + offset += total_subsongs*0x24; + } + + loop_flag = 0; + start_offset = offset + stream_offset; + + + /* 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->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + + vgmstream->meta_type = meta_WAVEBATCH; + + switch(codec) { + case 0x00: /* PCM16 [NASCAR Unleashed (3DS), Solar Flux Pocket (Android)] */ + vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + + case 0x01: /* PCM8 [Cars 2 (3DS)] */ + vgmstream->coding_type = coding_PCM8; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + break; + + case 0x02: { /* DSP [WRC FIA World Rally Championship (3DS)] */ + size_t config_size = (0x20+0x14)*channel_count + (0x0c)*channel_count; /* coefs+hist + padding */ + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = (stream_size - config_size) / channel_count; /* full interleave*/; + + dsp_read_coefs(vgmstream,streamFile,start_offset+0x00,0x20+0x14,big_endian); + dsp_read_hist (vgmstream,streamFile,start_offset+0x20,0x14+0x20,big_endian); + start_offset += config_size; + + break; + } + + default: + VGM_LOG("WAVEBATCH: unknown codec %x\n", codec); + goto fail; + } + + + + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); /* always null-terminated */ + + + 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/wii_mus.c b/Frameworks/vgmstream/vgmstream/src/meta/wii_mus.c index 2f36e5381..488930be4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wii_mus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wii_mus.c @@ -100,9 +100,9 @@ VGMSTREAM * init_vgmstream_wii_mus(STREAMFILE *streamFile) { vgmstream->ch[1].adpcm_history1_16 = channel[1].initial_hist1; vgmstream->ch[1].adpcm_history2_16 = channel[1].initial_hist2; - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,interleave); + 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,interleave); + vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[1].streamfile) goto fail; /* open the file for reading */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wii_str.c b/Frameworks/vgmstream/vgmstream/src/meta/wii_str.c deleted file mode 100644 index 460a5bad9..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/wii_str.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "meta.h" -#include "../util.h" - -VGMSTREAM * init_vgmstream_wii_str(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - STREAMFILE * infileSTH = NULL; - char filename[PATH_LIMIT]; - - char * filenameSTH = NULL; - - int i, j, channel_count, loop_flag; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("str",filename_extension(filename))) goto fail; - - /* check for .MIH file */ - filenameSTH=(char *)malloc(strlen(filename)+1); - - if (!filenameSTH) goto fail; - - strcpy(filenameSTH,filename); - strcpy(filenameSTH+strlen(filenameSTH)-3,"sth"); - - infileSTH = streamFile->open(streamFile,filenameSTH,STREAMFILE_DEFAULT_BUFFER_SIZE); - - /* STH File is necessary, so we can't confuse those file */ - /* with others .STR file as it is a very common extension */ - if (!infileSTH) goto fail; - - if(read_32bitLE(0x2C,infileSTH)!=0) - goto fail; - - channel_count = read_32bitBE(0x70,infileSTH); - - if(channel_count==1) - loop_flag = (read_32bitBE(0xD4,infileSTH)==0x00740000); - else - loop_flag = (read_32bitBE(0x124,infileSTH)==0x00740000); - - /* 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(0x38,infileSTH); - - vgmstream->interleave_block_size=0x8000; - vgmstream->num_samples=read_32bitBE(0x34,infileSTH); - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - - vgmstream->meta_type = meta_WII_STR; - - if(loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - - /* 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+=(off_t)(vgmstream->interleave_block_size*i); - - for(j=0; j<16; j++) { - vgmstream->ch[i].adpcm_coef[j]=read_16bitBE(0xAC+(j*2)+(i*0x50),infileSTH); - } - } - } - - close_streamfile(infileSTH); infileSTH=NULL; - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (infileSTH) close_streamfile(infileSTH); - if (filenameSTH) {free(filenameSTH); filenameSTH=NULL;} - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wii_sts.c b/Frameworks/vgmstream/vgmstream/src/meta/wii_sts.c index 5f5f2f948..328ddf8a9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wii_sts.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wii_sts.c @@ -71,7 +71,7 @@ VGMSTREAM * init_vgmstream_wii_sts(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,36); + vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); vgmstream->ch[i].offset = 0x50+(i*(start_offset+0x26-0x50)); if (!vgmstream->ch[i].streamfile) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wsi.c b/Frameworks/vgmstream/vgmstream/src/meta/wsi.c new file mode 100644 index 000000000..3f02260de --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wsi.c @@ -0,0 +1,83 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .wsi - blocked dsp [Alone in the Dark (Wii)] */ +VGMSTREAM * init_vgmstream_wsi(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, header_offset; + size_t header_spacing; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "wsi")) + goto fail; + + channel_count = read_32bitBE(0x04,streamFile); + if (channel_count != 2) goto fail; /* assumed */ + + /* check for consistent block headers */ + { + off_t block_offset; + off_t block_size_has_been; + int i; + + block_offset = read_32bitBE(0x00,streamFile); + if (block_offset < 0x08) goto fail; + + block_size_has_been = block_offset; + + /* check 4 blocks, to get an idea */ + for (i = 0; i < 4*channel_count; i++) { + off_t block_size = read_32bitBE(block_offset,streamFile); + + 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 */ + + 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 */ + + block_offset += block_size; + } + } + + start_offset = read_32bitBE(0x00, streamFile); + header_offset = start_offset + 0x10; + header_spacing = read_32bitBE(start_offset,streamFile); + + /* contains standard DSP header, but since it's blocked validations (start/loop ps, etc) + * will fail, so no point to handle as standard DSP */ + loop_flag = read_16bitBE(header_offset+0x0c,streamFile); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_DSP_WSI; + vgmstream->sample_rate = read_32bitBE(header_offset+0x08,streamFile); + + vgmstream->num_samples = read_32bitBE(header_offset+0x00,streamFile) / 14 * 14; /* remove incomplete last frame */ + vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bitBE(header_offset+0x10,streamFile)); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bitBE(header_offset+0x14,streamFile))+1; + 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_blocked_wsi; + dsp_read_coefs_be(vgmstream, streamFile, header_offset+0x1c, header_spacing); + dsp_read_hist_be(vgmstream, streamFile, header_offset+0x40, header_spacing); + + 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/wv2.c b/Frameworks/vgmstream/vgmstream/src/meta/wv2.c new file mode 100644 index 000000000..c9592ff62 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wv2.c @@ -0,0 +1,43 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* WAV2 - from Infrogrames North America games [Slave Zero (PC) (PS2)] */ +VGMSTREAM * init_vgmstream_wv2(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count; + + + /* checks */ + if ( !check_extensions(streamFile,"wv2") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x57415632) /* "WAV2" */ + goto fail; + + start_offset = 0x1c; + data_size = get_streamfile_size(streamFile) - start_offset; + channel_count = read_8bit(0x0c,streamFile); + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_WV2; + vgmstream->sample_rate = read_32bitLE(0x10,streamFile); + vgmstream->num_samples = ima_bytes_to_samples(data_size,channel_count); /* also 0x18 */ + + vgmstream->coding_type = coding_DVI_IMA_int; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0xFA; + + 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/wv6.c b/Frameworks/vgmstream/vgmstream/src/meta/wv6.c new file mode 100644 index 000000000..bbbf62ec2 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wv6.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* WV6 - Gorilla Systems PC games [Spy Kids: Mega Mission Zone (PC), Lilo & Stitch: Hawaiian Adventure (PC)] */ +VGMSTREAM * init_vgmstream_wv6(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "wv6")) + goto fail; + + if (read_32bitLE(0x00,streamFile) != get_streamfile_size(streamFile)) + goto fail; + if (read_32bitBE(0x2c,streamFile) != 0x57563620 || /* "WV6 " */ + read_32bitBE(0x30,streamFile) != 0x494D415F) /* "IMA_" ("WV6 IMA_ADPCM COMPRESSED 16 BIT AUDIO") */ + goto fail; + + /* 0x54/58/5c/60/6c: unknown (reject to catch possible stereo files, but don't seem to exist) */ + if (read_32bitLE(0x54,streamFile) != 0x01 || + read_32bitLE(0x58,streamFile) != 0x01 || + read_32bitLE(0x5c,streamFile) != 0x10 || + read_32bitLE(0x68,streamFile) != 0x01 || + read_32bitLE(0x6c,streamFile) != 0x88) + goto fail; + /* 0x64: PCM size (samples*channels*2) */ + + channel_count = 1; + loop_flag = 0; + start_offset = 0x8c; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x60, streamFile); + vgmstream->num_samples = ima_bytes_to_samples(read_32bitLE(0x88,streamFile), channel_count); + + vgmstream->meta_type = meta_WV6; + vgmstream->coding_type = coding_WV6_IMA; + vgmstream->layout_type = layout_none; + + read_string(vgmstream->stream_name,0x1c+1, 0x04,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/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index bc946e7ff..f78b12425 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -172,8 +172,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { else if (ww.format == 0x0002 && ww.block_align == 0x104 * ww.channels) { //ww.codec = SWITCH_ADPCM; /* unknown codec, found in Bayonetta 2 (Switch) - * frames of 0x104 per ch, possibly frame header is hist1(2)/hist2(2)/predictor(1) - * (may write 2 header samples + FF*2 nibbles = 0x200 samples per block?) */ + * frames of 0x104 per ch, possibly frame header is hist1(2)/hist2(2)/index(1) + * (may write 2 header samples + FF*2 nibbles = 0x200 samples per block?) + * index only goes up to ~0xb, may be a shift/scale value */ goto fail; } @@ -251,8 +252,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* older Wwise (~<2012) */ switch(vorb_size) { - 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) */ + case 0x2C: /* earliest (~2009), [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)?] */ + case 0x28: /* early (~2009) [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; @@ -260,21 +261,23 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { cfg.setup_type = WWV_HEADER_TRIAD; break; - //case 0x32: /* ? */ case 0x34: /* common (2010~2011) */ + case 0x32: /* very rare (mid 2011) [Saints Row the 3rd (PC)] */ data_offsets = 0x18; block_offsets = 0x30; cfg.header_type = WWV_TYPE_6; cfg.packet_type = WWV_STANDARD; cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ break; - case 0x2a: /* uncommon (mid 2011), ex. infamous 2 PS3 */ + + case 0x2a: /* uncommon (mid 2011), [inFamous 2 (PS3)] */ data_offsets = 0x10; block_offsets = 0x28; cfg.header_type = WWV_TYPE_2; cfg.packet_type = WWV_MODIFIED; cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; break; + default: VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size); goto fail; @@ -499,9 +502,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } case OPUS: { /* Switch */ - uint8_t buf[0x100]; - size_t bytes, skip; - ffmpeg_custom_config cfg = {0}; + size_t skip = 0; /* Wwise doesn't seem to use it? (0x138 /0x3E8 ~default) */ /* values up to 0x14 seem fixed and similar to HEVAG's (block_align 0x02/04, bits_per_sample 0x10) */ if (ww.fmt_size == 0x28) { @@ -518,15 +519,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { goto fail; } - skip = 0; /* Wwise doesn't seem to use it? (0x138 /0x3E8 ~default) */ - - cfg.type = FFMPEG_SWITCH_OPUS; - //cfg.big_endian = ww.big_endian; /* internally BE */ - - bytes = ffmpeg_make_opus_header(buf,0x100, ww.channels, skip, ww.sample_rate); - vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,ww.data_size, &cfg); + vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,ww.data_size, vgmstream->channels, skip, vgmstream->sample_rate); if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/x360_tra.c b/Frameworks/vgmstream/vgmstream/src/meta/x360_tra.c index 8554076cd..d1227a688 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/x360_tra.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/x360_tra.c @@ -45,7 +45,7 @@ VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,36); + 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/xau_konami.c b/Frameworks/vgmstream/vgmstream/src/meta/xau_konami.c new file mode 100644 index 000000000..b8c6eb67d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xau_konami.c @@ -0,0 +1,60 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* XAU - from Konami games [Yu-Gi-Oh - The Dawn of Destiny (Xbox)] */ +VGMSTREAM * init_vgmstream_xau_konami(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, header_offset; + size_t stream_size; + int loop_flag, channel_count, sample_rate; + off_t loop_start, loop_end; + + + /* checks */ + if ( !check_extensions(streamFile,"xau") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x53465842) /* "SFXB" */ + goto fail; + + //todo: subsongs used in sfx packs (rare) + if (read_32bitLE(0x54,streamFile) != 1) /* subsong count */ + goto fail; + + start_offset = 0x60 + read_32bitLE(0x34,streamFile); + header_offset = 0x60 + 0x20 + 0x40*0; /* target subsong */ + + if (read_32bitBE(header_offset+0x00,streamFile) != 0x52494646) /* "RIFF" */ + goto fail; + if (read_16bitLE(header_offset+0x14,streamFile) != 0x01) /* codec */ + goto fail; + channel_count = read_16bitLE(header_offset+0x16,streamFile); + sample_rate = read_32bitLE(header_offset+0x18,streamFile); + loop_start = read_32bitLE(header_offset+030,streamFile); + loop_end = read_32bitLE(header_offset+0x34,streamFile); + loop_flag = (loop_end > 0); + + stream_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_XAU_KONAMI; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = pcm_bytes_to_samples(stream_size,channel_count,16); + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start,channel_count,16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end,channel_count,16); + + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xbox_ims.c b/Frameworks/vgmstream/vgmstream/src/meta/xbox_ims.c index 220eeac13..1c6f60ad9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xbox_ims.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xbox_ims.c @@ -37,7 +37,7 @@ VGMSTREAM * init_vgmstream_xbox_matx(STREAMFILE *streamFile) { /* open the file for reading by each channel */ { for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,36); + 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/xbox_xvas.c b/Frameworks/vgmstream/vgmstream/src/meta/xbox_xvas.c index 6ee37b860..acd09c8b3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xbox_xvas.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xbox_xvas.c @@ -9,10 +9,10 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { int loop_flag, channel_count; size_t data_size; - /* check extension */ + + /* checks */ if (!check_extensions(streamFile,"xvas")) goto fail; - if (read_32bitLE(0x00,streamFile) != 0x69 && /* codec */ read_32bitLE(0x08,streamFile) != 0x48) /* block size (probably 0x24 for mono) */ goto fail; @@ -23,10 +23,12 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { data_size = read_32bitLE(0x24,streamFile); data_size -= (data_size / 0x20000) * 0x20; /* blocks of 0x20000 with padding */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_XBOX_XVAS; vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); if(loop_flag) { @@ -38,12 +40,9 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_blocked_xvas; - vgmstream->meta_type = meta_XBOX_XVAS; if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; - - block_update_xvas(start_offset,vgmstream); return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xmd.c b/Frameworks/vgmstream/vgmstream/src/meta/xmd.c new file mode 100644 index 000000000..88012a4f3 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xmd.c @@ -0,0 +1,69 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* XMD - from Konami Xbox games [Silent Hill 4 (Xbox), Castlevania Curse of Darkness (Xbox)] */ +VGMSTREAM * init_vgmstream_xmd(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate; + size_t data_size, loop_start, frame_size; + + + /* checks (.xmd comes from bigfiles with filenames) */ + if (!check_extensions(streamFile, "xmd")) + goto fail; + + if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) == 0x786D6400) { /* "xmd\0" */ + /* v2 from Castlevania: Curse of Darkness */ + channel_count = read_8bit(0x03,streamFile); + sample_rate = (uint16_t)read_16bitLE(0x04, streamFile); + data_size = read_32bitLE(0x06, streamFile); + loop_flag = read_8bit(0x0a,streamFile); + loop_start = read_32bitLE(0x0b, streamFile); + /* 0x0f(2): unknown+config? */ + frame_size = 0x15; + start_offset = 0x11; + } + else { + /* v1 from Silent Hill 4 */ + channel_count = read_8bit(0x00,streamFile); + sample_rate = (uint16_t)read_16bitLE(0x01, streamFile); + data_size = read_32bitLE(0x03, streamFile); + loop_flag = read_8bit(0x07,streamFile); + loop_start = read_32bitLE(0x08, streamFile); + + frame_size = 0x0d; + start_offset = 0x0c; + } + + /* extra checks just in case */ + if (data_size > get_streamfile_size(streamFile)) + goto fail; /* v1 .xmd are sector-aligned with padding */ + if (channel_count > 2) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = data_size / frame_size / channel_count * ((frame_size-0x06)*2 + 2); /* bytes to samples */ + if (loop_flag) { + vgmstream->loop_start_sample = loop_start / frame_size / channel_count * ((frame_size-0x06)*2 + 2); /* bytes to samples */ + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + vgmstream->meta_type = meta_XMD; + vgmstream->coding_type = coding_XMD; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = frame_size; + + 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/xvag.c b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c index 313b664e6..27614b610 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c @@ -1,34 +1,54 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" +#include "xvag_streamfile.h" -static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end); +typedef struct { + int big_endian; + int channels; + int sample_rate; + int codec; + + int factor; + + int loop_flag; + int num_samples; + int loop_start; + int loop_end; + + int subsongs; + int layers; + + size_t data_size; + off_t stream_offset; +} xvag_header; + +static int init_xvag_atrac9(STREAMFILE *streamFile, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset); +static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_header * xvag, off_t chunk_offset, off_t start_offset); /* XVAG - Sony's Scream Tool/Stream Creator format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE* temp_streamFile = NULL; + xvag_header xvag = {0}; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int loop_flag = 0, channel_count, codec; - int big_endian; - int sample_rate, num_samples, interleave_factor, multistreams = 0; + off_t start_offset, chunk_offset, first_offset = 0x20; + size_t chunk_size; int total_subsongs = 0, target_subsong = streamFile->stream_index; - off_t start_offset, loop_start = 0, loop_end = 0, chunk_offset; - off_t first_offset = 0x20; - size_t chunk_size, stream_size; - /* check extension, case insensitive */ + /* checks */ + /* .xvag: standard + * (extensionless): The Last Of Us (PS3) speech files */ if (!check_extensions(streamFile,"xvag,")) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x58564147) /* "XVAG" */ goto fail; /* endian flag (XVAGs of the same game can use BE or LE, usually when reusing from other platforms) */ - big_endian = read_8bit(0x08,streamFile) & 0x01; - if (big_endian) { + xvag.big_endian = read_8bit(0x08,streamFile) & 0x01; + if (xvag.big_endian) { read_32bit = read_32bitBE; } else { read_32bit = read_32bitLE; @@ -40,30 +60,33 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { * 0x0a: always 0? * 0x0b: version-flag? (0x5f/0x60/0x61/0x62/etc) */ - /* "fmat": base format (always first) */ - if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, big_endian, 1)) /*"fmat"*/ - goto fail; - channel_count = read_32bit(chunk_offset+0x00,streamFile); - codec = read_32bit(chunk_offset+0x04,streamFile); - num_samples = read_32bit(chunk_offset+0x08,streamFile); - /* 0x0c: samples again? playable section? */ - VGM_ASSERT(num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n"); - interleave_factor = read_32bit(chunk_offset+0x10,streamFile); - sample_rate = read_32bit(chunk_offset+0x14,streamFile); - stream_size = read_32bit(chunk_offset+0x18,streamFile); + /* "fmat": base format (always first) */ + if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, xvag.big_endian, 1)) /*"fmat"*/ + goto fail; + xvag.channels = read_32bit(chunk_offset+0x00,streamFile); + xvag.codec = read_32bit(chunk_offset+0x04,streamFile); + xvag.num_samples = read_32bit(chunk_offset+0x08,streamFile); + /* 0x0c: samples again? */ + VGM_ASSERT(xvag.num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n"); + + xvag.factor = read_32bit(chunk_offset+0x10,streamFile); /* for interleave */ + xvag.sample_rate = read_32bit(chunk_offset+0x14,streamFile); + xvag.data_size = read_32bit(chunk_offset+0x18,streamFile); /* not always accurate */ /* extra data, seen in versions 0x61+ */ if (chunk_size > 0x1c) { - /* number of interleaved subsong layers */ - total_subsongs = read_32bit(chunk_offset+0x1c,streamFile); - /* number of interleaved bitstreams per layer (multistreams * channels_per_stream = channels) */ - multistreams = read_32bit(chunk_offset+0x20,streamFile); + /* number of interleaved subsongs */ + xvag.subsongs = read_32bit(chunk_offset+0x1c,streamFile); + /* number of interleaved layers (layers * channels_per_layer = channels) */ + xvag.layers = read_32bit(chunk_offset+0x20,streamFile); } else { - total_subsongs = 1; - multistreams = 1; + xvag.subsongs = 1; + xvag.layers = 1; } + + total_subsongs = xvag.subsongs; if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; @@ -71,68 +94,72 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* other chunks: */ /* "cpan": pan/volume per channel */ /* "cues": cue/labels (rare) */ + /* "md5 ": hash (rare) */ /* "0000": end chunk before start_offset */ - /* some XVAG seem to do full loops, this should detect them as looping (basically tests is last frame is empty) */ - //todo remove, looping seems external and specified in Scream Tool's bank formats - if (codec == 0x06 && total_subsongs == 1) { - loop_flag = ps_adpcm_find_loop_offsets(streamFile, channel_count, start_offset, &loop_start, &loop_end); + /* XVAG has no looping, but some PS3 PS-ADPCM seems to do full loops (without data flags) */ + if (xvag.codec == 0x06 && xvag.subsongs == 1) { + size_t file_size = get_streamfile_size(streamFile); + /* simply test if last frame is not empty = may loop */ + xvag.loop_flag = (read_8bit(file_size - 0x01, streamFile) != 0); + xvag.loop_start = 0; + xvag.loop_end = ps_bytes_to_samples(file_size - start_offset, xvag.channels); } + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(xvag.channels,xvag.loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = (stream_size / total_subsongs); vgmstream->meta_type = meta_XVAG; + vgmstream->sample_rate = xvag.sample_rate; + vgmstream->num_samples = xvag.num_samples; + if (xvag.loop_flag) { + vgmstream->loop_start_sample = xvag.loop_start; + vgmstream->loop_end_sample = xvag.loop_end; + } + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = (xvag.data_size / total_subsongs); - switch (codec) { - case 0x06: /* VAG (PS ADPCM): God of War III (PS3), Uncharted 1/2 (PS3), Ratchet and Clank Future (PS3) */ - case 0x07: /* SVAG? (PS ADPCM with extended table): inFamous 1 (PS3) */ - if (multistreams > 1 && multistreams != vgmstream->channels) goto fail; - if (total_subsongs > 1 && multistreams > 1) goto fail; - if (total_subsongs > 1 && vgmstream->channels > 1) goto fail; /* unknown layout */ + switch (xvag.codec) { + case 0x06: /* VAG (PS-ADPCM): God of War III (PS3), Uncharted 1/2 (PS3), Ratchet and Clank Future (PS3) */ + case 0x07: /* SVAG? (PS-ADPCM with extended table?): inFamous 1 (PS3) */ + if (xvag.subsongs > 1 && xvag.layers > 1) goto fail; + if (xvag.layers > 1 && xvag.layers != xvag.channels) goto fail; + if (xvag.subsongs > 1 && xvag.channels > 1) goto fail; /* unknown layout */ vgmstream->coding_type = coding_PSX; - if (total_subsongs > 1) { /* God of War 3 (PS4) */ + if (xvag.subsongs > 1) { /* God of War 3 (PS4) */ vgmstream->layout_type = layout_blocked_xvag_subsong; vgmstream->interleave_block_size = 0x10; - vgmstream->full_block_size = 0x10 * interleave_factor * total_subsongs; - vgmstream->current_block_size = 0x10 * interleave_factor; - start_offset += 0x10 * interleave_factor * (target_subsong-1); + vgmstream->full_block_size = 0x10 * xvag.factor * xvag.subsongs; + vgmstream->current_block_size = 0x10 * xvag.factor; + start_offset += vgmstream->current_block_size * (target_subsong-1); } else { vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10 * interleave_factor; /* usually 1, bigger in GoW3 PS4 */ + vgmstream->interleave_block_size = 0x10 * xvag.factor; /* usually 1, bigger in GoW3 PS4 */ } - - if (loop_flag) { - vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); - vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); - } - break; #ifdef VGM_USE_MPEG - case 0x08: { /* MPEG: The Last of Us (PS3), Uncharted 3 (PS3), Medieval Moves (PS3) */ + case 0x08: { /* MPEG: The Last of Us (PS3), Uncharted 3 (PS3), Medieval Moves (PS3) */ mpeg_custom_config cfg = {0}; + if (xvag.subsongs > 1) goto fail; + if (xvag.subsongs > 1 && xvag.layers > 1) goto fail; /* often 2ch per MPEG and rarely 1ch (GoW3 PS4) */ - if (multistreams > 1 && !(multistreams*1 == vgmstream->channels || multistreams*2 == vgmstream->channels)) goto fail; - if (total_subsongs > 1) goto fail; - //todo rare test file in The Last of Us PS4 uses 6ch with 1 2ch stream, surround MPEG/mp3pro? + if (xvag.layers > 1 && !(xvag.layers*1 == vgmstream->channels || xvag.layers*2 == vgmstream->channels)) goto fail; + //todo rare test file in The Last of Us PS4 uses 6ch with one 2ch stream, surround MPEG/mp3pro? (decoded samples map to 6ch) /* "mpin": mpeg info */ /* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */ - if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, big_endian, 1)) /*"mpin"*/ + if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"mpin"*/ goto fail; cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */ - cfg.interleave = cfg.chunk_size * interleave_factor; + cfg.interleave = cfg.chunk_size * xvag.factor; vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); if (!vgmstream->codec_data) goto fail; @@ -143,135 +170,125 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { #ifdef VGM_USE_ATRAC9 case 0x09: { /* ATRAC9: Sly Cooper and the Thievius Raccoonus (Vita), The Last of Us Remastered (PS4) */ - atrac9_config cfg = {0}; + if (xvag.subsongs > 1 && xvag.layers > 1) goto fail; /* "a9in": ATRAC9 info */ - /* 0x00: block align, 0x04: samples per block, 0x0c: fact num_samples (no change), 0x10: encoder delay1 */ - if (!find_chunk(streamFile, 0x6139696E,first_offset,0, &chunk_offset,NULL, big_endian, 1)) /*"a9in"*/ + /* 0x00: frame size, 0x04: samples per frame, 0x0c: fact num_samples (no change), 0x10: encoder delay1 */ + if (!find_chunk(streamFile, 0x6139696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"a9in"*/ goto fail; - cfg.type = ATRAC9_XVAG; - cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(chunk_offset+0x08,streamFile); - cfg.encoder_delay = read_32bit(chunk_offset+0x14,streamFile); + if (xvag.layers > 1) { + /* some Vita/PS4 multichannel [flower (Vita), Uncharted Collection (PS4)]. PS4 ATRAC9 also + * does single-stream >2ch, but this can do configs ATRAC9 can't, like 5ch/14ch/etc */ + vgmstream->layout_data = build_layered_xvag(streamFile, &xvag, chunk_offset, start_offset); + if (!vgmstream->layout_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_layered; - if (total_subsongs > 1 && multistreams > 1) { - goto fail; /* not known */ + break; + } + else { + /* interleaved subsongs (section layers) */ + size_t frame_size = read_32bit(chunk_offset+0x00,streamFile); + + if (!init_xvag_atrac9(streamFile, vgmstream, &xvag, chunk_offset)) + goto fail; + temp_streamFile = setup_xvag_streamfile(streamFile, start_offset, frame_size*xvag.factor,frame_size, (target_subsong-1), total_subsongs); + if (!temp_streamFile) goto fail; + start_offset = 0; } - else if (total_subsongs > 1) { - /* interleaves 'multiplier' superframes per subsong (all share config_data) */ - cfg.interleave_skip = read_32bit(chunk_offset+0x00,streamFile) * interleave_factor; - cfg.subsong_skip = total_subsongs; - /* start in subsong's first superframe */ - start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); - } - else if (multistreams > 1) { - /* Vita multichannel (flower) interleaves streams like MPEG - * PS4 (The Last of Us) uses ATRAC9's multichannel directly instead (multistreams==1) */ - goto fail;//todo add - } - //if (multistreams == vgmstream->channels) goto fail; - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; break; } #endif - //case 0x??: /* PCM? */ default: goto fail; } - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamFile,start_offset)) goto fail; - - if (vgmstream->layout_type == layout_blocked_xvag_subsong) - block_update_xvag_subsong(start_offset, vgmstream); - + close_streamfile(temp_streamFile); return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); return NULL; } +#ifdef VGM_USE_ATRAC9 +static int init_xvag_atrac9(STREAMFILE *streamFile, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE; + atrac9_config cfg = {0}; -static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end) { - uint8_t testBuffer[0x10]; - int loopStartPointsCount=0; - int loopEndPointsCount=0; - off_t readOffset = 0; - off_t loopStartPoints[0x10]; - off_t loopEndPoints[0x10]; + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(chunk_offset+0x08,streamFile); + cfg.encoder_delay = read_32bit(chunk_offset+0x14,streamFile); - off_t loopStart = 0; - off_t loopEnd = 0; - off_t fileLength; - int loop_flag = 0; + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; - readOffset=start_offset; - fileLength = get_streamfile_size(streamFile); - - // get the loops the same way we get on .MIB - do { - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - // Loop Start ... - if(testBuffer[0x01]==0x06) { - if(loopStartPointsCount<0x10) { - loopStartPoints[loopStartPointsCount] = readOffset-0x10; - loopStartPointsCount++; - } - } - - // Loop End ... - if(((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) || (testBuffer[0x01]==0x01)) { - if(loopEndPointsCount<0x10) { - loopEndPoints[loopEndPointsCount] = readOffset; //-0x10; - loopEndPointsCount++; - } - } - - } while (readOffset<((int32_t)fileLength)); - - // Calc Loop Points & Interleave ... - if(loopStartPointsCount>=channel_count) { - // can't get more then 0x10 loop point ! - if((loopStartPointsCount<=0x0F) && (loopStartPointsCount>=2)) { - // Always took the first 2 loop points - loopStart=loopStartPoints[1]-start_offset; - loop_flag=1; - } else { - loopStart=0; - } - } - - if(loopEndPointsCount>=channel_count) { - // can't get more then 0x10 loop point ! - if((loopEndPointsCount<=0x0F) && (loopEndPointsCount>=2)) { - loop_flag=1; - loopEnd=loopEndPoints[loopEndPointsCount-1]-start_offset; - } else { - loopEnd=0; - } - } - - // as i can get on the header if a song is looped or not - // if try to take the loop marker on the file - // if the last byte of the file is = 00 is assume that the song is not looped - // i know that i can cover 95% of the file, but can't work on some of them - if(read_8bit((fileLength-1),streamFile)==0) - loop_flag=0; - - if (loop_flag) { - *loop_start = loopStart; - *loop_end = loopEnd; - } - - return loop_flag; + return 1; +fail: + return 0; +} +#endif + +static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_header * xvag, off_t chunk_offset, off_t start_offset) { + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE; + int i, layers = xvag->layers; + + + /* init layout */ + data = init_layout_layered(layers); + if (!data) goto fail; + + /* interleaves frames per substreams */ + for (i = 0; i < layers; i++) { + int layer_channels = xvag->channels / layers; /* all streams must be equal (XVAG limitation) */ + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, xvag->loop_flag); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = xvag->sample_rate; + data->layers[i]->num_samples = xvag->num_samples; + + switch(xvag->codec) { +#ifdef VGM_USE_ATRAC9 + case 0x09: { + size_t frame_size = read_32bit(chunk_offset+0x00,streamFile); + + if (!init_xvag_atrac9(streamFile, data->layers[i], xvag, chunk_offset)) + goto fail; + temp_streamFile = setup_xvag_streamfile(streamFile, start_offset, frame_size*xvag->factor,frame_size, i, layers); + if (!temp_streamFile) goto fail; + break; + } +#endif + default: + goto fail; + } + + if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) { + goto fail; + } + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + close_streamfile(temp_streamFile); + return data; + +fail: + close_streamfile(temp_streamFile); + free_layout_layered(data); + return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xvag_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/xvag_streamfile.h new file mode 100644 index 000000000..bb9820ac0 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xvag_streamfile.h @@ -0,0 +1,163 @@ +#ifndef _XVAG_STREAMFILE_H_ +#define _XVAG_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + int stream_number; + int stream_count; + size_t interleave_size; + size_t frame_size; + off_t stream_offset; + //size_t stream_size; + + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + + size_t skip_size; /* size to skip from a block start to reach data start */ + size_t data_size; /* logical size of the block */ + + size_t logical_size; +} xvag_io_data; + + +static size_t xvag_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xvag_io_data* data) { + size_t total_read = 0; + + /* ignore bad reads */ + if (offset < 0 || offset > data->logical_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->data_size = 0; + } + + /* read blocks, one at a time */ + while (length > 0) { + + /* ignore EOF */ + if (data->logical_offset >= data->logical_size) { + break; + } + + /* process new block */ + if (data->data_size == 0) { + data->skip_size = data->interleave_size * data->stream_number; + data->data_size = data->interleave_size; + + /* some ATRAC9 XVAG have padding+RIFF at start [The Last of Us (PS4), Farpoint (PS4)] */ + if (data->logical_offset == 0 && read_32bitBE(data->physical_offset+data->skip_size,streamfile) == 0) { + data->skip_size += data->frame_size; + data->data_size -= data->frame_size; + } + } + + /* move to next block */ + if (offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->interleave_size*data->stream_count; + data->logical_offset += data->data_size; + data->data_size = 0; + continue; + } + + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_offset; + + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + + offset += bytes_done; + total_read += bytes_done; + length -= bytes_done; + dest += bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + + } + + return total_read; +} + +static size_t xvag_io_size(STREAMFILE *streamfile, xvag_io_data* data) { + off_t physical_offset = data->stream_offset; + off_t max_physical_offset = get_streamfile_size(streamfile); + size_t logical_size = 0; + + if (data->logical_size) + return data->logical_size; + + /* get size of the logical stream */ + while (physical_offset < max_physical_offset) { + size_t skip_size = data->interleave_size * data->stream_number; + size_t data_size = data->interleave_size; + + /* some ATRAC9 XVAG have padding+RIFF at start [The Last of Us (PS4), Farpoint (PS4)] */ + if (logical_size == 0 && read_32bitBE(physical_offset+skip_size,streamfile) == 0) { + skip_size += data->frame_size; + data_size -= data->frame_size; + } + + logical_size += data_size; + physical_offset += data->interleave_size*data->stream_count; + } + + if (logical_size > max_physical_offset) + return 0; + data->logical_size = logical_size; + return data->logical_size; +} + +/* Prepares custom IO for XVAG, which interleaves many superframes per subsong/layer. + * May have start padding, even with only one subsong. All layers share config_data too. */ +static STREAMFILE* setup_xvag_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t interleave_size, size_t frame_size, int stream_number, int stream_count) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + xvag_io_data io_data = {0}; + size_t io_data_size = sizeof(xvag_io_data); + + io_data.stream_number = stream_number; + io_data.stream_count = stream_count; + io_data.stream_offset = stream_offset; + //io_data.stream_size = stream_size; + io_data.interleave_size = interleave_size; + io_data.frame_size = frame_size; + io_data.physical_offset = stream_offset; + io_data.logical_size = xvag_io_size(streamFile, &io_data); /* force init */ + + if (io_data.logical_size == 0) { + VGM_LOG("XVAG: wrong logical size\n"); + goto fail; + } + + /* 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, xvag_io_read,xvag_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + + +#endif /* _XVAG_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index e970c78b4..7f6f9f14f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -83,13 +83,15 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile,"xwb")) + /* .xwb: standard + * .xna: Touhou Makukasai ~ Fantasy Danmaku Festival (PC) */ + if (!check_extensions(streamFile,"xwb,xna")) goto fail; if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */ (read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */ goto fail; - xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44;/* WBND */ + xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44; /* WBND */ if (xwb.little_endian) { read_32bit = read_32bitLE; } else { @@ -111,8 +113,8 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { 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_size = xwb.entry_elem_size * xwb.total_subsongs; xwb.data_offset = xwb.entry_offset + xwb.entry_size; xwb.data_size = get_streamfile_size(streamFile) - xwb.data_offset; @@ -381,6 +383,10 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { xwb.loop_start_sample = msd.loop_start_sample; xwb.loop_end_sample = msd.loop_end_sample; + /* for XWB v22 (and below?) this seems normal [Project Gotham Racing (X360)] */ + if (xwb.num_samples == 0) + xwb.num_samples = msd.num_samples; + // todo fix properly (XWB loop_start/end seem to count padding samples while XMA1 RIFF doesn't) //this doesn't seem ok because can fall within 0 to 512 (ie.- first frame, 384) //if (xwb.loop_start_sample) xwb.loop_start_sample -= 512; @@ -704,17 +710,17 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head off = 0; if (xsb_version <= XSB_XACT1_MAX) { - xsb.xsb_wavebanks_count = 1; //read_8bit(0x22, streamFile); - xsb.xsb_sounds_count = read_16bit(0x1e, streamFile);//@ 0x1a? 0x1c? + xsb.xsb_wavebanks_count = 1; //(uint8_t)read_8bit(0x22, streamFile); + xsb.xsb_sounds_count = (uint16_t)read_16bit(0x1e, streamFile);//@ 0x1a? 0x1c? //xsb.xsb_names_size = 0; //xsb.xsb_names_offset = 0; xsb.xsb_nameoffsets_offset = 0; xsb.xsb_sounds_offset = 0x38; } else if (xsb_version <= XSB_XACT2_MAX) { - xsb.xsb_simple_sounds_count = read_16bit(0x09, streamFile); - xsb.xsb_complex_sounds_count = read_16bit(0x0B, streamFile); - xsb.xsb_wavebanks_count = read_8bit(0x11, streamFile); - xsb.xsb_sounds_count = read_16bit(0x12, streamFile); + xsb.xsb_simple_sounds_count = (uint16_t)read_16bit(0x09, streamFile); + xsb.xsb_complex_sounds_count = (uint16_t)read_16bit(0x0B, streamFile); + xsb.xsb_wavebanks_count = (uint8_t)read_8bit(0x11, streamFile); + xsb.xsb_sounds_count = (uint16_t)read_16bit(0x12, streamFile); //0x14: 16b unk //xsb.xsb_names_size = read_32bit(0x16, streamFile); xsb.xsb_simple_sounds_offset = read_32bit(0x1a, streamFile); @@ -723,9 +729,9 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head xsb.xsb_nameoffsets_offset = read_32bit(0x3a, streamFile); xsb.xsb_sounds_offset = read_32bit(0x3e, streamFile); } else { - xsb.xsb_simple_sounds_count = read_16bit(0x13, streamFile); - xsb.xsb_complex_sounds_count = read_16bit(0x15, streamFile); - xsb.xsb_wavebanks_count = read_8bit(0x1b, streamFile); + xsb.xsb_simple_sounds_count = (uint16_t)read_16bit(0x13, streamFile); + xsb.xsb_complex_sounds_count = (uint16_t)read_16bit(0x15, streamFile); + xsb.xsb_wavebanks_count = (uint8_t)read_8bit(0x1b, streamFile); xsb.xsb_sounds_count = read_16bit(0x1c, streamFile); //xsb.xsb_names_size = read_32bit(0x1e, streamFile); xsb.xsb_simple_sounds_offset = read_32bit(0x22, streamFile); @@ -765,21 +771,21 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head size = 0x14; if (flag != 0x01) { - VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented\n", flag, off); + //VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented\n", flag, off); goto fail; } - s->wavebank = 0; //read_8bit(off+suboff + 0x02, streamFile); - s->stream_index = read_16bit(off+0x02, streamFile); + s->wavebank = 0; //(uint8_t)read_8bit(off+suboff + 0x02, streamFile); + s->stream_index = (uint16_t)read_16bit(off+0x02, streamFile); s->sound_offset = off; - s->name_offset = read_16bit(off+0x04, streamFile); + s->name_offset = (uint16_t)read_16bit(off+0x04, streamFile); } else { /* Each XSB sound has a variable size and somewhere inside is the stream/wavebank index. * Various flags control the sound layout, but I can't make sense of them so quick hack instead */ flag = read_8bit(off+0x00, streamFile); //0x01 16b unk, 0x03: 8b unk 04: 16b unk, 06: 8b unk - size = read_16bit(off+0x07, streamFile); + size = (uint16_t)read_16bit(off+0x07, streamFile); if (!(flag & 0x01)) { /* simple sound */ suboff = 0x09; @@ -788,7 +794,7 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head if (flag==0x01 || flag==0x03 || flag==0x05 || flag==0x07) { if (size == 0x49) { //grotesque hack for Eschatos (these flags are way too complex) suboff = 0x23; - } else if (size % 2 == 1 && read_16bit(off+size-0x2, streamFile)!=0) { + } else if (size % 2 == 1 && (uint16_t)read_16bit(off+size-0x2, streamFile)!=0) { suboff = size - 0x08 - 0x07; //7 unk bytes at the end } else { suboff = size - 0x08; @@ -796,18 +802,18 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head //} else if (flag==0x11) { /* Stardew Valley (Switch) */ // suboff = size; //??? } else { - VGM_LOG("XSB: xsb flag 0x%x (size=%x) at offset 0x%08lx not implemented\n", flag, size, off); + //VGM_LOG("XSB: xsb flag 0x%x (size=%x) at offset 0x%08lx not implemented\n", flag, size, off); goto fail; } } - s->stream_index = read_16bit(off+suboff + 0x00, streamFile); - s->wavebank = read_8bit(off+suboff + 0x02, streamFile); + s->stream_index = (uint16_t)read_16bit(off+suboff + 0x00, streamFile); + s->wavebank = (uint8_t)read_8bit(off+suboff + 0x02, streamFile); s->sound_offset = off; } if (s->wavebank+1 > xsb.xsb_wavebanks_count) { - VGM_LOG("XSB: unknown xsb wavebank id %i at offset 0x%lx\n", s->wavebank, off); + //VGM_LOG("XSB: unknown xsb wavebank id %i at offset 0x%lx\n", s->wavebank, off); goto fail; } @@ -877,7 +883,7 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head 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"); + //VGM_LOG("XSB: multiple xsb wavebanks with the same number of sounds, use -w to specify one of the wavebanks\n"); goto fail; } @@ -892,17 +898,17 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head } if (!cfg__selected_wavebank) { - VGM_LOG("XSB: multiple xsb wavebanks but autodetect didn't work\n"); + //VGM_LOG("XSB: multiple xsb wavebanks but autodetect didn't work\n"); goto fail; } if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count == 0) { - VGM_LOG("XSB: xsb selected wavebank %i has no sounds\n", cfg__selected_wavebank); + //VGM_LOG("XSB: xsb selected wavebank %i has no sounds\n", cfg__selected_wavebank); goto fail; } if (cfg__start_sound) { 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); + //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; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index b285164ea..6723274d0 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -9,13 +9,15 @@ /* a STREAMFILE that operates via standard IO using a buffer */ typedef struct { STREAMFILE sf; /* callbacks */ + FILE * infile; /* actual FILE */ - char name[PATH_LIMIT]; - off_t offset; /* current offset */ - size_t validsize; /* current buffer size */ + char name[PATH_LIMIT]; /* FILE filename */ + off_t offset; /* last read offset (info) */ + off_t buffer_offset; /* current buffer data start */ uint8_t * buffer; /* data buffer */ size_t buffersize; /* max buffer size */ - size_t filesize; /* cached file size (max offset) */ + size_t validsize; /* current buffer size */ + size_t filesize; /* buffered file size */ } STDIOSTREAMFILE; static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize); @@ -28,9 +30,9 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse return 0; /* is the part of the requested length in the buffer? */ - if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) { + if (offset >= streamfile->buffer_offset && offset < streamfile->buffer_offset + streamfile->validsize) { size_t length_to_read; - off_t offset_into_buffer = offset - streamfile->offset; + off_t offset_into_buffer = offset - streamfile->buffer_offset; length_to_read = streamfile->validsize - offset_into_buffer; if (length_to_read > length) @@ -43,32 +45,34 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse 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 */ + size_t length_to_read; - /* 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 */ + /* ignore requests at EOF */ + if (offset >= streamfile->filesize) { + //offset = streamfile->filesize; /* seems fseek doesn't clamp offset */ + VGM_ASSERT_ONCE(offset > streamfile->filesize, "STDIO: reading over filesize 0x%x @ 0x%"PRIx64" + 0x%x\n", streamfile->filesize, (off64_t)offset, length); + break; } /* position to new offset */ if (fseeko(streamfile->infile,offset,SEEK_SET)) { - streamfile->offset = streamfile->filesize; - return 0; /* fail miserably (fseek shouldn't fail and reach this) */ + break; /* this shouldn't happen in our code */ } - streamfile->offset = offset; + +#ifdef _MSC_VER + /* Workaround a bug that appears when compiling with MSVC (later versions). + * This bug is deterministic and seemingly appears randomly after seeking. + * It results in fread returning data from the wrong area of the file. + * HPS is one format that is almost always affected by this. */ + fseek(streamfile->infile, ftell(streamfile->infile), SEEK_SET); +#endif + + /* fill the buffer (offset now is beyond buffer_offset) */ + streamfile->buffer_offset = offset; + streamfile->validsize = fread(streamfile->buffer,sizeof(uint8_t),streamfile->buffersize,streamfile->infile); /* decide how much must be read this time */ if (length > streamfile->buffersize) @@ -76,46 +80,41 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse else length_to_read = length; - /* fill the buffer */ - length_read = fread(streamfile->buffer,sizeof(uint8_t),streamfile->buffersize,streamfile->infile); - 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 */ + /* give up on partial reads (EOF) */ + if (streamfile->validsize < length_to_read) { + memcpy(dest,streamfile->buffer,streamfile->validsize); + offset += streamfile->validsize; + length_read_total += streamfile->validsize; + break; } /* use the new buffer */ memcpy(dest,streamfile->buffer,length_to_read); + offset += length_to_read; length_read_total += length_to_read; length -= length_to_read; dest += length_to_read; - offset += length_to_read; } + streamfile->offset = offset; /* last fread offset */ return length_read_total; } - +static size_t get_size_stdio(STDIOSTREAMFILE * streamfile) { + return streamfile->filesize; +} +static off_t get_offset_stdio(STDIOSTREAMFILE *streamfile) { + return streamfile->offset; +} +static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t length) { + strncpy(buffer,streamfile->name,length); + buffer[length-1]='\0'; +} static void close_stdio(STDIOSTREAMFILE * streamfile) { fclose(streamfile->infile); free(streamfile->buffer); free(streamfile); } -static size_t get_size_stdio(STDIOSTREAMFILE * streamfile) { - return streamfile->filesize; -} - -static off_t get_offset_stdio(STDIOSTREAMFILE *streamFile) { - return streamFile->offset; -} - -static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t length) { - strncpy(buffer,streamfile->name,length); - buffer[length-1]='\0'; -} - static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const filename,size_t buffersize) { int newfd; FILE *newfile; @@ -143,19 +142,14 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil } static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize) { - uint8_t * buffer; - STDIOSTREAMFILE * streamfile; + uint8_t * buffer = NULL; + STDIOSTREAMFILE * streamfile = NULL; buffer = calloc(buffersize,1); - if (!buffer) { - return NULL; - } + if (!buffer) goto fail; streamfile = calloc(1,sizeof(STDIOSTREAMFILE)); - if (!streamfile) { - free(buffer); - return NULL; - } + if (!streamfile) goto fail; streamfile->sf.read = (void*)read_stdio; streamfile->sf.get_size = (void*)get_size_stdio; @@ -175,7 +169,20 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char fseeko(streamfile->infile,0,SEEK_END); streamfile->filesize = ftello(streamfile->infile); + /* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF + * (happens in banks like FSB, though rarely). Can be remedied with the + * preprocessor (-D_FILE_OFFSET_BITS=64 in GCC) but it's not well tested. */ + if (streamfile->filesize == 0xFFFFFFFF) { /* -1 on error */ + VGM_LOG("STREAMFILE: ftell error\n"); + goto fail; /* can be ignored but may result in strange/unexpected behaviors */ + } + return &streamfile->sf; + +fail: + free(buffer); + free(streamfile); + return NULL; } static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize) { @@ -208,7 +215,8 @@ typedef struct { STREAMFILE sf; STREAMFILE *inner_sf; - off_t offset; /* current buffer data start */ + off_t offset; /* last read offset (info) */ + off_t buffer_offset; /* current buffer data start */ uint8_t * buffer; /* data buffer */ size_t buffersize; /* max buffer size */ size_t validsize; /* current buffer size */ @@ -223,9 +231,9 @@ static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t o return 0; /* is the part of the requested length in the buffer? */ - if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) { + if (offset >= streamfile->buffer_offset && offset < streamfile->buffer_offset + streamfile->validsize) { size_t length_to_read; - off_t offset_into_buffer = offset - streamfile->offset; + off_t offset_into_buffer = offset - streamfile->buffer_offset; length_to_read = streamfile->validsize - offset_into_buffer; if (length_to_read > length) @@ -238,27 +246,21 @@ static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t o 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 */ + size_t length_to_read; - /* 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 */ + /* ignore requests at EOF */ + if (offset >= streamfile->filesize) { + //offset = streamfile->filesize; /* seems fseek doesn't clamp offset */ + VGM_ASSERT_ONCE(offset > streamfile->filesize, "BUFFER: reading over filesize 0x%x @ 0x%"PRIx64" + 0x%x\n", streamfile->filesize, (off64_t)offset, length); + break; } - streamfile->offset = offset; + /* fill the buffer (offset now is beyond buffer_offset) */ + streamfile->buffer_offset = offset; + streamfile->validsize = streamfile->inner_sf->read(streamfile->inner_sf, streamfile->buffer, streamfile->buffer_offset, streamfile->buffersize); /* decide how much must be read this time */ if (length > streamfile->buffersize) @@ -266,31 +268,30 @@ static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t o 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 */ + /* give up on partial reads (EOF) */ + if (streamfile->validsize < length_to_read) { + memcpy(dest,streamfile->buffer,streamfile->validsize); + offset += streamfile->validsize; + length_read_total += streamfile->validsize; + break; } /* use the new buffer */ memcpy(dest,streamfile->buffer,length_to_read); + offset += length_to_read; length_read_total += length_to_read; length -= length_to_read; dest += length_to_read; - offset += length_to_read; } + streamfile->offset = offset; /* last fread offset */ 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? + return streamfile->offset; /* cache */ } static void buffer_get_name(BUFFER_STREAMFILE *streamfile, char *buffer, size_t length) { streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ @@ -765,6 +766,10 @@ fail: /* **************************************************** */ +STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname) { + return streamFile->open(streamFile,pathname,STREAMFILE_DEFAULT_BUFFER_SIZE); +} + STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) { char filename_ext[PATH_LIMIT]; @@ -1028,6 +1033,7 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in return 0; } +/* copies name as-is (may include full path included) */ void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { streamFile->get_name(streamFile,buffer,size); } @@ -1053,6 +1059,18 @@ void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) strcpy(buffer, foldername); } } +/* copies the filename without path or extension */ +void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size) { + char *ext; + + get_streamfile_filename(streamFile,buffer,size); + + ext = strrchr(buffer,'.'); + if (ext) { + ext[0] = '\0'; /* remove .ext from buffer */ + } +} +/* copies path removing name (NULL when if filename has no path) */ void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { const char *path; @@ -1071,3 +1089,33 @@ void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) { streamFile->get_name(streamFile,filename,size); strcpy(filename, filename_extension(filename)); } + +/* debug util, mainly for custom IO testing */ +void dump_streamfile(STREAMFILE *streamFile, const char* out) { +#ifdef VGM_DEBUG_OUTPUT + off_t offset = 0; + FILE *f = NULL; + + if (out) { + f = fopen(out,"wb"); + if (!f) return; + } + + VGM_LOG("dump streamfile, size: %x\n", get_streamfile_size(streamFile)); + while (offset < get_streamfile_size(streamFile)) { + uint8_t buffer[0x8000]; + size_t read; + + read = read_streamfile(buffer,offset,0x8000,streamFile); + if (out) + fwrite(buffer,sizeof(uint8_t),read, f); + else + VGM_LOGB(buffer,read,0); + offset += read; + } + + if (out) { + fclose(f); + } +#endif +} diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index f7a3949f6..85f964747 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -16,16 +16,29 @@ #include "streamtypes.h" #include "util.h" + +/* MSVC fixes (though mingw uses MSVCRT but not MSC_VER, maybe use AND?) */ #if defined(__MSVCRT__) || defined(_MSC_VER) -#include -#define fseeko fseek -#define ftello ftell -#define dup _dup -#ifdef fileno -#undef fileno -#endif -#define fileno _fileno -#define fdopen _fdopen + #include + + #ifndef fseeko + #define fseeko fseek + #endif + #ifndef ftello + #define ftello ftell + #endif + + #define dup _dup + + #ifdef fileno + #undef fileno + #endif + #define fileno _fileno + #define fdopen _fdopen + +// #ifndef off64_t +// #define off_t __int64 +// #endif #endif #if defined(XBMC) @@ -98,6 +111,10 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakena * 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 (path)+filename. + * Just a wrapper, to avoid having to access the STREAMFILE's callbacks directly. */ +STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname); + /* 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); @@ -197,6 +214,9 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in 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_basename(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); + +void dump_streamfile(STREAMFILE *streamFile, const char* out); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index a2c5765f7..c5469cf4f 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -77,14 +77,14 @@ void concatn(int length, char * dst, const char * src); /* Simple stdout logging for debugging and regression testing purposes. - * Needs C99 variadic macros, uses do..while to force ; as statement */ + * Needs C99 variadic macros, uses do..while to force ";" as statement */ #ifdef VGM_DEBUG_OUTPUT /* equivalent to printf when condition is true */ #define VGM_ASSERT(condition, ...) \ - do { if (condition) printf(__VA_ARGS__); } while (0) + do { if (condition) {printf(__VA_ARGS__);} } while (0) #define VGM_ASSERT_ONCE(condition, ...) \ - do { static int written; if (!written) { if (condition) printf(__VA_ARGS__); written = 1; } } while (0) + do { static int written; if (!written) { if (condition) {printf(__VA_ARGS__); written = 1;} } } while (0) /* equivalent to printf */ #define VGM_LOG(...) \ do { printf(__VA_ARGS__); } while (0) @@ -110,6 +110,7 @@ void concatn(int length, char * dst, const char * src); #else/*VGM_DEBUG_OUTPUT*/ #define VGM_ASSERT(condition, ...) /* nothing */ +#define VGM_ASSERT_ONCE(condition, ...) /* nothing */ #define VGM_LOG(...) /* nothing */ #define VGM_LOG_ONCE(...) /* nothing */ #define VGM_LOGF() /* nothing */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index c23956fbc..35f1cfecb 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -32,7 +32,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_dsp_std, init_vgmstream_ngc_dsp_std_le, init_vgmstream_ngc_mdsp_std, - init_vgmstream_ngc_dsp_csmp, + init_vgmstream_csmp, + init_vgmstream_rfrm, init_vgmstream_cstr, init_vgmstream_gcsw, init_vgmstream_ps2_ads, @@ -41,18 +42,15 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_cdxa, init_vgmstream_ps2_rxws, init_vgmstream_ps2_rxw, - init_vgmstream_ps2_int, init_vgmstream_ngc_dsp_stm, init_vgmstream_ps2_exst, init_vgmstream_ps2_svag, - init_vgmstream_ps2_mib, + init_vgmstream_mib_mih, init_vgmstream_ngc_mpdsp, init_vgmstream_ps2_mic, init_vgmstream_ngc_dsp_std_int, - init_vgmstream_raw, - init_vgmstream_ps2_vag, + init_vgmstream_vag, init_vgmstream_psx_gms, - init_vgmstream_ps2_str, init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, init_vgmstream_xbox_wavm, @@ -63,9 +61,9 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_genh, #ifdef VGM_USE_VORBIS init_vgmstream_ogg_vorbis, - init_vgmstream_sli_ogg, - init_vgmstream_sfl, #endif + init_vgmstream_sli_ogg, + init_vgmstream_sfl_ogg, #if 0 init_vgmstream_mp4_aac, #endif @@ -142,13 +140,13 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_sat_sap, init_vgmstream_dc_idvi, init_vgmstream_ps2_rnd, - init_vgmstream_wii_idsp, + init_vgmstream_idsp_tt, init_vgmstream_kraw, init_vgmstream_ps2_omu, init_vgmstream_ps2_xa2, - init_vgmstream_idsp2, - init_vgmstream_idsp3, - init_vgmstream_idsp4, + init_vgmstream_nub_idsp, + init_vgmstream_idsp_nl, + init_vgmstream_idsp_ie, init_vgmstream_ngc_ymf, init_vgmstream_sadl, init_vgmstream_ps2_ccc, @@ -212,7 +210,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_tk5, init_vgmstream_ps2_vsf_tta, init_vgmstream_ads, - init_vgmstream_wii_str, init_vgmstream_ps2_mcg, init_vgmstream_zsd, init_vgmstream_ps2_vgs, @@ -266,9 +263,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_dsp_mpds, init_vgmstream_dsp_str_ig, init_vgmstream_ea_swvr, - init_vgmstream_ngc_dsp_sth_str1, - init_vgmstream_ngc_dsp_sth_str2, - init_vgmstream_ngc_dsp_sth_str3, init_vgmstream_ps2_b1s, init_vgmstream_ps2_wad, init_vgmstream_dsp_xiii, @@ -299,7 +293,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_strlr, init_vgmstream_lsf_n1nj4n, init_vgmstream_vawx, - init_vgmstream_pc_snds, init_vgmstream_ps2_wmus, init_vgmstream_hyperscan_kvag, init_vgmstream_ios_psnd, @@ -307,7 +300,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_pc_adp_otns, init_vgmstream_eb_sfx, init_vgmstream_eb_sf0, - init_vgmstream_ps3_klbs, init_vgmstream_ps2_mtaf, init_vgmstream_tun, init_vgmstream_wpd, @@ -322,7 +314,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_vbk, init_vgmstream_otm, init_vgmstream_bcstm, - init_vgmstream_3ds_idsp, + init_vgmstream_idsp_nus3, init_vgmstream_kt_g1l, init_vgmstream_kt_wiibgm, init_vgmstream_ktss, @@ -359,7 +351,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_wii_04sw, init_vgmstream_ea_bnk, init_vgmstream_ea_abk, - init_vgmstream_ea_hdr, + init_vgmstream_ea_hdr_dat, + init_vgmstream_ea_idx_big, init_vgmstream_ea_schl_fixed, init_vgmstream_sk_aud, init_vgmstream_stm, @@ -372,6 +365,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_opus_shinen, init_vgmstream_opus_nus3, init_vgmstream_opus_nlsd, + init_vgmstream_opus_nxa, init_vgmstream_pc_al2, init_vgmstream_pc_ast, init_vgmstream_naac, @@ -380,6 +374,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_vxn, init_vgmstream_ea_snr_sns, init_vgmstream_ea_sps, + init_vgmstream_ea_abk_new, + init_vgmstream_ea_hdr_sth_dat, init_vgmstream_ngc_vid1, init_vgmstream_flx, init_vgmstream_mogg, @@ -412,14 +408,49 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_opus_ppp, init_vgmstream_ubi_bao_pk, init_vgmstream_dsp_switch_audio, - init_vgmstream_dsp_sadf, + init_vgmstream_sadf, init_vgmstream_h4m, init_vgmstream_ps2_ads_container, init_vgmstream_asf, + init_vgmstream_xmd, + init_vgmstream_cks, + init_vgmstream_ckb, + init_vgmstream_wv6, + init_vgmstream_str_wav, + init_vgmstream_wavebatch, + init_vgmstream_hd3_bd3, + init_vgmstream_bnk_sony, + init_vgmstream_nus3bank, + init_vgmstream_scd_sscf, + init_vgmstream_dsp_vag, + init_vgmstream_dsp_itl_ch, + init_vgmstream_a2m, + init_vgmstream_ahv, + init_vgmstream_msv, + init_vgmstream_sdf_ps2, + init_vgmstream_svg, + init_vgmstream_vis, + init_vgmstream_sdf_3ds, + init_vgmstream_vai, + init_vgmstream_aif_asobo, + init_vgmstream_ao, + init_vgmstream_apc, + init_vgmstream_wv2, + init_vgmstream_xau_konami, + init_vgmstream_derf, + init_vgmstream_utk, + init_vgmstream_adpcm_capcom, + init_vgmstream_ue4opus, - init_vgmstream_txth, /* should go at the end (lower priority) */ + + /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ + init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ + init_vgmstream_ps2_int, /* .int raw PS-ADPCM */ + init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */ + init_vgmstream_pc_snds, /* .snds PC, after ps_headerless */ + init_vgmstream_raw, /* .raw PCM */ #ifdef VGM_USE_FFMPEG - init_vgmstream_ffmpeg, /* should go at the end */ + init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ #endif }; @@ -464,23 +495,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { } /* test if candidate for dual stereo */ - if (vgmstream->channels == 1 && ( - (vgmstream->meta_type == meta_DSP_STD) || - (vgmstream->meta_type == meta_PS2_VAGp) || - (vgmstream->meta_type == meta_GENH) || - (vgmstream->meta_type == meta_TXTH) || - (vgmstream->meta_type == meta_KRAW) || - (vgmstream->meta_type == meta_PS2_MIB) || - (vgmstream->meta_type == meta_NGC_LPS) || - (vgmstream->meta_type == meta_DSP_YGO) || - (vgmstream->meta_type == meta_DSP_AGSC) || - (vgmstream->meta_type == meta_PS2_SMPL) || - (vgmstream->meta_type == meta_NGCA) || - (vgmstream->meta_type == meta_NUB_VAG) || - (vgmstream->meta_type == meta_SPT_SPD) || - (vgmstream->meta_type == meta_EB_SFX) || - (vgmstream->meta_type == meta_CWAV) - )) { + if (vgmstream->channels == 1 && vgmstream->allow_dual_stereo == 1) { try_dual_file_stereo(vgmstream, streamFile, init_vgmstream_functions[i]); } @@ -495,6 +510,14 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { } #endif + /* files can have thousands subsongs, but let's put a limit */ + if (vgmstream->num_streams < 0 || vgmstream->num_streams > 65535) { + VGM_LOG("VGMSTREAM: wrong num_streams (ns=%i)\n", vgmstream->num_streams); + close_vgmstream(vgmstream); + continue; + } + + /* save info */ /* stream_index 0 may be used by plugins to signal "vgmstream default" (IOW don't force to 1) */ if (!vgmstream->stream_index) @@ -526,7 +549,8 @@ VGMSTREAM * init_vgmstream_from_STREAMFILE(STREAMFILE *streamFile) { return init_vgmstream_internal(streamFile); } -/* Reset a VGMSTREAM to its state at the start of playback. +/* Reset a VGMSTREAM to its state at the start of playback + * (when a plugin needs to seek back to zero, for instance). * Note that this does not reset the constituent STREAMFILES. */ void reset_vgmstream(VGMSTREAM * vgmstream) { /* copy the vgmstream back into itself */ @@ -551,7 +575,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #endif if (vgmstream->coding_type==coding_CRI_HCA) { - reset_hca(vgmstream); + reset_hca(vgmstream->codec_data); } if (vgmstream->coding_type==coding_EA_MT) { @@ -575,15 +599,14 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #endif #ifdef VGM_USE_G7221 - if (vgmstream->coding_type==coding_G7221 || - vgmstream->coding_type==coding_G7221C) { + if (vgmstream->coding_type==coding_G7221C) { reset_g7221(vgmstream); } #endif #ifdef VGM_USE_G719 if (vgmstream->coding_type==coding_G719) { - reset_g719(vgmstream); + reset_g719(vgmstream->codec_data, vgmstream->channels); } #endif @@ -599,6 +622,12 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { } #endif +#ifdef VGM_USE_CELT + if (vgmstream->coding_type==coding_CELT_FSB) { + reset_celt_fsb(vgmstream); + } +#endif + #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type==coding_FFmpeg) { reset_ffmpeg(vgmstream); @@ -606,7 +635,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #endif if (vgmstream->coding_type==coding_ACM) { - reset_acm(vgmstream); + reset_acm(vgmstream->codec_data); } if (vgmstream->coding_type == coding_NWA) { @@ -644,8 +673,10 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int looped) { VGMSTREAMCHANNEL * loop_channels; /* up to ~16 aren't too rare for multilayered files, more is probably a bug */ - if (channel_count <= 0 || channel_count > 64) + if (channel_count <= 0 || channel_count > 64) { + VGM_LOG("VGMSTREAM: error allocating %i channels\n", channel_count); return NULL; + } vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!vgmstream) return NULL; @@ -721,7 +752,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) { } if (vgmstream->coding_type==coding_EA_MT) { - free_ea_mt(vgmstream->codec_data); + free_ea_mt(vgmstream->codec_data, vgmstream->channels); vgmstream->codec_data = NULL; } @@ -751,8 +782,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) { #endif #ifdef VGM_USE_G7221 - if (vgmstream->coding_type == coding_G7221 || - vgmstream->coding_type == coding_G7221C) { + if (vgmstream->coding_type == coding_G7221C) { free_g7221(vgmstream); vgmstream->codec_data = NULL; } @@ -760,7 +790,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) { #ifdef VGM_USE_G719 if (vgmstream->coding_type == coding_G719) { - free_g719(vgmstream); + free_g719(vgmstream->codec_data, vgmstream->channels); vgmstream->codec_data = NULL; } #endif @@ -779,6 +809,13 @@ void close_vgmstream(VGMSTREAM * vgmstream) { } #endif +#ifdef VGM_USE_CELT + if (vgmstream->coding_type == coding_CELT_FSB) { + free_celt_fsb(vgmstream->codec_data); + vgmstream->codec_data = NULL; + } +#endif + if (vgmstream->coding_type==coding_ACM) { free_acm(vgmstream->codec_data); vgmstream->codec_data = NULL; @@ -861,20 +898,19 @@ void close_vgmstream(VGMSTREAM * vgmstream) { /* calculate samples based on player's config */ int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM * vgmstream) { if (vgmstream->loop_flag) { - if (fadeseconds < 0) { /* a bit hack-y to avoid signature change */ + if (vgmstream->loop_target == (int)looptimes) { /* set externally, as this function is info-only */ /* Continue playing the file normally after looping, instead of fading. * Most files cut abruply after the loop, but some do have proper endings. * With looptimes = 1 this option should give the same output vs loop disabled */ int loop_count = (int)looptimes; /* no half loops allowed */ - //vgmstream->loop_target = loop_count; /* handled externally, as this is into-only */ return vgmstream->loop_start_sample + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count + (vgmstream->num_samples - vgmstream->loop_end_sample); } else { - return (int32_t)(vgmstream->loop_start_sample + return vgmstream->loop_start_sample + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes - + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate); + + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate; } } else { @@ -890,13 +926,48 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa vgmstream->loop_ch = calloc(vgmstream->channels,sizeof(VGMSTREAMCHANNEL)); /* loop_ch will be populated when decoded samples reach loop start */ } + else if (!loop_flag && vgmstream->loop_flag) { + /* not important though */ + free(vgmstream->loop_ch); + vgmstream->loop_ch = NULL; + } + vgmstream->loop_flag = loop_flag; if (loop_flag) { vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; + } else { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = 0; + } + + /* propagate changes to layouts that need them */ + if (vgmstream->layout_type == layout_layered) { + int i; + layered_layout_data *data = vgmstream->layout_data; + for (i = 0; i < data->layer_count; i++) { + vgmstream_force_loop(data->layers[i], loop_flag, loop_start_sample, loop_end_sample); + } + } + /* segmented layout only works (ATM) with exact/header loop, full loop or no loop */ +} + +void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target) { + if (!vgmstream) return; + + vgmstream->loop_target = loop_target; /* loop count must be rounded (int) as otherwise target is meaningless */ + + /* propagate changes to layouts that need them */ + if (vgmstream->layout_type == layout_layered) { + int i; + layered_layout_data *data = vgmstream->layout_data; + for (i = 0; i < data->layer_count; i++) { + vgmstream_set_loop_target(data->layers[i], loop_target); + } } } + /* Decode data into sample buffer */ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { switch (vgmstream->layout_type) { @@ -904,7 +975,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre render_vgmstream_interleave(buffer,sample_count,vgmstream); break; case layout_none: - render_vgmstream_nolayout(buffer,sample_count,vgmstream); + render_vgmstream_flat(buffer,sample_count,vgmstream); break; case layout_blocked_mxch: case layout_blocked_ast: @@ -943,6 +1014,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_ea_wve_ad10: case layout_blocked_sthd: case layout_blocked_h4m: + case layout_blocked_xa_aiff: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_aix: @@ -959,6 +1031,26 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre } + /* swap channels if set, to create custom channel mappings */ + if (vgmstream->channel_mappings_on) { + int ch_from,ch_to,s; + sample temp; + for (s = 0; s < sample_count; s++) { + for (ch_from = 0; ch_from < vgmstream->channels; ch_from++) { + if (ch_from > 32) + continue; + + ch_to = vgmstream->channel_mappings[ch_from]; + if (ch_to < 1 || ch_to > 32 || ch_to > vgmstream->channels-1 || ch_from == ch_to) + continue; + + temp = buffer[s*vgmstream->channels + ch_from]; + buffer[s*vgmstream->channels + ch_from] = buffer[s*vgmstream->channels + ch_to]; + buffer[s*vgmstream->channels + ch_to] = temp; + } + } + } + /* 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) { @@ -997,10 +1089,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCM16BE: case coding_PCM16_int: case coding_PCM8: - case coding_PCM8_U: case coding_PCM8_int: - case coding_PCM8_SB_int: + case coding_PCM8_U: case coding_PCM8_U_int: + case coding_PCM8_SB: case coding_ULAW: case coding_ULAW_int: case coding_ALAW: @@ -1021,6 +1113,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_SDX2_int: case coding_CBD2: case coding_ACM: + case coding_DERF: case coding_NWA: case coding_SASSC: return 1; @@ -1034,6 +1127,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_IMA_int: case coding_DVI_IMA_int: case coding_3DS_IMA: + case coding_WV6_IMA: + case coding_ALP_IMA: return 2; case coding_XBOX_IMA: case coding_XBOX_IMA_mch: @@ -1055,8 +1150,11 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return (0x800 - 0x04) * 2; case coding_RAD_IMA_mono: return 32; + case coding_H4M_IMA: + return 0; /* variable (block-controlled) */ case coding_XA: + return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */ case coding_PSX: case coding_PSX_badflags: case coding_HEVAG: @@ -1073,7 +1171,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 128; case coding_MSADPCM: - return (vgmstream->interleave_block_size-(7-1)*vgmstream->channels)*2/vgmstream->channels; + return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; + case coding_MSADPCM_ck: + return (vgmstream->interleave_block_size - 0x07)*2 + 2; 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: @@ -1093,9 +1193,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { #ifdef VGM_USE_G7221 case coding_G7221C: - return 32000/50; - case coding_G7221: - return 16000/50; + return 32000/50; /* Siren7: 16000/50 */ #endif #ifdef VGM_USE_G719 case coding_G719: @@ -1122,10 +1220,12 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 256; /* (0x8c - 0xc) * 2 */ case coding_ASF: return 32; /* (0x11 - 0x1) * 2 */ + case coding_XMD: + return (vgmstream->interleave_block_size - 0x06)*2 + 2; case coding_EA_MT: - return 432; + return 0; /* 432, but variable in looped files */ case coding_CRI_HCA: - return clHCA_samplesPerBlock; + return 0; /* 1024 - delay/padding (which can be bigger than 1024) */ #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) case coding_MP4_AAC: return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame; @@ -1137,6 +1237,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { #ifdef VGM_USE_ATRAC9 case coding_ATRAC9: return 0; /* varies with config data, usually 256 or 1024 */ +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + return 0; /* 512? */ #endif default: return 0; @@ -1169,10 +1273,10 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_PCM16_int: return 0x02; case coding_PCM8: - case coding_PCM8_U: case coding_PCM8_int: - case coding_PCM8_SB_int: + case coding_PCM8_U: case coding_PCM8_U_int: + case coding_PCM8_SB: case coding_ULAW: case coding_ULAW_int: case coding_ALAW: @@ -1183,6 +1287,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_SDX2: case coding_SDX2_int: case coding_CBD2: + case coding_DERF: case coding_NWA: case coding_SASSC: return 0x01; @@ -1192,6 +1297,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_DVI_IMA: case coding_DVI_IMA_int: case coding_3DS_IMA: + case coding_WV6_IMA: + case coding_ALP_IMA: return 0x01; case coding_MS_IMA: case coding_RAD_IMA: @@ -1205,7 +1312,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x14; case coding_SNDS_IMA: case coding_OTNS_IMA: - return 0; + return 0; //todo: 0x01? case coding_UBI_IMA: /* variable (PCM then IMA) */ return 0; case coding_XBOX_IMA: @@ -1219,9 +1326,11 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x24 * vgmstream->channels; case coding_APPLE_IMA4: return 0x22; + case coding_H4M_IMA: + return 0x00; /* variable (block-controlled) */ case coding_XA: - return 0x0e*vgmstream->channels; + return 0x80; case coding_PSX: case coding_PSX_badflags: case coding_HEVAG: @@ -1241,6 +1350,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x4c*vgmstream->channels; case coding_MSADPCM: + case coding_MSADPCM_ck: return vgmstream->interleave_block_size; case coding_WS: return vgmstream->current_block_size; @@ -1259,7 +1369,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { #ifdef VGM_USE_G7221 case coding_G7221C: - case coding_G7221: #endif #ifdef VGM_USE_G719 case coding_G719: @@ -1280,11 +1389,17 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x8c; case coding_ASF: return 0x11; + case coding_XMD: + return vgmstream->interleave_block_size; case coding_EA_MT: return 0; /* variable (frames of bit counts or PCM frames) */ #ifdef VGM_USE_ATRAC9 case coding_ATRAC9: return 0; /* varies with config data, usually 0x100-200 */ +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + return 0; /* varies, usually 0x80-100 */ #endif default: /* Vorbis, MPEG, ACM, etc */ return 0; @@ -1312,439 +1427,408 @@ int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream) { /* Decode samples into the buffer. Assume that we have written samples_written into the * buffer already, and we have samples_to_do consecutive samples ahead of us. */ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample * buffer) { - int chan; + int ch; switch (vgmstream->coding_type) { case coding_CRI_ADX: - for (chan=0;chanchannels;chan++) { - decode_adx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_CRI_ADX_exp: - for (chan=0;chanchannels;chan++) { - decode_adx_exp(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx_exp(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_CRI_ADX_fixed: - for (chan=0;chanchannels;chan++) { - decode_adx_fixed(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx_fixed(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_CRI_ADX_enc_8: case coding_CRI_ADX_enc_9: - for (chan=0;chanchannels;chan++) { - decode_adx_enc(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx_enc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_NGC_DSP: - for (chan=0;chanchannels;chan++) { - decode_ngc_dsp(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dsp(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_NGC_DSP_subint: - for (chan=0;chanchannels;chan++) { - decode_ngc_dsp_subint(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, - chan, vgmstream->interleave_block_size); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dsp_subint(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, + ch, vgmstream->interleave_block_size); } break; + case coding_PCM16LE: - for (chan=0;chanchannels;chan++) { - decode_pcm16LE(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16le(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + 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, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16be(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCM16_int: - for (chan=0;chanchannels;chan++) { - decode_pcm16_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->codec_endian); } break; case coding_PCM8: - for (chan=0;chanchannels;chan++) { - decode_pcm8(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; - case coding_PCM8_U: - for (chan=0;chanchannels;chan++) { - decode_pcm8_unsigned(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCM8_int: - for (chan=0;chanchannels;chan++) { - decode_pcm8_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; - case coding_PCM8_SB_int: - for (chan=0;chanchannels;chan++) { - decode_pcm8_sb_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + case coding_PCM8_U: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_unsigned(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCM8_U_int: - for (chan=0;chanchannels;chan++) { - decode_pcm8_unsigned_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_unsigned_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; + case coding_PCM8_SB: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_sb(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_ULAW: - for (chan=0;chanchannels;chan++) { - decode_ulaw(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ulaw(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_ULAW_int: - for (chan=0;chanchannels;chan++) { - decode_ulaw_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ulaw_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_ALAW: - for (chan=0;chanchannels;chan++) { - decode_alaw(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_alaw(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCMFLOAT: - for (chan=0;chanchannels;chan++) { - decode_pcmfloat(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcmfloat(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->codec_endian); } break; case coding_NDS_IMA: - for (chan=0;chanchannels;chan++) { - decode_nds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_DAT4_IMA: - for (chan=0;chanchannels;chan++) { - decode_dat4_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_dat4_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_XBOX_IMA: - for (chan=0;chanchannels;chan++) { - decode_xbox_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + case coding_XBOX_IMA_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_XBOX_IMA); + decode_xbox_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, + is_stereo); } 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); - } - break; - case coding_XBOX_IMA_int: - for (chan=0;chanchannels;chan++) { - decode_xbox_ima_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xbox_ima_mch(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_MS_IMA: - for (chan=0;chanchannels;chan++) { - decode_ms_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ms_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_RAD_IMA: - for (chan=0;chanchannels;chan++) { - decode_rad_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_rad_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_RAD_IMA_mono: - for (chan=0;chanchannels;chan++) { - decode_rad_ima_mono(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_rad_ima_mono(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_NGC_DTK: - for (chan=0;chanchannels;chan++) { - decode_ngc_dtk(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dtk(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_G721: - for (chan=0;chanchannels;chan++) { - decode_g721(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g721(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_NGC_AFC: - for (chan=0;chanchannels;chan++) { - decode_ngc_afc(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_afc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PSX: - for (chan=0;chanchannels;chan++) { - decode_psx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0); } break; case coding_PSX_badflags: - for (chan=0;chanchannels;chan++) { - decode_psx_badflags(&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, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 1); } break; case coding_PSX_cfg: - for (chan=0;chanchannels;chan++) { - decode_psx_configurable(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, vgmstream->interleave_block_size); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx_configurable(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); + } + break; + case coding_HEVAG: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_hevag(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_XA: - for (chan=0;chanchannels;chan++) { - decode_xa(vgmstream,buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_EA_XA: - for (chan=0;chanchannels;chan++) { - decode_ea_xa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_EA_XA_int: - for (chan=0;chanchannels;chan++) { - decode_ea_xa_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_EA_XA_V2: - for (chan=0;chanchannels;chan++) { - decode_ea_xa_v2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa_v2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_MAXIS_XA: - for (chan=0;chanchannels;chan++) { - decode_maxis_xa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_maxis_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_EA_XAS: - for (chan=0;chanchannels;chan++) { - decode_ea_xas(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xas(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; #ifdef VGM_USE_VORBIS case coding_OGG_VORBIS: - decode_ogg_vorbis(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); + decode_ogg_vorbis(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; case coding_VORBIS_custom: - decode_vorbis_custom(vgmstream, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); + decode_vorbis_custom(vgmstream, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif case coding_CRI_HCA: - decode_hca(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); + decode_hca(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, + samples_to_do); break; #ifdef VGM_USE_FFMPEG case coding_FFmpeg: decode_ffmpeg(vgmstream, - buffer+samples_written*vgmstream->channels, - samples_to_do, - vgmstream->channels); + buffer+samples_written*vgmstream->channels,samples_to_do,vgmstream->channels); break; #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) case coding_MP4_AAC: - decode_mp4_aac(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); + decode_mp4_aac(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif case coding_SDX2: - for (chan=0;chanchannels;chan++) { - decode_sdx2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sdx2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_SDX2_int: - for (chan=0;chanchannels;chan++) { - decode_sdx2_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sdx2_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_CBD2: - for (chan=0;chanchannels;chan++) { - decode_cbd2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cbd2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_CBD2_int: - for (chan=0;chanchannels;chan++) { - decode_cbd2_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cbd2_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; + case coding_DERF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_derf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_IMA: case coding_IMA_int: case coding_DVI_IMA: case coding_DVI_IMA_int: - for (chan=0;chanchannels;chan++) { + for (ch = 0; ch < vgmstream->channels; ch++) { int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_IMA) || (vgmstream->channels > 1 && vgmstream->coding_type == coding_DVI_IMA); int is_high_first = vgmstream->coding_type == coding_DVI_IMA || vgmstream->coding_type == coding_DVI_IMA_int; - decode_standard_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, chan, is_stereo, is_high_first); + decode_standard_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, + is_stereo, is_high_first); } break; case coding_3DS_IMA: - for (chan=0;chanchannels;chan++) { - decode_3ds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_3ds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; + case coding_WV6_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wv6_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_ALP_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_alp_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_APPLE_IMA4: - for (chan=0;chanchannels;chan++) { - decode_apple_ima4(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_apple_ima4(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_SNDS_IMA: - for (chan=0;chanchannels;chan++) { - decode_snds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_snds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_OTNS_IMA: - for (chan=0;chanchannels;chan++) { - decode_otns_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_otns_ima(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_FSB_IMA: - for (chan=0;chanchannels;chan++) { - decode_fsb_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_fsb_ima(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_WWISE_IMA: - for (chan=0;chanchannels;chan++) { - decode_wwise_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wwise_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_REF_IMA: - for (chan=0;chanchannels;chan++) { - decode_ref_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ref_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_AWC_IMA: - for (chan=0;chanchannels;chan++) { - decode_awc_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_awc_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_UBI_IMA: - for (chan=0;chanchannels;chan++) { - decode_ubi_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ubi_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); + } + break; + case coding_H4M_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + uint16_t frame_format = (uint16_t)((vgmstream->codec_config >> 8) & 0xFFFF); + + decode_h4m_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, + frame_format); } break; case coding_WS: - for (chan=0;chanchannels;chan++) { - decode_ws(vgmstream,chan,buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ws(vgmstream,ch,buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; @@ -1754,175 +1838,158 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to case coding_MPEG_layer1: case coding_MPEG_layer2: case coding_MPEG_layer3: - decode_mpeg( - vgmstream, - buffer+samples_written*vgmstream->channels, - samples_to_do, - vgmstream->channels); + decode_mpeg(vgmstream,buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif #ifdef VGM_USE_G7221 - case coding_G7221: case coding_G7221C: - for (chan=0;chanchannels;chan++) { - decode_g7221(vgmstream, - buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, - samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g7221(vgmstream, buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,samples_to_do, ch); } break; #endif #ifdef VGM_USE_G719 case coding_G719: - for (chan=0;chanchannels;chan++) { - decode_g719(vgmstream, - buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, - samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g719(vgmstream, buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,samples_to_do, ch); } break; #endif #ifdef VGM_USE_MAIATRAC3PLUS case coding_AT3plus: - for (chan=0;chanchannels;chan++) { - decode_at3plus(vgmstream, - buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, - samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_at3plus(vgmstream, buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,samples_to_do, ch); } break; #endif #ifdef VGM_USE_ATRAC9 case coding_ATRAC9: - decode_atrac9(vgmstream, - buffer+samples_written*vgmstream->channels, - samples_to_do, - vgmstream->channels); + decode_atrac9(vgmstream, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); + break; +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + decode_celt_fsb(vgmstream, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif case coding_ACM: - decode_acm(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels, + decode_acm(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, samples_to_do, vgmstream->channels); break; case coding_NWA: decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa, - buffer+samples_written*vgmstream->channels, - samples_to_do - ); + buffer+samples_written*vgmstream->channels, samples_to_do); break; case coding_MSADPCM: if (vgmstream->channels == 2) { - decode_msadpcm_stereo(vgmstream, - buffer+samples_written*vgmstream->channels, - vgmstream->samples_into_block, - samples_to_do); + decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels, + vgmstream->samples_into_block,samples_to_do); } - else if (vgmstream->channels == 1) - { - decode_msadpcm_mono(vgmstream, - buffer+samples_written*vgmstream->channels, - vgmstream->samples_into_block, - samples_to_do); + else if (vgmstream->channels == 1) { + decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0); + } + break; + case coding_MSADPCM_ck: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_msadpcm_ck(vgmstream,buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_AICA: case coding_AICA_int: - for (chan=0;chanchannels;chan++) { + for (ch = 0; ch < vgmstream->channels; ch++) { 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); + decode_aica(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, + 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); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_yamaha(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } 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); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_yamaha_nxap(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_NDS_PROCYON: - for (chan=0;chanchannels;chan++) { - decode_nds_procyon(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nds_procyon(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_L5_555: - for (chan=0;chanchannels;chan++) { - decode_l5_555(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_l5_555(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } - break; case coding_SASSC: - for (chan=0;chanchannels;chan++) { - decode_SASSC(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sassc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_LSF: - for (chan=0;chanchannels;chan++) { - decode_lsf(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_lsf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_MTAF: - for (chan=0;chanchannels;chan++) { - decode_mtaf(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, - chan, vgmstream->channels); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mtaf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_MTA2: - for (chan=0;chanchannels;chan++) { - decode_mta2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mta2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_MC3: - for (chan=0;chanchannels;chan++) { - decode_mc3(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mc3(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } 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); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_fadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_ASF: - for (chan=0;chanchannels;chan++) { - decode_asf(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_asf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_XMD: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xmd(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, + vgmstream->interleave_block_size); } break; case coding_EA_MT: - for (chan=0;chanchannels;chan++) { - decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels, samples_to_do, ch); } break; default: @@ -1997,7 +2064,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { /* prepare certain codecs' internal state for looping */ if (vgmstream->coding_type==coding_CRI_HCA) { - loop_hca(vgmstream); + loop_hca(vgmstream->codec_data); } if (vgmstream->coding_type==coding_EA_MT) { @@ -2038,6 +2105,12 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { } #endif +#ifdef VGM_USE_CELT + if (vgmstream->coding_type==coding_CELT_FSB) { + seek_celt_fsb(vgmstream, vgmstream->loop_sample); + } +#endif + #ifdef VGM_USE_MPEG if (vgmstream->coding_type==coding_MPEG_custom || vgmstream->coding_type==coding_MPEG_ealayer3 || @@ -2128,8 +2201,14 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { switch (vgmstream->coding_type) { #ifdef VGM_USE_FFMPEG case coding_FFmpeg: { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - if (vgmstream->codec_data) { + ffmpeg_codec_data *data = (ffmpeg_codec_data *)vgmstream->codec_data; + if (!data && vgmstream->layout_data) { + layered_layout_data* layout_data = vgmstream->layout_data; + if (layout_data->layers[0]->coding_type == coding_FFmpeg) + data = layout_data->layers[0]->codec_data; + } + + if (data) { if (data->codec && data->codec->long_name) { snprintf(temp,TEMPSIZE,"%s",data->codec->long_name); } else if (data->codec && data->codec->name) { @@ -2169,7 +2248,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { "\n"); concatn(length,desc,temp); - if (vgmstream->layout_type == layout_interleave) { + if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) { snprintf(temp,TEMPSIZE, "interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size); @@ -2183,16 +2262,18 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { } } - /* codecs with blocks + headers (there are more, this is a start) */ + /* codecs with configurable frame size */ if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) { switch (vgmstream->coding_type) { case coding_MSADPCM: + case coding_MSADPCM_ck: case coding_MS_IMA: case coding_MC3: case coding_WWISE_IMA: case coding_REF_IMA: + case coding_PSX_cfg: snprintf(temp,TEMPSIZE, - "block size: %#x bytes\n", + "frame size: %#x bytes\n", (int32_t)vgmstream->interleave_block_size); concatn(length,desc,temp); break; @@ -2252,8 +2333,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea {"left","right"}, {"Left","Right"}, {".V0",".V1"}, /* Homura (PS2) */ - {".L",".R"}, /* Crash Nitro Racing (PS2) */ - {"_0","_1"}, //unneeded? + {".L",".R"}, /* Crash Nitro Racing (PS2), Gradius V (PS2) */ + {"_0","_1"}, //fake for Homura/unneeded? }; char new_filename[PATH_LIMIT]; char * ext; @@ -2400,79 +2481,42 @@ fail: return; } -/* average bitrate helper */ -static int get_vgmstream_average_bitrate_channel_count(VGMSTREAM * vgmstream) -{ - //AAX, AIX, ACM? - - 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) { - ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *) vgmstream->codec_data; - return (data) ? 1 : 0; - } -#endif - if (vgmstream->coding_type==coding_CRI_HCA) { - hca_codec_data *data = (hca_codec_data *) vgmstream->codec_data; - return (data) ? 1 : 0; - } -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type==coding_FFmpeg) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - return (data) ? 1 : 0; - } -#endif -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type==coding_MP4_AAC) { - mp4_aac_codec_data *data = (mp4_aac_codec_data *) vgmstream->codec_data; - return (data) ? 1 : 0; - } -#endif - return vgmstream->channels; -} - -/* average bitrate helper */ -static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int channel) -{ - //AAX, AIX? +/* average bitrate helper to get STREAMFILE for a channel, since some codecs may use their own */ +static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int 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; + nwa_codec_data *data = vgmstream->codec_data; + return (data && data->nwa) ? data->nwa->file : NULL; } if (vgmstream->coding_type==coding_ACM) { - acm_codec_data *data = (acm_codec_data *) vgmstream->codec_data; - if (data && data->file) - return data->file->streamfile; + acm_codec_data *data = vgmstream->codec_data; + return (data && data->handle) ? data->streamfile : NULL; } #ifdef VGM_USE_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; + ogg_vorbis_codec_data *data = vgmstream->codec_data; + return data ? data->ov_streamfile.streamfile : NULL; } #endif if (vgmstream->coding_type==coding_CRI_HCA) { - hca_codec_data *data = (hca_codec_data *) vgmstream->codec_data; - return data->streamfile; + hca_codec_data *data = vgmstream->codec_data; + return data ? data->streamfile : NULL; } #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type==coding_FFmpeg) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - return data->streamfile; + ffmpeg_codec_data *data = vgmstream->codec_data; + return data ? data->streamfile : NULL; } #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) if (vgmstream->coding_type==coding_MP4_AAC) { - mp4_aac_codec_data *data = (mp4_aac_codec_data *) vgmstream->codec_data; - return data->if_file.streamfile; + mp4_aac_codec_data *data = vgmstream->codec_data; + return data ? data->if_file.streamfile : NULL; } #endif + return vgmstream->ch[channel].streamfile; } @@ -2485,15 +2529,15 @@ static int get_vgmstream_average_bitrate_from_streamfile(STREAMFILE * streamfile /* Return the average bitrate in bps of all unique files contained within this stream. */ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { - char path_current[PATH_LIMIT]; - char path_compare[PATH_LIMIT]; + STREAMFILE *streamfiles[64]; + const size_t streamfiles_max = 64; /* arbitrary max, */ + size_t streamfiles_size = 0; + size_t streams_size = 0; + unsigned int ch, sub; - unsigned int i, j; int bitrate = 0; int sample_rate = vgmstream->sample_rate; int length_samples = vgmstream->num_samples; - int channels; - STREAMFILE * streamFile; if (!sample_rate || !length_samples) return 0; @@ -2503,44 +2547,70 @@ 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) + + /* make a list of used streamfiles (repeats will be filtered below) */ 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) { - bitrate += get_vgmstream_average_bitrate_from_streamfile(streamFile, sample_rate, length_samples); + for (sub = 0; sub < data->segment_count; sub++) { + streams_size += data->segments[sub]->stream_size; + for (ch = 0; ch < data->segments[sub]->channels; ch++) { + if (streamfiles_size >= streamfiles_max) continue; + streamfiles[streamfiles_size] = get_vgmstream_average_bitrate_channel_streamfile(data->segments[sub], ch); + streamfiles_size++; + } } } - - /* Compares files by absolute paths, so bitrate doesn't multiply when the same STREAMFILE is reopened per channel */ - for (i = 1; i < channels; ++i) { - streamFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, i); - if (!streamFile) - continue; - streamFile->get_name(streamFile, path_current, sizeof(path_current)); - for (j = 0; j < i; ++j) { - STREAMFILE * compareFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, j); - if (!compareFile) + else if (vgmstream->layout_type==layout_layered) { + layered_layout_data *data = vgmstream->layout_data; + for (sub = 0; sub < data->layer_count; sub++) { + streams_size += data->layers[sub]->stream_size; + for (ch = 0; ch < data->layers[sub]->channels; ch++) { + if (streamfiles_size >= streamfiles_max) continue; + streamfiles[streamfiles_size] = get_vgmstream_average_bitrate_channel_streamfile(data->layers[sub], ch); + streamfiles_size++; + } + } + } + else { + for (ch = 0; ch < vgmstream->channels; ch++) { + if (streamfiles_size >= streamfiles_max) continue; - streamFile->get_name(compareFile, path_compare, sizeof(path_compare)); - if (!strcmp(path_current, path_compare)) - break; + streamfiles[streamfiles_size] = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch); + streamfiles_size++; + } + } + + /* could have a sum of all sub-VGMSTREAMs */ + if (streams_size) { + return get_vgmstream_average_bitrate_from_size(streams_size, sample_rate, length_samples); + } + + /* compare files by absolute paths, so bitrate doesn't multiply when the same STREAMFILE is + * reopened per channel, also skipping repeated pointers. */ + { + char path_current[PATH_LIMIT]; + char path_compare[PATH_LIMIT]; + unsigned int i, j; + + for (i = 0; i < streamfiles_size; i++) { + STREAMFILE * currentFile = streamfiles[i]; + if (!currentFile) continue; + get_streamfile_name(currentFile, path_current, sizeof(path_current)); + + for (j = 0; j < i; j++) { + STREAMFILE * compareFile = streamfiles[j]; + if (!compareFile) continue; + if (currentFile == compareFile) + break; + get_streamfile_name(compareFile, path_compare, sizeof(path_compare)); + if (strcmp(path_current, path_compare) == 0) + break; + } + + if (i == j) { /* current STREAMFILE hasn't appeared previously */ + bitrate += get_vgmstream_average_bitrate_from_streamfile(currentFile, sample_rate, length_samples); + } } - if (j == i) - bitrate += get_vgmstream_average_bitrate_from_streamfile(streamFile, sample_rate, length_samples); } return bitrate; @@ -2554,11 +2624,12 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { * Should be called in metas before returning the VGMSTREAM. */ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) { - STREAMFILE * file; + STREAMFILE * file = NULL; char filename[PATH_LIMIT]; int ch; int use_streamfile_per_channel = 0; int use_same_offset_per_channel = 0; + int is_stereo_codec = 0; /* stream/offsets not needed, managed by layout */ @@ -2568,7 +2639,9 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s return 1; /* stream/offsets not needed, managed by decoder */ - if (vgmstream->coding_type == coding_NWA) + if (vgmstream->coding_type == coding_NWA || + vgmstream->coding_type == coding_ACM || + vgmstream->coding_type == coding_CRI_HCA) return 1; #ifdef VGM_USE_FFMPEG @@ -2582,11 +2655,21 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s use_streamfile_per_channel = 1; } + /* if blocked layout (implicit) use multiple streamfiles; using only one leads to + * lots of buffer-trashing, with all the jumping around in the block layout */ + if (vgmstream->layout_type != layout_none && vgmstream->layout_type != layout_interleave) { + use_streamfile_per_channel = 1; + } + /* for mono or codecs like IMA (XBOX, MS IMA, MS ADPCM) where channels work with the same bytes */ if (vgmstream->layout_type == layout_none) { use_same_offset_per_channel = 1; } + /* stereo codecs interleave in 2ch pairs (interleave size should still be: full_block_size / channels) */ + if (vgmstream->layout_type == layout_interleave && vgmstream->coding_type == coding_XBOX_IMA) { + is_stereo_codec = 1; + } streamFile->get_name(streamFile,filename,sizeof(filename)); /* open the file for reading by each channel */ @@ -2600,6 +2683,10 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s off_t offset; if (use_same_offset_per_channel) { offset = start_offset; + } else if (is_stereo_codec) { + int ch_mod = (ch & 1) ? ch - 1 : ch; /* adjust odd channels (ch 0,1,2,3,4,5 > ch 0,0,2,2,4,4) */ + offset = start_offset + vgmstream->interleave_block_size*ch_mod; + //VGM_LOG("ch%i offset=%lx\n", ch,offset); } else { offset = start_offset + vgmstream->interleave_block_size*ch; } @@ -2616,6 +2703,9 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s } } + /* init first block for blocked layout (if not blocked this will do nothing) */ + block_update(start_offset, vgmstream); + /* EA-MT decoder is a bit finicky and needs this when channel offsets change */ if (vgmstream->coding_type == coding_EA_MT) { flush_ea_mt(vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 59065ce1e..5c8941ab1 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -10,19 +10,10 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ #include "streamfile.h" -/* Due mostly to licensing issues, Vorbis, MPEG, G.722.1, etc decoding is - * done by external libraries. - * If someone wants to do a standalone build, they can do it by simply - * removing these defines (and the references to the libraries in the Makefile) */ -#ifndef VGM_DISABLE_VORBIS +/* Due mostly to licensing issues, Vorbis, MPEG, G.722.1, etc decoding is done by external libraries. + * Libs are disabled by default, defined on compile-time for builds that support it */ #define VGM_USE_VORBIS -#endif - -#ifndef VGM_DISABLE_MPEG #define VGM_USE_MPEG -#endif - -/* disabled by default, defined on compile-time for builds that support it */ #define VGM_USE_G7221 #define VGM_USE_G719 //#define VGM_USE_MP4V2 @@ -30,6 +21,8 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ //#define VGM_USE_MAIATRAC3PLUS #define VGM_USE_FFMPEG #define VGM_USE_ATRAC9 +//#define VGM_USE_CELT + #ifdef VGM_USE_VORBIS #ifdef __APPLE__ @@ -46,10 +39,6 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ #include #endif -#ifdef VGM_USE_G719 -#include -#endif - #ifdef VGM_USE_MP4V2 #define MP4V2_NO_STDINT_DEFS #include @@ -73,11 +62,9 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ #ifdef BUILD_VGMSTREAM #include "coding/g72x_state.h" -#include "coding/acm_decoder.h" #include "coding/nwa_decoder.h" #else #include "g72x_state.h" -#include "acm_decoder.h" #include "nwa_decoder.h" #endif @@ -92,7 +79,7 @@ typedef enum { coding_PCM8_int, /* 8-bit PCM with sample-level interleave (for blocks) */ coding_PCM8_U, /* 8-bit PCM, unsigned (0x80 = 0) */ coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave (for blocks) */ - coding_PCM8_SB_int, /* 8-bit PCM, sign bit (others are 2's complement) with sample-level interleave (for blocks) */ + coding_PCM8_SB, /* 8-bit PCM, sign bit (others are 2's complement) */ coding_ULAW, /* 8-bit u-Law (non-linear PCM) */ coding_ULAW_int, /* 8-bit u-Law (non-linear PCM) with sample-level interleave (for blocks) */ @@ -131,6 +118,11 @@ typedef enum { coding_DVI_IMA, /* DVI IMA ADPCM (stereo or mono, high nibble first) */ coding_DVI_IMA_int, /* DVI IMA ADPCM (mono/interleave, high nibble first) */ coding_3DS_IMA, /* 3DS IMA ADPCM */ + coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ + coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */ + coding_WV6_IMA, /* Gorilla Systems WV6 4-bit IMA ADPCM */ + coding_ALP_IMA, /* High Voltage ALP 4-bit IMA ADPCM */ + coding_MS_IMA, /* Microsoft IMA ADPCM */ coding_XBOX_IMA, /* XBOX IMA ADPCM */ coding_XBOX_IMA_mch, /* XBOX IMA ADPCM (multichannel) */ @@ -140,15 +132,15 @@ typedef enum { coding_RAD_IMA, /* Radical IMA ADPCM */ 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 */ coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */ coding_WWISE_IMA, /* Audiokinetic Wwise IMA ADPCM */ coding_REF_IMA, /* Reflections IMA ADPCM */ coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */ coding_UBI_IMA, /* Ubisoft IMA ADPCM */ + coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ - coding_MSADPCM, /* Microsoft ADPCM */ + coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */ + coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */ coding_WS, /* Westwood Studios VBR ADPCM */ coding_AICA, /* Yamaha AICA ADPCM (stereo) */ coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */ @@ -156,19 +148,21 @@ typedef enum { 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 */ coding_LSF, /* lsf ADPCM (Fastlane Street Racing iPhone)*/ coding_MTAF, /* Konami MTAF ADPCM */ coding_MTA2, /* Konami MTA2 ADPCM */ coding_MC3, /* Paradigm MC3 3-bit ADPCM */ coding_FADPCM, /* FMOD FADPCM 4-bit ADPCM */ coding_ASF, /* Argonaut ASF 4-bit ADPCM */ + coding_XMD, /* Konami XMD 4-bit ADPCM */ /* others */ coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ coding_SDX2_int, /* SDX2 2:1 Squareroot-Delta-Exact compression with sample-level interleave */ coding_CBD2, /* CBD2 2:1 Cuberoot-Delta-Exact compression DPCM */ coding_CBD2_int, /* CBD2 2:1 Cuberoot-Delta-Exact compression, with sample-level interleave */ + coding_SASSC, /* Activision EXAKT SASSC 8-bit DPCM */ + coding_DERF, /* DERF 8-bit DPCM */ coding_ACM, /* InterPlay ACM */ coding_NWA, /* VisualArt's NWA */ @@ -190,7 +184,6 @@ typedef enum { #endif #ifdef VGM_USE_G7221 - coding_G7221, /* ITU G.722.1 (Polycom Siren 7) */ coding_G7221C, /* ITU G.722.1 annex C (Polycom Siren 14) */ #endif @@ -210,6 +203,10 @@ typedef enum { coding_ATRAC9, /* Sony ATRAC9 (MDCT-based) */ #endif +#ifdef VGM_USE_CELT + coding_CELT_FSB, /* Custom Xiph CELT (MDCT-based) */ +#endif + #ifdef VGM_USE_FFMPEG coding_FFmpeg, /* Formats handled by FFmpeg (ATRAC3, XMA, AC3, etc) */ #endif @@ -261,6 +258,7 @@ typedef enum { layout_blocked_ea_wve_ad10, /* EA WVE Ad10 blocks */ layout_blocked_sthd, /* Dream Factory STHD */ layout_blocked_h4m, /* H4M video */ + layout_blocked_xa_aiff, /* XA in AIFF files [Crusader: No Remorse (SAT), Road Rash (3DO)] */ /* otherwise odd */ layout_aix, /* CRI AIX's wheels within wheels */ @@ -272,13 +270,14 @@ typedef enum { /* The meta type specifies how we know what we know about the file. * We may know because of a header we read, some of it may have been guessed from filenames, etc. */ typedef enum { - /* DSP-specific */ + meta_DSP_STD, /* Nintendo standard GC ADPCM (DSP) header */ - meta_DSP_CSMP, /* Retro: Metroid Prime 3, Donkey Kong Country Returns */ meta_DSP_CSTR, /* Star Fox Assault "Cstr" */ meta_DSP_RS03, /* Retro: Metroid Prime 2 "RS03" */ meta_DSP_STM, /* Paper Mario 2 STM */ - meta_DSP_AGSC, /* Retro: Metroid Prime 2 title */ + meta_AGSC, /* Retro: Metroid Prime 2 title */ + meta_CSMP, /* Retro: Metroid Prime 3 (Wii), Donkey Kong Country Returns (Wii) */ + meta_RFRM, /* Retro: Donkey Kong Country Tropical Freeze (Wii U) */ meta_DSP_MPDSP, /* Monopoly Party single header stereo */ meta_DSP_JETTERS, /* Bomberman Jetters .dsp */ meta_DSP_MSS, /* Free Radical GC games */ @@ -286,14 +285,13 @@ typedef enum { meta_DSP_STR, /* Conan .str files */ meta_DSP_SADB, /* .sad */ meta_DSP_WSI, /* .wsi */ - meta_DSP_WII_IDSP, /* .gcm with IDSP header */ + meta_IDSP_TT, /* Traveller's Tales games */ meta_DSP_WII_MUS, /* .mus */ meta_DSP_WII_WSD, /* Phantom Brave (WII) */ meta_WII_NDP, /* Vertigo (Wii) */ meta_DSP_YGO, /* Konami: Yu-Gi-Oh! The Falsebound Kingdom (NGC), Hikaru no Go 3 (NGC) */ meta_DSP_SADF, /* Procyon Studio SADF - Xenoblade Chronicles 2 (Switch) */ - /* Nintendo */ meta_STRM, /* Nintendo STRM */ meta_RSTM, /* Nintendo RSTM (Revolution Stream, similar to STRM) */ meta_AFC, /* AFC */ @@ -335,19 +333,17 @@ typedef enum { meta_PS2_RAW, /* RAW Interleaved Format */ meta_PS2_EXST, /* Shadow of Colossus EXST */ meta_PS2_SVAG, /* Konami SVAG */ - meta_PS2_MIB, /* MIB File */ + meta_PS_HEADERLESS, /* headerless PS-ADPCM */ meta_PS2_MIB_MIH, /* MIB File + MIH Header*/ meta_PS2_MIC, /* KOEI MIC File */ meta_PS2_VAGi, /* VAGi Interleaved File */ meta_PS2_VAGp, /* VAGp Mono File */ - meta_PS2_VAGm, /* VAGp Mono File */ meta_PS2_pGAV, /* VAGp with Little Endian Header */ meta_PSX_GMS, /* GMS File (used in PS1 & PS2) [no header_id] */ - meta_PS2_STR, /* Pacman STR+STH files */ + meta_STR_WAV, /* Blitz Games STR+WAV files */ meta_PS2_ILD, /* ILD File */ meta_PS2_PNB, /* PsychoNauts Bgm File */ - meta_PS2_VAGs, /* VAG Stereo from Kingdom Hearts */ - meta_PS2_VPK, /* VPK Audio File */ + meta_VPK, /* VPK Audio File */ meta_PS2_BMDX, /* Beatmania thing */ meta_PS2_IVB, /* Langrisser 3 IVB */ meta_PS2_SND, /* some Might & Magics SSND header */ @@ -398,7 +394,9 @@ typedef enum { meta_KRAW, /* Geometry Wars - Galaxies */ meta_PS2_OMU, /* PS2 Int file with Header */ meta_PS2_XA2, /* XG3 Extreme-G Racing */ - meta_IDSP, /* Chronicles of Narnia, Soul Calibur Legends, Mario Strikers Charged */ + meta_NUB_IDSP, /* Soul Calibur Legends (Wii) */ + meta_IDSP_NL, /* Mario Strikers Charged (Wii) */ + meta_IDSP_IE, /* Defencer (GC) */ meta_SPT_SPD, /* Various (SPT+SPT DSP) */ meta_ISH_ISD, /* Various (ISH+ISD DSP) */ meta_GSP_GSB, /* Tecmo games (Super Swing Golf 1 & 2, Quamtum Theory) */ @@ -507,7 +505,6 @@ typedef enum { meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */ meta_NGC_DSP_IADP, /* Gamecube Interleave DSP */ meta_PS2_TK5, /* Tekken 5 Stream Files */ - meta_WII_STR, /* House of The Dead Overkill STR+STH */ meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */ meta_ZSD, /* Dragon Booster ZSD */ meta_RedSpark, /* "RedSpark" RSD (MadWorld) */ @@ -547,7 +544,6 @@ typedef enum { meta_NGC_DSP_MPDS, /* Big Air Freestyle, Terminator 3 */ meta_DSP_STR_IG, /* Micro Machines, Superman Superman: Shadow of Apokolis */ meta_EA_SWVR, /* Future Cop L.A.P.D., Freekstyle */ - meta_NGC_DSP_STH_STR, /* SpongeBob Squarepants (NGC), Taz Wanted (NGC), Cubix (NGC), Tak (WII)*/ meta_PS2_B1S, /* 7 Wonders of the ancient world */ meta_PS2_WAD, /* The golden Compass */ meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */ @@ -587,7 +583,6 @@ typedef enum { meta_OTNS_ADP, /* Omikron: The Nomad Soul .adp (PC/DC) */ meta_EB_SFX, /* Excitebots .sfx */ meta_EB_SF0, /* Excitebots .sf0 */ - meta_PS3_KLBS, /* L@VE ONCE (PS3) */ meta_PS2_MTAF, /* Metal Gear Solid 3 MTAF */ meta_PS2_VAG1, /* Metal Gear Solid 3 VAG1 */ meta_PS2_VAG2, /* Metal Gear Solid 3 VAG2 */ @@ -602,7 +597,7 @@ typedef enum { meta_OTM, /* Otomedius (Arcade) */ meta_CSTM, /* Nintendo 3DS CSTM (Century Stream) */ meta_FSTM, /* Nintendo Wii U FSTM (caFe? Stream) */ - meta_3DS_IDSP, /* Nintendo 3DS/Wii U IDSP */ + meta_IDSP_NUS3, /* Namco 3DS/Wii U IDSP */ meta_KT_WIIBGM, /* Koei Tecmo WiiBGM */ meta_KTSS, /* Koei Tecmo Nintendo Stream (KNS) */ meta_MCA, /* Capcom MCA "MADP" */ @@ -610,6 +605,7 @@ typedef enum { meta_HCA, /* CRI HCA */ meta_PS2_SVAG_SNK, /* SNK PS2 SVAG */ meta_PS2_VDS_VDM, /* Graffiti Kingdom */ + meta_FFMPEG, /* any file supported by FFmpeg */ meta_X360_CXS, /* Eternal Sonata (Xbox 360) */ meta_AKB, /* SQEX iOS */ meta_NUB_XMA, /* Namco XMA from NUB archives */ @@ -689,10 +685,35 @@ typedef enum { meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */ meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */ meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */ + meta_XMD, /* Konami XMD [Silent Hill 4 (Xbox), Castlevania: Curse of Darkness (Xbox)] */ + meta_CKS, /* Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */ + meta_CKB, /* Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */ + meta_WV6, /* Gorilla Systems PC games */ + meta_WAVEBATCH, /* Firebrand Games */ + meta_HD3_BD3, /* Sony PS3 bank */ + meta_BNK_SONY, /* Sony Scream Tool bank */ + meta_SCD_SSCF, /* Square Enix SCD old version */ + meta_DSP_VAG, /* Penny-Punching Princess (Switch) sfx */ + meta_DSP_ITL, /* Charinko Hero (GC) */ + meta_A2M, /* Scooby-Doo! Unmasked (PS2) */ + meta_AHV, /* Headhunter (PS2) */ + meta_MSV, /* Fight Club (PS2) */ + meta_SDF_PS2, /* Agent Hugo - Lemoon Twist (PS2) */ + meta_SVG, /* Hunter - The Reckoning - Wayward (PS2) */ + meta_VIS, /* AirForce Delta Strike (PS2) */ + meta_VAI, /* Ratatouille (GC) */ + meta_SDF_3DS, /* Gummy Bears Mini Golf (3DS) */ + meta_AIF_ASOBO, /* Ratatouille (PC) */ + meta_AO, /* Cloudphobia (PC) */ + meta_APC, /* MegaRace 3 (PC) */ + meta_WV2, /* Slave Zero (PC) */ + meta_XAU_KONAMI, /* Yu-Gi-Oh - The Dawn of Destiny (Xbox) */ + meta_DERF, /* Stupid Invaders (PC) */ + meta_UTK, + meta_NXA, + meta_ADPCM_CAPCOM, + meta_UE4OPUS, -#ifdef VGM_USE_FFMPEG - meta_FFmpeg, -#endif } meta_t; @@ -747,28 +768,42 @@ typedef struct { /* main vgmstream info */ typedef struct { /* basics */ - int32_t num_samples; /* the actual number of samples in this stream */ - int32_t sample_rate; /* sample rate in Hz */ - int channels; /* number of channels */ - coding_t coding_type; /* type of encoding */ - layout_t layout_type; /* type of layout for data */ - meta_t meta_type; /* how we know the metadata */ - - /* subsongs */ - int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ - 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 */ + int32_t num_samples; /* the actual max number of samples */ + int32_t sample_rate; /* sample rate in Hz */ + int channels; /* number of channels */ + coding_t coding_type; /* type of encoding */ + layout_t layout_type; /* type of layout */ + meta_t meta_type; /* type of metadata */ /* looping */ - int loop_flag; /* is this stream looped? */ - int32_t loop_start_sample; /* first sample of the loop (included in the loop) */ - int32_t loop_end_sample; /* last sample of the loop (not included in the loop) */ + int loop_flag; /* is this stream looped? */ + int32_t loop_start_sample; /* first sample of the loop (included in the loop) */ + int32_t loop_end_sample; /* last sample of the loop (not included in the loop) */ /* layouts/block */ - size_t interleave_block_size; /* interleave, or block/frame size (depending on the codec) */ - size_t interleave_last_block_size; /* smaller interleave for last block */ + size_t interleave_block_size; /* interleave, or block/frame size (depending on the codec) */ + size_t interleave_last_block_size; /* smaller interleave for last block */ + + /* subsongs */ + int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ + int stream_index; /* selected subsong (also 1-based) */ + size_t stream_size; /* info to properly calculate bitrate in case of subsongs */ + char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */ + + /* config */ + int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */ + uint32_t channel_mask; /* to silence crossfading subsongs/layers */ + int channel_mappings_on; /* channel mappings are active */ + int channel_mappings[32]; /* swap channel "i" with "[i]" */ + /* config requests, players must read and honor these values */ + /* (ideally internally would work as a player, but for now player must do it manually) */ + double config_loop_count; + double config_fade_time; + double config_fade_delay; + int config_ignore_loop; + int config_force_loop; + int config_ignore_fade; + /* channel state */ VGMSTREAMCHANNEL * ch; /* pointer to array of channels */ @@ -777,8 +812,8 @@ typedef struct { /* layout/block state */ size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */ - int32_t current_sample; /* number of samples we've passed */ - int32_t samples_into_block; /* number of samples into the current block */ + int32_t current_sample; /* number of samples we've passed (for loop detection) */ + int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */ off_t current_block_offset; /* start of this block (offset of block header) */ size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */ size_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ @@ -793,22 +828,16 @@ typedef struct { /* loop state */ int hit_loop; /* have we seen the loop yet? */ - /* counters for "loop + play end of the stream instead of fading" (not used/needed otherwise) */ - int loop_count; /* number of complete loops (1=looped once) */ - int loop_target; /* max loops before continuing with the stream end */ + int loop_count; /* counter of complete loops (1=looped once) */ + int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */ /* decoder specific */ int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */ - int codec_version; /* flag for codecs with minor variations */ - - uint8_t xa_channel; /* XA ADPCM: selected channel */ - int32_t xa_sector_length; /* XA ADPCM: XA block */ - uint8_t xa_headerless; /* XA ADPCM: headerless XA */ - int8_t xa_get_high_nibble; /* XA ADPCM: mono/stereo nibble selection (XA state could be simplified) */ + int codec_config; /* flags for codecs or layouts with minor variations; meaning is up to the codec */ int32_t ws_output_size; /* WS ADPCM: output bytes for this block */ - void * start_vgmstream; /* a copy of the VGMSTREAM as it was at the beginning of the stream (for AAX/AIX/SCD) */ + void * start_vgmstream; /* a copy of the VGMSTREAM as it was at the beginning of the stream (for custom layouts) */ /* Data the codec needs for the whole stream. This is for codecs too * different from vgmstream's structure to be reasonably shoehorned into @@ -816,9 +845,7 @@ 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). */ + /* Same, for special layouts. layout_data + codec_data may exist at the same time. */ void * layout_data; } VGMSTREAM; @@ -853,7 +880,6 @@ typedef enum { VORBIS_OGL, /* Shin'en OGL: custom packet headers */ VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */ VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */ - //VORBIS_LYN /* Ubisoft LyN: two interleaved Ogg (including setup, duplicated) */ } vorbis_custom_t; /* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ @@ -1011,7 +1037,8 @@ typedef struct { #ifdef VGM_USE_G719 typedef struct { sample buffer[960]; - g719_handle *handle; + void *handle; + int16_t *code_buffer; } g719_codec_data; #endif @@ -1025,46 +1052,25 @@ typedef struct { #endif #ifdef VGM_USE_ATRAC9 - -/* custom ATRAC9 modes */ -typedef enum { - ATRAC9_DEFAULT = 0, /* ATRAC9 standard */ - ATRAC9_XVAG, /* Sony XVAG: interleaved subsongs, Vita multichannel interleaves 2ch xN superframes */ - ATRAC9_KMA9, /* Koei Tecmo KMA9: interleaved subsongs */ - //ATRAC9_FSB, /* FMOD FSB: Vita multichannel interleaves 2ch xN superframes */ - //ATRAC9_EATRAX, /* EA EATrax: buffered ATRAC9 in SPS blocks (superframes can be split between blocks) */ -} atrac9_custom_t; - +/* ATRAC9 config */ typedef struct { - atrac9_custom_t type; - - int channels; /* to detect weird multichannel */ - uint32_t config_data; /* ATRAC9 config header */ - int encoder_delay; /* initial samples to discard */ - - size_t interleave_skip; /* XVAG */ - size_t subsong_skip; /* XVAG */ + int channels; /* to detect weird multichannel */ + uint32_t config_data; /* ATRAC9 config header */ + int encoder_delay; /* initial samples to discard */ } atrac9_config; +typedef struct atrac9_codec_data atrac9_codec_data; +#endif -typedef struct { - uint8_t *data_buffer; - size_t data_buffer_size; - - sample *sample_buffer; - size_t samples_filled; /* number of samples in the buffer */ - size_t samples_used; /* number of samples extracted from the buffer */ - - int samples_to_discard; - - atrac9_config config; - - void *handle; /* decoder handle */ -} atrac9_codec_data; +#ifdef VGM_USE_CELT +typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t; +typedef struct celt_codec_data celt_codec_data; #endif /* libacm interface */ typedef struct { - ACMStream *file; + STREAMFILE *streamfile; + void *handle; + void *io_config; } acm_codec_data; #define AIX_BUFFER_SIZE 0x1000 @@ -1086,7 +1092,6 @@ typedef struct { int segment_count; VGMSTREAM **segments; int current_segment; - int loop_segment; } segmented_layout_data; /* for files made of "horizontal" layers, one per group of channels (using a complete sub-VGMSTREAM) */ @@ -1103,57 +1108,34 @@ typedef struct { typedef struct { STREAMFILE *streamfile; - uint64_t start; - //uint64_t size; clHCA_stInfo info; - unsigned int curblock; - unsigned int sample_ptr; - unsigned int samples_discard; - signed short sample_buffer[clHCA_samplesPerBlock * 16]; - //clHCA * hca exists here (pre-alloc'ed) + + signed short *sample_buffer; + size_t samples_filled; + size_t samples_consumed; + size_t samples_to_discard; + + void* data_buffer; + + unsigned int current_block; + + void* handle; } hca_codec_data; #ifdef VGM_USE_FFMPEG -/* Custom FFMPEG modes */ -typedef enum { - FFMPEG_STANDARD, /* default FFmpeg */ - FFMPEG_SWITCH_OPUS, /* Opus without Ogg layer */ - FFMPEG_EA_XMA, /* XMA with padding removed and custom streams in SNS blocks */ - //FFMPEG_EA_SCHL, /* Normal header+data (ex. ATRAC3) in SCxx blocks */ - //FFMPEG_SFH, /* ATRAC3plus header+data in SFH blocks */ - //FFMPEG_AWC_XMA, /* XMA data in AWC blocks, 1 streams per channel */ -} ffmpeg_custom_t; - -/* config for the above modes */ -typedef struct { - int stream_index; /* FFmpeg's sub-stream (as opposed to an internal stream in custom read/seeks) */ - int codec_endian; - int channels; - - ffmpeg_custom_t type; /* ffmpeg subtype */ - size_t virtual_size; /* external value, if meta needs to know/supply it */ - - /* internal sequences, when needed */ - int sequence; - int samples_done; -} ffmpeg_custom_config; - typedef struct { /*** IO internals ***/ STREAMFILE *streamfile; - uint64_t real_start; // absolute start within the streamfile - uint64_t real_offset; // absolute offset within the streamfile - uint64_t real_size; // max size within the streamfile - uint64_t virtual_offset; // computed offset FFmpeg sees (including fake header) - uint64_t virtual_size; // computed size FFmpeg sees (including fake header) - uint64_t virtual_base; // info/base virtual_offset equivalent to current real_offset, block aligned (*not* including fake header) + uint64_t start; // absolute start within the streamfile + uint64_t offset; // absolute offset within the streamfile + uint64_t size; // max size within the streamfile + uint64_t logical_offset; // computed offset FFmpeg sees (including fake header) + uint64_t logical_size; // computed size FFmpeg sees (including fake header) uint64_t header_size; // fake header (parseable by FFmpeg) prepended on reads uint8_t *header_insert_block; // fake header data (ie. RIFF) - ffmpeg_custom_config config; /* custom config/state */ - /*** "public" API (read-only) ***/ // stream info int channels; @@ -1220,11 +1202,7 @@ typedef struct { #endif #endif -typedef struct { - int pcm_blocks; - int utk_context_size; - void** utk_context; -} ea_mt_codec_data; +typedef struct ea_mt_codec_data ea_mt_codec_data; #if 0 @@ -1246,15 +1224,15 @@ typedef struct { //info for opaque VGMSTREAM typedef struct { - int channels; - int sample_rate; - int num_samples; - int loop_start_sample; - int loop_end_sample; - int loop_flag; - int num_streams; - int current_sample; - int average_bitrate; + const int channels; + const int sample_rate; + const int num_samples; + const int loop_start_sample; + const int loop_end_sample; + const int loop_flag; + const int num_streams; + const int current_sample; + const int average_bitrate; } VGMSTREAM_INFO; void vgmstream_get_info(VGMSTREAM* vgmstream, VGMSTREAM_INFO *vgmstream_info); @@ -1295,13 +1273,17 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length); /* Return the average bitrate in bps of all unique files contained within this stream. */ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream); -/* List supported formats and return elements in the list, for plugins that need to know. */ +/* List supported formats and return elements in the list, for plugins that need to know. + * The list disables some common formats that may conflict (.wav, .ogg, etc). */ const char ** vgmstream_get_formats(size_t * size); /* Force enable/disable internal looping. Should be done before playing anything, * and not all codecs support arbitrary loop values ATM. */ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sample, int loop_end_sample); +/* Set number of max loops to do, then play up to stream end (for songs with proper endings) */ +void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target); + /* -------------------------------------------------------------------------*/ /* vgmstream "private" API */ /* -------------------------------------------------------------------------*/