Updated VGMStream to r1050-1726-g5b99d40c.

This commit is contained in:
Christopher Snowhill 2018-09-26 19:40:44 -07:00
parent 46e5b89a8b
commit 2a5221da25
234 changed files with 16718 additions and 10938 deletions

View file

@ -94,7 +94,6 @@
83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.c */; }; 83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.c */; };
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; }; 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, ); }; }; 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 */; }; 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 */; }; 83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */; };
83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.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 */; }; 8349A91F1FE6258200E26435 /* naac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9061FE6258100E26435 /* naac.c */; };
834D3A6E19F47C98001C54F6 /* g1l.c in Sources */ = {isa = PBXBuildFile; fileRef = 834D3A6D19F47C98001C54F6 /* g1l.c */; }; 834D3A6E19F47C98001C54F6 /* g1l.c in Sources */ = {isa = PBXBuildFile; fileRef = 834D3A6D19F47C98001C54F6 /* g1l.c */; };
834D795520E4F0D400C4A5CC /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83F4128F1E932F9A002E37D0 /* Vorbis.framework */; }; 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 */; }; 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 */; }; 835027131ED119E000C25929 /* mta2_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 835027121ED119E000C25929 /* mta2_decoder.c */; };
8350C0551E071881009E0A93 /* xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0541E071881009E0A93 /* xma.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 */; }; 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 */; }; 836F6B4718BDB8880095E648 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 836F6B4518BDB8880095E648 /* InfoPlist.strings */; };
836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE018BDC2180095E648 /* acm_decoder.c */; }; 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 */; }; 836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE218BDC2180095E648 /* adx_decoder.c */; };
836F6F2318BDC2190095E648 /* coding.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6DE518BDC2180095E648 /* coding.h */; }; 836F6F2318BDC2190095E648 /* coding.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6DE518BDC2180095E648 /* coding.h */; };
836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE718BDC2180095E648 /* g721_decoder.c */; }; 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 */; }; 836F6F4118BDC2190095E648 /* blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0418BDC2180095E648 /* blocked.c */; };
836F6F4A18BDC2190095E648 /* interleave.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0D18BDC2180095E648 /* interleave.c */; }; 836F6F4A18BDC2190095E648 /* interleave.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E0D18BDC2180095E648 /* interleave.c */; };
836F6F4D18BDC2190095E648 /* layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6E1018BDC2180095E648 /* layout.h */; }; 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 */; }; 836F6F6518BDC2190095E648 /* 2dx9.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2918BDC2180095E648 /* 2dx9.c */; };
836F6F6618BDC2190095E648 /* aax.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2A18BDC2180095E648 /* aax.c */; }; 836F6F6618BDC2190095E648 /* aax.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2A18BDC2180095E648 /* aax.c */; };
836F6F6718BDC2190095E648 /* acm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E2B18BDC2180095E648 /* acm.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 */; }; 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 */; }; 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 */; }; 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 */; }; 836F6F8218BDC2190095E648 /* ea_schl.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4618BDC2180095E648 /* ea_schl.c */; };
836F6F8418BDC2190095E648 /* emff.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E4818BDC2180095E648 /* emff.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 */; }; 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 */; }; 836F6F8D18BDC2190095E648 /* gsp_gsb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5118BDC2180095E648 /* gsp_gsb.c */; };
836F6F8E18BDC2190095E648 /* halpst.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5218BDC2180095E648 /* halpst.c */; }; 836F6F8E18BDC2190095E648 /* halpst.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5218BDC2180095E648 /* halpst.c */; };
836F6F8F18BDC2190095E648 /* his.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5318BDC2180095E648 /* his.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 */; }; 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 */; }; 836F6F9218BDC2190095E648 /* ish_isd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5618BDC2180095E648 /* ish_isd.c */; };
836F6F9318BDC2190095E648 /* ivaud.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5718BDC2180095E648 /* ivaud.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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 836F704218BDC2190095E648 /* wii_sts.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0618BDC2190095E648 /* wii_sts.c */; };
836F704318BDC2190095E648 /* wpd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F0718BDC2190095E648 /* wpd.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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 838BDB641D3AF08C0022CA6F /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB611D3AF08C0022CA6F /* libavcodec.a */; };
838BDB651D3AF08C0022CA6F /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB621D3AF08C0022CA6F /* libavformat.a */; }; 838BDB651D3AF08C0022CA6F /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB621D3AF08C0022CA6F /* libavformat.a */; };
838BDB661D3AF08C0022CA6F /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB631D3AF08C0022CA6F /* libavutil.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 = "<group>"; }; 832389511D224C0800482226 /* hca_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca_decoder.c; sourceTree = "<group>"; };
83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = "<group>"; }; 83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = "<group>"; };
83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = "<group>"; }; 83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = "<group>"; };
83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils_switch_opus.c; sourceTree = "<group>"; };
83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_xma.c; sourceTree = "<group>"; }; 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_xma.c; sourceTree = "<group>"; };
83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = "<group>"; }; 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = "<group>"; };
83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = "<group>"; }; 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = "<group>"; };
@ -718,6 +755,58 @@
8349A9051FE6258100E26435 /* bar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bar.c; sourceTree = "<group>"; }; 8349A9051FE6258100E26435 /* bar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bar.c; sourceTree = "<group>"; };
8349A9061FE6258100E26435 /* naac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = naac.c; sourceTree = "<group>"; }; 8349A9061FE6258100E26435 /* naac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = naac.c; sourceTree = "<group>"; };
834D3A6D19F47C98001C54F6 /* g1l.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g1l.c; sourceTree = "<group>"; }; 834D3A6D19F47C98001C54F6 /* g1l.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g1l.c; sourceTree = "<group>"; };
834FE0AA215C798A000A5D3D /* acm_decoder_util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm_decoder_util.c; sourceTree = "<group>"; };
834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_custom_opus.c; sourceTree = "<group>"; };
834FE0AC215C798B000A5D3D /* acm_decoder_libacm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acm_decoder_libacm.h; sourceTree = "<group>"; };
834FE0AD215C798B000A5D3D /* derf_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = derf_decoder.c; sourceTree = "<group>"; };
834FE0AE215C798B000A5D3D /* atrac9_decoder.c.orig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = atrac9_decoder.c.orig; sourceTree = "<group>"; };
834FE0AF215C798C000A5D3D /* acm_decoder_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm_decoder_decode.c; sourceTree = "<group>"; };
834FE0B0215C798C000A5D3D /* xmd_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmd_decoder.c; sourceTree = "<group>"; };
834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_mt_decoder_utk.h; sourceTree = "<group>"; };
834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = celt_fsb_decoder.c; sourceTree = "<group>"; };
834FE0BC215C79A8000A5D3D /* blocked_xa_aiff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xa_aiff.c; sourceTree = "<group>"; };
834FE0BD215C79A9000A5D3D /* flat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = flat.c; sourceTree = "<group>"; };
834FE0C0215C79E5000A5D3D /* ao.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ao.c; sourceTree = "<group>"; };
834FE0C1215C79E5000A5D3D /* aif_asobo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aif_asobo.c; sourceTree = "<group>"; };
834FE0C2215C79E6000A5D3D /* str_wav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = str_wav.c; sourceTree = "<group>"; };
834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kma9_streamfile.h; sourceTree = "<group>"; };
834FE0C4215C79E6000A5D3D /* fsb_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_interleave_streamfile.h; sourceTree = "<group>"; };
834FE0C5215C79E6000A5D3D /* ue4opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ue4opus.c; sourceTree = "<group>"; };
834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xvag_streamfile.h; sourceTree = "<group>"; };
834FE0C7215C79E7000A5D3D /* apc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apc.c; sourceTree = "<group>"; };
834FE0C8215C79E7000A5D3D /* a2m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = a2m.c; sourceTree = "<group>"; };
834FE0C9215C79E7000A5D3D /* wv6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wv6.c; sourceTree = "<group>"; };
834FE0CA215C79E7000A5D3D /* bnk_sony.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bnk_sony.c; sourceTree = "<group>"; };
834FE0CB215C79E8000A5D3D /* wsi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wsi.c; sourceTree = "<group>"; };
834FE0CC215C79E8000A5D3D /* wv2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wv2.c; sourceTree = "<group>"; };
834FE0CD215C79E8000A5D3D /* derf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = derf.c; sourceTree = "<group>"; };
834FE0CE215C79E8000A5D3D /* vis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vis.c; sourceTree = "<group>"; };
834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adpcm_capcom.c; sourceTree = "<group>"; };
834FE0D0215C79E8000A5D3D /* wavebatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wavebatch.c; sourceTree = "<group>"; };
834FE0D1215C79E9000A5D3D /* nus3bank.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3bank.c; sourceTree = "<group>"; };
834FE0D2215C79E9000A5D3D /* xau_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau_konami.c; sourceTree = "<group>"; };
834FE0D3215C79E9000A5D3D /* vai.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vai.c; sourceTree = "<group>"; };
834FE0D4215C79E9000A5D3D /* vpk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vpk.c; sourceTree = "<group>"; };
834FE0D5215C79E9000A5D3D /* ps_headerless.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps_headerless.c; sourceTree = "<group>"; };
834FE0D6215C79E9000A5D3D /* sqex_scd_sscf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqex_scd_sscf.c; sourceTree = "<group>"; };
834FE0D7215C79EA000A5D3D /* svg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svg.c; sourceTree = "<group>"; };
834FE0D8215C79EA000A5D3D /* csmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = csmp.c; sourceTree = "<group>"; };
834FE0D9215C79EA000A5D3D /* rfrm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rfrm.c; sourceTree = "<group>"; };
834FE0DA215C79EA000A5D3D /* ea_schl_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_schl_streamfile.h; sourceTree = "<group>"; };
834FE0DB215C79EA000A5D3D /* nxa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nxa.c; sourceTree = "<group>"; };
834FE0DC215C79EA000A5D3D /* xmd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmd.c; sourceTree = "<group>"; };
834FE0DD215C79EB000A5D3D /* utk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utk.c; sourceTree = "<group>"; };
834FE0DE215C79EB000A5D3D /* mib_mih.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mib_mih.c; sourceTree = "<group>"; };
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hd3_bd3.c; sourceTree = "<group>"; };
834FE0E0215C79EB000A5D3D /* idsp_ie.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idsp_ie.c; sourceTree = "<group>"; };
834FE0E1215C79EB000A5D3D /* nub_idsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub_idsp.c; sourceTree = "<group>"; };
834FE0E2215C79EB000A5D3D /* ps2_psh_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ps2_psh_streamfile.h; sourceTree = "<group>"; };
834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb5_interleave_streamfile.h; sourceTree = "<group>"; };
834FE0E4215C79EC000A5D3D /* vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vag.c; sourceTree = "<group>"; };
834FE0E5215C79EC000A5D3D /* ahv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ahv.c; sourceTree = "<group>"; };
834FE0E6215C79EC000A5D3D /* sdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sdf.c; sourceTree = "<group>"; };
834FE0E7215C79EC000A5D3D /* msv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msv.c; sourceTree = "<group>"; };
834FE0E8215C79EC000A5D3D /* ck.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ck.c; sourceTree = "<group>"; };
8350270C1ED119D200C25929 /* ps3_mta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_mta2.c; sourceTree = "<group>"; }; 8350270C1ED119D200C25929 /* ps3_mta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_mta2.c; sourceTree = "<group>"; };
835027121ED119E000C25929 /* mta2_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mta2_decoder.c; sourceTree = "<group>"; }; 835027121ED119E000C25929 /* mta2_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mta2_decoder.c; sourceTree = "<group>"; };
8350C0541E071881009E0A93 /* xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma.c; sourceTree = "<group>"; }; 8350C0541E071881009E0A93 /* xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma.c; sourceTree = "<group>"; };
@ -726,7 +815,6 @@
836F6B4418BDB8880095E648 /* libvgmstream-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "libvgmstream-Info.plist"; sourceTree = "<group>"; }; 836F6B4418BDB8880095E648 /* libvgmstream-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "libvgmstream-Info.plist"; sourceTree = "<group>"; };
836F6B4618BDB8880095E648 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 836F6B4618BDB8880095E648 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
836F6DE018BDC2180095E648 /* acm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm_decoder.c; sourceTree = "<group>"; }; 836F6DE018BDC2180095E648 /* acm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm_decoder.c; sourceTree = "<group>"; };
836F6DE118BDC2180095E648 /* acm_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acm_decoder.h; sourceTree = "<group>"; };
836F6DE218BDC2180095E648 /* adx_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adx_decoder.c; sourceTree = "<group>"; }; 836F6DE218BDC2180095E648 /* adx_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adx_decoder.c; sourceTree = "<group>"; };
836F6DE518BDC2180095E648 /* coding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding.h; sourceTree = "<group>"; }; 836F6DE518BDC2180095E648 /* coding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding.h; sourceTree = "<group>"; };
836F6DE718BDC2180095E648 /* g721_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g721_decoder.c; sourceTree = "<group>"; }; 836F6DE718BDC2180095E648 /* g721_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g721_decoder.c; sourceTree = "<group>"; };
@ -756,7 +844,6 @@
836F6E0418BDC2180095E648 /* blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked.c; sourceTree = "<group>"; }; 836F6E0418BDC2180095E648 /* blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked.c; sourceTree = "<group>"; };
836F6E0D18BDC2180095E648 /* interleave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interleave.c; sourceTree = "<group>"; }; 836F6E0D18BDC2180095E648 /* interleave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interleave.c; sourceTree = "<group>"; };
836F6E1018BDC2180095E648 /* layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = layout.h; sourceTree = "<group>"; }; 836F6E1018BDC2180095E648 /* layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = layout.h; sourceTree = "<group>"; };
836F6E1418BDC2180095E648 /* nolayout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nolayout.c; sourceTree = "<group>"; };
836F6E2918BDC2180095E648 /* 2dx9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2dx9.c; sourceTree = "<group>"; }; 836F6E2918BDC2180095E648 /* 2dx9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2dx9.c; sourceTree = "<group>"; };
836F6E2A18BDC2180095E648 /* aax.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aax.c; sourceTree = "<group>"; }; 836F6E2A18BDC2180095E648 /* aax.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aax.c; sourceTree = "<group>"; };
836F6E2B18BDC2180095E648 /* acm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm.c; sourceTree = "<group>"; }; 836F6E2B18BDC2180095E648 /* acm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acm.c; sourceTree = "<group>"; };
@ -782,7 +869,6 @@
836F6E4118BDC2180095E648 /* dc_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dc_str.c; sourceTree = "<group>"; }; 836F6E4118BDC2180095E648 /* dc_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dc_str.c; sourceTree = "<group>"; };
836F6E4318BDC2180095E648 /* dmsg_segh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dmsg_segh.c; sourceTree = "<group>"; }; 836F6E4318BDC2180095E648 /* dmsg_segh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dmsg_segh.c; sourceTree = "<group>"; };
836F6E4418BDC2180095E648 /* dsp_bdsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_bdsp.c; sourceTree = "<group>"; }; 836F6E4418BDC2180095E648 /* dsp_bdsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_bdsp.c; sourceTree = "<group>"; };
836F6E4518BDC2180095E648 /* dsp_sth_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_sth_str.c; sourceTree = "<group>"; };
836F6E4618BDC2180095E648 /* ea_schl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl.c; sourceTree = "<group>"; }; 836F6E4618BDC2180095E648 /* ea_schl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl.c; sourceTree = "<group>"; };
836F6E4818BDC2180095E648 /* emff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emff.c; sourceTree = "<group>"; }; 836F6E4818BDC2180095E648 /* emff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emff.c; sourceTree = "<group>"; };
836F6E4918BDC2180095E648 /* exakt_sc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exakt_sc.c; sourceTree = "<group>"; }; 836F6E4918BDC2180095E648 /* exakt_sc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exakt_sc.c; sourceTree = "<group>"; };
@ -795,7 +881,6 @@
836F6E5118BDC2180095E648 /* gsp_gsb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gsp_gsb.c; sourceTree = "<group>"; }; 836F6E5118BDC2180095E648 /* gsp_gsb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gsp_gsb.c; sourceTree = "<group>"; };
836F6E5218BDC2180095E648 /* halpst.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = halpst.c; sourceTree = "<group>"; }; 836F6E5218BDC2180095E648 /* halpst.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = halpst.c; sourceTree = "<group>"; };
836F6E5318BDC2180095E648 /* his.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = his.c; sourceTree = "<group>"; }; 836F6E5318BDC2180095E648 /* his.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = his.c; sourceTree = "<group>"; };
836F6E5418BDC2180095E648 /* idsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idsp.c; sourceTree = "<group>"; };
836F6E5518BDC2180095E648 /* ios_psnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ios_psnd.c; sourceTree = "<group>"; }; 836F6E5518BDC2180095E648 /* ios_psnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ios_psnd.c; sourceTree = "<group>"; };
836F6E5618BDC2180095E648 /* ish_isd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ish_isd.c; sourceTree = "<group>"; }; 836F6E5618BDC2180095E648 /* ish_isd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ish_isd.c; sourceTree = "<group>"; };
836F6E5718BDC2180095E648 /* ivaud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ivaud.c; sourceTree = "<group>"; }; 836F6E5718BDC2180095E648 /* ivaud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ivaud.c; sourceTree = "<group>"; };
@ -872,7 +957,6 @@
836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = "<group>"; }; 836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = "<group>"; };
836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = "<group>"; }; 836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = "<group>"; };
836F6EAA18BDC2180095E648 /* ps2_mcg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mcg.c; sourceTree = "<group>"; }; 836F6EAA18BDC2180095E648 /* ps2_mcg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mcg.c; sourceTree = "<group>"; };
836F6EAB18BDC2180095E648 /* ps2_mib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mib.c; sourceTree = "<group>"; };
836F6EAC18BDC2180095E648 /* ps2_mic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mic.c; sourceTree = "<group>"; }; 836F6EAC18BDC2180095E648 /* ps2_mic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mic.c; sourceTree = "<group>"; };
836F6EAD18BDC2180095E648 /* ps2_mihb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mihb.c; sourceTree = "<group>"; }; 836F6EAD18BDC2180095E648 /* ps2_mihb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mihb.c; sourceTree = "<group>"; };
836F6EAE18BDC2180095E648 /* ps2_msa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_msa.c; sourceTree = "<group>"; }; 836F6EAE18BDC2180095E648 /* ps2_msa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_msa.c; sourceTree = "<group>"; };
@ -891,19 +975,16 @@
836F6EBE18BDC2190095E648 /* ps2_spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_spm.c; sourceTree = "<group>"; }; 836F6EBE18BDC2190095E648 /* ps2_spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_spm.c; sourceTree = "<group>"; };
836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = "<group>"; }; 836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = "<group>"; };
836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = "<group>"; }; 836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = "<group>"; };
836F6EC218BDC2190095E648 /* ps2_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_str.c; sourceTree = "<group>"; };
836F6EC318BDC2190095E648 /* ps2_strlr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_strlr.c; sourceTree = "<group>"; }; 836F6EC318BDC2190095E648 /* ps2_strlr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_strlr.c; sourceTree = "<group>"; };
836F6EC418BDC2190095E648 /* ps2_svag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag.c; sourceTree = "<group>"; }; 836F6EC418BDC2190095E648 /* ps2_svag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag.c; sourceTree = "<group>"; };
836F6EC518BDC2190095E648 /* ps2_tec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tec.c; sourceTree = "<group>"; }; 836F6EC518BDC2190095E648 /* ps2_tec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tec.c; sourceTree = "<group>"; };
836F6EC618BDC2190095E648 /* ps2_tk5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tk5.c; sourceTree = "<group>"; }; 836F6EC618BDC2190095E648 /* ps2_tk5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tk5.c; sourceTree = "<group>"; };
836F6EC718BDC2190095E648 /* ps2_vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vag.c; sourceTree = "<group>"; };
836F6EC818BDC2190095E648 /* ps2_vas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vas.c; sourceTree = "<group>"; }; 836F6EC818BDC2190095E648 /* ps2_vas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vas.c; sourceTree = "<group>"; };
836F6EC918BDC2190095E648 /* ps2_vbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vbk.c; sourceTree = "<group>"; }; 836F6EC918BDC2190095E648 /* ps2_vbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vbk.c; sourceTree = "<group>"; };
836F6ECA18BDC2190095E648 /* ps2_vgs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vgs.c; sourceTree = "<group>"; }; 836F6ECA18BDC2190095E648 /* ps2_vgs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vgs.c; sourceTree = "<group>"; };
836F6ECB18BDC2190095E648 /* ps2_vgv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vgv.c; sourceTree = "<group>"; }; 836F6ECB18BDC2190095E648 /* ps2_vgv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vgv.c; sourceTree = "<group>"; };
836F6ECC18BDC2190095E648 /* ps2_vms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vms.c; sourceTree = "<group>"; }; 836F6ECC18BDC2190095E648 /* ps2_vms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vms.c; sourceTree = "<group>"; };
836F6ECD18BDC2190095E648 /* ps2_voi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_voi.c; sourceTree = "<group>"; }; 836F6ECD18BDC2190095E648 /* ps2_voi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_voi.c; sourceTree = "<group>"; };
836F6ECE18BDC2190095E648 /* ps2_vpk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vpk.c; sourceTree = "<group>"; };
836F6ECF18BDC2190095E648 /* ps2_wad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wad.c; sourceTree = "<group>"; }; 836F6ECF18BDC2190095E648 /* ps2_wad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wad.c; sourceTree = "<group>"; };
836F6ED018BDC2190095E648 /* ps2_wb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wb.c; sourceTree = "<group>"; }; 836F6ED018BDC2190095E648 /* ps2_wb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wb.c; sourceTree = "<group>"; };
836F6ED118BDC2190095E648 /* ps2_wmus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wmus.c; sourceTree = "<group>"; }; 836F6ED118BDC2190095E648 /* ps2_wmus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wmus.c; sourceTree = "<group>"; };
@ -911,7 +992,6 @@
836F6ED318BDC2190095E648 /* ps2_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa30.c; sourceTree = "<group>"; }; 836F6ED318BDC2190095E648 /* ps2_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa30.c; sourceTree = "<group>"; };
836F6ED518BDC2190095E648 /* ps3_cps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_cps.c; sourceTree = "<group>"; }; 836F6ED518BDC2190095E648 /* ps3_cps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_cps.c; sourceTree = "<group>"; };
836F6ED618BDC2190095E648 /* ps3_ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_ivag.c; sourceTree = "<group>"; }; 836F6ED618BDC2190095E648 /* ps3_ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_ivag.c; sourceTree = "<group>"; };
836F6ED718BDC2190095E648 /* ps3_klbs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_klbs.c; sourceTree = "<group>"; };
836F6ED818BDC2190095E648 /* ps3_msf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_msf.c; sourceTree = "<group>"; }; 836F6ED818BDC2190095E648 /* ps3_msf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_msf.c; sourceTree = "<group>"; };
836F6ED918BDC2190095E648 /* ps3_past.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_past.c; sourceTree = "<group>"; }; 836F6ED918BDC2190095E648 /* ps3_past.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_past.c; sourceTree = "<group>"; };
836F6EDD18BDC2190095E648 /* psx_cdxa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psx_cdxa.c; sourceTree = "<group>"; }; 836F6EDD18BDC2190095E648 /* psx_cdxa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psx_cdxa.c; sourceTree = "<group>"; };
@ -952,7 +1032,6 @@
836F6F0218BDC2190095E648 /* wii_ras.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_ras.c; sourceTree = "<group>"; }; 836F6F0218BDC2190095E648 /* wii_ras.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_ras.c; sourceTree = "<group>"; };
836F6F0318BDC2190095E648 /* wii_smp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_smp.c; sourceTree = "<group>"; }; 836F6F0318BDC2190095E648 /* wii_smp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_smp.c; sourceTree = "<group>"; };
836F6F0418BDC2190095E648 /* wii_sng.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_sng.c; sourceTree = "<group>"; }; 836F6F0418BDC2190095E648 /* wii_sng.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_sng.c; sourceTree = "<group>"; };
836F6F0518BDC2190095E648 /* wii_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_str.c; sourceTree = "<group>"; };
836F6F0618BDC2190095E648 /* wii_sts.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_sts.c; sourceTree = "<group>"; }; 836F6F0618BDC2190095E648 /* wii_sts.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wii_sts.c; sourceTree = "<group>"; };
836F6F0718BDC2190095E648 /* wpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wpd.c; sourceTree = "<group>"; }; 836F6F0718BDC2190095E648 /* wpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wpd.c; sourceTree = "<group>"; };
836F6F0818BDC2190095E648 /* ws_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ws_aud.c; sourceTree = "<group>"; }; 836F6F0818BDC2190095E648 /* ws_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ws_aud.c; sourceTree = "<group>"; };
@ -982,9 +1061,6 @@
83709E031ECBC1A4005C03D3 /* ta_aac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ta_aac.c; sourceTree = "<group>"; }; 83709E031ECBC1A4005C03D3 /* ta_aac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ta_aac.c; sourceTree = "<group>"; };
83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mc3_decoder.c; sourceTree = "<group>"; }; 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mc3_decoder.c; sourceTree = "<group>"; };
83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psv_decoder.c; sourceTree = "<group>"; }; 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psv_decoder.c; sourceTree = "<group>"; };
8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils_ea_xma.c; sourceTree = "<group>"; };
8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ffmpeg_decoder_utils.h; sourceTree = "<group>"; };
8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils.c; sourceTree = "<group>"; };
838BDB611D3AF08C0022CA6F /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = "<group>"; }; 838BDB611D3AF08C0022CA6F /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = "<group>"; };
838BDB621D3AF08C0022CA6F /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = "<group>"; }; 838BDB621D3AF08C0022CA6F /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = "<group>"; };
838BDB631D3AF08C0022CA6F /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = "<group>"; }; 838BDB631D3AF08C0022CA6F /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = "<group>"; };
@ -1200,22 +1276,25 @@
836F6DDF18BDC2180095E648 /* coding */ = { 836F6DDF18BDC2180095E648 /* coding */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
834FE0AF215C798C000A5D3D /* acm_decoder_decode.c */,
834FE0AC215C798B000A5D3D /* acm_decoder_libacm.h */,
834FE0AA215C798A000A5D3D /* acm_decoder_util.c */,
836F6DE018BDC2180095E648 /* acm_decoder.c */, 836F6DE018BDC2180095E648 /* acm_decoder.c */,
836F6DE118BDC2180095E648 /* acm_decoder.h */,
836F6DE218BDC2180095E648 /* adx_decoder.c */, 836F6DE218BDC2180095E648 /* adx_decoder.c */,
8315958320FEC831007002F0 /* asf_decoder.c */, 8315958320FEC831007002F0 /* asf_decoder.c */,
8306B08120984517000302D4 /* at3plus_decoder.c */, 8306B08120984517000302D4 /* at3plus_decoder.c */,
830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */,
834FE0AE215C798B000A5D3D /* atrac9_decoder.c.orig */,
834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */,
831BA6221EAC61CB00CF89B0 /* coding_utils.c */, 831BA6221EAC61CB00CF89B0 /* coding_utils.c */,
836F6DE518BDC2180095E648 /* coding.h */, 836F6DE518BDC2180095E648 /* coding.h */,
834FE0AD215C798B000A5D3D /* derf_decoder.c */,
834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */,
8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */, 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */,
83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */, 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */,
83AA5D151F6E2F600020821C /* ea_xas_decoder.c */, 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */,
8306B08320984517000302D4 /* fadpcm_decoder.c */, 8306B08320984517000302D4 /* fadpcm_decoder.c */,
8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */, 834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */,
83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */,
8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */,
8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */,
838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */, 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */,
836F6DE918BDC2180095E648 /* g72x_state.h */, 836F6DE918BDC2180095E648 /* g72x_state.h */,
83D7318B1A749EEE00CA1366 /* g719_decoder.c */, 83D7318B1A749EEE00CA1366 /* g719_decoder.c */,
@ -1259,6 +1338,7 @@
839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */,
836F6DFD18BDC2180095E648 /* ws_decoder.c */, 836F6DFD18BDC2180095E648 /* ws_decoder.c */,
836F6DFE18BDC2180095E648 /* xa_decoder.c */, 836F6DFE18BDC2180095E648 /* xa_decoder.c */,
834FE0B0215C798C000A5D3D /* xmd_decoder.c */,
8306B08220984517000302D4 /* yamaha_decoder.c */, 8306B08220984517000302D4 /* yamaha_decoder.c */,
); );
path = coding; path = coding;
@ -1301,14 +1381,15 @@
8306B0A120984551000302D4 /* blocked_vs.c */, 8306B0A120984551000302D4 /* blocked_vs.c */,
8306B09D20984551000302D4 /* blocked_ws_aud.c */, 8306B09D20984551000302D4 /* blocked_ws_aud.c */,
8306B09F20984551000302D4 /* blocked_wsi.c */, 8306B09F20984551000302D4 /* blocked_wsi.c */,
834FE0BC215C79A8000A5D3D /* blocked_xa_aiff.c */,
8306B0912098454E000302D4 /* blocked_xa.c */, 8306B0912098454E000302D4 /* blocked_xa.c */,
83A21F7A201D895B000F04B9 /* blocked_xvag.c */, 83A21F7A201D895B000F04B9 /* blocked_xvag.c */,
8306B08C2098454D000302D4 /* blocked_xvas.c */, 8306B08C2098454D000302D4 /* blocked_xvas.c */,
836F6E0418BDC2180095E648 /* blocked.c */, 836F6E0418BDC2180095E648 /* blocked.c */,
834FE0BD215C79A9000A5D3D /* flat.c */,
836F6E0D18BDC2180095E648 /* interleave.c */, 836F6E0D18BDC2180095E648 /* interleave.c */,
8306B0902098454E000302D4 /* layered.c */, 8306B0902098454E000302D4 /* layered.c */,
836F6E1018BDC2180095E648 /* layout.h */, 836F6E1018BDC2180095E648 /* layout.h */,
836F6E1418BDC2180095E648 /* nolayout.c */,
8306B0892098454D000302D4 /* segmented.c */, 8306B0892098454D000302D4 /* segmented.c */,
); );
path = layout; path = layout;
@ -1318,19 +1399,25 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
836F6E2918BDC2180095E648 /* 2dx9.c */, 836F6E2918BDC2180095E648 /* 2dx9.c */,
834FE0C8215C79E7000A5D3D /* a2m.c */,
8306B0C82098458D000302D4 /* aax_utf.h */, 8306B0C82098458D000302D4 /* aax_utf.h */,
836F6E2A18BDC2180095E648 /* aax.c */, 836F6E2A18BDC2180095E648 /* aax.c */,
836F6E2B18BDC2180095E648 /* acm.c */, 836F6E2B18BDC2180095E648 /* acm.c */,
834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */,
836F6E2C18BDC2180095E648 /* ads.c */, 836F6E2C18BDC2180095E648 /* ads.c */,
8349A9021FE6258100E26435 /* adx_keys.h */, 8349A9021FE6258100E26435 /* adx_keys.h */,
831BA60E1EAC61A500CF89B0 /* adx.c */, 831BA60E1EAC61A500CF89B0 /* adx.c */,
8349A9001FE6258000E26435 /* afc.c */, 8349A9001FE6258000E26435 /* afc.c */,
836F6E2F18BDC2180095E648 /* agsc.c */, 836F6E2F18BDC2180095E648 /* agsc.c */,
834FE0E5215C79EC000A5D3D /* ahv.c */,
836F6E3018BDC2180095E648 /* ahx.c */, 836F6E3018BDC2180095E648 /* ahx.c */,
834FE0C1215C79E5000A5D3D /* aif_asobo.c */,
836F6E3118BDC2180095E648 /* aifc.c */, 836F6E3118BDC2180095E648 /* aifc.c */,
8349A8F61FE6257E00E26435 /* aix_streamfile.h */, 8349A8F61FE6257E00E26435 /* aix_streamfile.h */,
836F6E3218BDC2180095E648 /* aix.c */, 836F6E3218BDC2180095E648 /* aix.c */,
836F6E3318BDC2180095E648 /* akb.c */, 836F6E3318BDC2180095E648 /* akb.c */,
834FE0C0215C79E5000A5D3D /* ao.c */,
834FE0C7215C79E7000A5D3D /* apc.c */,
836F6E3418BDC2180095E648 /* apple_caff.c */, 836F6E3418BDC2180095E648 /* apple_caff.c */,
8315958820FEC83F007002F0 /* asf.c */, 8315958820FEC83F007002F0 /* asf.c */,
836F6E3518BDC2180095E648 /* ast.c */, 836F6E3518BDC2180095E648 /* ast.c */,
@ -1347,11 +1434,14 @@
83A5F75E198DF021009AF94C /* bfwav.c */, 83A5F75E198DF021009AF94C /* bfwav.c */,
836F6E3818BDC2180095E648 /* bgw.c */, 836F6E3818BDC2180095E648 /* bgw.c */,
83299FCE1E7660C7003A3242 /* bik.c */, 83299FCE1E7660C7003A3242 /* bik.c */,
834FE0CA215C79E7000A5D3D /* bnk_sony.c */,
836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3918BDC2180095E648 /* bnsf.c */,
836F6E3A18BDC2180095E648 /* brstm.c */, 836F6E3A18BDC2180095E648 /* brstm.c */,
83EDE5D71A70951A005F5D84 /* btsnd.c */, 83EDE5D71A70951A005F5D84 /* btsnd.c */,
8306B0CF2098458F000302D4 /* caf.c */, 8306B0CF2098458F000302D4 /* caf.c */,
836F6E3B18BDC2180095E648 /* capdsp.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */,
834FE0E8215C79EC000A5D3D /* ck.c */,
834FE0D8215C79EA000A5D3D /* csmp.c */,
836F6E3C18BDC2180095E648 /* Cstr.c */, 836F6E3C18BDC2180095E648 /* Cstr.c */,
836F6E3D18BDC2180095E648 /* dc_asd.c */, 836F6E3D18BDC2180095E648 /* dc_asd.c */,
836F6E3E18BDC2180095E648 /* dc_dcsw_dcs.c */, 836F6E3E18BDC2180095E648 /* dc_dcsw_dcs.c */,
@ -1359,14 +1449,15 @@
836F6E4018BDC2180095E648 /* dc_kcey.c */, 836F6E4018BDC2180095E648 /* dc_kcey.c */,
836F6E4118BDC2180095E648 /* dc_str.c */, 836F6E4118BDC2180095E648 /* dc_str.c */,
8349A8EE1FE6257C00E26435 /* dec.c */, 8349A8EE1FE6257C00E26435 /* dec.c */,
834FE0CD215C79E8000A5D3D /* derf.c */,
836F6E4318BDC2180095E648 /* dmsg_segh.c */, 836F6E4318BDC2180095E648 /* dmsg_segh.c */,
83299FCF1E7660C7003A3242 /* dsp_adx.c */, 83299FCF1E7660C7003A3242 /* dsp_adx.c */,
836F6E4418BDC2180095E648 /* dsp_bdsp.c */, 836F6E4418BDC2180095E648 /* dsp_bdsp.c */,
836F6E4518BDC2180095E648 /* dsp_sth_str.c */,
8349A8FF1FE6258000E26435 /* ea_1snh.c */, 8349A8FF1FE6258000E26435 /* ea_1snh.c */,
8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */, 8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */,
8349A8F71FE6257E00E26435 /* ea_eaac.c */, 8349A8F71FE6257E00E26435 /* ea_eaac.c */,
830165981F256BD000CA0941 /* ea_schl_fixed.c */, 830165981F256BD000CA0941 /* ea_schl_fixed.c */,
834FE0DA215C79EA000A5D3D /* ea_schl_streamfile.h */,
836F6E4618BDC2180095E648 /* ea_schl.c */, 836F6E4618BDC2180095E648 /* ea_schl.c */,
83EED5D1203A8BC7008BEB45 /* ea_swvr.c */, 83EED5D1203A8BC7008BEB45 /* ea_swvr.c */,
8306B0C42098458D000302D4 /* ea_wve_ad10.c */, 8306B0C42098458D000302D4 /* ea_wve_ad10.c */,
@ -1379,8 +1470,10 @@
836F6E4B18BDC2180095E648 /* ffw.c */, 836F6E4B18BDC2180095E648 /* ffw.c */,
8349A8FD1FE6257F00E26435 /* flx.c */, 8349A8FD1FE6257F00E26435 /* flx.c */,
83A21F81201D8981000F04B9 /* fsb_encrypted.c */, 83A21F81201D8981000F04B9 /* fsb_encrypted.c */,
834FE0C4215C79E6000A5D3D /* fsb_interleave_streamfile.h */,
83A21F7E201D8980000F04B9 /* fsb_keys.h */, 83A21F7E201D8980000F04B9 /* fsb_keys.h */,
836F6E4C18BDC2180095E648 /* fsb.c */, 836F6E4C18BDC2180095E648 /* fsb.c */,
834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */,
83F5F8821908D0A400C8E65F /* fsb5.c */, 83F5F8821908D0A400C8E65F /* fsb5.c */,
834D3A6D19F47C98001C54F6 /* g1l.c */, 834D3A6D19F47C98001C54F6 /* g1l.c */,
836F6E4D18BDC2180095E648 /* gca.c */, 836F6E4D18BDC2180095E648 /* gca.c */,
@ -1392,12 +1485,14 @@
836F6E5218BDC2180095E648 /* halpst.c */, 836F6E5218BDC2180095E648 /* halpst.c */,
83AA5D211F6E2F9C0020821C /* hca_keys.h */, 83AA5D211F6E2F9C0020821C /* hca_keys.h */,
8323894F1D2246C300482226 /* hca.c */, 8323894F1D2246C300482226 /* hca.c */,
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */,
836F6E5318BDC2180095E648 /* his.c */, 836F6E5318BDC2180095E648 /* his.c */,
836F6E5418BDC2180095E648 /* idsp.c */, 834FE0E0215C79EB000A5D3D /* idsp_ie.c */,
836F6E5518BDC2180095E648 /* ios_psnd.c */, 836F6E5518BDC2180095E648 /* ios_psnd.c */,
836F6E5618BDC2180095E648 /* ish_isd.c */, 836F6E5618BDC2180095E648 /* ish_isd.c */,
836F6E5718BDC2180095E648 /* ivaud.c */, 836F6E5718BDC2180095E648 /* ivaud.c */,
836F6E5818BDC2180095E648 /* ivb.c */, 836F6E5818BDC2180095E648 /* ivb.c */,
834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */,
83A21F83201D8981000F04B9 /* kma9.c */, 83A21F83201D8981000F04B9 /* kma9.c */,
836F6E5918BDC2180095E648 /* kraw.c */, 836F6E5918BDC2180095E648 /* kraw.c */,
830EBE122004656E0023AA10 /* ktss.c */, 830EBE122004656E0023AA10 /* ktss.c */,
@ -1407,11 +1502,13 @@
83709E001ECBC1A4005C03D3 /* mc3.c */, 83709E001ECBC1A4005C03D3 /* mc3.c */,
83EDE5D61A70951A005F5D84 /* mca.c */, 83EDE5D61A70951A005F5D84 /* mca.c */,
836F6E5E18BDC2180095E648 /* meta.h */, 836F6E5E18BDC2180095E648 /* meta.h */,
834FE0DE215C79EB000A5D3D /* mib_mih.c */,
836F6E5F18BDC2180095E648 /* mn_str.c */, 836F6E5F18BDC2180095E648 /* mn_str.c */,
8349A9031FE6258100E26435 /* mogg.c */, 8349A9031FE6258100E26435 /* mogg.c */,
836F6E6018BDC2180095E648 /* mp4.c */, 836F6E6018BDC2180095E648 /* mp4.c */,
8306B0CB2098458E000302D4 /* msb_msh.c */, 8306B0CB2098458E000302D4 /* msb_msh.c */,
83709E011ECBC1A4005C03D3 /* mss.c */, 83709E011ECBC1A4005C03D3 /* mss.c */,
834FE0E7215C79EC000A5D3D /* msv.c */,
836F6E6118BDC2180095E648 /* msvp.c */, 836F6E6118BDC2180095E648 /* msvp.c */,
836F6E6218BDC2180095E648 /* mus_acm.c */, 836F6E6218BDC2180095E648 /* mus_acm.c */,
836F6E6318BDC2180095E648 /* musc.c */, 836F6E6318BDC2180095E648 /* musc.c */,
@ -1445,9 +1542,12 @@
8349A8FA1FE6257E00E26435 /* ngc_vid1.c */, 8349A8FA1FE6257E00E26435 /* ngc_vid1.c */,
836F6E7E18BDC2180095E648 /* ngc_ymf.c */, 836F6E7E18BDC2180095E648 /* ngc_ymf.c */,
836F6E7F18BDC2180095E648 /* ngca.c */, 836F6E7F18BDC2180095E648 /* ngca.c */,
834FE0E1215C79EB000A5D3D /* nub_idsp.c */,
83AB8C731E8072A100086084 /* nub_vag.c */, 83AB8C731E8072A100086084 /* nub_vag.c */,
83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */, 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */,
834FE0D1215C79E9000A5D3D /* nus3bank.c */,
836F6E8118BDC2180095E648 /* nwa.c */, 836F6E8118BDC2180095E648 /* nwa.c */,
834FE0DB215C79EA000A5D3D /* nxa.c */,
8306B0C02098458C000302D4 /* nxap.c */, 8306B0C02098458C000302D4 /* nxap.c */,
83A21F7F201D8980000F04B9 /* ogg_vorbis.c */, 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */,
831BA60F1EAC61A500CF89B0 /* ogl.c */, 831BA60F1EAC61A500CF89B0 /* ogl.c */,
@ -1470,6 +1570,7 @@
836F6E8C18BDC2180095E648 /* pos.c */, 836F6E8C18BDC2180095E648 /* pos.c */,
8306B0C52098458D000302D4 /* ppst_streamfile.h */, 8306B0C52098458D000302D4 /* ppst_streamfile.h */,
8306B0D620984590000302D4 /* ppst.c */, 8306B0D620984590000302D4 /* ppst.c */,
834FE0D5215C79E9000A5D3D /* ps_headerless.c */,
836F6E8D18BDC2180095E648 /* ps2_2pfs.c */, 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */,
836F6E8E18BDC2180095E648 /* ps2_adm.c */, 836F6E8E18BDC2180095E648 /* ps2_adm.c */,
836F6E8F18BDC2180095E648 /* ps2_ads.c */, 836F6E8F18BDC2180095E648 /* ps2_ads.c */,
@ -1498,7 +1599,6 @@
836F6EA818BDC2180095E648 /* ps2_leg.c */, 836F6EA818BDC2180095E648 /* ps2_leg.c */,
836F6EA918BDC2180095E648 /* ps2_lpcm.c */, 836F6EA918BDC2180095E648 /* ps2_lpcm.c */,
836F6EAA18BDC2180095E648 /* ps2_mcg.c */, 836F6EAA18BDC2180095E648 /* ps2_mcg.c */,
836F6EAB18BDC2180095E648 /* ps2_mib.c */,
836F6EAC18BDC2180095E648 /* ps2_mic.c */, 836F6EAC18BDC2180095E648 /* ps2_mic.c */,
836F6EAD18BDC2180095E648 /* ps2_mihb.c */, 836F6EAD18BDC2180095E648 /* ps2_mihb.c */,
836F6EAE18BDC2180095E648 /* ps2_msa.c */, 836F6EAE18BDC2180095E648 /* ps2_msa.c */,
@ -1507,6 +1607,7 @@
836F6EB218BDC2180095E648 /* ps2_p2bt.c */, 836F6EB218BDC2180095E648 /* ps2_p2bt.c */,
8349A8F21FE6257D00E26435 /* ps2_pcm.c */, 8349A8F21FE6257D00E26435 /* ps2_pcm.c */,
836F6EB318BDC2180095E648 /* ps2_pnb.c */, 836F6EB318BDC2180095E648 /* ps2_pnb.c */,
834FE0E2215C79EB000A5D3D /* ps2_psh_streamfile.h */,
836F6EB418BDC2180095E648 /* ps2_psh.c */, 836F6EB418BDC2180095E648 /* ps2_psh.c */,
836F6EB618BDC2180095E648 /* ps2_rnd.c */, 836F6EB618BDC2180095E648 /* ps2_rnd.c */,
836F6EB718BDC2180095E648 /* ps2_rstm.c */, 836F6EB718BDC2180095E648 /* ps2_rstm.c */,
@ -1518,13 +1619,11 @@
836F6EBE18BDC2190095E648 /* ps2_spm.c */, 836F6EBE18BDC2190095E648 /* ps2_spm.c */,
836F6EBF18BDC2190095E648 /* ps2_sps.c */, 836F6EBF18BDC2190095E648 /* ps2_sps.c */,
836F6EC018BDC2190095E648 /* ps2_ster.c */, 836F6EC018BDC2190095E648 /* ps2_ster.c */,
836F6EC218BDC2190095E648 /* ps2_str.c */,
836F6EC318BDC2190095E648 /* ps2_strlr.c */, 836F6EC318BDC2190095E648 /* ps2_strlr.c */,
8350C0591E071990009E0A93 /* ps2_svag_snk.c */, 8350C0591E071990009E0A93 /* ps2_svag_snk.c */,
836F6EC418BDC2190095E648 /* ps2_svag.c */, 836F6EC418BDC2190095E648 /* ps2_svag.c */,
836F6EC518BDC2190095E648 /* ps2_tec.c */, 836F6EC518BDC2190095E648 /* ps2_tec.c */,
836F6EC618BDC2190095E648 /* ps2_tk5.c */, 836F6EC618BDC2190095E648 /* ps2_tk5.c */,
836F6EC718BDC2190095E648 /* ps2_vag.c */,
836F6EC818BDC2190095E648 /* ps2_vas.c */, 836F6EC818BDC2190095E648 /* ps2_vas.c */,
836F6EC918BDC2190095E648 /* ps2_vbk.c */, 836F6EC918BDC2190095E648 /* ps2_vbk.c */,
831BA6101EAC61A500CF89B0 /* ps2_vds_vdm.c */, 831BA6101EAC61A500CF89B0 /* ps2_vds_vdm.c */,
@ -1532,7 +1631,6 @@
836F6ECB18BDC2190095E648 /* ps2_vgv.c */, 836F6ECB18BDC2190095E648 /* ps2_vgv.c */,
836F6ECC18BDC2190095E648 /* ps2_vms.c */, 836F6ECC18BDC2190095E648 /* ps2_vms.c */,
836F6ECD18BDC2190095E648 /* ps2_voi.c */, 836F6ECD18BDC2190095E648 /* ps2_voi.c */,
836F6ECE18BDC2190095E648 /* ps2_vpk.c */,
836F6ECF18BDC2190095E648 /* ps2_wad.c */, 836F6ECF18BDC2190095E648 /* ps2_wad.c */,
836F6ED018BDC2190095E648 /* ps2_wb.c */, 836F6ED018BDC2190095E648 /* ps2_wb.c */,
836F6ED118BDC2190095E648 /* ps2_wmus.c */, 836F6ED118BDC2190095E648 /* ps2_wmus.c */,
@ -1541,7 +1639,6 @@
836F6ED318BDC2190095E648 /* ps2_xa30.c */, 836F6ED318BDC2190095E648 /* ps2_xa30.c */,
836F6ED518BDC2190095E648 /* ps3_cps.c */, 836F6ED518BDC2190095E648 /* ps3_cps.c */,
836F6ED618BDC2190095E648 /* ps3_ivag.c */, 836F6ED618BDC2190095E648 /* ps3_ivag.c */,
836F6ED718BDC2190095E648 /* ps3_klbs.c */,
836F6ED818BDC2190095E648 /* ps3_msf.c */, 836F6ED818BDC2190095E648 /* ps3_msf.c */,
8350270C1ED119D200C25929 /* ps3_mta2.c */, 8350270C1ED119D200C25929 /* ps3_mta2.c */,
836F6ED918BDC2190095E648 /* ps3_past.c */, 836F6ED918BDC2190095E648 /* ps3_past.c */,
@ -1550,6 +1647,7 @@
836F6EDF18BDC2190095E648 /* psx_gms.c */, 836F6EDF18BDC2190095E648 /* psx_gms.c */,
836F6EE118BDC2190095E648 /* raw.c */, 836F6EE118BDC2190095E648 /* raw.c */,
836F6EE218BDC2190095E648 /* redspark.c */, 836F6EE218BDC2190095E648 /* redspark.c */,
834FE0D9215C79EA000A5D3D /* rfrm.c */,
836F6EE318BDC2190095E648 /* riff.c */, 836F6EE318BDC2190095E648 /* riff.c */,
836F6EE418BDC2190095E648 /* rkv.c */, 836F6EE418BDC2190095E648 /* rkv.c */,
836F6EE518BDC2190095E648 /* rs03.c */, 836F6EE518BDC2190095E648 /* rs03.c */,
@ -1565,6 +1663,7 @@
836F6EED18BDC2190095E648 /* sat_sap.c */, 836F6EED18BDC2190095E648 /* sat_sap.c */,
8349A8F51FE6257D00E26435 /* scd_pcm.c */, 8349A8F51FE6257D00E26435 /* scd_pcm.c */,
836F6EEE18BDC2190095E648 /* sd9.c */, 836F6EEE18BDC2190095E648 /* sd9.c */,
834FE0E6215C79EC000A5D3D /* sdf.c */,
836F6EEF18BDC2190095E648 /* sdt.c */, 836F6EEF18BDC2190095E648 /* sdt.c */,
836F6EF018BDC2190095E648 /* seg.c */, 836F6EF018BDC2190095E648 /* seg.c */,
836F6EF118BDC2190095E648 /* sfl.c */, 836F6EF118BDC2190095E648 /* sfl.c */,
@ -1575,6 +1674,7 @@
8306B0C72098458D000302D4 /* smv.c */, 8306B0C72098458D000302D4 /* smv.c */,
83A21F82201D8981000F04B9 /* sps_n1.c */, 83A21F82201D8981000F04B9 /* sps_n1.c */,
836F6EF318BDC2190095E648 /* spt_spd.c */, 836F6EF318BDC2190095E648 /* spt_spd.c */,
834FE0D6215C79E9000A5D3D /* sqex_scd_sscf.c */,
8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */, 8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */,
836F6EF418BDC2190095E648 /* sqex_scd.c */, 836F6EF418BDC2190095E648 /* sqex_scd.c */,
83A21F84201D8981000F04B9 /* sqex_sead.c */, 83A21F84201D8981000F04B9 /* sqex_sead.c */,
@ -1582,7 +1682,9 @@
83AA5D231F6E2F9C0020821C /* stm.c */, 83AA5D231F6E2F9C0020821C /* stm.c */,
836F6EF618BDC2190095E648 /* str_asr.c */, 836F6EF618BDC2190095E648 /* str_asr.c */,
836F6EF718BDC2190095E648 /* str_snds.c */, 836F6EF718BDC2190095E648 /* str_snds.c */,
834FE0C2215C79E6000A5D3D /* str_wav.c */,
836F6EF818BDC2190095E648 /* stx.c */, 836F6EF818BDC2190095E648 /* stx.c */,
834FE0D7215C79EA000A5D3D /* svg.c */,
836F6EF918BDC2190095E648 /* svs.c */, 836F6EF918BDC2190095E648 /* svs.c */,
831BA6121EAC61A500CF89B0 /* sxd.c */, 831BA6121EAC61A500CF89B0 /* sxd.c */,
83709E031ECBC1A4005C03D3 /* ta_aac.c */, 83709E031ECBC1A4005C03D3 /* ta_aac.c */,
@ -1597,8 +1699,14 @@
8306B0CA2098458E000302D4 /* ubi_lyn.c */, 8306B0CA2098458E000302D4 /* ubi_lyn.c */,
831BA6131EAC61A500CF89B0 /* ubi_raki.c */, 831BA6131EAC61A500CF89B0 /* ubi_raki.c */,
8349A8F41FE6257D00E26435 /* ubi_sb.c */, 8349A8F41FE6257D00E26435 /* ubi_sb.c */,
834FE0C5215C79E6000A5D3D /* ue4opus.c */,
834FE0DD215C79EB000A5D3D /* utk.c */,
834FE0E4215C79EC000A5D3D /* vag.c */,
834FE0D3215C79E9000A5D3D /* vai.c */,
831BA6141EAC61A500CF89B0 /* vawx.c */, 831BA6141EAC61A500CF89B0 /* vawx.c */,
836F6EFD18BDC2190095E648 /* vgs.c */, 836F6EFD18BDC2190095E648 /* vgs.c */,
834FE0CE215C79E8000A5D3D /* vis.c */,
834FE0D4215C79E9000A5D3D /* vpk.c */,
836F6EFE18BDC2190095E648 /* vs.c */, 836F6EFE18BDC2190095E648 /* vs.c */,
8349A8F91FE6257E00E26435 /* vsf_tta.c */, 8349A8F91FE6257E00E26435 /* vsf_tta.c */,
836F6EFF18BDC2190095E648 /* vsf.c */, 836F6EFF18BDC2190095E648 /* vsf.c */,
@ -1606,22 +1714,26 @@
8306B0C22098458C000302D4 /* waf.c */, 8306B0C22098458C000302D4 /* waf.c */,
8306B0D02098458F000302D4 /* wave_segmented.c */, 8306B0D02098458F000302D4 /* wave_segmented.c */,
8306B0C92098458E000302D4 /* wave.c */, 8306B0C92098458E000302D4 /* wave.c */,
834FE0D0215C79E8000A5D3D /* wavebatch.c */,
83CAB8E11F0B0745001BC993 /* wii_04sw.c */, 83CAB8E11F0B0745001BC993 /* wii_04sw.c */,
836F6F0018BDC2190095E648 /* wii_bns.c */, 836F6F0018BDC2190095E648 /* wii_bns.c */,
836F6F0118BDC2190095E648 /* wii_mus.c */, 836F6F0118BDC2190095E648 /* wii_mus.c */,
836F6F0218BDC2190095E648 /* wii_ras.c */, 836F6F0218BDC2190095E648 /* wii_ras.c */,
836F6F0318BDC2190095E648 /* wii_smp.c */, 836F6F0318BDC2190095E648 /* wii_smp.c */,
836F6F0418BDC2190095E648 /* wii_sng.c */, 836F6F0418BDC2190095E648 /* wii_sng.c */,
836F6F0518BDC2190095E648 /* wii_str.c */,
836F6F0618BDC2190095E648 /* wii_sts.c */, 836F6F0618BDC2190095E648 /* wii_sts.c */,
836F6F0718BDC2190095E648 /* wpd.c */, 836F6F0718BDC2190095E648 /* wpd.c */,
836F6F0818BDC2190095E648 /* ws_aud.c */, 836F6F0818BDC2190095E648 /* ws_aud.c */,
834FE0CB215C79E8000A5D3D /* wsi.c */,
834FE0CC215C79E8000A5D3D /* wv2.c */,
834FE0C9215C79E7000A5D3D /* wv6.c */,
836F6F0918BDC2190095E648 /* wvs.c */, 836F6F0918BDC2190095E648 /* wvs.c */,
83FF0EBB1E93282100C58054 /* wwise.c */, 83FF0EBB1E93282100C58054 /* wwise.c */,
83AB8C741E8072A100086084 /* x360_ast.c */, 83AB8C741E8072A100086084 /* x360_ast.c */,
831BA6151EAC61A500CF89B0 /* x360_cxs.c */, 831BA6151EAC61A500CF89B0 /* x360_cxs.c */,
831BA6171EAC61A500CF89B0 /* x360_pasx.c */, 831BA6171EAC61A500CF89B0 /* x360_pasx.c */,
836F6F0A18BDC2190095E648 /* x360_tra.c */, 836F6F0A18BDC2190095E648 /* x360_tra.c */,
834FE0D2215C79E9000A5D3D /* xau_konami.c */,
833A7A2D1ED11961003EC53E /* xau.c */, 833A7A2D1ED11961003EC53E /* xau.c */,
836F6F0B18BDC2190095E648 /* xbox_hlwav.c */, 836F6F0B18BDC2190095E648 /* xbox_hlwav.c */,
836F6F0C18BDC2190095E648 /* xbox_ims.c */, 836F6F0C18BDC2190095E648 /* xbox_ims.c */,
@ -1629,8 +1741,10 @@
836F6F0F18BDC2190095E648 /* xbox_xmu.c */, 836F6F0F18BDC2190095E648 /* xbox_xmu.c */,
836F6F1018BDC2190095E648 /* xbox_xvas.c */, 836F6F1018BDC2190095E648 /* xbox_xvas.c */,
8350C0541E071881009E0A93 /* xma.c */, 8350C0541E071881009E0A93 /* xma.c */,
834FE0DC215C79EA000A5D3D /* xmd.c */,
830EBE112004656E0023AA10 /* xnb.c */, 830EBE112004656E0023AA10 /* xnb.c */,
836F6F1218BDC2190095E648 /* xss.c */, 836F6F1218BDC2190095E648 /* xss.c */,
834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */,
83345A4E1F8AEB2800B2EAA4 /* xvag.c */, 83345A4E1F8AEB2800B2EAA4 /* xvag.c */,
836F6F1318BDC2190095E648 /* xwb.c */, 836F6F1318BDC2190095E648 /* xwb.c */,
83A21F7D201D8980000F04B9 /* xwc.c */, 83A21F7D201D8980000F04B9 /* xwc.c */,
@ -1676,30 +1790,36 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
836F705518BDC2190095E648 /* streamtypes.h in Headers */, 836F705518BDC2190095E648 /* streamtypes.h in Headers */,
836F6F1F18BDC2190095E648 /* acm_decoder.h in Headers */,
836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */, 836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */,
83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */,
834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */,
8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */,
834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */,
8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */, 8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */,
836F6F2718BDC2190095E648 /* g72x_state.h in Headers */, 836F6F2718BDC2190095E648 /* g72x_state.h in Headers */,
8306B0E020984590000302D4 /* ppst_streamfile.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 */, 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
836F705418BDC2190095E648 /* streamfile.h in Headers */, 836F705418BDC2190095E648 /* streamfile.h in Headers */,
8374EE401F787AB600033E90 /* ffmpeg_decoder_utils.h in Headers */,
83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */, 83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */,
8323894B1D22419B00482226 /* clHCA.h in Headers */, 8323894B1D22419B00482226 /* clHCA.h in Headers */,
839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */, 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */,
834FE10C215C79ED000A5D3D /* fsb5_interleave_streamfile.h in Headers */,
839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */, 839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */,
8306B0E320984590000302D4 /* aax_utf.h in Headers */, 8306B0E320984590000302D4 /* aax_utf.h in Headers */,
8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */, 8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */,
836F705918BDC2190095E648 /* vgmstream.h in Headers */, 836F705918BDC2190095E648 /* vgmstream.h in Headers */,
8306B0DE20984590000302D4 /* awc_xma_streamfile.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 */, 839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */,
834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */,
48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */, 48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */,
834FE10B215C79ED000A5D3D /* ps2_psh_streamfile.h in Headers */,
836F705718BDC2190095E648 /* util.h in Headers */, 836F705718BDC2190095E648 /* util.h in Headers */,
836F6F9A18BDC2190095E648 /* meta.h in Headers */, 836F6F9A18BDC2190095E648 /* meta.h in Headers */,
8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */,
8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */, 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */,
834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */,
8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */, 8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */,
8349A91B1FE6258200E26435 /* adx_keys.h in Headers */, 8349A91B1FE6258200E26435 /* adx_keys.h in Headers */,
836F6F4D18BDC2190095E648 /* layout.h in Headers */, 836F6F4D18BDC2190095E648 /* layout.h in Headers */,
@ -1854,6 +1974,7 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
834FE0B7215C798C000A5D3D /* atrac9_decoder.c.orig in Resources */,
836F6B4718BDB8880095E648 /* InfoPlist.strings in Resources */, 836F6B4718BDB8880095E648 /* InfoPlist.strings in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -1867,6 +1988,7 @@
files = ( files = (
839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */, 839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */,
83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */, 83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */,
834FE104215C79ED000A5D3D /* nxa.c in Sources */,
8306B0B020984552000302D4 /* blocked_ps2_strlr.c in Sources */, 8306B0B020984552000302D4 /* blocked_ps2_strlr.c in Sources */,
8349A9071FE6258200E26435 /* dec.c in Sources */, 8349A9071FE6258200E26435 /* dec.c in Sources */,
839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */, 839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */,
@ -1876,11 +1998,13 @@
839E21E31F2EDAF100EE54D7 /* mpeg_custom_utils_ahx.c in Sources */, 839E21E31F2EDAF100EE54D7 /* mpeg_custom_utils_ahx.c in Sources */,
8306B0AF20984552000302D4 /* blocked_rws.c in Sources */, 8306B0AF20984552000302D4 /* blocked_rws.c in Sources */,
839E21EB1F2EDB0600EE54D7 /* sk_aud.c in Sources */, 839E21EB1F2EDB0600EE54D7 /* sk_aud.c in Sources */,
834FE0F1215C79ED000A5D3D /* a2m.c in Sources */,
8301659A1F256BD000CA0941 /* txth.c in Sources */, 8301659A1F256BD000CA0941 /* txth.c in Sources */,
8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */, 8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */,
8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */, 8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */,
83CAB8E31F0B0755001BC993 /* pc_xa30.c in Sources */, 83CAB8E31F0B0755001BC993 /* pc_xa30.c in Sources */,
83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */, 83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */,
834FE0EB215C79ED000A5D3D /* str_wav.c in Sources */,
8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */, 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */,
83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */, 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */,
83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */, 83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */,
@ -1888,7 +2012,6 @@
8306B0EE20984590000302D4 /* smc_smh.c in Sources */, 8306B0EE20984590000302D4 /* smc_smh.c in Sources */,
836F6FAD18BDC2190095E648 /* ngc_dsp_konami.c in Sources */, 836F6FAD18BDC2190095E648 /* ngc_dsp_konami.c in Sources */,
836F6FF818BDC2190095E648 /* ps2_smpl.c in Sources */, 836F6FF818BDC2190095E648 /* ps2_smpl.c in Sources */,
836F6F8118BDC2190095E648 /* dsp_sth_str.c in Sources */,
836F703818BDC2190095E648 /* ubi_ckd.c in Sources */, 836F703818BDC2190095E648 /* ubi_ckd.c in Sources */,
836F705318BDC2190095E648 /* streamfile.c in Sources */, 836F705318BDC2190095E648 /* streamfile.c in Sources */,
836F6F7418BDC2190095E648 /* bgw.c in Sources */, 836F6F7418BDC2190095E648 /* bgw.c in Sources */,
@ -1899,6 +2022,7 @@
836F6F7D18BDC2190095E648 /* dc_str.c in Sources */, 836F6F7D18BDC2190095E648 /* dc_str.c in Sources */,
83A5F75F198DF021009AF94C /* bfwav.c in Sources */, 83A5F75F198DF021009AF94C /* bfwav.c in Sources */,
836F702018BDC2190095E648 /* rkv.c in Sources */, 836F702018BDC2190095E648 /* rkv.c in Sources */,
834FE0F4215C79ED000A5D3D /* wsi.c in Sources */,
836F703218BDC2190095E648 /* str_asr.c in Sources */, 836F703218BDC2190095E648 /* str_asr.c in Sources */,
836F6FB218BDC2190095E648 /* ngc_gcub.c in Sources */, 836F6FB218BDC2190095E648 /* ngc_gcub.c in Sources */,
836F702818BDC2190095E648 /* sat_dvi.c in Sources */, 836F702818BDC2190095E648 /* sat_dvi.c in Sources */,
@ -1919,13 +2043,16 @@
836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */, 836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */,
8306B0DD20984590000302D4 /* waf.c in Sources */, 8306B0DD20984590000302D4 /* waf.c in Sources */,
8306B0B320984552000302D4 /* blocked_thp.c in Sources */, 8306B0B320984552000302D4 /* blocked_thp.c in Sources */,
834FE0B6215C798C000A5D3D /* derf_decoder.c in Sources */,
836F702318BDC2190095E648 /* rsf.c in Sources */, 836F702318BDC2190095E648 /* rsf.c in Sources */,
834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */,
83299FD01E7660C7003A3242 /* bik.c in Sources */, 83299FD01E7660C7003A3242 /* bik.c in Sources */,
836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */, 836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */,
8306B0EF20984590000302D4 /* ubi_bao.c in Sources */, 8306B0EF20984590000302D4 /* ubi_bao.c in Sources */,
836F6FBB18BDC2190095E648 /* ngca.c in Sources */, 836F6FBB18BDC2190095E648 /* ngca.c in Sources */,
8306B0E220984590000302D4 /* smv.c in Sources */, 8306B0E220984590000302D4 /* smv.c in Sources */,
8349A91E1FE6258200E26435 /* bar.c in Sources */, 8349A91E1FE6258200E26435 /* bar.c in Sources */,
834FE110215C79ED000A5D3D /* msv.c in Sources */,
832389521D224C0800482226 /* hca_decoder.c in Sources */, 832389521D224C0800482226 /* hca_decoder.c in Sources */,
836F6F9418BDC2190095E648 /* ivb.c in Sources */, 836F6F9418BDC2190095E648 /* ivb.c in Sources */,
836F6F8D18BDC2190095E648 /* gsp_gsb.c in Sources */, 836F6F8D18BDC2190095E648 /* gsp_gsb.c in Sources */,
@ -1942,6 +2069,8 @@
836F6F6818BDC2190095E648 /* ads.c in Sources */, 836F6F6818BDC2190095E648 /* ads.c in Sources */,
8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */, 8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */,
83AB8C761E8072A100086084 /* x360_ast.c in Sources */, 83AB8C761E8072A100086084 /* x360_ast.c in Sources */,
834FE105215C79ED000A5D3D /* xmd.c in Sources */,
834FE0F6215C79ED000A5D3D /* derf.c in Sources */,
836F6F8B18BDC2190095E648 /* genh.c in Sources */, 836F6F8B18BDC2190095E648 /* genh.c in Sources */,
8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */, 8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */,
836F705118BDC2190095E648 /* zsd.c in Sources */, 836F705118BDC2190095E648 /* zsd.c in Sources */,
@ -1962,6 +2091,7 @@
836F705618BDC2190095E648 /* util.c in Sources */, 836F705618BDC2190095E648 /* util.c in Sources */,
8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */, 8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */,
8306B0EA20984590000302D4 /* caf.c in Sources */, 8306B0EA20984590000302D4 /* caf.c in Sources */,
834FE107215C79ED000A5D3D /* mib_mih.c in Sources */,
836F703618BDC2190095E648 /* thp.c in Sources */, 836F703618BDC2190095E648 /* thp.c in Sources */,
836F6F7818BDC2190095E648 /* Cstr.c in Sources */, 836F6F7818BDC2190095E648 /* Cstr.c in Sources */,
836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */, 836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */,
@ -1971,8 +2101,8 @@
836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */, 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */,
83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */, 83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */,
836F6FA818BDC2190095E648 /* nds_swav.c in Sources */, 836F6FA818BDC2190095E648 /* nds_swav.c in Sources */,
8374EE411F787AB600033E90 /* ffmpeg_decoder_utils.c in Sources */,
8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */, 8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */,
834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */,
833A7A2E1ED11961003EC53E /* xau.c in Sources */, 833A7A2E1ED11961003EC53E /* xau.c in Sources */,
836F6FB518BDC2190095E648 /* ngc_pdt.c in Sources */, 836F6FB518BDC2190095E648 /* ngc_pdt.c in Sources */,
836F6F6C18BDC2190095E648 /* ahx.c in Sources */, 836F6F6C18BDC2190095E648 /* ahx.c in Sources */,
@ -1993,10 +2123,13 @@
836F6F6618BDC2190095E648 /* aax.c in Sources */, 836F6F6618BDC2190095E648 /* aax.c in Sources */,
836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */, 836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */,
8306B0BC20984552000302D4 /* blocked_vs.c in Sources */, 8306B0BC20984552000302D4 /* blocked_vs.c in Sources */,
834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */,
836F6F6718BDC2190095E648 /* acm.c in Sources */, 836F6F6718BDC2190095E648 /* acm.c in Sources */,
834FE0FD215C79ED000A5D3D /* vpk.c in Sources */,
8306B0DF20984590000302D4 /* ea_wve_ad10.c in Sources */, 8306B0DF20984590000302D4 /* ea_wve_ad10.c in Sources */,
836F6F8A18BDC2190095E648 /* gcsw.c in Sources */, 836F6F8A18BDC2190095E648 /* gcsw.c in Sources */,
836F6F9C18BDC2190095E648 /* mp4.c in Sources */, 836F6F9C18BDC2190095E648 /* mp4.c in Sources */,
834FE101215C79ED000A5D3D /* csmp.c in Sources */,
8306B0A720984552000302D4 /* blocked_xvas.c in Sources */, 8306B0A720984552000302D4 /* blocked_xvas.c in Sources */,
8349A9101FE6258200E26435 /* ea_eaac.c in Sources */, 8349A9101FE6258200E26435 /* ea_eaac.c in Sources */,
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */, 836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */,
@ -2008,11 +2141,14 @@
836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */, 836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */,
83709E071ECBC1A4005C03D3 /* mss.c in Sources */, 83709E071ECBC1A4005C03D3 /* mss.c in Sources */,
836F6F8F18BDC2190095E648 /* his.c in Sources */, 836F6F8F18BDC2190095E648 /* his.c in Sources */,
834FE0E9215C79ED000A5D3D /* ao.c in Sources */,
836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */, 836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */,
8306B08520984518000302D4 /* yamaha_decoder.c in Sources */, 8306B08520984518000302D4 /* yamaha_decoder.c in Sources */,
836F6FEF18BDC2190095E648 /* ps2_pnb.c in Sources */, 836F6FEF18BDC2190095E648 /* ps2_pnb.c in Sources */,
836F6FCB18BDC2190095E648 /* ps2_ads.c in Sources */, 836F6FCB18BDC2190095E648 /* ps2_ads.c in Sources */,
834FE108215C79ED000A5D3D /* hd3_bd3.c in Sources */,
836F6FD318BDC2190095E648 /* ps2_ccc.c in Sources */, 836F6FD318BDC2190095E648 /* ps2_ccc.c in Sources */,
834FE0FC215C79ED000A5D3D /* vai.c in Sources */,
83AA5D171F6E2F600020821C /* mpeg_custom_utils_ealayer3.c in Sources */, 83AA5D171F6E2F600020821C /* mpeg_custom_utils_ealayer3.c in Sources */,
83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */, 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */,
836F704C18BDC2190095E648 /* xbox_xvas.c in Sources */, 836F704C18BDC2190095E648 /* xbox_xvas.c in Sources */,
@ -2021,7 +2157,6 @@
836F703A18BDC2190095E648 /* vs.c in Sources */, 836F703A18BDC2190095E648 /* vs.c in Sources */,
8306B0F220984590000302D4 /* ubi_jade.c in Sources */, 8306B0F220984590000302D4 /* ubi_jade.c in Sources */,
836F6FF918BDC2190095E648 /* ps2_snd.c in Sources */, 836F6FF918BDC2190095E648 /* ps2_snd.c in Sources */,
836F6F9018BDC2190095E648 /* idsp.c in Sources */,
836F6F2918BDC2190095E648 /* l5_555_decoder.c in Sources */, 836F6F2918BDC2190095E648 /* l5_555_decoder.c in Sources */,
836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */, 836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */,
836F6F7918BDC2190095E648 /* dc_asd.c in Sources */, 836F6F7918BDC2190095E648 /* dc_asd.c in Sources */,
@ -2029,15 +2164,16 @@
836F701A18BDC2190095E648 /* psx_fag.c in Sources */, 836F701A18BDC2190095E648 /* psx_fag.c in Sources */,
836F703B18BDC2190095E648 /* vsf.c in Sources */, 836F703B18BDC2190095E648 /* vsf.c in Sources */,
836F6F8218BDC2190095E648 /* ea_schl.c in Sources */, 836F6F8218BDC2190095E648 /* ea_schl.c in Sources */,
836F700A18BDC2190095E648 /* ps2_vpk.c in Sources */,
836F6F7318BDC2190095E648 /* bcstm.c in Sources */, 836F6F7318BDC2190095E648 /* bcstm.c in Sources */,
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */, 83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */,
836F704018BDC2190095E648 /* wii_sng.c in Sources */, 836F704018BDC2190095E648 /* wii_sng.c in Sources */,
834FE10D215C79ED000A5D3D /* vag.c in Sources */,
8349A9131FE6258200E26435 /* ngc_vid1.c in Sources */, 8349A9131FE6258200E26435 /* ngc_vid1.c in Sources */,
8350C0551E071881009E0A93 /* xma.c in Sources */, 8350C0551E071881009E0A93 /* xma.c in Sources */,
836F6F3218BDC2190095E648 /* ngc_dsp_decoder.c in Sources */, 836F6F3218BDC2190095E648 /* ngc_dsp_decoder.c in Sources */,
836F704218BDC2190095E648 /* wii_sts.c in Sources */, 836F704218BDC2190095E648 /* wii_sts.c in Sources */,
836F703918BDC2190095E648 /* vgs.c in Sources */, 836F703918BDC2190095E648 /* vgs.c in Sources */,
834FE0F8215C79ED000A5D3D /* adpcm_capcom.c in Sources */,
836F6F2C18BDC2190095E648 /* mp4_aac_decoder.c in Sources */, 836F6F2C18BDC2190095E648 /* mp4_aac_decoder.c in Sources */,
836F701F18BDC2190095E648 /* riff.c in Sources */, 836F701F18BDC2190095E648 /* riff.c in Sources */,
83AA5D191F6E2F600020821C /* ea_xas_decoder.c in Sources */, 83AA5D191F6E2F600020821C /* ea_xas_decoder.c in Sources */,
@ -2064,6 +2200,7 @@
836F6FFF18BDC2190095E648 /* ps2_strlr.c in Sources */, 836F6FFF18BDC2190095E648 /* ps2_strlr.c in Sources */,
836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */, 836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */,
8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */, 8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */,
834FE0EA215C79ED000A5D3D /* aif_asobo.c in Sources */,
836F700418BDC2190095E648 /* ps2_vas.c in Sources */, 836F700418BDC2190095E648 /* ps2_vas.c in Sources */,
836F6F9818BDC2190095E648 /* mattel_hyperscan.c in Sources */, 836F6F9818BDC2190095E648 /* mattel_hyperscan.c in Sources */,
8306B0BA20984552000302D4 /* blocked_wsi.c in Sources */, 8306B0BA20984552000302D4 /* blocked_wsi.c in Sources */,
@ -2074,22 +2211,21 @@
836F702218BDC2190095E648 /* rsd.c in Sources */, 836F702218BDC2190095E648 /* rsd.c in Sources */,
8349A90D1FE6258200E26435 /* ubi_sb.c in Sources */, 8349A90D1FE6258200E26435 /* ubi_sb.c in Sources */,
83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */, 83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */,
834FE0BB215C798C000A5D3D /* celt_fsb_decoder.c in Sources */,
836F702518BDC2190095E648 /* rwx.c in Sources */, 836F702518BDC2190095E648 /* rwx.c in Sources */,
836F6F3A18BDC2190095E648 /* sdx2_decoder.c in Sources */, 836F6F3A18BDC2190095E648 /* sdx2_decoder.c in Sources */,
836F6FAF18BDC2190095E648 /* ngc_dsp_std.c in Sources */, 836F6FAF18BDC2190095E648 /* ngc_dsp_std.c in Sources */,
836F6F7118BDC2190095E648 /* ast.c in Sources */, 836F6F7118BDC2190095E648 /* ast.c in Sources */,
834FE0BF215C79A9000A5D3D /* flat.c in Sources */,
836F6FE318BDC2190095E648 /* ps2_khv.c in Sources */, 836F6FE318BDC2190095E648 /* ps2_khv.c in Sources */,
836F701318BDC2190095E648 /* ps3_klbs.c in Sources */,
836F6F6B18BDC2190095E648 /* agsc.c in Sources */, 836F6F6B18BDC2190095E648 /* agsc.c in Sources */,
836F6FF018BDC2190095E648 /* ps2_psh.c in Sources */, 836F6FF018BDC2190095E648 /* ps2_psh.c in Sources */,
836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */, 836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */,
836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */, 836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */,
836F6F3118BDC2190095E648 /* ngc_afc_decoder.c in Sources */, 836F6F3118BDC2190095E648 /* ngc_afc_decoder.c in Sources */,
836F6FE718BDC2190095E648 /* ps2_mib.c in Sources */,
836F6F9918BDC2190095E648 /* maxis_xa.c in Sources */, 836F6F9918BDC2190095E648 /* maxis_xa.c in Sources */,
836F702118BDC2190095E648 /* rs03.c in Sources */, 836F702118BDC2190095E648 /* rs03.c in Sources */,
836F6F8418BDC2190095E648 /* emff.c in Sources */, 836F6F8418BDC2190095E648 /* emff.c in Sources */,
836F704118BDC2190095E648 /* wii_str.c in Sources */,
836F6F8818BDC2190095E648 /* fsb.c in Sources */, 836F6F8818BDC2190095E648 /* fsb.c in Sources */,
836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */, 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */,
836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */, 836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */,
@ -2097,12 +2233,12 @@
836F6FC718BDC2190095E648 /* pona.c in Sources */, 836F6FC718BDC2190095E648 /* pona.c in Sources */,
8306B0B820984552000302D4 /* blocked_ws_aud.c in Sources */, 8306B0B820984552000302D4 /* blocked_ws_aud.c in Sources */,
836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */, 836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */,
836F6F5118BDC2190095E648 /* nolayout.c in Sources */,
83AA5D241F6E2F9C0020821C /* awc.c in Sources */, 83AA5D241F6E2F9C0020821C /* awc.c in Sources */,
8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */, 8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */,
8306B0E620984590000302D4 /* msb_msh.c in Sources */, 8306B0E620984590000302D4 /* msb_msh.c in Sources */,
836F702918BDC2190095E648 /* sat_sap.c in Sources */, 836F702918BDC2190095E648 /* sat_sap.c in Sources */,
836F6F3718BDC2190095E648 /* pcm_decoder.c in Sources */, 836F6F3718BDC2190095E648 /* pcm_decoder.c in Sources */,
834FE106215C79ED000A5D3D /* utk.c in Sources */,
831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */, 831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */,
836F700218BDC2190095E648 /* ps2_tk5.c in Sources */, 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */,
83AA5D271F6E2F9C0020821C /* stm.c in Sources */, 83AA5D271F6E2F9C0020821C /* stm.c in Sources */,
@ -2121,9 +2257,9 @@
831BA61F1EAC61A500CF89B0 /* x360_cxs.c in Sources */, 831BA61F1EAC61A500CF89B0 /* x360_cxs.c in Sources */,
836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */, 836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */,
8349A90B1FE6258200E26435 /* ps2_pcm.c in Sources */, 8349A90B1FE6258200E26435 /* ps2_pcm.c in Sources */,
836F6FFE18BDC2190095E648 /* ps2_str.c in Sources */,
836F6F4A18BDC2190095E648 /* interleave.c in Sources */, 836F6F4A18BDC2190095E648 /* interleave.c in Sources */,
83EDE5D81A70951A005F5D84 /* mca.c in Sources */, 83EDE5D81A70951A005F5D84 /* mca.c in Sources */,
834FE0F3215C79ED000A5D3D /* bnk_sony.c in Sources */,
8306B0AE20984552000302D4 /* blocked_filp.c in Sources */, 8306B0AE20984552000302D4 /* blocked_filp.c in Sources */,
831BA61B1EAC61A500CF89B0 /* sgxd.c in Sources */, 831BA61B1EAC61A500CF89B0 /* sgxd.c in Sources */,
838BDB6C1D3AFAB10022CA6F /* ffmpeg_decoder.c in Sources */, 838BDB6C1D3AFAB10022CA6F /* ffmpeg_decoder.c in Sources */,
@ -2131,20 +2267,22 @@
836F6FA518BDC2190095E648 /* nds_rrds.c in Sources */, 836F6FA518BDC2190095E648 /* nds_rrds.c in Sources */,
836F702F18BDC2190095E648 /* spt_spd.c in Sources */, 836F702F18BDC2190095E648 /* spt_spd.c in Sources */,
836F704618BDC2190095E648 /* x360_tra.c in Sources */, 836F704618BDC2190095E648 /* x360_tra.c in Sources */,
834FE0F0215C79ED000A5D3D /* apc.c in Sources */,
836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */, 836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */,
836F701918BDC2190095E648 /* psx_cdxa.c in Sources */, 836F701918BDC2190095E648 /* psx_cdxa.c in Sources */,
834D3A6E19F47C98001C54F6 /* g1l.c in Sources */, 834D3A6E19F47C98001C54F6 /* g1l.c in Sources */,
836F6FC318BDC2190095E648 /* pc_smp.c in Sources */, 836F6FC318BDC2190095E648 /* pc_smp.c in Sources */,
836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */, 836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */,
834FE0B4215C798C000A5D3D /* ffmpeg_decoder_custom_opus.c in Sources */,
8349A9091FE6258200E26435 /* pc_ast.c in Sources */, 8349A9091FE6258200E26435 /* pc_ast.c in Sources */,
8349A91C1FE6258200E26435 /* mogg.c in Sources */, 8349A91C1FE6258200E26435 /* mogg.c in Sources */,
834FE0F5215C79ED000A5D3D /* wv2.c in Sources */,
836F703D18BDC2190095E648 /* wii_mus.c in Sources */, 836F703D18BDC2190095E648 /* wii_mus.c in Sources */,
836F700D18BDC2190095E648 /* ps2_wmus.c in Sources */, 836F700D18BDC2190095E648 /* ps2_wmus.c in Sources */,
831BA61C1EAC61A500CF89B0 /* sxd.c in Sources */, 831BA61C1EAC61A500CF89B0 /* sxd.c in Sources */,
836F6F8018BDC2190095E648 /* dsp_bdsp.c in Sources */, 836F6F8018BDC2190095E648 /* dsp_bdsp.c in Sources */,
836F6F9618BDC2190095E648 /* lsf.c in Sources */, 836F6F9618BDC2190095E648 /* lsf.c in Sources */,
8306B0AB20984552000302D4 /* layered.c in Sources */, 8306B0AB20984552000302D4 /* layered.c in Sources */,
8374EE3E1F787AB600033E90 /* ffmpeg_decoder_utils_ea_xma.c in Sources */,
8306B0EC20984590000302D4 /* pcm_sre.c in Sources */, 8306B0EC20984590000302D4 /* pcm_sre.c in Sources */,
836F6FC818BDC2190095E648 /* pos.c in Sources */, 836F6FC818BDC2190095E648 /* pos.c in Sources */,
8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */, 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */,
@ -2155,6 +2293,7 @@
836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */, 836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */,
836F701218BDC2190095E648 /* ps3_ivag.c in Sources */, 836F701218BDC2190095E648 /* ps3_ivag.c in Sources */,
83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */, 83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */,
834FE0FE215C79ED000A5D3D /* ps_headerless.c in Sources */,
8306B0BB20984552000302D4 /* blocked_gsb.c in Sources */, 8306B0BB20984552000302D4 /* blocked_gsb.c in Sources */,
836F6F7718BDC2190095E648 /* capdsp.c in Sources */, 836F6F7718BDC2190095E648 /* capdsp.c in Sources */,
836F6FB018BDC2190095E648 /* ngc_dsp_ygo.c in Sources */, 836F6FB018BDC2190095E648 /* ngc_dsp_ygo.c in Sources */,
@ -2163,13 +2302,16 @@
836F703718BDC2190095E648 /* tun.c in Sources */, 836F703718BDC2190095E648 /* tun.c in Sources */,
836F700B18BDC2190095E648 /* ps2_wad.c in Sources */, 836F700B18BDC2190095E648 /* ps2_wad.c in Sources */,
8349A9161FE6258200E26435 /* flx.c in Sources */, 8349A9161FE6258200E26435 /* flx.c in Sources */,
834FE0BE215C79A9000A5D3D /* blocked_xa_aiff.c in Sources */,
831BA61E1EAC61A500CF89B0 /* vawx.c in Sources */, 831BA61E1EAC61A500CF89B0 /* vawx.c in Sources */,
836F702A18BDC2190095E648 /* sd9.c in Sources */, 836F702A18BDC2190095E648 /* sd9.c in Sources */,
836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */, 836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */,
836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */, 836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */,
836F6FF618BDC2190095E648 /* ps2_sfs.c in Sources */, 836F6FF618BDC2190095E648 /* ps2_sfs.c in Sources */,
834FE10E215C79ED000A5D3D /* ahv.c in Sources */,
8306B08420984518000302D4 /* at3plus_decoder.c in Sources */, 8306B08420984518000302D4 /* at3plus_decoder.c in Sources */,
8349A90E1FE6258200E26435 /* scd_pcm.c in Sources */, 8349A90E1FE6258200E26435 /* scd_pcm.c in Sources */,
834FE0F9215C79ED000A5D3D /* wavebatch.c in Sources */,
836F6F9518BDC2190095E648 /* kraw.c in Sources */, 836F6F9518BDC2190095E648 /* kraw.c in Sources */,
836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */, 836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */,
8306B0E920984590000302D4 /* opus.c in Sources */, 8306B0E920984590000302D4 /* opus.c in Sources */,
@ -2178,17 +2320,22 @@
83A21F86201D8981000F04B9 /* xwc.c in Sources */, 83A21F86201D8981000F04B9 /* xwc.c in Sources */,
8306B0A620984552000302D4 /* blocked_ea_wve_ad10.c in Sources */, 8306B0A620984552000302D4 /* blocked_ea_wve_ad10.c in Sources */,
83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */, 83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */,
834FE0FA215C79ED000A5D3D /* nus3bank.c in Sources */,
836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */, 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */,
834FE0B9215C798C000A5D3D /* xmd_decoder.c in Sources */,
8306B0B220984552000302D4 /* blocked_mxch.c in Sources */, 8306B0B220984552000302D4 /* blocked_mxch.c in Sources */,
836F6F8618BDC2190095E648 /* excitebots.c in Sources */, 836F6F8618BDC2190095E648 /* excitebots.c in Sources */,
836F6FF418BDC2190095E648 /* rws.c in Sources */, 836F6FF418BDC2190095E648 /* rws.c in Sources */,
834FE100215C79ED000A5D3D /* svg.c in Sources */,
836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */, 836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */,
836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */, 836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */,
836F6F3C18BDC2190095E648 /* xa_decoder.c in Sources */, 836F6F3C18BDC2190095E648 /* xa_decoder.c in Sources */,
834FE10A215C79ED000A5D3D /* nub_idsp.c in Sources */,
83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */, 83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */,
836F6F9118BDC2190095E648 /* ios_psnd.c in Sources */, 836F6F9118BDC2190095E648 /* ios_psnd.c in Sources */,
836F700618BDC2190095E648 /* ps2_vgs.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 */, 836F6FAA18BDC2190095E648 /* ngc_bh2pcm.c in Sources */,
831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */, 831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */,
836F6F3018BDC2190095E648 /* nds_procyon_decoder.c in Sources */, 836F6F3018BDC2190095E648 /* nds_procyon_decoder.c in Sources */,
@ -2208,12 +2355,14 @@
836F6F8E18BDC2190095E648 /* halpst.c in Sources */, 836F6F8E18BDC2190095E648 /* halpst.c in Sources */,
836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */, 836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */,
836F702618BDC2190095E648 /* s14_sss.c in Sources */, 836F702618BDC2190095E648 /* s14_sss.c in Sources */,
834FE102215C79ED000A5D3D /* rfrm.c in Sources */,
83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */,
8323894A1D22419B00482226 /* clHCA.c in Sources */, 8323894A1D22419B00482226 /* clHCA.c in Sources */,
836F702E18BDC2190095E648 /* sli.c in Sources */, 836F702E18BDC2190095E648 /* sli.c in Sources */,
836F701D18BDC2190095E648 /* raw.c in Sources */, 836F701D18BDC2190095E648 /* raw.c in Sources */,
836F6FDE18BDC2190095E648 /* ps2_ild.c in Sources */, 836F6FDE18BDC2190095E648 /* ps2_ild.c in Sources */,
836F703E18BDC2190095E648 /* wii_ras.c in Sources */, 836F703E18BDC2190095E648 /* wii_ras.c in Sources */,
834FE0EE215C79ED000A5D3D /* ue4opus.c in Sources */,
836F6FEA18BDC2190095E648 /* ps2_msa.c in Sources */, 836F6FEA18BDC2190095E648 /* ps2_msa.c in Sources */,
836F6F3618BDC2190095E648 /* ogg_vorbis_decoder.c in Sources */, 836F6F3618BDC2190095E648 /* ogg_vorbis_decoder.c in Sources */,
836F704718BDC2190095E648 /* xbox_hlwav.c in Sources */, 836F704718BDC2190095E648 /* xbox_hlwav.c in Sources */,
@ -2232,15 +2381,19 @@
836F6F3818BDC2190095E648 /* psx_decoder.c in Sources */, 836F6F3818BDC2190095E648 /* psx_decoder.c in Sources */,
836F6F2D18BDC2190095E648 /* mpeg_decoder.c in Sources */, 836F6F2D18BDC2190095E648 /* mpeg_decoder.c in Sources */,
836F6F2618BDC2190095E648 /* g7221_decoder.c in Sources */, 836F6F2618BDC2190095E648 /* g7221_decoder.c in Sources */,
834FE0F2215C79ED000A5D3D /* wv6.c in Sources */,
836F6FA218BDC2190095E648 /* naomi_adpcm.c in Sources */, 836F6FA218BDC2190095E648 /* naomi_adpcm.c in Sources */,
836F6FBF18BDC2190095E648 /* otm.c in Sources */, 836F6FBF18BDC2190095E648 /* otm.c in Sources */,
8306B0B420984552000302D4 /* blocked_tra.c in Sources */, 8306B0B420984552000302D4 /* blocked_tra.c in Sources */,
836F6FDD18BDC2190095E648 /* ps2_ikm.c in Sources */, 836F6FDD18BDC2190095E648 /* ps2_ikm.c in Sources */,
834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */,
8306B0B520984552000302D4 /* blocked_emff.c in Sources */, 8306B0B520984552000302D4 /* blocked_emff.c in Sources */,
836F6FBD18BDC2190095E648 /* nwa.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */,
834FE0F7215C79ED000A5D3D /* vis.c in Sources */,
83A21F8C201D8982000F04B9 /* kma9.c in Sources */, 83A21F8C201D8982000F04B9 /* kma9.c in Sources */,
8342469420C4D23000926E48 /* h4m.c in Sources */, 8342469420C4D23000926E48 /* h4m.c in Sources */,
836F6FC418BDC2190095E648 /* pc_snds.c in Sources */, 836F6FC418BDC2190095E648 /* pc_snds.c in Sources */,
834FE111215C79ED000A5D3D /* ck.c in Sources */,
836F704E18BDC2190095E648 /* xss.c in Sources */, 836F704E18BDC2190095E648 /* xss.c in Sources */,
836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */,
836F6F4118BDC2190095E648 /* blocked.c in Sources */, 836F6F4118BDC2190095E648 /* blocked.c in Sources */,
@ -2260,7 +2413,6 @@
836F705018BDC2190095E648 /* ydsp.c in Sources */, 836F705018BDC2190095E648 /* ydsp.c in Sources */,
8306B0B720984552000302D4 /* blocked_str_snds.c in Sources */, 8306B0B720984552000302D4 /* blocked_str_snds.c in Sources */,
836F702718BDC2190095E648 /* sat_baka.c in Sources */, 836F702718BDC2190095E648 /* sat_baka.c in Sources */,
83345A4A1F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c in Sources */,
836F704B18BDC2190095E648 /* xbox_xmu.c in Sources */, 836F704B18BDC2190095E648 /* xbox_xmu.c in Sources */,
832389501D2246C300482226 /* hca.c in Sources */, 832389501D2246C300482226 /* hca.c in Sources */,
836F701B18BDC2190095E648 /* psx_gms.c in Sources */, 836F701B18BDC2190095E648 /* psx_gms.c in Sources */,

File diff suppressed because it is too large Load diff

View file

@ -5,61 +5,82 @@
extern "C" { extern "C" {
#endif #endif
enum { clHCA_samplesPerBlock = 0x80 * 8 };
/* Must pass at least 8 bytes of data to this function. Returns -1 on non-match, or /* Must pass at least 8 bytes of data to this function.
* positive byte count on success. */ * Returns <0 on non-match, or header size on success. */
int clHCA_isOurFile0(const void *data); int clHCA_isOurFile(const void *data, unsigned int size);
/* Must pass a full header block for success. Returns 0 on success, -1 on failure. */
int clHCA_isOurFile1(const void *data, unsigned int size);
/* The opaque state structure. */ /* The opaque state structure. */
typedef struct clHCA clHCA; 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(); int clHCA_sizeof();
void clHCA_clear(clHCA *, unsigned int ciphKey1, unsigned int ciphKey2); void clHCA_clear(clHCA *);
void clHCA_done(clHCA *); void clHCA_done(clHCA *);
/* Or you could let the library allocate it. */ /* Or you could let the library allocate it. */
clHCA * clHCA_new(unsigned int ciphKey1, unsigned int ciphKey2); clHCA * clHCA_new();
void clHCA_delete(clHCA *); void clHCA_delete(clHCA *);
/* Requires a pre-allocated data structure. /* Parses the HCA header. Must be called before any decoding may be performed,
* Before any decoding may be performed, the header block must be passed in. * and size must be at least headerSize long. The recommended way is to detect
* The recommended way of doing this is to detect the header length with * the header length with clHCA_isOurFile, then read data and call this.
* clHCA_isOurFile0, validate the header with clHCA_isOurFile1, then pass * May be called multiple times to reset decoder state.
* it to this function, with the address of 0. * Returns 0 on success, <0 on failure. */
* Subsequent decodes with non-zero address are assumed to be sample blocks, int clHCA_DecodeHeader(clHCA *, void *data, unsigned int size);
* 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);
typedef struct clHCA_stInfo { typedef struct clHCA_stInfo {
unsigned int version; unsigned int version;
unsigned int dataOffset; unsigned int headerSize;
unsigned int samplingRate; unsigned int samplingRate;
unsigned int channelCount; unsigned int channelCount;
unsigned int blockSize; unsigned int blockSize;
unsigned int blockCount; unsigned int blockCount;
unsigned int encoderDelay; /* samples appended to the beginning */
unsigned int encoderPadding; /* samples appended to the end */
unsigned int loopEnabled; unsigned int loopEnabled;
unsigned int loopStart; unsigned int loopStartBlock;
unsigned int loopEnd; 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; 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; } clHCA_stInfo;
/* Retrieve information relevant for decoding and playback with this function. /* Retrieves header information for decoding and playback (it's the caller's responsability
* Must be called after successfully decoding a header block with clHCA_Decode, * to apply looping, encoder delay/skip samples, etc). May be called after clHCA_DecodeHeader.
* or else it will fail. * Returns 0 on success, <0 on failure. */
* Returns 0 on success, -1 on failure. */
int clHCA_getInfo(clHCA *, clHCA_stInfo *out); 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -63,7 +63,7 @@ int32_t SASSC_steps[256] =
-53261, -54797, -56333, -57870, -59406, -60942, -62479, 64015, -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; int i;
int32_t sample_count; int32_t sample_count;
int32_t hist = stream->adpcm_history1_32; int32_t hist = stream->adpcm_history1_32;

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "coding.h" #include "coding.h"
#include "../vgmstream.h" #include "acm_decoder_libacm.h"
#include "../streamtypes.h" #include <stdio.h>
#include "acm_decoder.h"
#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;
/************************************** static int acm_read_streamfile(void *ptr, int size, int n, void *arg);
* Stream processing 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. */ acm_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number) {
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* data = NULL; acm_codec_data* data = NULL;
ACMStream *acm_stream = NULL;
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
data = calloc(1,sizeof(acm_codec_data)); data = calloc(1,sizeof(acm_codec_data));
if (!data) goto fail; if (!data) goto fail;
streamFile->get_name(streamFile,filename,sizeof(filename)); data->io_config = calloc(1,sizeof(acm_io_config));
if (acm_open_decoder(&acm_stream,streamFile,filename) != ACM_OK) if (!data->io_config) goto fail;
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; return data;
@ -844,7 +61,7 @@ fail:
} }
void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing) { 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; int32_t samples_read = 0;
while (samples_read < samples_to_do) { 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) { void reset_acm(acm_codec_data *data) {
acm_codec_data *data = vgmstream->codec_data; if (!data || !data->handle)
return;
if (data && data->file) { acm_seek_pcm(data->handle, 0);
acm_reset(data->file);
}
} }
void free_acm(acm_codec_data *data) { void free_acm(acm_codec_data *data) {
if (data) { if (!data)
if (data->file) { return;
acm_close(data->file);
} acm_close(data->handle);
close_streamfile(data->streamfile);
free(data->io_config);
free(data); 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);
}

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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);
}

View file

@ -1,10 +1,9 @@
/* /*
* libacm - Interplay ACM audio decoder. * libacm - Interplay ACM audio decoder.
* *
* Copyright (c) 2004-2008, Marko Kreen * Copyright (c) 2004-2010, Marko Kreen
* Copyright (c) 2008, Adam Gashlin
* *
* 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 * purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies. * copyright notice and this permission notice appear in all copies.
* *
@ -20,18 +19,11 @@
#ifndef __LIBACM_H #ifndef __LIBACM_H
#define __LIBACM_H #define __LIBACM_H
#ifdef BUILD_VGMSTREAM #define LIBACM_VERSION "1.1"
#include "../streamfile.h"
#else
#include "streamfile.h"
#endif
#define LIBACM_VERSION "1.0-svn"
#define ACM_ID 0x032897 #define ACM_ID 0x032897
#define ACM_WORD 2 #define ACM_WORD 2
#define ACM_HEADER_LEN 14
#define ACM_OK 0 #define ACM_OK 0
#define ACM_ERR_OTHER -1 #define ACM_ERR_OTHER -1
#define ACM_ERR_OPEN -2 #define ACM_ERR_OPEN -2
@ -47,21 +39,35 @@ typedef struct ACMInfo {
unsigned rate; unsigned rate;
unsigned acm_id; unsigned acm_id;
unsigned acm_version; unsigned acm_version;
unsigned acm_channels; /* channels from header (usually wrong) */
unsigned acm_level; unsigned acm_level;
unsigned acm_cols; /* 1 << acm_level */ unsigned acm_cols; /* 1 << acm_level */
unsigned acm_rows; unsigned acm_rows;
} ACMInfo; } 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 { struct ACMStream {
ACMInfo info; ACMInfo info;
unsigned total_values; unsigned total_values;
/* acm data stream */ /* acm data stream */
STREAMFILE *streamfile; void *io_arg;
acm_io_callbacks io;
unsigned data_len; unsigned data_len;
/* acm stream buffer */ /* acm stream buffer */
unsigned bit_avail; unsigned char *buf;
unsigned buf_max, buf_size, buf_pos, bit_avail;
unsigned bit_data; unsigned bit_data;
unsigned buf_start_ofs; unsigned buf_start_ofs;
@ -76,16 +82,36 @@ struct ACMStream {
/* result */ /* result */
unsigned block_ready:1; unsigned block_ready:1;
unsigned file_eof:1; unsigned file_eof:1;
unsigned wavc_file:1;
unsigned stream_pos; /* in words. absolute */ unsigned stream_pos; /* in words. absolute */
unsigned block_pos; /* in words, relative */ unsigned block_pos; /* in words, relative */
}; };
typedef struct ACMStream ACMStream; typedef struct ACMStream ACMStream;
/* decode.c */ /* 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 acm_read(ACMStream *acm, void *buf, unsigned nbytes,
int bigendianp, int wordlen, int sgned); int bigendianp, int wordlen, int sgned);
void acm_close(ACMStream *acm); 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 #endif

View file

@ -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 <stdio.h>
#include <string.h>
#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;
}

View file

@ -1,13 +1,13 @@
#include "coding.h" #include "coding.h"
/* Decodec Argonaut's ASF ADPCM codec. Algorithm follows Croc2_asf2raw.exe, and the waveform /* Decodes Argonaut's ASF ADPCM codec, used in some of their PC games.
* looks almost correct, but should reverse engineer asfcodec.adl (DLL) for accuracy. */ * 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) { void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
off_t frame_offset; off_t frame_offset;
int i, frames_in, sample_count = 0; int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame; 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 hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_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; frames_in = first_sample / samples_per_frame;
first_sample = 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; frame_offset = stream->offset + bytes_per_frame*frames_in;
shift = (read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; shift = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf;
mode = (read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 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++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample; int32_t new_sample;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile); 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 */ new_sample = i&1 ? /* high nibble first */
get_low_nibble_signed(nibbles): get_low_nibble_signed(nibbles):
get_high_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) { /* mode is checked as a flag, so there are 2 modes only, but lower nibble
case 0x00: * may have other values at last frame (ex 0x02/09), could be control flags (loop related?) */
new_sample = new_sample + hist1; if (mode & 0x4) { /* ~filters: 2, -1 */
//new_sample = (new_sample + (hist1 << 6)) >> 6; /* maybe? */ new_sample = (new_sample + (hist1 << 7) - (hist2 << 6)) >> 6;
break; }
else { /* ~filters: 1, 0 */
case 0x04: new_sample = (new_sample + (hist1 << 6)) >> 6;
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;
} }
//new_sample = clamp16(new_sample); /* must not */ //new_sample = clamp16(new_sample); /* must not */
new_sample = new_sample & 0xFFFF; /* probably unnecessary */ outbuf[sample_count] = (int16_t)new_sample;
outbuf[sample_count] = new_sample;
sample_count += channelspacing; sample_count += channelspacing;
hist2 = hist1; hist2 = hist1;

View file

@ -4,13 +4,31 @@
#include <libatrac9/libatrac9.h> #include <libatrac9/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) { atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
int status; int status;
uint8_t config_data[4]; uint8_t config_data[4];
Atrac9CodecInfo info = {0};
atrac9_codec_data *data = NULL; atrac9_codec_data *data = NULL;
data = calloc(1, sizeof(atrac9_codec_data)); data = calloc(1, sizeof(atrac9_codec_data));
if (!data) goto fail;
data->handle = Atrac9GetHandle(); data->handle = Atrac9GetHandle();
if (!data->handle) goto fail; if (!data->handle) goto fail;
@ -19,20 +37,20 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
status = Atrac9InitDecoder(data->handle, config_data); status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail; if (status < 0) goto fail;
status = Atrac9GetCodecInfo(data->handle, &info); status = Atrac9GetCodecInfo(data->handle, &data->info);
if (status < 0) goto fail; 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); //;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) { if (cfg->channels && cfg->channels != data->info.channels) {
VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, 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 */ goto fail; /* unknown multichannel layout */
} }
/* must hold at least one superframe and its samples */ /* 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->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; data->samples_to_discard = cfg->encoder_delay;
@ -41,6 +59,7 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
return data; return data;
fail: fail:
free_atrac9(data);
return NULL; return NULL;
} }
@ -82,62 +101,25 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do,
int bytes_used = 0; int bytes_used = 0;
uint8_t *buffer = data->data_buffer; uint8_t *buffer = data->data_buffer;
size_t bytes; size_t bytes;
Atrac9CodecInfo info = {0};
data->samples_used = 0; data->samples_used = 0;
/* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives /* 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). */ * 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 */ /* read one raw block (superframe) and advance offsets */
bytes = read_streamfile(data->data_buffer,stream->offset, info.superframeSize,stream->streamfile); bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile);
if (bytes != data->data_buffer_size) { if (bytes != data->data_buffer_size) goto decode_fail;
VGM_LOG("ATRAC9: read %x vs expected %x bytes at %lx\n", bytes, info.superframeSize, stream->offset);
goto decode_fail;
}
stream->offset += bytes; 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 */ /* 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); status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used);
if (status < 0) goto decode_fail; if (status < 0) goto decode_fail;
buffer += bytes_used; 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: decode_fail:
/* on error just put some 0 samples */ /* 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); 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_used = 0;
data->samples_filled = 0; data->samples_filled = 0;
data->samples_to_discard = 0; data->samples_to_discard = data->config.encoder_delay;
return; return;
@ -189,12 +171,43 @@ void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) {
reset_atrac9(vgmstream); 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 = num_sample;
data->samples_to_discard += data->config.encoder_delay; data->samples_to_discard += data->config.encoder_delay;
/* loop offsets are set during decode; force them to stream start so discard works */ /* loop offsets are set during decode; force them to stream start so discard works */
if (vgmstream->loop_ch) if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
#endif
} }
void free_atrac9(atrac9_codec_data *data) { 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) { size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) {
Atrac9CodecInfo info = {0}; return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe);
int status; }
status = Atrac9GetCodecInfo(data->handle, &info); #if 0 //not needed (for now)
if (status < 0) goto fail; 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: fail:
return 0; return 0;
} }
#endif #endif
#endif

View file

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

View file

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

View file

@ -15,24 +15,28 @@ void decode_g721(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
void g72x_init_state(struct g72x_state *state_ptr); void g72x_init_state(struct g72x_state *state_ptr);
/* ima_decoder */ /* 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_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_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(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_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_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_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_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_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_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 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 ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels); size_t 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); void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* pcm_decoder */ /* pcm_decoder */
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);
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);
void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); 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(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_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(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(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_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); 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); size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
/* psx_decoder */ /* psx_decoder */
void decode_psx(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_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); void decode_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_bytes_to_samples(size_t bytes, int channels);
size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, 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 */ /* 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); size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked);
/* ea_xa_decoder */ /* 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); void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* acm_decoder */ /* 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 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); void free_acm(acm_codec_data *data);
/* nwa_decoder */ /* nwa_decoder */
@ -113,7 +120,8 @@ void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do);
/* msadpcm_decoder */ /* msadpcm_decoder */
void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do); 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); long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
/* yamaha_decoder */ /* yamaha_decoder */
@ -129,14 +137,14 @@ void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels
/* l5_555_decoder */ /* l5_555_decoder */
void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* SASSC_decoder */ /* sassc_decoder */
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);
/* lsf_decode */ /* lsf_decode */
void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* mtaf_decoder */ /* 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 */ /* mta2_decoder */
void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); 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 */ /* asf_decoder */
void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); 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_decoder*/
ea_mt_codec_data *init_ea_mt(int channel_count, int type); ea_mt_codec_data *init_ea_mt(int channels, 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_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 reset_ea_mt(VGMSTREAM * vgmstream);
void flush_ea_mt(VGMSTREAM *vgmstream); void flush_ea_mt(VGMSTREAM *vgmstream);
void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample); 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 */ /* hca_decoder */
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); hca_codec_data *init_hca(STREAMFILE *streamFile);
void reset_hca(VGMSTREAM *vgmstream); void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do);
void loop_hca(VGMSTREAM *vgmstream); void reset_hca(hca_codec_data * data);
void loop_hca(hca_codec_data * data);
void free_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 #ifdef VGM_USE_VORBIS
/* ogg_vorbis_decoder */ /* ogg_vorbis_decoder */
@ -204,8 +222,8 @@ void free_g7221(VGMSTREAM *vgmstream);
/* g719_decoder */ /* g719_decoder */
g719_codec_data *init_g719(int channel_count, int frame_size); 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 decode_g719(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel);
void reset_g719(VGMSTREAM *vgmstream); void reset_g719(g719_codec_data * data, int channels);
void free_g719(VGMSTREAM *vgmstream); void free_g719(g719_codec_data * data, int channels);
#endif #endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #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 seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample);
void free_atrac9(atrac9_codec_data *data); void free_atrac9(atrac9_codec_data *data);
size_t atrac9_bytes_to_samples(size_t bytes, 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 #endif
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
/* ffmpeg_decoder */ /* ffmpeg_decoder */
ffmpeg_codec_data *init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); ffmpeg_codec_data *init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
ffmpeg_codec_data *init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size); ffmpeg_codec_data *init_ffmpeg_header_offset(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 decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels);
void reset_ffmpeg(VGMSTREAM *vgmstream); 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); 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_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 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 #endif
/* coding_utils */ /* coding_utils */
int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, size_t chunk_size, uint16_t codec); 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); 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 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_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int * loop_flag, int32_t * out_num_samples, int32_t * out_loop_start_sample, int32_t * out_loop_end_sample, int be);
void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample); void 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 atrac3_bytes_to_samples(size_t bytes, int full_block_align);
size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align); size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align);

View file

@ -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) */ /* full packet skip, no new frames start in this packet (prev frames can end here) */
if (packet_skip_count == 0x7FF) { /* XMA1, 11b */ 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; packet_skip_count = 0;
continue; continue;
} }
else if (packet_skip_count == 0xFF) { /* XMA2, 8b*/ 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; packet_skip_count = 0;
continue; continue;
} }
offset += packet_size * (packet_skip_count); /* skip packets not owned by the first stream, since we only need samples from it */ 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 */ /* 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; 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; frame_offset_b += 1;
if (flag) { if (flag) {
int new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile); 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 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; 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; frame_offset_b += 1;
if (flag) { if (flag) {
int new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile); 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 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; 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; 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 */ /* OTHER STUFF */

View file

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

View file

@ -1,17 +1,15 @@
#include "coding.h" #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 * 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). * with different encoding parameters. Later revisions may have PCM blocks (rare).
* *
* Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode * Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode
* Info: http://wiki.niotso.org/UTK * 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_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)) #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_MAX(x,y) ((x)>(y)?(x):(y))
#define UTK_CLAMP(x,min,max) UTK_MIN(UTK_MAX(x,min),max) #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. struct ea_mt_codec_data {
** This matters when pitch_lag > 216 on the first subframe of any given frame. */ STREAMFILE *streamfile;
typedef struct UTKContext { uint8_t buffer[UTK_BUFFER_SIZE];
uint8_t buffer[UTK_BUFFER_SIZE]; //vgmstream extra off_t offset;
STREAMFILE * streamfile; //vgmstream extra off_t loop_offset;
off_t offset; //vgmstream extra int loop_sample;
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;
enum { int pcm_blocks;
MDL_NORMAL = 0, int samples_filled;
MDL_LARGEPULSE = 1 int samples_used;
int samples_done;
int samples_discard;
void* utk_context;
}; };
static const float utk_rc_table[64] = { static size_t ea_mt_read_callback(void *dest, int size, void *arg);
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 const uint8_t utk_codebooks[2][256] = { ea_mt_codec_data *init_ea_mt(int channels, int pcm_blocks) {
{ /* normal model */ return init_ea_mt_loops(channels, pcm_blocks, 0, NULL);
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;
} }
static int16_t utk_read_i16(UTKContext *ctx) ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) {
{
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 *data = NULL; ea_mt_codec_data *data = NULL;
int i; 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; if (!data) goto fail;
data->pcm_blocks = pcm_blocks; for (i = 0; i < channels; i++) {
data->utk_context_size = channel_count; 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*)); data[i].pcm_blocks = pcm_blocks;
if (!data->utk_context) goto fail; data[i].loop_sample = loop_sample;
if (loop_offsets)
data[i].loop_offset = loop_offsets[i];
for (i = 0; i < channel_count; i++) { utk_set_callback(data[i].utk_context, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback);
data->utk_context[i] = calloc(1, sizeof(UTKContext));
if (!data->utk_context[i]) goto fail;
utk_init(data->utk_context[i]);
} }
return data; return data;
fail: fail:
free_ea_mt(data); free_ea_mt(data, channels);
return NULL; 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; ea_mt_codec_data *data = vgmstream->codec_data;
int i, sample_count = 0, frame_samples; ea_mt_codec_data *ch_data = &data[channel];
UTKContext* ctx = data->utk_context[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; while (samples_done < samples_to_do) {
first_sample = first_sample % frame_samples;
/* don't decode again if we didn't consume the current frame. if (ch_data->samples_filled) {
* UTKContext saves the sample buffer, and can't re-decode a frame */ /* consume current frame */
if (!ctx->samples_filled) { int samples_to_get = ch_data->samples_filled;
if (data->pcm_blocks)
/* 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); utk_rev3_decode_frame(ctx);
else else
utk_decode_frame(ctx); utk_decode_frame(ctx);
ctx->samples_filled = 1;
}
/* copy samples */ ch_data->samples_used = 0;
for (i = first_sample; i < first_sample+samples_to_do; i++) { ch_data->samples_filled = 432;
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; ea_mt_codec_data *data = vgmstream->codec_data;
int i; int i;
size_t bytes;
if (!data) return; 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++) { 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; data[i].streamfile = vgmstream->ch[i].streamfile; /* maybe should keep its own STREAMFILE? */
ctx->offset = is_start ? vgmstream->ch[i].channel_start_offset : vgmstream->ch[i].offset; if (is_start)
ctx->samples_filled = 0; 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); if (is_start) {
ctx->offset += sizeof(ctx->buffer); utk_reset(ctx);
ctx->ptr = ctx->buffer; ctx->parsed_header = 0;
ctx->end = ctx->buffer + bytes; data[i].samples_done = 0;
ctx->bits_count = 0; }
data[i].samples_filled = 0;
data[i].samples_discard = samples_discard;
} }
} }
void flush_ea_mt(VGMSTREAM *vgmstream) { 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) { 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) { void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample) {
flush_ea_mt_internal(vgmstream, 1); flush_ea_mt_offsets(vgmstream, 1, num_sample);
//todo discard loop (though this should be adecuate as probably only uses full loops, if at all)
} }
void free_ea_mt(ea_mt_codec_data *data) { void free_ea_mt(ea_mt_codec_data *data, int channels) {
int i; int i;
if (!data) if (!data)
return; return;
for (i = 0; i < data->utk_context_size; i++) { for (i = 0; i < channels; i++) {
free(data->utk_context[i]); free(data[i].utk_context);
} }
free(data->utk_context);
free(data); 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;
}

View file

@ -0,0 +1,469 @@
#ifndef _EA_MT_DECODER_UTK_H_
#define _EA_MT_DECODER_UTK_H_
#include <stdint.h>
#include <string.h>
/* 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_ */

View file

@ -1,10 +1,9 @@
#include "coding.h" #include "coding.h"
#include "ffmpeg_decoder_utils.h"
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
/* internal sizes, can be any value */ /* 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 #define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024
@ -24,7 +23,7 @@ static void g_init_ffmpeg() {
g_ffmpeg_initialized = 1; g_ffmpeg_initialized = 1;
av_log_set_flags(AV_LOG_SKIP_REPEATED); av_log_set_flags(AV_LOG_SKIP_REPEATED);
av_log_set_level(AV_LOG_ERROR); av_log_set_level(AV_LOG_ERROR);
av_register_all(); //av_register_all(); /* not needed in newer versions */
g_ffmpeg_initialized = 2; g_ffmpeg_initialized = 2;
} }
} }
@ -185,53 +184,38 @@ fail:
/* ******************************************** */ /* ******************************************** */
/* AVIO callback: read stream, handling custom data */ /* 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; ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque;
int ret = 0; int bytes = 0;
int max_to_copy = 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 */ /* clamp reads */
if (data->virtual_offset + buf_size > data->virtual_size) if (data->logical_offset + read_size > data->logical_size)
buf_size = data->virtual_size - data->virtual_offset; read_size = data->logical_size - data->logical_offset;
if (buf_size == 0) if (read_size == 0)
return ret; return bytes;
/* handle reads on inserted header */ /* handle reads on inserted header */
if (data->header_size) { if (data->header_size && data->logical_offset < data->header_size) {
if (data->virtual_offset < data->header_size) { max_to_copy = (int)(data->header_size - data->logical_offset);
max_to_copy = (int)(data->header_size - data->virtual_offset); if (max_to_copy > read_size)
if (max_to_copy > buf_size) { max_to_copy = read_size;
max_to_copy = buf_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->logical_offset, max_to_copy);
memcpy(buf, data->header_insert_block + data->virtual_offset, max_to_copy);
buf += max_to_copy; buf += max_to_copy;
buf_size -= max_to_copy; read_size -= max_to_copy;
data->virtual_offset += max_to_copy; /* adjust for reads below */ data->logical_offset += max_to_copy;
if (buf_size == 0) { if (read_size == 0) {
return max_to_copy; /* offset still in header */ return max_to_copy; /* offset still in header */
} }
} }
}
/* main read */ /* main read */
switch(data->config.type) { bytes = read_streamfile(buf, data->offset, read_size, data->streamfile);
case FFMPEG_EA_XMA: ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break; data->logical_offset += bytes;
case FFMPEG_SWITCH_OPUS: ret = ffmpeg_custom_read_switch_opus(data, buf, buf_size); break; data->offset += bytes;
//case FFMPEG_EA_SCHL: ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break; return bytes + max_to_copy;
//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;
} }
/* AVIO callback: write stream not needed */ /* 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 */ /* get cache'd size */
if (whence & AVSEEK_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->logical_size;
return data->virtual_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); whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE);
/* find the final offset FFmpeg sees (within fake header + virtual size) */ /* find the final offset FFmpeg sees (within fake header + virtual size) */
switch (whence) { switch (whence) {
case SEEK_SET: /* absolute */ case SEEK_SET: /* absolute */
break; break;
case SEEK_CUR: /* relative to current */ case SEEK_CUR: /* relative to current */
offset += data->virtual_offset; offset += data->logical_offset;
break; break;
case SEEK_END: /* relative to file end (should be negative) */ case SEEK_END: /* relative to file end (should be negative) */
offset += data->virtual_size; offset += data->logical_size;
break; break;
} }
/* clamp offset; fseek does this too */ /* clamp offset; fseek does this too */
if (offset > data->virtual_size) if (offset > data->logical_size)
offset = data->virtual_size; offset = data->logical_size;
else if (offset < 0) else if (offset < 0)
offset = 0; offset = 0;
/* no change */
if (data->virtual_offset == offset) {
return ret;
}
/* seeks inside fake header */ /* seeks inside fake header */
if (offset < data->header_size) { if (offset < data->header_size) {
data->virtual_offset = offset; data->logical_offset = offset;
data->real_offset = data->real_start; data->offset = data->start;
return ret; return ret;
} }
/* main seek */ /* main seek */
switch(data->config.type) { data->logical_offset = offset;
case FFMPEG_EA_XMA: offset = ffmpeg_custom_seek_eaxma(data, offset); break; data->offset = data->start + (offset - data->header_size);
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);
return ret; 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 */ /* MAIN INIT/DECODER */
/* ******************************************** */ /* ******************************************** */
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { 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. * 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'. * 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. * 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. * 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]; char filename[PATH_LIMIT];
ffmpeg_codec_data * data; ffmpeg_codec_data * data;
int errcode, i; int errcode, i;
int targetSubsong = streamFile->stream_index;
int streamIndex, streamCount; int streamIndex, streamCount;
AVStream *stream; AVStream *stream;
AVCodecParameters *codecPar = NULL; AVCodecParameters *codecPar = NULL;
AVRational tb; AVRational tb;
@ -356,14 +303,9 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header,
if (!data) return NULL; if (!data) return NULL;
streamFile->get_name( streamFile, filename, sizeof(filename) ); streamFile->get_name( streamFile, filename, sizeof(filename) );
data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!data->streamfile) goto fail; if (!data->streamfile) goto fail;
if (config) {
memcpy(&data->config, config, sizeof(ffmpeg_custom_config));
}
/* ignore bad combos */ /* ignore bad combos */
if ((header && !header_size) || (!header && header_size)) if ((header && !header_size) || (!header && header_size))
goto fail; goto fail;
@ -375,13 +317,15 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header,
if (!data->header_insert_block) goto fail; if (!data->header_insert_block) goto fail;
} }
data->real_start = start; data->start = start;
data->real_offset = data->real_start; data->offset = start;
data->real_size = size; data->size = size;
data->virtual_offset = 0; if (data->size == 0 || data->start + data->size > get_streamfile_size(streamFile)) {
data->virtual_size = ffmpeg_size(data); VGM_LOG("FFmpeg: wrong start+size found\n");
data->virtual_base = 0; data->size = get_streamfile_size(streamFile) - data->start;
if (data->virtual_size == 0) goto fail; }
data->logical_offset = 0;
data->logical_size = data->header_size + data->size;
/* setup IO, attempt to autodetect format and gather some info */ /* setup IO, attempt to autodetect format and gather some info */
data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE); 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++; streamCount++;
/* select Nth audio stream if specified, or first one */ /* 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; codecPar = stream->codecpar;
streamIndex = i; streamIndex = i;
} }
@ -420,7 +364,7 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header,
if (i != streamIndex) if (i != streamIndex)
stream->discard = AVDISCARD_ALL; /* disable demuxing for other streams */ 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; if (streamIndex < 0 || !codecPar) goto fail;
data->streamIndex = streamIndex; 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; 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); data->codec = avcodec_find_decoder(data->codecCtx->codec_id);
if (!data->codec) goto fail; 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); data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
/* setup decode buffer */ /* 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 ); data->sampleBuffer = av_malloc( data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels );
if (!data->sampleBuffer) if (!data->sampleBuffer)
goto fail; goto fail;

View file

@ -0,0 +1,537 @@
#include "coding.h"
#include "../streamfile.h"
#include <string.h>
/**
* 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<bytes;++i)
crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 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<<audiosize)/400;
} else if ((data[0]&0x60) == 0x60)
{
audiosize = (data[0]&0x08) ? Fs/50 : Fs/100;
} else {
audiosize = ((data[0]>>3)&0x3);
if (audiosize == 3)
audiosize = Fs*60/1000;
else
audiosize = (Fs<<audiosize)/100;
}
return audiosize;
}
/* from opus_decoder.c's opus_packet_get_nb_frames */
static int opus_packet_get_nb_frames(const uint8_t * packet, int len) {
int count;
if (len<1)
return 0;
count = packet[0]&0x3;
if (count==0)
return 1;
else if (count!=3)
return 2;
else if (len<2)
return 0;
else
return packet[1]&0x3F;
}
/* ******************************** */
static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule) {
size_t page_done, lacing_done = 0;
uint64_t absolute_granule = granule; /* wrong values seem validated (0, less than real samples, etc) */
int header_type_flag = (page_sequence==0 ? 2 : 0);
int stream_serial_number = 0x7667; /* 0 is legal, but should be specified */
int checksum = 0;
int segment_count;
if (0x1b + (data_size/0xFF + 1) + data_size > 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

View file

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

View file

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

View file

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

View file

@ -1,362 +0,0 @@
#include "coding.h"
#include "ffmpeg_decoder_utils.h"
#include <string.h>
#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<bytes;++i)
crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 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<<audiosize)/400;
} else if ((data[0]&0x60) == 0x60)
{
audiosize = (data[0]&0x08) ? Fs/50 : Fs/100;
} else {
audiosize = ((data[0]>>3)&0x3);
if (audiosize == 3)
audiosize = Fs*60/1000;
else
audiosize = (Fs<<audiosize)/100;
}
return audiosize;
}
static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule) {
size_t page_done, lacing_done = 0;
uint64_t absolute_granule = granule; /* seem wrong values matter for Opus (0, less than real samples, etc) */
int header_type_flag = (page_sequence==0 ? 2 : 0);
int stream_serial_number = 0x7667; /* 0 is legal, but should be specified */
int checksum = 0;
int segment_count;
if (0x1b + (data_size/0xFF + 1) + data_size > 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

View file

@ -1,19 +1,28 @@
#include "coding.h" #include "coding.h"
#include "../util.h"
#ifdef VGM_USE_G719 #ifdef VGM_USE_G719
#include "../stack_alloc.h" #include <g719/g719.h>
#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) { g719_codec_data *init_g719(int channel_count, int frame_size) {
int i; int i;
g719_codec_data *data = NULL; 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 */ data = calloc(channel_count, sizeof(g719_codec_data)); /* one decoder per channel */
if (!data) goto fail; if (!data) goto fail;
for (i = 0; i < channel_count; i++) { for (i = 0; i < channel_count; i++) {
data[i].handle = g719_init(frame_size); /* Siren 22 == 22khz bandwidth */ data[i].handle = g719_init(frame_size); /* Siren 22 == 22khz bandwidth */
if (!data[i].handle) goto fail; 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; return data;
@ -29,51 +38,42 @@ fail:
return NULL; return NULL;
} }
void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) { void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) {
VGMSTREAMCHANNEL *ch = &vgmstream->ch[channel]; VGMSTREAMCHANNEL *ch = &vgmstream->ch[channel];
g719_codec_data *data = vgmstream->codec_data; g719_codec_data *data = vgmstream->codec_data;
g719_codec_data *ch_data = &data[channel]; g719_codec_data *ch_data = &data[channel];
int i; int i;
if (0 == vgmstream->samples_into_block) if (0 == vgmstream->samples_into_block) {
{ read_streamfile((uint8_t*)ch_data->code_buffer, ch->offset, vgmstream->interleave_block_size, ch->streamfile);
VARDECL(int16_t,code_buffer); g719_decode_frame(ch_data->handle, ch_data->code_buffer, ch_data->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);
} }
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]; outbuf[i*channelspacing] = ch_data->buffer[vgmstream->samples_into_block+i];
} }
} }
void reset_g719(VGMSTREAM *vgmstream) { void reset_g719(g719_codec_data * data, int channels) {
g719_codec_data *data = vgmstream->codec_data;
int i; int i;
if (!data) return; if (!data) return;
for (i = 0; i < vgmstream->channels; i++) for (i = 0; i < channels; i++) {
{
g719_reset(data[i].handle); g719_reset(data[i].handle);
} }
} }
void free_g719(VGMSTREAM *vgmstream) { void free_g719(g719_codec_data * data, int channels) {
g719_codec_data *data = (g719_codec_data *) vgmstream->codec_data;
if (data)
{
int i; int i;
if (!data) return;
for (i = 0; i < vgmstream->channels; i++) for (i = 0; i < channels; i++) {
{
g719_free(data[i].handle); g719_free(data[i].handle);
free(data[i].code_buffer);
} }
free(data); free(data);
}
} }
#endif #endif

View file

@ -1,12 +1,16 @@
#include "coding.h" #include "coding.h"
#include "../util.h"
#ifdef VGM_USE_G7221 #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) { g7221_codec_data * init_g7221(int channel_count, int frame_size) {
int i; int i;
g7221_codec_data *data = NULL; 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 */ data = calloc(channel_count, sizeof(g7221_codec_data)); /* one decoder per channel */
if (!data) goto fail; 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]; g7221_codec_data *ch_data = &data[channel];
int i; int i;
if (0 == vgmstream->samples_into_block) if (0 == vgmstream->samples_into_block) {
{ int16_t code_buffer[G7221_MAX_CODES];
int16_t code_buffer[960/8];
vgmstream->ch[channel].streamfile->read(ch->streamfile, (uint8_t*)code_buffer, ch->offset, vgmstream->interleave_block_size); 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); 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]; outbuf[i*channelspacing] = ch_data->buffer[vgmstream->samples_into_block+i];
} }
} }
@ -54,25 +56,20 @@ void reset_g7221(VGMSTREAM *vgmstream) {
int i; int i;
if (!data) return; if (!data) return;
for (i = 0; i < vgmstream->channels; i++) for (i = 0; i < vgmstream->channels; i++) {
{
g7221_reset(data[i].handle); g7221_reset(data[i].handle);
} }
} }
void free_g7221(VGMSTREAM *vgmstream) { void free_g7221(VGMSTREAM *vgmstream) {
g7221_codec_data *data = (g7221_codec_data *) vgmstream->codec_data; g7221_codec_data *data = (g7221_codec_data *) vgmstream->codec_data;
if (data)
{
int i; int i;
if (!data) return;
for (i = 0; i < vgmstream->channels; i++) for (i = 0; i < vgmstream->channels; i++) {
{
g7221_free(data[i].handle); g7221_free(data[i].handle);
} }
free(data); free(data);
}
} }
#endif #endif

View file

@ -1,110 +1,208 @@
#include "coding.h" #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; int samples_done = 0;
int32_t samples_remain = clHCA_samplesPerBlock - data->sample_ptr; const unsigned int channels = data->info.channelCount;
void *hca_data = NULL;
clHCA *hca;
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;
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);
while ( samples_done < samples_to_do ) {
const unsigned int blockSize = data->info.blockSize; 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 ( read_streamfile((uint8_t*) hca_data, data->start + address, blockSize, data->streamfile) != blockSize ) while (samples_done < samples_to_do) {
break;
if ( clHCA_Decode( hca, hca_data, blockSize, address ) < 0 ) if (data->samples_filled) {
break; int samples_to_get = data->samples_filled;
++data->curblock; if (data->samples_to_discard) {
/* discard samples for looping */
clHCA_DecodeSamples16( hca, data->sample_buffer ); if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
samples_remain = clHCA_samplesPerBlock; data->samples_to_discard -= samples_to_get;
data->sample_ptr = 0;
if ( data->samples_discard ) {
if ( samples_remain <= data->samples_discard ) {
data->samples_discard -= samples_remain;
samples_remain = 0;
} }
else { else {
samples_remain -= data->samples_discard; /* get max samples and copy */
data->sample_ptr = data->samples_discard; if (samples_to_get > samples_to_do - samples_done)
data->samples_discard = 0; 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;
} }
if ( samples_remain > samples_to_do - samples_done ) samples_remain = samples_to_do - samples_done; /* mark consumed samples */
memcpy( outbuf, data->sample_buffer, samples_remain * channelCount * sizeof(sample) ); data->samples_consumed += samples_to_get;
samples_done += samples_remain; data->samples_filled -= samples_to_get;
outbuf += samples_remain * channelCount; }
data->sample_ptr = samples_remain; else {
off_t offset = data->info.headerSize + data->current_block * blockSize;
int status;
size_t bytes;
/* EOF/error */
if (data->current_block >= data->info.blockCount) {
memset(outbuf, 0, (samples_to_do - samples_done) * channels * sizeof(sample));
break;
} }
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(hca_codec_data * data) {
void reset_hca(VGMSTREAM *vgmstream) {
hca_codec_data *data = vgmstream->codec_data;
if (!data) return; if (!data) return;
data->curblock = 0; data->current_block = 0;
data->sample_ptr = clHCA_samplesPerBlock; data->samples_filled = 0;
data->samples_discard = 0; data->samples_consumed = 0;
data->samples_to_discard = data->info.encoderDelay;
} }
void loop_hca(VGMSTREAM *vgmstream) { void loop_hca(hca_codec_data * data) {
hca_codec_data *data = (hca_codec_data *)(vgmstream->codec_data);
if (!data) return; if (!data) return;
data->curblock = data->info.loopStart; data->current_block = data->info.loopStartBlock;
data->sample_ptr = clHCA_samplesPerBlock; data->samples_filled = 0;
data->samples_discard = 0; data->samples_consumed = 0;
data->samples_to_discard = data->info.loopStartDelay;
} }
void free_hca(hca_codec_data * data) { void free_hca(hca_codec_data * data) {
if (data) { if (!data) return;
clHCA *hca = (clHCA *)(data + 1);
clHCA_done(hca);
if (data->streamfile)
close_streamfile(data->streamfile); close_streamfile(data->streamfile);
clHCA_done(data->handle);
free(data->handle);
free(data->data_buffer);
free(data->sample_buffer);
free(data); 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;
} }

View file

@ -147,7 +147,7 @@ static void snds_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
*hist1 = clamp16(sample_decoded); *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) { 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; 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]; step = ADPCMTable[*step_index];
delta = 0; delta = 0;
if(sample_nibble & 4) delta = step << 2; if(sample_nibble & 4) delta = step * 4;
if(sample_nibble & 2) delta += step << 1; if(sample_nibble & 2) delta += step * 2;
if(sample_nibble & 1) delta += step; if(sample_nibble & 1) delta += step;
delta >>= 2; delta >>= 2;
if (sample_nibble & 8) delta = -delta; 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; 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 */ /* DVI/IMA */
/* ************************************ */ /* ************************************ */
@ -269,6 +307,50 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample *
stream->adpcm_step_index = step_index; 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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
off_t byte_offset = stream->offset + i/2;
int nibble_shift = (i&1?0:4); //high nibble first
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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
off_t byte_offset = stream->offset + i/2;
int nibble_shift = (i&1?0:4); //high nibble first
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 */ /* 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). /* 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 * Defined in Xbox's SDK. Usable in mono or stereo modes (both suitable for interleaved multichannel). */
* (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 is_stereo) {
void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, frames_in, sample_pos = 0, block_samples, frame_size;
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
off_t frame_offset;
/* internal interleave (fixed size), mixed channels */ /* external interleave (fixed size), stereo/mono */
int block_samples = (0x24-0x4) * 2; block_samples = (0x24 - 0x4) * 2;
frames_in = first_sample / block_samples;
first_sample = 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) { if (first_sample == 0) {
off_t header_offset = (channelspacing & 1) ? off_t header_offset = is_stereo ?
stream->offset + 0x24*(channel) + 0x00: frame_offset + 0x04*(channel % 2) :
stream->offset + 0x48*(channel/2) + 0x04*(channel%2); frame_offset + 0x00;
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
step_index = read_8bit(header_offset+0x02,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; if (step_index > 88) step_index=88;
/* write header sample (even samples per block, skips last nibble) */ /* write header sample (even samples per block, skips last nibble) */
outbuf[sample_count] = (short)(hist1); outbuf[sample_pos] = (short)(hist1);
sample_count += channelspacing; sample_pos += channelspacing;
first_sample += 1; first_sample += 1;
samples_to_do -= 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++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = (channelspacing & 1) ? off_t byte_offset = is_stereo ?
(stream->offset + 0x24*(channel) + 0x04) + (i-1)/2: frame_offset + 0x04*2 + 0x04*(channel % 2) + 0x04*2*((i-1)/8) + ((i-1)%8)/2 :
(stream->offset + 0x48*(channel/2) + 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?4:0); /* low nibble first */ 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) { if (i < block_samples) {
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1); outbuf[sample_pos] = (short)(hist1);
sample_count += channelspacing; 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_history1_32 = hist1;
stream->adpcm_step_index = step_index; 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; 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). /* 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). * Apparently clamps to -32767 unlike standard's -32768 (probably not noticeable).
* Info here: http://problemkaputt.de/gbatek.htm#dssoundnotes */ * 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; 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) { size_t ima_bytes_to_samples(size_t bytes, int channels) {
/* 2 samples per byte (2 nibbles) in stereo or mono config */ /* 2 samples per byte (2 nibbles) in stereo or mono config */

View file

@ -127,16 +127,37 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
goto fail; goto fail;
current_data_size = info.frame_size; 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. */ * 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) { if ((info.layer == 3 && data->config.fsb_padding) || data->config.fsb_padding == 16) {
current_padding = (current_data_size % data->config.fsb_padding) current_padding = (current_data_size % data->config.fsb_padding)
? data->config.fsb_padding - (current_data_size % data->config.fsb_padding) ? data->config.fsb_padding - (current_data_size % data->config.fsb_padding)
: 0; : 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, 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; break;
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */ 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) */ /* calculate frame length (from hcs's fsb_mpeg) */
switch (info->frame_samples) { switch (info->frame_samples) {
case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; 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; 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; case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 1152/8 = 144 */
default: goto fail; default: goto fail;
} }

View file

@ -59,7 +59,7 @@ static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, o
} }
fail: 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' */ return 0; /* keep on truckin' */
} }

View file

@ -103,7 +103,7 @@ int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset,
ok = ealayer3_parse_frame(data, &is, &eaf); ok = ealayer3_parse_frame(data, &is, &eaf);
if (!ok) goto fail; 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; *coding_type = coding_MPEG_ealayer3;
data->channels_per_frame = eaf.channels; 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->version != eaf_1->version
|| eaf_0->granule_index == eaf_1->granule_index || eaf_0->granule_index == eaf_1->granule_index
|| !eaf_0->common_size || !eaf_1->common_size)) { || !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; 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) { 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 */ /* 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 { else {
/* fill ancillary data (should be ignored, but 0x00 seems to improve mpg123's free bitrate detection) */ /* 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) if (!eaf->pcm_size)
return 1; 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_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%lx\n", eaf->v1_pcm_number, 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) */ /* read + write PCM block samples (always BE) */
for (i = 0; i < eaf->v1_pcm_number * data->channels_per_frame; i++) { for (i = 0; i < eaf->v1_pcm_number * data->channels_per_frame; i++) {

View file

@ -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); 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) { 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? goto fail; //handle MPG123_DONE?
} }
if (read_offset > 0x5000) { /* don't hang in some incorrectly detected formats */ 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; 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; default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break;
} }
if (!ok) { 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 */ 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); //;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", ms->bytes_in_buffer, stream->offset);

View file

@ -1,17 +1,15 @@
#include "../util.h" #include "../util.h"
#include "coding.h" #include "coding.h"
/* used to compute next scale */
static const int ADPCMTable[16] = static const int msadpcm_steps[16] = {
{
230, 230, 230, 230, 230, 230, 230, 230,
307, 409, 512, 614, 307, 409, 512, 614,
768, 614, 512, 409, 768, 614, 512, 409,
307, 230, 230, 230 307, 230, 230, 230
}; };
static const int ADPCMCoeffs[7][2] = static const int msadpcm_coefs[7][2] = {
{
{ 256, 0 }, { 256, 0 },
{ 512, -256 }, { 512, -256 },
{ 0, 0 }, { 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) { void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) {
VGMSTREAMCHANNEL *ch1,*ch2; VGMSTREAMCHANNEL *ch1,*ch2;
int i;
int framesin;
STREAMFILE *streamfile; STREAMFILE *streamfile;
off_t offset; int i, frames_in;
size_t bytes_per_frame, samples_per_frame;
framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream); off_t frame_offset;
first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream);
ch1 = &vgmstream->ch[0]; ch1 = &vgmstream->ch[0];
ch2 = &vgmstream->ch[1]; ch2 = &vgmstream->ch[1];
streamfile = ch1->streamfile; 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) { 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[0] = ch1->adpcm_history2_16;
outbuf[1] = ch2->adpcm_history2_16; outbuf[1] = ch2->adpcm_history2_16;
outbuf += 2;
outbuf+=2;
first_sample++; first_sample++;
samples_to_do--; 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[0] = ch1->adpcm_history1_16;
outbuf[1] = ch2->adpcm_history1_16; outbuf[1] = ch2->adpcm_history1_16;
outbuf += 2;
outbuf+=2;
first_sample++; first_sample++;
samples_to_do--; samples_to_do--;
} }
for (i=first_sample; i<first_sample+samples_to_do; i++) { /* decode nibbles */
int j; for (i = first_sample; i < first_sample+samples_to_do; i++) {
int ch;
for (j=0;j<2;j++) for (ch = 0; ch < 2; ch++) {
{ VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch];
VGMSTREAMCHANNEL *ch = &vgmstream->ch[j]; int32_t hist1,hist2, predicted;
int sample_nibble = int sample_nibble = (ch == 0) ? /* L = high nibble first */
(j == 0 ? get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) :
get_high_nibble_signed(read_8bit(offset+14+i-2,streamfile)) : get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile));
get_low_nibble_signed(read_8bit(offset+14+i-2,streamfile))
);
int32_t hist1,hist2;
int32_t predicted;
hist1 = ch->adpcm_history1_16; hist1 = stream->adpcm_history1_16;
hist2 = ch->adpcm_history2_16; hist2 = stream->adpcm_history2_16;
predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1]; predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
predicted /= 256; predicted = predicted / 256;
predicted += sample_nibble*ch->adpcm_scale; predicted = predicted + sample_nibble*stream->adpcm_scale;
outbuf[0] = clamp16(predicted); outbuf[0] = clamp16(predicted);
ch->adpcm_history2_16 = ch->adpcm_history1_16;
ch->adpcm_history1_16 = outbuf[0]; stream->adpcm_history2_16 = stream->adpcm_history1_16;
ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] * stream->adpcm_history1_16 = outbuf[0];
ch->adpcm_scale) / 256; stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10; if (stream->adpcm_scale < 0x10)
stream->adpcm_scale = 0x10;
outbuf++; outbuf++;
} }
} }
} }
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) {
VGMSTREAMCHANNEL *ch1; VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
int i; int i, frames_in;
int framesin; size_t bytes_per_frame, samples_per_frame;
STREAMFILE *streamfile; off_t frame_offset;
off_t offset;
framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream); /* external interleave (variable size), mono */
first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream); 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]; frame_offset = stream->offset + frames_in*bytes_per_frame;
streamfile = ch1->streamfile;
offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream);
if (first_sample==0) { /* parse frame header */
ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0]; if (first_sample == 0) {
ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1]; stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
ch1->adpcm_scale = read_16bitLE(offset+1,streamfile); stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
ch1->adpcm_history1_16 = read_16bitLE(offset+3,streamfile); stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
ch1->adpcm_history2_16 = read_16bitLE(offset+5,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; /* write header samples (needed) */
if (first_sample == 0) {
outbuf++; outbuf[0] = stream->adpcm_history2_16;
outbuf += channelspacing;
first_sample++; first_sample++;
samples_to_do--; 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[0] = stream->adpcm_history1_16;
outbuf += channelspacing;
outbuf++;
first_sample++; first_sample++;
samples_to_do--; samples_to_do--;
} }
for (i=first_sample; i<first_sample+samples_to_do; i++) { /* decode nibbles */
{ for (i = first_sample; i < first_sample+samples_to_do; i++) {
VGMSTREAMCHANNEL *ch = &vgmstream->ch[0]; int32_t hist1,hist2, predicted;
int sample_nibble = int sample_nibble = (i & 1) ? /* high nibble first */
(i & 1 ? get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
get_low_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile)) : get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
get_high_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile))
);
int32_t hist1,hist2;
int32_t predicted;
hist1 = ch->adpcm_history1_16; hist1 = stream->adpcm_history1_16;
hist2 = ch->adpcm_history2_16; hist2 = stream->adpcm_history2_16;
predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1]; predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
predicted /= 256; predicted = predicted / 256;
predicted += sample_nibble*ch->adpcm_scale; predicted = predicted + sample_nibble*stream->adpcm_scale;
outbuf[0] = clamp16(predicted); 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;
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;
} }
} }

View file

@ -79,7 +79,7 @@ static void mta2_block_update(VGMSTREAMCHANNEL * stream) {
} }
if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */ 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; stream->offset += 0x10;
repeat = 0; repeat = 0;
} }

View file

@ -109,7 +109,7 @@ static void mtaf_block_update(VGMSTREAMCHANNEL * stream) {
} }
if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */ 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; stream->offset += 0x10;
repeat = 0; repeat = 0;
} }
@ -125,7 +125,7 @@ static void mtaf_block_update(VGMSTREAMCHANNEL * stream) {
} }
#endif #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; int32_t sample_count;
int i; int i;
int c = channel%2; /* global channel to track channel */ 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_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 */ 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 */ /* avoid index out of range in corrupt files */
if (init_idx < 0) { if (init_idx < 0) {
init_idx = 0; init_idx = 0;

View file

@ -1,51 +1,65 @@
#include "coding.h" #include "coding.h"
#include "../util.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) { 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; off_t frame_offset;
int32_t sample_count; int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
int framesin = first_sample/28; uint8_t coef_index, shift_factor;
uint8_t q = read_8bit(framesin*32+stream->offset+channel,stream->streamfile);
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32; int32_t hist2 = stream->adpcm_history2_32;
first_sample = first_sample%28;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { /* external interleave (fixed size), stereo */
int sample_byte = read_8bit(framesin*32+stream->offset+4+i,stream->streamfile); 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: case 0:
hist = 0; hist = 0; // (hist1 * 0) - (hist2 * 0);
break; break;
case 1: case 1:
hist = (hist1 * 0x3c); hist = (hist1 * 60); // - (hist2 * 0);
break; break;
case 2: case 2:
hist = (hist1 * 0x73) - (hist2 * 0x34); hist = (hist1 * 115) - (hist2 * 52);
break; break;
case 3: case 3:
hist = (hist1 * 0x62) - (hist2 * 0x37); hist = (hist1 * 98) - (hist2 * 55);
break; break;
} }
hist = (hist + 32) >> 6;
hist = (hist+0x20)>>6;
if (hist > 0x1fffff) hist = 0x1fffff; if (hist > 0x1fffff) hist = 0x1fffff;
if (hist < -0x200000) hist = -0x200000; 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; hist2 = hist1;
hist1 = new_sample;
hist1 = ((((channel==0? outbuf[sample_count] = clamp16(new_sample >> 6);
get_low_nibble_signed(sample_byte): sample_count += channelspacing;
get_high_nibble_signed(sample_byte)
) << 12) >> (q & 0xf)) << 6) + hist;
outbuf[sample_count] = clamp16(hist1 >> 6);
} }
stream->adpcm_history1_32 = hist1; stream->adpcm_history1_32 = hist1;

View file

@ -2,7 +2,7 @@
#include "../util.h" #include "../util.h"
#include <math.h> #include <math.h>
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; int i;
int32_t sample_count; 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; int i;
int32_t sample_count; 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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
outbuf[sample_count]=read_16bit(stream->offset+i*2*channelspacing,stream->streamfile);
}
}
void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i; int i;
int32_t sample_count; 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; int i;
int32_t sample_count; int32_t sample_count;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int16_t v = (uint8_t)read_8bit(stream->offset+i*channelspacing,stream->streamfile); int16_t v = (uint8_t)read_8bit(stream->offset+i,stream->streamfile);
if (v&0x80) v = 0-(v&0x7f); outbuf[sample_count] = v*0x100 - 0x8000;
outbuf[sample_count] = v*0x100;
} }
} }
@ -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; int i;
int32_t sample_count; int32_t sample_count;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int16_t v = (uint8_t)read_8bit(stream->offset+i,stream->streamfile); int16_t v = (uint8_t)read_8bit(stream->offset+i,stream->streamfile);
outbuf[sample_count] = v*0x100 - 0x8000; if (v&0x80) v = 0-(v&0x7f);
} outbuf[sample_count] = v*0x100;
}
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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
outbuf[sample_count]=read_16bit(stream->offset+i*2*channelspacing,stream->streamfile);
} }
} }

View file

@ -1,40 +1,24 @@
#include <math.h>
#include "coding.h" #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) */ /* PS-ADPCM table, defined as rational numbers (as in the spec) */
static const double VAG_f[16][2] = { static const double ps_adpcm_coefs_f[5][2] = {
{ 0.0 , 0.0 }, { 0.0 , 0.0 },
{ 60.0 / 64.0 , 0.0 }, { 60.0 / 64.0 , 0.0 },
{ 115.0 / 64.0 , -52.0 / 64.0 }, { 115.0 / 64.0 , -52.0 / 64.0 },
{ 98.0 / 64.0 , -55.0 / 64.0 }, { 98.0 / 64.0 , -55.0 / 64.0 },
{ 122.0 / 64.0 , -60.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 */ /* PS-ADPCM table, defined as spec_coef*64 (for int implementations) */
static const int8_t VAG_coefs[5][2] = { static const int ps_adpcm_coefs_i[5][2] = {
{ 0 , 0 }, { 0 , 0 },
{ 60 , 0 }, { 60 , 0 },
{ 115 , -52 }, { 115 , -52 },
{ 98 , -55 }, { 98 , -55 },
{ 122 , -60 }, { 122 , -60 },
/* extended */ #if 0
/* extended table from PPSSPP (PSP emu), found by tests (unused?) */
{ 0 , 0 }, { 0 , 0 },
{ 0 , 0 }, { 0 , 0 },
{ 52 , 0 }, { 52 , 0 },
@ -46,163 +30,69 @@ static const int8_t VAG_coefs[5][2] = {
{ 2 ,-216 }, { 2 ,-216 },
{ 125 , -6 }, { 125 , -6 },
{ 0 ,-151 }, { 0 ,-151 },
};
#endif #endif
};
/** /* Decodes Sony's PS-ADPCM (sometimes called SPU-ADPCM or VAG, just "ADPCM" in the SDK docs).
* Sony's PS ADPCM (sometimes called VAG), decodes 16 bytes into 28 samples. * Very similar to XA ADPCM (see xa_decoder for extended info).
* The first 2 bytes are a header (shift, predictor, optional flag).
* All variants are the same with minor differences.
* *
* Flags: * Some official PC tools decode using float coefs (from the spec), as does this code, but
* 0x0: Nothing * consoles/games/libs would vary (PS1 could do it in hardware using BRR/XA's logic, FMOD/PS3
* 0x1: End marker + decode * may use int math in software, etc). There are inaudible rounding diffs between implementations.
* 0x2: Loop region
* 0x3: Loop end
* 0x4: Start marker
* 0x5: ?
* 0x6: Loop start
* 0x7: End marker + don't decode
* 0x8+ Not valid
*/ */
/* default */ /* standard PS-ADPCM (float math version) */
void decode_psx(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) {
off_t frame_offset;
int predict_nr, shift_factor, sample; int i, frames_in, sample_count = 0;
int32_t hist1=stream->adpcm_history1_32; size_t bytes_per_frame, samples_per_frame;
int32_t hist2=stream->adpcm_history2_32; uint8_t coef_index, shift_factor, flag;
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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
sample=0;
if(flag<0x07) {
short sample_byte = (short)read_8bit(stream->offset+(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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
short sample_byte = (short)read_8bit(stream->offset+(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;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32; int32_t hist2 = stream->adpcm_history2_32;
int i, sample_count, bytes_per_frame, samples_per_frame; /* external interleave (fixed size), mono */
const int header_size = 1; bytes_per_frame = 0x10;
int framesin; samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */
frames_in = first_sample / samples_per_frame;
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;
first_sample = 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 */ /* parse frame header */
byte = (uint8_t)read_8bit(stream->offset+(framesin*frame_size)+header_size+first_sample/2,stream->streamfile); 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 */
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 (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);
} }
for (i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { outbuf[sample_count] = new_sample;
sample = 0; sample_count += channelspacing;
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
}
outbuf[sample_count] = clamp16(sample);
hist2 = hist1; hist2 = hist1;
hist1 = sample; hist1 = new_sample;
} }
stream->adpcm_history1_32 = hist1; 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) { size_t ps_bytes_to_samples(size_t bytes, int channels) {
return bytes / channels / 0x10 * 28; 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) { size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels) {
return bytes / channels / frame_size * 28; return bytes / channels / frame_size * 28;
} }

View file

@ -69,6 +69,7 @@ vorbis_custom_codec_data * init_vorbis_custom(STREAMFILE *streamFile, off_t star
return data; return data;
fail: fail:
VGM_LOG("VORBIS: init fail at around 0x%"PRIx64"\n", (off64_t)start_offset);
free_vorbis_custom(data); free_vorbis_custom(data);
return NULL; return NULL;
} }
@ -136,7 +137,6 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
default: goto decode_fail; default: goto decode_fail;
} }
if(!ok) { if(!ok) {
VGM_LOG("Vorbis: cannot parse packet @ around %lx\n",stream->offset);
goto decode_fail; 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 */ /* parse the fake ogg packet into a logical vorbis block */
rc = vorbis_synthesis(&data->vb,&data->op); rc = vorbis_synthesis(&data->vb,&data->op);
if (rc == OV_ENOTAUDIO) { 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); //VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0);
continue; /* seems ok? */ continue; /* rarely happens, seems ok? */
} else if (rc != 0) { } else if (rc != 0) goto decode_fail;
VGM_LOG("Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset);
goto decode_fail;
}
/* finally decode the logical block into samples */ /* finally decode the logical block into samples */
rc = vorbis_synthesis_blockin(&data->vd,&data->vb); rc = vorbis_synthesis_blockin(&data->vd,&data->vb);
if (rc != 0) { if (rc != 0) goto decode_fail; /* ? */
VGM_LOG("Vorbis: cannot decode Vorbis block @ %lx\n",stream->offset);
goto decode_fail; /* ? */
}
data->samples_full = 1; data->samples_full = 1;
} }
@ -167,6 +162,7 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
decode_fail: decode_fail:
/* on error just put some 0 samples */ /* 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)); memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample));
} }

View file

@ -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) */ /* 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); data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile);
stream->offset += 2; stream->offset += 2;
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) { if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */
VGM_LOG("FSB Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2);
goto fail; /* EOF or end padding */
}
/* read raw block */ /* read raw block */
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
stream->offset += bytes; stream->offset += data->op.bytes;
if (bytes != data->op.bytes) { if (bytes != data->op.bytes) goto fail; /* wrong packet? */
VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes);
goto fail; /* wrong packet? */
}
return 1; return 1;

View file

@ -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) */ /* get next packet size from the OGL 16b header (upper 14b) */
data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2; data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2;
stream->offset += 2; stream->offset += 2;
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) { if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */
VGM_LOG("OGL Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2);
goto fail; /* EOF or end padding */
}
/* read raw block */ /* read raw block */
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
stream->offset += data->op.bytes; stream->offset += data->op.bytes;
if (bytes != data->op.bytes) { if (bytes != data->op.bytes) goto fail; /* wrong packet? */
VGM_LOG("OGL Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes);
goto fail; /* wrong packet? */
}
return 1; return 1;

View file

@ -61,7 +61,6 @@ int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorb
return 1; return 1;
fail: fail:
VGM_LOG("SK Vorbis: failed to setup init packets @ around 0x%08lx\n", offset);
return 0; return 0;
} }
@ -70,31 +69,27 @@ int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_
off_t packet_offset = 0; off_t packet_offset = 0;
size_t packet_size = 0; size_t packet_size = 0;
int page_packets; int page_packets;
int res;
/* read OggS/SK page and get current packet */ /* 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++; data->current_packet++;
if (!res || packet_size > data->buffer_size) goto fail;
/* read raw block */ /* read raw block */
data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile); data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile);
if (data->op.bytes != packet_size) { if (data->op.bytes != packet_size) goto fail; /* wrong packet? */
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? */
}
/* go next page when processed all packets in page */
if (data->current_packet >= page_packets) { 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; if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, -1)) goto fail;
stream->offset = packet_offset + packet_size; stream->offset = packet_offset + packet_size;
data->current_packet = 0; data->current_packet = 0;
} }
return 1; return 1;
fail: fail:
VGM_LOG("SK Vorbis: failed to parse packet @ around 0x%08lx\n", stream->offset);
return 0; return 0;
} }
@ -170,7 +165,7 @@ static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_p
return 1; return 1;
fail: 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; 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 */ put_8bit (buf+0x00, read_8bit(packet_offset,streamFile)); /* packet_type */
memcpy (buf+0x01, "vorbis", 6); /* id */ memcpy (buf+0x01, "vorbis", 6); /* id */
bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,streamFile); /* copy rest (all except id+"SK") */ bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,streamFile); /* copy rest (all except id+"SK") */
if (packet_size-0x03 != bytes) { 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);
return 0; return 0;
}
return 0x07+packet_size-0x03; return 0x07+packet_size-0x03;
} }

View file

@ -68,27 +68,20 @@ int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_code
/* get packet info the VID1 header */ /* get packet info the VID1 header */
get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes); get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes);
if (data->op.bytes == 0) { if (data->op.bytes == 0 || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */
VGM_LOG("VID1 Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset);
goto fail; /* EOF or end padding */
}
/* read raw block */ /* read raw block */
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
stream->offset += data->op.bytes; stream->offset += data->op.bytes;
if (bytes != data->op.bytes) { if (bytes != data->op.bytes) goto fail; /* wrong packet? */
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
//todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea
/* test block end (weird size calc but seems ok) */ /* test block end (weird size calc but seems ok) */
if ((stream->offset - (data->block_offset + 0x34)) >= (data->block_size - 0x06)) { if ((stream->offset - (data->block_offset + 0x34)) >= (data->block_size - 0x06)) {
stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile); stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile);
} }
return 1; return 1;
fail: fail:

View file

@ -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 */ /* 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); 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) { if (!header_size || packet_size > data->buffer_size) goto fail;
VGM_LOG("Wwise Vorbis: wrong packet (0x%x) @ %lx\n", packet_size, stream->offset);
goto fail;
}
data->op.bytes = rebuild_packet(data->buffer, data->buffer_size, stream->streamfile,stream->offset, data, data->config.big_endian); 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; stream->offset += header_size + packet_size;
if (!data->op.bytes || data->op.bytes >= 0xFFFF) { if (!data->op.bytes || data->op.bytes >= 0xFFFF) goto fail;
VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-header_size-packet_size);
goto fail;
}
return 1; return 1;
@ -184,7 +178,7 @@ static size_t rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *stream
if (!rc) goto fail; if (!rc) goto fail;
if (ow.b_off % 8 != 0) { 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; goto fail;
} }
@ -228,7 +222,7 @@ static size_t rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamF
if (!rc) goto fail; if (!rc) goto fail;
if (ow.b_off % 8 != 0) { 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; 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 */ /* check that we used exactly all bytes */
/* note: if all bits are used in the last byte there will be one extra 0 byte */ /* 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 ) { 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; goto fail;
} }

View file

@ -2,14 +2,15 @@
#include "../util.h" #include "../util.h"
// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do // 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. // XA coefs int math in different ways (see comments below), not 100% accurate.
// May be implemented like the SNES/SPC700 BRR (see BSNES' brr.cpp, hardware-tested). // May be implemented like the SNES/SPC700 BRR.
/* XA ADPCM gain values */ /* XA ADPCM gain values */
static const double K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 }; 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 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 */ /* K0/1 floats to int, K*2^10 = K*(1<<10) = K*1024 */
static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); } 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. /* 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. * The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new.
@ -29,55 +30,100 @@ static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); }
* (rounding differences should be inaudible, so public implementations may be approximations) * (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 * 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 * differently, maybe using one of the above methods in software/CPU, but in XA's case may be done
* the SNES/SPC700 BRR, with specific per-filter ops. * 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 * 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. * 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 * 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}; void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]); off_t frame_offset, sp_offset;
off_t sp_offset; int i,j, frames_in, samples_done = 0, sample_count = 0;
int i; size_t bytes_per_frame, samples_per_frame;
int frames_in, sample_count = 0;
int32_t coef1, coef2, coef_index, shift_factor;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32; int32_t hist2 = stream->adpcm_history2_32;
/* external interleave (fixed size), mono/stereo */ /* external interleave (fixed size), mono/stereo */
frames_in = first_sample / (28*2 / channelspacing); bytes_per_frame = 0x80;
first_sample = first_sample % 28; 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 */ /* data layout (mono):
vgmstream->xa_get_high_nibble = !vgmstream->xa_get_high_nibble; * - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames
if (first_sample && channelspacing==1) * (handled externally, this decoder only gets frames)
vgmstream->xa_get_high_nibble = !vgmstream->xa_get_high_nibble; * - 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 */ if (read_32bitBE(frame_offset+0x00,stream->streamfile) != read_32bitBE(frame_offset+0x04,stream->streamfile) ||
sp_offset = stream->offset+head_table[frames_in]+vgmstream->xa_get_high_nibble; read_32bitBE(frame_offset+0x08,stream->streamfile) != read_32bitBE(frame_offset+0x0c,stream->streamfile)) {
coef_index = (read_8bit(sp_offset,stream->streamfile) >> 4) & 0xf; VGM_LOG("bad frames at %"PRIx64"\n", (off64_t)frame_offset);
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);
/* decode subframes */
for (i = 0; i < 8 / channelspacing; i++) {
int32_t coef1, coef2;
uint8_t coef_index, shift_factor;
/* 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;
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %"PRIx64"\n", (off64_t)sp_offset);
if (coef_index > 4) if (coef_index > 4)
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */ coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
if (shift_factor > 12) if (shift_factor > 12)
shift_factor = 9; /* supposedly, from Nocash PSX docs */ shift_factor = 9; /* supposedly, from Nocash PSX docs */
coef1 = IK0(coef_index); coef1 = get_IK0(coef_index);
coef2 = IK1(coef_index); coef2 = get_IK1(coef_index);
/* decode nibbles */ /* decode subframe nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) { for(j = 0; j < 28; j++) {
uint8_t nibbles;
int32_t new_sample; int32_t new_sample;
uint8_t nibbles = (uint8_t)read_8bit(stream->offset+0x10+frames_in+(i*0x04),stream->streamfile);
new_sample = vgmstream->xa_get_high_nibble ? 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 >> 4) & 0x0f :
(nibbles ) & 0x0f; (nibbles ) & 0x0f;
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
new_sample = new_sample << 4; new_sample = new_sample << 4;
new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10); new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10);
@ -87,8 +133,11 @@ void decode_xa(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32
new_sample = new_sample >> 4; new_sample = new_sample >> 4;
new_sample = clamp16(new_sample); new_sample = clamp16(new_sample);
outbuf[sample_count] = new_sample; outbuf[samples_done * channelspacing] = new_sample;
sample_count += channelspacing; samples_done++;
sample_count++;
}
} }
stream->adpcm_history1_32 = hist1; 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) { size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked) {
if (is_blocked) { if (is_blocked) {
//todo with -0x10 misses the last sector, not sure if bug or feature return (bytes / 0x930) * (28*8/ channels) * 18;
return ((bytes - 0x10) / 0x930) * (0x900 - 18*0x10) * 2 / channels;
} }
else { else {
return ((bytes / 0x80)*0xE0) / 2; return (bytes / 0x80) * (28*8 / channels);
} }
} }

View file

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

View file

@ -4,16 +4,17 @@
/* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here /* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
* to inform plugins that need it. Common extensions are commented out to avoid stealing them. */ * 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 */ /* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */
static const char* extension_list[] = { 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", "04sw",
"2dx9", "2dx9",
"2pfs", "2pfs",
"800",
//"aac", //common, also tri-Ace's //"aac", //common, also tri-Ace's
"aa3", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) "aa3", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
@ -32,6 +33,8 @@ static const char* extension_list[] = {
"afc", "afc",
"agsc", "agsc",
"ahx", "ahx",
"ahv",
"ai",
//"aif", //common //"aif", //common
"aifc", //common? "aifc", //common?
"aifcl", //fake extension, for AIF??? "aifcl", //fake extension, for AIF???
@ -41,8 +44,8 @@ static const char* extension_list[] = {
"akb", "akb",
"al2", "al2",
"amts", //fake extension/header id for .stm (to be removed) "amts", //fake extension/header id for .stm (to be removed)
"ao", //txth/reserved [Cloudphobia (PC)] "ao",
"apc", //txth/reserved [MegaRace 3 (PC)] "apc",
"as4", "as4",
"asd", "asd",
"asf", "asf",
@ -65,7 +68,7 @@ static const char* extension_list[] = {
"bar", "bar",
"bcstm", "bcstm",
"bcwav", "bcwav",
"bd3", //txth/reserved [Elevator Action Deluxe (PS3)] "bd3",
"bdsp", "bdsp",
"bfstm", "bfstm",
"bfwav", "bfwav",
@ -77,9 +80,7 @@ static const char* extension_list[] = {
"bik", "bik",
"bika", "bika",
"bik2", "bik2",
"bik2a",
"bk2", "bk2",
"bk2a",
"bmdx", "bmdx",
"bms", "bms",
"bnk", "bnk",
@ -97,13 +98,17 @@ static const char* extension_list[] = {
"ccc", "ccc",
"cd", "cd",
"cfn", //fake extension/header id for .caf (to be removed) "cfn", //fake extension/header id for .caf (to be removed)
"ckb",
"ckd", "ckd",
"cks",
"cnk", "cnk",
"cps", "cps",
"csmp",
"cvs", "cvs",
"cxs", "cxs",
"da", "da",
"dax",
"dbm", "dbm",
"dcs", "dcs",
"ddsp", "ddsp",
@ -158,15 +163,17 @@ static const char* extension_list[] = {
"iadp", "iadp",
"idsp", "idsp",
"idvi", //fake extension for .pcm (to be removed) "idvi", //fake extension for .pcm (to be removed)
"idx",
"ikm", "ikm",
"ild", "ild",
"int", "int",
"isd", "isd",
"isws", "isws",
"itl", //txth/reserved [Charinko Hero (GC)] "itl",
"ivaud", "ivaud",
"ivag", "ivag",
"ivb", "ivb",
"ivs", //txth/reserved [Burnout 2 (PS2)]
"joe", "joe",
"jstm", "jstm",
@ -185,6 +192,7 @@ static const char* extension_list[] = {
"laac", //fake extension, for AAC (tri-Ace/FFmpeg) "laac", //fake extension, for AAC (tri-Ace/FFmpeg)
"lac3", //fake extension, for AC3 "lac3", //fake extension, for AC3
"leg", "leg",
"lflac", //fake extension, FFmpeg, not parsed, use with .pos pair for fun
"lmp4", //fake extension, for MP4s "lmp4", //fake extension, for MP4s
"logg", //fake extension, for OGGs "logg", //fake extension, for OGGs
"lopus", //fake extension, for OPUS "lopus", //fake extension, for OPUS
@ -210,6 +218,7 @@ static const char* extension_list[] = {
"mihb", "mihb",
"mnstr", "mnstr",
"mogg", "mogg",
//"mp3", //common
//"mp4", //common //"mp4", //common
//"mpc", //FFmpeg, not parsed (musepack) //common //"mpc", //FFmpeg, not parsed (musepack) //common
"mpdsp", "mpdsp",
@ -221,7 +230,7 @@ static const char* extension_list[] = {
"msd", "msd",
"msf", "msf",
"mss", "mss",
"msv", //txh/reserved [Fight Club (PS2)] "msv",
"msvp", "msvp",
"mta2", "mta2",
"mtaf", "mtaf",
@ -241,6 +250,7 @@ static const char* extension_list[] = {
"npsf", //fake extension/header id for .nps (to be removed) "npsf", //fake extension/header id for .nps (to be removed)
"nus3bank", "nus3bank",
"nwa", "nwa",
"nxa",
//"ogg", //common //"ogg", //common
"ogl", "ogl",
@ -306,7 +316,7 @@ static const char* extension_list[] = {
"scd", "scd",
"sck", "sck",
"sd9", "sd9",
"sdf", //txth/reserved [Gummy Bears Mini Golf (3DS), Agent Hugo - Lemoon Twist (PS2)] "sdf",
"sdt", "sdt",
"seg", "seg",
"sf0", "sf0",
@ -330,6 +340,7 @@ static const char* extension_list[] = {
"snr", "snr",
"sns", "sns",
"snu", "snu",
"sod",
"son", "son",
"spd", "spd",
"spm", "spm",
@ -350,7 +361,7 @@ static const char* extension_list[] = {
"stx", "stx",
"svag", "svag",
"svs", "svs",
"svg", //txth/reserved [Hunter: The Reckoning - Wayward (PS2)] "svg",
"swag", "swag",
"swav", "swav",
"swd", "swd",
@ -360,6 +371,7 @@ static const char* extension_list[] = {
"sxd2", "sxd2",
"tec", "tec",
"tgq",
"thp", "thp",
"tk5", "tk5",
"tra", "tra",
@ -369,13 +381,16 @@ static const char* extension_list[] = {
"txtp", "txtp",
"tydsp", "tydsp",
"ue4opus",
"ulw", "ulw",
"um3", "um3",
"utk",
"uv",
"v0", "v0",
//"v1", //dual channel with v0 //"v1", //dual channel with v0
"vag", "vag",
"vai", //txth/reserved [Ratatouille (GC)] "vai",
"vas", "vas",
"vawx", "vawx",
"vb", "vb",
@ -386,7 +401,7 @@ static const char* extension_list[] = {
"vgs", "vgs",
"vgv", "vgv",
"vig", "vig",
"vis", //txth/reserved [AirForce Delta (PS2)] "vis",
"vms", "vms",
"voi", "voi",
"vpk", "vpk",
@ -402,7 +417,9 @@ static const char* extension_list[] = {
"wam", "wam",
"was", "was",
//"wav", //common //"wav", //common
"wavc",
"wave", "wave",
"wavebatch",
"wavm", "wavm",
"wb", "wb",
"wem", "wem",
@ -413,10 +430,13 @@ static const char* extension_list[] = {
"wpd", "wpd",
"wsd", "wsd",
"wsi", "wsi",
"wv2", //txth/reserved [Slave Zero (PC)] "wua",
"wv2",
"wv6",
"wve", "wve",
"wvs", "wvs",
"x",
"xa", "xa",
"xa2", "xa2",
"xa30", "xa30",
@ -427,11 +447,13 @@ static const char* extension_list[] = {
"xmu", "xmu",
"xnb", "xnb",
"xsf", "xsf",
"xsew",
"xss", "xss",
"xvag", "xvag",
"xvas", "xvas",
"xwav",//fake, to be removed "xwav",//fake, to be removed
"xwb", "xwb",
"xmd",
"xwc", "xwc",
"xwm", //FFmpeg, not parsed (XWMA) "xwm", //FFmpeg, not parsed (XWMA)
"xwma", //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_int, "8-bit PCM with 1 byte interleave (block)"},
{coding_PCM8_U, "8-bit unsigned PCM"}, {coding_PCM8_U, "8-bit unsigned PCM"},
{coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"}, {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, "8-bit u-Law"},
{coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"}, {coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"},
{coding_ALAW, "8-bit a-Law"}, {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, "Intel DVI 4-bit IMA ADPCM"},
{coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (mono/interleave)"}, {coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (mono/interleave)"},
{coding_3DS_IMA, "3DS IMA 4-bit ADPCM"}, {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_MS_IMA, "Microsoft 4-bit IMA ADPCM"},
{coding_XBOX_IMA, "XBOX 4-bit IMA ADPCM"}, {coding_XBOX_IMA, "XBOX 4-bit IMA ADPCM"},
{coding_XBOX_IMA_mch, "XBOX 4-bit IMA ADPCM (multichannel)"}, {coding_XBOX_IMA_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, "Radical 4-bit IMA ADPCM"},
{coding_RAD_IMA_mono, "Radical 4-bit IMA ADPCM (mono/interleave)"}, {coding_RAD_IMA_mono, "Radical 4-bit IMA ADPCM (mono/interleave)"},
{coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"}, {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_FSB_IMA, "FSB 4-bit IMA ADPCM"},
{coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"}, {coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"},
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
{coding_UBI_IMA, "Ubisoft 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, "Microsoft 4-bit ADPCM"},
{coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"},
{coding_WS, "Westwood Studios VBR ADPCM"}, {coding_WS, "Westwood Studios VBR ADPCM"},
{coding_AICA, "Yamaha AICA 4-bit ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"},
{coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"}, {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_YAMAHA_NXAP, "Yamaha NXAP 4-bit ADPCM"},
{coding_NDS_PROCYON, "Procyon Studio Digital Sound Elements NDS 4-bit APDCM"}, {coding_NDS_PROCYON, "Procyon Studio Digital Sound Elements NDS 4-bit APDCM"},
{coding_L5_555, "Level-5 0x555 4-bit ADPCM"}, {coding_L5_555, "Level-5 0x555 4-bit ADPCM"},
{coding_SASSC, "Activision / EXAKT SASSC 8-bit DPCM"},
{coding_LSF, "lsf 4-bit ADPCM"}, {coding_LSF, "lsf 4-bit ADPCM"},
{coding_MTAF, "Konami MTAF 4-bit ADPCM"}, {coding_MTAF, "Konami MTAF 4-bit ADPCM"},
{coding_MTA2, "Konami MTA2 4-bit ADPCM"}, {coding_MTA2, "Konami MTA2 4-bit ADPCM"},
{coding_MC3, "Paradigm MC3 3-bit ADPCM"}, {coding_MC3, "Paradigm MC3 3-bit ADPCM"},
{coding_FADPCM, "FMOD FADPCM 4-bit ADPCM"}, {coding_FADPCM, "FMOD FADPCM 4-bit ADPCM"},
{coding_ASF, "Argonaut ASF 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, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, {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, "Cuberoot-delta-exact (CBD2) 8-bit DPCM"},
{coding_CBD2_int, "Cuberoot-delta-exact (CBD2) 8-bit DPCM with 1 byte interleave"}, {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_ACM, "InterPlay ACM"},
{coding_NWA, "VisualArt's NWA DPCM"}, {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)"}, {coding_MPEG_layer3, "MPEG Layer III Audio (MP3)"},
#endif #endif
#ifdef VGM_USE_G7221 #ifdef VGM_USE_G7221
{coding_G7221, "ITU G.722.1 (Polycom Siren 7)"},
{coding_G7221C, "ITU G.722.1 annex C (Polycom Siren 14)"}, {coding_G7221C, "ITU G.722.1 annex C (Polycom Siren 14)"},
#endif #endif
#ifdef VGM_USE_G719 #ifdef VGM_USE_G719
@ -586,13 +615,16 @@ static const coding_info coding_info_list[] = {
#ifdef VGM_USE_ATRAC9 #ifdef VGM_USE_ATRAC9
{coding_ATRAC9, "ATRAC9"}, {coding_ATRAC9, "ATRAC9"},
#endif #endif
#ifdef VGM_USE_CELT
{coding_CELT_FSB, "Custom CELT"},
#endif
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
{coding_FFmpeg, "FFmpeg"}, {coding_FFmpeg, "FFmpeg"},
#endif #endif
}; };
static const layout_info layout_info_list[] = { static const layout_info layout_info_list[] = {
{layout_none, "flat (no layout)"}, {layout_none, "flat"},
{layout_interleave, "interleave"}, {layout_interleave, "interleave"},
{layout_segmented, "segmented"}, {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_ea_wve_ad10, "blocked (EA WVE Ad10)"},
{layout_blocked_sthd, "blocked (STHD)"}, {layout_blocked_sthd, "blocked (STHD)"},
{layout_blocked_h4m, "blocked (H4M)"}, {layout_blocked_h4m, "blocked (H4M)"},
{layout_blocked_xa_aiff, "blocked (XA AIFF)"},
}; };
static const meta_info meta_info_list[] = { static const meta_info meta_info_list[] = {
@ -647,15 +680,16 @@ static const meta_info meta_info_list[] = {
{meta_AIX, "CRI AIX header"}, {meta_AIX, "CRI AIX header"},
{meta_AAX, "CRI AAX header"}, {meta_AAX, "CRI AAX header"},
{meta_UTF_DSP, "CRI ADPCM_WII header"}, {meta_UTF_DSP, "CRI ADPCM_WII header"},
{meta_DSP_AGSC, "Retro Studios AGSC header"}, {meta_AGSC, "Retro Studios AGSC header"},
{meta_DSP_CSMP, "Retro Studios CSMP header"}, {meta_CSMP, "Retro Studios CSMP header"},
{meta_RFRM, "Retro Studios RFRM header"},
{meta_NGC_ADPDTK, "Nintendo ADP raw header"}, {meta_NGC_ADPDTK, "Nintendo ADP raw header"},
{meta_RSF, "Retro Studios RSF raw header"}, {meta_RSF, "Retro Studios RSF raw header"},
{meta_AFC, "Nintendo AFC header"}, {meta_AFC, "Nintendo AFC header"},
{meta_AST, "Nintendo AST header"}, {meta_AST, "Nintendo AST header"},
{meta_HALPST, "HAL Laboratory HALPST header"}, {meta_HALPST, "HAL Laboratory HALPST header"},
{meta_DSP_RS03, "Retro Studios RS03 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_DSP_CSTR, "Namco Cstr header"},
{meta_GCSW, "GCSW header"}, {meta_GCSW, "GCSW header"},
{meta_PS2_SShd, "Sony ADS header"}, {meta_PS2_SShd, "Sony ADS header"},
@ -667,28 +701,26 @@ static const meta_info meta_info_list[] = {
{meta_FWAV, "Nintendo FWAV header"}, {meta_FWAV, "Nintendo FWAV header"},
{meta_PSX_XA, "RIFF/CDXA header"}, {meta_PSX_XA, "RIFF/CDXA header"},
{meta_PS2_RXWS, "Sony RXWS 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_PS2_OMU, "Alter Echo OMU Header"},
{meta_DSP_STM, "Nintendo STM header"}, {meta_DSP_STM, "Intelligent Systems STM header"},
{meta_PS2_EXST, "EXST header"}, {meta_PS2_EXST, "Sony EXST header"},
{meta_PS2_SVAG, "Konami SVAG 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_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"},
{meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"},
{meta_PS2_MIC, "assume KOEI MIC file by .mic extension"}, {meta_PS2_MIC, "assume KOEI MIC file by .mic extension"},
{meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"}, {meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"},
{meta_DSP_MSS, "Double DSP header stereo by .mss extension"}, {meta_DSP_MSS, "Double DSP header stereo by .mss extension"},
{meta_DSP_GCM, "Double DSP header stereo by .gcm extension"}, {meta_DSP_GCM, "Double DSP header stereo by .gcm extension"},
{meta_DSP_WII_IDSP, "Wii IDSP Double DSP header"}, {meta_IDSP_TT, "Traveller's Tales IDSP header"},
{meta_RSTM_SPM, "Nintendo RSTM header and .brstmspm extension"}, {meta_RSTM_SPM, "Nintendo RSTM header (brstmspm)"},
{meta_RAW, "assumed RAW PCM file by .raw extension"}, {meta_RAW, "assumed RAW PCM file by .raw extension"},
{meta_PS2_VAGi, "Sony VAG Interleaved header (VAGi)"}, {meta_PS2_VAGi, "Sony VAGi header"},
{meta_PS2_VAGp, "Sony VAG Mono header (VAGp)"}, {meta_PS2_VAGp, "Sony VAGp header"},
{meta_PS2_VAGs, "Sony VAG Stereo header (VAGp)"}, {meta_PS2_pGAV, "Sony pGAV header"},
{meta_PS2_VAGm, "Sony VAG Mono header (VAGm)"},
{meta_PS2_pGAV, "Sony VAG Stereo Little Endian header (pGAV)"},
{meta_PSX_GMS, "assumed Grandia GMS file by .gms extension"}, {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_ILD, "ILD header"},
{meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"},
{meta_XBOX_WAVM, "Xbox WAVM raw header"}, {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, "Electronic Arts SCHl header (variable)"},
{meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"}, {meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"},
{meta_CAF, "tri-Crescendo CAF Header"}, {meta_CAF, "tri-Crescendo CAF Header"},
{meta_PS2_VPK, "VPK Header"}, {meta_VPK, "SCE America VPK Header"},
{meta_GENH, "GENH generic header"}, {meta_GENH, "GENH generic header"},
{meta_DSP_SADB, "Procyon Studio SADB header"}, {meta_DSP_SADB, "Procyon Studio SADB header"},
{meta_SADL, "Procyon Studio SADL 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_NAOMI_SPSD, "Naomi SPSD header"},
{meta_FFXI_BGW, "BGW BGMStream header"}, {meta_FFXI_BGW, "BGW BGMStream header"},
{meta_FFXI_SPW, "SPW SeWave header"}, {meta_FFXI_SPW, "SPW SeWave header"},
{meta_PS2_ASS, "ASS Header"}, {meta_PS2_ASS, "SystemSoft .ASS header"},
{meta_IDSP, "IDSP 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_UBI_JADE, "Ubisoft Jade RIFF header"},
{meta_PS2_SEG, "SEG (PS2) Header"}, {meta_PS2_SEG, "SEG (PS2) Header"},
{meta_XBOX_SEG, "SEG (XBOX) 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_SND, "Might and Magic SSND Header"},
{meta_PS2_VSF_TTA, "VSF with SMSS Header"}, {meta_PS2_VSF_TTA, "VSF with SMSS Header"},
{meta_ADS, "dhSS Header"}, {meta_ADS, "dhSS Header"},
{meta_WII_STR, "HOTD Overkill - STR+STH WII Header"},
{meta_PS2_MCG, "Gunvari MCG Header"}, {meta_PS2_MCG, "Gunvari MCG Header"},
{meta_ZSD, "ZSD Header"}, {meta_ZSD, "ZSD Header"},
{meta_RedSpark, "RedSpark Header"}, {meta_RedSpark, "RedSpark Header"},
@ -879,7 +912,7 @@ static const meta_info meta_info_list[] = {
{meta_SSS, "Namco .SSS raw header"}, {meta_SSS, "Namco .SSS raw header"},
{meta_PS2_GCM, "GCM 'MCG' Header"}, {meta_PS2_GCM, "GCM 'MCG' Header"},
{meta_PS2_SMPL, "Homura SMPL 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_PC_SMP, "Ghostbusters .smp Header"},
{meta_NGC_PDT, "Hudson .PDT header"}, {meta_NGC_PDT, "Hudson .PDT header"},
{meta_NGC_RKV, "Legacy of Kain - Blood Omen 2 RKV GC 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_B1S, "B1S header"},
{meta_PS2_WAD, "WAD header"}, {meta_PS2_WAD, "WAD header"},
{meta_DSP_XIII, "XIII dsp header"}, {meta_DSP_XIII, "XIII dsp header"},
{meta_NGC_DSP_STH_STR, "STH dsp header"},
{meta_DSP_CABELAS, "Cabelas games dsp header"}, {meta_DSP_CABELAS, "Cabelas games dsp header"},
{meta_PS2_ADM, "Dragon Quest V .ADM raw header"}, {meta_PS2_ADM, "Dragon Quest V .ADM raw header"},
{meta_PS2_LPCM, "LPCM 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_OTNS_ADP, "Omikron: The Nomad Soul ADP header"},
{meta_EB_SFX, "Excitebots .sfx header"}, {meta_EB_SFX, "Excitebots .sfx header"},
{meta_EB_SF0, "assumed Excitebots .sf0 by extension"}, {meta_EB_SF0, "assumed Excitebots .sf0 by extension"},
{meta_PS3_KLBS, "klBS Header"},
{meta_PS2_MTAF, "Konami MTAF header"}, {meta_PS2_MTAF, "Konami MTAF header"},
{meta_PS2_VAG1, "Konami VAG Mono header (VAG1)"}, {meta_PS2_VAG1, "Konami VAG1 header"},
{meta_PS2_VAG2, "Konami VAG Stereo header (VAG2)"}, {meta_PS2_VAG2, "Konami VAG2 header"},
{meta_TUN, "Lego Racers ALP header"}, {meta_TUN, "Lego Racers ALP header"},
{meta_WPD, "WPD 'DPW' header"}, {meta_WPD, "WPD 'DPW' header"},
{meta_MN_STR, "Mini Ninjas 'STR' 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_UBI_CKD, "Ubisoft CKD RIFF header"},
{meta_PS2_VBK, "PS2 VBK Header"}, {meta_PS2_VBK, "PS2 VBK Header"},
{meta_OTM, "Otomedius OTM Header"}, {meta_OTM, "Otomedius OTM Header"},
{meta_CSTM, "Nintendo 3DS CSTM Header"}, {meta_CSTM, "Nintendo CSTM Header"},
{meta_FSTM, "Nintendo Wii U FSTM Header"}, {meta_FSTM, "Nintendo FSTM Header"},
{meta_KT_WIIBGM, "Koei Tecmo WiiBGM Header"}, {meta_KT_WIIBGM, "Koei Tecmo WiiBGM Header"},
{meta_KTSS, "Koei Tecmo Nintendo Stream KTSS 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_WIIU_BTSND, "Nintendo Wii U Menu Boot Sound"},
{meta_MCA, "Capcom MCA header"}, {meta_MCA, "Capcom MCA header"},
{meta_XB3D_ADX, "Xenoblade 3D ADX 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_SVAG_SNK, "SNK SVAG header"},
{meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"}, {meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"},
{meta_FFMPEG, "FFmpeg supported file format"},
{meta_X360_CXS, "tri-Crescendo CXS header"}, {meta_X360_CXS, "tri-Crescendo CXS header"},
{meta_AKB, "Square-Enix AKB header"}, {meta_AKB, "Square-Enix AKB header"},
{meta_NUB_XMA, "Namco NUB XMA 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_H4M, "Hudson HVQM4 header"},
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"}, {meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
{meta_ASF, "Argonaut ASF 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
}; };

View file

@ -1,16 +1,18 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.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) { 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); frame_size = get_vgmstream_frame_size(vgmstream);
int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
int samples_this_block; samples_this_block = 0;
/* get samples in the current block */
if (vgmstream->current_block_samples) { if (vgmstream->current_block_samples) {
samples_this_block = 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... */ } 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; samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame;
} }
/* decode all samples */
while (samples_written < sample_count) { while (samples_written < sample_count) {
int samples_to_do; int samples_to_do;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { 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) { if (vgmstream->current_block_samples) {
samples_this_block = 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... */ } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */
@ -35,29 +38,28 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
continue; continue;
} }
/* probably block bug or EOF, next calcs would give wrong values and buffer segfaults */
if (samples_this_block < 0) { 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)); 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); 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; samples_to_do = sample_count - samples_written;
if (vgmstream->current_block_offset >= 0) { if (samples_to_do > 0) {
/* samples_this_block = 0 is allowed (empty block): do nothing then move to next block */ /* 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); 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;
}
}
samples_written += samples_to_do; samples_written += samples_to_do;
vgmstream->current_sample += samples_to_do; vgmstream->current_sample += 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 */ /* move to next block when all samples are consumed */
if (vgmstream->samples_into_block==samples_this_block if (vgmstream->samples_into_block == samples_this_block
/*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ //todo
block_update(vgmstream); 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); frame_size = get_vgmstream_frame_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
/* get samples in the current block */
if (vgmstream->current_block_samples) { if (vgmstream->current_block_samples) {
samples_this_block = 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... */ } 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 *
} }
} }
/* helper functions to parse new block */
static void block_update(VGMSTREAM * vgmstream) { void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
switch (vgmstream->layout_type) { switch (vgmstream->layout_type) {
case layout_blocked_ast: case layout_blocked_ast:
block_update_ast(vgmstream->next_block_offset,vgmstream); block_update_ast(block_offset,vgmstream);
break; break;
case layout_blocked_mxch: case layout_blocked_mxch:
block_update_mxch(vgmstream->next_block_offset,vgmstream); block_update_mxch(block_offset,vgmstream);
break; break;
case layout_blocked_halpst: case layout_blocked_halpst:
if (vgmstream->next_block_offset>=0) block_update_halpst(block_offset,vgmstream);
block_update_halpst(vgmstream->next_block_offset,vgmstream);
else
vgmstream->current_block_offset = -1;
break; break;
case layout_blocked_xa: case layout_blocked_xa:
block_update_xa(vgmstream->next_block_offset,vgmstream); block_update_xa(block_offset,vgmstream);
break; break;
case layout_blocked_ea_schl: case layout_blocked_ea_schl:
block_update_ea_schl(vgmstream->next_block_offset,vgmstream); block_update_ea_schl(block_offset,vgmstream);
break; break;
case layout_blocked_ea_1snh: case layout_blocked_ea_1snh:
block_update_ea_1snh(vgmstream->next_block_offset,vgmstream); block_update_ea_1snh(block_offset,vgmstream);
break; break;
case layout_blocked_caf: case layout_blocked_caf:
block_update_caf(vgmstream->next_block_offset,vgmstream); block_update_caf(block_offset,vgmstream);
break; break;
case layout_blocked_wsi: case layout_blocked_wsi:
block_update_wsi(vgmstream->next_block_offset,vgmstream); block_update_wsi(block_offset,vgmstream);
break; break;
case layout_blocked_str_snds: case layout_blocked_str_snds:
block_update_str_snds(vgmstream->next_block_offset,vgmstream); block_update_str_snds(block_offset,vgmstream);
break; break;
case layout_blocked_ws_aud: case layout_blocked_ws_aud:
block_update_ws_aud(vgmstream->next_block_offset,vgmstream); block_update_ws_aud(block_offset,vgmstream);
break; break;
case layout_blocked_matx: case layout_blocked_matx:
block_update_matx(vgmstream->next_block_offset,vgmstream); block_update_matx(block_offset,vgmstream);
break; break;
case layout_blocked_dec: case layout_blocked_dec:
block_update_dec(vgmstream->next_block_offset,vgmstream); block_update_dec(block_offset,vgmstream);
break; break;
case layout_blocked_emff_ps2: case layout_blocked_emff_ps2:
block_update_emff_ps2(vgmstream->next_block_offset,vgmstream); block_update_emff_ps2(block_offset,vgmstream);
break; break;
case layout_blocked_emff_ngc: case layout_blocked_emff_ngc:
block_update_emff_ngc(vgmstream->next_block_offset,vgmstream); block_update_emff_ngc(block_offset,vgmstream);
break; break;
case layout_blocked_gsb: case layout_blocked_gsb:
block_update_gsb(vgmstream->next_block_offset,vgmstream); block_update_gsb(block_offset,vgmstream);
break; break;
case layout_blocked_vs: case layout_blocked_vs:
block_update_vs(vgmstream->next_block_offset,vgmstream); block_update_vs(block_offset,vgmstream);
break; break;
case layout_blocked_xvas: case layout_blocked_xvas:
block_update_xvas(vgmstream->next_block_offset,vgmstream); block_update_xvas(block_offset,vgmstream);
break; break;
case layout_blocked_thp: case layout_blocked_thp:
block_update_thp(vgmstream->next_block_offset,vgmstream); block_update_thp(block_offset,vgmstream);
break; break;
case layout_blocked_filp: case layout_blocked_filp:
block_update_filp(vgmstream->next_block_offset,vgmstream); block_update_filp(block_offset,vgmstream);
break; break;
case layout_blocked_ivaud: case layout_blocked_ivaud:
block_update_ivaud(vgmstream->next_block_offset,vgmstream); block_update_ivaud(block_offset,vgmstream);
break; break;
case layout_blocked_ea_swvr: case layout_blocked_ea_swvr:
block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); block_update_ea_swvr(block_offset,vgmstream);
break; break;
case layout_blocked_adm: case layout_blocked_adm:
block_update_adm(vgmstream->next_block_offset,vgmstream); block_update_adm(block_offset,vgmstream);
break; break;
case layout_blocked_bdsp: case layout_blocked_bdsp:
block_update_bdsp(vgmstream->next_block_offset,vgmstream); block_update_bdsp(block_offset,vgmstream);
break; break;
case layout_blocked_tra: case layout_blocked_tra:
block_update_tra(vgmstream->next_block_offset,vgmstream); block_update_tra(block_offset,vgmstream);
break; break;
case layout_blocked_ps2_iab: case layout_blocked_ps2_iab:
block_update_ps2_iab(vgmstream->next_block_offset,vgmstream); block_update_ps2_iab(block_offset,vgmstream);
break; break;
case layout_blocked_ps2_strlr: case layout_blocked_ps2_strlr:
block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream); block_update_ps2_strlr(block_offset,vgmstream);
break; break;
case layout_blocked_rws: case layout_blocked_rws:
block_update_rws(vgmstream->next_block_offset,vgmstream); block_update_rws(block_offset,vgmstream);
break; break;
case layout_blocked_hwas: case layout_blocked_hwas:
block_update_hwas(vgmstream->next_block_offset,vgmstream); block_update_hwas(block_offset,vgmstream);
break; break;
case layout_blocked_ea_sns: case layout_blocked_ea_sns:
block_update_ea_sns(vgmstream->next_block_offset,vgmstream); block_update_ea_sns(block_offset,vgmstream);
break; break;
case layout_blocked_awc: case layout_blocked_awc:
block_update_awc(vgmstream->next_block_offset,vgmstream); block_update_awc(block_offset,vgmstream);
break; break;
case layout_blocked_vgs: case layout_blocked_vgs:
block_update_vgs(vgmstream->next_block_offset,vgmstream); block_update_vgs(block_offset,vgmstream);
break; break;
case layout_blocked_vawx: case layout_blocked_vawx:
block_update_vawx(vgmstream->next_block_offset,vgmstream); block_update_vawx(block_offset,vgmstream);
break; break;
case layout_blocked_xvag_subsong: case layout_blocked_xvag_subsong:
block_update_xvag_subsong(vgmstream->next_block_offset,vgmstream); block_update_xvag_subsong(block_offset,vgmstream);
break; break;
case layout_blocked_ea_wve_au00: 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; break;
case layout_blocked_ea_wve_ad10: 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; break;
case layout_blocked_sthd: case layout_blocked_sthd:
block_update_sthd(vgmstream->next_block_offset,vgmstream); block_update_sthd(block_offset,vgmstream);
break; break;
case layout_blocked_h4m: case layout_blocked_h4m:
block_update_h4m(vgmstream->next_block_offset,vgmstream); block_update_h4m(block_offset,vgmstream);
break; break;
default: case layout_blocked_xa_aiff:
block_update_xa_aiff(block_offset,vgmstream);
break;
default: /* not a blocked layout */
break; break;
} }
} }

View file

@ -1,18 +1,21 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.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) { void block_update_ast(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i; int i;
vgmstream->current_block_offset = block_offset; size_t block_data, header_size;
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;
for (i=0;i<vgmstream->channels;i++) { /* 0x00: "BLCK", rest: null */
vgmstream->ch[i].offset = vgmstream->current_block_offset + block_data = read_32bitBE(block_offset+0x04,streamFile);
0x20 + vgmstream->current_block_size*i; 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;
} }
} }

View file

@ -11,12 +11,23 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
size_t file_size = get_streamfile_size(streamFile); 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) { while (block_offset < file_size) {
uint32_t id = read_32bitBE(block_offset+0x00,streamFile); uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
block_size = read_32bitLE(block_offset+0x04,streamFile); /* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */
if (block_size > 0x00F00000) /* BE in SAT, but one file may have both BE and LE chunks */ if (guess_endianness32bit(block_offset+0x04,streamFile))
block_size = read_32bitBE(block_offset+0x04,streamFile); block_size = read_32bitBE(block_offset+0x04,streamFile);
else
block_size = read_32bitLE(block_offset+0x04,streamFile);
block_header = 0; 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 */ else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
block_header = 0x08; block_header = 0x08;
} }
else if (id == 0x00000000) { else if (id == 0x00000000 || id == 0xFFFFFFFF || id == 0x31534E65) { /* EOF or "1SNe" */
vgmstream->current_block_samples = -1;
break; break;
} }
@ -45,6 +57,8 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
vgmstream->current_block_offset = block_offset; vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size; vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_size = block_size - block_header; vgmstream->current_block_size = block_size - block_header;
if (vgmstream->current_block_samples == -1)
return;
/* set new channel offsets and block sizes */ /* set new channel offsets and block sizes */
@ -72,7 +86,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
break; break;
case coding_DVI_IMA: 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_samples = read_32bit(block_offset + block_header, streamFile);
vgmstream->current_block_size = 0; // - (0x04 + 0x08*vgmstream->channels); /* should be equivalent */ 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; 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, //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); // "EA 1SHN blocked: different expected vs block num samples at %lx\n", block_offset);
} }
else { else {
for(i = 0; i < vgmstream->channels; i++) { for(i = 0; i < vgmstream->channels; i++) {

View file

@ -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); 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); block_size = read_32bitBE(block_offset + 0x04,streamFile);
else else
block_size = read_32bitLE(block_offset + 0x04,streamFile); 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) */ /* 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++) { 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_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].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile);

View file

@ -34,7 +34,7 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) {
uint32_t frame_samples = read_32bitBE(block_offset+0x08, streamFile); uint32_t frame_samples = read_32bitBE(block_offset+0x08, streamFile);
size_t block_skip; size_t block_skip;
if (vgmstream->codec_version & 0x80) { if (vgmstream->codec_config & 0x80) {
frame_samples /= 2; /* ??? */ frame_samples /= 2; /* ??? */
} }
@ -42,40 +42,21 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) {
block_size = 0x08 + frame_size; block_size = 0x08 + frame_size;
block_samples = frame_samples; block_samples = frame_samples;
/* skip data from other audio tracks */ /* 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; 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 %"PRIx64"\n", frame_format, (off64_t)block_offset);
VGM_ASSERT(frame_format == 1, "H4M: unknown frame_format %x at %lx\n", frame_format, 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++) { 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; 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--;
}
}
else { else {
block_size = 0x08 + frame_size; block_size = 0x08 + frame_size;
block_samples = 0; /* signal new block_update_h4m */ block_samples = 0; /* signal new block_update_h4m */

View file

@ -6,6 +6,7 @@ void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile; STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i; int i;
off_t channel_block_size; off_t channel_block_size;
//int is_first_offset =
/* assume that all channels have the same size for this block */ /* 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++) { for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + channel_block_size*i + 0x10; 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;
}
}
} }

View file

@ -2,72 +2,66 @@
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../vgmstream.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) { void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i; int i;
int8_t currentChannel=0; size_t block_samples;
int8_t subAudio=0; 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 /* XA mode2/form2 sector, size 0x930
* 0x00: sync word * 0x00: sync word
* 0x0c: header = minute, second, sector, mode (always 0x02) * 0x0c: header = minute, second, sector, mode (always 0x02)
* 0x10: subheader = file, channel (marker), submode flags, xa header * 0x10: subheader = file, channel (substream marker), submode flags, xa header
* 0x14: subheader again * 0x14: subheader again (for error correction)
* 0x18: data * 0x18: data
* 0x918: unused * 0x918: unused
* 0x92c: EDC/checksum or null * 0x92c: EDC/checksum or null
* 0x930: end * 0x930: end
* (in non-blocked ISO 2048 mode1/data chunks are 0x800)
*/ */
/* submode flag bits (typical audio value = 0x64) /* channel markers supposedly could be used to interleave streams, ex. audio languages within video
* - 7: end of file * (extractors may split .XA using channels?) */
* - 6: real time mode VGM_ASSERT(block_offset + 0x930 < get_streamfile_size(streamFile) &&
* - 5: sector form (0=form1, 1=form2) (uint8_t)read_8bit(block_offset + 0x000 + 0x11,streamFile) !=
* - 4: trigger (for application) (uint8_t)read_8bit(block_offset + 0x930 + 0x11,streamFile),
* - 3: data sector "XA block: subchannel change at %"PRIx64"\n", (off64_t)block_offset);
* - 2: audio sector
* - 1: video sector /* submode flag bits (typical audio value = 0x64 01100100)
* - 0: end of audio * - 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 ? /* audio sector must set/not set certain flags, as per spec */
if (vgmstream->xa_sector_length == (18*0x80)) { if (!(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02)) {
vgmstream->xa_sector_length = 0; if (xa_submode & 0x20) {
/* form2 audio: size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */
// 0x30 of unused bytes/sector :( block_samples = (28*8 / vgmstream->channels) * 18;
if (!vgmstream->xa_headerless) { }
block_offset += 0x30; else { /* rare, found with empty audio [Glint Glitters (PS1), Dance! Dance! Dance! (PS1)] */
begin: /* form1 audio: size 0x800, 16 frames of size 0x80 with 8 subframes of 28 samples (rest is garbage/other data) */
// Search for selected channel & valid audio block_samples = (28*8 / vgmstream->channels) * 16;
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;
} }
} }
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_offset = block_offset;
vgmstream->current_block_samples = block_samples;
vgmstream->next_block_offset = block_offset + 0x930;
// Quid : how to stop the current channel ??? for (i = 0; i < vgmstream->channels; i++) {
// i set up 0 to current_block_size to make vgmstream not playing bad samples vgmstream->ch[i].offset = block_offset + 0x18;
// 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;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset;
} }
} }

View file

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

View file

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

View file

@ -1,64 +1,92 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.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) { void render_vgmstream_interleave(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 has_interleave_last = vgmstream->interleave_last_block_size && vgmstream->channels > 1;
int frame_size = get_vgmstream_frame_size(vgmstream);
int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
int samples_this_block;
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) {
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_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) {
frame_size = get_vgmstream_frame_size(vgmstream); frame_size = get_vgmstream_frame_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame;
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;
}
/* mono interleaved stream with no layout set, just behave like flat layout */
if (samples_this_block == 0 && vgmstream->channels == 1)
samples_this_block = vgmstream->num_samples;
while (samples_written < sample_count) {
int samples_to_do;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
/* 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; continue;
} }
samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); 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) if (samples_to_do == 0) { /* happens when interleave is not set */
samples_to_do=sample_count-samples_written; 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); decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);
samples_written += samples_to_do; samples_written += samples_to_do;
vgmstream->current_sample += 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; /* move to next interleaved block when all samples are consumed */
if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && if (vgmstream->samples_into_block == samples_this_block) {
int ch;
if (has_interleave_last &&
vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { vgmstream->current_sample + samples_this_block > vgmstream->num_samples) {
/* adjust values again if inside last interleave */
frame_size = get_vgmstream_shortframe_size(vgmstream); frame_size = get_vgmstream_shortframe_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream);
samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame;
for (chan=0;chan<vgmstream->channels;chan++) if (samples_this_block == 0 && vgmstream->channels == 1)
vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*(vgmstream->channels-chan)+vgmstream->interleave_last_block_size*chan; samples_this_block = vgmstream->num_samples;
} else {
for (chan=0;chan<vgmstream->channels;chan++) for (ch = 0; ch < vgmstream->channels; ch++) {
vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*vgmstream->channels; 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;
} }
} }

View file

@ -1,45 +1,51 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.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_BUF_SIZE 512
#define LAYER_MAX_CHANNELS 6 /* at least 2, but let's be generous */ #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) { void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) {
sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS]; int samples_written = 0;
int32_t samples_done = 0;
layered_layout_data *data = vgmstream->layout_data; 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) while (samples_written < sample_count) {
samples_to_do = sample_count - samples_done; 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++) { for (layer = 0; layer < data->layer_count; layer++) {
int s,l_ch; int s, layer_ch;
int layer_channels = data->layers[layer]->channels; 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]); 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++) { for (s = 0; s < samples_to_do; s++) {
size_t layer_sample = s*layer_channels + l_ch; size_t layer_sample = s*layer_channels + layer_ch;
size_t buffer_sample = (samples_done+s)*vgmstream->channels + (layer*layer_channels+l_ch); size_t buffer_sample = (samples_written+s)*vgmstream->channels + ch;
buffer[buffer_sample] = interleave_buf[layer_sample]; buffer[buffer_sample] = interleave_buf[layer_sample];
} }
ch++;
}
} }
} samples_written += samples_to_do;
vgmstream->current_sample = data->layers[0]->current_sample; /* just in case it's used for info */
samples_done += samples_to_do; //vgmstream->samples_into_block = 0; /* handled in each layer */
} }
} }

View file

@ -6,6 +6,7 @@
/* blocked layouts */ /* blocked layouts */
void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); 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_ast(off_t block_ofset, VGMSTREAM * vgmstream);
void block_update_mxch(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_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_sthd(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_h4m(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream);
/* other layouts */ /* other layouts */
void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); 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); void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View file

@ -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_written<sample_count) {
int samples_to_do;
if (vgmstream->loop_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;
}
}

View file

@ -2,35 +2,54 @@
#include "../vgmstream.h" #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) { 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; segmented_layout_data *data = vgmstream->layout_data;
//int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
while (samples_written<sample_count) {
while (samples_written < sample_count) {
int samples_to_do; int samples_to_do;
int samples_this_block = data->segments[data->current_segment]->num_samples; int samples_this_block = data->segments[data->current_segment]->num_samples;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
//todo can only loop in a segment start /* handle looping, finding loop segment */
// (for arbitrary values find loop segment from loop_start_sample, and skip N samples until loop start) int loop_segment = 0, samples = 0, loop_samples_skip = 0;
data->current_segment = data->loop_segment; 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]); reset_vgmstream(data->segments[data->current_segment]);
vgmstream->samples_into_block = 0; vgmstream->samples_into_block = 0;
continue; continue;
} }
samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); samples_to_do = vgmstream_samples_to_do(samples_this_block, sample_count, vgmstream);
if (samples_to_do > sample_count - samples_written)
if (samples_written+samples_to_do > sample_count) samples_to_do = sample_count - samples_written;
samples_to_do=sample_count-samples_written;
/* detect segment change and restart */
if (samples_to_do == 0) { if (samples_to_do == 0) {
data->current_segment++; data->current_segment++;
reset_vgmstream(data->segments[data->current_segment]); reset_vgmstream(data->segments[data->current_segment]);
vgmstream->samples_into_block = 0; vgmstream->samples_into_block = 0;
continue; continue;
} }
@ -40,7 +59,7 @@ void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM
samples_written += samples_to_do; samples_written += samples_to_do;
vgmstream->current_sample += samples_to_do; vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block+=samples_to_do; vgmstream->samples_into_block += samples_to_do;
} }
} }

View file

@ -280,7 +280,7 @@ VGMSTREAM * init_vgmstream_cstr(STREAMFILE *streamFile) {
{ {
int i; int i;
for (i=0;i<2;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; if (!vgmstream->ch[i].streamfile) goto fail;

View file

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

View file

@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
int loop_flag = 0, channel_count = 0; int loop_flag = 0, channel_count = 0;
int32_t sample_count, loop_start_sample = 0, loop_end_sample = 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; segmented_layout_data *data = NULL;
int table_error = 0; int table_error = 0;
@ -102,7 +102,6 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
if (!loop_flag && segment_loop_flag) { if (!loop_flag && segment_loop_flag) {
loop_start_sample = sample_count; loop_start_sample = sample_count;
loop_segment = i;
} }
sample_count += data->segments[i]->num_samples; sample_count += data->segments[i]->num_samples;
@ -130,7 +129,6 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_segmented; vgmstream->layout_type = layout_segmented;
vgmstream->layout_data = data; vgmstream->layout_data = data;
data->loop_segment = loop_segment;
return vgmstream; return vgmstream;

View file

@ -1,29 +1,48 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.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)] */ /* ACM - InterPlay infinity engine games [Planescape: Torment (PC), Baldur's Gate (PC)] */
VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
int loop_flag = 0, channel_count, sample_rate, num_samples; int loop_flag = 0, channel_count, sample_rate, num_samples;
int force_channel_number = 0;
acm_codec_data *data = NULL; acm_codec_data *data = NULL;
/* checks */ /* 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; 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; 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 */ /* init decoder */
{ {
data = init_acm(streamFile); ACMStream *handle;
data = init_acm(streamFile, force_channel_number);
if (!data) goto fail; if (!data) goto fail;
channel_count = data->file->info.channels; handle = data->handle;
sample_rate = data->file->info.rate; channel_count = handle->info.channels;
num_samples = data->file->total_values / data->file->info.channels; sample_rate = handle->info.rate;
num_samples = handle->total_values / handle->info.channels;
} }

View file

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

View file

@ -10,7 +10,7 @@
#define MAX_TEST_FRAMES (INT_MAX/0x8000) #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 */ /* ADX - CRI Middleware format */
VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
@ -75,13 +75,13 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
/* encryption */ /* encryption */
if (version_signature == 0x0408) { 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; coding_type = coding_CRI_ADX_enc_8;
version_signature = 0x0400; version_signature = 0x0400;
} }
} }
else if (version_signature == 0x0409) { 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; coding_type = coding_CRI_ADX_enc_9;
version_signature = 0x0400; version_signature = 0x0400;
} }
@ -140,6 +140,14 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
* 0x00 (4): "AINF"; 0x04 (4): size; 0x08 (10): str_id * 0x00 (4): "AINF"; 0x04 (4): size; 0x08 (10): str_id
* 0x18 (2): volume (0=base/max?, negative=reduce) * 0x18 (2): volume (0=base/max?, negative=reduce)
* 0x1c (2): pan l; 0x1e (2): pan r (0=base, max +-128) */ * 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 */ else if (version_signature == 0x0500) { /* found in some SFD: Buggy Heat, appears to have no loop */
header_type = meta_ADX_05; header_type = meta_ADX_05;
@ -238,20 +246,19 @@ fail:
/* return 0 if not found, 1 if found and set parameters */ /* 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 * scales = NULL;
uint16_t * prescales = NULL; uint16_t * prescales = NULL;
int bruteframe=0,bruteframecount=-1; int bruteframe = 0, bruteframe_count = -1;
int startoff, endoff; int startoff, endoff;
int rc = 0; int i, rc = 0;
/* try to find key in external file first */ /* try to find key in external file first */
{ {
uint8_t keybuf[6]; 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_start = get_16bitBE(keybuf+0);
*xor_mult = get_16bitBE(keybuf+2); *xor_mult = get_16bitBE(keybuf+2);
*xor_add = get_16bitBE(keybuf+4); *xor_add = get_16bitBE(keybuf+4);
@ -259,86 +266,80 @@ static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_
} }
} }
/* setup totals */
/* 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? */
{ {
int framecount=(endoff-startoff)/18; int frame_count;
if (framecount<bruteframecount || bruteframecount<0)
bruteframecount=framecount; startoff = read_16bitBE(2, streamFile) + 4;
endoff = (read_32bitBE(12, streamFile) + 31) / 32 * 18 * read_8bit(7, streamFile) + startoff;
frame_count = (endoff - startoff) / 18;
if (frame_count < bruteframe_count || bruteframe_count < 0)
bruteframe_count = frame_count;
} }
/* find longest run of nonzero frames */ /* find longest run of nonzero frames */
{ {
int longest=-1,longest_length=-1; int longest = -1, longest_length = -1;
int i; int length = 0;
int length=0; for (i = 0; i < bruteframe_count; i++) {
for (i=0;i<bruteframecount;i++) { static const unsigned char zeroes[18] = {0};
static const unsigned char zeroes[18]={0};
unsigned char buf[18]; unsigned char buf[18];
read_streamfile(buf, startoff+i*18, 18, file); read_streamfile(buf, startoff + i * 18, 18, streamFile);
if (memcmp(zeroes,buf,18)) length++; if (memcmp(zeroes, buf, 18))
else length=0; length++;
else
length = 0;
if (length > longest_length) { if (length > longest_length) {
longest_length=length; longest_length = length;
longest=i-length+1; longest = i - length + 1;
if (longest_length >= 0x8000) break; if (longest_length >= 0x8000)
break;
} }
} }
if (longest==-1) { if (longest == -1) {
goto find_key_cleanup; goto find_key_cleanup;
} }
bruteframecount = longest_length; bruteframe_count = longest_length;
bruteframe = longest; bruteframe = longest;
} }
{
/* try to guess key */ /* try to guess key */
{
const adxkey_info * keys = NULL; const adxkey_info * keys = NULL;
int keycount = 0, keymask = 0; int keycount = 0, keymask = 0;
int scales_to_do; int scales_to_do;
int key_id; int key_id;
/* allocate storage for scales */ /* 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)); scales = malloc(scales_to_do*sizeof(uint16_t));
if (!scales) { if (!scales) goto find_key_cleanup;
goto find_key_cleanup;
}
/* prescales are those scales before the first frame we test /* prescales are those scales before the first frame we test
* against, we use these to compute the actual start */ * against, we use these to compute the actual start */
if (bruteframe > 0) { if (bruteframe > 0) {
int i;
/* allocate memory for the prescales */ /* allocate memory for the prescales */
prescales = malloc(bruteframe*sizeof(uint16_t)); prescales = malloc(bruteframe*sizeof(uint16_t));
if (!prescales) { if (!prescales) goto find_key_cleanup;
goto find_key_cleanup;
}
/* read the prescales */ /* read the prescales */
for (i=0; i<bruteframe; i++) { for (i=0; i<bruteframe; i++) {
prescales[i] = read_16bitBE(startoff+i*18, file); prescales[i] = read_16bitBE(startoff+i*18, streamFile);
} }
} }
/* read in the scales */ /* read in the scales */
{
int i;
for (i=0; i < scales_to_do; i++) { for (i=0; i < scales_to_do; i++) {
scales[i] = read_16bitBE(startoff+(bruteframe+i)*18, file); scales[i] = read_16bitBE(startoff+(bruteframe+i)*18, streamFile);
}
} }
if (type == 8) if (type == 8) {
{
keys = adxkey8_list; keys = adxkey8_list;
keycount = adxkey8_list_count; keycount = adxkey8_list_count;
keymask = 0x6000; keymask = 0x6000;
} }
else if (type == 9) else if (type == 9) {
{
/* smarter XOR as seen in PSO2. The scale is technically 13 bits, /* smarter XOR as seen in PSO2. The scale is technically 13 bits,
* but the maximum value assigned by the encoder is 0x1000. * but the maximum value assigned by the encoder is 0x1000.
* This is written to the ADX file as 0xFFF, leaving the high bit * This is written to the ADX file as 0xFFF, leaving the high bit
@ -348,50 +349,70 @@ static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_
keymask = 0x1000; keymask = 0x1000;
} }
/* guess each of the keys */ /* try all keys until one decrypts correctly vs expected values */
for (key_id=0;key_id<keycount;key_id++) { for (key_id = 0; key_id < keycount; key_id++) {
/* test pre-scales */ uint16_t key_xor, key_mul, key_add;
uint16_t xor = keys[key_id].start; uint16_t xor, mul, add;
uint16_t mult = keys[key_id].mult;
uint16_t add = keys[key_id].add;
int i;
#ifdef ADX_VERIFY_DERIVED_KEYS /* get pre-derived XOR values or derive if needed */
{ if (keys[key_id].start || keys[key_id].mult || keys[key_id].add) {
uint16_t test_start, test_mult, test_add; key_xor = keys[key_id].start;
if (type == 8 && keys[key_id].key8) { key_mul = keys[key_id].mult;
process_cri_key8(keys[key_id].key8, &test_start, &test_mult, &test_add); key_add = keys[key_id].add;
VGM_LOG("key8: pre=%04x %04x %04x vs calc=%04x %04x %04x = %s (\"%s\")\n", }
xor,mult,add, test_start,test_mult,test_add, xor==test_start && mult==test_mult && add==test_add ? "ok" : "ko", keys[key_id].key8); else if (type == 8 && keys[key_id].key8) {
derive_adx_key8(keys[key_id].key8, &key_xor, &key_mul, &key_add);
} }
else if (type == 9 && keys[key_id].key9) { else if (type == 9 && keys[key_id].key9) {
process_cri_key9(keys[key_id].key9, &test_start, &test_mult, &test_add); derive_adx_key9(keys[key_id].key9, &key_xor, &key_mul, &key_add);
}
else {
VGM_LOG("ADX: incorrectly defined key id=%i\n", key_id);
continue;
}
/* temp test values */
xor = key_xor;
mul = key_mul;
add = key_add;
#if 0
/* derive and print all keys in the list, quick validity test */
{
uint16_t test_xor, test_mul, test_add;
xor = keys[key_id].start;
mul = keys[key_id].mult;
add = keys[key_id].add;
if (type == 8 && keys[key_id].key8) {
derive_adx_key8(keys[key_id].key8, &test_xor, &test_mul, &test_add);
VGM_LOG("key8: pre=%04x %04x %04x vs calc=%04x %04x %04x = %s (\"%s\")\n",
xor,mul,add, test_xor,test_mul,test_add,
xor==test_xor && mul==test_mul && add==test_add ? "ok" : "ko", keys[key_id].key8);
}
else if (type == 9 && keys[key_id].key9) {
derive_adx_key9(keys[key_id].key9, &test_xor, &test_mul, &test_add);
VGM_LOG("key9: pre=%04x %04x %04x vs calc=%04x %04x %04x = %s (%"PRIu64")\n", VGM_LOG("key9: pre=%04x %04x %04x vs calc=%04x %04x %04x = %s (%"PRIu64")\n",
xor,mult,add, test_start,test_mult,test_add, xor==test_start && mult==test_mult && add==test_add ? "ok" : "ko", keys[key_id].key9); xor,mul,add, test_xor,test_mul,test_add,
xor==test_xor && mul==test_mul && add==test_add ? "ok" : "ko", keys[key_id].key9);
} }
continue; continue;
} }
#endif #endif
for (i=0;i<bruteframe &&
((prescales[i]&keymask)==(xor&keymask) ||
prescales[i]==0);
i++) {
xor = xor * mult + add;
}
if (i == bruteframe) /* test vs prescales while xor looks valid */
{ for (i = 0; i < bruteframe && ((prescales[i] & keymask) == (xor & keymask) || prescales[i] == 0); i++) {
/* test */ xor = xor * mul + add;
for (i=0;i<scales_to_do &&
(scales[i]&keymask)==(xor&keymask);i++) {
xor = xor * mult + add;
} }
if (i == scales_to_do) if (i == bruteframe) {
{ /* test vs scales while xor looks valid */
*xor_start = keys[key_id].start; for (i = 0; i < scales_to_do && (scales[i] & keymask) == (xor & keymask); i++) {
*xor_mult = keys[key_id].mult; xor = xor * mul + add;
*xor_add = keys[key_id].add; }
/* key is good */
if (i == scales_to_do) {
*xor_start = key_xor;
*xor_mult = key_mul;
*xor_add = key_add;
rc = 1; rc = 1;
goto find_key_cleanup; goto find_key_cleanup;
@ -401,8 +422,7 @@ static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_
} }
find_key_cleanup: find_key_cleanup:
if (scales) free(scales); free(scales);
if (prescales) free(prescales); free(prescales);
return rc; return rc;
} }

View file

@ -2,13 +2,6 @@
#define _ADX_KEYS_H_ #define _ADX_KEYS_H_
/* Tests each start/mult/add vs derived key8/9 (if provided), as most where brute forced.
* Ie. uncommenting this and adding a new key8/key9 + compiling with VGM_DEBUG_OUTPUT
* will print its derived key (useful as games often use the same key for hca and adx type 9).
* Mainly for debugging purposes (info from VGAudio / ADX_Decoder). */
//#define ADX_VERIFY_DERIVED_KEYS 1
typedef struct { typedef struct {
uint16_t start,mult,add; /* XOR values derived from the actual key */ uint16_t start,mult,add; /* XOR values derived from the actual key */
char* key8; /* keystring used by type 8 encryption */ char* key8; /* keystring used by type 8 encryption */
@ -19,6 +12,7 @@ typedef struct {
* List of known keys, cracked from the sound files. * List of known keys, cracked from the sound files.
* Keystrings (type 8) and keycodes (type 9) from VGAudio / game's executables / 2ch.net. * Keystrings (type 8) and keycodes (type 9) from VGAudio / game's executables / 2ch.net.
* Multiple keys may work for a game due to how they are derived. * Multiple keys may work for a game due to how they are derived.
* start/mult/add are optional (0,0,0) if key8/9 are provided, but take priority if given.
*/ */
static const adxkey_info adxkey8_list[] = { static const adxkey_info adxkey8_list[] = {
@ -179,6 +173,18 @@ static const adxkey_info adxkey8_list[] = {
/* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */ /* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */
{0x55d9,0x46d3,0x5b01, "SONMYOJI",0}, {0x55d9,0x46d3,0x5b01, "SONMYOJI",0},
/* Girls Bravo: Romance 15's [PS2] */
{0x658f,0x4a89,0x5213, "GBRAVO",0},
/* Kashimashi! Girl Meets Girl - Hajimete no Natsu Monogatari (PS2) */
{0x6109,0x5135,0x673f, "KASHIM",0},
/* Bakumatsu Renka - Karyuu Kenshiden (PS2) */
{0x4919,0x612d,0x4919, "RENRENKA22",0},
/* Tensei Hakkenshi - Fuumaroku (PS2) */
{0x5761,0x6283,0x4531, "HAKKEN",0},
}; };
static const adxkey_info adxkey9_list[] = { static const adxkey_info adxkey9_list[] = {
@ -216,9 +222,7 @@ static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list
static const int adxkey9_list_count = sizeof(adxkey9_list) / sizeof(adxkey9_list[0]); static const int adxkey9_list_count = sizeof(adxkey9_list) / sizeof(adxkey9_list[0]);
#ifdef ADX_VERIFY_DERIVED_KEYS /* preloaded list used to derive keystrings from ADX_Decoder (see VGAudio for how to calculate) */
/* used to derive keystrings, see VGAudio for how to calculate */
static const uint16_t key8_primes[0x400] = { static const uint16_t key8_primes[0x400] = {
0x401B,0x4021,0x4025,0x402B,0x4031,0x403F,0x4043,0x4045,0x405D,0x4061,0x4067,0x406D,0x4087,0x4091,0x40A3,0x40A9, 0x401B,0x4021,0x4025,0x402B,0x4031,0x403F,0x4043,0x4045,0x405D,0x4061,0x4067,0x406D,0x4087,0x4091,0x40A3,0x40A9,
0x40B1,0x40B7,0x40BD,0x40DB,0x40DF,0x40EB,0x40F7,0x40F9,0x4109,0x410B,0x4111,0x4115,0x4121,0x4133,0x4135,0x413B, 0x40B1,0x40B7,0x40BD,0x40DB,0x40DF,0x40EB,0x40F7,0x40F9,0x4109,0x410B,0x4111,0x4115,0x4121,0x4133,0x4135,0x413B,
@ -286,7 +290,7 @@ static const uint16_t key8_primes[0x400] = {
0x6779,0x6781,0x6785,0x6791,0x67AB,0x67BD,0x67C1,0x67CD,0x67DF,0x67E5,0x6803,0x6809,0x6811,0x6817,0x682D,0x6839, 0x6779,0x6781,0x6785,0x6791,0x67AB,0x67BD,0x67C1,0x67CD,0x67DF,0x67E5,0x6803,0x6809,0x6811,0x6817,0x682D,0x6839,
}; };
static void process_cri_key8(const char * key8, uint16_t * out_start, uint16_t * out_mult, uint16_t * out_add) { static void derive_adx_key8(const char * key8, uint16_t * out_start, uint16_t * out_mult, uint16_t * out_add) {
size_t key_size; size_t key_size;
uint16_t start = 0, mult = 0, add = 0; uint16_t start = 0, mult = 0, add = 0;
int i; int i;
@ -312,7 +316,7 @@ end:
} }
static void process_cri_key9(uint64_t key9, uint16_t * out_start, uint16_t * out_mult, uint16_t * out_add) { static void derive_adx_key9(uint64_t key9, uint16_t * out_start, uint16_t * out_mult, uint16_t * out_add) {
uint16_t start = 0, mult = 0, add = 0; uint16_t start = 0, mult = 0, add = 0;
/* 0 is ignored by CRI's encoder, only from 1..18446744073709551615 */ /* 0 is ignored by CRI's encoder, only from 1..18446744073709551615 */
@ -336,6 +340,4 @@ end:
*out_add = add; *out_add = add;
} }
#endif
#endif/*_ADX_KEYS_H_*/ #endif/*_ADX_KEYS_H_*/

View file

@ -46,7 +46,7 @@ VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile) {
int i; int i;
/* both channels use same buffer, as interleave is so small */ /* 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; if (!chstreamfile) goto fail;
for (i=0;i<channel_count;i++) { for (i=0;i<channel_count;i++) {

View file

@ -42,7 +42,8 @@ VGMSTREAM * init_vgmstream_agsc(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_NGC_DSP; vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_none; 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++) { for (i=0;i<16;i++) {
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(header_offset+0xf6+i*2,streamFile); vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(header_offset+0xf6+i*2,streamFile);

View file

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

View file

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

View file

@ -2,7 +2,7 @@
#include "../layout/layout.h" #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) { static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) {
uint8_t buf[10]; uint8_t buf[10];
int32_t exponent; int32_t exponent;
@ -26,8 +26,7 @@ static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) {
return mantissa*((buf[0]&0x80)?-1:1); return mantissa*((buf[0]&0x80)?-1:1);
} }
static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset, static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset, int marker_id) {
int marker_id) {
uint16_t marker_count; uint16_t marker_count;
int i; int i;
off_t marker_offset; 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 */ /* Audio Interchange File Format AIFF/AIFF-C - from Mac/3DO games */
VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0;
off_t file_size = -1; size_t file_size;
int channel_count = 0; coding_t coding_type = 0;
int sample_count = 0; int channel_count = 0, sample_count = 0, sample_size = 0, sample_rate = 0;
int sample_size = 0; int interleave = 0;
int sample_rate = 0;
int coding_type = -1;
off_t start_offset = -1;
int interleave = -1;
int loop_flag = 0; int loop_flag = 0;
int32_t loop_start = -1; int32_t loop_start = 0, loop_end = 0;
int32_t loop_end = -1;
int AIFFext = 0; int is_aiff_ext = 0, is_aifc_ext = 0, is_aiff = 0, is_aifc = 0;
int AIFCext = 0; int FormatVersionChunkFound = 0, CommonChunkFound = 0, SoundDataChunkFound = 0, MarkerChunkFound = 0, InstrumentChunkFound = 0;
int AIFF = 0; off_t MarkerChunkOffset = -1, InstrumentChunkOffset = -1;
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;
/* checks */ /* checks */
/* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC /* .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")) { if (check_extensions(streamFile, "aif")) {
AIFCext = 1; is_aifc_ext = 1;
AIFFext = 1; is_aiff_ext = 1;
} }
else if (check_extensions(streamFile, "aifc,aifcl,afc,cbd2,bgm")) { else if (check_extensions(streamFile, "aifc,aifcl,afc,cbd2,bgm")) {
AIFCext = 1; is_aifc_ext = 1;
} }
else if (check_extensions(streamFile, "aiff,aiffl")) { else if (check_extensions(streamFile, "aiff,acm,adp,ai,aiffl")) {
AIFFext = 1; is_aiff_ext = 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 { else {
goto fail; goto fail;
} }
file_size = get_streamfile_size(streamFile); 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 */ /* read through chunks to verify format and find metadata */
{ {
@ -125,7 +111,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
while (current_chunk < file_size) { while (current_chunk < file_size) {
uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); 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 /* chunks must be padded to an even number of bytes but chunk
* size does not include that padding */ * size does not include that padding */
@ -136,14 +122,14 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
switch(chunk_type) { switch(chunk_type) {
case 0x46564552: /* "FVER" (version info) */ case 0x46564552: /* "FVER" (version info) */
if (FormatVersionChunkFound) goto fail; 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; FormatVersionChunkFound = 1;
/* specific size */ /* specific size */
if (chunk_size != 4) goto fail; if (chunk_size != 4) goto fail;
/* Version 1 of AIFF-C spec timestamp */ /* 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; break;
case 0x434F4D4D: /* "COMM" (main header) */ case 0x434F4D4D: /* "COMM" (main header) */
@ -153,11 +139,11 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
channel_count = read_16bitBE(current_chunk+8,streamFile); channel_count = read_16bitBE(current_chunk+8,streamFile);
if (channel_count <= 0) goto fail; 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_size = read_16bitBE(current_chunk+0x0e,streamFile);
sample_rate = read80bitSANE(current_chunk+0x10,streamFile); sample_rate = read80bitSANE(current_chunk+0x10,streamFile);
if (AIFC) { if (is_aifc) {
switch (read_32bitBE(current_chunk+0x1a,streamFile)) { switch (read_32bitBE(current_chunk+0x1a,streamFile)) {
case 0x53445832: /* "SDX2" [3DO games: Super Street Fighter II Turbo (3DO), etc] */ case 0x53445832: /* "SDX2" [3DO games: Super Street Fighter II Turbo (3DO), etc] */
coding_type = coding_SDX2; coding_type = coding_SDX2;
@ -171,7 +157,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
coding_type = coding_DVI_IMA_int; coding_type = coding_DVI_IMA_int;
if (channel_count != 1) break; /* don't know how stereo DVI is laid out */ if (channel_count != 1) break; /* don't know how stereo DVI is laid out */
break; break;
case 0x696D6134: /* "ima4" [Alida (PC) Lunar SSS (iOS)] */ case 0x696D6134: /* "ima4" [Alida (PC), Lunar SSS (iOS)] */
coding_type = coding_APPLE_IMA4; coding_type = coding_APPLE_IMA4;
interleave = 0x22; interleave = 0x22;
sample_count = sample_count * ((interleave-0x2)*2); 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 */ /* string size and human-readable AIFF-C codec follows */
} }
else if (AIFF) { else if (is_aiff) {
switch (sample_size) { switch (sample_size) {
case 8: case 8:
coding_type = coding_PCM8; coding_type = coding_PCM8;
@ -192,6 +178,9 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
coding_type = coding_PCM16BE; coding_type = coding_PCM16BE;
interleave = 2; interleave = 2;
break; break;
case 4: /* Crusader: No Remorse (SAT), Road Rash (3DO) */
coding_type = coding_XA;
break;
default: default:
VGM_LOG("AIFF: unknown codec\n"); VGM_LOG("AIFF: unknown codec\n");
goto fail; goto fail;
@ -200,10 +189,12 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
break; break;
case 0x53534E44: /* "SSND" (main data) */ case 0x53534E44: /* "SSND" (main data) */
case 0x4150434D: /* "APCM" (main data for XA) */
if (SoundDataChunkFound) goto fail; if (SoundDataChunkFound) goto fail;
SoundDataChunkFound = 1; 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; break;
case 0x4D41524B: /* "MARK" (loops) */ case 0x4D41524B: /* "MARK" (loops) */
@ -229,10 +220,10 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
} }
} }
if (AIFC) { if (is_aifc) {
if (!FormatVersionChunkFound || !CommonChunkFound || !SoundDataChunkFound) if (!FormatVersionChunkFound || !CommonChunkFound || !SoundDataChunkFound)
goto fail; goto fail;
} else if (AIFF) { } else if (is_aiff) {
if (!CommonChunkFound || !SoundDataChunkFound) if (!CommonChunkFound || !SoundDataChunkFound)
goto fail; goto fail;
} }
@ -260,7 +251,8 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
* We shouldn't have a loop point that overflows an int32_t * We shouldn't have a loop point that overflows an int32_t
* anyway. */ * anyway. */
loop_flag = 1; 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->loop_end_sample = loop_end;
vgmstream->coding_type = coding_type; vgmstream->coding_type = coding_type;
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->layout_type = (channel_count > 1) ? layout_interleave : layout_none;
vgmstream->interleave_block_size = interleave; vgmstream->interleave_block_size = interleave;
}
if (AIFC) if (is_aifc)
vgmstream->meta_type = meta_AIFC; vgmstream->meta_type = meta_AIFC;
else if (AIFF) else if (is_aiff)
vgmstream->meta_type = meta_AIFF; vgmstream->meta_type = meta_AIFF;

View file

@ -1,167 +1,150 @@
#include "meta.h" #include "meta.h"
#include "aix_streamfile.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 * init_vgmstream_aix(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamFileAIX = NULL; STREAMFILE * streamFileAIX = NULL;
STREAMFILE * streamFileADX = NULL; int loop_flag = 0, channel_count, sample_rate;
char filename[PATH_LIMIT]; int32_t sample_count, loop_start_sample = 0, loop_end_sample = 0;
off_t *segment_offset = NULL; off_t *segment_offset = NULL;
int32_t *samples_in_segment = NULL; int32_t *segment_samples = NULL;
int32_t sample_count;
int loop_flag = 0;
int32_t loop_start_sample=0;
int32_t loop_end_sample=0;
aix_codec_data *data = NULL; aix_codec_data *data = NULL;
off_t data_offset;
off_t first_AIXP; off_t layer_list_offset;
off_t stream_list_offset; off_t layer_list_end;
off_t stream_list_end;
const int segment_list_entry_size = 0x10;
const off_t segment_list_offset = 0x20; 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 segment_count, layer_count;
int sample_rate; int i, j;
int i;
/* check extension, case insensitive */ /* checks */
streamFile->get_name(streamFile,filename,sizeof(filename)); if (!check_extensions(streamFile, "aix"))
if (strcasecmp("aix",filename_extension(filename))) goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41495846 || /* "AIXF" */
if (read_32bitBE(0x0,streamFile) != 0x41495846 || /* "AIXF" */ read_32bitBE(0x08,streamFile) != 0x01000014 || /* version? */
read_32bitBE(0x08,streamFile) != 0x01000014 ||
read_32bitBE(0x0c,streamFile) != 0x00000800) read_32bitBE(0x0c,streamFile) != 0x00000800)
goto fail; 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); 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) layer_list_offset = segment_list_offset + segment_count*segment_list_entry_size + 0x10;
goto fail; if (layer_list_offset >= data_offset) goto fail;
if (segment_count < 1)
goto fail;
sample_rate = read_32bitBE(stream_list_offset+8,streamFile); segment_samples = calloc(segment_count,sizeof(int32_t));
if (sample_rate < 300 || sample_rate > 96000) if (!segment_samples) goto fail;
goto fail;
samples_in_segment = calloc(segment_count,sizeof(int32_t));
if (!samples_in_segment)
goto fail;
segment_offset = calloc(segment_count,sizeof(off_t)); segment_offset = calloc(segment_count,sizeof(off_t));
if (!segment_offset) if (!segment_offset) goto fail;
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); sample_rate = read_32bitBE(layer_list_offset+0x08,streamFile); /* first layer's sample rate */
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]);*/ 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 */ /* all segments must have equal sample rate */
if (read_32bitBE(segment_list_offset+segment_list_entry_size*i+0x0c,streamFile) != 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), /* segments > 0 can have 0 sample rate (Ryu ga gotoku: Kenzan! tenkei_sng1.aix),
seems to indicate same sample rate as first */ seems to indicate same sample rate as first */
if (!(i > 0 && read_32bitBE(segment_list_offset+segment_list_entry_size*i+0x0c,streamFile) == 0)) if (!(i > 0 && read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x0c,streamFile) == 0))
goto fail; goto fail;
} }
} }
if (segment_offset[0] != first_AIXP) if (segment_offset[0] != data_offset)
goto fail; goto fail;
}
stream_count = (uint8_t)read_8bit(stream_list_offset,streamFile); /* base layer header */
if (stream_count < 1) layer_count = (uint8_t)read_8bit(layer_list_offset,streamFile);
goto fail; if (layer_count < 1) goto fail;
stream_list_end = stream_list_offset + 0x8 + stream_count * 8;
if (stream_list_end >= first_AIXP) layer_list_end = layer_list_offset + 0x08 + layer_count*layer_list_entry_size;
goto fail; if (layer_list_end >= data_offset) goto fail;
/* parse layers table */
channel_count = 0; 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 */ /* 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; 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 */ /* check for existence of segments */
for (i = 0; i < segment_count; i++) for (i = 0; i < segment_count; i++) {
{ off_t aixp_offset = segment_offset[i];
int j; for (j = 0; j < layer_count; j++) {
off_t AIXP_offset = segment_offset[i]; if (read_32bitBE(aixp_offset,streamFile) != 0x41495850) /* "AIXP" */
for (j = 0; j < stream_count; j++)
{
if (read_32bitBE(AIXP_offset,streamFile)!=0x41495850) /* "AIXP" */
goto fail; goto fail;
if (read_8bit(AIXP_offset+8,streamFile)!=j) if (read_8bit(aixp_offset+0x08,streamFile) != j)
goto fail; 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);*/ /* open base streamfile, that will be shared by all open_aix_with_STREAMFILE */
streamFileAIX = streamFile->open(streamFile,filename,sample_rate*0.1*segment_count); {
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; if (!streamFileAIX) goto fail;
}
/* init layout */
{
data = malloc(sizeof(aix_codec_data)); data = malloc(sizeof(aix_codec_data));
if (!data) goto fail; if (!data) goto fail;
data->segment_count = segment_count; data->segment_count = segment_count;
data->stream_count = stream_count; data->stream_count = layer_count;
data->adxs = malloc(sizeof(STREAMFILE *)*segment_count*stream_count); data->adxs = calloc(segment_count * layer_count, sizeof(VGMSTREAM*));
if (!data->adxs) goto fail; if (!data->adxs) goto fail;
for (i=0;i<segment_count*stream_count;i++) {
data->adxs[i] = NULL;
}
data->sample_counts = calloc(segment_count,sizeof(int32_t)); data->sample_counts = calloc(segment_count,sizeof(int32_t));
if (!data->sample_counts) goto fail; if (!data->sample_counts) goto fail;
memcpy(data->sample_counts,samples_in_segment,segment_count*sizeof(int32_t));
/* for each segment */ memcpy(data->sample_counts,segment_samples,segment_count*sizeof(int32_t));
for (i = 0; i < segment_count; i++) }
{
int j; /* open each segment / layer subfile */
/* for each stream */ for (i = 0; i < segment_count; i++) {
for (j = 0; j < stream_count; j++) 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 *adx; VGMSTREAM *temp_vgmstream;
/*printf("try opening segment %d/%d stream %d/%d %x\n",i,segment_count,j,stream_count,segment_offset[i]);*/ STREAMFILE * temp_streamFile = open_aix_with_STREAMFILE(streamFileAIX,segment_offset[i],j);
streamFileADX = open_aix_with_STREAMFILE(streamFileAIX,segment_offset[i],j); if (!temp_streamFile) goto fail;
if (!streamFileADX) goto fail;
adx = data->adxs[i*stream_count+j] = init_vgmstream_adx(streamFileADX); temp_vgmstream = data->adxs[i*layer_count+j] = init_vgmstream_adx(temp_streamFile);
if (!adx)
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; goto fail;
close_streamfile(streamFileADX); streamFileADX = NULL; memcpy(temp_vgmstream->start_ch,temp_vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*temp_vgmstream->channels);
memcpy(temp_vgmstream->start_vgmstream,temp_vgmstream,sizeof(VGMSTREAM));
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));
} }
} }
if (segment_count > 1) /* get looping and samples */
{
loop_flag = 1;
}
sample_count = 0; 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]; sample_count += data->sample_counts[i];
if (i == 0) if (i == 0)
@ -170,45 +153,45 @@ VGMSTREAM * init_vgmstream_aix(STREAMFILE *streamFile) {
loop_end_sample = sample_count; loop_end_sample = sample_count;
} }
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->num_samples = sample_count;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = sample_count;
vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample; vgmstream->loop_end_sample = loop_end_sample;
vgmstream->meta_type = meta_AIX;
vgmstream->coding_type = data->adxs[0]->coding_type; vgmstream->coding_type = data->adxs[0]->coding_type;
vgmstream->layout_type = layout_aix; vgmstream->layout_type = layout_aix;
vgmstream->meta_type = meta_AIX;
vgmstream->ch[0].streamfile = streamFileAIX; vgmstream->ch[0].streamfile = streamFileAIX;
data->current_segment = 0; data->current_segment = 0;
vgmstream->codec_data = data; vgmstream->codec_data = data;
free(segment_offset); free(segment_offset);
free(samples_in_segment); free(segment_samples);
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (streamFileAIX) close_streamfile(streamFileAIX); close_streamfile(streamFileAIX);
if (streamFileADX) close_streamfile(streamFileADX); close_vgmstream(vgmstream);
if (vgmstream) close_vgmstream(vgmstream); free(segment_samples);
if (samples_in_segment) free(samples_in_segment); free(segment_offset);
if (segment_offset) free(segment_offset);
/* free aix layout */
if (data) { if (data) {
if (data->adxs) if (data->adxs) {
{
int i; int i;
for (i=0;i<data->segment_count*data->stream_count;i++) for (i = 0; i < data->segment_count*data->stream_count; i++) {
if (data->adxs)
close_vgmstream(data->adxs[i]); close_vgmstream(data->adxs[i]);
}
free(data->adxs); free(data->adxs);
} }
if (data->sample_counts) if (data->sample_counts) {
{
free(data->sample_counts); free(data->sample_counts);
} }
free(data); free(data);

View file

@ -188,7 +188,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset, material_offset, extradata_offset; off_t start_offset, material_offset, extradata_offset;
size_t material_size, extradata_size, stream_size; 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; int total_subsongs, target_subsong = streamFile->stream_index;
/* check extensions */ /* check extensions */
@ -232,9 +232,9 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
material_size = read_16bitLE(material_offset+0x04,streamFile); material_size = read_16bitLE(material_offset+0x04,streamFile);
sample_rate = (uint16_t)read_16bitLE(material_offset+0x06,streamFile); sample_rate = (uint16_t)read_16bitLE(material_offset+0x06,streamFile);
stream_size = read_32bitLE(material_offset+0x08,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); loop_end = read_32bitLE(material_offset+0x14,streamFile);
extradata_size = read_32bitLE(material_offset+0x18,streamFile); extradata_size = read_32bitLE(material_offset+0x18,streamFile);
/* rest: ? (empty or 0x3f80) */ /* 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) /* 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 */ * Actual num_samples would be loop_end_sample+1, but more testing is needed */
vgmstream->num_samples = num_samples; vgmstream->num_samples = read_32bitLE(material_offset+0x0c,streamFile);//num_samples;
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = read_32bitLE(material_offset+0x10,streamFile);//loop_start;
vgmstream->loop_end_sample = loop_end; vgmstream->loop_end_sample = loop_end;
break; break;
} }

View file

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

View file

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

View file

@ -2,90 +2,60 @@
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../util.h" #include "../util.h"
/* .AST - from Nintendo games [Super Mario Galaxy (Wii), Pac-Man Vs (GC)] */
VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT]; off_t start_offset;
int loop_flag, channel_count, codec;
coding_t coding_type;
int codec_number; /* checks */
int channel_count; if (!check_extensions(streamFile, "ast"))
int loop_flag; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */
size_t max_block; goto fail;
if (read_16bitBE(0x0a,streamFile) != 0x10) /* ? */
/* 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))
goto fail; goto fail;
/* check for a first block */ if (read_32bitBE(0x04,streamFile)+0x40 != get_streamfile_size(streamFile))
if (read_32bitBE(0x40,streamFile)!=0x424C434B) /* "BLCK" */ 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);
start_offset = 0x40;
if (read_32bitBE(start_offset,streamFile) != 0x424C434B) /* "BLCK" */
goto fail; 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);
switch (codec_number) { /* build the VGMSTREAM */
case 0: vgmstream = allocate_vgmstream(channel_count,loop_flag);
coding_type = coding_NGC_AFC; 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; break;
case 1: case 0x01: /* Mario Kart: Double Dash!! (GC) */
coding_type = coding_PCM16BE; vgmstream->coding_type = coding_PCM16BE;
break; break;
default: default:
goto fail; goto fail;
} }
/* build the VGMSTREAM */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
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;i<channel_count;i++) {
vgmstream->ch[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);
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View file

@ -120,10 +120,12 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
vgmstream = init_vgmstream_riff(temp_streamFile); vgmstream = init_vgmstream_riff(temp_streamFile);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
break; break;
#ifdef VGM_USE_VORBIS
case KOVS: case KOVS:
vgmstream = init_vgmstream_ogg_vorbis(temp_streamFile); vgmstream = init_vgmstream_ogg_vorbis(temp_streamFile);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
break; break;
#endif
case KTSS: case KTSS:
vgmstream = init_vgmstream_ktss(temp_streamFile); vgmstream = init_vgmstream_ktss(temp_streamFile);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;

View file

@ -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)) if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset))
goto fail; goto fail;
if (vgmstream->layout_type == layout_blocked_awc)
block_update_awc(awc.stream_offset, vgmstream);
return vgmstream; return vgmstream;
fail: 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. /* 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 */ * Each block has a header/seek table and interleaved data for all channels */
if (awc->is_music && read_32bit(awc->stream_offset, streamFile) != 0) { 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; goto fail;
} }

View file

@ -5,26 +5,25 @@
typedef struct { typedef struct {
/* config */ /* config */
int channel;
int channel_count;
size_t block_size;
off_t stream_offset; off_t stream_offset;
size_t stream_size; size_t stream_size;
int channel_count;
int channel;
size_t chunk_size;
/* state */ /* state */
off_t logical_offset; /* offset that corresponds to physical_offset */ off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */ off_t physical_offset; /* actual file offset */
off_t next_block_offset; /* physical offset of the next block start */
off_t last_offset; /* physical offset of where the last block ended */
size_t current_data_size;
size_t current_consumed_size;
size_t total_size; /* size of the resulting XMA data */ 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; } 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_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); 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. /* 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; size_t frame_size = 0x800;
/* ignore bad reads */ /* ignore bad reads */
if (offset < 0 || offset > data->total_size) { if (offset < 0 || offset > data->logical_size) {
return 0; 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) { if (offset < data->logical_offset) {
data->logical_offset = 0x00; data->logical_offset = 0x00;
data->physical_offset = data->stream_offset; data->physical_offset = data->stream_offset;
data->next_block_offset = 0; data->data_size = 0;
data->last_offset = 0;
data->current_data_size = 0;
data->current_consumed_size = 0;
} }
/* read from block, moving to next when all data is consumed */ /* read blocks, one at a time */
while (length > 0) { while (length > 0) {
size_t to_read, bytes_read;
/* new block */ /* ignore EOF */
if (data->current_data_size == 0) { 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); size_t header_size = get_block_header_size(streamfile, data->physical_offset, data);
/* header table entries = frames... I hope */ /* 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 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 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_samples = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x08, streamfile);
size_t repeat_size = 0; size_t repeat_size = 0;
/* if there are repeat samples current block repeats some frames from last block, find out size */ /* if there are repeat samples current block repeats some frames from last block, find out size */
if (repeat_samples && data->last_offset) { if (repeat_samples) {
off_t data_offset = data->physical_offset + header_size + skip_size; off_t data_offset = data->physical_offset + header_size + others_size;
repeat_size = get_repeated_data_size(streamfile, data_offset, data->last_offset); repeat_size = get_repeated_data_size(streamfile, data_offset, repeat_samples);
} }
data->next_block_offset = data->physical_offset + data->chunk_size; data->skip_size = header_size + others_size + repeat_size;
data->physical_offset += header_size + skip_size + repeat_size; /* data start */ data->data_size = data_size - repeat_size;
data->current_data_size = data_size - repeat_size; /* readable max in this block */ }
data->current_consumed_size = 0;
/* 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; continue;
} }
/* block end, go next */ /* read data */
if (data->current_consumed_size == data->current_data_size) { {
data->last_offset = data->physical_offset; /* where last block ended */ size_t bytes_consumed, bytes_done, to_read;
data->physical_offset = data->next_block_offset;
data->current_data_size = 0;
continue;
}
/* requested offset is further along, pretend we consumed data and try again */ bytes_consumed = offset - data->logical_offset;
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; to_read = data->data_size - bytes_consumed;
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) if (to_read > length)
to_read = length; to_read = length;
if (to_read == 0) bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
return total_read; /* should never happen... */
/* finally read and move buffer/offsets */ offset += bytes_done;
bytes_read = read_streamfile(dest, data->physical_offset, to_read, streamfile); total_read += bytes_done;
total_read += bytes_read; length -= bytes_done;
if (bytes_read != to_read) dest += bytes_done;
return total_read; /* couldn't read fully */
dest += bytes_read; if (bytes_done != to_read || bytes_done == 0) {
offset += bytes_read; break; /* error/EOF */
length -= bytes_read; }
}
data->physical_offset += bytes_read;
data->logical_offset += bytes_read;
data->current_consumed_size += bytes_read;
} }
return total_read; return total_read;
} }
static size_t awc_xma_io_size(STREAMFILE *streamfile, awc_xma_io_data* data) { 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 frame_size = 0x800;
size_t total_size = 0; size_t logical_size = 0;
if (data->total_size) if (data->logical_size)
return data->total_size; return data->logical_size;
physical_offset = data->stream_offset; physical_offset = data->stream_offset;
max_physical_offset = data->stream_offset + data->stream_size; 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) { while (physical_offset < max_physical_offset) {
size_t header_size = get_block_header_size(streamfile, physical_offset, data); size_t header_size = get_block_header_size(streamfile, physical_offset, data);
/* header table entries = frames... I hope */ /* 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; size_t repeat_size = 0;
/* if there are repeat samples current block repeats some frames from last block, find out size */ /* 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; 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; logical_size += data_size - repeat_size;
total_size += data_size - repeat_size; physical_offset += data->block_size;
physical_offset += data->chunk_size;
} }
data->total_size = total_size; data->logical_size = logical_size;
return data->total_size; return data->logical_size;
} }
/* Prepares custom IO for AWC XMA, which is interleaved XMA in AWC blocks */ /* 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; STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
awc_xma_io_data io_data = {0}; awc_xma_io_data io_data = {0};
size_t io_data_size = sizeof(awc_xma_io_data); 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_offset = stream_offset;
io_data.stream_size = stream_size; io_data.stream_size = stream_size;
io_data.chunk_size = chunk_size; io_data.block_size = block_size;
io_data.channel_count = channel_count;
io_data.channel = channel;
io_data.physical_offset = stream_offset; 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 */ /* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile); 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; if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile; 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; return temp_streamFile;
fail: 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 */ /* 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) { static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t next_offset, size_t repeat_samples) {
uint8_t new_frame[0x800];/* buffer to avoid fseek back and forth */ const size_t frame_size = 0x800;
size_t frame_size = 0x800; const size_t samples_per_subframe = 512;
off_t offset; size_t samples_this_frame;
int i; uint8_t subframes;
/* read block first frame */ //todo: fix this
if (read_streamfile(new_frame,new_offset, frame_size,streamFile) != frame_size) /* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that.
goto fail; * 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 */ subframes = ((uint8_t)read_8bit(next_offset,streamFile) >> 2) & 0x3F; /* peek into frame header */
offset = last_offset - 0x4000; /* typical max is 1 frame of ~0x800, no way to know exact size */ samples_this_frame = subframes*samples_per_subframe;
while (offset < last_offset) { if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */
/* compare frame vs prev block data */ return frame_size;
for (i = 0; i < frame_size; i++) {
if ((uint8_t)read_8bit(offset+i,streamFile) != new_frame[i])
break;
} }
else {
/* frame fully compared? */ return 0;
if (i == frame_size)
return last_offset - offset;
else
offset += i+1;
} }
fail:
VGM_LOG("AWC: can't find repeat size, new=0x%08lx, last=0x%08lx\n", new_offset, last_offset);
return 0; /* keep on truckin' */
} }
/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */ /* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */

View file

@ -1,165 +1,133 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../coding/coding.h"
/* BCSTM - Nintendo 3DS format */ /* BCSTM - Nintendo 3DS format */
VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; 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; 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") ) if ( !check_extensions(streamFile,"bcstm") )
goto fail; goto fail;
/* check header */ /* CSTM header */
if ((uint32_t)read_32bitBE(0, streamFile) != 0x4353544D) /* "CSTM" */ 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; goto fail;
if ((uint16_t)read_16bitLE(4, streamFile) != 0xFEFF) /* get sections (should always appear in the same order) */
goto fail; {
int i;
section_count = read_16bitLE(0x10, streamFile); int section_count = read_16bitLE(0x10, streamFile);
for (i = 0; i < section_count; i++) { for (i = 0; i < section_count; i++) {
temp_id = read_16bitLE(0x14 + i * 0xc, streamFile); /* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */
switch(temp_id) { uint16_t section_id = read_16bitLE(0x14 + i*0x0c+0x00, streamFile);
case 0x4000: switch(section_id) {
info_offset = read_32bitLE(0x18 + i * 0xc, streamFile); case 0x4000: info_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
/* size_t info_size = read_32bitLE(0x1c + i * 0xc, streamFile); */ case 0x4001: seek_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
break; case 0x4002: data_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
case 0x4001: //case 0x4003: /* off_t regn_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ /* ? */
seek_offset = read_32bitLE(0x18 + i * 0xc, streamFile); //case 0x4004: /* off_t pdat_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: default:
break; break;
} }
} }
}
if (info_offset == 0) goto fail; /* INFO section */
if ((uint32_t)read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
goto fail; goto fail;
codec = read_8bit(info_offset + 0x20, streamFile);
/* check type details */
codec_number = read_8bit(info_offset + 0x20, streamFile);
loop_flag = read_8bit(info_offset + 0x21, streamFile); loop_flag = read_8bit(info_offset + 0x21, streamFile);
channel_count = read_8bit(info_offset + 0x22, 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; /* 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" */
/* 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; j<vgmstream->channels; 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.
if (seek_offset == 0) goto fail; if (seek_offset == 0) goto fail;
start_offset = seek_offset; start_offset = seek_offset;
} else { is_camelot_ima = 1;
}
else {
if (data_offset == 0) goto fail; if (data_offset == 0) goto fail;
start_offset = data_offset + 0x20; 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)) if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;
fail: fail:

View file

@ -1,171 +1,235 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../coding/coding.h"
#include "../stack_alloc.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 */ /* BFSTM - Nintendo Wii U format */
VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
coding_t coding_type; off_t start_offset;
coding_t coding_PCM16; 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; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(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 */ /* checks */
if ( !check_extensions(streamFile,"bfstm") ) if ( !check_extensions(streamFile,"bfstm") )
goto fail; goto fail;
/* check header */ /* FSTM header */
if ((uint32_t)read_32bitBE(0, streamFile) != 0x4653544D) /* "FSTM" */ if (read_32bitBE(0x00, streamFile) != 0x4653544D) /* "FSTM" */
goto fail; 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_32bit = read_32bitBE;
read_16bit = read_16bitBE; read_16bit = read_16bitBE;
coding_PCM16 = coding_PCM16BE; big_endian = 1;
} else if ((uint16_t)read_16bitBE(4, streamFile) == 0xFFFE) { /* Blaster Master Zero 3DS */ } else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* Switch games */
read_32bit = read_32bitLE; read_32bit = read_32bitLE;
read_16bit = read_16bitLE; read_16bit = read_16bitLE;
coding_PCM16 = coding_PCM16LE; big_endian = 0;
} else { } else {
goto fail; goto fail;
} }
section_count = read_16bit(0x10, streamFile); /* 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++) { for (i = 0; i < section_count; i++) {
temp_id = read_16bit(0x14 + i * 0xc, streamFile); /* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */
switch(temp_id) { uint16_t section_id = read_16bit(0x14 + i*0xc+0x00, streamFile);
case 0x4000: switch(section_id) {
info_offset = read_32bit(0x18 + i * 0xc, streamFile); case 0x4000: info_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break;
/* size_t info_size = read_32bit(0x1c + i * 0xc, streamFile); */ case 0x4001: /* seek_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); */ break;
break; case 0x4002: data_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break;
case 0x4001: #if BFSTM_ENABLE_REGION_SUBSONGS
seek_offset = read_32bit(0x18 + i * 0xc, streamFile); case 0x4003: regn_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break;
/* size_t seek_size = read_32bit(0x1c + i * 0xc, streamFile); */ #endif
break; case 0x4004: /* pdat_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); */ break; /* prefetch data */
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: default:
break; break;
} }
} }
if (info_offset == 0) goto fail; if (info_offset == 0 || data_offset == 0)
if ((uint32_t)read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
goto fail; goto fail;
}
/* INFO section */
/* check type details */ if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
codec_number = read_8bit(info_offset + 0x20, streamFile); goto fail;
codec = read_8bit(info_offset + 0x20, streamFile);
loop_flag = read_8bit(info_offset + 0x21, streamFile); loop_flag = read_8bit(info_offset + 0x21, streamFile);
channel_count = read_8bit(info_offset + 0x22, streamFile); channel_count = read_8bit(info_offset + 0x22, streamFile);
#if BFSTM_ENABLE_REGION_SUBSONGS
region_count = read_8bit(info_offset + 0x23, streamFile);
#endif
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; start_offset = data_offset + 0x20;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag); vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = read_32bit(info_offset + 0x2c, streamFile);
vgmstream->sample_rate = read_32bit(info_offset + 0x24, streamFile); vgmstream->sample_rate = read_32bit(info_offset + 0x24, streamFile);
/* channels and loop flag are set by allocate_vgmstream */ vgmstream->num_samples = read_32bit(info_offset + 0x2c, streamFile);
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_start_sample = read_32bit(info_offset + 0x28, streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples; 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_FSTM; vgmstream->meta_type = meta_FSTM;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
if (ima)
vgmstream->interleave_block_size = 0x200;
else {
vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile); vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile);
vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, streamFile); vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, streamFile);
}
if (vgmstream->coding_type == coding_NGC_DSP) { switch(codec) {
off_t coeff_ptr_table; case 0x00:
VARDECL(off_t, coef_offset); vgmstream->coding_type = coding_PCM8;
ALLOC(coef_offset, channel_count, off_t); break;
coeff_ptr_table = read_32bit(info_offset + 0x1c, streamFile) + info_offset + 8; // Getting pointer for coefficient pointer table case 0x01:
vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE;
break;
case 0x02:
vgmstream->coding_type = coding_NGC_DSP;
for (i = 0; i < channel_count; i++) { {
tempoffset1 = read_32bit(coeff_ptr_table + 8 + i * 8, streamFile); int i, c;
coef_offset[i] = tempoffset1 + coeff_ptr_table; off_t channel_indexes, channel_info_offset, coefs_offset;
coef_offset[i] += read_32bit(coef_offset[i] + 4, streamFile);
}
for (j = 0; j<vgmstream->channels; j++) { channel_indexes = info_offset+0x08 + read_32bit(info_offset + 0x1C, streamFile);
for (i = 0; i<16; i++) { for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bit(coef_offset[j] + i * 2, streamFile); 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;
if (ima) { // No SEEK (ADPC) header, so just start where the SEEK header is supposed to be. default: /* 0x03: IMA? */
if (seek_offset == 0) goto fail; goto fail;
start_offset = seek_offset;
} else {
if (data_offset == 0) goto fail;
start_offset = data_offset + 0x20;
} }
/* open the file for reading by each channel */ #if BFSTM_ENABLE_REGION_SUBSONGS
start_offset += bfstm_set_regions(streamFile, vgmstream, region_count, regn_offset, codec, big_endian);
#endif
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;
fail: fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; 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

View file

@ -35,7 +35,7 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
/*0x2c: unk (vol?) */ /*0x2c: unk (vol?) */
/*0x2d: unk (0x10?) */ /*0x2d: unk (0x10?) */
channel_count = read_8bit(0x2e,streamFile); 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)) if (file_size != get_streamfile_size(streamFile))
goto fail; goto fail;

View file

@ -2,25 +2,27 @@
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util.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) */ /* BINK 1/2 - RAD Game Tools movies (audio/video format) */
VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0; 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; size_t stream_size;
/* checks */
/* check extension, case insensitive (bika = manually demuxed audio) */ /* .bik/bik2/bk2: standard
if (!check_extensions(streamFile,"bik,bika,bik2,bik2a,bk2,bk2a")) goto fail; * .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) */ /* 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 && if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 &&
(read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail; (read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail;
/* find target stream info and samples */ /* 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; goto fail;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
@ -36,11 +38,9 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
#ifdef VGM_USE_FFMPEG #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_header_offset(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile));
vgmstream->codec_data = init_ffmpeg_config(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; 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. * 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) * (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 *offsets = NULL;
uint32_t num_frames, num_samples_b = 0; uint32_t num_frames, num_samples_b = 0;
off_t cur_offset; off_t cur_offset;
int i, j, sample_rate, channel_count; 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 stream_size = 0;
size_t filesize = get_streamfile_size(streamFile); size_t filesize = get_streamfile_size(streamFile);

View file

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

View file

@ -2,17 +2,18 @@
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util.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 * init_vgmstream_bnsf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, first_offset = 0x0C; off_t start_offset = 0, first_offset = 0x0C;
int loop_flag = 0, channel_count = 0; int loop_flag = 0, channel_count = 0, sample_rate;
int sample_rate, num_samples, loop_start = 0, loop_end = 0, block_size; int num_samples, loop_start = 0, loop_end = 0, loop_adjust, block_samples;
uint32_t codec, subcodec = 0; 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; off_t loop_chunk = 0, sfmt_chunk, sdat_chunk;
/* checks */
if (!check_extensions(streamFile,"bnsf")) if (!check_extensions(streamFile,"bnsf"))
goto fail; goto fail;
if (read_32bitBE(0,streamFile) != 0x424E5346) /* "BNSF" */ if (read_32bitBE(0,streamFile) != 0x424E5346) /* "BNSF" */
@ -20,8 +21,9 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
codec = read_32bitBE(0x08,streamFile); codec = read_32bitBE(0x08,streamFile);
/* check RIFF size (for siren22 it's full size) */ /* check file size (siren22 uses full size) */
if (read_32bitBE(0x04,streamFile) + (codec == 0x49533232 ? 0 : 8) != get_streamfile_size(streamFile)) bnsf_size = read_32bitBE(0x04,streamFile);
if (bnsf_size + (codec == 0x49533232 ? 0x00 : 0x08) != get_streamfile_size(streamFile))
goto fail; goto fail;
if (!find_chunk_be(streamFile, 0x73666d74,first_offset,0, &sfmt_chunk,NULL)) /* "sfmt" */ 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; goto fail;
if ( find_chunk_be(streamFile, 0x6C6F6F70,first_offset,0, &loop_chunk,NULL)) { /* "loop" */ if ( find_chunk_be(streamFile, 0x6C6F6F70,first_offset,0, &loop_chunk,NULL)) { /* "loop" */
loop_flag = 1; loop_flag = 1;
loop_start = read_32bitBE(loop_chunk+0x00, streamFile); loop_start = read_32bitBE(loop_chunk+0x00, streamFile); /* block-aligned */
loop_end = read_32bitBE(loop_chunk+0x04,streamFile); 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); channel_count = read_16bitBE(sfmt_chunk+0x02,streamFile);
sample_rate = read_32bitBE(sfmt_chunk+0x04,streamFile); sample_rate = read_32bitBE(sfmt_chunk+0x04,streamFile);
num_samples = read_32bitBE(sfmt_chunk+0x08,streamFile); num_samples = read_32bitBE(sfmt_chunk+0x08,streamFile);
/* 0x0c: some kind of sample count (skip?), can be 0 when no loops */ loop_adjust = read_32bitBE(sfmt_chunk+0x0c,streamFile); /* 0 if no looping */
block_size = read_16bitBE(sfmt_chunk+0x10,streamFile); block_size = read_16bitBE(sfmt_chunk+0x10,streamFile);
//block_samples = read_16bitBE(sfmt_chunk+0x12,streamFile); block_samples = read_16bitBE(sfmt_chunk+0x12,streamFile);
/* max_samples = sdat_size/block_size*block_samples //num_samples is smaller */ //max_samples = sdat_size / block_size * block_samples /* num_samples is smaller */
start_offset = sdat_chunk; 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 */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
@ -52,15 +58,15 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples; 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->loop_end_sample = loop_end;
vgmstream->meta_type = meta_BNSF; vgmstream->meta_type = meta_BNSF;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size/channel_count; 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 /* Late IS14 voice/ambient files use subcodec 0x02 [Tales of Zestiria (PS3/PC), The Idolm@ster 2 (PS3)].
* frames decode ok but rest fails, must be some subtle decoding variation (not encryption) */ * Bitstream looks modified (most noticeable in silent frames), probably not encrypted, still 1ch frames */
if (subcodec != 0) if (subcodec != 0)
goto fail; goto fail;

View file

@ -54,9 +54,6 @@ VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile) {
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
goto fail; goto fail;
block_update_caf(start_offset,vgmstream);
return vgmstream; return vgmstream;
fail: fail:

View file

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

View file

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

View file

@ -98,7 +98,7 @@ VGMSTREAM * init_vgmstream_dc_dcsw_dcs(STREAMFILE *streamFile) {
/* open the file for reading by each channel */ /* open the file for reading by each channel */
{ {
for (i=0;i<channel_count;i++) { for (i=0;i<channel_count;i++) {
vgmstream->ch[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; if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].channel_start_offset=

View file

@ -13,14 +13,16 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) {
int loop_flag, channel_count, sample_rate, loop_start = 0, loop_end = 0; 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") ) if ( !check_extensions(streamFile,"dec,de2") )
goto fail; goto fail;
/* Gurumin has extra data, maybe related to rhythm (~0x50000) */ /* Gurumin has extra data, maybe related to rhythm (~0x50000) */
if (check_extensions(streamFile,"de2")) { if (check_extensions(streamFile,"de2")) {
/* still not sure what this is for, but consistently 0xb */ /* 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! */ /* legitimate! really! */
riff_off = 0x10 + (read_32bitLE(0x0c,streamFile) ^ read_32bitLE(0x04,streamFile)); 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); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_DEC;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = pcm_size / 2 / channel_count; vgmstream->num_samples = pcm_size / 2 / channel_count;
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = loop_start;
@ -76,14 +79,8 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = 0x800; vgmstream->interleave_block_size = 0x800;
vgmstream->layout_type = layout_blocked_dec; vgmstream->layout_type = layout_blocked_dec;
vgmstream->meta_type = meta_DEC;
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail; goto fail;
block_update_dec(start_offset, vgmstream);
return vgmstream; return vgmstream;
fail: fail:

View file

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

View file

@ -42,7 +42,7 @@ VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE *streamFile) {
/* open the file for reading by each channel */ /* open the file for reading by each channel */
{ {
for (i=0;i<channel_count;i++) { for (i=0;i<channel_count;i++) {
vgmstream->ch[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; if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].channel_start_offset=

View file

@ -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;i<channel_count;i++)
{
vgmstream->ch[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;j<vgmstream->channels;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;i<channel_count;i++)
{
vgmstream->ch[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;j<vgmstream->channels;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;i<channel_count;i++)
{
vgmstream->ch[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;j<vgmstream->channels;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;
}

View file

@ -21,7 +21,7 @@ typedef struct {
int big_endian; int big_endian;
int loop_flag; int loop_flag;
int is_sead; int is_sead;
int codec_version; int codec_config;
} ea_header; } ea_header;
static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); 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 */ /* checks */
/* .asf/as4: common, cnk: some PS games, .sng: fake for plugins (to mimic EA SCHl's common extension) */ /* .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; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */ 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) */ case EA_CODEC_IMA: /* Need for Speed II (PC) */
if (ea.bits && ea.bits!=2) goto fail; /* only in EACS */ if (ea.bits && ea.bits!=2) goto fail; /* only in EACS */
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
vgmstream->codec_version = ea.codec_version; vgmstream->codec_config = ea.codec_config;
break; break;
case EA_CODEC_PSX: /* Need for Speed (PS) */ case EA_CODEC_PSX: /* Need for Speed (PS) */
@ -96,12 +97,9 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
goto fail; goto fail;
} }
/* open files; channel offsets are updated below */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail; goto fail;
block_update_ea_1snh(start_offset,vgmstream);
return vgmstream; return vgmstream;
fail: 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? */ VGM_ASSERT(ea->type != 0, "EA EACS: unknown type\n"); /* block type? */
if (ea->codec == EA_CODEC_IMA) 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) { else if (ea->is_sead) {
/* alt subheader (found in some PC videos) */ /* 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); ea->codec = read_32bit(offset+0x08, streamFile);
if (ea->codec == EA_CODEC_IMA) 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); set_ea_1snh_num_samples(streamFile, 0x00, ea);
if (ea->loop_start_offset) /* offset found, now find actual start sample */ 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 */ else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
block_header = 0x08; block_header = 0x08;
} }
else if (id == 0x00000000) { else if (id == 0x00000000 || id == 0xFFFFFFFF || id == 0x31534E65) { /* EOF or "1SNe" */
break; break;
} }
else if (id == 0x31534E6C) { /* "1SNl" loop point found */ 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); block_samples = ps_bytes_to_samples(block_size - block_header, ea->channels);
break; break;
case EA_CODEC_IMA: case EA_CODEC_IMA:
if (ea->codec_version == 1) if (ea->codec_config == 1)
block_samples = read_32bit(block_offset + block_header, streamFile); block_samples = read_32bit(block_offset + block_header, streamFile);
else else
block_samples = ima_bytes_to_samples(block_size - block_header, ea->channels); block_samples = ima_bytes_to_samples(block_size - block_header, ea->channels);

View file

@ -5,7 +5,38 @@
/* EAAudioCore formats, EA's current audio middleware */ /* 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 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 */ /* .SNR+SNS - from EA latest games (~2008-2013), v0 header */
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) { 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)] */ /* SNR headers normally need an external SNS file, but some have data [Burnout Paradise, NFL2013 (iOS)] */
if (get_streamfile_size(streamFile) > 0x10) { if (get_streamfile_size(streamFile) > 0x10) {
off_t start_offset; off_t start_offset = get_snr_size(streamFile, 0x00);
switch(read_8bit(0x04,streamFile)) { /* flags */
case 0x60: start_offset = 0x10; break;
case 0x20: start_offset = 0x0c; break;
default: start_offset = 0x08; break;
}
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS); vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
} }
@ -55,7 +79,7 @@ VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile) {
goto fail; goto fail;
/* SPS block start: 0x00(1): block flag (header=0x48); 0x01(3): block size (usually 0x0c-0x14) */ /* 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; goto fail;
start_offset = read_32bitBE(0x00, streamFile) & 0x00FFFFFF; start_offset = read_32bitBE(0x00, streamFile) & 0x00FFFFFF;
@ -109,7 +133,7 @@ fail:
} }
/* .SPS - from Frostbite engine games, v1 header */ /* .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; VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, header_offset = 0, sps_offset, max_offset; off_t start_offset = 0, header_offset = 0, sps_offset, max_offset;
@ -155,6 +179,313 @@ fail:
return NULL; 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). /* 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), * 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). * 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) { 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; VGMSTREAM * vgmstream = NULL;
STREAMFILE* temp_streamFile = NULL; STREAMFILE* temp_streamFile = NULL;
int channel_count, loop_flag = 0, version, codec, channel_config, sample_rate, flags; uint32_t header1, header2;
uint32_t num_samples, loop_start = 0, loop_end = 0; eaac_header eaac = {0};
/* EA SNR/SPH header */ /* EA SNR/SPH header */
version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0x0F; header1 = (uint32_t)read_32bitBE(header_offset + 0x00, streamHead);
codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0x0F; header2 = (uint32_t)read_32bitBE(header_offset + 0x04, streamHead);
channel_config = read_8bit(header_offset + 0x01,streamHead) & 0xFE; eaac.version = (header1 >> 28) & 0x0F; /* 4 bits */
sample_rate = read_32bitBE(header_offset + 0x00,streamHead) & 0x1FFFF; /* some Dead Space 2 (PC) uses 96000 */ eaac.codec = (header1 >> 24) & 0x0F; /* 4 bits */
flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead) & 0xFE; //todo upper nibble only? (the first bit is part of size) eaac.channel_config = (header1 >> 18) & 0x3F; /* 6 bits */
num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x01FFFFFF; eaac.sample_rate = (header1 & 0x03FFFF); /* 18 bits (some Dead Space 2 (PC) do use 96000) */
/* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers): eaac.flags = (header2 >> 28) & 0x0F; /* 4 bits *//* TODO: maybe even 3 bits and not 4? */
* &0x20: 1 int (usually 0x00), &0x00/40: nothing, &0x60: 2 ints (usually 0x00 and 0x14) */ 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) */ /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than block flags) */
if (version != 0 && version != 1) { if (eaac.version != EAAC_VERSION_V0 && eaac.version != EAAC_VERSION_V1) {
VGM_LOG("EA SNS/SPS: unknown version\n"); VGM_LOG("EA EAAC: unknown version\n");
goto fail; goto fail;
} }
/* 0x40: stream asset, 0x20: full loop, 0x00: default/RAM asset */ /* catch unknown/garbage values just in case */
if (flags != 0x60 && flags != 0x40 && flags != 0x20 && flags != 0x00) { if (eaac.flags != EAAC_FLAG_NONE && !(eaac.flags & (EAAC_FLAG_LOOPED | EAAC_FLAG_STREAMED))) {
VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags); VGM_LOG("EA EAAC: unknown flags 0x%02x\n", eaac.flags);
goto fail; goto fail;
} }
/* seen in sfx and Dead Space ambient tracks */ /* Non-streamed sounds are stored as a single block (may not set block end flags) */
if (flags & 0x20) { eaac.streamed = (eaac.flags & EAAC_FLAG_STREAMED) != 0;
loop_flag = 1;
loop_start = 0; /* get loops (fairly involved due to the multiple layouts and mutant streamfiles)
loop_end = num_samples; * 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;
} }
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */ //todo need more cases to test how layout/streamfiles react
//channel_count = ((channel_config >> 2) & 0xf) + 1; /* likely, but better fail with unknown values */ if (eaac.loop_start > 0 && !(eaac.codec == EAAC_CODEC_EALAYER3_V1 ||
switch(channel_config) { eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM || eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE)) {
case 0x00: channel_count = 1; break; VGM_LOG("EA EAAC: unknown actual looping for non-EALayer3\n");
case 0x04: channel_count = 2; break; goto fail;
case 0x0c: channel_count = 4; break; }
case 0x14: channel_count = 6; break; }
case 0x1c: channel_count = 8; break;
/* 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: default:
VGM_LOG("EA SNS/SPS: unknown channel config 0x%02x\n", channel_config); VGM_LOG("EA EAAC: unknown channel config 0x%02x\n", eaac.channel_config);
goto fail; goto fail; /* fail with unknown values just in case */
} }
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(eaac.channels,eaac.loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = eaac.sample_rate;
vgmstream->num_samples = num_samples; vgmstream->num_samples = eaac.num_samples;
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = eaac.loop_start;
vgmstream->loop_end_sample = loop_end; vgmstream->loop_end_sample = eaac.loop_end;
vgmstream->meta_type = meta_type; vgmstream->meta_type = meta_type;
/* EA decoder list and known internal FourCCs */ /* 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->coding_type = coding_PCM16_int;
vgmstream->codec_endian = 1; vgmstream->codec_endian = 1;
vgmstream->layout_type = layout_blocked_ea_sns; vgmstream->layout_type = layout_blocked_ea_sns;
break; break;
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
case 0x03: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */ case EAAC_CODEC_EAXMA: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */
uint8_t buf[0x100]; vgmstream->layout_data = build_layered_eaaudiocore_eaxma(streamData, &eaac);
int bytes, block_size, block_count; if (!vgmstream->layout_data) goto fail;
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;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_layered;
break; break;
} }
#endif #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->coding_type = coding_EA_XAS;
vgmstream->layout_type = layout_blocked_ea_sns; vgmstream->layout_type = layout_blocked_ea_sns;
break; break;
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
case 0x05: /* "EL31": EALayer3 v1 [Need for Speed: Hot Pursuit (PS3)] */ case EAAC_CODEC_EALAYER3_V1: /* "EL31": EALayer3 v1 [Need for Speed: Hot Pursuit (PS3)] */
case 0x06: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */ case EAAC_CODEC_EALAYER3_V2_PCM: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */
case 0x07: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */ case EAAC_CODEC_EALAYER3_V2_SPIKE: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */
mpeg_custom_config cfg = {0}; 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 /* EALayer3 needs custom IO that removes blocks on reads to fix some edge cases in L32P
* (otherwise, and removing discards, it'd work with layout_blocked_ea_sns) */ * and to properly apply discard modes (see ealayer3 decoder)
temp_streamFile = setup_eaac_streamfile(streamData, version, codec, start_offset, 0); * (otherwise, and after removing discard code, it'd work with layout_blocked_ea_sns) */
if (!temp_streamFile) goto fail;
start_offset = 0x00; /* must point to the custom streamfile's beginning */ start_offset = 0x00; /* must point to the custom streamfile's beginning */
/* layout is still blocks, but should work fine with the custom mpeg decoder */ 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); vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
}
break; break;
} }
#endif #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->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_blocked_ea_sns; vgmstream->layout_type = layout_blocked_ea_sns;
/* DSP coefs are read in the blocks */ /* DSP coefs are read in the blocks */
break; break;
#ifdef VGM_USE_ATRAC9 #ifdef VGM_USE_ATRAC9
case 0x0a: { /* EATrax */ case EAAC_CODEC_EATRAX: { /* EATrax (unknown FourCC) [Need for Speed: Most Wanted (Vita)] */
atrac9_config cfg = {0}; 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); cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead);
/* 0x10: frame size? (same as config data?) */ /* 0x10: frame size? (same as config data?) */
total_size = read_32bitLE(header_offset + 0x0c,streamHead); /* actual data size without blocks, LE b/c why make sense */ /* 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); vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9; vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none; 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, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset);
temp_streamFile = setup_eaac_streamfile(streamData, version, codec, start_offset, total_size);
if (!temp_streamFile) goto fail; if (!temp_streamFile) goto fail;
start_offset = 0x00; /* must point to the custom streamfile's beginning */
break; break;
} }
#endif #endif
case 0x00: /* "NONE" (internal 'codec not set' flag) */ case EAAC_CODEC_EASPEEX: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */
case 0x01: /* not used/reserved? /MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */ /* TODO */
case 0x09: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ case EAAC_CODEC_EAOPUS: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */
case 0x0b: /* ? */ /* TODO */
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */
case 0x0d: /* ? */
case 0x0e: /* ? */
case 0x0f: /* ? */
default: 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; goto fail;
} }
if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamData,start_offset)) if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamData,start_offset))
goto fail; goto fail;
if (vgmstream->layout_type == layout_blocked_ea_sns)
block_update_ea_sns(start_offset, vgmstream);
close_streamfile(temp_streamFile); close_streamfile(temp_streamFile);
return vgmstream; return vgmstream;
@ -341,3 +684,144 @@ fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; 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;
}

View file

@ -2,140 +2,184 @@
#define _EA_EAAC_STREAMFILE_H_ #define _EA_EAAC_STREAMFILE_H_
#include "../streamfile.h" #include "../streamfile.h"
#define XMA_FRAME_SIZE 0x800
typedef struct { typedef struct {
/* config */
int version;
int codec;
int streamed;
int stream_number;
int stream_count;
off_t stream_offset;
/* state */ /* state */
off_t logical_offset; /* offset that corresponds to physical_offset */ off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */ off_t physical_offset; /* actual file offset */
/* config */ uint32_t block_flag; /* current block flags */
int version; size_t block_size; /* current block size */
int codec; size_t skip_size; /* size to skip from a block start to reach data start */
off_t start_offset; size_t data_size; /* logical size of the block */
size_t total_size; /* size of the resulting substream */ size_t extra_size; /* extra padding/etc size of the block */
size_t logical_size;
} eaac_io_data; } eaac_io_data;
/* Reads skipping EA's block headers, so the resulting data is smaller or larger than physical 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) { 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; size_t total_read = 0;
/* ignore bad reads */ /* ignore bad reads */
if (offset < 0 || offset > data->total_size) { if (offset < 0 || offset > data->logical_size) {
return total_read; return total_read;
} }
/* previous offset: re-start as we can't map logical<>physical offsets /* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */ * (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) { if (offset < data->logical_offset) {
data->physical_offset = data->start_offset; data->physical_offset = data->stream_offset;
data->logical_offset = 0x00; 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) { 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); /* ignore EOF (implicitly handles block end flags) */
block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF; if (data->logical_offset >= data->logical_size) {
break;
if (data->version == 1 && block_flag == 0x48) { }
data->physical_offset += block_size;
continue; /* skip header block */ /* 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;
} }
if (data->version == 1 && block_flag == 0x45)
break; /* stop on last block (always empty) */
switch(data->codec) { switch(data->codec) {
#if 0 case 0x03: { /* EA-XMA */
case 0x03: /* block format: 0x04=num-samples, (size*4 + N XMA packets) per stream (with 1/2ch XMA headers) */
data_size = block_size - ???; int i;
extra_size = (data_size % 0x800); /* deflated padding */
data->skip_size = 0x04 + 0x04;
skip_size = 0x08 + 0x04*data->stream_count; 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; break;
#endif }
case 0x05: /* EALayer3 v1 */ case 0x05: /* EALayer3 v1 */
case 0x06: /* EALayer3 v2 "PCM" */ case 0x06: /* EALayer3 v2 "PCM" */
case 0x07: /* EALayer3 v2 "Spike" */ case 0x07: /* EALayer3 v2 "Spike" */
data_size = block_size - 0x08; data->skip_size = 0x08;
skip_size = 0x08; data->data_size = data->block_size - data->skip_size;
break; break;
case 0x0a: /* EATrax */ case 0x0a: /* EATrax */
data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* should be block_size - 0x08 */ data->skip_size = 0x08;
skip_size = 0x08; data->data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* also block_size - 0x08 */
break; break;
#if 0
case 0x0c: /* EA Opus */
data->skip_size = 0x08;
data->data_size = data->block_size - data->skip_size;
break;
#endif
default: default:
return total_read; return total_read;
} }
}
/* requested offset is outside current block, try next */ /* move to next block */
if (offset >= data->logical_offset + data_size) { if (offset >= data->logical_offset + data->data_size + data->extra_size) {
data->physical_offset += block_size; data->physical_offset += data->block_size;
data->logical_offset += data_size; data->logical_offset += data->data_size + data->extra_size;
data->data_size = 0;
data->extra_size = 0;
continue; continue;
} }
/* reads could fall in the middle of the block */ /* read data */
intradata_offset = offset - data->logical_offset; {
intrablock_offset = skip_size + intradata_offset; size_t bytes_consumed, bytes_done, to_read;
/* clamp reads up to this block's end */ bytes_consumed = offset - data->logical_offset;
to_read = (data_size - intradata_offset);
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) if (to_read > length)
to_read = length; to_read = length;
if (to_read == 0) bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
break; /* should never happen... */ }
else { /* offset falls within logical padded data */
/* finally read and move buffer/offsets */ to_read = data->data_size + data->extra_size - bytes_consumed;
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile); if (to_read > length)
total_read += bytes_read; to_read = length;
if (bytes_read != to_read) memset(dest, 0xFF, to_read); /* no real need though, padding is ignored */
break; /* couldn't read fully */ bytes_done = to_read;
}
dest += bytes_read; break;
offset += bytes_read;
length -= bytes_read;
/* block fully read, go next */
if (intradata_offset + bytes_read == data_size) {
data->physical_offset += block_size;
data->logical_offset += data_size;
} }
if (data->version == 0 && block_flag == 0x80) default:
break; /* stop on last block */ 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;
}
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
length -= bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
} }
return total_read; return total_read;
} }
static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
off_t physical_offset, max_physical_offset; off_t physical_offset, max_physical_offset;
size_t total_size = 0; size_t logical_size = 0;
if (data->total_size) if (data->logical_size)
return data->total_size; return data->logical_size;
physical_offset = data->start_offset; physical_offset = data->stream_offset;
max_physical_offset = get_streamfile_size(streamfile) - data->start_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) { 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_flag = (uint8_t)read_8bit(physical_offset+0x00,streamfile);
block_size = read_32bitBE(physical_offset+0x00,streamfile) & 0x00FFFFFF; block_size = read_32bitBE(physical_offset+0x00,streamfile) & 0x00FFFFFF;
if (data->version == 0 && block_flag != 0x00 && block_flag != 0x80) 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) { if (data->version == 1 && block_flag == 0x48) {
physical_offset += block_size; 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) if (data->version == 1 && block_flag == 0x45)
break; /* stop on last block (always empty) */ break; /* stop on last block (always empty) */
if (data->version == 1 && block_flag != 0x44) if (data->version == 1 && block_flag != 0x44)
break; /* data block expected */ break; /* unknown block */
switch(data->codec) { switch(data->codec) {
#if 0 case 0x03: /* EA-XMA */
case 0x03: skip_size = 0x04 + 0x04;
data_size = block_size - ???; for (i = 0; i < data->stream_number; i++) {
data_size += (data_size % 0x800); /* deflated padding */ 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; break;
#endif
case 0x05: /* EALayer3 v1 */ case 0x05: /* EALayer3 v1 */
case 0x06: /* EALayer3 v2 "PCM" */ case 0x06: /* EALayer3 v2 "PCM" */
case 0x07: /* EALayer3 v2 "Spike" */ case 0x07: /* EALayer3 v2 "Spike" */
@ -160,40 +210,62 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
break; break;
case 0x0a: /* EATrax */ 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; 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: default:
return 0; return 0;
} }
physical_offset += block_size; 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 */ break; /* stop on last block */
} }
data->total_size = total_size; /* logical size can be bigger in EA-XMA though */
return data->total_size; 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: /* 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) * - 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 * - 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; STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
eaac_io_data io_data = {0}; eaac_io_data io_data = {0};
size_t io_data_size = sizeof(eaac_io_data); size_t io_data_size = sizeof(eaac_io_data);
io_data.version = version; io_data.version = version;
io_data.codec = codec; io_data.codec = codec;
io_data.start_offset = start_offset; io_data.streamed = streamed;
io_data.total_size = total_size; /* optional */ io_data.stream_number = stream_number;
io_data.physical_offset = start_offset; 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 */ /* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile); new_streamFile = open_wrap_streamfile(streamFile);

View file

@ -1,6 +1,7 @@
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "ea_schl_streamfile.h"
/* header version */ /* header version */
#define EA_VERSION_NONE -1 #define EA_VERSION_NONE -1
@ -29,7 +30,7 @@
#define EA_CODEC1_NONE -1 #define EA_CODEC1_NONE -1
#define EA_CODEC1_PCM 0x00 #define EA_CODEC1_PCM 0x00
#define EA_CODEC1_VAG 0x01 // unsure #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_MT10 0x09
//#define EA_CODEC1_N64 ? //#define EA_CODEC1_N64 ?
@ -46,8 +47,31 @@
#define EA_CODEC2_XBOXADPCM 0x14 #define EA_CODEC2_XBOXADPCM 0x14
#define EA_CODEC2_MT5 0x16 #define EA_CODEC2_MT5 0x16
#define EA_CODEC2_EALAYER3 0x17 #define EA_CODEC2_EALAYER3 0x17
#define EA_CODEC2_ATRAC3PLUS 0x1B /* Medal of Honor Heroes 2 (PSP) */ #define EA_CODEC2_ATRAC3PLUS 0x1B
//todo #define EA_CODEC2_ATRAC9 0x-- /* supposedly exists */
/* 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 #define EA_MAX_CHANNELS 6
@ -66,15 +90,16 @@ typedef struct {
off_t offsets[EA_MAX_CHANNELS]; off_t offsets[EA_MAX_CHANNELS];
off_t coefs[EA_MAX_CHANNELS]; off_t coefs[EA_MAX_CHANNELS];
off_t loops[EA_MAX_CHANNELS];
int big_endian; int big_endian;
int loop_flag; int loop_flag;
int codec_version; int codec_config;
} ea_header; } ea_header;
static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams); static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams);
static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int total_streams); static VGMSTREAM * parse_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 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 int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream);
static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); static 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) { VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
/* check extension; exts don't seem enforced by EA's tools, but usually: /* 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) */ * 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; goto fail;
/* check header */ /* check header */
if (read_32bitBE(0x00,streamFile) != 0x5343486C && /* "SCHl" */ if (read_32bitBE(0x00,streamFile) != EA_BLOCKID_HEADER && /* "SCHl" */
read_32bitBE(0x00,streamFile) != 0x5348454E && /* "SHEN" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */
read_32bitBE(0x00,streamFile) != 0x53484652 && /* "SHFR" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */
read_32bitBE(0x00,streamFile) != 0x53484745 && /* "SHGE" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */
read_32bitBE(0x00,streamFile) != 0x53484954 && /* "SHIT" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */
read_32bitBE(0x00,streamFile) != 0x53485350 && /* "SHSP" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */
read_32bitBE(0x00,streamFile) != 0x53485255 && /* "SHRU" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */
read_32bitBE(0x00,streamFile) != 0x53484A41) /* "SHJA" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA)) /* "SHJA" */
goto fail; goto fail;
/* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. /* 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; goto fail;
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */ /* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
if (read_32bitBE(0x00,streamFile) == 0x424E4B6C || /* "BNKl" (common) */ if (read_32bitBE(0x100,streamFile) == EA_BNK_HEADER_LE)
read_32bitBE(0x00,streamFile) == 0x424E4B62) /* "BNKb" (FIFA 98 SS) */
offset = 0;
else if (read_32bitBE(0x100,streamFile) == 0x424E4B6C) /* "BNKl" (common) */
offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */ offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */
else else
goto fail; offset = 0x00;
return parse_bnk_header(streamFile, offset, streamFile->stream_index, 0); return parse_bnk_header(streamFile, offset, streamFile->stream_index, 0);
@ -131,13 +153,15 @@ fail:
return NULL; 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) { VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; 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; 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; uint32_t i, j, k, num_sounds, total_sound_tables;
uint16_t num_tables; uint16_t num_tables;
uint8_t version, sound_type, num_entries; uint8_t sound_type, num_entries;
off_t sound_table_offsets[0x2000]; off_t sound_table_offsets[0x2000];
STREAMFILE * astData = NULL; STREAMFILE * astData = NULL;
VGMSTREAM * vgmstream; VGMSTREAM * vgmstream;
@ -151,10 +175,6 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
if (read_32bitBE(0x00, streamFile) != 0x41424B43) /* "ABKC" */ if (read_32bitBE(0x00, streamFile) != 0x41424B43) /* "ABKC" */
goto fail; goto fail;
version = read_8bit(0x06, streamFile);
if (version > 0x01)
goto fail;
/* use table offset to check endianness */ /* use table offset to check endianness */
if (guess_endianness32bit(0x1C,streamFile)) { if (guess_endianness32bit(0x1C,streamFile)) {
read_32bit = read_32bitBE; 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) target_stream = 1;
if (target_stream < 0) goto fail; if (target_stream < 0)
goto fail;
num_tables = read_16bit(0x0A, streamFile); num_tables = read_16bit(0x0A, streamFile);
header_table_offset = read_32bit(0x1C, streamFile); header_table_offset = read_32bit(0x1C, streamFile);
@ -173,9 +194,16 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
target_entry_offset = 0; target_entry_offset = 0;
total_sound_tables = 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++) { for (i = 0; i < num_tables; i++) {
num_entries = read_8bit(header_table_offset + 0x24, streamFile); num_entries = read_8bit(header_table_offset + 0x24, streamFile);
base_offset = read_32bit(header_table_offset + 0x2C, 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++) { for (j = 0; j < num_entries; j++) {
value_offset = read_32bit(header_table_offset + 0x3C + 0x04 * j, streamFile); 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; sound_table_offsets[total_sound_tables++] = table_offset;
num_sounds = read_32bit(table_offset, streamFile); num_sounds = read_32bit(table_offset, streamFile);
if (num_sounds == 0xffffffff) goto fail; /* EOF read */
for (k = 0; k < num_sounds; k++) { for (k = 0; k < num_sounds; k++) {
entry_offset = table_offset + 0x04 + 0x0C * 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 */ /* some of these dummies pointing at sound 0 in BNK */
if (sound_type == 0x00 && read_32bit(entry_offset + 0x04, streamFile) == 0) 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; 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(?) */ /* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed and prefetched(?) */
/* 0x01: ??? */ /* 0x01: ??? */
/* 0x04: index for normal sounds, offset for streamed sounds */ /* 0x04: index for normal sounds, offset for streamed sounds */
/* 0x08: offset for prefetched 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) { switch (sound_type) {
case 0x00: case 0x00:
if (!bnk_offset) goto fail; if (!bnk_offset)
if (read_32bitBE(bnk_offset, streamFile) != 0x424E4B6C && /* "BNKl" */
read_32bitBE(bnk_offset,streamFile) != 0x424E4B62) /* BNKb */
goto fail; goto fail;
bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1; bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1;
vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, total_sounds); vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, total_sounds);
if (!vgmstream) goto fail; if (!vgmstream)
goto fail;
break; break;
case 0x01: case 0x01:
case 0x02: case 0x02:
astData = open_streamfile_by_ext(streamFile, "ast"); astData = open_streamfile_by_ext(streamFile, "ast");
if (!astData) goto fail; if (!astData)
goto fail;
if (sound_type == 0x01) if (sound_type == 0x01)
schl_offset = read_32bit(target_entry_offset + 0x04, streamFile); schl_offset = read_32bit(target_entry_offset + 0x04, streamFile);
else else
schl_offset = read_32bit(target_entry_offset + 0x08, streamFile); 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; goto fail;
vgmstream = parse_schl_block(astData, schl_offset, total_sounds); vgmstream = parse_schl_block(astData, schl_offset, total_sounds);
if (!vgmstream) goto fail; if (!vgmstream)
goto fail;
break; break;
default: default:
@ -265,38 +298,87 @@ fail:
return NULL; return NULL;
} }
/* EA HDR/DAT combo - frequently used for storing speech */ /* 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(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) {
int target_stream = streamFile->stream_index; int target_stream = streamFile->stream_index;
uint8_t userdata_size, total_sounds; uint8_t userdata_size, total_sounds;
size_t total_size; off_t schl_offset, offset_mult;
off_t schl_offset; STREAMFILE *datFile = NULL;
STREAMFILE *datFile = NULL, *sthFile = NULL;
VGMSTREAM *vgmstream; 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*); int32_t (*read_32bit)(off_t,STREAMFILE*);
int16_t (*read_16bit)(off_t,STREAMFILE*); int16_t (*read_16bit)(off_t,STREAMFILE*);
/* No nice way to validate these so we do what we can */ /* seems to always start with 0x00000001 */
sthFile = open_streamfile_by_ext(streamFile, "sth"); if (read_32bitLE(0x00, streamFile) != 0x00000001 &&
if (sthFile) goto fail; /* newer version using SNR/SNS */ read_32bitBE(0x00, streamFile) != 0x00000001)
goto fail;
datFile = open_streamfile_by_ext(streamFile, "dat"); bigFile = open_streamfile_by_ext(streamFile, "big");
if (!datFile) goto fail; if (!bigFile)
goto fail;
/* 0x00: hash */ if (read_32bitBE(0x00, bigFile) != EA_BLOCKID_HEADER)
/* 0x02: sub-ID (used for different police voices in NFS games) */ goto fail;
/* 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 (target_stream == 0) target_stream = 1; /* use number of files for endianness check */
if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) goto fail; if (guess_endianness32bit(0x04,streamFile)) {
if (guess_endianness16bit(0x08,streamFile)) {
read_32bit = read_32bitBE; read_32bit = read_32bitBE;
read_16bit = read_16bitBE; read_16bit = read_16bitBE;
} else { } else {
@ -304,24 +386,57 @@ VGMSTREAM * init_vgmstream_ea_hdr(STREAMFILE *streamFile) {
read_16bit = read_16bitLE; read_16bit = read_16bitLE;
} }
total_size = (size_t)read_16bit(0x08, streamFile) * 0x0100; num_hdr = read_32bit(0x04, streamFile);
if (total_size > get_streamfile_size(datFile)) goto fail; if (read_32bit(0x54,streamFile) != num_hdr)
/* 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 */
goto fail; goto fail;
vgmstream = parse_schl_block(datFile, schl_offset, total_sounds); if (target_stream == 0) target_stream = 1;
if (!vgmstream) goto fail; 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; return vgmstream;
fail: fail:
close_streamfile(datFile); close_streamfile(bigFile);
close_streamfile(sthFile);
return NULL; return NULL;
} }
@ -331,14 +446,17 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int to
size_t header_size; size_t header_size;
ea_header ea = { 0 }; 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); header_size = read_32bitBE(offset + 0x04, streamFile);
else ea.codec_config |= 0x02;
}
else {
header_size = read_32bitLE(offset + 0x04, streamFile); header_size = read_32bitLE(offset + 0x04, streamFile);
}
header_offset = offset + 0x08; 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; goto fail;
start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */
@ -360,21 +478,26 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta
int i, bnk_version; int i, bnk_version;
int total_bnk_sounds, real_bnk_sounds = 0; int total_bnk_sounds, real_bnk_sounds = 0;
/* use header size as endianness flag */ /* check header */
if (guess_endianness32bit(offset + 0x08,streamFile)) { /* BNK header endianness is platform-native */
if (read_32bitBE(offset + 0x00, streamFile) == EA_BNK_HEADER_BE) {
read_32bit = read_32bitBE; read_32bit = read_32bitBE;
read_16bit = read_16bitBE; read_16bit = read_16bitBE;
} else { }
else if (read_32bitBE(offset + 0x00, streamFile) == EA_BNK_HEADER_LE) {
read_32bit = read_32bitLE; read_32bit = read_32bitLE;
read_16bit = read_16bitLE; read_16bit = read_16bitLE;
} }
else {
goto fail;
}
bnk_version = read_8bit(offset + 0x04,streamFile); bnk_version = read_8bit(offset + 0x04,streamFile);
total_bnk_sounds = read_16bit(offset + 0x06,streamFile); total_bnk_sounds = read_16bit(offset + 0x06,streamFile);
/* check multi-streams */ /* check multi-streams */
switch(bnk_version) { 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; table_offset = 0x0c;
header_size = read_32bit(offset + 0x08,streamFile); /* full size */ header_size = read_32bit(offset + 0x08,streamFile); /* full size */
break; 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 (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; goto fail;
/* fix absolute offsets so it works in next funcs */ /* 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 */ 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 */ /* rest is common */
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams ? total_streams : real_bnk_sounds); 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->loop_end_sample = ea->loop_end;
vgmstream->codec_endian = ea->big_endian; 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; 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]) { 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 */ int interleave = (vgmstream->num_samples / 14 * 8); /* full interleave */
for (i = 0; i < vgmstream->channels; i++) { for (i = 0; i < vgmstream->channels; i++) {
ea->offsets[i] = ea->offsets[0] + interleave*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->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 /* 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). * 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) { switch (ea->codec2) {
case EA_CODEC2_EAXA: /* EA-XA, CDXA ADPCM variant */ 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) if (ea->platform != EA_PLATFORM_SAT && ea->channels > 1)
vgmstream->coding_type = coding_EA_XA; /* original version, stereo stream */ vgmstream->coding_type = coding_EA_XA; /* original version, stereo stream */
else else
@ -586,19 +703,57 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
use_pcm_blocks = 1; 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->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; if (!vgmstream->codec_data) goto fail;
break; 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: default:
VGM_LOG("EA SCHl: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform); VGM_LOG("EA SCHl: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
goto fail; goto fail;
} }
/* open files; channel offsets are updated below */ /* open files; channel offsets are updated below */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail; goto fail;
@ -606,54 +761,44 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
if (is_bnk) { if (is_bnk) {
/* setup channel offsets */ /* 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++) { for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = ea->offsets[0]; 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++) { // for (i = 0; i < vgmstream->channels; i++) {
// vgmstream->ch[i].offset = ea->offsets[0] + vgmstream->interleave_block_size*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++) { for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = ea->offsets[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; else if (vgmstream->layout_type == layout_blocked_ea_schl) {
/* regular SCHls, except ATRAC3plus */
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 {
if (total_streams == 0) { if (total_streams == 0) {
/* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this */ /* 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); int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream);
if (total_samples > vgmstream->num_samples) if (total_samples > vgmstream->num_samples)
vgmstream->num_samples = total_samples; vgmstream->num_samples = total_samples;
} }
/* setup first block to update offsets */
block_update_ea_schl(start_offset,vgmstream);
} }
return 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) */ /* 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; off_t offset = begin_offset;
uint32_t platform_id; uint32_t platform_id;
int is_header_end = 0; int is_header_end = 0;
int is_bnk = bnk_version;
/* null defaults as 0 can be valid */ /* null defaults as 0 can be valid */
ea->version = EA_VERSION_NONE; 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); uint8_t patch_type = read_8bit(offset,streamFile);
offset++; 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) { switch(patch_type) {
case 0x00: /* signals non-default block rate and maybe other stuff; or padding after 0xFF */ case 0x00: /* signals non-default block rate and maybe other stuff; or padding after 0xFF */
if (!is_header_end) 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 0x05: /* unknown (usually 0x50 except Madden NFL 3DS: 0x3e800) */
case 0x06: /* priority (0..100, always 0x65 for streams, others for BNKs; rarely ommited) */ 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 0x08: /* release envelope (BNK only) */
case 0x09: /* related to playback envelope (BNK only) */ case 0x09: /* related to playback envelope (BNK only) */
case 0x0A: /* bend range (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 0x0C: /* pan offset (BNK only) */
case 0x0D: /* random pan offset range (BNK only) */ case 0x0D: /* random pan offset range (BNK only) */
case 0x0E: /* volume (BNK only) */ case 0x0E: /* volume (BNK only) */
case 0x0F: /* random volume range (BNK only) */ case 0x0F: /* random volume range (BNK only) */
case 0x10: /* detune (BNK only) */ case 0x10: /* detune (BNK only) */
case 0x11: /* random detune range (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 0x13: /* effect bus (0..127) */
case 0x14: /* emdedded user data (free size/value) */ 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 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 0x1B: /* unknown (movie only?) */
case 0x1C: /* initial envelope volume (BNK only) */ case 0x1C: /* initial envelope volume (BNK only) */
case 0x1D: /* unknown, rare [NASCAR 06 (Xbox)] */ case 0x1D: /* unknown, rare [NASCAR 06 (Xbox)] */
case 0x1E: case 0x1E: /* related to ch1? (BNK only) */
case 0x1F: case 0x1F:
case 0x20: case 0x20:
case 0x21: case 0x21: /* related to ch2? (BNK only) */
case 0x22: case 0x22:
case 0x23: case 0x23:
case 0x24: /* master random detune range (BNK only) */ case 0x24: /* master random detune range (BNK only) */
case 0x25: /* unknown */ case 0x25: /* unknown */
case 0x26: /* unknown, rare [FIFA 07 (Xbox)] */
read_patch(streamFile, &offset); read_patch(streamFile, &offset);
break; break;
case 0xFC: /* padding for alignment between patches */ case 0xFC: /* padding for alignment between patches */
case 0xFE: /* padding? (actually exists?) */
case 0xFD: /* info section start marker */ case 0xFD: /* info section start marker */
break; 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); ea->loop_start = read_patch(streamFile, &offset);
break; break;
case 0x87: /* loop end sample */ 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; break;
/* channel offsets (BNK only), can be the equal for all channels or interleaved; not necessarily contiguous */ /* 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); read_patch(streamFile, &offset);
break; 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 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) */ 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) */ /* (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 0x8D: /* unknown, rare [FIFA 07 (GC)] */
case 0x8E: case 0x8E:
case 0x92: /* bytes per sample? */ 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 0x98: /* embedded time stretch 1 (long data for who-knows-what) */
case 0x99: /* embedded time stretch 2 */ case 0x99: /* embedded time stretch 2 */
case 0x9C: /* azimuth ch1 */ 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) */ case 0xFF: /* header end (then 0-padded so it's 32b aligned) */
is_header_end = 1; is_header_end = 1;
break; 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: 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; goto fail;
break;
} }
} }
@ -982,26 +1154,33 @@ 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_X360: ea->sample_rate = 44100; break;
case EA_PLATFORM_PSP: ea->sample_rate = 22050; break; case EA_PLATFORM_PSP: ea->sample_rate = 22050; break;
case EA_PLATFORM_PS3: ea->sample_rate = 44100; 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: default:
VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform); VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform);
goto fail; goto fail;
} }
} }
/* special flag: 1=has ADPCM history per block, 0=doesn't */ /* some codecs have ADPCM hist at the start of every block in streams (but not BNKs) */
if (ea->codec2 == EA_CODEC2_GCADPCM && ea->platform == EA_PLATFORM_3DS) { if (!is_bnk) {
ea->codec_version = 1; 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;
} }
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;
} }
} }
return offset; return offset;
@ -1014,24 +1193,27 @@ fail:
* music (.map/lin). Subfiles always share header, except num_samples. */ * 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) { static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream) {
int num_samples = 0; int num_samples = 0;
int new_schl = 0; int multiple_schl = 0;
/* calc num_samples as playable data size varies between files/blocks */ /* calc num_samples as playable data size varies between files/blocks */
{ {
vgmstream->next_block_offset = start_offset; vgmstream->next_block_offset = start_offset;
do { do {
uint32_t block_id = read_32bitBE(vgmstream->next_block_offset+0x00,streamFile); 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) */ if (block_id == EA_BLOCKID_HEADER) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
new_schl = 1; multiple_schl = 1;
block_update_ea_schl(vgmstream->next_block_offset,vgmstream); block_update_ea_schl(vgmstream->next_block_offset,vgmstream);
num_samples += vgmstream->current_block_samples; num_samples += vgmstream->current_block_samples;
} }
while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); 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) */ /* 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"); ;VGM_LOG("EA SCHl: multiple SCHl found\n");
return num_samples; 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); block_size = read_32bitBE(block_offset+0x04,streamFile);
switch(block_id) { switch(block_id) {
case 0x5343446C: /* "SCDl" */ case EA_BLOCKID_DATA: /* "SCDl" */
case 0x5344454E: /* "SDEN" */ case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_EN: /* "SDEN" */
case 0x53444652: /* "SDFR" */ case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_FR: /* "SDFR" */
case 0x53444745: /* "SDGE" */ case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_GE: /* "SDGE" */
case 0x53444954: /* "SDIT" */ case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_IT: /* "SDIT" */
case 0x53445350: /* "SDSP" */ case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_SP: /* "SDSP" */
case 0x53445255: /* "SDRU" */ case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_RU: /* "SDRU" */
case 0x53444A41: /* "SDJA" */ 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 */ 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; return block_offset + 0x0c + ea->channels*0x04 + offset;
case 0x00000000: case 0x00000000:

View file

@ -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)) if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail; goto fail;
/* setup first block to update offsets */
block_update_ea_schl(start_offset,vgmstream);
return vgmstream; return vgmstream;
fail: fail:

View file

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

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